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