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