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