1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* kdc/tdumputil.c - utilities for tab-separated, etc. files */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert * Copyright (C) 2015 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert * All rights reserved.
6*7f2fe78bSCy Schubert *
7*7f2fe78bSCy Schubert * Redistribution and use in source and binary forms, with or without
8*7f2fe78bSCy Schubert * modification, are permitted provided that the following conditions
9*7f2fe78bSCy Schubert * are met:
10*7f2fe78bSCy Schubert *
11*7f2fe78bSCy Schubert * * Redistributions of source code must retain the above copyright
12*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer.
13*7f2fe78bSCy Schubert *
14*7f2fe78bSCy Schubert * * Redistributions in binary form must reproduce the above copyright
15*7f2fe78bSCy Schubert * notice, this list of conditions and the following disclaimer in
16*7f2fe78bSCy Schubert * the documentation and/or other materials provided with the
17*7f2fe78bSCy Schubert * distribution.
18*7f2fe78bSCy Schubert *
19*7f2fe78bSCy Schubert * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*7f2fe78bSCy Schubert * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*7f2fe78bSCy Schubert * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*7f2fe78bSCy Schubert * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23*7f2fe78bSCy Schubert * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24*7f2fe78bSCy Schubert * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*7f2fe78bSCy Schubert * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26*7f2fe78bSCy Schubert * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*7f2fe78bSCy Schubert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28*7f2fe78bSCy Schubert * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*7f2fe78bSCy Schubert * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30*7f2fe78bSCy Schubert * OF THE POSSIBILITY OF SUCH DAMAGE.
31*7f2fe78bSCy Schubert */
32*7f2fe78bSCy Schubert
33*7f2fe78bSCy Schubert #include "k5-int.h"
34*7f2fe78bSCy Schubert #include "k5-platform.h" /* for vasprintf */
35*7f2fe78bSCy Schubert #include <assert.h>
36*7f2fe78bSCy Schubert #include <stdarg.h>
37*7f2fe78bSCy Schubert #include <stdio.h>
38*7f2fe78bSCy Schubert #include <stdlib.h>
39*7f2fe78bSCy Schubert #include <string.h>
40*7f2fe78bSCy Schubert
41*7f2fe78bSCy Schubert #include "tdumputil.h"
42*7f2fe78bSCy Schubert
43*7f2fe78bSCy Schubert /*
44*7f2fe78bSCy Schubert * Structure describing flavor of a tabular output format.
45*7f2fe78bSCy Schubert *
46*7f2fe78bSCy Schubert * fieldsep is the field separator
47*7f2fe78bSCy Schubert *
48*7f2fe78bSCy Schubert * recordsep is the record/line separator
49*7f2fe78bSCy Schubert *
50*7f2fe78bSCy Schubert * quotechar begins and ends a quoted field. If an instance of quotechar
51*7f2fe78bSCy Schubert * occurs within a quoted field value, it is doubled.
52*7f2fe78bSCy Schubert *
53*7f2fe78bSCy Schubert * Values are only quoted if they contain fieldsep, recordsep, or quotechar.
54*7f2fe78bSCy Schubert */
55*7f2fe78bSCy Schubert struct flavor {
56*7f2fe78bSCy Schubert int fieldsep; /* field separator */
57*7f2fe78bSCy Schubert int recordsep; /* record separator */
58*7f2fe78bSCy Schubert int quotechar; /* quote character */
59*7f2fe78bSCy Schubert };
60*7f2fe78bSCy Schubert
61*7f2fe78bSCy Schubert struct rechandle {
62*7f2fe78bSCy Schubert FILE *fh;
63*7f2fe78bSCy Schubert const char *rectype;
64*7f2fe78bSCy Schubert int do_sep;
65*7f2fe78bSCy Schubert struct flavor flavor;
66*7f2fe78bSCy Schubert };
67*7f2fe78bSCy Schubert
68*7f2fe78bSCy Schubert static const struct flavor tabsep = {
69*7f2fe78bSCy Schubert '\t', /* fieldsep */
70*7f2fe78bSCy Schubert '\n', /* recordsep */
71*7f2fe78bSCy Schubert '\0' /* quotechar */
72*7f2fe78bSCy Schubert };
73*7f2fe78bSCy Schubert
74*7f2fe78bSCy Schubert static const struct flavor csv = {
75*7f2fe78bSCy Schubert ',', /* fieldsep */
76*7f2fe78bSCy Schubert '\n', /* recordsep */
77*7f2fe78bSCy Schubert '"' /* quotechar */
78*7f2fe78bSCy Schubert };
79*7f2fe78bSCy Schubert
80*7f2fe78bSCy Schubert /*
81*7f2fe78bSCy Schubert * Double any quote characters present in a quoted field.
82*7f2fe78bSCy Schubert */
83*7f2fe78bSCy Schubert static char *
qquote(struct flavor * fl,const char * s)84*7f2fe78bSCy Schubert qquote(struct flavor *fl, const char *s)
85*7f2fe78bSCy Schubert {
86*7f2fe78bSCy Schubert const char *sp;
87*7f2fe78bSCy Schubert struct k5buf buf;
88*7f2fe78bSCy Schubert
89*7f2fe78bSCy Schubert k5_buf_init_dynamic(&buf);
90*7f2fe78bSCy Schubert for (sp = s; *sp != '\0'; sp++) {
91*7f2fe78bSCy Schubert k5_buf_add_len(&buf, sp, 1);
92*7f2fe78bSCy Schubert if (*sp == fl->quotechar)
93*7f2fe78bSCy Schubert k5_buf_add_len(&buf, sp, 1);
94*7f2fe78bSCy Schubert }
95*7f2fe78bSCy Schubert return k5_buf_cstring(&buf);
96*7f2fe78bSCy Schubert }
97*7f2fe78bSCy Schubert
98*7f2fe78bSCy Schubert /*
99*7f2fe78bSCy Schubert * Write an optionally quoted field.
100*7f2fe78bSCy Schubert */
101*7f2fe78bSCy Schubert static int
writequoted(struct rechandle * h,const char * fmt,va_list ap)102*7f2fe78bSCy Schubert writequoted(struct rechandle *h, const char *fmt, va_list ap)
103*7f2fe78bSCy Schubert {
104*7f2fe78bSCy Schubert int doquote = 0, ret;
105*7f2fe78bSCy Schubert char *s = NULL, *qs = NULL;
106*7f2fe78bSCy Schubert struct flavor fl = h->flavor;
107*7f2fe78bSCy Schubert
108*7f2fe78bSCy Schubert assert(fl.quotechar != '\0');
109*7f2fe78bSCy Schubert ret = vasprintf(&s, fmt, ap);
110*7f2fe78bSCy Schubert if (ret < 0)
111*7f2fe78bSCy Schubert return ret;
112*7f2fe78bSCy Schubert if (strchr(s, fl.fieldsep) != NULL)
113*7f2fe78bSCy Schubert doquote = 1;
114*7f2fe78bSCy Schubert if (strchr(s, fl.recordsep) != NULL)
115*7f2fe78bSCy Schubert doquote = 1;
116*7f2fe78bSCy Schubert if (strchr(s, fl.quotechar) != NULL)
117*7f2fe78bSCy Schubert doquote = 1;
118*7f2fe78bSCy Schubert
119*7f2fe78bSCy Schubert if (doquote) {
120*7f2fe78bSCy Schubert qs = qquote(&fl, s);
121*7f2fe78bSCy Schubert if (qs == NULL) {
122*7f2fe78bSCy Schubert ret = -1;
123*7f2fe78bSCy Schubert goto cleanup;
124*7f2fe78bSCy Schubert }
125*7f2fe78bSCy Schubert ret = fprintf(h->fh, "%c%s%c", fl.quotechar, qs, fl.quotechar);
126*7f2fe78bSCy Schubert } else {
127*7f2fe78bSCy Schubert ret = fprintf(h->fh, "%s", s);
128*7f2fe78bSCy Schubert }
129*7f2fe78bSCy Schubert cleanup:
130*7f2fe78bSCy Schubert free(s);
131*7f2fe78bSCy Schubert free(qs);
132*7f2fe78bSCy Schubert return ret;
133*7f2fe78bSCy Schubert }
134*7f2fe78bSCy Schubert
135*7f2fe78bSCy Schubert /*
136*7f2fe78bSCy Schubert * Return a rechandle with the requested file handle and rectype.
137*7f2fe78bSCy Schubert *
138*7f2fe78bSCy Schubert * rectype must be a valid pointer for the entire lifetime of the rechandle (or
139*7f2fe78bSCy Schubert * null)
140*7f2fe78bSCy Schubert */
141*7f2fe78bSCy Schubert static struct rechandle *
rechandle_common(FILE * fh,const char * rectype)142*7f2fe78bSCy Schubert rechandle_common(FILE *fh, const char *rectype)
143*7f2fe78bSCy Schubert {
144*7f2fe78bSCy Schubert struct rechandle *h = calloc(1, sizeof(*h));
145*7f2fe78bSCy Schubert
146*7f2fe78bSCy Schubert if (h == NULL)
147*7f2fe78bSCy Schubert return NULL;
148*7f2fe78bSCy Schubert h->fh = fh;
149*7f2fe78bSCy Schubert h->rectype = rectype;
150*7f2fe78bSCy Schubert h->do_sep = 0;
151*7f2fe78bSCy Schubert return h;
152*7f2fe78bSCy Schubert }
153*7f2fe78bSCy Schubert
154*7f2fe78bSCy Schubert /*
155*7f2fe78bSCy Schubert * Return a rechandle for tab-separated output.
156*7f2fe78bSCy Schubert */
157*7f2fe78bSCy Schubert struct rechandle *
rechandle_tabsep(FILE * fh,const char * rectype)158*7f2fe78bSCy Schubert rechandle_tabsep(FILE *fh, const char *rectype)
159*7f2fe78bSCy Schubert {
160*7f2fe78bSCy Schubert struct rechandle *h = rechandle_common(fh, rectype);
161*7f2fe78bSCy Schubert
162*7f2fe78bSCy Schubert if (h == NULL)
163*7f2fe78bSCy Schubert return NULL;
164*7f2fe78bSCy Schubert h->flavor = tabsep;
165*7f2fe78bSCy Schubert return h;
166*7f2fe78bSCy Schubert }
167*7f2fe78bSCy Schubert
168*7f2fe78bSCy Schubert /*
169*7f2fe78bSCy Schubert * Return a rechandle for CSV output.
170*7f2fe78bSCy Schubert */
171*7f2fe78bSCy Schubert struct rechandle *
rechandle_csv(FILE * fh,const char * rectype)172*7f2fe78bSCy Schubert rechandle_csv(FILE *fh, const char *rectype)
173*7f2fe78bSCy Schubert {
174*7f2fe78bSCy Schubert struct rechandle *h = rechandle_common(fh, rectype);
175*7f2fe78bSCy Schubert
176*7f2fe78bSCy Schubert if (h == NULL)
177*7f2fe78bSCy Schubert return NULL;
178*7f2fe78bSCy Schubert h->flavor = csv;
179*7f2fe78bSCy Schubert return h;
180*7f2fe78bSCy Schubert }
181*7f2fe78bSCy Schubert
182*7f2fe78bSCy Schubert /*
183*7f2fe78bSCy Schubert * Free a rechandle.
184*7f2fe78bSCy Schubert */
185*7f2fe78bSCy Schubert void
rechandle_free(struct rechandle * h)186*7f2fe78bSCy Schubert rechandle_free(struct rechandle *h)
187*7f2fe78bSCy Schubert {
188*7f2fe78bSCy Schubert free(h);
189*7f2fe78bSCy Schubert }
190*7f2fe78bSCy Schubert
191*7f2fe78bSCy Schubert /*
192*7f2fe78bSCy Schubert * Start a record. This includes writing a record type prefix (rectype) if
193*7f2fe78bSCy Schubert * specified.
194*7f2fe78bSCy Schubert */
195*7f2fe78bSCy Schubert int
startrec(struct rechandle * h)196*7f2fe78bSCy Schubert startrec(struct rechandle *h)
197*7f2fe78bSCy Schubert {
198*7f2fe78bSCy Schubert if (h->rectype == NULL) {
199*7f2fe78bSCy Schubert h->do_sep = 0;
200*7f2fe78bSCy Schubert return 0;
201*7f2fe78bSCy Schubert }
202*7f2fe78bSCy Schubert h->do_sep = 1;
203*7f2fe78bSCy Schubert return fputs(h->rectype, h->fh);
204*7f2fe78bSCy Schubert }
205*7f2fe78bSCy Schubert
206*7f2fe78bSCy Schubert /*
207*7f2fe78bSCy Schubert * Write a single field of a record. This includes writing a separator
208*7f2fe78bSCy Schubert * character, if appropriate.
209*7f2fe78bSCy Schubert */
210*7f2fe78bSCy Schubert int
writefield(struct rechandle * h,const char * fmt,...)211*7f2fe78bSCy Schubert writefield(struct rechandle *h, const char *fmt, ...)
212*7f2fe78bSCy Schubert {
213*7f2fe78bSCy Schubert int ret = 0;
214*7f2fe78bSCy Schubert va_list ap;
215*7f2fe78bSCy Schubert struct flavor fl = h->flavor;
216*7f2fe78bSCy Schubert
217*7f2fe78bSCy Schubert if (h->do_sep) {
218*7f2fe78bSCy Schubert ret = fputc(fl.fieldsep, h->fh);
219*7f2fe78bSCy Schubert if (ret < 0)
220*7f2fe78bSCy Schubert return ret;
221*7f2fe78bSCy Schubert }
222*7f2fe78bSCy Schubert h->do_sep = 1;
223*7f2fe78bSCy Schubert va_start(ap, fmt);
224*7f2fe78bSCy Schubert if (fl.quotechar == '\0')
225*7f2fe78bSCy Schubert ret = vfprintf(h->fh, fmt, ap);
226*7f2fe78bSCy Schubert else
227*7f2fe78bSCy Schubert ret = writequoted(h, fmt, ap);
228*7f2fe78bSCy Schubert va_end(ap);
229*7f2fe78bSCy Schubert return ret;
230*7f2fe78bSCy Schubert }
231*7f2fe78bSCy Schubert
232*7f2fe78bSCy Schubert /*
233*7f2fe78bSCy Schubert * Finish a record (line).
234*7f2fe78bSCy Schubert */
235*7f2fe78bSCy Schubert int
endrec(struct rechandle * h)236*7f2fe78bSCy Schubert endrec(struct rechandle *h)
237*7f2fe78bSCy Schubert {
238*7f2fe78bSCy Schubert int ret = 0;
239*7f2fe78bSCy Schubert struct flavor fl = h->flavor;
240*7f2fe78bSCy Schubert
241*7f2fe78bSCy Schubert ret = fputc(fl.recordsep, h->fh);
242*7f2fe78bSCy Schubert h->do_sep = 0;
243*7f2fe78bSCy Schubert return ret;
244*7f2fe78bSCy Schubert }
245*7f2fe78bSCy Schubert
246*7f2fe78bSCy Schubert /*
247*7f2fe78bSCy Schubert * Write a header line if h->rectype is null. (If rectype is set, it will be
248*7f2fe78bSCy Schubert * prefixed to output lines, most likely in a mixed record type output file, so
249*7f2fe78bSCy Schubert * it doesn't make sense to output a header line in that case.)
250*7f2fe78bSCy Schubert */
251*7f2fe78bSCy Schubert int
writeheader(struct rechandle * h,char * const * a)252*7f2fe78bSCy Schubert writeheader(struct rechandle *h, char * const *a)
253*7f2fe78bSCy Schubert {
254*7f2fe78bSCy Schubert int ret = 0;
255*7f2fe78bSCy Schubert char * const *p;
256*7f2fe78bSCy Schubert
257*7f2fe78bSCy Schubert if (h->rectype != NULL)
258*7f2fe78bSCy Schubert return 0;
259*7f2fe78bSCy Schubert for (p = a; *p != NULL; p++) {
260*7f2fe78bSCy Schubert ret = writefield(h, "%s", *p);
261*7f2fe78bSCy Schubert if (ret < 0)
262*7f2fe78bSCy Schubert return ret;
263*7f2fe78bSCy Schubert }
264*7f2fe78bSCy Schubert ret = endrec(h);
265*7f2fe78bSCy Schubert return ret;
266*7f2fe78bSCy Schubert }
267