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