GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
} |
Generated by: GCOVR (Version 3.3) |