1 /* $NetBSD: printf.c,v 1.33.8.1 2009/10/14 18:37:30 sborrill Exp $ */
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
34 #include <sys/types.h>
55 static void conv_escape_str(char *, void (*)(int));
56 static char *conv_escape(char *, char *);
57 static char *conv_expand(const char *);
58 static int getchr(void);
59 static double getdouble(void);
60 static int getwidth(void);
61 static intmax_t getintmax(void);
62 static uintmax_t getuintmax(void);
63 static char *getstr(void);
64 static char *mklong(const char *, int);
65 static void check_conversion(const char *, const char *);
66 static void usage(void);
68 static void b_count(int);
69 static void b_output(int);
70 static size_t b_length
;
76 #ifdef BUILTIN /* csh builtin */
77 #define main progprintf
80 #ifdef SHELL /* sh (aka ash) builtin */
81 #define main printfcmd
82 #include "../../bin/sh/bltin/bltin.h"
85 #define PF(f, func) { \
86 if (fieldwidth != -1) { \
87 if (precision != -1) \
88 error = printf(f, fieldwidth, precision, func); \
90 error = printf(f, fieldwidth, func); \
91 } else if (precision != -1) \
92 error = printf(f, precision, func); \
94 error = printf(f, func); \
98 #define APF(cpp, f, func) { \
99 if (fieldwidth != -1) { \
100 if (precision != -1) \
101 error = asprintf(cpp, f, fieldwidth, precision, func); \
103 error = asprintf(cpp, f, fieldwidth, func); \
104 } else if (precision != -1) \
105 error = asprintf(cpp, f, precision, func); \
107 error = asprintf(cpp, f, func); \
112 int main(int, char *[]);
114 int main(int argc
, char *argv
[])
117 int fieldwidth
, precision
;
123 #if !defined(SHELL) && !defined(BUILTIN)
124 (void)setlocale (LC_ALL
, "");
127 while ((ch
= getopt(argc
, argv
, "")) != -1) {
146 #define SKIP1 "#-+ 0"
147 #define SKIP2 "0123456789"
150 * Basic algorithm is to scan the format string for conversion
151 * specifications -- once one is found, find out if the field
152 * width or precision is a '*'; if it is, gather up value.
153 * Note, format strings are reused as necessary to use up the
154 * provided arguments, arguments of zero/null string are
155 * provided to use up the format string.
158 /* find next format specification */
159 for (fmt
= format
; (ch
= *fmt
++) != '\0';) {
162 fmt
= conv_escape(fmt
, &c_ch
);
166 if (ch
!= '%' || (*fmt
== '%' && ++fmt
)) {
171 /* Ok - we've found a format specification,
172 Save its address for a later printf(). */
175 /* skip to field width */
176 fmt
+= strspn(fmt
, SKIP1
);
179 fieldwidth
= getwidth();
183 /* skip to possible '.', get following precision */
184 fmt
+= strspn(fmt
, SKIP2
);
189 precision
= getwidth();
195 fmt
+= strspn(fmt
, SKIP2
);
199 warnx("missing format character");
202 /* null terminate format string to we can use it
203 as an argument to printf. */
209 const char *p
= conv_expand(getstr());
219 /* There has to be a better way to do this,
220 * but the string we generate might have
224 /* Free on entry in case shell longjumped out */
231 /* Count number of bytes we want to output */
233 conv_escape_str(cp
, b_count
);
234 t
= malloc(b_length
+ 1);
237 (void)memset(t
, 'x', b_length
);
239 /* Get printf to calculate the lengths */
242 /* APF(&a, start, t); */
246 /* Output leading spaces and data bytes */
247 conv_escape_str(cp
, b_output
);
248 /* Add any trailing spaces */
268 intmax_t p
= getintmax();
269 char *f
= mklong(start
, ch
);
279 uintmax_t p
= getuintmax();
280 char *f
= mklong(start
, ch
);
291 double p
= getdouble();
298 warnx("%s: invalid directive", start
);
303 /* escape if a \c was encountered */
305 return rval
& ~0x100;
307 } while (gargv
!= argv
&& *gargv
);
309 return rval
& ~0x100;
311 warn("print failed");
315 /* helper functions for conv_escape_str */
324 /* Output one converted character for every 'x' in the 'format' */
346 * Print SysV echo(1) style escape string
347 * Halts processing string if a \c escape is encountered.
350 conv_escape_str(char *str
, void (*do_putchar
)(int))
356 while ((ch
= *str
++) != '\0') {
364 /* \c as in SYSV echo - abort all processing.... */
370 * %b string octal constants are not like those in C.
371 * They start with a \0, and are followed by 0, 1, 2,
376 for (i
= 0; i
< 3; i
++) {
377 if (!isdigit((unsigned char)*str
) || *str
> '7')
379 octnum
= (octnum
<< 3) | (*str
++ - '0');
385 /* \[M][^|-]C as defined by vis(3) */
386 if (ch
== 'M' && *str
== '-') {
387 do_putchar(0200 | str
[1]);
391 if (ch
== 'M' && *str
== '^') {
407 /* Finally test for sequences valid in the format string */
408 str
= conv_escape(str
- 1, &c
);
414 * Print "standard" escape characters
417 conv_escape(char *str
, char *conv_ch
)
421 char num_buf
[4], *num_end
;
426 case '0': case '1': case '2': case '3':
427 case '4': case '5': case '6': case '7':
431 num_buf
[2] = ch
? str
[1] : 0;
433 value
= strtoul(num_buf
, &num_end
, 8);
434 str
+= num_end
- (num_buf
+ 1);
438 /* Hexadecimal character constants are not required to be
439 supported (by SuS v1) because there is no consistent
440 way to detect the end of the constant.
441 Supporting 2 byte constants is a compromise. */
444 num_buf
[1] = ch
? str
[1] : 0;
446 value
= strtoul(num_buf
, &num_end
, 16);
447 str
+= num_end
- num_buf
;
450 case '\\': value
= '\\'; break; /* backslash */
451 case '\'': value
= '\''; break; /* single quote */
452 case '"': value
= '"'; break; /* double quote */
453 case 'a': value
= '\a'; break; /* alert */
454 case 'b': value
= '\b'; break; /* backspace */
455 case 'e': value
= ESCAPE
; break; /* escape */
456 case 'f': value
= '\f'; break; /* form-feed */
457 case 'n': value
= '\n'; break; /* newline */
458 case 'r': value
= '\r'; break; /* carriage-return */
459 case 't': value
= '\t'; break; /* tab */
460 case 'v': value
= '\v'; break; /* vertical-tab */
463 warnx("unknown escape sequence `\\%c'", ch
);
473 /* expand a string so that everything is printable */
476 conv_expand(const char *str
)
478 static char *conv_str
;
484 /* get a buffer that is definitely large enough.... */
485 conv_str
= malloc(4 * strlen(str
) + 1);
490 while ((ch
= *(const unsigned char *)str
++) != '\0') {
492 /* Use C escapes for expected control characters */
493 case '\\': ch
= '\\'; break; /* backslash */
494 case '\'': ch
= '\''; break; /* single quote */
495 case '"': ch
= '"'; break; /* double quote */
496 case '\a': ch
= 'a'; break; /* alert */
497 case '\b': ch
= 'b'; break; /* backspace */
498 case ESCAPE
: ch
= 'e'; break; /* escape */
499 case '\f': ch
= 'f'; break; /* form-feed */
500 case '\n': ch
= 'n'; break; /* newline */
501 case '\r': ch
= 'r'; break; /* carriage-return */
502 case '\t': ch
= 't'; break; /* tab */
503 case '\v': ch
= 'v'; break; /* vertical-tab */
505 /* Copy anything printable */
510 /* Use vis(3) encodings for the rest */
539 mklong(const char *str
, int ch
)
541 static char copy
[64];
544 len
= strlen(str
) + 2;
545 if (len
> sizeof copy
) {
546 warnx("format %s too complex\n", str
);
549 (void)memmove(copy
, str
, len
- 3);
552 copy
[len
- 1] = '\0';
561 return (int)**gargv
++;
567 static char empty
[] = "";
585 val
= strtoul(s
, &ep
, 0);
586 check_conversion(s
, ep
);
588 /* Arbitrarily 'restrict' field widths to 1Mbyte */
589 if (val
< 0 || val
> 1 << 20) {
590 warnx("%s: invalid field width", s
);
608 if (*cp
== '\"' || *cp
== '\'')
612 val
= strtol(cp
, &ep
, 0);
613 check_conversion(cp
, ep
);
628 if (*cp
== '\"' || *cp
== '\'')
631 /* strtoumax won't error -ve values */
632 while (isspace(*(unsigned char *)cp
))
635 warnx("%s: expected positive numeric value", cp
);
641 val
= strtoul(cp
, &ep
, 0);
642 check_conversion(cp
, ep
);
655 if (**gargv
== '\"' || **gargv
== '\'')
656 return (double) *((*gargv
++)+1);
659 val
= strtod(*gargv
, &ep
);
660 check_conversion(*gargv
++, ep
);
665 check_conversion(const char *s
, const char *ep
)
669 warnx("%s: expected numeric value", s
);
671 warnx("%s: not completely converted", s
);
673 } else if (errno
== ERANGE
) {
674 warnx("%s: %s", s
, strerror(ERANGE
));
682 (void)fprintf(stderr
, "Usage: %s format [arg ...]\n", getprogname());