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