1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 2002-2012 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 <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * dss printf implementation
23 */
24
25 #include "dsshdr.h"
26
27 #include <ast_float.h>
28
29 struct Arg_s; typedef struct Arg_s Arg_t;
30
31 struct Arg_s
32 {
33 Cxvariable_t* variable;
34 Cxexpr_t* expr;
35 Cxtype_t* cast;
36 Cxedit_t* edit;
37 char* details;
38 char* qb;
39 char* qe;
40 unsigned short type;
41 unsigned short fmt;
42 unsigned char flags;
43 };
44
45 struct Format_s
46 {
47 Format_t* next;
48 char* oformat;
49 char* nformat;
50 Arg_t arg[1];
51 };
52
53 typedef struct Fmt_s
54 {
55 Sffmt_t fmt;
56 Cx_t* cx;
57 void* data;
58 int errors;
59 Arg_t* ap;
60 } Fmt_t;
61
62 typedef union
63 {
64 char** p;
65 char* s;
66 Sflong_t q;
67 long l;
68 int i;
69 short h;
70 char c;
71 double f;
72 } Value_t;
73
74 #define DSS_FORMAT_char 1
75 #define DSS_FORMAT_float 2
76 #define DSS_FORMAT_int 3
77 #define DSS_FORMAT_long 4
78 #define DSS_FORMAT_string 5
79
80 #define DSS_FORMAT_quote 0x01
81
82 /*
83 * sfio %! extension function
84 */
85
86 static int
getfmt(Sfio_t * sp,void * vp,Sffmt_t * dp)87 getfmt(Sfio_t* sp, void* vp, Sffmt_t* dp)
88 {
89 register Fmt_t* fp = (Fmt_t*)dp;
90 register Arg_t* ap = fp->ap++;
91 Value_t* value = (Value_t*)vp;
92 Cxoperand_t ret;
93
94 if (ap->expr && cxeval(fp->cx, ap->expr, fp->data, &ret) < 0 || cxcast(fp->cx, &ret, ap->variable, ap->cast, fp->data, ap->details))
95 {
96 fp->errors++;
97 return -1;
98 }
99 fp->fmt.flags |= SFFMT_VALUE;
100 switch (ap->type)
101 {
102 case DSS_FORMAT_char:
103 fp->fmt.size = sizeof(int);
104 if (ret.value.number < 1)
105 value->c = 0;
106 else if (ret.value.number > UCHAR_MAX)
107 value->c = UCHAR_MAX;
108 else
109 value->c = (unsigned char)ret.value.number;
110 break;
111 case DSS_FORMAT_float:
112 fp->fmt.size = sizeof(double);
113 value->f = ret.value.number;
114 break;
115 case DSS_FORMAT_int:
116 #if 0
117 /*
118 * this code is technically correct but overly
119 * complicates script portability between architectures
120 * with differing sizeof(int) and/or sizeof(long)
121 */
122
123 fp->fmt.size = sizeof(int);
124 if (((ret.value.number >= 0) ? ret.value.number : -ret.value.number) < 1)
125 value->i = 0;
126 else if (ret.value.number > UINT_MAX)
127 value->i = INT_MAX;
128 else if (ret.value.number < INT_MIN)
129 value->i = INT_MAX;
130 else
131 value->i = (unsigned int)ret.value.number;
132 break;
133 #endif
134 case DSS_FORMAT_long:
135 fp->fmt.size = sizeof(Sflong_t);
136 if (((ret.value.number >= 0) ? ret.value.number : -ret.value.number) < 1)
137 value->q = 0;
138 else if (ret.value.number > FLTMAX_UINTMAX_MAX)
139 value->q = FLTMAX_INTMAX_MAX;
140 else if (ret.value.number < FLTMAX_INTMAX_MIN)
141 value->q = FLTMAX_INTMAX_MAX;
142 else
143 value->q = (Sfulong_t)((Sflong_t)ret.value.number);
144 break;
145 case DSS_FORMAT_string:
146 if (ap->fmt & (FMT_EXP_CHAR|FMT_EXP_LINE|FMT_EXP_NOCR|FMT_EXP_NONL|FMT_EXP_WIDE))
147 ret.value.string.size = strexp(ret.value.string.data, ap->fmt);
148 if (ap->edit)
149 cxsub(fp->cx, ap->edit, &ret);
150 if (ap->flags & DSS_FORMAT_quote)
151 ret.value.string.size = strlen(ret.value.string.data = fmtquote(ret.value.string.data, ap->qb, ap->qe, ret.value.string.size, ap->fmt));
152 value->s = ret.value.string.data;
153 fp->fmt.size = ret.value.string.size;
154 break;
155 }
156 return 0;
157 }
158
159 /*
160 * printf
161 */
162
163 ssize_t
dssprintf(Dss_t * dss,Cx_t * cx,Sfio_t * sp,const char * format,Dssrecord_t * record)164 dssprintf(Dss_t* dss, Cx_t* cx, Sfio_t* sp, const char* format, Dssrecord_t* record)
165 {
166 Format_t* fp;
167 size_t n;
168 int q;
169 int l;
170 Fmt_t fmt;
171
172 if (!cx)
173 cx = dss->cx;
174 if (!format)
175 {
176 Cxvariable_t* vp;
177 unsigned char* p;
178 unsigned char* e;
179 Cxinstruction_t x;
180 Cxoperand_t r;
181 Cxoperand_t a;
182
183 /*
184 * {print --all} : all fields with non-default values
185 */
186
187 if (!(q = dss->meth->cx->width))
188 {
189 if (!cx->getf && !(cx->getf = cxcallout(cx, CX_GET, 0, 0, cx->disc)))
190 {
191 if (cx->disc->errorf)
192 (*cx->disc->errorf)(NiL, cx->disc, 3, "cx CX_GET callout must be defined");
193 return 0;
194 }
195 n = q = 0;
196 if (dss->meth->cx->fields)
197 {
198 for (vp = (Cxvariable_t*)dtfirst(dss->meth->cx->fields); vp; vp = (Cxvariable_t*)dtnext(dss->meth->cx->fields, vp), n++)
199 if (!(vp->header.flags & CX_DEPRECATED) && q < (l = strlen(vp->name)))
200 q = l;
201 }
202 else if (dss->meth->data)
203 {
204 for (vp = (Cxvariable_t*)dss->meth->data; vp->name; vp++, n++)
205 if (!(vp->header.flags & CX_DEPRECATED) && q < (l = strlen(vp->name)))
206 q = l;
207 }
208 dss->meth->cx->width = q += 2;
209 }
210 n = 0;
211 if (sp && (vp = dss->meth->cx->fields ? (Cxvariable_t*)dtfirst(dss->meth->cx->fields) : dss->meth->data ? (Cxvariable_t*)dss->meth->data : 0))
212 {
213 for (;;)
214 {
215 if (dss->meth->cx->fields)
216 {
217 if (!vp)
218 break;
219 }
220 else if (!vp->name)
221 break;
222 if (!vp->type->generic && !(vp->header.flags & CX_DEPRECATED))
223 {
224 x.data.variable = vp;
225 if (!(*cx->getf)(cx, &x, &r, &a, NiL, record, cx->disc))
226 {
227 switch (cxrepresentation(vp->type))
228 {
229 case CX_buffer:
230 if (!(p = (unsigned char*)r.value.buffer.data))
231 goto next;
232 if (r.value.buffer.size)
233 {
234 e = p + r.value.buffer.size;
235 while (p < e && !*p)
236 p++;
237 if (p >= e)
238 goto next;
239 }
240 break;
241 case CX_number:
242 if (!r.value.number)
243 goto next;
244 break;
245 case CX_string:
246 if (!r.value.string.size)
247 goto next;
248 break;
249 case CX_void:
250 goto next;
251 }
252 if (!cxcast(cx, &r, vp, cx->state->type_string, record, NiL) && r.value.string.size)
253 n += sfprintf(sp, "%s%-*s%-.*s\n", vp->name, q - strlen(vp->name), "", r.value.string.size, r.value.string.data);
254 }
255 }
256 next:
257 if (dss->meth->cx->fields)
258 vp = (Cxvariable_t*)dtnext(dss->meth->cx->fields, vp);
259 else
260 vp++;
261 }
262 n += sfprintf(sp, "\n");
263 }
264 return n;
265 }
266 for (fp = dss->print; fp && fp->oformat != (char*)format; fp = fp->next);
267 if (!fp)
268 {
269 register char* s;
270 register char* t;
271 register char* d;
272 register char* v;
273 register Arg_t* ap;
274 int x;
275 char* f;
276 char* o;
277 char* w;
278 char* details['z' - 'a' + 1];
279
280 f = s = (char*)format;
281 memset(details, 0, sizeof(details));
282 d = 0;
283 l = 0;
284 n = 0;
285 q = 0;
286 w = 0;
287 for (;;)
288 {
289 switch (*s++)
290 {
291 case 0:
292 if (q)
293 {
294 if (dss->disc->errorf)
295 (*dss->disc->errorf)(NiL, dss->disc, 2, "%s: format character omitted", f);
296 return -1;
297 }
298 break;
299 case '%':
300 if (*s != '%')
301 {
302 q = 1;
303 n++;
304 f = s - 1;
305 }
306 continue;
307 case '(':
308 if (q == 1)
309 {
310 q++;
311 for (;;)
312 {
313 switch (*s++)
314 {
315 case 0:
316 s--;
317 break;
318 case '(':
319 q++;
320 continue;
321 case ')':
322 if (--q == 1)
323 break;
324 continue;
325 case ':':
326 if (*s == ':')
327 s++;
328 else if (!d)
329 d = s;
330 continue;
331 default:
332 continue;
333 }
334 break;
335 }
336 if (d)
337 {
338 l += s - d + 1;
339 d = 0;
340 }
341 }
342 continue;
343 case 'c':
344 case 'd':
345 case 'e':
346 case 'f':
347 case 'g':
348 case 'o':
349 case 's':
350 case 'u':
351 case 'x':
352 if (q == 1)
353 q = 0;
354 continue;
355 default:
356 continue;
357 }
358 break;
359 }
360 if (!(fp = vmnewof(dss->vm, 0, Format_t, 1, (n - 1) * sizeof(Arg_t) + strlen(format) + 2 * n + l + 2)))
361 {
362 if (dss->disc->errorf)
363 (*dss->disc->errorf)(NiL, dss->disc, ERROR_SYSTEM|2, "out of space");
364 return -1;
365 }
366 fp->oformat = (char*)format;
367 fp->next = dss->print;
368 dss->print = fp;
369 ap = &fp->arg[0];
370 s = t = fp->nformat = (char*)(&fp->arg[n]);
371 strcpy(t, format);
372 f = t + strlen(format) + 2 * n + 1;
373 q = 0;
374 d = 0;
375 l = 0;
376 for (;;)
377 {
378 switch (*t++ = *s++)
379 {
380 case 0:
381 *(t - 1) = '\n';
382 *t = 0;
383 break;
384 case '%':
385 if (*s == '%')
386 *t++ = *s++;
387 else
388 q = 1;
389 continue;
390 case '(':
391 if (q == 1)
392 {
393 q++;
394 t--;
395 x = 0;
396 v = s;
397 for (;;)
398 {
399 switch (*s++)
400 {
401 case 0:
402 if (dss->disc->errorf)
403 (*dss->disc->errorf)(NiL, dss->disc, 2, "%s: %(...) imbalance", fp->oformat);
404 return -1;
405 case '(':
406 if (!d)
407 x = 1;
408 q++;
409 continue;
410 case ')':
411 if (--q == 1)
412 break;
413 continue;
414 case ':':
415 if (*s == ':')
416 s++;
417 else if (!d && q == 2)
418 d = s;
419 continue;
420 case ',':
421 if (!d)
422 x = 1;
423 continue;
424 default:
425 if (!d && cx->table->opcode[*(unsigned char*)(s - 1)])
426 x = 1;
427 continue;
428 }
429 break;
430 }
431 if (d)
432 *(d - 1) = 0;
433 *(s - 1) = 0;
434 if (*v)
435 {
436 if (x)
437 {
438 void* pop;
439
440 if (!(pop = cxpush(cx, NiL, NiL, v, (d ? d : s) - v, 0)))
441 return -1;
442 ap->expr = cxcomp(cx);
443 cxpop(cx, pop);
444 if (!ap->expr)
445 return -1;
446 }
447 else if (cx->referencef)
448 {
449 Cxoperand_t a;
450 Cxoperand_t b;
451 Cxoperand_t r;
452
453 a.type = cx->state->type_string;
454 a.value.string.size = s - v - 1;
455 a.value.string.data = v;
456 b.type = a.type;
457 if ((*cx->referencef)(cx, NiL, &r, &b, &a, NiL, cx->disc))
458 return -1;
459 ap->variable = r.value.variable;
460 }
461 else if (!(ap->variable = cxvariable(cx, v, NiL, cx->disc)))
462 return -1;
463 }
464 else if (d)
465 {
466 w = d;
467 d = 0;
468 }
469 }
470 continue;
471 case 'c':
472 if (q == 1)
473 {
474 ap->type = DSS_FORMAT_char;
475 ap->cast = cx->state->type_number;
476 goto set;
477 }
478 continue;
479 case 'd':
480 case 'o':
481 case 'u':
482 case 'x':
483 if (q == 1)
484 {
485 if (l > 1 || ap->variable && (ap->variable->format.width == 8 || ap->variable->type->format.width == 8))
486 {
487 n = *(t - 1);
488 *(t - 1) = 'l';
489 *t++ = 'l';
490 *t++ = n;
491 ap->type = DSS_FORMAT_long;
492 }
493 else
494 ap->type = DSS_FORMAT_int;
495 ap->cast = cx->state->type_number;
496 goto set;
497 }
498 continue;
499 case 'e':
500 case 'f':
501 case 'g':
502 if (q == 1)
503 {
504 ap->type = DSS_FORMAT_float;
505 ap->cast = cx->state->type_number;
506 goto set;
507 }
508 continue;
509 case 'h':
510 if (q == 1)
511 t--;
512 continue;
513 case 'l':
514 if (q == 1)
515 {
516 t--;
517 l++;
518 }
519 continue;
520 case 's':
521 if (q == 1)
522 {
523 ap->type = DSS_FORMAT_string;
524 ap->cast = cx->state->type_string;
525 set:
526 if (w)
527 {
528 details[*(s-1) - 'a'] = w;
529 w = 0;
530 fp->nformat = t = s;
531 continue;
532 }
533 if (!ap->variable && !ap->expr)
534 {
535 if (dss->disc->errorf)
536 {
537 *t = 0;
538 (*dss->disc->errorf)(NiL, dss->disc, 2, "%s: (variable) omitted in format", fp->nformat);
539 }
540 return -1;
541 }
542 l = 0;
543 q = 0;
544 if (d || (d = details[*(s-1) - 'a']) || (d = cx->state->type_string->format.details))
545 {
546 ap->fmt = FMT_ALWAYS|FMT_ESCAPED;
547 while (*d)
548 {
549 o = 0;
550 v = d;
551 while (*d)
552 if (*d++ == ':')
553 {
554 *(o = d - 1) = 0;
555 break;
556 }
557 if (strneq(v, "edit=", 5))
558 {
559 if (o)
560 *o = ':';
561 if (ap->edit = cxedit(cx, v + 5, dss->disc))
562 {
563 d = v + 5 + ap->edit->re.re_npat;
564 if (d == o)
565 d++;
566 }
567 }
568 else if (strneq(v, "endquote=", 8))
569 {
570 ap->qe = v += 8;
571 while (*f++ = *v++);
572 }
573 else if (streq(v, "expand"))
574 {
575 ap->fmt |= FMT_EXP_CHAR|FMT_EXP_LINE|FMT_EXP_WIDE;
576 continue;
577 }
578 else if (strneq(v, "expand=", 7))
579 {
580 v += 7;
581 while (*v)
582 {
583 if (*v == '|' || *v == ',')
584 {
585 v++;
586 continue;
587 }
588 if (strneq(v, "all", 3))
589 {
590 ap->fmt |= FMT_EXP_CHAR|FMT_EXP_LINE|FMT_EXP_WIDE;
591 break;
592 }
593 else if (strneq(v, "char", 4))
594 {
595 v += 4;
596 ap->fmt |= FMT_EXP_CHAR;
597 }
598 else if (strneq(v, "line", 4))
599 {
600 v += 4;
601 ap->fmt |= FMT_EXP_LINE;
602 }
603 else if (strneq(v, "nocr", 4))
604 {
605 v += 4;
606 ap->fmt |= FMT_EXP_NOCR;
607 }
608 else if (strneq(v, "nonl", 4))
609 {
610 v += 4;
611 ap->fmt |= FMT_EXP_NONL;
612 }
613 else if (strneq(v, "wide", 4))
614 {
615 v += 4;
616 ap->fmt |= FMT_EXP_WIDE;
617 }
618 else
619 while (*v && *v != '|' && *v != ',')
620 v++;
621 }
622 continue;
623 }
624 else if (streq(v, "escape"))
625 ap->fmt &= ~FMT_ESCAPED;
626 else if (strneq(v, "opt", 3))
627 ap->fmt &= ~FMT_ALWAYS;
628 else if (streq(v, "quote") || strneq(v, "quote=", 6))
629 {
630 if (v[5])
631 {
632 ap->qb = v += 6;
633 while (*f++ = *v++);
634 }
635 else
636 ap->qb = "\"";
637 if (!ap->qe)
638 ap->qe = ap->qb;
639 }
640 else if (streq(v, "shell") || strneq(v, "shell=", 6))
641 {
642 ap->fmt |= FMT_SHELL;
643 if (v[5])
644 {
645 ap->qb = v += 6;
646 while (*f++ = *v++);
647 }
648 else
649 ap->qb = "$'";
650 if (!ap->qe)
651 ap->qe = "'";
652 }
653 else if (streq(v, "wide"))
654 ap->fmt |= FMT_WIDE;
655 else
656 {
657 if (*d)
658 *(d - 1) = ':';
659 d = v;
660 break;
661 }
662 ap->flags |= DSS_FORMAT_quote;
663 }
664 ap->details = f;
665 while (*f++ = *d++);
666 d = 0;
667 }
668 if (ap->variable && !ap->edit && cxisstring(ap->variable->type) && ap->variable->format.map && ap->variable->format.map->part && ap->variable->format.map->part->edit)
669 ap->edit = ap->variable->format.map->part->edit;
670 ap++;
671 }
672 continue;
673 case 'L':
674 if (q == 1)
675 {
676 t--;
677 l += 2;
678 }
679 continue;
680 default:
681 continue;
682 }
683 break;
684 }
685 if (!sp)
686 return 0;
687 }
688 memset(&fmt, 0, sizeof(fmt));
689 fmt.fmt.version = SFIO_VERSION;
690 fmt.fmt.form = fp->nformat;
691 fmt.fmt.extf = getfmt;
692 fmt.cx = cx;
693 fmt.data = record;
694 fmt.ap = &fp->arg[0];
695 n = sfprintf(sp, "%!", &fmt);
696 return !sp ? 0 : (fmt.errors || n <= 0 && sp && sferror(sp)) ? -1 : n;
697 }
698