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