Blame src/kadmin/dbutil/tdumputil.c

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