]> git.saurik.com Git - apple/shell_cmds.git/blob - printf/printf.c
5c82411c08d9bb5a2c51514e4906a614723b67cb
[apple/shell_cmds.git] / 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.37 2005/08/05 08:18:00 stefanf Exp $";
48 #endif /* not lint */
49
50 #include <sys/types.h>
51
52 #include <err.h>
53 #include <errno.h>
54 #include <inttypes.h>
55 #include <limits.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #ifdef SHELL
62 #define main printfcmd
63 #include "bltin/bltin.h"
64 #include "memalloc.h"
65 #else
66 #define warnx1(a, b, c) warnx(a)
67 #define warnx2(a, b, c) warnx(a, b)
68 #define warnx3(a, b, c) warnx(a, b, c)
69 #endif
70
71 #ifndef BUILTIN
72 #include <locale.h>
73 #endif
74
75 #define PF(f, func) do { \
76 char *b = NULL; \
77 if (havewidth) \
78 if (haveprec) \
79 (void)asprintf(&b, f, fieldwidth, precision, func); \
80 else \
81 (void)asprintf(&b, f, fieldwidth, func); \
82 else if (haveprec) \
83 (void)asprintf(&b, f, precision, func); \
84 else \
85 (void)asprintf(&b, f, func); \
86 if (b) { \
87 (void)fputs(b, stdout); \
88 free(b); \
89 } \
90 } while (0)
91
92 static int asciicode(void);
93 static char *doformat(char *, int *);
94 static int escape(char *, int, size_t *);
95 static int getchr(void);
96 static int getfloating(long double *, int);
97 static int getint(int *);
98 static int getnum(intmax_t *, uintmax_t *, int);
99 static const char
100 *getstr(void);
101 static char *mknum(char *, int);
102 static void usage(void);
103
104 static char **gargv;
105
106 int
107 #ifdef BUILTIN
108 progprintf(int argc, char *argv[])
109 #else
110 main(int argc, char *argv[])
111 #endif
112 {
113 size_t len;
114 int ch, chopped, end, rval;
115 char *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 fmt = format = *argv;
144 chopped = escape(fmt, 1, &len); /* backslash interpretation */
145 rval = end = 0;
146 gargv = ++argv;
147 for (;;) {
148 start = fmt;
149 while (fmt < format + len) {
150 if (fmt[0] == '%') {
151 fwrite(start, 1, fmt - start, stdout);
152 if (fmt[1] == '%') {
153 /* %% prints a % */
154 putchar('%');
155 fmt += 2;
156 } else {
157 fmt = doformat(fmt, &rval);
158 if (fmt == NULL)
159 return (1);
160 end = 0;
161 }
162 start = fmt;
163 } else
164 fmt++;
165 }
166
167 if (end == 1) {
168 warnx1("missing format character", NULL, NULL);
169 return (1);
170 }
171 fwrite(start, 1, fmt - start, stdout);
172 if (chopped || !*gargv)
173 return (rval);
174 /* Restart at the beginning of the format string. */
175 fmt = format;
176 end = 1;
177 }
178 /* NOTREACHED */
179 }
180
181
182 static char *
183 doformat(char *start, int *rval)
184 {
185 static const char skip1[] = "#'-+ 0";
186 static const char skip2[] = "0123456789";
187 char *fmt;
188 int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
189 char convch, nextch;
190
191 fmt = start + 1;
192 /* skip to field width */
193 fmt += strspn(fmt, skip1);
194 if (*fmt == '*') {
195 if (getint(&fieldwidth))
196 return (NULL);
197 havewidth = 1;
198 ++fmt;
199 } else {
200 havewidth = 0;
201
202 /* skip to possible '.', get following precision */
203 fmt += strspn(fmt, skip2);
204 }
205 if (*fmt == '.') {
206 /* precision present? */
207 ++fmt;
208 if (*fmt == '*') {
209 if (getint(&precision))
210 return (NULL);
211 haveprec = 1;
212 ++fmt;
213 } else {
214 haveprec = 0;
215
216 /* skip to conversion char */
217 fmt += strspn(fmt, skip2);
218 }
219 } else
220 haveprec = 0;
221 if (!*fmt) {
222 warnx1("missing format character", NULL, NULL);
223 return (NULL);
224 }
225
226 /*
227 * Look for a length modifier. POSIX doesn't have these, so
228 * we only support them for floating-point conversions, which
229 * are extensions. This is useful because the L modifier can
230 * be used to gain extra range and precision, while omitting
231 * it is more likely to produce consistent results on different
232 * architectures. This is not so important for integers
233 * because overflow is the only bad thing that can happen to
234 * them, but consider the command printf %a 1.1
235 */
236 if (*fmt == 'L') {
237 mod_ldbl = 1;
238 fmt++;
239 if (!strchr("aAeEfFgG", *fmt)) {
240 warnx2("bad modifier L for %%%c", *fmt, NULL);
241 return (NULL);
242 }
243 } else {
244 mod_ldbl = 0;
245 }
246
247 convch = *fmt;
248 nextch = *++fmt;
249 *fmt = '\0';
250 switch (convch) {
251 case 'b': {
252 size_t len;
253 char *p;
254 int getout;
255
256 #ifdef SHELL
257 p = savestr(getstr());
258 #else
259 p = strdup(getstr());
260 #endif
261 if (p == NULL) {
262 warnx2("%s", strerror(ENOMEM), NULL);
263 return (NULL);
264 }
265 getout = escape(p, 0, &len);
266 *(fmt - 1) = 's';
267 PF(start, p);
268 *(fmt - 1) = 'b';
269 #ifdef SHELL
270 ckfree(p);
271 #else
272 free(p);
273 #endif
274 if (getout)
275 return (fmt);
276 break;
277 }
278 case 'c': {
279 char p;
280
281 p = getchr();
282 PF(start, p);
283 break;
284 }
285 case 's': {
286 const char *p;
287
288 p = getstr();
289 PF(start, p);
290 break;
291 }
292 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
293 char *f;
294 intmax_t val;
295 uintmax_t uval;
296 int signedconv;
297
298 signedconv = (convch == 'd' || convch == 'i');
299 if ((f = mknum(start, convch)) == NULL)
300 return (NULL);
301 if (getnum(&val, &uval, signedconv))
302 *rval = 1;
303 if (signedconv)
304 PF(f, val);
305 else
306 PF(f, uval);
307 break;
308 }
309 case 'e': case 'E':
310 case 'f': case 'F':
311 case 'g': case 'G':
312 case 'a': case 'A': {
313 long double p;
314
315 if (getfloating(&p, mod_ldbl))
316 *rval = 1;
317 if (mod_ldbl)
318 PF(start, p);
319 else
320 PF(start, (double)p);
321 break;
322 }
323 default:
324 warnx2("illegal format character %c", convch, NULL);
325 return (NULL);
326 }
327 *fmt = nextch;
328 return (fmt);
329 }
330
331 static char *
332 mknum(char *str, int ch)
333 {
334 static char *copy;
335 static size_t copy_size;
336 char *newcopy;
337 size_t len, newlen;
338
339 len = strlen(str) + 2;
340 if (len > copy_size) {
341 newlen = ((len + 1023) >> 10) << 10;
342 #ifdef SHELL
343 if ((newcopy = ckrealloc(copy, newlen)) == NULL)
344 #else
345 if ((newcopy = realloc(copy, newlen)) == NULL)
346 #endif
347 {
348 warnx2("%s", strerror(ENOMEM), NULL);
349 return (NULL);
350 }
351 copy = newcopy;
352 copy_size = newlen;
353 }
354
355 memmove(copy, str, len - 3);
356 copy[len - 3] = 'j';
357 copy[len - 2] = ch;
358 copy[len - 1] = '\0';
359 return (copy);
360 }
361
362 static int
363 escape(char *fmt, int percent, size_t *len)
364 {
365 char *save, *store;
366 int value, c;
367
368 for (save = store = fmt; (c = *fmt); ++fmt, ++store) {
369 if (c != '\\') {
370 *store = c;
371 continue;
372 }
373 switch (*++fmt) {
374 case '\0': /* EOS, user error */
375 *store = '\\';
376 *++store = '\0';
377 *len = store - save;
378 return (0);
379 case '\\': /* backslash */
380 case '\'': /* single quote */
381 *store = *fmt;
382 break;
383 case 'a': /* bell/alert */
384 *store = '\a';
385 break;
386 case 'b': /* backspace */
387 *store = '\b';
388 break;
389 case 'c':
390 *store = '\0';
391 *len = store - save;
392 return (1);
393 case 'f': /* form-feed */
394 *store = '\f';
395 break;
396 case 'n': /* newline */
397 *store = '\n';
398 break;
399 case 'r': /* carriage-return */
400 *store = '\r';
401 break;
402 case 't': /* horizontal tab */
403 *store = '\t';
404 break;
405 case 'v': /* vertical tab */
406 *store = '\v';
407 break;
408 /* octal constant */
409 case '0': case '1': case '2': case '3':
410 case '4': case '5': case '6': case '7':
411 for (c = *fmt == '0' ? 4 : 3, value = 0;
412 c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
413 value <<= 3;
414 value += *fmt - '0';
415 }
416 --fmt;
417 if (percent && value == '%') {
418 *store++ = '%';
419 *store = '%';
420 } else
421 *store = value;
422 break;
423 default:
424 *store = *fmt;
425 break;
426 }
427 }
428 *store = '\0';
429 *len = store - save;
430 return (0);
431 }
432
433 static int
434 getchr(void)
435 {
436 if (!*gargv)
437 return ('\0');
438 return ((int)**gargv++);
439 }
440
441 static const char *
442 getstr(void)
443 {
444 if (!*gargv)
445 return ("");
446 return (*gargv++);
447 }
448
449 static int
450 getint(int *ip)
451 {
452 intmax_t val;
453 uintmax_t uval;
454 int rval;
455
456 if (getnum(&val, &uval, 1))
457 return (1);
458 rval = 0;
459 if (val < INT_MIN || val > INT_MAX) {
460 warnx3("%s: %s", *gargv, strerror(ERANGE));
461 rval = 1;
462 }
463 *ip = (int)val;
464 return (rval);
465 }
466
467 static int
468 getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
469 {
470 char *ep;
471 int rval;
472
473 if (!*gargv) {
474 *ip = 0;
475 return (0);
476 }
477 if (**gargv == '"' || **gargv == '\'') {
478 if (signedconv)
479 *ip = asciicode();
480 else
481 *uip = asciicode();
482 return (0);
483 }
484 rval = 0;
485 errno = 0;
486 if (signedconv)
487 *ip = strtoimax(*gargv, &ep, 0);
488 else
489 *uip = strtoumax(*gargv, &ep, 0);
490 if (ep == *gargv) {
491 warnx2("%s: expected numeric value", *gargv, NULL);
492 rval = 1;
493 }
494 else if (*ep != '\0') {
495 warnx2("%s: not completely converted", *gargv, NULL);
496 rval = 1;
497 }
498 if (errno == ERANGE) {
499 warnx3("%s: %s", *gargv, strerror(ERANGE));
500 rval = 1;
501 }
502 ++gargv;
503 return (rval);
504 }
505
506 static int
507 getfloating(long double *dp, int mod_ldbl)
508 {
509 char *ep;
510 int rval;
511
512 if (!*gargv) {
513 *dp = 0.0;
514 return (0);
515 }
516 if (**gargv == '"' || **gargv == '\'') {
517 *dp = asciicode();
518 return (0);
519 }
520 rval = 0;
521 errno = 0;
522 if (mod_ldbl)
523 *dp = strtold(*gargv, &ep);
524 else
525 *dp = strtod(*gargv, &ep);
526 if (ep == *gargv) {
527 warnx2("%s: expected numeric value", *gargv, NULL);
528 rval = 1;
529 } else if (*ep != '\0') {
530 warnx2("%s: not completely converted", *gargv, NULL);
531 rval = 1;
532 }
533 if (errno == ERANGE) {
534 warnx3("%s: %s", *gargv, strerror(ERANGE));
535 rval = 1;
536 }
537 ++gargv;
538 return (rval);
539 }
540
541 static int
542 asciicode(void)
543 {
544 int ch;
545
546 ch = **gargv;
547 if (ch == '\'' || ch == '"')
548 ch = (*gargv)[1];
549 ++gargv;
550 return (ch);
551 }
552
553 static void
554 usage(void)
555 {
556 (void)fprintf(stderr, "usage: printf format [arguments ...]\n");
557 }