diff options
Diffstat (limited to 'libbb/printf.c')
-rw-r--r-- | libbb/printf.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/libbb/printf.c b/libbb/printf.c new file mode 100644 index 000000000..686257699 --- /dev/null +++ b/libbb/printf.c | |||
@@ -0,0 +1,177 @@ | |||
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 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | /* Mar 12, 2003 Manuel Novoa III | ||
24 | * | ||
25 | * While fwrite(), fputc(), fputs(), etc. all set the stream error flag | ||
26 | * on failure, the *printf functions are unique in that they can fail | ||
27 | * for reasons not related to the actual output itself. Among the possible | ||
28 | * reasons for failure which don't set the streams error indicator, | ||
29 | * SUSv3 lists EILSEQ, EINVAL, and ENOMEM. | ||
30 | * | ||
31 | * In some cases, it would be desireable to have a group of *printf() | ||
32 | * functions available that _always_ set the stream error indicator on | ||
33 | * failure. That would allow us to defer error checking until applet | ||
34 | * exit. Unfortunately, there is no standard way of setting a streams | ||
35 | * error indicator... even though we can clear it with clearerr(). | ||
36 | * | ||
37 | * Therefore, we have to resort to implementation dependent code. Feel | ||
38 | * free to send patches for stdio implementations where the following | ||
39 | * fails. | ||
40 | * | ||
41 | * NOTE: None of this is threadsafe. As busybox is a nonthreaded app, | ||
42 | * that isn't currently an issue. | ||
43 | */ | ||
44 | |||
45 | #include <stdio.h> | ||
46 | #include <stdarg.h> | ||
47 | #include "libbb.h" | ||
48 | |||
49 | #if defined(__UCLIBC__) | ||
50 | |||
51 | # if defined(__FLAG_ERROR) | ||
52 | /* Using my newer stdio implementation. Unlocked macros are: | ||
53 | * #define __CLEARERR(stream) \ | ||
54 | ((stream)->modeflags &= ~(__FLAG_EOF|__FLAG_ERROR), (void)0) | ||
55 | * #define __FEOF(stream) ((stream)->modeflags & __FLAG_EOF) | ||
56 | * #define __FERROR(stream) ((stream)->modeflags & __FLAG_ERROR) | ||
57 | */ | ||
58 | #define SET_FERROR_UNLOCKED(S) ((S)->modeflags |= __FLAG_ERROR) | ||
59 | |||
60 | #elif defined(__MODE_ERR) | ||
61 | /* Using either the original stdio implementation (from dev86) or | ||
62 | * my original stdio rewrite. Macros were: | ||
63 | * #define ferror(fp) (((fp)->mode&__MODE_ERR) != 0) | ||
64 | * #define feof(fp) (((fp)->mode&__MODE_EOF) != 0) | ||
65 | * #define clearerr(fp) ((fp)->mode &= ~(__MODE_EOF|__MODE_ERR),0) | ||
66 | */ | ||
67 | #define SET_FERROR_UNLOCKED(S) ((S)->mode |= __MODE_ERR) | ||
68 | |||
69 | #else | ||
70 | #error unknown uClibc stdio implemenation! | ||
71 | #endif | ||
72 | |||
73 | #elif defined(__GLIBC__) | ||
74 | |||
75 | # if defined(_STDIO_USES_IOSTREAM) | ||
76 | /* Apparently using the newer libio implementation, with associated defines: | ||
77 | * #define _IO_feof_unlocked(__fp) (((__fp)->_flags & _IO_EOF_SEEN) != 0) | ||
78 | * #define _IO_ferror_unlocked(__fp) (((__fp)->_flags & _IO_ERR_SEEN) != 0) | ||
79 | */ | ||
80 | #define SET_FERROR_UNLOCKED(S) ((S)->_flags |= _IO_ERR_SEEN) | ||
81 | |||
82 | # else | ||
83 | /* Assume the older version of glibc which used a bitfield entry | ||
84 | * as a stream error flag. The associated defines were: | ||
85 | * #define __clearerr(stream) ((stream)->__error = (stream)->__eof = 0) | ||
86 | * #define feof_unlocked(stream) ((stream)->__eof != 0) | ||
87 | * #define ferror_unlocked(stream) ((stream)->__error != 0) | ||
88 | */ | ||
89 | #define SET_FERROR_UNLOCKED(S) ((S)->__error = 1) | ||
90 | |||
91 | # endif | ||
92 | |||
93 | #elif defined(__NEWLIB_H__) | ||
94 | /* I honestly don't know if there are different versions of stdio in | ||
95 | * newlibs history. Anyway, here's what's current. | ||
96 | * #define __sfeof(p) (((p)->_flags & __SEOF) != 0) | ||
97 | * #define __sferror(p) (((p)->_flags & __SERR) != 0) | ||
98 | * #define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF))) | ||
99 | */ | ||
100 | #define SET_FERROR_UNLOCKED(S) ((S)->_flags |= __SERR) | ||
101 | |||
102 | #elif defined(__dietlibc__) | ||
103 | /* | ||
104 | * WARNING!!! dietlibc is quite buggy. WARNING!!! | ||
105 | * | ||
106 | * Some example bugs as of March 12, 2003... | ||
107 | * 1) fputc() doesn't set the error indicator on failure. | ||
108 | * 2) freopen() doesn't maintain the same stream object, contary to | ||
109 | * standards. This makes it useless in its primary role of | ||
110 | * reassociating stdin/stdout/stderr. | ||
111 | * 3) printf() often fails to correctly format output when conversions | ||
112 | * involve padding. It is also practically useless for floating | ||
113 | * point output. | ||
114 | * | ||
115 | * But, if you're determined to use it anyway, (as of the current version) | ||
116 | * you can extract the information you need from dietstdio.h. See the | ||
117 | * other library implementations for examples. | ||
118 | */ | ||
119 | #error dietlibc is currently not supported. Please see the commented source. | ||
120 | |||
121 | #else /* some other lib */ | ||
122 | /* Please see the comments for the above supported libaries for examples | ||
123 | * of what is required to support your stdio implementation. | ||
124 | */ | ||
125 | #error Your stdio library is currently not supported. Please see the commented source. | ||
126 | #endif | ||
127 | |||
128 | #ifdef L_vfprintf | ||
129 | extern int bb_vfprintf(FILE * __restrict stream, | ||
130 | const char * __restrict format, | ||
131 | va_list arg) | ||
132 | { | ||
133 | int rv; | ||
134 | |||
135 | if ((rv = vfprintf(stream, format, arg)) < 0) { | ||
136 | SET_FERROR_UNLOCKED(stream); | ||
137 | } | ||
138 | |||
139 | return rv; | ||
140 | } | ||
141 | #endif | ||
142 | |||
143 | #ifdef L_vprintf | ||
144 | extern int bb_vprintf(const char * __restrict format, va_list arg) | ||
145 | { | ||
146 | return bb_vfprintf(stdout, format, arg); | ||
147 | } | ||
148 | #endif | ||
149 | |||
150 | #ifdef L_fprintf | ||
151 | extern int bb_fprintf(FILE * __restrict stream, | ||
152 | const char * __restrict format, ...) | ||
153 | { | ||
154 | va_list arg; | ||
155 | int rv; | ||
156 | |||
157 | va_start(arg, format); | ||
158 | rv = bb_vfprintf(stream, format, arg); | ||
159 | va_end(arg); | ||
160 | |||
161 | return rv; | ||
162 | } | ||
163 | #endif | ||
164 | |||
165 | #ifdef L_printf | ||
166 | extern int bb_printf(const char * __restrict format, ...) | ||
167 | { | ||
168 | va_list arg; | ||
169 | int rv; | ||
170 | |||
171 | va_start(arg, format); | ||
172 | rv = bb_vfprintf(stdout, format, arg); | ||
173 | va_end(arg); | ||
174 | |||
175 | return rv; | ||
176 | } | ||
177 | #endif | ||