1/* printf.c - Format and Print the data. 2 * 3 * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com> 4 * Copyright 2014 Kyungwan Han <asura321@gmail.com> 5 * 6 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html 7 * 8 * todo: *m$ ala printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec); 9 10USE_PRINTF(NEWTOY(printf, "<1?^", TOYFLAG_USR|TOYFLAG_BIN)) 11 12config PRINTF 13 bool "printf" 14 default y 15 help 16 usage: printf FORMAT [ARGUMENT...] 17 18 Format and print ARGUMENT(s) according to FORMAT, using C printf syntax 19 (% escapes for cdeEfgGiosuxX, \ escapes for abefnrtv0 or \OCTAL or \xHEX). 20*/ 21 22#define FOR_printf 23#include "toys.h" 24 25// Detect matching character (return true/false) and advance pointer if match. 26static int eat(char **s, char c) 27{ 28 int x = (**s == c); 29 30 if (x) ++*s; 31 32 return x; 33} 34 35// Parse escape sequences. 36static int handle_slash(char **esc_val, int posix) 37{ 38 char *ptr = *esc_val; 39 int len, base = 0; 40 unsigned result = 0, num; 41 42 if (*ptr == 'c') xexit(); 43 44 // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits. 45 if (eat(&ptr, 'x')) base = 16; 46 else { 47 if (posix && *ptr=='0') ptr++; 48 if (*ptr >= '0' && *ptr <= '7') base = 8; 49 } 50 len = (char []){0,3,2}[base/8]; 51 52 // Not a hex or octal escape? (This catches trailing \) 53 if (!len) { 54 if (!(result = unescape(*ptr))) result = '\\'; 55 else ++*esc_val; 56 57 return result; 58 } 59 60 while (len) { 61 num = tolower(*ptr) - '0'; 62 if (num >= 'a'-'0') num += '0'-'a'+10; 63 if (num >= base) { 64 // Don't parse invalid hex value ala "\xvd", print it verbatim 65 if (base == 16 && len == 2) { 66 ptr--; 67 result = '\\'; 68 } 69 break; 70 } 71 result = (result*base)+num; 72 ptr++; 73 len--; 74 } 75 *esc_val = ptr; 76 77 return result; 78} 79 80void printf_main(void) 81{ 82 char **arg = toys.optargs+1; 83 84 // Repeat format until arguments consumed 85 for (;;) { 86 int seen = 0; 87 char *f = *toys.optargs; 88 89 // Loop through characters in format 90 while (*f) { 91 if (eat(&f, '\\')) putchar(handle_slash(&f, 0)); 92 else if (!eat(&f, '%') || *f == '%') putchar(*f++); 93 94 // Handle %escape 95 else { 96 char c, *end = 0, *aa, *to = toybuf; 97 int wp[] = {0,-1}, i = 0; 98 99 // Parse width.precision between % and type indicator. 100 *to++ = '%'; 101 while (strchr("-+# '0", *f) && (to-toybuf)<10) *to++ = *f++; 102 for (;;) { 103 if (eat(&f, '*')) { 104 if (*arg) wp[i] = atolx(*arg++); 105 } else while (*f >= '0' && *f <= '9') wp[i] = (wp[i]*10)+(*f++)-'0'; 106 if (i++ || !eat(&f, '.')) break; 107 wp[1] = 0; 108 } 109 c = *f++; 110 seen = sprintf(to, "*.*%c", c);; 111 errno = 0; 112 aa = *arg ? *arg++ : ""; 113 114 // Output %esc using parsed format string 115 if (c == 'b') { 116 while (*aa) putchar(eat(&aa, '\\') ? handle_slash(&aa, 1) : *aa++); 117 118 continue; 119 } else if (c == 'c') printf(toybuf, wp[0], wp[1], *aa); 120 else if (c == 's') printf(toybuf, wp[0], wp[1], aa); 121 else if (strchr("diouxX", c)) { 122 long long ll; 123 124 if (*aa == '\'' || *aa == '"') ll = aa[1]; 125 else ll = strtoll(aa, &end, 0); 126 127 sprintf(to, "*.*ll%c", c); 128 printf(toybuf, wp[0], wp[1], ll); 129 } else if (strchr("feEgG", c)) { 130 long double ld = strtold(aa, &end); 131 132 sprintf(to, "*.*L%c", c); 133 printf(toybuf, wp[0], wp[1], ld); 134 } else error_exit("bad %%%c@%ld", c, (long)(f-*toys.optargs)); 135 136 if (end && (errno || *end)) perror_msg("bad %%%c %s", c, aa); 137 } 138 } 139 140 // Posix says to keep looping through format until we consume all args. 141 // This only works if the format actually consumed at least one arg. 142 if (!seen || !*arg) break; 143 } 144} 145