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