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