GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/printf/printf.c Lines: 68 187 36.4 %
Date: 2016-12-06 Branches: 38 182 20.9 %

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

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

5
				switch(convch) {
165
				case 'c': {
166
					char p = getchr();
167
					PF(start, p);
168
					break;
169
				}
170
				case 's': {
171
4
					char *p = getstr();
172

4
					PF(start, p);
173
					break;
174
				}
175
				case 'd':
176
				case 'i': {
177
					long p;
178
1
					char *f = mklong(start, convch);
179
1
					if (!f) {
180
						warnx("out of memory");
181
						return (1);
182
					}
183
1
					p = getlong();
184

1
					PF(f, p);
185
					break;
186
				}
187
				case 'o':
188
				case 'u':
189
				case 'x':
190
				case 'X': {
191
					unsigned long p;
192
					char *f = mklong(start, convch);
193
					if (!f) {
194
						warnx("out of memory");
195
						return (1);
196
					}
197
					p = getulong();
198
					PF(f, p);
199
					break;
200
				}
201
				case 'a':
202
				case 'A':
203
				case 'e':
204
				case 'E':
205
				case 'f':
206
				case 'F':
207
				case 'g':
208
				case 'G': {
209
					double p = getdouble();
210
					PF(start, p);
211
					break;
212
				}
213
				default:
214
					warnx ("%s: invalid directive", start);
215
					return(1);
216
				}
217
5
				*(fmt + 1) = nextch;
218
5
				break;
219
220
			case '\\':
221
24
				fmt += print_escape(fmt);
222
24
				break;
223
224
			default:
225
120
				putchar (*fmt);
226
				break;
227
			}
228
		}
229

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



24
	switch (*str) {
289
	case '0': case '1': case '2': case '3':
290
	case '4': case '5': case '6': case '7':
291
		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
292
			value <<= 3;
293
			value += octtobin(*str);
294
		}
295
		putchar(value);
296
		return str - start - 1;
297
		/* NOTREACHED */
298
299
	case 'x':
300
		str++;
301
		for (value = 0; isxdigit((unsigned char)*str); str++) {
302
			value <<= 4;
303
			value += hextobin(*str);
304
		}
305
		if (value > UCHAR_MAX) {
306
			warnx ("escape sequence out of range for character");
307
			rval = 1;
308
		}
309
		putchar (value);
310
		return str - start - 1;
311
		/* NOTREACHED */
312
313
	case '\\':			/* backslash */
314
		putchar('\\');
315
		break;
316
317
	case '\'':			/* single quote */
318
		putchar('\'');
319
		break;
320
321
	case '"':			/* double quote */
322
		putchar('"');
323
		break;
324
325
	case 'a':			/* alert */
326
		putchar('\a');
327
		break;
328
329
	case 'b':			/* backspace */
330
		putchar('\b');
331
		break;
332
333
	case 'e':			/* escape */
334
#ifdef __GNUC__
335
		putchar('\e');
336
#else
337
		putchar(033);
338
#endif
339
		break;
340
341
	case 'f':			/* form-feed */
342
		putchar('\f');
343
		break;
344
345
	case 'n':			/* newline */
346
24
		putchar('\n');
347
		break;
348
349
	case 'r':			/* carriage-return */
350
		putchar('\r');
351
		break;
352
353
	case 't':			/* tab */
354
		putchar('\t');
355
		break;
356
357
	case 'v':			/* vertical-tab */
358
		putchar('\v');
359
		break;
360
361
	case '\0':
362
		warnx("null escape sequence");
363
		rval = 1;
364
		return 0;
365
366
	default:
367
		putchar(*str);
368
		warnx("unknown escape sequence `\\%c'", *str);
369
		rval = 1;
370
	}
371
372
24
	return 1;
373
}
374
375
static char *
376
mklong(const char *str, int ch)
377
1
{
378
	static char *copy;
379
	static int copysize;
380
	int len;
381
382
1
	len = strlen(str) + 2;
383
1
	if (copysize < len) {
384
		char *newcopy;
385
1
		copysize = len + 256;
386
387
1
		newcopy = realloc(copy, copysize);
388
1
		if (newcopy == NULL) {
389
			copysize = 0;
390
			free(copy);
391
			copy = NULL;
392
			return (NULL);
393
		}
394
1
		copy = newcopy;
395
	}
396
1
	(void) memmove(copy, str, len - 3);
397
1
	copy[len - 3] = 'l';
398
1
	copy[len - 2] = ch;
399
1
	copy[len - 1] = '\0';
400
1
	return (copy);
401
}
402
403
static int
404
getchr(void)
405
{
406
	if (!*gargv)
407
		return((int)'\0');
408
	return((int)**gargv++);
409
}
410
411
static char *
412
getstr(void)
413
4
{
414
4
	if (!*gargv)
415
		return("");
416
4
	return(*gargv++);
417
}
418
419
static char *number = "+-.0123456789";
420
static int
421
getint(void)
422
{
423
	if (!*gargv)
424
		return(0);
425
426
	if (strchr(number, **gargv))
427
		return(atoi(*gargv++));
428
429
	return 0;
430
}
431
432
static long
433
getlong(void)
434
1
{
435
	long val;
436
	char *ep;
437
438
1
	if (!*gargv)
439
		return(0L);
440
441
1
	if (**gargv == '\"' || **gargv == '\'')
442
		return (long) *((*gargv++)+1);
443
444
1
	errno = 0;
445
1
	val = strtol (*gargv, &ep, 0);
446
1
	check_conversion(*gargv++, ep);
447
1
	return val;
448
}
449
450
static unsigned long
451
getulong(void)
452
{
453
	unsigned long val;
454
	char *ep;
455
456
	if (!*gargv)
457
		return(0UL);
458
459
	if (**gargv == '\"' || **gargv == '\'')
460
		return (unsigned long) *((*gargv++)+1);
461
462
	errno = 0;
463
	val = strtoul (*gargv, &ep, 0);
464
	check_conversion(*gargv++, ep);
465
	return val;
466
}
467
468
static double
469
getdouble(void)
470
{
471
	double val;
472
	char *ep;
473
474
	if (!*gargv)
475
		return(0.0);
476
477
	if (**gargv == '\"' || **gargv == '\'')
478
		return (double) *((*gargv++)+1);
479
480
	errno = 0;
481
	val = strtod (*gargv, &ep);
482
	check_conversion(*gargv++, ep);
483
	return val;
484
}
485
486
static void
487
check_conversion(const char *s, const char *ep)
488
1
{
489
1
	if (*ep) {
490
		if (ep == s)
491
			warnx ("%s: expected numeric value", s);
492
		else
493
			warnx ("%s: not completely converted", s);
494
		rval = 1;
495
1
	} else if (errno == ERANGE) {
496
		warnc(ERANGE, "%s", s);
497
		rval = 1;
498
	}
499
1
}
500
501
static void
502
usage(void)
503
{
504
	(void)fprintf(stderr, "usage: printf format [argument ...]\n");
505
}