VirtualBox

source: kBuild/trunk/src/gmake/kmkbuiltin/printf.c@ 803

最後變更 在這個檔案從803是 775,由 bird 提交於 18 年 前

ported printf to MSC.

檔案大小: 14.2 KB
 
1/* $NetBSD: printf.c,v 1.31 2005/03/22 23:55:46 dsl Exp $ */
2
3/*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. 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/cdefs.h>
33#ifndef lint
34#if !defined(BUILTIN) && !defined(SHELL)
35__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
36 The Regents of the University of California. All rights reserved.\n");
37#endif
38#endif
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95";
43#else
44__RCSID("$NetBSD: printf.c,v 1.31 2005/03/22 23:55:46 dsl Exp $");
45#endif
46#endif*/ /* not lint */
47
48#include <sys/types.h>
49
50#include <ctype.h>
51#include "err.h"
52#include <errno.h>
53#ifndef _MSC_VER
54#include <inttypes.h>
55#endif
56#include <limits.h>
57#include <locale.h>
58#include <stdarg.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#ifndef _MSC_VER
63#include <unistd.h>
64#else
65#include "mscfakes.h"
66#endif
67
68#ifdef __GNUC__
69#define ESCAPE '\e'
70#else
71#define ESCAPE 033
72#endif
73
74static void conv_escape_str(char *, void (*)(int));
75static char *conv_escape(char *, char *);
76static char *conv_expand(const char *);
77static int getchr(void);
78static double getdouble(void);
79static int getwidth(void);
80static intmax_t getintmax(void);
81static uintmax_t getuintmax(void);
82static char *getstr(void);
83static char *mklong(const char *, int);
84static void check_conversion(const char *, const char *);
85static void usage(void);
86
87static void b_count(int);
88static void b_output(int);
89static size_t b_length;
90static char *b_fmt;
91
92static int rval;
93static char **gargv;
94
95#ifdef BUILTIN /* csh builtin */
96#define kmk_builtin_printf progprintf
97#endif
98
99#ifdef SHELL /* sh (aka ash) builtin */
100#define kmk_builtin_printf printfcmd
101#include "../../bin/sh/bltin/bltin.h"
102#endif /* SHELL */
103
104#define PF(f, func) { \
105 if (fieldwidth != -1) { \
106 if (precision != -1) \
107 (void)printf(f, fieldwidth, precision, func); \
108 else \
109 (void)printf(f, fieldwidth, func); \
110 } else if (precision != -1) \
111 (void)printf(f, precision, func); \
112 else \
113 (void)printf(f, func); \
114}
115
116#define APF(cpp, f, func) { \
117 if (fieldwidth != -1) { \
118 if (precision != -1) \
119 (void)asprintf(cpp, f, fieldwidth, precision, func); \
120 else \
121 (void)asprintf(cpp, f, fieldwidth, func); \
122 } else if (precision != -1) \
123 (void)asprintf(cpp, f, precision, func); \
124 else \
125 (void)asprintf(cpp, f, func); \
126}
127
128int kmk_builtin_printf(int argc, char *argv[])
129{
130 char *fmt, *start;
131 int fieldwidth, precision;
132 char nextch;
133 char *format;
134 int ch;
135
136 /* kmk: reinitialize globals */
137 b_length = 0;
138 b_fmt = NULL;
139 rval = 0;
140 gargv = NULL;
141
142 /* kmk: reset getopt and set progname */
143 g_progname = argv[0];
144 opterr = 1;
145 optarg = NULL;
146 optopt = 0;
147#if defined(__FreeBSD__) || defined(__EMX__) || defined(__APPLE__)
148 optreset = 1;
149 optind = 1;
150#else
151 optind = 0; /* init */
152#endif
153
154#if !defined(SHELL) && !defined(BUILTIN) && !defined(kmk_builtin_printf) /* kmk did this already. */
155 (void)setlocale (LC_ALL, "");
156#endif
157
158 while ((ch = getopt(argc, argv, "")) != -1) {
159 switch (ch) {
160 case '?':
161 default:
162 usage();
163 return 1;
164 }
165 }
166 argc -= optind;
167 argv += optind;
168
169 if (argc < 1) {
170 usage();
171 return 1;
172 }
173
174 format = *argv;
175 gargv = ++argv;
176
177#define SKIP1 "#-+ 0"
178#define SKIP2 "*0123456789"
179 do {
180 /*
181 * Basic algorithm is to scan the format string for conversion
182 * specifications -- once one is found, find out if the field
183 * width or precision is a '*'; if it is, gather up value.
184 * Note, format strings are reused as necessary to use up the
185 * provided arguments, arguments of zero/null string are
186 * provided to use up the format string.
187 */
188
189 /* find next format specification */
190 for (fmt = format; (ch = *fmt++) != '\0';) {
191 if (ch == '\\') {
192 char c_ch;
193 fmt = conv_escape(fmt, &c_ch);
194 putchar(c_ch);
195 continue;
196 }
197 if (ch != '%' || (*fmt == '%' && ++fmt)) {
198 (void)putchar(ch);
199 continue;
200 }
201
202 /* Ok - we've found a format specification,
203 Save its address for a later printf(). */
204 start = fmt - 1;
205
206 /* skip to field width */
207 fmt += strspn(fmt, SKIP1);
208 fieldwidth = *fmt == '*' ? getwidth() : -1;
209
210 /* skip to possible '.', get following precision */
211 fmt += strspn(fmt, SKIP2);
212 if (*fmt == '.')
213 ++fmt;
214 precision = *fmt == '*' ? getwidth() : -1;
215
216 fmt += strspn(fmt, SKIP2);
217
218 ch = *fmt;
219 if (!ch) {
220 warnx("missing format character");
221 return (1);
222 }
223 /* null terminate format string to we can use it
224 as an argument to printf. */
225 nextch = fmt[1];
226 fmt[1] = 0;
227 switch (ch) {
228
229 case 'B': {
230 const char *p = conv_expand(getstr());
231 *fmt = 's';
232 PF(start, p);
233 break;
234 }
235 case 'b': {
236 /* There has to be a better way to do this,
237 * but the string we generate might have
238 * embedded nulls. */
239 static char *a, *t;
240 char *cp = getstr();
241 /* Free on entry in case shell longjumped out */
242 if (a != NULL)
243 free(a);
244 a = NULL;
245 if (t != NULL)
246 free(t);
247 t = NULL;
248 /* Count number of bytes we want to output */
249 b_length = 0;
250 conv_escape_str(cp, b_count);
251 t = malloc(b_length + 1);
252 if (t == NULL)
253 break;
254 memset(t, 'x', b_length);
255 t[b_length] = 0;
256 /* Get printf to calculate the lengths */
257 *fmt = 's';
258 APF(&a, start, t);
259 b_fmt = a;
260 /* Output leading spaces and data bytes */
261 conv_escape_str(cp, b_output);
262 /* Add any trailing spaces */
263 printf("%s", b_fmt);
264 break;
265 }
266 case 'c': {
267 char p = getchr();
268 PF(start, p);
269 break;
270 }
271 case 's': {
272 char *p = getstr();
273 PF(start, p);
274 break;
275 }
276 case 'd':
277 case 'i': {
278 intmax_t p = getintmax();
279 char *f = mklong(start, ch);
280 PF(f, p);
281 break;
282 }
283 case 'o':
284 case 'u':
285 case 'x':
286 case 'X': {
287 uintmax_t p = getuintmax();
288 char *f = mklong(start, ch);
289 PF(f, p);
290 break;
291 }
292 case 'e':
293 case 'E':
294 case 'f':
295 case 'g':
296 case 'G': {
297 double p = getdouble();
298 PF(start, p);
299 break;
300 }
301 default:
302 warnx("%s: invalid directive", start);
303 return 1;
304 }
305 *fmt++ = ch;
306 *fmt = nextch;
307 /* escape if a \c was encountered */
308 if (rval & 0x100)
309 return rval & ~0x100;
310 }
311 } while (gargv != argv && *gargv);
312
313 return rval;
314}
315
316/* helper functions for conv_escape_str */
317
318static void
319/*ARGSUSED*/
320b_count(int ch)
321{
322 b_length++;
323}
324
325/* Output one converted character for every 'x' in the 'format' */
326
327static void
328b_output(int ch)
329{
330 for (;;) {
331 switch (*b_fmt++) {
332 case 0:
333 b_fmt--;
334 return;
335 case ' ':
336 putchar(' ');
337 break;
338 default:
339 putchar(ch);
340 return;
341 }
342 }
343}
344
345
346/*
347 * Print SysV echo(1) style escape string
348 * Halts processing string if a \c escape is encountered.
349 */
350static void
351conv_escape_str(char *str, void (*do_putchar)(int))
352{
353 int value;
354 int ch;
355 char c;
356
357 while ((ch = *str++) != '\0') {
358 if (ch != '\\') {
359 do_putchar(ch);
360 continue;
361 }
362
363 ch = *str++;
364 if (ch == 'c') {
365 /* \c as in SYSV echo - abort all processing.... */
366 rval |= 0x100;
367 break;
368 }
369
370 /*
371 * %b string octal constants are not like those in C.
372 * They start with a \0, and are followed by 0, 1, 2,
373 * or 3 octal digits.
374 */
375 if (ch == '0') {
376 int octnum = 0, i;
377 for (i = 0; i < 3; i++) {
378 if (!isdigit((unsigned char)*str) || *str > '7')
379 break;
380 octnum = (octnum << 3) | (*str++ - '0');
381 }
382 do_putchar(octnum);
383 continue;
384 }
385
386 /* \[M][^|-]C as defined by vis(3) */
387 if (ch == 'M' && *str == '-') {
388 do_putchar(0200 | str[1]);
389 str += 2;
390 continue;
391 }
392 if (ch == 'M' && *str == '^') {
393 str++;
394 value = 0200;
395 ch = '^';
396 } else
397 value = 0;
398 if (ch == '^') {
399 ch = *str++;
400 if (ch == '?')
401 value |= 0177;
402 else
403 value |= ch & 037;
404 do_putchar(value);
405 continue;
406 }
407
408 /* Finally test for sequences valid in the format string */
409 str = conv_escape(str - 1, &c);
410 do_putchar(c);
411 }
412}
413
414/*
415 * Print "standard" escape characters
416 */
417static char *
418conv_escape(char *str, char *conv_ch)
419{
420 int value;
421 int ch;
422 char num_buf[4], *num_end;
423
424 ch = *str++;
425
426 switch (ch) {
427 case '0': case '1': case '2': case '3':
428 case '4': case '5': case '6': case '7':
429 num_buf[0] = ch;
430 ch = str[0];
431 num_buf[1] = ch;
432 num_buf[2] = ch ? str[1] : 0;
433 num_buf[3] = 0;
434 value = strtoul(num_buf, &num_end, 8);
435 str += num_end - (num_buf + 1);
436 break;
437
438 case 'x':
439 /* Hexadecimal character constants are not required to be
440 supported (by SuS v1) because there is no consistent
441 way to detect the end of the constant.
442 Supporting 2 byte constants is a compromise. */
443 ch = str[0];
444 num_buf[0] = ch;
445 num_buf[1] = ch ? str[1] : 0;
446 num_buf[2] = 0;
447 value = strtoul(num_buf, &num_end, 16);
448 str += num_end - num_buf;
449 break;
450
451 case '\\': value = '\\'; break; /* backslash */
452 case '\'': value = '\''; break; /* single quote */
453 case '"': value = '"'; break; /* double quote */
454 case 'a': value = '\a'; break; /* alert */
455 case 'b': value = '\b'; break; /* backspace */
456 case 'e': value = ESCAPE; break; /* escape */
457 case 'f': value = '\f'; break; /* form-feed */
458 case 'n': value = '\n'; break; /* newline */
459 case 'r': value = '\r'; break; /* carriage-return */
460 case 't': value = '\t'; break; /* tab */
461 case 'v': value = '\v'; break; /* vertical-tab */
462
463 default:
464 warnx("unknown escape sequence `\\%c'", ch);
465 rval = 1;
466 value = ch;
467 break;
468 }
469
470 *conv_ch = value;
471 return str;
472}
473
474/* expand a string so that everything is printable */
475
476static char *
477conv_expand(const char *str)
478{
479 static char *conv_str;
480 static char no_memory[] = "<no memory>";
481 char *cp;
482 int ch;
483
484 if (conv_str)
485 free(conv_str);
486 /* get a buffer that is definitely large enough.... */
487 conv_str = malloc(4 * strlen(str) + 1);
488 if (!conv_str)
489 return no_memory;
490 cp = conv_str;
491
492 while ((ch = *(const unsigned char *)str++) != '\0') {
493 switch (ch) {
494 /* Use C escapes for expected control characters */
495 case '\\': ch = '\\'; break; /* backslash */
496 case '\'': ch = '\''; break; /* single quote */
497 case '"': ch = '"'; break; /* double quote */
498 case '\a': ch = 'a'; break; /* alert */
499 case '\b': ch = 'b'; break; /* backspace */
500 case ESCAPE: ch = 'e'; break; /* escape */
501 case '\f': ch = 'f'; break; /* form-feed */
502 case '\n': ch = 'n'; break; /* newline */
503 case '\r': ch = 'r'; break; /* carriage-return */
504 case '\t': ch = 't'; break; /* tab */
505 case '\v': ch = 'v'; break; /* vertical-tab */
506 default:
507 /* Copy anything printable */
508 if (isprint(ch)) {
509 *cp++ = ch;
510 continue;
511 }
512 /* Use vis(3) encodings for the rest */
513 *cp++ = '\\';
514 if (ch & 0200) {
515 *cp++ = 'M';
516 ch &= ~0200;
517 }
518 if (ch == 0177) {
519 *cp++ = '^';
520 *cp++ = '?';
521 continue;
522 }
523 if (ch < 040) {
524 *cp++ = '^';
525 *cp++ = ch | 0100;
526 continue;
527 }
528 *cp++ = '-';
529 *cp++ = ch;
530 continue;
531 }
532 *cp++ = '\\';
533 *cp++ = ch;
534 }
535
536 *cp = 0;
537 return conv_str;
538}
539
540static char *
541mklong(const char *str, int ch)
542{
543 static char copy[64];
544 size_t len;
545
546 len = strlen(str) + 2;
547 if (len > sizeof copy) {
548 warnx("format %s too complex\n", str);
549 len = 4;
550 }
551 (void)memmove(copy, str, len - 3);
552 copy[len - 3] = 'j';
553 copy[len - 2] = ch;
554 copy[len - 1] = '\0';
555 return copy;
556}
557
558static int
559getchr(void)
560{
561 if (!*gargv)
562 return 0;
563 return (int)**gargv++;
564}
565
566static char *
567getstr(void)
568{
569 static char empty[] = "";
570 if (!*gargv)
571 return empty;
572 return *gargv++;
573}
574
575static int
576getwidth(void)
577{
578 long val;
579 char *s, *ep;
580
581 s = *gargv;
582 if (!*gargv)
583 return (0);
584 gargv++;
585
586 errno = 0;
587 val = strtoul(s, &ep, 0);
588 check_conversion(s, ep);
589
590 /* Arbitrarily 'restrict' field widths to 1Mbyte */
591 if (val < 0 || val > 1 << 20) {
592 warnx("%s: invalid field width", s);
593 return 0;
594 }
595
596 return val;
597}
598
599static intmax_t
600getintmax(void)
601{
602 intmax_t val;
603 char *cp, *ep;
604
605 cp = *gargv;
606 if (cp == NULL)
607 return 0;
608 gargv++;
609
610 if (*cp == '\"' || *cp == '\'')
611 return *(cp+1);
612
613 errno = 0;
614 val = strtoimax(cp, &ep, 0);
615 check_conversion(cp, ep);
616 return val;
617}
618
619static uintmax_t
620getuintmax(void)
621{
622 uintmax_t val;
623 char *cp, *ep;
624
625 cp = *gargv;
626 if (cp == NULL)
627 return 0;
628 gargv++;
629
630 if (*cp == '\"' || *cp == '\'')
631 return *(cp + 1);
632
633 /* strtoumax won't error -ve values */
634 while (isspace(*(unsigned char *)cp))
635 cp++;
636 if (*cp == '-') {
637 warnx("%s: expected positive numeric value", cp);
638 rval = 1;
639 return 0;
640 }
641
642 errno = 0;
643 val = strtoumax(cp, &ep, 0);
644 check_conversion(cp, ep);
645 return val;
646}
647
648static double
649getdouble(void)
650{
651 double val;
652 char *ep;
653
654 if (!*gargv)
655 return (0.0);
656
657 if (**gargv == '\"' || **gargv == '\'')
658 return (double) *((*gargv++)+1);
659
660 errno = 0;
661 val = strtod(*gargv, &ep);
662 check_conversion(*gargv++, ep);
663 return val;
664}
665
666static void
667check_conversion(const char *s, const char *ep)
668{
669 if (*ep) {
670 if (ep == s)
671 warnx("%s: expected numeric value", s);
672 else
673 warnx("%s: not completely converted", s);
674 rval = 1;
675 } else if (errno == ERANGE) {
676 warnx("%s: %s", s, strerror(ERANGE));
677 rval = 1;
678 }
679}
680
681static void
682usage(void)
683{
684 (void)fprintf(stderr, "Usage: %s format [arg ...]\n", g_progname);
685}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette