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