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