1 /* $OpenBSD: printf.c,v 1.17 2009/10/27 23:59:41 deraadt Exp $ */
2
3 /*-
4 * Copyright (c) 1989 The Regents of the University of California.
5 * 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.
15 * 3. 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 */
31
32 #ifdef MKSH_PRINTF_BUILTIN
33 /* MirBSD Korn Shell */
34 #include "sh.h"
35 #else
36 /* stand-alone executable */
37 #include <sys/types.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <locale.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #define vstrchr strchr
48 #endif
49
50 __RCSID("$MirOS: src/usr.bin/printf/printf.c,v 1.20 2012/12/22 22:19:27 tg Exp $");
51
52 static int print_escape_str(const char *);
53 static int print_escape(const char *);
54
55 static int getchr(void);
56 #ifndef MKSH_PRINTF_BUILTIN
57 static double getdouble(void);
58 #endif
59 static int getinteger(void);
60 static long getlong(void);
61 static unsigned long getulong(void);
62 static const char *getstr(void);
63 static char *mklong(const char *, int);
64 static void check_conversion(const char *, const char *);
65 static int usage(void);
66 static int real_main(char *, const char *[]);
67
68 static int rval;
69 static const char **gargv;
70
71 #ifdef MKSH_PRINTF_BUILTIN
72 static int s_get(void);
73 static void s_put(int);
74 static void s_prt(int);
75
76 static const char *s_ptr;
77
78 #define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
79 #endif
80
81 #define isodigit(c) ((c) >= '0' && (c) <= '7')
82 #define octtobin(c) ((c) - '0')
83 #define hextobin(c) ((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
84
85 #define PF(f, func) do { \
86 if (fieldwidth) \
87 if (precision) \
88 uprintf(f, fieldwidth, precision, func); \
89 else \
90 uprintf(f, fieldwidth, func); \
91 else if (precision) \
92 uprintf(f, precision, func); \
93 else \
94 uprintf(f, func); \
95 } while (/* CONSTCOND */ 0)
96
97 #ifndef vstrchr
98 #define vstrchr strchr
99 #endif
100
101 #ifndef cstrerror
102 #define cstrerror(e) ((const char *)strerror(e))
103 #endif
104
105 #ifdef MKSH_PRINTF_BUILTIN
106 #define ufprintf shf_fprintf
107 #define uprintf shprintf
108 #define uputc(c) shf_putchar((c), shl_stdout)
109 #define ustderr shl_out
110 #define uwarnx warningf
111 #define UWARNX false,
112
113 int
c_printf(const char ** wp)114 c_printf(const char **wp)
115 {
116 int rv;
117 const char *old_kshname;
118 char *fmt;
119 uint8_t old_utfmode;
120
121 old_kshname = kshname;
122 kshname = wp[0];
123 ++wp;
124 if (wp[0] && !strcmp(wp[0], "--"))
125 ++wp;
126 if (wp[0]) {
127 strdupx(fmt, wp[0], ATEMP);
128 old_utfmode = UTFMODE;
129 UTFMODE = 0;
130 rv = real_main(fmt, wp);
131 UTFMODE = old_utfmode;
132 afree(fmt, ATEMP);
133 } else
134 rv = usage();
135 kshname = old_kshname;
136 return (rv);
137 }
138 #else
139 #define ufprintf fprintf
140 #define uprintf printf
141 #define uputc putchar
142 #define ustderr stderr
143 #define uwarnx warnx
144 #define UWARNX /* nothing */
145
146 int
main(int argc,char * argv[])147 main(int argc, char *argv[])
148 {
149 setlocale(LC_ALL, "");
150
151 if (argc > 1 && !strcmp(argv[1], "--")) {
152 --argc;
153 ++argv;
154 }
155
156 ++argv;
157 return (argc < 2 ? usage() : real_main(argv[0], (const char **)argv));
158 }
159 #endif
160
161 static int
real_main(char * format,const char * argv[])162 real_main(char *format, const char *argv[])
163 {
164 char *fmt, *start;
165 int fieldwidth, precision;
166 char convch, nextch;
167
168 gargv = ++argv;
169
170 #define SKIP1 "#-+ 0"
171 #define SKIP2 "0123456789"
172 do {
173 /*
174 * Basic algorithm is to scan the format string for conversion
175 * specifications -- once one is found, find out if the field
176 * width or precision is a '*'; if it is, gather up value.
177 * Note, format strings are reused as necessary to use up the
178 * provided arguments, arguments of zero/null string are
179 * provided to use up the format string.
180 */
181
182 /* find next format specification */
183 for (fmt = format; *fmt; fmt++) {
184 switch (*fmt) {
185 case '%':
186 start = fmt++;
187
188 if (*fmt == '%') {
189 uputc('%');
190 break;
191 } else if (*fmt == 'b') {
192 const char *p = getstr();
193 if (print_escape_str(p)) {
194 return (rval);
195 }
196 break;
197 } else if (!*fmt) {
198 goto synerr;
199 }
200
201 /* skip to field width */
202 for (; vstrchr(SKIP1, *fmt); ++fmt)
203 ;
204 if (*fmt == '*') {
205 ++fmt;
206 fieldwidth = getinteger();
207 } else if (!*fmt) {
208 goto synerr;
209 } else
210 fieldwidth = 0;
211
212 /* skip to field precision */
213 for (; vstrchr(SKIP2, *fmt); ++fmt)
214 ;
215 precision = 0;
216 if (*fmt == '.') {
217 ++fmt;
218 if (*fmt == '*') {
219 ++fmt;
220 precision = getinteger();
221 }
222 if (!*fmt)
223 goto synerr;
224 for (; vstrchr(SKIP2, *fmt); ++fmt)
225 ;
226 }
227
228 if (!*fmt) {
229 synerr:
230 uwarnx(UWARNX "missing format character");
231 return (1);
232 }
233
234 convch = *fmt;
235 nextch = *(fmt + 1);
236 *(fmt + 1) = '\0';
237 switch(convch) {
238 case 'c': {
239 char p = getchr();
240 PF(start, p);
241 break;
242 }
243 case 's': {
244 const char *p = getstr();
245 PF(start, p);
246 break;
247 }
248 case 'd':
249 case 'i': {
250 long p;
251 char *f = mklong(start, convch);
252 if (!f) {
253 uwarnx(UWARNX "out of memory");
254 return (1);
255 }
256 p = getlong();
257 PF(f, p);
258 break;
259 }
260 case 'o':
261 case 'u':
262 case 'x':
263 case 'X': {
264 unsigned long p;
265 char *f = mklong(start, convch);
266 if (!f) {
267 uwarnx(UWARNX "out of memory");
268 return (1);
269 }
270 p = getulong();
271 PF(f, p);
272 break;
273 }
274 #ifndef MKSH_PRINTF_BUILTIN
275 #if 0
276 case 'a':
277 case 'A':
278 #endif
279 case 'e':
280 case 'E':
281 case 'f':
282 case 'F':
283 case 'g':
284 case 'G': {
285 double p = getdouble();
286 PF(start, p);
287 break;
288 }
289 #endif
290
291 default:
292 uwarnx(UWARNX "%s: invalid directive", start);
293 return (1);
294 }
295 *(fmt + 1) = nextch;
296 break;
297
298 case '\\':
299 fmt += print_escape(fmt);
300 break;
301
302 default:
303 uputc(*fmt);
304 break;
305 }
306 }
307 } while (gargv > argv && *gargv);
308
309 return (rval);
310 }
311
312
313 /*
314 * Print SysV echo(1) style escape string
315 * Halts processing string and returns 1 if a \c escape is encountered.
316 */
317 static int
print_escape_str(const char * str)318 print_escape_str(const char *str)
319 {
320 #ifndef MKSH_PRINTF_BUILTIN
321 int value;
322 #endif
323 int c;
324
325 while (*str) {
326 if (*str == '\\') {
327 #ifdef MKSH_PRINTF_BUILTIN
328 s_ptr = str + 1;
329 c = unbksl(false, s_get, s_put);
330 str = s_ptr;
331 if (c == -1) {
332 if ((c = *str++) == 'c')
333 return (1);
334 else if (!c)
335 --str;
336 uputc(c);
337 uwarnx(UWARNX
338 "unknown escape sequence `\\%c'", c);
339 rval = 1;
340 } else
341 s_prt(c);
342 continue;
343 #else
344 str++;
345 /*
346 * %b string octal constants are not like those in C.
347 * They start with a \0, and are followed by 0, 1, 2,
348 * or 3 octal digits.
349 */
350 if (*str == '0') {
351 str++;
352 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
353 value <<= 3;
354 value += octtobin(*str);
355 }
356 uputc(value);
357 str--;
358 } else if (*str == 'c') {
359 return (1);
360 } else {
361 str--;
362 str += print_escape(str);
363 }
364 #endif
365 } else {
366 uputc(*str);
367 }
368 str++;
369 }
370
371 return (0);
372 }
373
374 /*
375 * Print "standard" escape characters
376 */
377 static int
print_escape(const char * str)378 print_escape(const char *str)
379 {
380 #ifdef MKSH_PRINTF_BUILTIN
381 int c;
382
383 s_ptr = str + 1;
384 c = unbksl(true, s_get, s_put);
385 if (c == -1) {
386 s_ptr = str + 1;
387 switch ((c = *s_ptr++)) {
388 case '\\':
389 case '\'':
390 case '"':
391 break;
392 case '\0':
393 --s_ptr;
394 /* FALLTHROUGH */
395 default:
396 uwarnx(UWARNX "unknown escape sequence `\\%c'", c);
397 rval = 1;
398 }
399 }
400 s_prt(c);
401 return (s_ptr - str - 1);
402 #else
403 const char *start = str;
404 int value;
405 int c;
406
407 str++;
408
409 switch (*str) {
410 case '0': case '1': case '2': case '3':
411 case '4': case '5': case '6': case '7':
412 for (c = 3, value = 0; c-- && isodigit(*str); str++) {
413 value <<= 3;
414 value += octtobin(*str);
415 }
416 uputc(value);
417 return (str - start - 1);
418 /* NOTREACHED */
419
420 case 'x':
421 str++;
422 for (value = 0; isxdigit(*str); str++) {
423 value <<= 4;
424 value += hextobin(*str);
425 }
426 if (value > UCHAR_MAX) {
427 uwarnx(UWARNX "escape sequence out of range for character");
428 rval = 1;
429 }
430 uputc(value);
431 return (str - start - 1);
432 /* NOTREACHED */
433
434 case '\\': /* backslash */
435 uputc('\\');
436 break;
437
438 case '\'': /* single quote */
439 uputc('\'');
440 break;
441
442 case '"': /* double quote */
443 uputc('"');
444 break;
445
446 case 'a': /* alert */
447 uputc('\a');
448 break;
449
450 case 'b': /* backspace */
451 uputc('\b');
452 break;
453
454 case 'e': /* escape */
455 uputc(033);
456 break;
457
458 case 'f': /* form-feed */
459 uputc('\f');
460 break;
461
462 case 'n': /* newline */
463 uputc('\n');
464 break;
465
466 case 'r': /* carriage-return */
467 uputc('\r');
468 break;
469
470 case 't': /* tab */
471 uputc('\t');
472 break;
473
474 case 'v': /* vertical-tab */
475 uputc('\v');
476 break;
477
478 default:
479 uputc(*str);
480 uwarnx(UWARNX "unknown escape sequence `\\%c'", *str);
481 rval = 1;
482 }
483
484 return (1);
485 #endif
486 }
487
488 static char *
mklong(const char * str,int ch)489 mklong(const char *str, int ch)
490 {
491 static char *copy;
492 static size_t copysize;
493 size_t len;
494
495 len = strlen(str) + 2;
496 if (copysize < len) {
497 char *newcopy;
498
499 copysize = len + 256;
500
501 newcopy = realloc(copy, copysize);
502 if (newcopy == NULL) {
503 copysize = 0;
504 free(copy);
505 copy = NULL;
506 return (NULL);
507 }
508 copy = newcopy;
509 }
510 (void) memmove(copy, str, len - 3);
511 copy[len - 3] = 'l';
512 copy[len - 2] = ch;
513 copy[len - 1] = '\0';
514 return (copy);
515 }
516
517 static int
getchr(void)518 getchr(void)
519 {
520 if (!*gargv)
521 return ((int)'\0');
522 return ((int)**gargv++);
523 }
524
525 static const char *
getstr(void)526 getstr(void)
527 {
528 if (!*gargv)
529 return ("");
530 return (*gargv++);
531 }
532
533 static const char *number = "+-.0123456789";
534 static int
getinteger(void)535 getinteger(void)
536 {
537 if (!*gargv)
538 return (0);
539
540 if (**gargv && vstrchr(number, **gargv))
541 return (atoi(*gargv++));
542
543 return (0);
544 }
545
546 static long
getlong(void)547 getlong(void)
548 {
549 long val;
550 char *ep;
551
552 if (!*gargv)
553 return (0L);
554
555 if (**gargv == '\"' || **gargv == '\'')
556 return ((long)*((*gargv++)+1));
557
558 errno = 0;
559 val = strtol(*gargv, &ep, 0);
560 check_conversion(*gargv++, ep);
561 return (val);
562 }
563
564 static unsigned long
getulong(void)565 getulong(void)
566 {
567 unsigned long val;
568 char *ep;
569
570 if (!*gargv)
571 return (0UL);
572
573 if (**gargv == '\"' || **gargv == '\'')
574 return ((unsigned long) *((*gargv++)+1));
575
576 errno = 0;
577 val = strtoul(*gargv, &ep, 0);
578 check_conversion(*gargv++, ep);
579 return (val);
580 }
581
582 #ifndef MKSH_PRINTF_BUILTIN
583 static double
getdouble(void)584 getdouble(void)
585 {
586 double val;
587 char *ep;
588
589 if (!*gargv)
590 return (0.0);
591
592 if (**gargv == '\"' || **gargv == '\'')
593 return ((double)*((*gargv++)+1));
594
595 errno = 0;
596 val = strtod(*gargv, &ep);
597 check_conversion(*gargv++, ep);
598 return (val);
599 }
600 #endif
601
602 static void
check_conversion(const char * s,const char * ep)603 check_conversion(const char *s, const char *ep)
604 {
605 if (*ep) {
606 if (ep == s)
607 uwarnx(UWARNX "%s: expected numeric value", s);
608 else
609 uwarnx(UWARNX "%s: not completely converted", s);
610 rval = 1;
611 } else if (errno == ERANGE) {
612 uwarnx(UWARNX "%s: %s", s, cstrerror(ERANGE));
613 rval = 1;
614 }
615 }
616
617 static int
usage(void)618 usage(void)
619 {
620 ufprintf(ustderr, "usage: printf format [arg ...]\n");
621 return (1);
622 }
623
624 #ifdef MKSH_PRINTF_BUILTIN
625 static int
s_get(void)626 s_get(void)
627 {
628 return (*s_ptr++);
629 }
630
631 static void
s_put(int c MKSH_A_UNUSED)632 s_put(int c MKSH_A_UNUSED)
633 {
634 --s_ptr;
635 }
636
637 static void
s_prt(int c)638 s_prt(int c)
639 {
640 char ts[4];
641
642 if ((unsigned int)c < 0x100) {
643 ts[0] = c;
644 c = 1;
645 } else
646 c = (int)utf_wctomb(ts, c - 0x100);
647
648 shf_write(ts, c, shl_stdout);
649 }
650 #endif
651