Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/bltin/printf.c
Parent Directory | Revision Log
Revision 815 -
(show 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 | /* |
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 | static int conv_escape_str(char *); |
44 | 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 | int done = conv_escape_str(getstr()); |
161 | char *p = stackblock(); |
162 | *fmt = 's'; |
163 | PF(start, p); |
164 | /* escape if a \c was encountered */ |
165 | if (done) |
166 | 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 | return rval; |
217 | 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 | static int |
227 | 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 | ch = 0x100; |
246 | 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 | } while (STPUTC(ch, cp), (char)ch); |
273 | |
274 | return ch; |
275 | } |
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 | nonl += conv_escape_str(*argv); |
455 | outstr(stackblock(), outs); |
456 | if (nonl > 0) |
457 | break; |
458 | |
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 | } |