/src/gdbm/tools/datconv.c
Line | Count | Source |
1 | | /* This file is part of GDBM, the GNU data base manager. |
2 | | Copyright (C) 1990-2025 Free Software Foundation, Inc. |
3 | | |
4 | | GDBM is free software; you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation; either version 3, or (at your option) |
7 | | any later version. |
8 | | |
9 | | GDBM is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with GDBM. If not, see <http://www.gnu.org/licenses/>. */ |
16 | | |
17 | | #include "gdbmtool.h" |
18 | | #include <wctype.h> |
19 | | |
20 | | #define DEFFMT(name, type, fmt) \ |
21 | | static int \ |
22 | 0 | name (PAGERFILE *fp, void *ptr, int size) \ |
23 | 0 | { \ |
24 | 0 | pager_printf (fp, fmt, *(type*) ptr); \ |
25 | 0 | return size; \ |
26 | 0 | } Unexecuted instantiation: datconv.c:f_char Unexecuted instantiation: datconv.c:f_short Unexecuted instantiation: datconv.c:f_ushort Unexecuted instantiation: datconv.c:f_int Unexecuted instantiation: datconv.c:f_uint Unexecuted instantiation: datconv.c:f_long Unexecuted instantiation: datconv.c:f_ulong Unexecuted instantiation: datconv.c:f_llong Unexecuted instantiation: datconv.c:f_ullong Unexecuted instantiation: datconv.c:f_float Unexecuted instantiation: datconv.c:f_double |
27 | | |
28 | | DEFFMT (f_char, char, "%c") |
29 | | DEFFMT (f_short, short, "%hd") |
30 | | DEFFMT (f_ushort, unsigned short, "%hu") |
31 | | DEFFMT (f_int, int, "%d") |
32 | | DEFFMT (f_uint, unsigned, "%u") |
33 | | DEFFMT (f_long, long, "%ld") |
34 | | DEFFMT (f_ulong, unsigned long, "%lu") |
35 | | DEFFMT (f_llong, long long, "%lld") |
36 | | DEFFMT (f_ullong, unsigned long long, "%llu") |
37 | | DEFFMT (f_float, float, "%f") |
38 | | DEFFMT (f_double, double, "%e") |
39 | | |
40 | | static int |
41 | | f_stringz (PAGERFILE *fp, void *ptr, int size) |
42 | 0 | { |
43 | 0 | wchar_t wc; |
44 | 0 | char *str = ptr; |
45 | 0 | int i; |
46 | |
|
47 | 0 | mbtowc (NULL, NULL, 0); |
48 | 0 | for (i = 0; i < size; ) |
49 | 0 | { |
50 | 0 | int n = mbtowc (&wc, &str[i], MB_CUR_MAX); |
51 | 0 | if (n == 0) |
52 | 0 | break; |
53 | 0 | if (n == -1 || !iswprint (wc)) |
54 | 0 | { |
55 | 0 | int c; |
56 | 0 | if ((c = escape (str[i]))) |
57 | 0 | pager_printf (fp, "\\%c", c); |
58 | 0 | else |
59 | 0 | pager_printf (fp, "\\%03o", *(unsigned char*)(str+i)); |
60 | 0 | i++; |
61 | 0 | } |
62 | 0 | else |
63 | 0 | { |
64 | 0 | pager_write (fp, str + i, n); |
65 | 0 | i += n; |
66 | 0 | } |
67 | 0 | } |
68 | 0 | return i + 1; |
69 | 0 | } |
70 | | |
71 | | static int |
72 | | f_string (PAGERFILE *fp, void *ptr, int size) |
73 | 392 | { |
74 | 392 | wchar_t wc; |
75 | 392 | char *str = ptr; |
76 | 392 | int i; |
77 | | |
78 | 392 | mbtowc (NULL, NULL, 0); |
79 | 1.29M | for (i = 0; i < size; ) |
80 | 1.29M | { |
81 | 1.29M | int n = mbtowc (&wc, &str[i], MB_CUR_MAX); |
82 | 1.29M | if (n == 0) |
83 | 1.00M | { |
84 | 1.00M | pager_printf (fp, "\\%03o", *(unsigned char*)(str+i)); |
85 | 1.00M | i++; |
86 | 1.00M | } |
87 | 282k | else if (n == -1 || !iswprint (wc)) |
88 | 198k | { |
89 | 198k | int c; |
90 | 198k | if ((c = escape (str[i]))) |
91 | 15.3k | pager_printf (fp, "\\%c", c); |
92 | 183k | else |
93 | 183k | pager_printf (fp, "\\%03o", *(unsigned char*)(str+i)); |
94 | 198k | i++; |
95 | 198k | } |
96 | 83.2k | else |
97 | 83.2k | { |
98 | 83.2k | pager_write (fp, str + i, n); |
99 | 83.2k | i += n; |
100 | 83.2k | } |
101 | 1.29M | } |
102 | 392 | return i; |
103 | 392 | } |
104 | | |
105 | | int |
106 | | s_char (struct xdatum *xd, char *str) |
107 | 0 | { |
108 | 0 | xd_store (xd, str, 1); |
109 | 0 | return 0; |
110 | 0 | } |
111 | | |
112 | | #define DEFNSCAN(name, type, temptype, strto) \ |
113 | | int \ |
114 | 0 | name (struct xdatum *xd, char *str) \ |
115 | 0 | { \ |
116 | 0 | temptype n; \ |
117 | 0 | type t; \ |
118 | 0 | char *p; \ |
119 | 0 | \ |
120 | 0 | errno = 0; \ |
121 | 0 | n = strto (str, &p, 0); \ |
122 | 0 | if (*p) \ |
123 | 0 | return 1; \ |
124 | 0 | if (errno == ERANGE || (t = n) != n) \ |
125 | 0 | return 1; \ |
126 | 0 | xd_store (xd, &t, sizeof (t)); \ |
127 | 0 | return 0; \ |
128 | 0 | } |
129 | | |
130 | 0 | DEFNSCAN(s_short, short, long, strtol); |
131 | 0 | DEFNSCAN(s_ushort, unsigned short, unsigned long, strtoul); |
132 | 0 | DEFNSCAN(s_int, int, long, strtol) |
133 | 0 | DEFNSCAN(s_uint, unsigned, unsigned long, strtol) |
134 | 0 | DEFNSCAN(s_long, long, long, strtoul) |
135 | 0 | DEFNSCAN(s_ulong, unsigned long, unsigned long, strtoul) |
136 | 0 | DEFNSCAN(s_llong, long long, long long, strtoll) |
137 | 0 | DEFNSCAN(s_ullong, unsigned long long, unsigned long long, strtoull) |
138 | | |
139 | | int |
140 | | s_double (struct xdatum *xd, char *str) |
141 | 0 | { |
142 | 0 | double d; |
143 | 0 | char *p; |
144 | | |
145 | 0 | errno = 0; |
146 | 0 | d = strtod (str, &p); |
147 | 0 | if (errno || *p) |
148 | 0 | return 1; |
149 | 0 | xd_store (xd, &d, sizeof (d)); |
150 | 0 | return 0; |
151 | 0 | } |
152 | | |
153 | | int |
154 | | s_float (struct xdatum *xd, char *str) |
155 | 0 | { |
156 | 0 | float d; |
157 | 0 | char *p; |
158 | | |
159 | 0 | errno = 0; |
160 | 0 | d = strtod (str, &p); |
161 | 0 | if (errno || *p) |
162 | 0 | return 1; |
163 | 0 | xd_store (xd, &d, sizeof (d)); |
164 | 0 | return 0; |
165 | 0 | } |
166 | | |
167 | | int |
168 | | s_stringz (struct xdatum *xd, char *str) |
169 | 0 | { |
170 | 0 | xd_store (xd, str, strlen (str) + 1); |
171 | 0 | return 0; |
172 | 0 | } |
173 | | |
174 | | int |
175 | | s_string (struct xdatum *xd, char *str) |
176 | 9.48k | { |
177 | 9.48k | xd_store (xd, str, strlen (str)); |
178 | 9.48k | return 0; |
179 | 9.48k | } |
180 | | |
181 | | static struct datadef datatab[] = { |
182 | | { "char", sizeof(char), f_char, s_char }, |
183 | | { "short", sizeof(short), f_short, s_short }, |
184 | | { "ushort", sizeof(unsigned short), f_ushort, s_ushort }, |
185 | | { "int", sizeof(int), f_int, s_int }, |
186 | | { "unsigned", sizeof(unsigned), f_uint, s_uint }, |
187 | | { "uint", sizeof(unsigned), f_uint, s_uint }, |
188 | | { "long", sizeof(long), f_long, s_long }, |
189 | | { "ulong", sizeof(unsigned long), f_ulong, s_ulong }, |
190 | | { "llong", sizeof(long long), f_llong, s_llong }, |
191 | | { "ullong", sizeof(unsigned long long), f_ullong, s_ullong }, |
192 | | { "float", sizeof(float), f_float, s_float }, |
193 | | { "double", sizeof(double), f_double, s_double }, |
194 | | { "stringz", 0, f_stringz, s_stringz }, |
195 | | { "string", 0, f_string, s_string }, |
196 | | { NULL } |
197 | | }; |
198 | | |
199 | | struct datadef * |
200 | | datadef_lookup (const char *name) |
201 | 5.13k | { |
202 | 5.13k | struct datadef *p; |
203 | | |
204 | 71.8k | for (p = datatab; p->name; p++) |
205 | 71.8k | if (strcmp (p->name, name) == 0) |
206 | 5.13k | return p; |
207 | 0 | return NULL; |
208 | 5.13k | } |
209 | | |
210 | | struct dsegm * |
211 | | dsegm_new (int type) |
212 | 5.13k | { |
213 | 5.13k | struct dsegm *p = emalloc (sizeof (*p)); |
214 | 5.13k | p->next = NULL; |
215 | 5.13k | p->type = type; |
216 | 5.13k | return p; |
217 | 5.13k | } |
218 | | |
219 | | struct dsegm * |
220 | | dsegm_new_field (struct datadef *type, char *id, int dim) |
221 | 5.13k | { |
222 | 5.13k | struct dsegm *p = dsegm_new (FDEF_FLD); |
223 | 5.13k | p->v.field.type = type; |
224 | 5.13k | p->v.field.name = id; |
225 | 5.13k | p->v.field.dim = dim; |
226 | 5.13k | return p; |
227 | 5.13k | } |
228 | | |
229 | | void |
230 | | dsegm_list_free (struct dsegm *dp) |
231 | 5.13k | { |
232 | 10.2k | while (dp) |
233 | 5.13k | { |
234 | 5.13k | struct dsegm *next = dp->next; |
235 | 5.13k | if (dp->type == FDEF_FLD) |
236 | 5.13k | free (dp->v.field.name); |
237 | 5.13k | free (dp); |
238 | 5.13k | dp = next; |
239 | 5.13k | } |
240 | 5.13k | } |
241 | | |
242 | | struct dsegm * |
243 | | dsegm_list_find (struct dsegm *dp, char const *name) |
244 | 0 | { |
245 | 0 | for (; dp; dp = dp->next) |
246 | 0 | if (dp->type == FDEF_FLD && dp->v.field.name && |
247 | 0 | strcmp (dp->v.field.name, name) == 0) |
248 | 0 | break; |
249 | 0 | return dp; |
250 | 0 | } |
251 | | |
252 | | void |
253 | | datum_format (PAGERFILE *fp, datum const *dat, struct dsegm *ds) |
254 | 392 | { |
255 | 392 | int off = 0; |
256 | 392 | char *delim[2]; |
257 | 392 | int first_field = 1; |
258 | | |
259 | 392 | if (!ds) |
260 | 0 | { |
261 | 0 | pager_printf (fp, "%.*s\n", dat->dsize, dat->dptr); |
262 | 0 | return; |
263 | 0 | } |
264 | | |
265 | 392 | if (variable_get ("delim1", VART_STRING, (void*) &delim[0])) |
266 | 0 | abort (); |
267 | 392 | if (variable_get ("delim2", VART_STRING, (void*) &delim[1])) |
268 | 0 | abort (); |
269 | | |
270 | 784 | for (; ds && off <= dat->dsize; ds = ds->next) |
271 | 392 | { |
272 | 392 | switch (ds->type) |
273 | 392 | { |
274 | 392 | case FDEF_FLD: |
275 | 392 | if (!first_field) |
276 | 0 | pager_writez (fp, delim[1]); |
277 | 392 | if (ds->v.field.name) |
278 | 0 | pager_printf (fp, "%s=", ds->v.field.name); |
279 | 392 | if (ds->v.field.dim > 1) |
280 | 0 | pager_printf (fp, "{ "); |
281 | 392 | if (ds->v.field.type->format) |
282 | 392 | { |
283 | 392 | int i, n; |
284 | | |
285 | 784 | for (i = 0; i < ds->v.field.dim; i++) |
286 | 392 | { |
287 | 392 | if (i) |
288 | 0 | pager_write (fp, delim[0], strlen (delim[0])); |
289 | 392 | if (off + ds->v.field.type->size > dat->dsize) |
290 | 0 | { |
291 | 0 | pager_printf (fp, _("(not enough data)")); |
292 | 0 | off += dat->dsize; |
293 | 0 | break; |
294 | 0 | } |
295 | 392 | else |
296 | 392 | { |
297 | 392 | n = ds->v.field.type->format (fp, |
298 | 392 | (char*) dat->dptr + off, |
299 | 392 | ds->v.field.type->size ? |
300 | 0 | ds->v.field.type->size : |
301 | 392 | dat->dsize - off); |
302 | 392 | off += n; |
303 | 392 | } |
304 | 392 | } |
305 | 392 | } |
306 | 392 | if (ds->v.field.dim > 1) |
307 | 0 | pager_printf (fp, " }"); |
308 | 392 | first_field = 0; |
309 | 392 | break; |
310 | | |
311 | 0 | case FDEF_OFF: |
312 | 0 | off = ds->v.n; |
313 | 0 | break; |
314 | | |
315 | 0 | case FDEF_PAD: |
316 | 0 | off += ds->v.n; |
317 | 0 | break; |
318 | 392 | } |
319 | 392 | } |
320 | 392 | } |
321 | | |
322 | | void |
323 | | datum_format_file (FILE *fp, datum const *dat, struct dsegm *ds) |
324 | 0 | { |
325 | 0 | PAGERFILE *pager = pager_open (fp, 0, NULL); |
326 | 0 | datum_format (pager, dat, ds); |
327 | 0 | pager_close (pager); |
328 | 0 | } |
329 | | |
330 | | struct xdatum |
331 | | { |
332 | | char *dptr; |
333 | | size_t dsize; |
334 | | size_t dmax; |
335 | | int off; |
336 | | }; |
337 | | |
338 | | void |
339 | | xd_expand (struct xdatum *xd, size_t size) |
340 | 9.48k | { |
341 | 9.48k | if (xd->dmax < size || 1) |
342 | 9.48k | { |
343 | 9.48k | xd->dptr = erealloc (xd->dptr, size); |
344 | 9.48k | memset (xd->dptr + xd->dmax, 0, size - xd->dmax); |
345 | 9.48k | xd->dmax = size; |
346 | 9.48k | } |
347 | 9.48k | } |
348 | | |
349 | | void |
350 | | xd_store (struct xdatum *xd, void *val, size_t size) |
351 | 9.48k | { |
352 | 9.48k | xd_expand (xd, xd->off + size); |
353 | 9.48k | memcpy (xd->dptr + xd->off, val, size); |
354 | 9.48k | xd->off += size; |
355 | 9.48k | if (xd->off > xd->dsize) |
356 | 9.48k | xd->dsize = xd->off; |
357 | 9.48k | } |
358 | | |
359 | | static int |
360 | | dsconv (struct xdatum *xd, struct dsegm *ds, struct kvpair *kv) |
361 | 9.48k | { |
362 | 9.48k | int i; |
363 | 9.48k | int err = 0; |
364 | 9.48k | struct slist *s; |
365 | | |
366 | 9.48k | if (!ds->v.field.type->scan) |
367 | 0 | abort (); |
368 | | |
369 | 9.48k | if (kv->type == KV_STRING && ds->v.field.dim > 1) |
370 | 0 | { |
371 | | /* If a char[] value was supplied as a quoted string. |
372 | | convert it to list for further processing */ |
373 | 0 | if (ds->v.field.type->size == 1) |
374 | 0 | { |
375 | 0 | struct slist *head = slist_new_l (kv->val.s, 1); |
376 | 0 | struct slist *tail = head; |
377 | 0 | char *p; |
378 | 0 | for (p = kv->val.s + 1; *p; p++) |
379 | 0 | slist_insert (&tail, slist_new_l (p, 1)); |
380 | 0 | free (kv->val.s); |
381 | 0 | kv->val.l = head; |
382 | 0 | kv->type = KV_LIST; |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | 9.48k | switch (kv->type) |
387 | 9.48k | { |
388 | 9.48k | case KV_STRING: |
389 | 9.48k | err = ds->v.field.type->scan (xd, kv->val.s); |
390 | 9.48k | if (err) |
391 | 0 | lerror (&kv->loc, _("cannot convert")); |
392 | 9.48k | break; |
393 | | |
394 | 0 | case KV_LIST: |
395 | 0 | for (i = 0, s = kv->val.l; i < ds->v.field.dim && s; i++, s = s->next) |
396 | 0 | { |
397 | 0 | err = ds->v.field.type->scan (xd, s->str); |
398 | 0 | if (err) |
399 | 0 | { |
400 | 0 | lerror (&kv->loc, _("cannot convert value #%d: %s"), i, s->str); |
401 | 0 | break; |
402 | 0 | } |
403 | 0 | } |
404 | 0 | if (s) |
405 | 0 | { |
406 | 0 | lerror (&kv->loc, "surplus initializers ignored"); |
407 | 0 | err = 1; |
408 | 0 | } |
409 | 9.48k | } |
410 | 9.48k | return err; |
411 | 9.48k | } |
412 | | |
413 | | static int |
414 | | datum_scan_notag (datum *dat, struct dsegm *ds, struct kvpair *kv) |
415 | 9.48k | { |
416 | 9.48k | struct xdatum xd; |
417 | 9.48k | int err = 0; |
418 | | |
419 | 9.48k | memset (&xd, 0, sizeof (xd)); |
420 | | |
421 | 18.9k | for (; err == 0 && ds && kv; ds = ds->next) |
422 | 9.48k | { |
423 | 9.48k | if (kv->key) |
424 | 0 | { |
425 | 0 | lerror (&kv->loc, |
426 | 0 | _("mixing tagged and untagged values is not allowed")); |
427 | 0 | err = 1; |
428 | 0 | break; |
429 | 0 | } |
430 | | |
431 | 9.48k | switch (ds->type) |
432 | 9.48k | { |
433 | 9.48k | case FDEF_FLD: |
434 | 9.48k | err = dsconv (&xd, ds, kv); |
435 | 9.48k | kv = kv->next; |
436 | 9.48k | break; |
437 | | |
438 | 0 | case FDEF_OFF: |
439 | 0 | xd_expand (&xd, ds->v.n); |
440 | 0 | xd.off = ds->v.n; |
441 | 0 | break; |
442 | | |
443 | 0 | case FDEF_PAD: |
444 | 0 | xd_expand (&xd, xd.off + ds->v.n); |
445 | 0 | xd.off += ds->v.n; |
446 | 0 | break; |
447 | 9.48k | } |
448 | 9.48k | } |
449 | | |
450 | 9.48k | if (err) |
451 | 0 | { |
452 | 0 | free (xd.dptr); |
453 | 0 | return 1; |
454 | 0 | } |
455 | | |
456 | 9.48k | dat->dptr = xd.dptr; |
457 | 9.48k | dat->dsize = xd.dsize; |
458 | | |
459 | 9.48k | return 0; |
460 | 9.48k | } |
461 | | |
462 | | static int |
463 | | datum_scan_tag (datum *dat, struct dsegm *ds, struct kvpair *kvlist) |
464 | 0 | { |
465 | 0 | struct xdatum xd; |
466 | 0 | int err = 0; |
467 | 0 | struct kvpair *kv; |
468 | | |
469 | | /* Check keywords for consistency */ |
470 | 0 | for (kv = kvlist; kv; kv = kv->next) |
471 | 0 | { |
472 | 0 | if (!kv->key) |
473 | 0 | { |
474 | 0 | lerror (&kv->loc, |
475 | 0 | _("mixing tagged and untagged values is not allowed")); |
476 | 0 | return 1; |
477 | 0 | } |
478 | 0 | if (!dsegm_list_find (ds, kv->key)) |
479 | 0 | { |
480 | 0 | lerror (&kv->loc, _("%s: no such field in datum"), kv->key); |
481 | 0 | return 1; |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | | /* Initialize datum */ |
486 | 0 | memset (&xd, 0, sizeof (xd)); |
487 | |
|
488 | 0 | for (; err == 0 && ds; ds = ds->next) |
489 | 0 | { |
490 | 0 | switch (ds->type) |
491 | 0 | { |
492 | 0 | case FDEF_FLD: |
493 | 0 | kv = kvlist_find (kvlist, ds->v.field.name); |
494 | 0 | if (kv) |
495 | 0 | err = dsconv (&xd, ds, kv); |
496 | 0 | else |
497 | 0 | { |
498 | 0 | size_t sz = ds->v.field.type->size * ds->v.field.dim; |
499 | 0 | xd_expand (&xd, xd.off + sz); |
500 | 0 | xd.off += sz; |
501 | 0 | } |
502 | 0 | break; |
503 | | |
504 | 0 | case FDEF_OFF: |
505 | 0 | xd_expand (&xd, ds->v.n); |
506 | 0 | xd.off = ds->v.n; |
507 | 0 | break; |
508 | | |
509 | 0 | case FDEF_PAD: |
510 | 0 | xd_expand (&xd, xd.off + ds->v.n); |
511 | 0 | xd.off += ds->v.n; |
512 | 0 | break; |
513 | 0 | } |
514 | 0 | } |
515 | | |
516 | 0 | if (err) |
517 | 0 | { |
518 | 0 | free (xd.dptr); |
519 | 0 | return 1; |
520 | 0 | } |
521 | | |
522 | 0 | dat->dptr = xd.dptr; |
523 | 0 | dat->dsize = xd.dsize; |
524 | | |
525 | 0 | return 0; |
526 | 0 | } |
527 | | |
528 | | int |
529 | | datum_scan (datum *dat, struct dsegm *ds, struct kvpair *kv) |
530 | 9.48k | { |
531 | 9.48k | return (kv->key ? datum_scan_tag : datum_scan_notag) (dat, ds, kv); |
532 | 9.48k | } |
533 | | |
534 | | void |
535 | | dsprint (PAGERFILE *fp, int what, struct dsegm *ds) |
536 | 3.79k | { |
537 | 3.79k | static char *dsstr[] = { "key", "content" }; |
538 | 3.79k | int delim; |
539 | | |
540 | 3.79k | pager_printf (fp, "define %s", dsstr[what]); |
541 | 3.79k | if (ds->next) |
542 | 0 | { |
543 | 0 | pager_printf (fp, " {\n"); |
544 | 0 | delim = '\t'; |
545 | 0 | } |
546 | 3.79k | else |
547 | 3.79k | delim = ' '; |
548 | 7.58k | for (; ds; ds = ds->next) |
549 | 3.79k | { |
550 | 3.79k | switch (ds->type) |
551 | 3.79k | { |
552 | 3.79k | case FDEF_FLD: |
553 | 3.79k | pager_printf (fp, "%c%s", delim, ds->v.field.type->name); |
554 | 3.79k | if (ds->v.field.name) |
555 | 0 | pager_printf (fp, " %s", ds->v.field.name); |
556 | 3.79k | if (ds->v.field.dim > 1) |
557 | 0 | pager_printf (fp, "[%d]", ds->v.field.dim); |
558 | 3.79k | break; |
559 | | |
560 | 0 | case FDEF_OFF: |
561 | 0 | pager_printf (fp, "%coffset %d", delim, ds->v.n); |
562 | 0 | break; |
563 | | |
564 | 0 | case FDEF_PAD: |
565 | 0 | pager_printf (fp, "%cpad %d", delim, ds->v.n); |
566 | 0 | break; |
567 | 3.79k | } |
568 | 3.79k | if (ds->next) |
569 | 0 | pager_putc (fp, ','); |
570 | 3.79k | pager_putc (fp, '\n'); |
571 | 3.79k | } |
572 | 3.79k | if (delim == '\t') |
573 | 0 | pager_writeln (fp, "}"); |
574 | 3.79k | } |