1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 2002-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * text method
23 *
24 * Glenn Fowler
25 * AT&T Research
26 */
27
28 static const char usage[] =
29 "[+DESCRIPTION?The \bdss\b text method describes newline-terminated"
30 " field-delimited text file data. The method schema is a \bscanf\b(3)"
31 " style format string with embedded field names of the form"
32 " \b%(\b\afield\a\b)\b\aformat-char\a \adelimiter\a ...]"
33 "[+?Use the \bdss\b \bflat\b method for generic record-oriented flat "
34 "file data.]"
35 "[+EXAMPLES]{"
36 " [+dss -x text::::\b\"%(name)s::%(passwd::Encrypted\\ password.)s::%(uid)d::%(gid)d::%(comment)s::%(home)s::%(shell)s\"\\ 'passwd==\"\"&&uid==0'\\ "
37 " /etc/passwd?Prints \b/etc/passwd\b entries with uid==0 and no"
38 " password.]"
39 "}"
40 "\n"
41 "\n--method=text[,option...]\n"
42 "\n"
43 ;
44
45 #include <dsslib.h>
46 #include <tm.h>
47
48 typedef uint32_t Ipaddr_t;
49
50 struct Text_s; typedef struct Text_s Text_t;
51
52 struct Text_s /* Dssmeth_t.data */
53 {
54 char* format;
55 Dt_t* dict;
56 int vars;
57 char name[1];
58 };
59
60 static char null[1];
61
62 extern Dsslib_t dss_lib_text;
63
64 /*
65 * text identf
66 */
67
68 static int
textident(Dssfile_t * file,void * buf,size_t n,Dssdisc_t * disc)69 textident(Dssfile_t* file, void* buf, size_t n, Dssdisc_t* disc)
70 {
71 return 1;
72 }
73
74 /*
75 * text fopenf
76 */
77
78 static int
textfopen(Dssfile_t * file,Dssdisc_t * disc)79 textfopen(Dssfile_t* file, Dssdisc_t* disc)
80 {
81 if (!(file->data = vmnewof(file->dss->vm, 0, Cxvalue_t, ((Text_t*)file->dss->meth->data)->vars, 0)))
82 {
83 if (disc->errorf)
84 (*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
85 return -1;
86 }
87 return 0;
88 }
89
90 /*
91 * text fclosef
92 */
93
94 static int
textfclose(Dssfile_t * file,Dssdisc_t * disc)95 textfclose(Dssfile_t* file, Dssdisc_t* disc)
96 {
97 if (!file || !file->data)
98 return -1;
99 vmfree(file->dss->vm, file->data);
100 return 0;
101 }
102
103 /*
104 * get one string token into p
105 */
106
107 static char*
lextok(register char * s,register int c,Cxstring_t * p)108 lextok(register char* s, register int c, Cxstring_t* p)
109 {
110 register char* t;
111 register int q;
112 char* b;
113 char* u;
114
115 b = s;
116 q = 0;
117 t = 0;
118 for (;;)
119 {
120 if (!*s)
121 {
122 if (!q)
123 {
124 if (c && c != ' ')
125 {
126 s = b;
127 b = null;
128 break;
129 }
130 }
131 if (t)
132 *t = 0;
133 break;
134 }
135 else if (*s == '\\')
136 {
137 u = s;
138 if (!*++s)
139 continue;
140 if (b == u)
141 b = s;
142 else if (!t)
143 t = u;
144 }
145 else if (q)
146 {
147 if (*s == q)
148 {
149 q = 0;
150 if (!t)
151 t = s;
152 s++;
153 continue;
154 }
155 else if (*s == '\r')
156 *s = 0;
157 }
158 else if (*s == '"' || *s == '\'')
159 {
160 q = *s++;
161 if (b == (s - 1))
162 b = s;
163 else if (!t)
164 t = s - 1;
165 continue;
166 }
167 else if (*s == c || c == ' ' && *s == '\t')
168 {
169 *s++ = 0;
170 if (t)
171 *t = 0;
172 if (c == ' ')
173 while (*s == ' ' || *s == '\t')
174 s++;
175 break;
176 }
177 if (t)
178 *t++ = *s;
179 s++;
180 }
181 p->data = b;
182 p->size = *b ? (s - b) : 0;
183 return s;
184 }
185
186 /*
187 * text readf
188 */
189
190 static int
textread(register Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)191 textread(register Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
192 {
193 register Cxvalue_t* data = (Cxvalue_t*)file->data;
194 register Text_t* text = (Text_t*)file->dss->meth->data;
195 register char* s;
196 register char* f;
197 register int c;
198 char* t;
199 int num;
200 int q;
201 Ipaddr_t a;
202
203 if (!(s = sfgetr(file->io, '\n', 1)))
204 return 0;
205 num = 0;
206 f = text->format;
207 for (;;)
208 {
209 switch (c = *f++)
210 {
211 case 0:
212 break;
213 case ' ':
214 while (*s == ' ' || *s == '\t')
215 s++;
216 break;
217 case '%':
218 switch (c = *f++)
219 {
220 case 'h':
221 case 'l':
222 q = c;
223 c = *f++;
224 break;
225 default:
226 q = 0;
227 break;
228 }
229 switch (c)
230 {
231 case 0:
232 f--;
233 continue;
234 case '%':
235 if (*s++ != c)
236 s = null;
237 continue;
238 case 'c':
239 if (data[num].number = *s)
240 s++;
241 num++;
242 break;
243 case 'd':
244 c = 10;
245 goto number;
246 case 'i':
247 if (!*s)
248 data[num].number = 0;
249 else
250 {
251 strtoip4(s, &t, &a, NiL);
252 data[num].number = a;
253 s = t;
254 }
255 num++;
256 break;
257 case 'n':
258 case 'u':
259 c = 0;
260 goto number;
261 case 'o':
262 c = 8;
263 goto number;
264 case 'x':
265 c = 16;
266 number:
267 if (!*s)
268 data[num].number = 0;
269 else
270 {
271 data[num].number = strtol(s, &t, c);
272 s = t;
273 }
274 num++;
275 break;
276 case 'f':
277 case 'g':
278 if (!*s)
279 data[num].number = 0;
280 else
281 {
282 data[num].number = strtod(s, &t);
283 s = t;
284 }
285 num++;
286 break;
287 case 's':
288 if (q = *f)
289 f++;
290 if (!*s)
291 {
292 data[num].string.data = null;
293 data[num].string.size = 0;
294 }
295 else
296 s = lextok(s, q, &data[num].string);
297 num++;
298 break;
299 case 't':
300 if (!*s)
301 data[num].number = 0;
302 else
303 {
304 data[num].number = tmdate(s, &t, NiL);
305 if (*t && *t != *f && *t != '\n')
306 data[num].number = strtol(s, &t, 0);
307 s = t;
308 }
309 num++;
310 break;
311 }
312 continue;
313 case '\n':
314 break;
315 default:
316 if (*s++ != c)
317 s = null;
318 continue;
319 }
320 break;
321 }
322 record->data = data;
323 return 1;
324 }
325
326 /*
327 * text writef
328 */
329
330 static int
textwrite(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)331 textwrite(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
332 {
333 register Text_t* text = (Text_t*)file->dss->meth->data;
334 Cxvalue_t* data = (Cxvalue_t*)record->data;
335 register char* f;
336 register int c;
337 int num;
338
339 num = 0;
340 f = text->format;
341 for (;;)
342 {
343 switch (c = *f++)
344 {
345 case 0:
346 break;
347 case ' ':
348 sfputc(file->io, ' ');
349 break;
350 case '%':
351 switch (c = *f++)
352 {
353 case 'h':
354 case 'l':
355 c = *f++;
356 break;
357 }
358 switch (c)
359 {
360 case 0:
361 f--;
362 continue;
363 case '%':
364 sfputc(file->io, '%');
365 continue;
366 case 'c':
367 sfputc(file->io, (int)data[num].number);
368 num++;
369 break;
370 case 'd':
371 case 'n':
372 case 'u':
373 sfprintf(file->io, "%d", (long)data[num].number);
374 num++;
375 break;
376 case 'i':
377 sfprintf(file->io, "%s", fmtip4((Ipaddr_t)data[num].number, -1));
378 num++;
379 break;
380 case 'f':
381 case 'g':
382 sfprintf(file->io, "%Lg", data[num].number);
383 num++;
384 break;
385 case 'o':
386 sfprintf(file->io, "%o", (long)data[num].number);
387 num++;
388 break;
389 case 'x':
390 sfprintf(file->io, "%x", (long)data[num].number);
391 num++;
392 break;
393 case 's':
394 sfprintf(file->io, "%-.*s", data[num].string.size, data[num].string.data);
395 num++;
396 break;
397 case 't':
398 sfprintf(file->io, "%s", fmttime("%K", (time_t)data[num].number));
399 num++;
400 break;
401 }
402 continue;
403 case '\n':
404 break;
405 default:
406 sfputc(file->io, c);
407 continue;
408 }
409 break;
410 }
411 sfputc(file->io, '\n');
412 return 0;
413 }
414
415 static Dssformat_t text_format =
416 {
417 "text",
418 "text format (2010-05-28)",
419 CXH,
420 textident,
421 textfopen,
422 textread,
423 textwrite,
424 0,
425 textfclose,
426 0,
427 0,
428 0
429 };
430
431 static int
op_get(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)432 op_get(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
433 {
434 r->value = ((Cxvalue_t*)DSSDATA(data))[((Cxvariable_t*)pc->data.variable)->index];
435 return 0;
436 }
437
438 static Cxcallout_t local_callouts[] =
439 {
440 CXC(CX_GET, "void", "void", op_get, 0)
441 };
442
443 /*
444 * methf
445 */
446
447 static Dssmeth_t*
textmeth(const char * name,const char * options,const char * schema,Dssdisc_t * disc,Dssmeth_t * ometh)448 textmeth(const char* name, const char* options, const char* schema, Dssdisc_t* disc, Dssmeth_t* ometh)
449 {
450 register Text_t* text;
451 register Dssmeth_t* meth;
452 register Cxvariable_t* var;
453 register char* s;
454 register char* t;
455 register char* f;
456 register int c;
457 char* d;
458 int p;
459 int index;
460
461 if (options)
462 {
463 if (dssoptlib(ometh->cx->buf, &dss_lib_text, usage, disc))
464 goto drop;
465 s = sfstruse(ometh->cx->buf);
466 for (;;)
467 {
468 switch (optstr(options, s))
469 {
470 case '?':
471 if (disc->errorf)
472 (*disc->errorf)(NiL, disc, ERROR_USAGE|4, "%s", opt_info.arg);
473 goto drop;
474 case ':':
475 if (disc->errorf)
476 (*disc->errorf)(NiL, disc, 2, "%s", opt_info.arg);
477 goto drop;
478 }
479 break;
480 }
481 }
482 if (!schema || !*schema)
483 return ometh;
484 if (!(meth = newof(0, Dssmeth_t, 1, sizeof(Text_t) + strlen(name) + 2 * strlen(schema) + 2)))
485 {
486 free(meth);
487 if (disc->errorf)
488 (*disc->errorf)(NiL, disc, 2, "out of space");
489 return 0;
490 }
491 *meth = *ometh;
492 meth->data = text = (Text_t*)(meth + 1);
493 text->format = strcopy(text->name, name) + 1;
494 index = 0;
495 s = (char*)schema;
496 f = text->format;
497 for (;;)
498 {
499 switch (c = *s++)
500 {
501 case 0:
502 break;
503 case '%':
504 *f++ = '%';
505 var = 0;
506 switch (c = *s++)
507 {
508 case 0:
509 goto invalid;
510 case 'h': case 'l': case 'L':
511 case '+': case '-': case '.': case '_':
512 case '0': case '1': case '2': case '3': case '4':
513 case '5': case '6': case '7': case '8': case '9':
514 continue;
515 case '%':
516 *f++ = '%';
517 continue;
518 case '(':
519 t = f;
520 d = 0;
521 p = 1;
522 for (;;)
523 {
524 switch (c = *s++)
525 {
526 case 0:
527 goto invalid;
528 case '(':
529 p++;
530 *t++ = c;
531 continue;
532 case ')':
533 if (!--p)
534 break;
535 *t++ = c;
536 continue;
537 case ':':
538 if (d)
539 *t++ = c;
540 else
541 {
542 *t++ = 0;
543 d = t;
544 }
545 continue;
546 default:
547 *t++ = c;
548 continue;
549 }
550 break;
551 }
552 *t = 0;
553 if (dtmatch(meth->cx->variables, f))
554 {
555 if (disc->errorf)
556 (*disc->errorf)(NiL, disc, 2, "%s: duplicate field", f);
557 goto drop;
558 }
559 if (!(var = newof(0, Cxvariable_t, 1, t - f + 1)))
560 {
561 if (disc->errorf)
562 (*disc->errorf)(NiL, disc, 2, "out of space");
563 goto drop;
564 }
565 var->index = index;
566 t = strcopy((char*)(var->name = (char*)(var + 1)), f);
567 if (d)
568 var->description = strcpy(t + 1, d);
569 break;
570 }
571 for (;;)
572 {
573 switch (c = *s++)
574 {
575 case 0:
576 goto invalid;
577 case 'h': case 'l': case 'L':
578 case '+': case '-': case '.': case '_':
579 case '0': case '1': case '2': case '3': case '4':
580 case '5': case '6': case '7': case '8': case '9':
581 continue;
582 }
583 break;
584 }
585 if (var)
586 {
587 switch (c)
588 {
589 case 'd':
590 case 'f':
591 case 'g':
592 case 'n':
593 case 'o':
594 case 'u':
595 case 'x':
596 var->type = (Cxtype_t*)"number";
597 break;
598 case 'i':
599 var->type = (Cxtype_t*)"ipaddr_t";
600 break;
601 case 's':
602 var->type = (Cxtype_t*)"string";
603 break;
604 case 't':
605 var->type = (Cxtype_t*)"time_t";
606 break;
607 default:
608 if (disc->errorf)
609 (*disc->errorf)(NiL, disc, 2, "%c: invalid field format >>>%s", c, s - 1);
610 goto drop;
611 }
612 if (cxaddvariable(meth->cx, var, disc))
613 goto drop;
614 }
615 index++;
616 *f++ = c;
617 continue;
618 case ' ':
619 case '\t':
620 case '\n':
621 if (f == text->format || *(f - 1) != ' ')
622 *f++ = ' ';
623 continue;
624 default:
625 *f++ = c;
626 continue;
627 }
628 break;
629 }
630 if (!(text->vars = index))
631 goto invalid;
632 *f = 0;
633 dtinsert(meth->formats, &text_format);
634 for (c = 0; c < elementsof(local_callouts); c++)
635 if (cxaddcallout(meth->cx, &local_callouts[c], disc))
636 return 0;
637 return meth;
638 invalid:
639 if (disc->errorf)
640 (*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "%s: invalid schema", schema);
641 drop:
642 free(meth);
643 return 0;
644 }
645
646 /*
647 * openf
648 */
649
650 static int
textopen(Dss_t * dss,Dssdisc_t * disc)651 textopen(Dss_t* dss, Dssdisc_t* disc)
652 {
653 return dss->meth->data ? 0 : -1;
654 }
655
656 static Dssmeth_t method =
657 {
658 "text",
659 "Newline-terminated field-delimited text file; the method schema is"
660 " a scanf(3) like format string with embedded field names of the form:"
661 " %(field1)format-char delimiter ...",
662 CXH,
663 textmeth,
664 textopen,
665 0,
666 0
667 };
668
669 Dsslib_t dss_lib_text =
670 {
671 "text",
672 "text method"
673 "[-1ls5Pp0?\n@(#)$Id: dss text method (AT&T Research) 2002-12-17 $\n]"
674 USAGE_LICENSE,
675 CXH,
676 0,
677 &method,
678 };
679