dsynth - Improve DynamicMaxWorkers
[dragonfly.git] / usr.bin / printf / printf.c
1 /*-
2  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
3  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
4  * Copyright (c) 1989, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * @(#) Copyright (c) 1989, 1993 The Regents of the University of California.  All rights reserved.
32  * @(#)printf.c 8.1 (Berkeley) 7/20/93
33  * $FreeBSD: head/usr.bin/printf/printf.c 279503 2015-03-01 21:46:55Z jilles $
34  */
35 /*
36  * Important: This file is used both as a standalone program /usr/bin/printf
37  * and as a builtin for /bin/sh (#define SHELL).
38  */
39
40 #include <sys/types.h>
41
42 #include <ctype.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <inttypes.h>
46 #include <limits.h>
47 #include <locale.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <wchar.h>
53
54 #ifdef SHELL
55 #define main printfcmd
56 #include "bltin/bltin.h"
57 #include "error.h"
58 #endif
59
60 #define PF(f, func) do {                                                \
61         char *b = NULL;                                                 \
62         if (havewidth)                                                  \
63                 if (haveprec)                                           \
64                         asprintf(&b, f, fieldwidth, precision, func);   \
65                 else                                                    \
66                         asprintf(&b, f, fieldwidth, func);              \
67         else if (haveprec)                                              \
68                 asprintf(&b, f, precision, func);                       \
69         else                                                            \
70                 asprintf(&b, f, func);                                  \
71         if (b) {                                                        \
72                 fputs(b, stdout);                                       \
73                 free(b);                                                \
74         }                                                               \
75 } while (0)
76
77 static int       asciicode(void);
78 static char     *printf_doformat(char *, int *);
79 static int       escape(char *, int, size_t *);
80 static int       getchr(void);
81 static int       getfloating(long double *, int);
82 static int       getint(int *);
83 static int       getnum(intmax_t *, uintmax_t *, int);
84 static const char
85                 *getstr(void);
86 static char     *mknum(char *, char);
87 static void      usage(void);
88
89 static const char digits[] = "0123456789";
90
91 static char end_fmt[1];
92
93 static int  myargc;
94 static char **myargv;
95 static char **gargv;
96 static char **maxargv;
97
98 int
99 main(int argc, char *argv[])
100 {
101         size_t len;
102         int end, rval;
103         char *format, *fmt, *start;
104
105 #ifndef SHELL
106         setlocale(LC_ALL, "");
107 #endif
108
109         /*
110          * We may not use getopt(3) because calling
111          * "printf -f%s oo" may not result in an invalid
112          * option error.
113          * However common usage and other implementations seem
114          * to indicate that we need to allow -- as a discardable
115          * option separator.
116          */
117         if (argc > 1 && strcmp(argv[1], "--") == 0) {
118                 argc--;
119                 argv++;
120         }
121
122         if (argc < 2) {
123                 usage();
124                 return (1);
125         }
126
127         argv++;
128
129 #ifdef SHELL
130         INTOFF;
131 #endif
132         /*
133          * Basic algorithm is to scan the format string for conversion
134          * specifications -- once one is found, find out if the field
135          * width or precision is a '*'; if it is, gather up value.  Note,
136          * format strings are reused as necessary to use up the provided
137          * arguments, arguments of zero/null string are provided to use
138          * up the format string.
139          */
140         fmt = format = *argv;
141         escape(fmt, 1, &len);           /* backslash interpretation */
142         rval = end = 0;
143         gargv = ++argv;
144
145         for (;;) {
146                 maxargv = gargv;
147
148                 myargv = gargv;
149                 for (myargc = 0; gargv[myargc]; myargc++)
150                         /* nop */;
151                 start = fmt;
152                 while (fmt < format + len) {
153                         if (fmt[0] == '%') {
154                                 fwrite(start, 1, fmt - start, stdout);
155                                 if (fmt[1] == '%') {
156                                         /* %% prints a % */
157                                         putchar('%');
158                                         fmt += 2;
159                                 } else {
160                                         fmt = printf_doformat(fmt, &rval);
161                                         if (fmt == NULL || fmt == end_fmt) {
162 #ifdef SHELL
163                                                 INTON;
164 #endif
165                                                 return (fmt == NULL ? 1 : rval);
166                                         }
167                                         end = 0;
168                                 }
169                                 start = fmt;
170                         } else
171                                 fmt++;
172                         if (gargv > maxargv)
173                                 maxargv = gargv;
174                 }
175                 gargv = maxargv;
176
177                 if (end == 1) {
178                         warnx("missing format character");
179 #ifdef SHELL
180                         INTON;
181 #endif
182                         return (1);
183                 }
184                 fwrite(start, 1, fmt - start, stdout);
185                 if (!*gargv) {
186 #ifdef SHELL
187                         INTON;
188 #endif
189                         return (rval);
190                 }
191                 /* Restart at the beginning of the format string. */
192                 fmt = format;
193                 end = 1;
194         }
195         /* NOTREACHED */
196 }
197
198
199 static char *
200 printf_doformat(char *fmt, int *rval)
201 {
202         static const char skip1[] = "#'-+ 0";
203         int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
204         char convch, nextch;
205         char start[strlen(fmt) + 1];
206         char **fargv;
207         char *dptr;
208         int l;
209
210         dptr = start;
211         *dptr++ = '%';
212         *dptr = 0;
213
214         fmt++;
215
216         /* look for "n$" field index specifier */
217         l = strspn(fmt, digits);
218         if ((l > 0) && (fmt[l] == '$')) {
219                 int idx = atoi(fmt);
220                 if (idx <= myargc) {
221                         gargv = &myargv[idx - 1];
222                 } else {
223                         gargv = &myargv[myargc];
224                 }
225                 if (gargv > maxargv)
226                         maxargv = gargv;
227                 fmt += l + 1;
228
229                 /* save format argument */
230                 fargv = gargv;
231         } else {
232                 fargv = NULL;
233         }
234
235         /* skip to field width */
236         while (*fmt && strchr(skip1, *fmt) != NULL) {
237                 *dptr++ = *fmt++;
238                 *dptr = 0;
239         }
240
241         if (*fmt == '*') {
242
243                 fmt++;
244                 l = strspn(fmt, digits);
245                 if ((l > 0) && (fmt[l] == '$')) {
246                         int idx = atoi(fmt);
247                         if (fargv == NULL) {
248                                 warnx("incomplete use of n$");
249                                 return (NULL);
250                         }
251                         if (idx <= myargc) {
252                                 gargv = &myargv[idx - 1];
253                         } else {
254                                 gargv = &myargv[myargc];
255                         }
256                         fmt += l + 1;
257                 } else if (fargv != NULL) {
258                         warnx("incomplete use of n$");
259                         return (NULL);
260                 }
261
262                 if (getint(&fieldwidth))
263                         return (NULL);
264                 if (gargv > maxargv)
265                         maxargv = gargv;
266                 havewidth = 1;
267
268                 *dptr++ = '*';
269                 *dptr = 0;
270         } else {
271                 havewidth = 0;
272
273                 /* skip to possible '.', get following precision */
274                 while (isdigit(*fmt)) {
275                         *dptr++ = *fmt++;
276                         *dptr = 0;
277                 }
278         }
279
280         if (*fmt == '.') {
281                 /* precision present? */
282                 fmt++;
283                 *dptr++ = '.';
284
285                 if (*fmt == '*') {
286
287                         fmt++;
288                         l = strspn(fmt, digits);
289                         if ((l > 0) && (fmt[l] == '$')) {
290                                 int idx = atoi(fmt);
291                                 if (fargv == NULL) {
292                                         warnx("incomplete use of n$");
293                                         return (NULL);
294                                 }
295                                 if (idx <= myargc) {
296                                         gargv = &myargv[idx - 1];
297                                 } else {
298                                         gargv = &myargv[myargc];
299                                 }
300                                 fmt += l + 1;
301                         } else if (fargv != NULL) {
302                                 warnx("incomplete use of n$");
303                                 return (NULL);
304                         }
305
306                         if (getint(&precision))
307                                 return (NULL);
308                         if (gargv > maxargv)
309                                 maxargv = gargv;
310                         haveprec = 1;
311                         *dptr++ = '*';
312                         *dptr = 0;
313                 } else {
314                         haveprec = 0;
315
316                         /* skip to conversion char */
317                         while (isdigit(*fmt)) {
318                                 *dptr++ = *fmt++;
319                                 *dptr = 0;
320                         }
321                 }
322         } else
323                 haveprec = 0;
324         if (!*fmt) {
325                 warnx("missing format character");
326                 return (NULL);
327         }
328         *dptr++ = *fmt;
329         *dptr = 0;
330
331         /*
332          * Look for a length modifier.  POSIX doesn't have these, so
333          * we only support them for floating-point conversions, which
334          * are extensions.  This is useful because the L modifier can
335          * be used to gain extra range and precision, while omitting
336          * it is more likely to produce consistent results on different
337          * architectures.  This is not so important for integers
338          * because overflow is the only bad thing that can happen to
339          * them, but consider the command  printf %a 1.1
340          */
341         if (*fmt == 'L') {
342                 mod_ldbl = 1;
343                 fmt++;
344                 if (!strchr("aAeEfFgG", *fmt)) {
345                         warnx("bad modifier L for %%%c", *fmt);
346                         return (NULL);
347                 }
348         } else {
349                 mod_ldbl = 0;
350         }
351
352         /* save the current arg offset, and set to the format arg */
353         if (fargv != NULL) {
354                 gargv = fargv;
355         }
356
357         convch = *fmt;
358         nextch = *++fmt;
359
360         *fmt = '\0';
361         switch (convch) {
362         case 'b': {
363                 size_t len;
364                 char *p;
365                 int getout;
366
367                 p = strdup(getstr());
368                 if (p == NULL) {
369                         warnx("%s", strerror(ENOMEM));
370                         return (NULL);
371                 }
372                 getout = escape(p, 0, &len);
373                 fputs(p, stdout);
374                 free(p);
375                 if (getout)
376                         return (end_fmt);
377                 break;
378         }
379         case 'c': {
380                 char p;
381
382                 p = getchr();
383                 PF(start, p);
384                 break;
385         }
386         case 's': {
387                 const char *p;
388
389                 p = getstr();
390                 PF(start, p);
391                 break;
392         }
393         case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
394                 char *f;
395                 intmax_t val;
396                 uintmax_t uval;
397                 int signedconv;
398
399                 signedconv = (convch == 'd' || convch == 'i');
400                 if ((f = mknum(start, convch)) == NULL)
401                         return (NULL);
402                 if (getnum(&val, &uval, signedconv))
403                         *rval = 1;
404                 if (signedconv)
405                         PF(f, val);
406                 else
407                         PF(f, uval);
408                 break;
409         }
410         case 'e': case 'E':
411         case 'f': case 'F':
412         case 'g': case 'G':
413         case 'a': case 'A': {
414                 long double p;
415
416                 if (getfloating(&p, mod_ldbl))
417                         *rval = 1;
418                 if (mod_ldbl)
419                         PF(start, p);
420                 else
421                         PF(start, (double)p);
422                 break;
423         }
424         default:
425                 warnx("illegal format character %c", convch);
426                 return (NULL);
427         }
428         *fmt = nextch;
429         /* return the gargv to the next element */
430         return (fmt);
431 }
432
433 static char *
434 mknum(char *str, char ch)
435 {
436         static char *copy;
437         static size_t copy_size;
438         char *newcopy;
439         size_t len, newlen;
440
441         len = strlen(str) + 2;
442         if (len > copy_size) {
443                 newlen = ((len + 1023) >> 10) << 10;
444                 if ((newcopy = realloc(copy, newlen)) == NULL) {
445                         warnx("%s", strerror(ENOMEM));
446                         return (NULL);
447                 }
448                 copy = newcopy;
449                 copy_size = newlen;
450         }
451
452         memmove(copy, str, len - 3);
453         copy[len - 3] = 'j';
454         copy[len - 2] = ch;
455         copy[len - 1] = '\0';
456         return (copy);
457 }
458
459 static int
460 escape(char *fmt, int percent, size_t *len)
461 {
462         char *save, *store, c;
463         int value;
464
465         for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) {
466                 if (c != '\\') {
467                         *store = c;
468                         continue;
469                 }
470                 switch (*++fmt) {
471                 case '\0':              /* EOS, user error */
472                         *store = '\\';
473                         *++store = '\0';
474                         *len = store - save;
475                         return (0);
476                 case '\\':              /* backslash */
477                 case '\'':              /* single quote */
478                         *store = *fmt;
479                         break;
480                 case 'a':               /* bell/alert */
481                         *store = '\a';
482                         break;
483                 case 'b':               /* backspace */
484                         *store = '\b';
485                         break;
486                 case 'c':
487                         if (!percent) {
488                                 *store = '\0';
489                                 *len = store - save;
490                                 return (1);
491                         }
492                         *store = 'c';
493                         break;
494                 case 'f':               /* form-feed */
495                         *store = '\f';
496                         break;
497                 case 'n':               /* newline */
498                         *store = '\n';
499                         break;
500                 case 'r':               /* carriage-return */
501                         *store = '\r';
502                         break;
503                 case 't':               /* horizontal tab */
504                         *store = '\t';
505                         break;
506                 case 'v':               /* vertical tab */
507                         *store = '\v';
508                         break;
509                                         /* octal constant */
510                 case '0': case '1': case '2': case '3':
511                 case '4': case '5': case '6': case '7':
512                         c = (!percent && *fmt == '0') ? 4 : 3;
513                         for (value = 0;
514                             c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
515                                 value <<= 3;
516                                 value += *fmt - '0';
517                         }
518                         --fmt;
519                         if (percent && value == '%') {
520                                 *store++ = '%';
521                                 *store = '%';
522                         } else
523                                 *store = (char)value;
524                         break;
525                 default:
526                         *store = *fmt;
527                         break;
528                 }
529         }
530         *store = '\0';
531         *len = store - save;
532         return (0);
533 }
534
535 static int
536 getchr(void)
537 {
538         if (!*gargv)
539                 return ('\0');
540         return ((int)**gargv++);
541 }
542
543 static const char *
544 getstr(void)
545 {
546         if (!*gargv)
547                 return ("");
548         return (*gargv++);
549 }
550
551 static int
552 getint(int *ip)
553 {
554         intmax_t val;
555         uintmax_t uval;
556         int rval;
557
558         if (getnum(&val, &uval, 1))
559                 return (1);
560         rval = 0;
561         if (val < INT_MIN || val > INT_MAX) {
562                 warnx("%s: %s", *gargv, strerror(ERANGE));
563                 rval = 1;
564         }
565         *ip = (int)val;
566         return (rval);
567 }
568
569 static int
570 getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
571 {
572         char *ep;
573         int rval;
574
575         if (!*gargv) {
576                 *ip = *uip = 0;
577                 return (0);
578         }
579         if (**gargv == '"' || **gargv == '\'') {
580                 if (signedconv)
581                         *ip = asciicode();
582                 else
583                         *uip = asciicode();
584                 return (0);
585         }
586         rval = 0;
587         errno = 0;
588         if (signedconv)
589                 *ip = strtoimax(*gargv, &ep, 0);
590         else
591                 *uip = strtoumax(*gargv, &ep, 0);
592         if (ep == *gargv) {
593                 warnx("%s: expected numeric value", *gargv);
594                 rval = 1;
595         }
596         else if (*ep != '\0') {
597                 warnx("%s: not completely converted", *gargv);
598                 rval = 1;
599         }
600         if (errno == ERANGE) {
601                 warnx("%s: %s", *gargv, strerror(ERANGE));
602                 rval = 1;
603         }
604         ++gargv;
605         return (rval);
606 }
607
608 static int
609 getfloating(long double *dp, int mod_ldbl)
610 {
611         char *ep;
612         int rval;
613
614         if (!*gargv) {
615                 *dp = 0.0;
616                 return (0);
617         }
618         if (**gargv == '"' || **gargv == '\'') {
619                 *dp = asciicode();
620                 return (0);
621         }
622         rval = 0;
623         errno = 0;
624         if (mod_ldbl)
625                 *dp = strtold(*gargv, &ep);
626         else
627                 *dp = strtod(*gargv, &ep);
628         if (ep == *gargv) {
629                 warnx("%s: expected numeric value", *gargv);
630                 rval = 1;
631         } else if (*ep != '\0') {
632                 warnx("%s: not completely converted", *gargv);
633                 rval = 1;
634         }
635         if (errno == ERANGE) {
636                 warnx("%s: %s", *gargv, strerror(ERANGE));
637                 rval = 1;
638         }
639         ++gargv;
640         return (rval);
641 }
642
643 static int
644 asciicode(void)
645 {
646         int ch;
647         wchar_t wch;
648         mbstate_t mbs;
649
650         ch = (unsigned char)**gargv;
651         if (ch == '\'' || ch == '"') {
652                 memset(&mbs, 0, sizeof(mbs));
653                 switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) {
654                 case (size_t)-2:
655                 case (size_t)-1:
656                         wch = (unsigned char)gargv[0][1];
657                         break;
658                 case 0:
659                         wch = 0;
660                         break;
661                 }
662                 ch = wch;
663         }
664         ++gargv;
665         return (ch);
666 }
667
668 static void
669 usage(void)
670 {
671         fprintf(stderr, "usage: printf format [arguments ...]\n");
672 }