Denis Vlasenko spotted the lack of bounds checking in my first attempt at
[oweals/busybox.git] / libbb / printf.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * *printf implementations for busybox
4  *
5  * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 /* Mar 12, 2003     Manuel Novoa III
11  *
12  * While fwrite(), fputc(), fputs(), etc. all set the stream error flag
13  * on failure, the *printf functions are unique in that they can fail
14  * for reasons not related to the actual output itself.  Among the possible
15  * reasons for failure which don't set the streams error indicator,
16  * SUSv3 lists EILSEQ, EINVAL, and ENOMEM.
17  *
18  * In some cases, it would be desirable to have a group of *printf()
19  * functions available that _always_ set the stream error indicator on
20  * failure.  That would allow us to defer error checking until applet
21  * exit.  Unfortunately, there is no standard way of setting a streams
22  * error indicator... even though we can clear it with clearerr().
23  */
24
25 /* Mar 22, 2006     Rich Felker III
26  *
27  * Actually there is a portable way to set the error indicator. See below.
28  * It is not thread-safe as written due to a race condition with file
29  * descriptors but since BB is not threaded that does not matter. It can be
30  * made thread-safe at the expense of slightly more code, if this is ever
31  * needed in the future.
32  */
33
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include "libbb.h"
39
40 #ifdef L_bb_vfprintf
41 int bb_vfprintf(FILE * __restrict stream,
42                                            const char * __restrict format,
43                                            va_list arg)
44 {
45         int rv;
46
47         if ((rv = vfprintf(stream, format, arg)) < 0) {
48                 /* The following sequence portably sets the error flag for
49                  * stream on any remotely POSIX-compliant implementation. */
50
51                 int errno_save = errno;
52                 int fd = fileno(stream);
53                 int tmp = dup(fd);
54
55                 fflush(stream);
56                 close(fd);
57                 /* Force an attempted write to nonexistant fd => EBADF */
58                 fputc(0, stream);
59                 fflush(stream);
60                 /* Restore the stream's original fd */
61                 dup2(tmp, fd);
62                 close(tmp);
63                 errno = errno_save;
64         }
65
66         return rv;
67 }
68 #endif
69
70 #ifdef L_bb_vprintf
71 int bb_vprintf(const char * __restrict format, va_list arg)
72 {
73         return bb_vfprintf(stdout, format, arg);
74 }
75 #endif
76
77 #ifdef L_bb_fprintf
78 int bb_fprintf(FILE * __restrict stream,
79                                           const char * __restrict format, ...)
80 {
81         va_list arg;
82         int rv;
83
84         va_start(arg, format);
85         rv = bb_vfprintf(stream, format, arg);
86         va_end(arg);
87
88         return rv;
89 }
90 #endif
91
92 #ifdef L_bb_printf
93 int bb_printf(const char * __restrict format, ...)
94 {
95         va_list arg;
96         int rv;
97
98         va_start(arg, format);
99         rv = bb_vfprintf(stdout, format, arg);
100         va_end(arg);
101
102         return rv;
103 }
104 #endif