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