Annotation of /trunk/mkinitrd-magellan/klibc/usr/dash/bltin/printf.c
Parent Directory | Revision Log
Revision 815 -
(hide annotations)
(download)
Fri Apr 24 18:32:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 9111 byte(s)
Fri Apr 24 18:32:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 9111 byte(s)
-updated to klibc-1.5.15
1 | niro | 532 | /* |
2 | * Copyright (c) 1989, 1993 | ||
3 | * The Regents of the University of California. All rights reserved. | ||
4 | * Copyright (c) 1997-2005 | ||
5 | * Herbert Xu <herbert@gondor.apana.org.au>. 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 | #include <sys/types.h> | ||
33 | |||
34 | #include <ctype.h> | ||
35 | #include <errno.h> | ||
36 | #include <inttypes.h> | ||
37 | #include <limits.h> | ||
38 | #include <stdarg.h> | ||
39 | #include <stdlib.h> | ||
40 | #include <string.h> | ||
41 | #include <unistd.h> | ||
42 | |||
43 | niro | 815 | static int conv_escape_str(char *); |
44 | niro | 532 | static char *conv_escape(char *, int *); |
45 | static int getchr(void); | ||
46 | static intmax_t getintmax(void); | ||
47 | static uintmax_t getuintmax(void); | ||
48 | static char *getstr(void); | ||
49 | static char *mklong(const char *, const char *); | ||
50 | static void check_conversion(const char *, const char *); | ||
51 | #ifdef HAVE_STRTOD | ||
52 | static double getdouble(void); | ||
53 | #endif | ||
54 | |||
55 | static int rval; | ||
56 | static char **gargv; | ||
57 | |||
58 | #define isodigit(c) ((c) >= '0' && (c) <= '7') | ||
59 | #define octtobin(c) ((c) - '0') | ||
60 | |||
61 | #include "bltin.h" | ||
62 | #include "system.h" | ||
63 | |||
64 | #define PF(f, func) { \ | ||
65 | switch ((char *)param - (char *)array) { \ | ||
66 | default: \ | ||
67 | (void)printf(f, array[0], array[1], func); \ | ||
68 | break; \ | ||
69 | case sizeof(*param): \ | ||
70 | (void)printf(f, array[0], func); \ | ||
71 | break; \ | ||
72 | case 0: \ | ||
73 | (void)printf(f, func); \ | ||
74 | break; \ | ||
75 | } \ | ||
76 | } | ||
77 | |||
78 | int printfcmd(int argc, char *argv[]) | ||
79 | { | ||
80 | char *fmt; | ||
81 | char *format; | ||
82 | int ch; | ||
83 | |||
84 | rval = 0; | ||
85 | |||
86 | nextopt(nullstr); | ||
87 | |||
88 | argv = argptr; | ||
89 | format = *argv; | ||
90 | |||
91 | if (!format) { | ||
92 | warnx("usage: printf format [arg ...]"); | ||
93 | goto err; | ||
94 | } | ||
95 | |||
96 | gargv = ++argv; | ||
97 | |||
98 | #define SKIP1 "#-+ 0" | ||
99 | #define SKIP2 "*0123456789" | ||
100 | do { | ||
101 | /* | ||
102 | * Basic algorithm is to scan the format string for conversion | ||
103 | * specifications -- once one is found, find out if the field | ||
104 | * width or precision is a '*'; if it is, gather up value. | ||
105 | * Note, format strings are reused as necessary to use up the | ||
106 | * provided arguments, arguments of zero/null string are | ||
107 | * provided to use up the format string. | ||
108 | */ | ||
109 | |||
110 | /* find next format specification */ | ||
111 | for (fmt = format; (ch = *fmt++) ;) { | ||
112 | char *start; | ||
113 | char nextch; | ||
114 | int array[2]; | ||
115 | int *param; | ||
116 | |||
117 | if (ch == '\\') { | ||
118 | int c_ch; | ||
119 | fmt = conv_escape(fmt, &c_ch); | ||
120 | ch = c_ch; | ||
121 | goto pc; | ||
122 | } | ||
123 | if (ch != '%' || (*fmt == '%' && (++fmt || 1))) { | ||
124 | pc: | ||
125 | putchar(ch); | ||
126 | continue; | ||
127 | } | ||
128 | |||
129 | /* Ok - we've found a format specification, | ||
130 | Save its address for a later printf(). */ | ||
131 | start = fmt - 1; | ||
132 | param = array; | ||
133 | |||
134 | /* skip to field width */ | ||
135 | fmt += strspn(fmt, SKIP1); | ||
136 | if (*fmt == '*') | ||
137 | *param++ = getintmax(); | ||
138 | |||
139 | /* skip to possible '.', get following precision */ | ||
140 | fmt += strspn(fmt, SKIP2); | ||
141 | if (*fmt == '.') | ||
142 | ++fmt; | ||
143 | if (*fmt == '*') | ||
144 | *param++ = getintmax(); | ||
145 | |||
146 | fmt += strspn(fmt, SKIP2); | ||
147 | |||
148 | ch = *fmt; | ||
149 | if (!ch) { | ||
150 | warnx("missing format character"); | ||
151 | goto err; | ||
152 | } | ||
153 | /* null terminate format string to we can use it | ||
154 | as an argument to printf. */ | ||
155 | nextch = fmt[1]; | ||
156 | fmt[1] = 0; | ||
157 | switch (ch) { | ||
158 | |||
159 | case 'b': { | ||
160 | niro | 815 | int done = conv_escape_str(getstr()); |
161 | char *p = stackblock(); | ||
162 | niro | 532 | *fmt = 's'; |
163 | PF(start, p); | ||
164 | /* escape if a \c was encountered */ | ||
165 | niro | 815 | if (done) |
166 | niro | 532 | goto out; |
167 | *fmt = 'b'; | ||
168 | break; | ||
169 | } | ||
170 | case 'c': { | ||
171 | int p = getchr(); | ||
172 | PF(start, p); | ||
173 | break; | ||
174 | } | ||
175 | case 's': { | ||
176 | char *p = getstr(); | ||
177 | PF(start, p); | ||
178 | break; | ||
179 | } | ||
180 | case 'd': | ||
181 | case 'i': { | ||
182 | intmax_t p = getintmax(); | ||
183 | char *f = mklong(start, fmt); | ||
184 | PF(f, p); | ||
185 | break; | ||
186 | } | ||
187 | case 'o': | ||
188 | case 'u': | ||
189 | case 'x': | ||
190 | case 'X': { | ||
191 | uintmax_t p = getuintmax(); | ||
192 | char *f = mklong(start, fmt); | ||
193 | PF(f, p); | ||
194 | break; | ||
195 | } | ||
196 | #ifdef HAVE_STRTOD | ||
197 | case 'e': | ||
198 | case 'E': | ||
199 | case 'f': | ||
200 | case 'g': | ||
201 | case 'G': { | ||
202 | double p = getdouble(); | ||
203 | PF(start, p); | ||
204 | break; | ||
205 | } | ||
206 | #endif | ||
207 | default: | ||
208 | warnx("%s: invalid directive", start); | ||
209 | goto err; | ||
210 | } | ||
211 | *++fmt = nextch; | ||
212 | } | ||
213 | } while (gargv != argv && *gargv); | ||
214 | |||
215 | out: | ||
216 | niro | 815 | return rval; |
217 | niro | 532 | err: |
218 | return 1; | ||
219 | } | ||
220 | |||
221 | |||
222 | /* | ||
223 | * Print SysV echo(1) style escape string | ||
224 | * Halts processing string if a \c escape is encountered. | ||
225 | */ | ||
226 | niro | 815 | static int |
227 | niro | 532 | conv_escape_str(char *str) |
228 | { | ||
229 | int ch; | ||
230 | char *cp; | ||
231 | |||
232 | /* convert string into a temporary buffer... */ | ||
233 | STARTSTACKSTR(cp); | ||
234 | |||
235 | do { | ||
236 | int c; | ||
237 | |||
238 | ch = *str++; | ||
239 | if (ch != '\\') | ||
240 | continue; | ||
241 | |||
242 | ch = *str++; | ||
243 | if (ch == 'c') { | ||
244 | /* \c as in SYSV echo - abort all processing.... */ | ||
245 | niro | 815 | ch = 0x100; |
246 | niro | 532 | continue; |
247 | } | ||
248 | |||
249 | /* | ||
250 | * %b string octal constants are not like those in C. | ||
251 | * They start with a \0, and are followed by 0, 1, 2, | ||
252 | * or 3 octal digits. | ||
253 | */ | ||
254 | if (ch == '0') { | ||
255 | unsigned char i; | ||
256 | i = 3; | ||
257 | ch = 0; | ||
258 | do { | ||
259 | unsigned k = octtobin(*str); | ||
260 | if (k > 7) | ||
261 | break; | ||
262 | str++; | ||
263 | ch <<= 3; | ||
264 | ch += k; | ||
265 | } while (--i); | ||
266 | continue; | ||
267 | } | ||
268 | |||
269 | /* Finally test for sequences valid in the format string */ | ||
270 | str = conv_escape(str - 1, &c); | ||
271 | ch = c; | ||
272 | niro | 815 | } while (STPUTC(ch, cp), (char)ch); |
273 | niro | 532 | |
274 | niro | 815 | return ch; |
275 | niro | 532 | } |
276 | |||
277 | /* | ||
278 | * Print "standard" escape characters | ||
279 | */ | ||
280 | static char * | ||
281 | conv_escape(char *str, int *conv_ch) | ||
282 | { | ||
283 | int value; | ||
284 | int ch; | ||
285 | |||
286 | ch = *str; | ||
287 | |||
288 | switch (ch) { | ||
289 | default: | ||
290 | case 0: | ||
291 | value = '\\'; | ||
292 | goto out; | ||
293 | |||
294 | case '0': case '1': case '2': case '3': | ||
295 | case '4': case '5': case '6': case '7': | ||
296 | ch = 3; | ||
297 | value = 0; | ||
298 | do { | ||
299 | value <<= 3; | ||
300 | value += octtobin(*str++); | ||
301 | } while (isodigit(*str) && --ch); | ||
302 | goto out; | ||
303 | |||
304 | case '\\': value = '\\'; break; /* backslash */ | ||
305 | case 'a': value = '\a'; break; /* alert */ | ||
306 | case 'b': value = '\b'; break; /* backspace */ | ||
307 | case 'f': value = '\f'; break; /* form-feed */ | ||
308 | case 'n': value = '\n'; break; /* newline */ | ||
309 | case 'r': value = '\r'; break; /* carriage-return */ | ||
310 | case 't': value = '\t'; break; /* tab */ | ||
311 | case 'v': value = '\v'; break; /* vertical-tab */ | ||
312 | } | ||
313 | |||
314 | str++; | ||
315 | out: | ||
316 | *conv_ch = value; | ||
317 | return str; | ||
318 | } | ||
319 | |||
320 | static char * | ||
321 | mklong(const char *str, const char *ch) | ||
322 | { | ||
323 | char *copy; | ||
324 | size_t len; | ||
325 | |||
326 | len = ch - str + 3; | ||
327 | STARTSTACKSTR(copy); | ||
328 | copy = makestrspace(len, copy); | ||
329 | memcpy(copy, str, len - 3); | ||
330 | copy[len - 3] = 'j'; | ||
331 | copy[len - 2] = *ch; | ||
332 | copy[len - 1] = '\0'; | ||
333 | return (copy); | ||
334 | } | ||
335 | |||
336 | static int | ||
337 | getchr(void) | ||
338 | { | ||
339 | int val = 0; | ||
340 | |||
341 | if (*gargv) | ||
342 | val = **gargv++; | ||
343 | return val; | ||
344 | } | ||
345 | |||
346 | static char * | ||
347 | getstr(void) | ||
348 | { | ||
349 | char *val = nullstr; | ||
350 | |||
351 | if (*gargv) | ||
352 | val = *gargv++; | ||
353 | return val; | ||
354 | } | ||
355 | |||
356 | static intmax_t | ||
357 | getintmax(void) | ||
358 | { | ||
359 | intmax_t val = 0; | ||
360 | char *cp, *ep; | ||
361 | |||
362 | cp = *gargv; | ||
363 | if (cp == NULL) | ||
364 | goto out; | ||
365 | gargv++; | ||
366 | |||
367 | val = (unsigned char) cp[1]; | ||
368 | if (*cp == '\"' || *cp == '\'') | ||
369 | goto out; | ||
370 | |||
371 | errno = 0; | ||
372 | val = strtoimax(cp, &ep, 0); | ||
373 | check_conversion(cp, ep); | ||
374 | out: | ||
375 | return val; | ||
376 | } | ||
377 | |||
378 | static uintmax_t | ||
379 | getuintmax(void) | ||
380 | { | ||
381 | uintmax_t val = 0; | ||
382 | char *cp, *ep; | ||
383 | |||
384 | cp = *gargv; | ||
385 | if (cp == NULL) | ||
386 | goto out; | ||
387 | gargv++; | ||
388 | |||
389 | val = (unsigned char) cp[1]; | ||
390 | if (*cp == '\"' || *cp == '\'') | ||
391 | goto out; | ||
392 | |||
393 | errno = 0; | ||
394 | val = strtoumax(cp, &ep, 0); | ||
395 | check_conversion(cp, ep); | ||
396 | out: | ||
397 | return val; | ||
398 | } | ||
399 | |||
400 | #ifdef HAVE_STRTOD | ||
401 | static double | ||
402 | getdouble(void) | ||
403 | { | ||
404 | double val; | ||
405 | char *cp, *ep; | ||
406 | |||
407 | cp = *gargv; | ||
408 | if (cp == NULL) | ||
409 | return 0; | ||
410 | gargv++; | ||
411 | |||
412 | if (*cp == '\"' || *cp == '\'') | ||
413 | return (unsigned char) cp[1]; | ||
414 | |||
415 | errno = 0; | ||
416 | val = strtod(cp, &ep); | ||
417 | check_conversion(cp, ep); | ||
418 | return val; | ||
419 | } | ||
420 | #endif | ||
421 | |||
422 | static void | ||
423 | check_conversion(const char *s, const char *ep) | ||
424 | { | ||
425 | if (*ep) { | ||
426 | if (ep == s) | ||
427 | warnx("%s: expected numeric value", s); | ||
428 | else | ||
429 | warnx("%s: not completely converted", s); | ||
430 | rval = 1; | ||
431 | } else if (errno == ERANGE) { | ||
432 | warnx("%s: %s", s, strerror(ERANGE)); | ||
433 | rval = 1; | ||
434 | } | ||
435 | } | ||
436 | |||
437 | int | ||
438 | echocmd(int argc, char **argv) | ||
439 | { | ||
440 | int nonl = 0; | ||
441 | struct output *outs = out1; | ||
442 | |||
443 | if (!*++argv) | ||
444 | goto end; | ||
445 | if (equal(*argv, "-n")) { | ||
446 | nonl = ~nonl; | ||
447 | if (!*++argv) | ||
448 | goto end; | ||
449 | } | ||
450 | |||
451 | do { | ||
452 | char c; | ||
453 | |||
454 | niro | 815 | nonl += conv_escape_str(*argv); |
455 | outstr(stackblock(), outs); | ||
456 | if (nonl > 0) | ||
457 | break; | ||
458 | niro | 532 | |
459 | c = ' '; | ||
460 | if (!*++argv) { | ||
461 | end: | ||
462 | if (nonl) { | ||
463 | break; | ||
464 | } | ||
465 | c = '\n'; | ||
466 | } | ||
467 | outc(c, outs); | ||
468 | } while (*argv); | ||
469 | return 0; | ||
470 | } |