krb5 commit: Add utility functions for tabular dumps
Tom Yu
tlyu at mit.edu
Mon Sep 14 14:13:22 EDT 2015
https://github.com/krb5/krb5/commit/31a63dd8f5719255f9aea0be54d79247ec3b9ab6
commit 31a63dd8f5719255f9aea0be54d79247ec3b9ab6
Author: Tom Yu <tlyu at mit.edu>
Date: Wed Sep 9 14:05:24 2015 -0400
Add utility functions for tabular dumps
These utility functions allow for tab-separated and comma-separated
(CSV) output. These are primarily to support the tabular dump
capability for kdb5_util. Additional output options can be added
later.
ticket: 8243 (new)
subjetct: Add tabular dump capability to kdb5_util
src/kadmin/dbutil/Makefile.in | 8 +
src/kadmin/dbutil/t_tdumputil.c | 115 ++++++++++++++++
src/kadmin/dbutil/t_tdumputil.py | 32 +++++
src/kadmin/dbutil/tdumputil.c | 266 ++++++++++++++++++++++++++++++++++++++
src/kadmin/dbutil/tdumputil.h | 51 +++++++
5 files changed, 472 insertions(+), 0 deletions(-)
diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in
index 047587a..0b2b405 100644
--- a/src/kadmin/dbutil/Makefile.in
+++ b/src/kadmin/dbutil/Makefile.in
@@ -28,4 +28,12 @@ install::
clean::
$(RM) $(PROG) $(OBJS) import_err.c import_err.h
+T_TDUMPUTIL_OBJS = t_tdumputil.o tdumputil.o
+
+t_tdumputil: $(T_TDUMPUTIL_OBJS) $(SUPPORT_DEPLIB)
+ $(CC_LINK) -o $@ $(T_TDUMPUTIL_OBJS) $(SUPPORT_LIB)
+
depend:: import_err.h
+
+check-pytests:: t_tdumputil
+ $(RUNPYTEST) $(srcdir)/t_tdumputil.py $(PYTESTFLAGS)
diff --git a/src/kadmin/dbutil/t_tdumputil.c b/src/kadmin/dbutil/t_tdumputil.c
new file mode 100644
index 0000000..a9ed0e5
--- /dev/null
+++ b/src/kadmin/dbutil/t_tdumputil.c
@@ -0,0 +1,115 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/t_tdumputil.c - test tdumputil.c functions */
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "tdumputil.h"
+
+static char *argv0;
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s: [-T rectype] [-c] nfields {fieldnames} {fields}\n",
+ argv0);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, csv = 0, i, nf;
+ char **a, *rectype = NULL;
+ struct rechandle *h;
+
+ argv0 = argv[0];
+ while ((ch = getopt(argc, argv, "T:c")) != -1) {
+ switch (ch) {
+ case 'T':
+ rectype = optarg;
+ break;
+ case 'c':
+ csv = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (csv)
+ h = rechandle_csv(stdout, rectype);
+ else
+ h = rechandle_tabsep(stdout, rectype);
+ if (h == NULL)
+ exit(1);
+
+ if (*argv == NULL)
+ usage();
+ nf = atoi(*argv);
+ argc--;
+ argv++;
+ a = calloc(nf + 1, sizeof(*a));
+ if (a == NULL)
+ exit(1);
+
+ for (i = 0; argv[i] != NULL && i < nf; i++)
+ a[i] = argv[i];
+ if (i != nf)
+ usage();
+ argv += nf;
+ a[nf] = NULL;
+
+ if (rectype == NULL && writeheader(h, a) < 0)
+ exit(1);
+ free(a);
+
+ while (*argv != NULL) {
+ if (startrec(h) < 0)
+ exit(1);
+ for (i = 0; argv[i] != NULL && i < nf; i++) {
+ if (writefield(h, "%s", argv[i]) < 0)
+ exit(1);
+ }
+ if (i != nf)
+ usage();
+ argv += nf;
+ if (endrec(h) < 0)
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/src/kadmin/dbutil/t_tdumputil.py b/src/kadmin/dbutil/t_tdumputil.py
new file mode 100755
index 0000000..5d7ac38
--- /dev/null
+++ b/src/kadmin/dbutil/t_tdumputil.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+from k5test import *
+from subprocess import *
+
+realm = K5Realm(create_kdb=False)
+
+def compare(s, expected, msg):
+ if s == expected:
+ return
+ print 'expected:', repr(expected)
+ print 'got:', repr(s)
+ fail(msg)
+
+out = realm.run(['./t_tdumputil', '2', 'field1', 'field2',
+ 'value1', 'value2'])
+expected = 'field1\tfield2\nvalue1\tvalue2\n'
+compare(out, expected, 'tab-separated values')
+
+out = realm.run(['./t_tdumputil', '-c', '2', 'field1', 'field2',
+ 'space value', 'comma,value',
+ 'quote"value', 'quotes""value'])
+expected = 'field1,field2\nspace value,"comma,value"\n' \
+ '"quote""value","quotes""""value"\n'
+compare(out, expected, 'comma-separated values')
+
+out = realm.run(['./t_tdumputil', '-T', 'rectype', '2', 'field1', 'field2',
+ 'value1', 'value2'])
+expected = 'rectype\tvalue1\tvalue2\n'
+compare(out, expected, 'rectype prefixed')
+
+success('tabdump utilities')
diff --git a/src/kadmin/dbutil/tdumputil.c b/src/kadmin/dbutil/tdumputil.c
new file mode 100644
index 0000000..2c14d56
--- /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;
+}
diff --git a/src/kadmin/dbutil/tdumputil.h b/src/kadmin/dbutil/tdumputil.h
new file mode 100644
index 0000000..b743731
--- /dev/null
+++ b/src/kadmin/dbutil/tdumputil.h
@@ -0,0 +1,51 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/tdumputil.h - 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.
+ */
+
+#ifndef TDUMPUTIL_H
+#define TDUMPUTIL_H
+
+struct rechandle;
+
+struct rechandle *rechandle_csv(FILE *, const char *);
+struct rechandle *rechandle_tabsep(FILE *, const char *);
+void rechandle_free(struct rechandle *);
+
+int startrec(struct rechandle *);
+int writefield(struct rechandle *, const char *, ...)
+#if !defined(__cplusplus) && (__GNUC__ > 2)
+ __attribute__((__format__(__printf__, 2, 3)))
+#endif
+ ;
+int endrec(struct rechandle *);
+int writeheader(struct rechandle *, char * const *);
+
+#endif /* TDUMPUTIL_H */
More information about the cvs-krb5
mailing list