summaryrefslogtreecommitdiff
path: root/src/kadmin/dbutil/tdumputil.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kadmin/dbutil/tdumputil.c')
-rw-r--r--src/kadmin/dbutil/tdumputil.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/kadmin/dbutil/tdumputil.c b/src/kadmin/dbutil/tdumputil.c
new file mode 100644
index 000000000000..2c14d5670e08
--- /dev/null
+++ b/src/kadmin/dbutil/tdumputil.c
@@ -0,0 +1,266 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/tdumputil.c - utilities for tab-separated, etc. files */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "k5-platform.h" /* for vasprintf */
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tdumputil.h"
+
+/*
+ * Structure describing flavor of a tabular output format.
+ *
+ * fieldsep is the field separator
+ *
+ * recordsep is the record/line separator
+ *
+ * quotechar begins and ends a quoted field. If an instance of quotechar
+ * occurs within a quoted field value, it is doubled.
+ *
+ * Values are only quoted if they contain fieldsep, recordsep, or quotechar.
+ */
+struct flavor {
+ int fieldsep; /* field separator */
+ int recordsep; /* record separator */
+ int quotechar; /* quote character */
+};
+
+struct rechandle {
+ FILE *fh;
+ const char *rectype;
+ int do_sep;
+ struct flavor flavor;
+};
+
+static const struct flavor tabsep = {
+ '\t', /* fieldsep */
+ '\n', /* recordsep */
+ '\0' /* quotechar */
+};
+
+static const struct flavor csv = {
+ ',', /* fieldsep */
+ '\n', /* recordsep */
+ '"' /* quotechar */
+};
+
+/*
+ * Double any quote characters present in a quoted field.
+ */
+static char *
+qquote(struct flavor *fl, const char *s)
+{
+ const char *sp;
+ struct k5buf buf;
+
+ k5_buf_init_dynamic(&buf);
+ for (sp = s; *sp != '\0'; sp++) {
+ k5_buf_add_len(&buf, sp, 1);
+ if (*sp == fl->quotechar)
+ k5_buf_add_len(&buf, sp, 1);
+ }
+ return buf.data;
+}
+
+/*
+ * Write an optionally quoted field.
+ */
+static int
+writequoted(struct rechandle *h, const char *fmt, va_list ap)
+{
+ int doquote = 0, ret;
+ char *s = NULL, *qs = NULL;
+ struct flavor fl = h->flavor;
+
+ assert(fl.quotechar != '\0');
+ ret = vasprintf(&s, fmt, ap);
+ if (ret < 0)
+ return ret;
+ if (strchr(s, fl.fieldsep) != NULL)
+ doquote = 1;
+ if (strchr(s, fl.recordsep) != NULL)
+ doquote = 1;
+ if (strchr(s, fl.quotechar) != NULL)
+ doquote = 1;
+
+ if (doquote) {
+ qs = qquote(&fl, s);
+ if (qs == NULL) {
+ ret = -1;
+ goto cleanup;
+ }
+ ret = fprintf(h->fh, "%c%s%c", fl.quotechar, qs, fl.quotechar);
+ } else {
+ ret = fprintf(h->fh, "%s", s);
+ }
+cleanup:
+ free(s);
+ free(qs);
+ return ret;
+}
+
+/*
+ * Return a rechandle with the requested file handle and rectype.
+ *
+ * rectype must be a valid pointer for the entire lifetime of the rechandle (or
+ * null)
+ */
+static struct rechandle *
+rechandle_common(FILE *fh, const char *rectype)
+{
+ struct rechandle *h = calloc(1, sizeof(*h));
+
+ if (h == NULL)
+ return NULL;
+ h->fh = fh;
+ h->rectype = rectype;
+ h->do_sep = 0;
+ return h;
+}
+
+/*
+ * Return a rechandle for tab-separated output.
+ */
+struct rechandle *
+rechandle_tabsep(FILE *fh, const char *rectype)
+{
+ struct rechandle *h = rechandle_common(fh, rectype);
+
+ if (h == NULL)
+ return NULL;
+ h->flavor = tabsep;
+ return h;
+}
+
+/*
+ * Return a rechandle for CSV output.
+ */
+struct rechandle *
+rechandle_csv(FILE *fh, const char *rectype)
+{
+ struct rechandle *h = rechandle_common(fh, rectype);
+
+ if (h == NULL)
+ return NULL;
+ h->flavor = csv;
+ return h;
+}
+
+/*
+ * Free a rechandle.
+ */
+void
+rechandle_free(struct rechandle *h)
+{
+ free(h);
+}
+
+/*
+ * Start a record. This includes writing a record type prefix (rectype) if
+ * specified.
+ */
+int
+startrec(struct rechandle *h)
+{
+ if (h->rectype == NULL) {
+ h->do_sep = 0;
+ return 0;
+ }
+ h->do_sep = 1;
+ return fputs(h->rectype, h->fh);
+}
+
+/*
+ * Write a single field of a record. This includes writing a separator
+ * character, if appropriate.
+ */
+int
+writefield(struct rechandle *h, const char *fmt, ...)
+{
+ int ret = 0;
+ va_list ap;
+ struct flavor fl = h->flavor;
+
+ if (h->do_sep) {
+ ret = fputc(fl.fieldsep, h->fh);
+ if (ret < 0)
+ return ret;
+ }
+ h->do_sep = 1;
+ va_start(ap, fmt);
+ if (fl.quotechar == '\0')
+ ret = vfprintf(h->fh, fmt, ap);
+ else
+ ret = writequoted(h, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+/*
+ * Finish a record (line).
+ */
+int
+endrec(struct rechandle *h)
+{
+ int ret = 0;
+ struct flavor fl = h->flavor;
+
+ ret = fputc(fl.recordsep, h->fh);
+ h->do_sep = 0;
+ return ret;
+}
+
+/*
+ * Write a header line if h->rectype is null. (If rectype is set, it will be
+ * prefixed to output lines, most likely in a mixed record type output file, so
+ * it doesn't make sense to output a header line in that case.)
+ */
+int
+writeheader(struct rechandle *h, char * const *a)
+{
+ int ret = 0;
+ char * const *p;
+
+ if (h->rectype != NULL)
+ return 0;
+ for (p = a; *p != NULL; p++) {
+ ret = writefield(h, "%s", *p);
+ if (ret < 0)
+ return ret;
+ }
+ ret = endrec(h);
+ return ret;
+}