printf.c revision 1.25 1 1.25 christos /* $NetBSD: printf.c,v 1.25 2002/11/24 22:35:45 christos Exp $ */
2 1.14 tls
3 1.1 cgd /*
4 1.17 mrg * Copyright (c) 1989, 1993
5 1.17 mrg * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.1 cgd * 3. All advertising materials mentioning features or use of this software
16 1.1 cgd * must display the following acknowledgement:
17 1.1 cgd * This product includes software developed by the University of
18 1.1 cgd * California, Berkeley and its contributors.
19 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
20 1.1 cgd * may be used to endorse or promote products derived from this software
21 1.1 cgd * without specific prior written permission.
22 1.1 cgd *
23 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 1.1 cgd * SUCH DAMAGE.
34 1.1 cgd */
35 1.1 cgd
36 1.16 christos #include <sys/cdefs.h>
37 1.1 cgd #ifndef lint
38 1.17 mrg #if !defined(BUILTIN) && !defined(SHELL)
39 1.17 mrg __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
40 1.17 mrg The Regents of the University of California. All rights reserved.\n");
41 1.17 mrg #endif
42 1.5 jtc #endif
43 1.1 cgd
44 1.1 cgd #ifndef lint
45 1.16 christos #if 0
46 1.17 mrg static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95";
47 1.16 christos #else
48 1.25 christos __RCSID("$NetBSD: printf.c,v 1.25 2002/11/24 22:35:45 christos Exp $");
49 1.16 christos #endif
50 1.1 cgd #endif /* not lint */
51 1.1 cgd
52 1.17 mrg #include <sys/types.h>
53 1.17 mrg
54 1.4 jtc #include <ctype.h>
55 1.19 perry #include <err.h>
56 1.19 perry #include <errno.h>
57 1.22 kleink #include <inttypes.h>
58 1.19 perry #include <limits.h>
59 1.19 perry #include <locale.h>
60 1.23 wiz #include <stdarg.h>
61 1.1 cgd #include <stdio.h>
62 1.4 jtc #include <stdlib.h>
63 1.2 mycroft #include <string.h>
64 1.19 perry #include <unistd.h>
65 1.4 jtc
66 1.25 christos #ifdef __GNUC__
67 1.25 christos #define ESCAPE '\e'
68 1.25 christos #else
69 1.25 christos #define ESCAPE 033
70 1.25 christos #endif
71 1.5 jtc
72 1.25 christos static char *conv_escape_str(char *);
73 1.25 christos static char *conv_escape(char *, char *);
74 1.25 christos static char *conv_expand(const char *);
75 1.23 wiz static int getchr(void);
76 1.23 wiz static double getdouble(void);
77 1.25 christos static int getwidth(void);
78 1.23 wiz static intmax_t getintmax(void);
79 1.25 christos static uintmax_t getuintmax(void);
80 1.23 wiz static char *getstr(void);
81 1.25 christos static char *mklong(const char *, int);
82 1.23 wiz static void check_conversion(const char *, const char *);
83 1.23 wiz static void usage(void);
84 1.25 christos
85 1.5 jtc static int rval;
86 1.5 jtc static char **gargv;
87 1.4 jtc
88 1.25 christos #ifdef BUILTIN /* csh builtin */
89 1.25 christos #define main progprintf
90 1.16 christos #endif
91 1.16 christos
92 1.25 christos #ifdef SHELL /* sh (aka ash) builtin */
93 1.5 jtc #define main printfcmd
94 1.5 jtc #include "../../bin/sh/bltin/bltin.h"
95 1.5 jtc #endif /* SHELL */
96 1.5 jtc
97 1.1 cgd #define PF(f, func) { \
98 1.25 christos if (fieldwidth != -1) { \
99 1.25 christos if (precision != -1) \
100 1.1 cgd (void)printf(f, fieldwidth, precision, func); \
101 1.1 cgd else \
102 1.1 cgd (void)printf(f, fieldwidth, func); \
103 1.25 christos } else if (precision != -1) \
104 1.1 cgd (void)printf(f, precision, func); \
105 1.1 cgd else \
106 1.1 cgd (void)printf(f, func); \
107 1.1 cgd }
108 1.1 cgd
109 1.25 christos int main(int, char **);
110 1.25 christos int main(int argc, char *argv[])
111 1.1 cgd {
112 1.18 lukem char *fmt, *start;
113 1.18 lukem int fieldwidth, precision;
114 1.25 christos char nextch;
115 1.4 jtc char *format;
116 1.5 jtc int ch;
117 1.4 jtc
118 1.5 jtc #if !defined(SHELL) && !defined(BUILTIN)
119 1.15 cgd (void)setlocale (LC_ALL, "");
120 1.5 jtc #endif
121 1.1 cgd
122 1.5 jtc while ((ch = getopt(argc, argv, "")) != -1) {
123 1.5 jtc switch (ch) {
124 1.5 jtc case '?':
125 1.5 jtc default:
126 1.5 jtc usage();
127 1.5 jtc return (1);
128 1.5 jtc }
129 1.1 cgd }
130 1.5 jtc argc -= optind;
131 1.5 jtc argv += optind;
132 1.1 cgd
133 1.5 jtc if (argc < 1) {
134 1.5 jtc usage();
135 1.5 jtc return (1);
136 1.5 jtc }
137 1.5 jtc
138 1.5 jtc format = *argv;
139 1.5 jtc gargv = ++argv;
140 1.4 jtc
141 1.7 jtc #define SKIP1 "#-+ 0"
142 1.7 jtc #define SKIP2 "*0123456789"
143 1.4 jtc do {
144 1.6 jtc /*
145 1.6 jtc * Basic algorithm is to scan the format string for conversion
146 1.6 jtc * specifications -- once one is found, find out if the field
147 1.6 jtc * width or precision is a '*'; if it is, gather up value.
148 1.6 jtc * Note, format strings are reused as necessary to use up the
149 1.6 jtc * provided arguments, arguments of zero/null string are
150 1.6 jtc * provided to use up the format string.
151 1.6 jtc */
152 1.6 jtc
153 1.6 jtc /* find next format specification */
154 1.25 christos for (fmt = format; (ch = *fmt++) ;) {
155 1.25 christos if (ch == '\\') {
156 1.25 christos char c_ch;
157 1.25 christos fmt = conv_escape(fmt, &c_ch);
158 1.25 christos putchar(c_ch);
159 1.25 christos continue;
160 1.25 christos }
161 1.25 christos if (ch != '%' || (*fmt == '%' && ++fmt)) {
162 1.25 christos (void)putchar(ch);
163 1.25 christos continue;
164 1.25 christos }
165 1.25 christos
166 1.25 christos /* Ok - we've found a format specification,
167 1.25 christos Save its address for a later printf(). */
168 1.25 christos start = fmt - 1;
169 1.25 christos
170 1.25 christos /* skip to field width */
171 1.25 christos fmt += strspn(fmt, SKIP1);
172 1.25 christos fieldwidth = *fmt == '*' ? getwidth() : -1;
173 1.25 christos
174 1.25 christos /* skip to possible '.', get following precision */
175 1.25 christos fmt += strspn(fmt, SKIP2);
176 1.25 christos if (*fmt == '.')
177 1.25 christos ++fmt;
178 1.25 christos precision = *fmt == '*' ? getwidth() : -1;
179 1.25 christos
180 1.25 christos fmt += strspn(fmt, SKIP2);
181 1.25 christos
182 1.25 christos ch = *fmt;
183 1.25 christos if (!ch) {
184 1.25 christos warnx("missing format character");
185 1.25 christos return (1);
186 1.25 christos }
187 1.25 christos /* null terminate format string to we can use it
188 1.25 christos as an argument to printf. */
189 1.25 christos nextch = fmt[1];
190 1.25 christos fmt[1] = 0;
191 1.25 christos switch (ch) {
192 1.25 christos
193 1.25 christos case 'B': {
194 1.25 christos const char *p = conv_expand(getstr());
195 1.25 christos *fmt = 's';
196 1.25 christos PF(start, p);
197 1.6 jtc break;
198 1.25 christos }
199 1.25 christos case 'b': {
200 1.25 christos char *p = conv_escape_str(getstr());
201 1.25 christos *fmt = 's';
202 1.25 christos PF(start, p);
203 1.25 christos break;
204 1.25 christos }
205 1.25 christos case 'c': {
206 1.25 christos char p = getchr();
207 1.25 christos PF(start, p);
208 1.25 christos break;
209 1.25 christos }
210 1.25 christos case 's': {
211 1.25 christos char *p = getstr();
212 1.25 christos PF(start, p);
213 1.25 christos break;
214 1.25 christos }
215 1.25 christos case 'd':
216 1.25 christos case 'i': {
217 1.25 christos intmax_t p = getintmax();
218 1.25 christos char *f = mklong(start, ch);
219 1.25 christos PF(f, p);
220 1.25 christos break;
221 1.25 christos }
222 1.25 christos case 'o':
223 1.25 christos case 'u':
224 1.25 christos case 'x':
225 1.25 christos case 'X': {
226 1.25 christos uintmax_t p = getuintmax();
227 1.25 christos char *f = mklong(start, ch);
228 1.25 christos PF(f, p);
229 1.25 christos break;
230 1.25 christos }
231 1.25 christos case 'e':
232 1.25 christos case 'E':
233 1.25 christos case 'f':
234 1.25 christos case 'g':
235 1.25 christos case 'G': {
236 1.25 christos double p = getdouble();
237 1.25 christos PF(start, p);
238 1.4 jtc break;
239 1.25 christos }
240 1.6 jtc default:
241 1.25 christos warnx("%s: invalid directive", start);
242 1.25 christos return (1);
243 1.4 jtc }
244 1.25 christos *fmt++ = ch;
245 1.25 christos *fmt = nextch;
246 1.25 christos /* escape if a \c was encountered */
247 1.25 christos if (rval & 0x100)
248 1.25 christos return (rval & ~0x100);
249 1.6 jtc }
250 1.25 christos } while (gargv != argv && *gargv);
251 1.1 cgd
252 1.6 jtc return (rval);
253 1.4 jtc }
254 1.4 jtc
255 1.4 jtc
256 1.4 jtc /*
257 1.4 jtc * Print SysV echo(1) style escape string
258 1.25 christos * Halts processing string if a \c escape is encountered.
259 1.4 jtc */
260 1.25 christos static char *
261 1.25 christos conv_escape_str(char *str)
262 1.4 jtc {
263 1.4 jtc int value;
264 1.25 christos int ch;
265 1.25 christos static char *conv_str;
266 1.25 christos char *cp;
267 1.4 jtc
268 1.25 christos /* convert string into a temporary buffer... */
269 1.25 christos if (conv_str)
270 1.25 christos free(conv_str);
271 1.25 christos conv_str = malloc(strlen(str) + 4);
272 1.25 christos if (!conv_str)
273 1.25 christos return "<no memory>";
274 1.25 christos cp = conv_str;
275 1.25 christos
276 1.25 christos while ((ch = *str++)) {
277 1.25 christos if (ch != '\\') {
278 1.25 christos *cp++ = ch;
279 1.25 christos continue;
280 1.25 christos }
281 1.25 christos
282 1.25 christos ch = *str++;
283 1.25 christos if (ch == 'c') {
284 1.25 christos /* \c as in SYSV echo - abort all processing.... */
285 1.25 christos rval |= 0x100;
286 1.25 christos break;
287 1.25 christos }
288 1.25 christos
289 1.25 christos /*
290 1.25 christos * %b string octal constants are not like those in C.
291 1.25 christos * They start with a \0, and are followed by 0, 1, 2,
292 1.25 christos * or 3 octal digits.
293 1.25 christos */
294 1.25 christos if (ch == '0') {
295 1.25 christos char octnum[4], *oct_end;
296 1.25 christos octnum[0] = str[0];
297 1.25 christos octnum[1] = str[1];
298 1.25 christos octnum[2] = str[2];
299 1.25 christos octnum[3] = 0;
300 1.25 christos *cp++ = strtoul(octnum, &oct_end, 8);
301 1.25 christos str += oct_end - octnum;
302 1.25 christos continue;
303 1.25 christos }
304 1.25 christos
305 1.25 christos /* \[M][^|-]C as defined by vis(3) */
306 1.25 christos if (ch == 'M' && *str == '-') {
307 1.25 christos *cp++ = 0200 | str[1];
308 1.25 christos str += 2;
309 1.25 christos continue;
310 1.25 christos }
311 1.25 christos if (ch == 'M' && *str == '^') {
312 1.4 jtc str++;
313 1.25 christos value = 0200;
314 1.25 christos ch = '^';
315 1.25 christos } else
316 1.25 christos value = 0;
317 1.25 christos if (ch == '^') {
318 1.25 christos ch = *str++;
319 1.25 christos if (ch == '?')
320 1.25 christos value |= 0177;
321 1.25 christos else
322 1.25 christos value |= ch & 037;
323 1.25 christos *cp++ = value;
324 1.25 christos continue;
325 1.1 cgd }
326 1.25 christos
327 1.25 christos /* Finally test for sequences valid in the format string */
328 1.25 christos str = conv_escape(str - 1, cp);
329 1.25 christos cp++;
330 1.4 jtc }
331 1.25 christos *cp = 0;
332 1.7 jtc
333 1.25 christos return conv_str;
334 1.4 jtc }
335 1.4 jtc
336 1.4 jtc /*
337 1.4 jtc * Print "standard" escape characters
338 1.4 jtc */
339 1.25 christos static char *
340 1.25 christos conv_escape(char *str, char *conv_ch)
341 1.4 jtc {
342 1.9 jtc int value;
343 1.25 christos int ch;
344 1.25 christos char num_buf[4], *num_end;
345 1.4 jtc
346 1.25 christos ch = *str++;
347 1.4 jtc
348 1.25 christos switch (ch) {
349 1.4 jtc case '0': case '1': case '2': case '3':
350 1.4 jtc case '4': case '5': case '6': case '7':
351 1.25 christos num_buf[0] = ch;
352 1.25 christos ch = str[0];
353 1.25 christos num_buf[1] = ch;
354 1.25 christos num_buf[2] = ch ? str[1] : 0;
355 1.25 christos num_buf[3] = 0;
356 1.25 christos value = strtoul(num_buf, &num_end, 8);
357 1.25 christos str += num_end - (num_buf + 1);
358 1.25 christos break;
359 1.4 jtc
360 1.4 jtc case 'x':
361 1.25 christos /* Hexadecimal character constants are not required to be
362 1.25 christos supported (by SuS v1) because there is no consistent
363 1.25 christos way to detect the end of the constant.
364 1.25 christos Supporting 2 byte constants is a compromise. */
365 1.25 christos ch = str[0];
366 1.25 christos num_buf[0] = ch;
367 1.25 christos num_buf[1] = ch ? str[1] : 0;
368 1.25 christos num_buf[2] = 0;
369 1.25 christos value = strtoul(num_buf, &num_end, 16);
370 1.25 christos str += num_end - num_buf;
371 1.25 christos break;
372 1.25 christos
373 1.25 christos case '\\': value = '\\'; break; /* backslash */
374 1.25 christos case '\'': value = '\''; break; /* single quote */
375 1.25 christos case '"': value = '"'; break; /* double quote */
376 1.25 christos case 'a': value = '\a'; break; /* alert */
377 1.25 christos case 'b': value = '\b'; break; /* backspace */
378 1.25 christos case 'e': value = ESCAPE; break; /* escape */
379 1.25 christos case 'f': value = '\f'; break; /* form-feed */
380 1.25 christos case 'n': value = '\n'; break; /* newline */
381 1.25 christos case 'r': value = '\r'; break; /* carriage-return */
382 1.25 christos case 't': value = '\t'; break; /* tab */
383 1.25 christos case 'v': value = '\v'; break; /* vertical-tab */
384 1.4 jtc
385 1.25 christos default:
386 1.25 christos warnx("unknown escape sequence `\\%c'", *str);
387 1.25 christos rval = 1;
388 1.25 christos value = *str;
389 1.4 jtc break;
390 1.25 christos }
391 1.4 jtc
392 1.25 christos *conv_ch = value;
393 1.25 christos return str;
394 1.25 christos }
395 1.4 jtc
396 1.25 christos /* expand a string so that everything is printable */
397 1.4 jtc
398 1.25 christos static char *
399 1.25 christos conv_expand(const char *str)
400 1.25 christos {
401 1.25 christos static char *conv_str;
402 1.25 christos char *cp;
403 1.25 christos int ch;
404 1.4 jtc
405 1.25 christos if (conv_str)
406 1.25 christos free(conv_str);
407 1.25 christos /* get a buffer that is definitely large enough.... */
408 1.25 christos conv_str = malloc(4 * strlen(str) + 1);
409 1.25 christos if (!conv_str)
410 1.25 christos return "<no memory>";
411 1.25 christos cp = conv_str;
412 1.4 jtc
413 1.25 christos while ((ch = *(unsigned char *)str++)) {
414 1.25 christos switch (ch) {
415 1.25 christos /* Use C escapes for expected control characters */
416 1.25 christos case '\\': ch = '\\'; break; /* backslash */
417 1.25 christos case '\'': ch = '\''; break; /* single quote */
418 1.25 christos case '"': ch = '"'; break; /* double quote */
419 1.25 christos case '\a': ch = 'a'; break; /* alert */
420 1.25 christos case '\b': ch = 'b'; break; /* backspace */
421 1.25 christos case ESCAPE: ch = 'e'; break; /* escape */
422 1.25 christos case '\f': ch = 'f'; break; /* form-feed */
423 1.25 christos case '\n': ch = 'n'; break; /* newline */
424 1.25 christos case '\r': ch = 'r'; break; /* carriage-return */
425 1.25 christos case '\t': ch = 't'; break; /* tab */
426 1.25 christos case '\v': ch = 'v'; break; /* vertical-tab */
427 1.25 christos default:
428 1.25 christos /* Copy anything printable */
429 1.25 christos if (isprint(ch)) {
430 1.25 christos *cp++ = ch;
431 1.25 christos continue;
432 1.25 christos }
433 1.25 christos /* Use vis(3) encodings for the rest */
434 1.25 christos *cp++ = '\\';
435 1.25 christos if (ch & 0200) {
436 1.25 christos *cp++ = 'M';
437 1.25 christos ch &= ~0200;
438 1.25 christos }
439 1.25 christos if (ch == 0177) {
440 1.25 christos *cp++ = '^';
441 1.25 christos *cp++ = '?';
442 1.25 christos continue;
443 1.25 christos }
444 1.25 christos if (ch < 040) {
445 1.25 christos *cp++ = '^';
446 1.25 christos *cp++ = ch | 0100;
447 1.25 christos continue;
448 1.25 christos }
449 1.25 christos *cp++ = '-';
450 1.25 christos *cp++ = ch;
451 1.25 christos continue;
452 1.25 christos }
453 1.25 christos *cp++ = '\\';
454 1.25 christos *cp++ = ch;
455 1.1 cgd }
456 1.4 jtc
457 1.25 christos *cp = 0;
458 1.25 christos return conv_str;
459 1.1 cgd }
460 1.1 cgd
461 1.5 jtc static char *
462 1.23 wiz mklong(const char *str, int ch)
463 1.1 cgd {
464 1.5 jtc static char copy[64];
465 1.15 cgd size_t len;
466 1.1 cgd
467 1.1 cgd len = strlen(str) + 2;
468 1.25 christos if (len > sizeof copy) {
469 1.25 christos warnx("format %s too complex\n", str);
470 1.25 christos len = 4;
471 1.25 christos }
472 1.15 cgd (void)memmove(copy, str, len - 3);
473 1.22 kleink copy[len - 3] = 'j';
474 1.1 cgd copy[len - 2] = ch;
475 1.1 cgd copy[len - 1] = '\0';
476 1.5 jtc return (copy);
477 1.1 cgd }
478 1.1 cgd
479 1.5 jtc static int
480 1.23 wiz getchr(void)
481 1.1 cgd {
482 1.1 cgd if (!*gargv)
483 1.17 mrg return ('\0');
484 1.17 mrg return ((int)**gargv++);
485 1.1 cgd }
486 1.1 cgd
487 1.5 jtc static char *
488 1.23 wiz getstr(void)
489 1.1 cgd {
490 1.1 cgd if (!*gargv)
491 1.17 mrg return ("");
492 1.17 mrg return (*gargv++);
493 1.1 cgd }
494 1.1 cgd
495 1.5 jtc static int
496 1.25 christos getwidth(void)
497 1.1 cgd {
498 1.25 christos long val;
499 1.25 christos char *s, *ep;
500 1.25 christos
501 1.25 christos s = *gargv;
502 1.1 cgd if (!*gargv)
503 1.25 christos return (0);
504 1.25 christos gargv++;
505 1.4 jtc
506 1.25 christos errno = 0;
507 1.25 christos val = strtoul(s, &ep, 0);
508 1.25 christos check_conversion(s, ep);
509 1.4 jtc
510 1.25 christos /* Arbitrarily 'restrict' field widths to 1Mbyte */
511 1.25 christos if (val < 0 || val > 1 << 20) {
512 1.25 christos warnx("%s: invalid field width", s);
513 1.25 christos return 0;
514 1.25 christos }
515 1.25 christos
516 1.25 christos return val;
517 1.1 cgd }
518 1.1 cgd
519 1.22 kleink static intmax_t
520 1.23 wiz getintmax(void)
521 1.1 cgd {
522 1.22 kleink intmax_t val;
523 1.25 christos char *cp, *ep;
524 1.1 cgd
525 1.25 christos cp = *gargv;
526 1.25 christos if (cp == NULL)
527 1.25 christos return 0;
528 1.25 christos gargv++;
529 1.4 jtc
530 1.25 christos if (*cp == '\"' || *cp == '\'')
531 1.25 christos return *(cp+1);
532 1.4 jtc
533 1.11 jtc errno = 0;
534 1.25 christos val = strtoimax(cp, &ep, 0);
535 1.25 christos check_conversion(cp, ep);
536 1.11 jtc return val;
537 1.11 jtc }
538 1.11 jtc
539 1.22 kleink static uintmax_t
540 1.23 wiz getuintmax(void)
541 1.11 jtc {
542 1.22 kleink uintmax_t val;
543 1.25 christos char *cp, *ep;
544 1.11 jtc
545 1.25 christos cp = *gargv;
546 1.25 christos if (cp == NULL)
547 1.25 christos return 0;
548 1.25 christos gargv++;
549 1.25 christos
550 1.25 christos if (*cp == '\"' || *cp == '\'')
551 1.25 christos return *(cp+1);
552 1.25 christos
553 1.25 christos /* strtoumax won't error -ve values */
554 1.25 christos while (isspace(*(unsigned char *)cp))
555 1.25 christos cp++;
556 1.25 christos if (*cp == '-') {
557 1.25 christos warnx("%s: expected positive numeric value", cp);
558 1.25 christos rval = 1;
559 1.25 christos return 0;
560 1.25 christos }
561 1.11 jtc
562 1.11 jtc errno = 0;
563 1.25 christos val = strtoumax(cp, &ep, 0);
564 1.25 christos check_conversion(cp, ep);
565 1.4 jtc return val;
566 1.1 cgd }
567 1.1 cgd
568 1.5 jtc static double
569 1.23 wiz getdouble(void)
570 1.1 cgd {
571 1.4 jtc double val;
572 1.4 jtc char *ep;
573 1.1 cgd
574 1.1 cgd if (!*gargv)
575 1.25 christos return (0.0);
576 1.4 jtc
577 1.13 jtc if (**gargv == '\"' || **gargv == '\'')
578 1.13 jtc return (double) *((*gargv++)+1);
579 1.1 cgd
580 1.11 jtc errno = 0;
581 1.25 christos val = strtod(*gargv, &ep);
582 1.12 jtc check_conversion(*gargv++, ep);
583 1.12 jtc return val;
584 1.12 jtc }
585 1.12 jtc
586 1.12 jtc static void
587 1.23 wiz check_conversion(const char *s, const char *ep)
588 1.12 jtc {
589 1.4 jtc if (*ep) {
590 1.12 jtc if (ep == s)
591 1.25 christos warnx("%s: expected numeric value", s);
592 1.11 jtc else
593 1.25 christos warnx("%s: not completely converted", s);
594 1.11 jtc rval = 1;
595 1.11 jtc } else if (errno == ERANGE) {
596 1.25 christos warnx("%s: %s", s, strerror(ERANGE));
597 1.4 jtc rval = 1;
598 1.4 jtc }
599 1.5 jtc }
600 1.5 jtc
601 1.5 jtc static void
602 1.23 wiz usage(void)
603 1.5 jtc {
604 1.5 jtc (void)fprintf(stderr, "usage: printf format [arg ...]\n");
605 1.1 cgd }
606