1 /*-
2  * tc.printf.c: A public-domain, minimal printf/sprintf routine that prints
3  *	       through the putchar() routine.  Feel free to use for
4  *	       anything...  -- 7/17/87 Paul Placeway
5  */
6 /*-
7  * Copyright (c) 1980, 1991 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*-
35  * Copyright (c) 2012-2018 Michael Scholz <mi-scholz@users.sourceforge.net>
36  * All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  *
59  * @(#)printf.c	2.1 1/2/18
60  */
61 
62 #if defined(HAVE_CONFIG_H)
63 #include "config.h"
64 #endif
65 
66 #include "fth.h"
67 #include "utils.h"
68 
69 #if defined(lint)
70 #undef va_arg
71 #define va_arg(a, b)		(a ? (b) 0 : (b) 0)
72 #endif
73 
74 /*
75  * limits.h
76  *   INT_MAX
77  *   CHAR_BIT
78  */
79 
80 #if !defined(INT_MAX)
81 #define INT_MAX			LONG_MAX
82 #endif
83 
84 #if !defined(CHAR_BIT)
85 #define CHAR_BIT		8
86 #endif
87 
88 /*
89  * float.h
90  *   DBL_MANT_DIG
91  *   DBL_MAX_EXP
92  *   DBL_MAX_10_EXP
93  */
94 #if !defined(DBL_MAX_10_EXP)
95 #define DBL_MAX_10_EXP		308
96 #endif
97 
98 #define DBL_BUF_LEN		(DBL_MAX_10_EXP + DBL_MANT_DIG + 3)
99 
100 /* should be bigger than any field to print */
101 #define INF			INT_MAX
102 
103 static char 	snil[] = "(nil)";
104 
105 static void 	doprnt(void (*) (int), const char *, va_list);
106 static void 	xaddchar(int);
107 static int 	fth_basic_printf(void *, int, const char *);
108 static int 	fth_basic_vprintf(void *, int, const char *, va_list);
109 static FTH 	format_argument_error(const char *);
110 static void 	string_doprnt(void (*) (int), const char *, FTH);
111 
112 static void
doprnt(void (* addchar)(int),const char * sfmt,va_list ap)113 doprnt(void (*addchar) (int), const char *sfmt, va_list ap)
114 {
115 	char           *bp;
116 	const char     *f;
117 
118 #if defined(HAVE_LONG_LONG)
119 	long long 	l;
120 	unsigned long long u;
121 #else
122 	long 		l;
123 	unsigned long 	u;
124 #endif
125 	FTH 		obj;
126 	char 		tb[DBL_BUF_LEN];
127 	ficlFloat 	d;
128 	char 		tf[7];
129 	int 		idx;
130 	size_t 		fs = DBL_BUF_LEN;
131 
132 	/* Octal: 3 bits per char */
133 	char 		buf[(CHAR_BIT * sizeof(l) + 2) / 3 + 1];
134 	int 		i;
135 	int 		fmt;
136 	unsigned char 	pad = ' ';
137 	int 		flush_left = 0, f_width = 0, prec = INF, hash = 0;
138 	int 		do_long = 0, do_size_t = 0;
139 	int 		sign = 0;
140 
141 	f = sfmt;
142 	for (; *f != '\0'; f++) {
143 		if (*f != '%') {
144 			/* then just out the char */
145 			(*addchar) (*f);
146 			continue;
147 		}
148 		f++;		/* skip the % */
149 
150 		if (*f == '#') {/* alternate form */
151 			hash = 1;
152 			f++;
153 		}
154 		if (*f == '-') {/* minus: flush left */
155 			flush_left = 1;
156 			f++;
157 		}
158 		if (*f == '0') {/* || *f == '.') XXX [ms] */
159 			pad = '0';	/* padding with 0 rather than blank */
160 			f++;
161 		}
162 		if (*f == '*') {/* field width */
163 			f_width = va_arg(ap, int);
164 			f++;
165 		} else if (isdigit((unsigned char) *f)) {
166 			f_width = atoi(f);
167 			while (isdigit((unsigned char) *f))
168 				f++;	/* skip the digits */
169 		}
170 		if (*f == '.') {/* precision */
171 			f++;
172 
173 			if (*f == '*') {
174 				prec = va_arg(ap, int);
175 				f++;
176 			} else if (isdigit((unsigned char) *f)) {
177 				prec = atoi(f);
178 
179 				while (isdigit((unsigned char) *f))
180 					f++;	/* skip the digits */
181 			}
182 		}
183 		if (*f == 'l') {/* long format */
184 			do_long++;
185 			f++;
186 
187 			if (*f == 'l') {
188 				do_long++;
189 				f++;
190 			}
191 		}
192 		if (*f == 'z') {/* size_t format */
193 			do_size_t++;
194 			f++;
195 		}
196 		fmt = (unsigned char) *f;
197 		bp = buf;
198 
199 		switch (fmt) {	/* do the format */
200 		case 'c':
201 			i = va_arg(ap, int);
202 			f_width--;	/* adjust for one char [ms] */
203 
204 			if (!flush_left)
205 				while (f_width-- > 0)
206 					(*addchar) (pad);
207 
208 			(*addchar) (i);
209 
210 			if (flush_left)
211 				while (f_width-- > 0)
212 					(*addchar) (' ');
213 			break;
214 		case 'd':
215 			switch (do_long) {
216 			case 0:
217 				if (do_size_t)
218 					l = (long) (va_arg(ap, size_t));
219 				else
220 					l = (long) (va_arg(ap, int));
221 				break;
222 			case 1:
223 #if !defined(HAVE_LONG_LONG)
224 			default:
225 #endif
226 				l = va_arg(ap, long);
227 				break;
228 #if defined(HAVE_LONG_LONG)
229 			default:
230 				l = va_arg(ap, long long);
231 				break;
232 #endif
233 			}
234 
235 			if (l < 0) {
236 				sign = 1;
237 				l = -l;
238 			}
239 
240 			do {
241 				*bp++ = (char) (l % 10 + '0');
242 			} while ((l /= 10) > 0);
243 
244 			if (sign)
245 				*bp++ = '-';
246 
247 			f_width = f_width - (int) (bp - buf);
248 
249 			if (!flush_left)
250 				while (f_width-- > 0)
251 					(*addchar) (pad);
252 
253 			for (bp--; bp >= buf; bp--)
254 				(*addchar) (*bp);
255 
256 			if (flush_left)
257 				while (f_width-- > 0)
258 					(*addchar) (' ');
259 			break;
260 		case 'p':
261 			do_long = 1;
262 			hash = 1;
263 			fmt = 'x';
264 			/* FALLTHROUGH */
265 		case 'b':	/* [ms] %b added */
266 		case 'B':
267 		case 'o':
268 		case 'O':
269 		case 'x':
270 		case 'X':
271 		case 'u':
272 		case 'U':
273 			switch (do_long) {
274 			case 0:
275 				if (do_size_t)
276 					u = va_arg(ap, size_t);
277 				else
278 					u = va_arg(ap, unsigned int);
279 				break;
280 			case 1:
281 #if !defined(HAVE_LONG_LONG)
282 			default:
283 #endif
284 				u = va_arg(ap, unsigned long);
285 				break;
286 #if defined(HAVE_LONG_LONG)
287 			default:
288 				u = va_arg(ap, unsigned long long);
289 				break;
290 #endif
291 			}
292 			switch (fmt) {
293 			case 'u':	/* unsigned decimal */
294 			case 'U':
295 				do {
296 					*bp++ = (char) (u % 10 + '0');
297 				} while ((u /= 10) > 0);
298 				goto out_d;
299 				break;
300 			case 'o':	/* octal */
301 			case 'O':
302 				do {
303 					*bp++ = (char) (u % 8 + '0');
304 				} while ((u /= 8) > 0);
305 
306 				if (hash)
307 					*bp++ = '0';
308 
309 				goto out_d;
310 				break;
311 			case 'b':	/* binary added [ms] */
312 			case 'B':
313 				do {
314 					*bp++ = (char) (u % 2 + '0');
315 				} while ((u /= 2) > 0);
316 
317 				if (hash) {
318 					*bp++ = (fmt == 'b') ? 'b' : 'B';
319 					*bp++ = '0';
320 				}
321 				goto out_d;
322 				break;
323 			case 'x':	/* hex */
324 			case 'X':
325 				do {
326 					char 		cn;
327 
328 					cn = (fmt == 'x') ? 'a' : 'A';
329 					i = (int) (u % 16);
330 
331 					if (i < 10)
332 						*bp++ = (char) (i + '0');
333 					else
334 						*bp++ = (char) (i - 10 + cn);
335 				} while ((u /= 16) > 0);
336 
337 				if (hash) {
338 					*bp++ = (fmt == 'x') ? 'x' : 'X';
339 					*bp++ = '0';
340 				}
341 				goto out_d;
342 				break;
343 			}
344 out_d:
345 			f_width = f_width - (int) (bp - buf);
346 
347 			if (!flush_left)
348 				while (f_width-- > 0)
349 					(*addchar) (pad);
350 
351 			for (bp--; bp >= buf; bp--)
352 				(*addchar) (*bp);
353 
354 			if (flush_left)
355 				while (f_width-- > 0)
356 					(*addchar) (' ');
357 			break;
358 
359 			/* [ms] ficlFloat added */
360 		case 'A':
361 		case 'E':
362 		case 'F':
363 		case 'G':
364 		case 'a':
365 		case 'e':
366 		case 'f':
367 		case 'g':
368 			d = va_arg(ap, ficlFloat);
369 			idx = 0;
370 			tf[idx++] = '%';
371 
372 			if (hash)
373 				tf[idx++] = '#';
374 
375 			if (f_width == 0) {
376 				if (prec == INF) {
377 					tf[idx++] = *f;
378 					tf[idx] = '\0';
379 					snprintf(tb, fs, tf, d);
380 				} else {
381 					tf[idx++] = '.';
382 					tf[idx++] = '*';
383 					tf[idx++] = *f;
384 					tf[idx] = '\0';
385 					snprintf(tb, fs, tf, prec, d);
386 				}
387 			} else {
388 				if (prec == INF) {
389 					tf[idx++] = '*';
390 					tf[idx++] = *f;
391 					tf[idx] = '\0';
392 					snprintf(tb, fs, tf, f_width, d);
393 				} else {
394 					tf[idx++] = '*';
395 					tf[idx++] = '.';
396 					tf[idx++] = '*';
397 					tf[idx++] = *f;
398 					tf[idx] = '\0';
399 					snprintf(tb, fs, tf, f_width, prec, d);
400 				}
401 			}
402 			bp = tb;
403 
404 			while (*bp != '\0')
405 				(*addchar) (*bp++);
406 
407 			break;
408 
409 			/*-
410 			 * FTH additions:
411 			 *
412 			 * %I -- fth_object_inspect
413 			 * %S -- fth_object_to_string
414 			 * %M -- fth_object_to_string_2
415 			 * %D -- fth_object_dump
416 			 */
417 		case 'I':
418 		case 'S':
419 		case 'M':
420 		case 'D':
421 			obj = va_arg(ap, FTH);
422 
423 			switch (*f) {
424 			case 'I':
425 				bp = fth_to_c_inspect(obj);
426 				break;
427 			case 'S':
428 				bp = fth_to_c_string(obj);
429 				break;
430 			case 'M':
431 				bp = fth_to_c_string_2(obj);
432 				break;
433 			case 'D':
434 				bp = fth_to_c_dump(obj);
435 				break;
436 			}
437 
438 			goto out_s;
439 			break;
440 		case 'Q':
441 		case 'q':
442 		case 's':
443 			bp = va_arg(ap, char *);
444 out_s:
445 
446 			if (bp == NULL)
447 				bp = snil;
448 
449 			if (prec == INF)
450 				f_width -= (int) strlen(bp);
451 			else
452 				f_width -= prec;
453 
454 			if (!flush_left)
455 				while (f_width-- > 0)
456 					(*addchar) (pad);
457 
458 			for (i = 0; *bp && i < prec; i++)
459 				(*addchar) ((unsigned char) *bp++);
460 
461 			if (flush_left)
462 				while (f_width-- > 0)
463 					(*addchar) (' ');
464 			break;
465 
466 		case '%':
467 			(*addchar) ('%');
468 			break;
469 
470 		default:
471 			break;
472 		}		/* switch(fmt) */
473 
474 		flush_left = 0;
475 		f_width = 0;
476 		prec = INF;
477 		hash = 0;
478 		do_size_t = 0;
479 		do_long = 0;
480 		sign = 0;
481 		pad = ' ';
482 	}			/* for */
483 }
484 
485 static char    *xstring;
486 static char    *xestring;
487 
488 static void
xaddchar(int c)489 xaddchar(int c)
490 {
491 	if (xestring == xstring)
492 		*xstring = '\0';
493 	else
494 		*xstring++ = (char) c;
495 }
496 
497 #if 0
498 static void
499 xputchar(int c)
500 {
501 	(void) fputc(c, stdout);
502 }
503 
504 /*
505  * From tcsh-src/tc.printf.c
506  */
507 void
508 xsnprintf(char *str, size_t size, const char *fmt,...)
509 {
510 	va_list 	va;
511 
512 	va_start(va, fmt);
513 	xstring = str;
514 	xestring = str + size - 1;
515 	doprnt(xaddchar, fmt, va);
516 	va_end(va);
517 	*xstring++ = '\0';
518 }
519 
520 void
521 xprintf(const char *fmt,...)
522 {
523 	va_list 	va;
524 
525 	va_start(va, fmt);
526 	doprnt(xputchar, fmt, va);
527 	va_end(va);
528 }
529 
530 void
531 xvprintf(const char *fmt, va_list va)
532 {
533 	doprnt(xputchar, fmt, va);
534 }
535 
536 void
537 xvsnprintf(char *str, size_t size, const char *fmt, va_list va)
538 {
539 	xstring = str;
540 	xestring = str + size - 1;
541 	doprnt(xaddchar, fmt, va);
542 	*xstring++ = '\0';
543 }
544 
545 char           *
546 xvasprintf(const char *fmt, va_list va)
547 {
548 	size_t 		size;
549 	char           *buf;
550 
551 	buf = NULL;
552 	size = 2048;		/* Arbitrary */
553 
554 	for (;;) {
555 		va_list 	copy;
556 
557 		buf = fth_realloc(buf, size);
558 		xstring = buf;
559 		xestring = buf + size - 1;
560 		va_copy(copy, va);
561 		doprnt(xaddchar, fmt, copy);
562 		va_end(copy);
563 
564 		if (xstring < xestring)
565 			break;
566 
567 		size *= 2;
568 	}
569 
570 	*xstring++ = '\0';
571 	return (fth_realloc(buf, xstring - buf));
572 }
573 
574 char           *
575 xasprintf(const char *fmt,...)
576 {
577 	va_list 	va;
578 	char           *ret;
579 
580 	va_start(va, fmt);
581 	ret = xvasprintf(fmt, va);
582 	va_end(va);
583 	return (ret);
584 }
585 
586 #endif				/* tcsh-src/tc.printf.c */
587 
588 /* === FTH Printf Function Set === */
589 
590 /*-
591  * fth_printf and friends
592  *
593  * Extra options:
594  *
595  * %I -- fth_object_inspect
596  * %S -- fth_object_to_string
597  * %M -- fth_object_to_string_2
598  * %D -- fth_object_dump
599  * %b -- binary
600  */
601 
602 enum {
603 	PORT_FICLOUT,		/* ficlVm *vm */
604 	PORT_FICLERR,		/* ficlVm *vm */
605 	PORT_FILE,		/* FILE   *fp */
606 	PORT_IO			/* FTH     io */
607 };
608 
609 /* defined in port.c */
610 extern out_cb 	fth_print_hook;
611 extern out_cb 	fth_error_hook;
612 
613 static int
fth_basic_printf(void * port,int type,const char * sstr)614 fth_basic_printf(void *port, int type, const char *sstr)
615 {
616 	char           *str;
617 	int 		len;
618 
619 	str = (char *) sstr;
620 	len = (int) fth_strlen(str);
621 
622 	if (len <= 0)
623 		return (0);
624 
625 	switch (type) {
626 	case PORT_FICLOUT:
627 		fth_print_p = 1;
628 		(*fth_print_hook) ((ficlVm *) port, str);
629 		break;
630 	case PORT_FICLERR:
631 		fth_print_p = 1;
632 		(*fth_error_hook) ((ficlVm *) port, str);
633 		break;
634 	case PORT_FILE:
635 		len = fputs(str, (FILE *) port);
636 		fflush(port);
637 		break;
638 	case PORT_IO:
639 	default:
640 		fth_io_write_and_flush((FTH) port, str);
641 		break;
642 	}
643 
644 	return (len);
645 }
646 
647 static int
fth_basic_vprintf(void * port,int type,const char * fmt,va_list ap)648 fth_basic_vprintf(void *port, int type, const char *fmt, va_list ap)
649 {
650 	char           *str;
651 	int 		len;
652 
653 	str = fth_vformat(fmt, ap);
654 	len = fth_basic_printf(port, type, str);
655 	FTH_FREE(str);
656 	return (len);
657 }
658 
659 /*
660  * Writes to Ficl output!
661  */
662 int
fth_print(const char * str)663 fth_print(const char *str)
664 {
665 	return (fth_basic_printf(FTH_FICL_VM(), PORT_FICLOUT, str));
666 }
667 
668 /*
669  * Writes to Ficl error output!
670  */
671 int
fth_error(const char * str)672 fth_error(const char *str)
673 {
674 	return (fth_basic_printf(FTH_FICL_VM(), PORT_FICLERR, str));
675 }
676 
677 /*
678  * Writes to Ficl output!
679  * Use fth_fprintf(stdout, ...) for explicit stdout.
680  */
681 int
fth_printf(const char * fmt,...)682 fth_printf(const char *fmt,...)
683 {
684 	int 		len;
685 	va_list 	ap;
686 
687 	va_start(ap, fmt);
688 	len = fth_vprintf(fmt, ap);
689 	va_end(ap);
690 	return (len);
691 }
692 
693 /*-
694  * Writes to Ficl output!
695  * Use fth_vfprintf(stdout, ...) for explicit stdout.
696  */
697 int
fth_vprintf(const char * fmt,va_list ap)698 fth_vprintf(const char *fmt, va_list ap)
699 {
700 	return (fth_basic_vprintf(FTH_FICL_VM(), PORT_FICLOUT, fmt, ap));
701 }
702 
703 /*-
704  * Writes to Ficl error output!
705  * Use fth_fprintf(stderr, ...) for explicit stderr.
706  */
707 int
fth_errorf(const char * fmt,...)708 fth_errorf(const char *fmt,...)
709 {
710 	int 		len;
711 	va_list 	ap;
712 
713 	va_start(ap, fmt);
714 	len = fth_verrorf(fmt, ap);
715 	va_end(ap);
716 	return (len);
717 }
718 
719 /*-
720  * Writes to Ficl error output!
721  * Use fth_vfprintf(stderr, ...) for explicit stderr.
722  */
723 int
fth_verrorf(const char * fmt,va_list ap)724 fth_verrorf(const char *fmt, va_list ap)
725 {
726 	return (fth_basic_vprintf(FTH_FICL_VM(), PORT_FICLERR, fmt, ap));
727 }
728 
729 /*
730  * Writes to FILE pointer.
731  */
732 int
fth_fprintf(FILE * fp,const char * fmt,...)733 fth_fprintf(FILE *fp, const char *fmt,...)
734 {
735 	int 		len;
736 	va_list 	ap;
737 
738 	va_start(ap, fmt);
739 	len = fth_vfprintf(fp, fmt, ap);
740 	va_end(ap);
741 	return (len);
742 }
743 
744 /*
745  * Writes to FILE pointer.
746  */
747 int
fth_vfprintf(FILE * fp,const char * fmt,va_list ap)748 fth_vfprintf(FILE *fp, const char *fmt, va_list ap)
749 {
750 	return (fth_basic_vprintf(fp, PORT_FILE, fmt, ap));
751 }
752 
753 /*
754  * Writes to IO object (io.c, port.c).
755  */
756 int
fth_ioprintf(FTH io,const char * fmt,...)757 fth_ioprintf(FTH io, const char *fmt,...)
758 {
759 	int 		len;
760 	va_list 	ap;
761 
762 	va_start(ap, fmt);
763 	len = fth_vioprintf(io, fmt, ap);
764 	va_end(ap);
765 	return (len);
766 }
767 
768 /*
769  * Writes to IO object (io.c, port.c).
770  */
771 int
fth_vioprintf(FTH io,const char * fmt,va_list ap)772 fth_vioprintf(FTH io, const char *fmt, va_list ap)
773 {
774 	return (fth_basic_vprintf((void *) io, PORT_IO, fmt, ap));
775 }
776 
777 /*-
778  * Writes to PORT object (port.c).
779  * PORT can be #f for Ficl stdout.
780  */
781 int
fth_port_printf(FTH port,const char * fmt,...)782 fth_port_printf(FTH port, const char *fmt,...)
783 {
784 	int 		len;
785 	va_list 	ap;
786 
787 	va_start(ap, fmt);
788 	len = fth_port_vprintf(port, fmt, ap);
789 	va_end(ap);
790 	return (len);
791 }
792 
793 /*-
794  * Writes to PORT object (port.c).
795  * PORT can be #f for Ficl stdout.
796  */
797 int
fth_port_vprintf(FTH port,const char * fmt,va_list ap)798 fth_port_vprintf(FTH port, const char *fmt, va_list ap)
799 {
800 	int 		len;
801 
802 	if (FTH_FALSE_P(port)) {
803 		len = fth_basic_vprintf(FTH_FICL_VM(), PORT_FICLOUT, fmt, ap);
804 		return (len);
805 	}
806 	if (FTH_IO_P(port)) {
807 		len = fth_basic_vprintf((void *) port, PORT_IO, fmt, ap);
808 		return (len);
809 	}
810 	FTH_ASSERT_ARGS(0, port, FTH_ARG1, "an io or #f");
811 	return (-1);
812 }
813 
814 /*
815  * Returned string must be freed!
816  */
817 char           *
fth_format(const char * fmt,...)818 fth_format(const char *fmt,...)
819 {
820 	char           *str;
821 	va_list 	ap;
822 
823 	va_start(ap, fmt);
824 	str = fth_vformat(fmt, ap);
825 	va_end(ap);
826 	return (str);
827 }
828 
829 /*
830  * Returned string must be freed!
831  */
832 char           *
fth_vformat(const char * fmt,va_list ap)833 fth_vformat(const char *fmt, va_list ap)
834 {
835 	char           *prev_xstring, *prev_xestring, *buf;
836 	size_t 		size;
837 
838 	prev_xstring = xstring;
839 	prev_xestring = xestring;
840 	size = 2048;		/* arbitrary */
841 	buf = NULL;
842 
843 	for (;;) {
844 		va_list 	copy;
845 
846 		buf = FTH_REALLOC(buf, size);
847 		xstring = buf;
848 		xestring = buf + size - 1;
849 		va_copy(copy, ap);
850 		doprnt(xaddchar, fmt, copy);
851 		va_end(copy);
852 
853 		if (xstring < xestring)
854 			break;
855 
856 		size *= 2;
857 	}
858 
859 	*xstring++ = '\0';
860 	size = (size_t) (xstring - buf);
861 	xstring = prev_xstring;
862 	xestring = prev_xestring;
863 	return (FTH_REALLOC(buf, size));
864 }
865 
866 /*
867  * Buffer must be big enough to hold data!
868  */
869 int
fth_sprintf(char * buffer,const char * fmt,...)870 fth_sprintf(char *buffer, const char *fmt,...)
871 {
872 	int 		len;
873 	va_list 	ap;
874 
875 	va_start(ap, fmt);
876 	len = fth_vsprintf(buffer, fmt, ap);
877 	va_end(ap);
878 	return (len);
879 }
880 
881 /*
882  * Buffer must be big enough to hold data!
883  */
884 int
fth_vsprintf(char * buffer,const char * fmt,va_list ap)885 fth_vsprintf(char *buffer, const char *fmt, va_list ap)
886 {
887 	if (buffer == NULL)
888 		return (-1);
889 	return (fth_vsnprintf(buffer, sizeof(buffer), fmt, ap));
890 }
891 
892 /*
893  * Writes at most size - 1 characters to buffer.
894  */
895 int
fth_snprintf(char * buffer,size_t size,const char * fmt,...)896 fth_snprintf(char *buffer, size_t size, const char *fmt,...)
897 {
898 	int 		len;
899 	va_list 	ap;
900 
901 	va_start(ap, fmt);
902 	len = fth_vsnprintf(buffer, size, fmt, ap);
903 	va_end(ap);
904 	return (len);
905 }
906 
907 /*
908  * Writes at most size - 1 characters to buffer.
909  */
910 int
fth_vsnprintf(char * str,size_t size,const char * fmt,va_list ap)911 fth_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
912 {
913 	char           *prev_xstring, *prev_xestring;
914 	int 		len;
915 
916 	prev_xstring = xstring;
917 	prev_xestring = xestring;
918 	xstring = str;
919 	xestring = str + size - 1;
920 	doprnt(xaddchar, fmt, ap);
921 	*xstring++ = '\0';
922 	len = (int) (xstring - str);
923 	xstring = prev_xstring;
924 	xestring = prev_xestring;
925 	return (len);
926 }
927 
928 /*
929  * Returned string must be freed!
930  */
931 int
fth_asprintf(char ** result,const char * fmt,...)932 fth_asprintf(char **result, const char *fmt,...)
933 {
934 	int 		len;
935 	va_list 	ap;
936 
937 	va_start(ap, fmt);
938 	len = fth_vasprintf(result, fmt, ap);
939 	va_end(ap);
940 	return (len);
941 }
942 
943 /*
944  * Returned string must be freed!
945  */
946 int
fth_vasprintf(char ** result,const char * fmt,va_list ap)947 fth_vasprintf(char **result, const char *fmt, va_list ap)
948 {
949 	*result = fth_vformat(fmt, ap);
950 	return ((int) fth_strlen(*result));
951 }
952 
953 /*
954  * Prints message wrapped in #<warning: ...> to Ficl error output.
955  */
956 int
fth_warning(const char * fmt,...)957 fth_warning(const char *fmt,...)
958 {
959 	int 		len;
960 	va_list 	ap;
961 
962 	len = fth_errorf("#<warning: ");
963 	va_start(ap, fmt);
964 	len += fth_verrorf(fmt, ap);
965 	va_end(ap);
966 	len += fth_errorf(">\n");
967 	return (len);
968 }
969 
970 /*
971  * Print message wrapped in #<DEBUG(C): ...> to stderr.
972  */
973 int
fth_debug(const char * fmt,...)974 fth_debug(const char *fmt,...)
975 {
976 	va_list 	ap;
977 	int 		len;
978 
979 	len = fth_fprintf(stderr, "#<DEBUG(C): ");
980 	va_start(ap, fmt);
981 	len += fth_vfprintf(stderr, fmt, ap);
982 	va_end(ap);
983 	len += fth_fprintf(stderr, ">\n");
984 	return (len);
985 }
986 
987 /*
988  * Doprnt for Fth source side with extra options %p, %s, %S, %m, %b.
989  */
990 
991 static FTH
format_argument_error(const char * format)992 format_argument_error(const char *format)
993 {
994 	fth_throw(FTH_ARGUMENT_ERROR,
995 	    "%s: too few arguments for format string \"%s\"",
996 	    RUNNING_WORD(), format);
997 	return (FTH_FALSE);
998 }
999 
1000 #define FTH_LONG_REF(X)							\
1001 	(FTH_FIXNUM_P(X) ?						\
1002 	(ficl2Integer)FIX_TO_INT(X) :					\
1003 	(FTH_LLONG_P(X) ? FTH_LONG_OBJECT(X) : (ficl2Integer)(X)))
1004 
1005 #define FTH_ULONG_REF(X)						\
1006 	(FTH_FIXNUM_P(X) ?						\
1007 	(ficl2Unsigned)FIX_TO_UNSIGNED(X) :				\
1008 	(FTH_ULLONG_P(X) ? FTH_ULONG_OBJECT(X) : (ficl2Unsigned)(X)))
1009 
1010 #define FTH_FLOAT_REF(X)						\
1011 	(((X) && FTH_FLOAT_T_P(X)) ? FTH_FLOAT_OBJECT(X) : (ficlFloat)(X))
1012 
1013 static void
string_doprnt(void (* addchar)(int),const char * sfmt,FTH ap)1014 string_doprnt(void (*addchar)(int), const char *sfmt, FTH ap)
1015 {
1016 	char           *bp;
1017 	const char     *f;
1018 	ficl2Integer 	l;
1019 	ficl2Unsigned 	u;
1020 
1021 	/* Octal: 3 bits per char */
1022 	char 		buf[(CHAR_BIT * sizeof(l) + 2) / 3 + 1];
1023 	ficlInteger 	i;
1024 	int 		fmt;
1025 	unsigned char 	pad = ' ';
1026 	int 		flush_left = 0, f_width = 0, prec = INF, hash = 0;
1027 	int 		sign = 0;
1028 	ficlInteger 	index = 0, len = fth_array_length(ap);
1029 	FTH 		val;
1030 	ficlFloat 	d;
1031 	char 		tb[DBL_BUF_LEN];
1032 	char 		tf[7];
1033 	int 		idx;
1034 	size_t 		fs = DBL_BUF_LEN;
1035 
1036 #define VA_ARG()							\
1037 	((index < len) ?						\
1038 	    fth_array_ref(ap, index++) : format_argument_error(sfmt))
1039 
1040 	f = sfmt;
1041 
1042 	for (; *f != '\0'; f++) {
1043 		if (*f != '%') {
1044 			/* then just out the char */
1045 			(*addchar) (*f);
1046 			continue;
1047 		}
1048 		f++;		/* skip the % */
1049 
1050 		if (*f == '#') {/* alternate form */
1051 			hash = 1;
1052 			f++;
1053 		}
1054 		if (*f == '-') {/* minus: flush left */
1055 			flush_left = 1;
1056 			f++;
1057 		}
1058 		if (*f == '0') {/* || *f == '.') XXX [ms] */
1059 			pad = '0';	/* padding with 0 rather than blank */
1060 			f++;
1061 		}
1062 		if (*f == '*') {/* field width */
1063 			val = VA_ARG();
1064 			f_width = (int) FTH_INT_REF(val);
1065 			f++;
1066 		} else if (isdigit((unsigned char) *f)) {
1067 			f_width = atoi(f);
1068 
1069 			while (isdigit((unsigned char) *f))
1070 				f++;	/* skip the digits */
1071 		}
1072 		if (*f == '.') {/* precision */
1073 			f++;
1074 
1075 			if (*f == '*') {
1076 				val = VA_ARG();
1077 				prec = (int) FTH_INT_REF(val);
1078 				f++;
1079 			} else if (isdigit((unsigned char) *f)) {
1080 				prec = atoi(f);
1081 				while (isdigit((unsigned char) *f))
1082 					f++;	/* skip the digits */
1083 			}
1084 		}
1085 		if (*f == 'l') {/* long format */
1086 			/* skip it */
1087 			f++;
1088 
1089 			if (*f == 'l') {
1090 				/* skip it */
1091 				f++;
1092 			}
1093 		}
1094 		if (*f == 'z') {/* size_t format */
1095 			/* skip it */
1096 			f++;
1097 		}
1098 		fmt = (unsigned char) *f;
1099 		bp = buf;
1100 
1101 		switch (fmt) {	/* do the format */
1102 		case 'c':
1103 			val = VA_ARG();
1104 			i = FTH_INT_REF(val);
1105 			f_width--;	/* adjust for one char [ms] */
1106 
1107 			if (!flush_left)
1108 				while (f_width-- > 0)
1109 					(*addchar) (pad);
1110 
1111 			(*addchar) ((int) i);
1112 
1113 			if (flush_left)
1114 				while (f_width-- > 0)
1115 					(*addchar) (' ');
1116 			break;
1117 		case 'd':
1118 			val = VA_ARG();
1119 			l = FTH_LONG_REF(val);
1120 
1121 			if (l < 0) {
1122 				sign = 1;
1123 				l = -l;
1124 			}
1125 
1126 			do {
1127 				*bp++ = (char) (l % 10 + '0');
1128 			} while ((l /= 10) > 0);
1129 
1130 			if (sign)
1131 				*bp++ = '-';
1132 
1133 			f_width = f_width - (int) (bp - buf);
1134 
1135 			if (!flush_left)
1136 				while (f_width-- > 0)
1137 					(*addchar) (pad);
1138 
1139 			for (bp--; bp >= buf; bp--)
1140 				(*addchar) (*bp);
1141 
1142 			if (flush_left)
1143 				while (f_width-- > 0)
1144 					(*addchar) (' ');
1145 			break;
1146 		case 'b':	/* [ms] %b added */
1147 		case 'B':
1148 		case 'o':
1149 		case 'O':
1150 		case 'x':
1151 		case 'X':
1152 		case 'u':
1153 		case 'U':
1154 			val = VA_ARG();
1155 			u = FTH_ULONG_REF(val);
1156 
1157 			switch (fmt) {
1158 			case 'u':	/* unsigned decimal */
1159 			case 'U':
1160 				do {
1161 					*bp++ = (char) (u % 10 + '0');
1162 				} while ((u /= 10) > 0);
1163 
1164 				goto out_d;
1165 				break;
1166 			case 'o':	/* octal */
1167 			case 'O':
1168 				do {
1169 					*bp++ = (char) (u % 8 + '0');
1170 				} while ((u /= 8) > 0);
1171 
1172 				if (hash)
1173 					*bp++ = '0';
1174 
1175 				goto out_d;
1176 				break;
1177 			case 'b':	/* binary added [ms] */
1178 			case 'B':
1179 				do {
1180 					*bp++ = (char) (u % 2 + '0');
1181 				} while ((u /= 2) > 0);
1182 
1183 				if (hash) {
1184 					*bp++ = (fmt == 'b') ? 'b' : 'B';
1185 					*bp++ = '0';
1186 				}
1187 				goto out_d;
1188 				break;
1189 			case 'x':	/* hex */
1190 			case 'X':
1191 				do {
1192 					char 		cn;
1193 
1194 					cn = (fmt == 'x') ? 'a' : 'A';
1195 					i = (int) (u % 16);
1196 
1197 					if (i < 10)
1198 						*bp++ = (char) (i + '0');
1199 					else
1200 						*bp++ = (char) (i - 10 + cn);
1201 				} while ((u /= 16) > 0);
1202 
1203 				if (hash) {
1204 					*bp++ = (fmt == 'x') ? 'x' : 'X';
1205 					*bp++ = '0';
1206 				}
1207 				goto out_d;
1208 				break;
1209 			}
1210 out_d:
1211 			f_width = f_width - (int) (bp - buf);
1212 
1213 			if (!flush_left)
1214 				while (f_width-- > 0)
1215 					(*addchar) (pad);
1216 
1217 			for (bp--; bp >= buf; bp--)
1218 				(*addchar) (*bp);
1219 
1220 			if (flush_left)
1221 				while (f_width-- > 0)
1222 					(*addchar) (' ');
1223 			break;
1224 			/* [ms] ficlFloat added */
1225 		case 'f':
1226 		case 'F':
1227 		case 'e':
1228 		case 'E':
1229 		case 'g':
1230 		case 'G':
1231 		case 'a':
1232 		case 'A':
1233 			val = VA_ARG();
1234 			d = FTH_FLOAT_REF(val);
1235 			idx = 0;
1236 			tf[idx++] = '%';
1237 
1238 			if (hash)
1239 				tf[idx++] = '#';
1240 
1241 			if (f_width == 0) {
1242 				if (prec == INF) {
1243 					tf[idx++] = *f;
1244 					tf[idx] = '\0';
1245 					snprintf(tb, fs, tf, d);
1246 				} else {
1247 					tf[idx++] = '.';
1248 					tf[idx++] = '*';
1249 					tf[idx++] = *f;
1250 					tf[idx] = '\0';
1251 					snprintf(tb, fs, tf, prec, d);
1252 				}
1253 			} else {
1254 				if (prec == INF) {
1255 					tf[idx++] = '*';
1256 					tf[idx++] = *f;
1257 					tf[idx] = '\0';
1258 					snprintf(tb, fs, tf, f_width, d);
1259 				} else {
1260 					tf[idx++] = '*';
1261 					tf[idx++] = '.';
1262 					tf[idx++] = '*';
1263 					tf[idx++] = *f;
1264 					tf[idx] = '\0';
1265 					snprintf(tb, fs, tf, f_width, prec, d);
1266 				}
1267 			}
1268 			bp = tb;
1269 
1270 			while (*bp != '\0')
1271 				(*addchar) (*bp++);
1272 			break;
1273 			/*-
1274 			 * FTH additions:
1275 			 *
1276 			 * %p -- object-inspect
1277 			 * %s -- object->string
1278 			 * %S -- object-dump
1279 			 * %m -- object-dump (old style)
1280 			 */
1281 		case 'p':
1282 		case 's':
1283 		case 'S':
1284 		case 'm':
1285 			val = VA_ARG();
1286 
1287 			switch (*f) {
1288 			case 'p':
1289 				bp = fth_to_c_inspect(val);
1290 				break;
1291 			case 's':
1292 				bp = fth_to_c_string(val);
1293 				break;
1294 			case 'S':
1295 			case 'm':
1296 			default:
1297 				bp = fth_to_c_dump(val);
1298 				break;
1299 			}
1300 
1301 			if (!bp)
1302 				bp = snil;
1303 
1304 			if (prec == INF)
1305 				f_width -= (int) strlen(bp);
1306 			else
1307 				f_width -= prec;
1308 
1309 			if (!flush_left)
1310 				while (f_width-- > 0)
1311 					(*addchar) (pad);
1312 
1313 			for (i = 0; *bp && i < prec; i++)
1314 				(*addchar) (*bp++);
1315 
1316 			if (flush_left)
1317 				while (f_width-- > 0)
1318 					(*addchar) (' ');
1319 			break;
1320 		case '%':
1321 			(*addchar) ('%');
1322 			break;
1323 		default:
1324 			break;
1325 		}		/* switch(fmt) */
1326 
1327 		flush_left = 0;
1328 		f_width = 0;
1329 		prec = INF;
1330 		hash = 0;
1331 		sign = 0;
1332 		pad = ' ';
1333 	}			/* for */
1334 }
1335 
1336 /*
1337  * Return formatted Fth string corresponding to C string 'fmt' and
1338  * Fth array 'args' containing as much arguments as 'fmt' requires.
1339  *
1340  * FTH fs, args;
1341  * args = fth_make_array_var(2, fth_make_int(10), fth_make_float(3.14));
1342  * fs = fth_string_vformat("print %d times %f\n", args);
1343  */
1344 FTH
fth_string_vformat(const char * fmt,FTH args)1345 fth_string_vformat(const char *fmt, FTH args)
1346 {
1347 	char           *prev_xstring, *prev_xestring, *buf;
1348 	size_t 		size;
1349 	FTH 		fs;
1350 
1351 	prev_xstring = xstring;
1352 	prev_xestring = xestring;
1353 	size = 2048;		/* arbitrary */
1354 	buf = NULL;
1355 
1356 	for (;;) {
1357 		buf = FTH_REALLOC(buf, size);
1358 		xstring = buf;
1359 		xestring = buf + size - 1;
1360 		string_doprnt(xaddchar, fmt, args);
1361 
1362 		if (xstring < xestring)
1363 			break;
1364 
1365 		size *= 2;
1366 	}
1367 
1368 	buf[xstring - buf] = '\0';
1369 	fs = fth_make_string(buf);
1370 	FTH_FREE(buf);
1371 	xstring = prev_xstring;
1372 	xestring = prev_xestring;
1373 	return (fs);
1374 }
1375 
1376 /*
1377  * printf.c ends here
1378  */
1379