blob: f0ddb862ef732fc996c2763f56373ff93d7286db [file] [log] [blame]
/* vi: set sw=4 ts=4: */
/*
* *printf implementations for busybox
*
* Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* 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().
*/
/* Mar 22, 2006 Rich Felker III
*
* Actually there is a portable way to set the error indicator. See below.
* It is not thread-safe as written due to a race condition with file
* descriptors but since BB is not threaded that does not matter. It can be
* made thread-safe at the expense of slightly more code, if this is ever
* needed in the future.
*/
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include "libbb.h"
int bb_vfprintf(FILE * __restrict stream,
const char * __restrict format,
va_list arg)
{
int rv;
if ((rv = vfprintf(stream, format, arg)) < 0) {
/* The following sequence portably sets the error flag for
* stream on any remotely POSIX-compliant implementation. */
int errno_save = errno;
int fd = fileno(stream);
int tmp = dup(fd);
fflush(stream);
close(fd);
/* Force an attempted write to nonexistant fd => EBADF */
fputc(0, stream);
fflush(stream);
/* Restore the stream's original fd */
dup2(tmp, fd);
close(tmp);
errno = errno_save;
}
return rv;
}
int bb_vprintf(const char * __restrict format, va_list arg)
{
return bb_vfprintf(stdout, format, arg);
}
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;
}
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;
}