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