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