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