Mercurial > hg > toybox
comparison toys/pending/printf.c @ 1645:2ffee259f519 draft
Another cleanup pass on printf.
author | Rob Landley <rob@landley.net> |
---|---|
date | Sat, 03 Jan 2015 20:31:41 -0600 |
parents | 54c092c3ee38 |
children | 35c035b7e3e0 |
comparison
equal
deleted
inserted
replaced
1644:492bd41f8b9a | 1645:2ffee259f519 |
---|---|
25 char *hv_w; | 25 char *hv_w; |
26 char *hv_p; | 26 char *hv_p; |
27 int encountered; | 27 int encountered; |
28 ) | 28 ) |
29 | 29 |
30 // Calculate width and precision from format string | 30 // Detect matching character (return true/valse) and advance pointer if match. |
31 static int get_w_p() | 31 static int eat(char **s, char c) |
32 { | 32 { |
33 char *ptr, *str = *toys.optargs; | 33 int x = (**s == c); |
34 | 34 |
35 errno = 0; | 35 if (x) ++*s; |
36 if (*str == '-') str++; | |
37 long value = strtol(str, &ptr, 10); | |
38 if (errno || (ptr && (*ptr != '\0' || ptr == str))) | |
39 perror_msg("Invalid num %s", *toys.optargs); | |
40 if (*--str == '-') return (int)(-1 * value); | |
41 | 36 |
42 return value; | 37 return x; |
43 } | 38 } |
44 | 39 |
45 // Add ll and L to Interger and floating point formats respectively. | 40 // Add ll and L to Interger and floating point formats respectively. |
46 static char *get_format(char *f) | 41 static char *get_format(char *f) |
47 { | 42 { |
99 } else if (*ptr == 'c') printf(fmt, arg ? *arg : 0); | 94 } else if (*ptr == 'c') printf(fmt, arg ? *arg : 0); |
100 | 95 |
101 if (format) free(format); | 96 if (format) free(format); |
102 } | 97 } |
103 | 98 |
104 // Handle the escape sequences. | 99 // Parse escape sequences. |
105 static int handle_slash(char **esc_val) | 100 static int handle_slash(char **esc_val) |
106 { | 101 { |
107 char *ptr = *esc_val; | 102 char *ptr = *esc_val; |
108 int esc_length = 0; | 103 int len = 1, base = 0; |
109 unsigned base = 0, num = 0, result = 0, count = 0; | 104 unsigned result = 0; |
110 | 105 |
111 /* | 106 if (*ptr == 'c') xexit(); |
112 * Hex escape sequence have only 1 or 2 digits, xHH. Oct escape sequence | |
113 * have 1,2 or 3 digits, xHHH. Leading "0" (\0HHH) we are ignoring. | |
114 */ | |
115 if (*ptr == 'x') { | |
116 ptr++; | |
117 esc_length++; | |
118 base = 16; | |
119 } else if (isdigit(*ptr)) base = 8; | |
120 | 107 |
121 while (esc_length < 3 && base) { | 108 // 0x12 hex escapes have 1-2 digits, \123 octal escapes have 1-3 digits. |
122 num = tolower(*ptr) - '0'; | 109 if (eat(&ptr, 'x')) base = 16; |
123 if (num > 10) num += ('0' - 'a' + 10); | 110 else if (*ptr >= '0' && *ptr <= '8') base = 8; |
111 len += (base-8)/8; | |
112 | |
113 // Not a hex or octal escape? (This catches trailing \) | |
114 if (!len) { | |
115 if (!(result = unescape(*ptr))) result = '\\'; | |
116 else ++*esc_val; | |
117 | |
118 return result; | |
119 } | |
120 | |
121 while (len) { | |
122 unsigned num = tolower(*ptr)-'0'; | |
123 | |
124 if (num > 10) num += '0'-'a'+10; | |
124 if (num >= base) { | 125 if (num >= base) { |
125 if (base == 16) { | 126 // Don't parse invalid hex value ala "\xvd", print it verbatim |
126 esc_length--; | 127 if (base == 16 && len == 2) { |
127 if (!esc_length) {// Invalid hex value eg. /xvd, print as it is /xvd | 128 ptr--; |
128 result = '\\'; | 129 result = '\\'; |
129 ptr--; | |
130 } | |
131 } | 130 } |
132 break; | 131 break; |
133 } | 132 } |
134 esc_length++; | 133 result = (result*base)+num; |
135 count = result = (count * base) + num; | |
136 ptr++; | 134 ptr++; |
137 } | 135 len--; |
138 if (base) ptr--; | |
139 else if (!(result = unescape(*ptr))) { | |
140 result = '\\'; | |
141 ptr--; // Let pointer pointing to / we will increment after returning. | |
142 } | 136 } |
143 *esc_val = ptr; | 137 *esc_val = ptr; |
138 | |
144 return (char)result; | 139 return (char)result; |
145 } | |
146 | |
147 // Handle "%b" option with '\' interpreted. | |
148 static void print_esc_str(char *str) | |
149 { | |
150 for (; *str; str++) { | |
151 if (*str == '\\') { | |
152 str++; | |
153 xputc(handle_slash(&str)); //print corresponding char | |
154 } else xputc(*str); | |
155 } | |
156 } | |
157 | |
158 // Parse the format string and print. | |
159 static void parse_print(char *format) | |
160 { | |
161 char *start, *p, *f = format; | |
162 int len = 0, width = 0, prec = 0; | |
163 | |
164 while (*f) { | |
165 if (*f == '%') { | |
166 start = f++; | |
167 len++; | |
168 if (*f == '%') { | |
169 xputc('%'); | |
170 break; | |
171 } | |
172 if (*f == 'b') { | |
173 if (*toys.optargs) { | |
174 print_esc_str(*toys.optargs++); | |
175 TT.encountered = 1; | |
176 } else print_esc_str(""); | |
177 break; | |
178 } | |
179 if (strchr("-+# ", *f)) f++, len++; | |
180 if (*f == '*') { | |
181 f++, len++; | |
182 if (*toys.optargs) { | |
183 width = get_w_p(); | |
184 toys.optargs++; | |
185 } | |
186 } else while (isdigit(*f)) f++, len++; | |
187 | |
188 if (*f == '.') { | |
189 f++, len++; | |
190 if (*f == '*') { | |
191 f++, len++; | |
192 if (*toys.optargs) { | |
193 prec = get_w_p(); | |
194 toys.optargs++; | |
195 } | |
196 } else { | |
197 while (isdigit(*f)) f++, len++; | |
198 } | |
199 } | |
200 if (!(p = strchr("diouxXfeEgGcs", *f))) | |
201 perror_exit("bad format@%ld", f-format); | |
202 else { | |
203 len++; | |
204 TT.hv_p = strstr(start, ".*"); | |
205 TT.hv_w = strchr(start, '*'); | |
206 //pitfall: handle diff b/w * and .* | |
207 if ((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL; | |
208 memcpy((p = xzalloc(len+1)), start, len); | |
209 print(p+len-1, width, prec); | |
210 if (*toys.optargs) toys.optargs++; | |
211 free(p); | |
212 p = NULL; | |
213 } | |
214 TT.encountered = 1; | |
215 } else if (*f == '\\' && f[1]) { | |
216 if (*++f == 'c') exit(0); //Got '\c', so no further output | |
217 xputc(handle_slash(&f)); | |
218 } else xputc(*f); | |
219 f++; | |
220 len = 0; | |
221 } | |
222 } | 140 } |
223 | 141 |
224 void printf_main(void) | 142 void printf_main(void) |
225 { | 143 { |
226 char *format = *toys.optargs++; | 144 char *format = *toys.optargs, **arg = toys.optargs+1, *f, *p; |
227 | 145 |
228 TT.encountered = 0; | 146 for (f = format; *f; f++) { |
229 parse_print(format); //printf acc. to format. | 147 if (eat(&f, '\\')) putchar(handle_slash(&f)); |
230 //Re-use FORMAT arg as necessary to convert all given ARGS. | 148 else if (*f != '%' || *++f == '%') xputc(*f); |
231 while (*toys.optargs && TT.encountered) parse_print(format); | 149 else if (*f == 'b') |
232 xflush(); | 150 for (p = *arg ? *(arg++) : ""; *p; p++) |
151 putchar(eat(&p, '\\') ? handle_slash(&p) : *p); | |
152 else { | |
153 char *start = f; | |
154 int wp[2], i; | |
155 | |
156 // todo: we currently ignore these? | |
157 if (strchr("-+# ", *f)) f++; | |
158 memset(wp, 0, 8); | |
159 for (i=0; i<2; i++) { | |
160 if (eat(&f, '*')) { | |
161 if (*arg) wp[i] = atolx(*(arg++)); | |
162 } else while (isdigit(*f)) f++; | |
163 if (!eat(&f, '.')) break; | |
164 } | |
165 if (!(p = strchr("diouxXfeEgGcs", *f))) | |
166 perror_exit("bad format@%ld", f-format); | |
167 else { | |
168 int len = f-start; | |
169 | |
170 TT.hv_p = strstr(start, ".*"); | |
171 TT.hv_w = strchr(start, '*'); | |
172 //pitfall: handle diff b/w * and .* | |
173 if ((TT.hv_w-1) == TT.hv_p) TT.hv_w = NULL; | |
174 memcpy((p = xzalloc(len+1)), start, len); | |
175 print(p+len-1, wp[0], wp[1]); | |
176 if (*arg) arg++; | |
177 free(p); | |
178 p = NULL; | |
179 } | |
180 TT.encountered = 1; | |
181 } | |
182 } | |
233 } | 183 } |