Initial import from FreeBSD RELENG_4:
[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
34 #if !defined(BUILTIN) && !defined(SHELL)
35 #ifndef lint
36 static char const copyright[] =
37 "@(#) Copyright (c) 1989, 1993\n\
38         The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40 #endif
41
42 #ifndef lint
43 #if 0
44 static char const sccsid[] = "@(#)printf.c      8.1 (Berkeley) 7/20/93";
45 #endif
46 static const char rcsid[] =
47   "$FreeBSD: src/usr.bin/printf/printf.c,v 1.12.6.6 2002/04/29 16:45:16 jmallett Exp $";
48 #endif /* not lint */
49
50 #include <sys/types.h>
51
52 #include <err.h>
53 #include <errno.h>
54 #include <limits.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 #ifdef SHELL
61 #define main printfcmd
62 #include "bltin/bltin.h"
63 #include "memalloc.h"
64 #else
65 #define warnx1(a, b, c)         warnx(a)
66 #define warnx2(a, b, c)         warnx(a, b)
67 #define warnx3(a, b, c)         warnx(a, b, c)
68 #endif
69
70 #ifndef BUILTIN
71 #include <locale.h>
72 #endif
73
74 #define PF(f, func) do { \
75         char *b = NULL; \
76         if (fieldwidth) \
77                 if (precision) \
78                         (void)asprintf(&b, f, fieldwidth, precision, func); \
79                 else \
80                         (void)asprintf(&b, f, fieldwidth, func); \
81         else if (precision) \
82                 (void)asprintf(&b, f, precision, func); \
83         else \
84                 (void)asprintf(&b, f, func); \
85         if (b) { \
86                 (void)fputs(b, stdout); \
87                 free(b); \
88         } \
89 } while (0)
90
91 static int       asciicode(void);
92 static int       escape(char *);
93 static int       getchr(void);
94 static int       getdouble(double *);
95 static int       getint(int *);
96 static int       getquads(quad_t *, u_quad_t *, int);
97 static const char
98                 *getstr(void);
99 static char     *mkquad(char *, int);
100 static void      usage(void);
101
102 static char **gargv;
103
104 int
105 #ifdef BUILTIN
106 progprintf(argc, argv)
107 #else
108 main(argc, argv)
109 #endif
110         int argc;
111         char *argv[];
112 {
113         static const char *skip1, *skip2;
114         int ch, chopped, end, fieldwidth, precision, rval;
115         char convch, nextch, *format, *fmt, *start;
116
117 #ifndef BUILTIN
118         (void) setlocale(LC_NUMERIC, "");
119 #endif
120         while ((ch = getopt(argc, argv, "")) != -1)
121                 switch (ch) {
122                 case '?':
123                 default:
124                         usage();
125                         return (1);
126                 }
127         argc -= optind;
128         argv += optind;
129
130         if (argc < 1) {
131                 usage();
132                 return (1);
133         }
134
135         /*
136          * Basic algorithm is to scan the format string for conversion
137          * specifications -- once one is found, find out if the field
138          * width or precision is a '*'; if it is, gather up value.  Note,
139          * format strings are reused as necessary to use up the provided
140          * arguments, arguments of zero/null string are provided to use
141          * up the format string.
142          */
143         skip1 = "#-+ 0";
144         skip2 = "0123456789";
145
146         chopped = escape(fmt = format = *argv); /* backslash interpretation */
147         rval = 0;
148         gargv = ++argv;
149         for (;;) {
150                 end = 0;
151                 /* find next format specification */
152 next:           for (start = fmt;; ++fmt) {
153                         if (!*fmt) {
154                                 /* avoid infinite loop */
155                                 if (chopped) {
156                                         (void)printf("%s", start);
157                                         return (rval);
158                                 }
159                                 if (end == 1) {
160                                         warnx1("missing format character",
161                                             NULL, NULL);
162                                         return (1);
163                                 }
164                                 end = 1;
165                                 if (fmt > start)
166                                         (void)printf("%s", start);
167                                 if (!*gargv)
168                                         return (rval);
169                                 fmt = format;
170                                 goto next;
171                         }
172                         /* %% prints a % */
173                         if (*fmt == '%') {
174                                 if (*++fmt != '%')
175                                         break;
176                                 *fmt++ = '\0';
177                                 (void)printf("%s", start);
178                                 goto next;
179                         }
180                 }
181
182                 /* skip to field width */
183                 for (; strchr(skip1, *fmt); ++fmt);
184                 if (*fmt == '*') {
185                         if (getint(&fieldwidth))
186                                 return (1);
187                         ++fmt;
188                 } else {
189                         fieldwidth = 0;
190
191                         /* skip to possible '.', get following precision */
192                         for (; strchr(skip2, *fmt); ++fmt);
193                 }
194                 if (*fmt == '.') {
195                         /* precision present? */
196                         ++fmt;
197                         if (*fmt == '*') {
198                                 if (getint(&precision))
199                                         return (1);
200                                 ++fmt;
201                         } else {
202                                 precision = 0;
203
204                                 /* skip to conversion char */
205                                 for (; strchr(skip2, *fmt); ++fmt);
206                         }
207                 } else
208                         precision = 0;
209                 if (!*fmt) {
210                         warnx1("missing format character", NULL, NULL);
211                         return (1);
212                 }
213
214                 convch = *fmt;
215                 nextch = *++fmt;
216                 *fmt = '\0';
217                 switch(convch) {
218                 case 'b': {
219                         char *p;
220                         int getout;
221
222 #ifdef SHELL
223                         p = savestr(getstr());
224 #else
225                         p = strdup(getstr());
226 #endif
227                         if (p == NULL) {
228                                 warnx2("%s", strerror(ENOMEM), NULL);
229                                 return (1);
230                         }
231                         getout = escape(p);
232                         *(fmt - 1) = 's';
233                         PF(start, p);
234                         *(fmt - 1) = 'b';
235 #ifdef SHELL
236                         ckfree(p);
237 #else
238                         free(p);
239 #endif
240                         if (getout)
241                                 return (rval);
242                         break;
243                 }
244                 case 'c': {
245                         char p;
246
247                         p = getchr();
248                         PF(start, p);
249                         break;
250                 }
251                 case 's': {
252                         const char *p;
253
254                         p = getstr();
255                         PF(start, p);
256                         break;
257                 }
258                 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
259                         char *f;
260                         quad_t val;
261                         u_quad_t uval;
262                         int signedconv;
263
264                         signedconv = (convch == 'd' || convch == 'i');
265                         if ((f = mkquad(start, convch)) == NULL)
266                                 return (1);
267                         if (getquads(&val, &uval, signedconv))
268                                 rval = 1;
269                         if (signedconv)
270                                 PF(f, val);
271                         else
272                                 PF(f, uval);
273                         break;
274                 }
275                 case 'e': case 'E': case 'f': case 'g': case 'G': {
276                         double p;
277
278                         if (getdouble(&p))
279                                 rval = 1;
280                         PF(start, p);
281                         break;
282                 }
283                 default:
284                         warnx2("illegal format character %c", convch, NULL);
285                         return (1);
286                 }
287                 *fmt = nextch;
288         }
289         /* NOTREACHED */
290 }
291
292 static char *
293 mkquad(str, ch)
294         char *str;
295         int ch;
296 {
297         static char *copy;
298         static size_t copy_size;
299         char *newcopy;
300         size_t len, newlen;
301
302         len = strlen(str) + 2;
303         if (len > copy_size) {
304                 newlen = ((len + 1023) >> 10) << 10;
305 #ifdef SHELL
306                 if ((newcopy = ckrealloc(copy, newlen)) == NULL)
307 #else
308                 if ((newcopy = realloc(copy, newlen)) == NULL)
309 #endif
310                 {
311                         warnx2("%s", strerror(ENOMEM), NULL);
312                         return (NULL);
313                 }
314                 copy = newcopy;
315                 copy_size = newlen;
316         }
317
318         memmove(copy, str, len - 3);
319         copy[len - 3] = 'q';
320         copy[len - 2] = ch;
321         copy[len - 1] = '\0';
322         return (copy);
323 }
324
325 static int
326 escape(fmt)
327         register char *fmt;
328 {
329         register char *store;
330         register int value, c;
331
332         for (store = fmt; (c = *fmt); ++fmt, ++store) {
333                 if (c != '\\') {
334                         *store = c;
335                         continue;
336                 }
337                 switch (*++fmt) {
338                 case '\0':              /* EOS, user error */
339                         *store = '\\';
340                         *++store = '\0';
341                         return (0);
342                 case '\\':              /* backslash */
343                 case '\'':              /* single quote */
344                         *store = *fmt;
345                         break;
346                 case 'a':               /* bell/alert */
347                         *store = '\7';
348                         break;
349                 case 'b':               /* backspace */
350                         *store = '\b';
351                         break;
352                 case 'c':
353                         *store = '\0';
354                         return (1);
355                 case 'f':               /* form-feed */
356                         *store = '\f';
357                         break;
358                 case 'n':               /* newline */
359                         *store = '\n';
360                         break;
361                 case 'r':               /* carriage-return */
362                         *store = '\r';
363                         break;
364                 case 't':               /* horizontal tab */
365                         *store = '\t';
366                         break;
367                 case 'v':               /* vertical tab */
368                         *store = '\13';
369                         break;
370                                         /* octal constant */
371                 case '0': case '1': case '2': case '3':
372                 case '4': case '5': case '6': case '7':
373                         for (c = *fmt == '0' ? 4 : 3, value = 0;
374                             c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
375                                 value <<= 3;
376                                 value += *fmt - '0';
377                         }
378                         --fmt;
379                         *store = value;
380                         break;
381                 default:
382                         *store = *fmt;
383                         break;
384                 }
385         }
386         *store = '\0';
387         return (0);
388 }
389
390 static int
391 getchr()
392 {
393         if (!*gargv)
394                 return ('\0');
395         return ((int)**gargv++);
396 }
397
398 static const char *
399 getstr()
400 {
401         if (!*gargv)
402                 return ("");
403         return (*gargv++);
404 }
405
406 static int
407 getint(ip)
408         int *ip;
409 {
410         quad_t val;
411         u_quad_t uval;
412         int rval;
413
414         if (getquads(&val, &uval, 1))
415                 return (1);
416         rval = 0;
417         if (val < INT_MIN || val > INT_MAX) {
418                 warnx3("%s: %s", *gargv, strerror(ERANGE));
419                 rval = 1;
420         }
421         *ip = (int)val;
422         return (rval);
423 }
424
425 static int
426 getquads(qp, uqp, signedconv)
427         quad_t *qp;
428         u_quad_t *uqp;
429         int signedconv;
430 {
431         char *ep;
432         int rval;
433
434         if (!*gargv) {
435                 *qp = 0;
436                 return (0);
437         }
438         if (**gargv == '"' || **gargv == '\'') {
439                 if (signedconv)
440                         *qp = asciicode();
441                 else
442                         *uqp = asciicode();
443                 return (0);
444         }
445         rval = 0;
446         errno = 0;
447         if (signedconv)
448                 *qp = strtoq(*gargv, &ep, 0);
449         else
450                 *uqp = strtouq(*gargv, &ep, 0);
451         if (ep == *gargv) {
452                 warnx2("%s: expected numeric value", *gargv, NULL);
453                 rval = 1;
454         }
455         else if (*ep != '\0') {
456                 warnx2("%s: not completely converted", *gargv, NULL);
457                 rval = 1;
458         }
459         if (errno == ERANGE) {
460                 warnx3("%s: %s", *gargv, strerror(ERANGE));
461                 rval = 1;
462         }
463         ++gargv;
464         return (rval);
465 }
466
467 static int
468 getdouble(dp)
469         double *dp;
470 {
471         char *ep;
472         int rval;
473
474         if (!*gargv)
475                 return (0);
476         if (**gargv == '"' || **gargv == '\'') {
477                 *dp = asciicode();
478                 return (0);
479         }
480         rval = 1;
481         errno = 0;
482         *dp = strtod(*gargv, &ep);
483         if (ep == *gargv) {
484                 warnx2("%s: expected numeric value", *gargv, NULL);
485                 rval = 1;
486         } else if (*ep != '\0') {
487                 warnx2("%s: not completely converted", *gargv, NULL);
488                 rval = 1;
489         }
490         if (errno == ERANGE) {
491                 warnx3("%s: %s", *gargv, strerror(ERANGE));
492                 rval = 1;
493         }
494         ++gargv;
495         return (rval);
496 }
497
498 static int
499 asciicode()
500 {
501         register int ch;
502
503         ch = **gargv;
504         if (ch == '\'' || ch == '"')
505                 ch = (*gargv)[1];
506         ++gargv;
507         return (ch);
508 }
509
510 static void
511 usage()
512 {
513         (void)fprintf(stderr, "usage: printf format [arg ...]\n");
514 }