Magellan Linux

Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/bltin/printf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 815 - (show annotations) (download)
Fri Apr 24 18:32:46 2009 UTC (15 years 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 }