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