GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/printf/printf.c Lines: 90 187 48.1 %
Date: 2017-11-13 Branches: 65 222 29.3 %

Line Branch Exec Source
1
/*	$OpenBSD: printf.c,v 1.26 2016/11/18 15:53:16 schwarze Exp $	*/
2
3
/*
4
 * Copyright (c) 1989 The Regents of the University of California.
5
 * 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 <ctype.h>
33
#include <err.h>
34
#include <errno.h>
35
#include <limits.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
static int	 print_escape_str(const char *);
42
static int	 print_escape(const char *);
43
44
static int	 getchr(void);
45
static double	 getdouble(void);
46
static int	 getint(void);
47
static long	 getlong(void);
48
static unsigned long getulong(void);
49
static char	*getstr(void);
50
static char	*mklong(const char *, int);
51
static void      check_conversion(const char *, const char *);
52
static void __dead usage(void);
53
54
static int	rval;
55
static char  **gargv;
56
57
#define isodigit(c)	((c) >= '0' && (c) <= '7')
58
#define octtobin(c)	((c) - '0')
59
#define hextobin(c)	((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
60
61
#define PF(f, func) { \
62
	if (havefieldwidth) \
63
		if (haveprecision) \
64
			(void)printf(f, fieldwidth, precision, func); \
65
		else \
66
			(void)printf(f, fieldwidth, func); \
67
	else if (haveprecision) \
68
		(void)printf(f, precision, func); \
69
	else \
70
		(void)printf(f, func); \
71
}
72
73
int
74
main(int argc, char *argv[])
75
{
76
	char *fmt, *start;
77
	int havefieldwidth, haveprecision;
78
	int fieldwidth, precision;
79
	char convch, nextch;
80
	char *format;
81
82
5312
	if (pledge("stdio flock rpath cpath wpath", NULL) == -1)
83
		err(1, "pledge");
84
85
	/* Need to accept/ignore "--" option. */
86

5312
	if (argc > 1 && strcmp(argv[1], "--") == 0) {
87
		argc--;
88
		argv++;
89
	}
90
91
2656
	if (argc < 2)
92
		usage();
93
94
2656
	format = *++argv;
95
2656
	gargv = ++argv;
96
97
#define SKIP1	"#-+ 0"
98
#define SKIP2	"0123456789"
99
2656
	do {
100
		/*
101
		 * Basic algorithm is to scan the format string for conversion
102
		 * specifications -- once one is found, find out if the field
103
		 * width or precision is a '*'; if it is, gather up value.
104
		 * Note, format strings are reused as necessary to use up the
105
		 * provided arguments, arguments of zero/null string are
106
		 * provided to use up the format string.
107
		 */
108
109
		/* find next format specification */
110
169772
		for (fmt = format; *fmt; fmt++) {
111
82230
			switch (*fmt) {
112
			case '%':
113
943
				start = fmt++;
114
115
943
				if (*fmt == '%') {
116
20
					putchar ('%');
117
					break;
118
933
				} else if (*fmt == 'b') {
119
					char *p = getstr();
120
					if (print_escape_str(p)) {
121
						return (rval);
122
					}
123
					break;
124
				}
125
126
				/* skip to field width */
127
1906
				for (; strchr(SKIP1, *fmt); ++fmt)
128
					;
129
933
				if (*fmt == '*') {
130
					++fmt;
131
					havefieldwidth = 1;
132
					fieldwidth = getint();
133
				} else
134
					havefieldwidth = 0;
135
136
				/* skip to field precision */
137
2752
				for (; strchr(SKIP2, *fmt); ++fmt)
138
					;
139
				haveprecision = 0;
140
933
				if (*fmt == '.') {
141
204
					++fmt;
142
204
					if (*fmt == '*') {
143
						++fmt;
144
						haveprecision = 1;
145
						precision = getint();
146
					}
147
1224
					for (; strchr(SKIP2, *fmt); ++fmt)
148
						;
149
				}
150
151
933
				if (!*fmt) {
152
					warnx ("missing format character");
153
					return(1);
154
				}
155
156
				convch = *fmt;
157
933
				nextch = *(fmt + 1);
158
933
				*(fmt + 1) = '\0';
159




933
				switch(convch) {
160
				case 'c': {
161
					char p = getchr();
162
					PF(start, p);
163
					break;
164
				}
165
				case 's': {
166
428
					char *p = getstr();
167

1284
					PF(start, p);
168
					break;
169
				}
170
				case 'd':
171
				case 'i': {
172
					long p;
173
5
					char *f = mklong(start, convch);
174
5
					if (!f) {
175
						warnx("out of memory");
176
						return (1);
177
					}
178
5
					p = getlong();
179

15
					PF(f, p);
180
5
					break;
181
				}
182
				case 'o':
183
				case 'u':
184
				case 'x':
185
				case 'X': {
186
					unsigned long p;
187
500
					char *f = mklong(start, convch);
188
500
					if (!f) {
189
						warnx("out of memory");
190
						return (1);
191
					}
192
500
					p = getulong();
193

1500
					PF(f, p);
194
500
					break;
195
				}
196
				case 'a':
197
				case 'A':
198
				case 'e':
199
				case 'E':
200
				case 'f':
201
				case 'F':
202
				case 'g':
203
				case 'G': {
204
					double p = getdouble();
205
					PF(start, p);
206
					break;
207
				}
208
				default:
209
					warnx ("%s: invalid directive", start);
210
					return(1);
211
				}
212
933
				*(fmt + 1) = nextch;
213
933
				break;
214
215
			case '\\':
216
6251
				fmt += print_escape(fmt);
217
6251
				break;
218
219
			default:
220
150072
				putchar (*fmt);
221
				break;
222
			}
223
		}
224

3385
	} while (gargv > argv && *gargv);
225
226
2656
	return (rval);
227
2656
}
228
229
230
/*
231
 * Print SysV echo(1) style escape string
232
 *	Halts processing string and returns 1 if a \c escape is encountered.
233
 */
234
static int
235
print_escape_str(const char *str)
236
{
237
	int value;
238
	int c;
239
240
	while (*str) {
241
		if (*str == '\\') {
242
			str++;
243
			/*
244
			 * %b string octal constants are not like those in C.
245
			 * They start with a \0, and are followed by 0, 1, 2,
246
			 * or 3 octal digits.
247
			 */
248
			if (*str == '0') {
249
				str++;
250
				for (c = 3, value = 0; c-- && isodigit(*str); str++) {
251
					value <<= 3;
252
					value += octtobin(*str);
253
				}
254
				putchar (value);
255
				str--;
256
			} else if (*str == 'c') {
257
				return 1;
258
			} else {
259
				str--;
260
				str += print_escape(str);
261
			}
262
		} else {
263
			putchar (*str);
264
		}
265
		str++;
266
	}
267
268
	return 0;
269
}
270
271
/*
272
 * Print "standard" escape characters
273
 */
274
static int
275
print_escape(const char *str)
276
{
277
	const char *start = str;
278
	int value;
279
	int c;
280
281
12502
	str++;
282
283





6251
	switch (*str) {
284
	case '0': case '1': case '2': case '3':
285
	case '4': case '5': case '6': case '7':
286

408
		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
287
51
			value <<= 3;
288
51
			value += octtobin(*str);
289
		}
290
102
		putchar(value);
291
51
		return str - start - 1;
292
		/* NOTREACHED */
293
294
	case 'x':
295
		str++;
296
		for (value = 0; isxdigit((unsigned char)*str); str++) {
297
			value <<= 4;
298
			value += hextobin(*str);
299
		}
300
		if (value > UCHAR_MAX) {
301
			warnx ("escape sequence out of range for character");
302
			rval = 1;
303
		}
304
		putchar (value);
305
		return str - start - 1;
306
		/* NOTREACHED */
307
308
	case '\\':			/* backslash */
309
306
		putchar('\\');
310
		break;
311
312
	case '\'':			/* single quote */
313
		putchar('\'');
314
		break;
315
316
	case '"':			/* double quote */
317
		putchar('"');
318
		break;
319
320
	case 'a':			/* alert */
321
		putchar('\a');
322
		break;
323
324
	case 'b':			/* backspace */
325
		putchar('\b');
326
		break;
327
328
	case 'e':			/* escape */
329
#ifdef __GNUC__
330
		putchar('\e');
331
#else
332
		putchar(033);
333
#endif
334
		break;
335
336
	case 'f':			/* form-feed */
337
		putchar('\f');
338
		break;
339
340
	case 'n':			/* newline */
341
9676
		putchar('\n');
342
		break;
343
344
	case 'r':			/* carriage-return */
345
		putchar('\r');
346
		break;
347
348
	case 't':			/* tab */
349
2418
		putchar('\t');
350
		break;
351
352
	case 'v':			/* vertical-tab */
353
		putchar('\v');
354
		break;
355
356
	case '\0':
357
		warnx("null escape sequence");
358
		rval = 1;
359
		return 0;
360
361
	default:
362
		putchar(*str);
363
		warnx("unknown escape sequence `\\%c'", *str);
364
		rval = 1;
365
	}
366
367
6200
	return 1;
368
6251
}
369
370
static char *
371
mklong(const char *str, int ch)
372
{
373
	static char *copy;
374
	static int copysize;
375
	int len;
376
377
1010
	len = strlen(str) + 2;
378
505
	if (copysize < len) {
379
		char *newcopy;
380
505
		copysize = len + 256;
381
382
505
		newcopy = realloc(copy, copysize);
383
505
		if (newcopy == NULL) {
384
			copysize = 0;
385
			free(copy);
386
			copy = NULL;
387
			return (NULL);
388
		}
389
505
		copy = newcopy;
390
505
	}
391
505
	(void) memmove(copy, str, len - 3);
392
505
	copy[len - 3] = 'l';
393
505
	copy[len - 2] = ch;
394
505
	copy[len - 1] = '\0';
395
505
	return (copy);
396
505
}
397
398
static int
399
getchr(void)
400
{
401
	if (!*gargv)
402
		return((int)'\0');
403
	return((int)**gargv++);
404
}
405
406
static char *
407
getstr(void)
408
{
409
856
	if (!*gargv)
410
		return("");
411
428
	return(*gargv++);
412
428
}
413
414
static char *number = "+-.0123456789";
415
static int
416
getint(void)
417
{
418
	if (!*gargv)
419
		return(0);
420
421
	if (strchr(number, **gargv))
422
		return(atoi(*gargv++));
423
424
	return 0;
425
}
426
427
static long
428
getlong(void)
429
{
430
	long val;
431
10
	char *ep;
432
433
5
	if (!*gargv)
434
		return(0L);
435
436

10
	if (**gargv == '\"' || **gargv == '\'')
437
		return (unsigned char) *((*gargv++)+1);
438
439
5
	errno = 0;
440
5
	val = strtol (*gargv, &ep, 0);
441
5
	check_conversion(*gargv++, ep);
442
5
	return val;
443
5
}
444
445
static unsigned long
446
getulong(void)
447
{
448
	unsigned long val;
449
1000
	char *ep;
450
451
500
	if (!*gargv)
452
		return(0UL);
453
454

1000
	if (**gargv == '\"' || **gargv == '\'')
455
		return (unsigned char) *((*gargv++)+1);
456
457
500
	errno = 0;
458
500
	val = strtoul (*gargv, &ep, 0);
459
500
	check_conversion(*gargv++, ep);
460
500
	return val;
461
500
}
462
463
static double
464
getdouble(void)
465
{
466
	double val;
467
	char *ep;
468
469
	if (!*gargv)
470
		return(0.0);
471
472
	if (**gargv == '\"' || **gargv == '\'')
473
		return (unsigned char) *((*gargv++)+1);
474
475
	errno = 0;
476
	val = strtod (*gargv, &ep);
477
	check_conversion(*gargv++, ep);
478
	return val;
479
}
480
481
static void
482
check_conversion(const char *s, const char *ep)
483
{
484
1010
	if (*ep) {
485
		if (ep == s)
486
			warnx ("%s: expected numeric value", s);
487
		else
488
			warnx ("%s: not completely converted", s);
489
		rval = 1;
490
505
	} else if (errno == ERANGE) {
491
		warnc(ERANGE, "%s", s);
492
		rval = 1;
493
	}
494
505
}
495
496
static void __dead
497
usage(void)
498
{
499
	(void)fprintf(stderr, "usage: printf format [argument ...]\n");
500
	exit(1);
501
}