Commit df9f3030 authored by philip's avatar philip Committed by Nathaniel Wesley Filardo
Browse files

Add snprintf and remove our sprintf

parent 5b6b2f98
......@@ -22,7 +22,7 @@ endif
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
DEFINES += -DHAVE_CONFIG_H
#############################################################
# Recursion Magic - Don't touch this!!
......
#ifndef SNP_CONFIG_H
#define SNP_CONFIG_H
#define TEST_SNPRINTF 0
#define HAVE_SNPRINTF 0
#define HAVE_VSNPRINTF 0
#define HAVE_ASPRINTF 0
#define HAVE_VASPRINTF 0
#define HAVE_STDARG_H 1
#define HAVE_STDLIB_H 1
#define HAVE_VA_COPY 0
#define HAVE___VA_COPY 0
#define HAVE_FLOAT_H 1
#define HAVE_INTTYPES_H 1
#define HAVE_LOCALE_H 0
#define HAVE_STDDEF_H 1
#define HAVE_STDINT_H 1
#define HAVE_UNSIGNED_LONG_LONG_INT 1
#define HAVE_UINTMAX_T 1
#define HAVE_LONG_DOUBLE 0
#define HAVE_LONG_LONG_INT 1
#define HAVE_INTMAX_T 1
#define HAVE_UINTPTR_T 1
#define HAVE_PTRDIFF_T 1
#define HAVE_LOCALECONV 0
#define HAVE_LCONV_DECIMAL_POINT 0
#define HAVE_LCONV_THOUSANDS_SEP 0
#define LDOUBLE_MIN_10_EXP DBL_MIN_10_EXP
#define LDOUBLE_MAX_10_EXP DBL_MAX_10_EXP
#define rpl_snprintf snprintf
#define rpl_vsnprintf vsnprintf
#define rpl_vasprintf vasprintf
#define rpl_asprintf asprintf
#endif /* SNP_CONFIG_H */
/*
* Copyright (c) 1995 Patrick Powell.
*
* This code is based on code written by Patrick Powell <papowell@astart.com>.
* It may be used for any purpose as long as this notice remains intact on all
* source code distributions.
*/
/*
* Copyright (c) 2008 Holger Weiss.
*
* This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
* My changes to the code may freely be used, modified and/or redistributed for
* any purpose. It would be nice if additions and fixes to this file (including
* trivial code cleanups) would be sent back in order to let me include them in
* the version available at <http://www.jhweiss.de/software/snprintf.html>.
* However, this is not a requirement for using or redistributing (possibly
* modified) versions of this file, nor is leaving this notice intact mandatory.
*/
/*
* History
*
* 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
*
* Fixed the detection of infinite floating point values on IRIX (and
* possibly other systems) and applied another few minor cleanups.
*
* 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
*
* Added a lot of new features, fixed many bugs, and incorporated various
* improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
* <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
* <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
* projects. The additions include: support the "e", "E", "g", "G", and
* "F" conversion specifiers (and use conversion style "f" or "F" for the
* still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
* "t", and "z" length modifiers; support the "#" flag and the (non-C99)
* "'" flag; use localeconv(3) (if available) to get both the current
* locale's decimal point character and the separator between groups of
* digits; fix the handling of various corner cases of field width and
* precision specifications; fix various floating point conversion bugs;
* handle infinite and NaN floating point values; don't attempt to write to
* the output buffer (which may be NULL) if a size of zero was specified;
* check for integer overflow of the field width, precision, and return
* values and during the floating point conversion; use the OUTCHAR() macro
* instead of a function for better performance; provide asprintf(3) and
* vasprintf(3) functions; add new test cases. The replacement functions
* have been renamed to use an "rpl_" prefix, the function calls in the
* main project (and in this file) must be redefined accordingly for each
* replacement function which is needed (by using Autoconf or other means).
* Various other minor improvements have been applied and the coding style
* was cleaned up for consistency.
*
* 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
*
* C99 compliant snprintf(3) and vsnprintf(3) functions return the number
* of characters that would have been written to a sufficiently sized
* buffer (excluding the '\0'). The original code simply returned the
* length of the resulting output string, so that's been fixed.
*
* 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
*
* The original code assumed that both snprintf(3) and vsnprintf(3) were
* missing. Some systems only have snprintf(3) but not vsnprintf(3), so
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
*
* 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
*
* The PGP code was using unsigned hexadecimal formats. Unfortunately,
* unsigned formats simply didn't work.
*
* 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
*
* Ok, added some minimal floating point support, which means this probably
* requires libm on most operating systems. Don't yet support the exponent
* (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just
* wasn't being exercised in ways which showed it, so that's been fixed.
* Also, formatted the code to Mutt conventions, and removed dead code left
* over from the original. Also, there is now a builtin-test, run with:
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
*
* 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
*
* This was ugly. It is still ugly. I opted out of floating point
* numbers, but the formatter understands just about everything from the
* normal C string format, at least as far as I can tell from the Solaris
* 2.5 printf(3S) man page.
*/
/*
* ToDo
*
* - Add wide character support.
* - Add support for "%a" and "%A" conversions.
* - Create test routines which predefine the expected results. Our test cases
* usually expose bugs in system implementations rather than in ours :-)
*/
/*
* Usage
*
* 1) The following preprocessor macros should be defined to 1 if the feature or
* file in question is available on the target system (by using Autoconf or
* other means), though basic functionality should be available as long as
* HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
*
* HAVE_VSNPRINTF
* HAVE_SNPRINTF
* HAVE_VASPRINTF
* HAVE_ASPRINTF
* HAVE_STDARG_H
* HAVE_STDDEF_H
* HAVE_STDINT_H
* HAVE_STDLIB_H
* HAVE_FLOAT_H
* HAVE_INTTYPES_H
* HAVE_LOCALE_H
* HAVE_LOCALECONV
* HAVE_LCONV_DECIMAL_POINT
* HAVE_LCONV_THOUSANDS_SEP
* HAVE_LONG_DOUBLE
* HAVE_LONG_LONG_INT
* HAVE_UNSIGNED_LONG_LONG_INT
* HAVE_INTMAX_T
* HAVE_UINTMAX_T
* HAVE_UINTPTR_T
* HAVE_PTRDIFF_T
* HAVE_VA_COPY
* HAVE___VA_COPY
*
* 2) The calls to the functions which should be replaced must be redefined
* throughout the project files (by using Autoconf or other means):
*
* #define vsnprintf rpl_vsnprintf
* #define snprintf rpl_snprintf
* #define vasprintf rpl_vasprintf
* #define asprintf rpl_asprintf
*
* 3) The required replacement functions should be declared in some header file
* included throughout the project files:
*
* #if HAVE_CONFIG_H
* #include <config.h>
* #endif
* #if HAVE_STDARG_H
* #include <stdarg.h>
* #if !HAVE_VSNPRINTF
* int rpl_vsnprintf(char *, size_t, const char *, va_list);
* #endif
* #if !HAVE_SNPRINTF
* int rpl_snprintf(char *, size_t, const char *, ...);
* #endif
* #if !HAVE_VASPRINTF
* int rpl_vasprintf(char **, const char *, va_list);
* #endif
* #if !HAVE_ASPRINTF
* int rpl_asprintf(char **, const char *, ...);
* #endif
* #endif
*
* Autoconf macros for handling step 1 and step 2 are available at
* <http://www.jhweiss.de/software/snprintf.html>.
*/
#if HAVE_CONFIG_H
#include "snp_config.h"
#endif /* HAVE_CONFIG_H */
#if TEST_SNPRINTF
#include <math.h> /* For pow(3), NAN, and INFINITY. */
#include <string.h> /* For strcmp(3). */
#if defined(__NetBSD__) || \
defined(__FreeBSD__) || \
defined(__OpenBSD__) || \
defined(__NeXT__) || \
defined(__bsd__)
#define OS_BSD 1
#elif defined(sgi) || defined(__sgi)
#ifndef __c99
#define __c99 /* Force C99 mode to get <stdint.h> included on IRIX 6.5.30. */
#endif /* !defined(__c99) */
#define OS_IRIX 1
#define OS_SYSV 1
#elif defined(__svr4__)
#define OS_SYSV 1
#elif defined(__linux__)
#define OS_LINUX 1
#endif /* defined(__NetBSD__) || defined(__FreeBSD__) || [...] */
#if HAVE_CONFIG_H /* Undefine definitions possibly done in config.h. */
#ifdef HAVE_SNPRINTF
#undef HAVE_SNPRINTF
#endif /* defined(HAVE_SNPRINTF) */
#ifdef HAVE_VSNPRINTF
#undef HAVE_VSNPRINTF
#endif /* defined(HAVE_VSNPRINTF) */
#ifdef HAVE_ASPRINTF
#undef HAVE_ASPRINTF
#endif /* defined(HAVE_ASPRINTF) */
#ifdef HAVE_VASPRINTF
#undef HAVE_VASPRINTF
#endif /* defined(HAVE_VASPRINTF) */
#ifdef snprintf
#undef snprintf
#endif /* defined(snprintf) */
#ifdef vsnprintf
#undef vsnprintf
#endif /* defined(vsnprintf) */
#ifdef asprintf
#undef asprintf
#endif /* defined(asprintf) */
#ifdef vasprintf
#undef vasprintf
#endif /* defined(vasprintf) */
#else /* By default, we assume a modern system for testing. */
#ifndef HAVE_STDARG_H
#define HAVE_STDARG_H 1
#endif /* HAVE_STDARG_H */
#ifndef HAVE_STDDEF_H
#define HAVE_STDDEF_H 1
#endif /* HAVE_STDDEF_H */
#ifndef HAVE_STDINT_H
#define HAVE_STDINT_H 1
#endif /* HAVE_STDINT_H */
#ifndef HAVE_STDLIB_H
#define HAVE_STDLIB_H 1
#endif /* HAVE_STDLIB_H */
#ifndef HAVE_FLOAT_H
#define HAVE_FLOAT_H 1
#endif /* HAVE_FLOAT_H */
#ifndef HAVE_INTTYPES_H
#define HAVE_INTTYPES_H 1
#endif /* HAVE_INTTYPES_H */
#ifndef HAVE_LOCALE_H
#define HAVE_LOCALE_H 1
#endif /* HAVE_LOCALE_H */
#ifndef HAVE_LOCALECONV
#define HAVE_LOCALECONV 1
#endif /* !defined(HAVE_LOCALECONV) */
#ifndef HAVE_LCONV_DECIMAL_POINT
#define HAVE_LCONV_DECIMAL_POINT 1
#endif /* HAVE_LCONV_DECIMAL_POINT */
#ifndef HAVE_LCONV_THOUSANDS_SEP
#define HAVE_LCONV_THOUSANDS_SEP 1
#endif /* HAVE_LCONV_THOUSANDS_SEP */
#ifndef HAVE_LONG_DOUBLE
#define HAVE_LONG_DOUBLE 1
#endif /* !defined(HAVE_LONG_DOUBLE) */
#ifndef HAVE_LONG_LONG_INT
#define HAVE_LONG_LONG_INT 1
#endif /* !defined(HAVE_LONG_LONG_INT) */
#ifndef HAVE_UNSIGNED_LONG_LONG_INT
#define HAVE_UNSIGNED_LONG_LONG_INT 1
#endif /* !defined(HAVE_UNSIGNED_LONG_LONG_INT) */
#ifndef HAVE_INTMAX_T
#define HAVE_INTMAX_T 1
#endif /* !defined(HAVE_INTMAX_T) */
#ifndef HAVE_UINTMAX_T
#define HAVE_UINTMAX_T 1
#endif /* !defined(HAVE_UINTMAX_T) */
#ifndef HAVE_UINTPTR_T
#define HAVE_UINTPTR_T 1
#endif /* !defined(HAVE_UINTPTR_T) */
#ifndef HAVE_PTRDIFF_T
#define HAVE_PTRDIFF_T 1
#endif /* !defined(HAVE_PTRDIFF_T) */
#ifndef HAVE_VA_COPY
#define HAVE_VA_COPY 1
#endif /* !defined(HAVE_VA_COPY) */
#ifndef HAVE___VA_COPY
#define HAVE___VA_COPY 1
#endif /* !defined(HAVE___VA_COPY) */
#endif /* HAVE_CONFIG_H */
#define snprintf rpl_snprintf
#define vsnprintf rpl_vsnprintf
#define asprintf rpl_asprintf
#define vasprintf rpl_vasprintf
#endif /* TEST_SNPRINTF */
#if !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || !HAVE_VASPRINTF
#include <stdio.h> /* For NULL, size_t, vsnprintf(3), and vasprintf(3). */
#ifdef VA_START
#undef VA_START
#endif /* defined(VA_START) */
#ifdef VA_SHIFT
#undef VA_SHIFT
#endif /* defined(VA_SHIFT) */
#if HAVE_STDARG_H
#include <stdarg.h>
#define VA_START(ap, last) va_start(ap, last)
#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
#else /* Assume <varargs.h> is available. */
#include <varargs.h>
#define VA_START(ap, last) va_start(ap) /* "last" is ignored. */
#define VA_SHIFT(ap, value, type) value = va_arg(ap, type)
#endif /* HAVE_STDARG_H */
#if !HAVE_VASPRINTF
#if HAVE_STDLIB_H
#include <stdlib.h> /* For malloc(3). */
#endif /* HAVE_STDLIB_H */
#ifdef VA_COPY
#undef VA_COPY
#endif /* defined(VA_COPY) */
#ifdef VA_END_COPY
#undef VA_END_COPY
#endif /* defined(VA_END_COPY) */
#if HAVE_VA_COPY
#define VA_COPY(dest, src) va_copy(dest, src)
#define VA_END_COPY(ap) va_end(ap)
#elif HAVE___VA_COPY
#define VA_COPY(dest, src) __va_copy(dest, src)
#define VA_END_COPY(ap) va_end(ap)
#else
#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list))
#define VA_END_COPY(ap) /* No-op. */
#define NEED_MYMEMCPY 1
static void *mymemcpy(void *, void *, size_t);
#endif /* HAVE_VA_COPY */
#endif /* !HAVE_VASPRINTF */
#if !HAVE_VSNPRINTF
#include <errno.h> /* For ERANGE and errno. */
#include <limits.h> /* For *_MAX. */
#if HAVE_FLOAT_H
#include <float.h> /* For *DBL_{MIN,MAX}_10_EXP. */
#endif /* HAVE_FLOAT_H */
#if HAVE_INTTYPES_H
#include <inttypes.h> /* For intmax_t (if not defined in <stdint.h>). */
#endif /* HAVE_INTTYPES_H */
#if HAVE_LOCALE_H
#include <locale.h> /* For localeconv(3). */
#endif /* HAVE_LOCALE_H */
#if HAVE_STDDEF_H
#include <stddef.h> /* For ptrdiff_t. */
#endif /* HAVE_STDDEF_H */
#if HAVE_STDINT_H
#include <stdint.h> /* For intmax_t. */
#endif /* HAVE_STDINT_H */
/* Support for unsigned long long int. We may also need ULLONG_MAX. */
#ifndef ULONG_MAX /* We may need ULONG_MAX as a fallback. */
#ifdef UINT_MAX
#define ULONG_MAX UINT_MAX
#else
#define ULONG_MAX INT_MAX
#endif /* defined(UINT_MAX) */
#endif /* !defined(ULONG_MAX) */
#ifdef ULLONG
#undef ULLONG
#endif /* defined(ULLONG) */
#if HAVE_UNSIGNED_LONG_LONG_INT
#define ULLONG unsigned long long int
#ifndef ULLONG_MAX
#define ULLONG_MAX ULONG_MAX
#endif /* !defined(ULLONG_MAX) */
#else
#define ULLONG unsigned long int
#ifdef ULLONG_MAX
#undef ULLONG_MAX
#endif /* defined(ULLONG_MAX) */
#define ULLONG_MAX ULONG_MAX
#endif /* HAVE_LONG_LONG_INT */
/* Support for uintmax_t. We also need UINTMAX_MAX. */
#ifdef UINTMAX_T
#undef UINTMAX_T
#endif /* defined(UINTMAX_T) */
#if HAVE_UINTMAX_T || defined(uintmax_t)
#define UINTMAX_T uintmax_t
#ifndef UINTMAX_MAX
#define UINTMAX_MAX ULLONG_MAX
#endif /* !defined(UINTMAX_MAX) */
#else
#define UINTMAX_T ULLONG
#ifdef UINTMAX_MAX
#undef UINTMAX_MAX
#endif /* defined(UINTMAX_MAX) */
#define UINTMAX_MAX ULLONG_MAX
#endif /* HAVE_UINTMAX_T || defined(uintmax_t) */
/* Support for long double. */
#ifndef LDOUBLE
#if HAVE_LONG_DOUBLE
#define LDOUBLE long double
#define LDOUBLE_MIN_10_EXP LDBL_MIN_10_EXP
#define LDOUBLE_MAX_10_EXP LDBL_MAX_10_EXP
#else
#define LDOUBLE double
#define LDOUBLE_MIN_10_EXP DBL_MIN_10_EXP
#define LDOUBLE_MAX_10_EXP DBL_MAX_10_EXP
#endif /* HAVE_LONG_DOUBLE */
#endif /* !defined(LDOUBLE) */
/* Support for long long int. */
#ifndef LLONG
#if HAVE_LONG_LONG_INT
#define LLONG long long int
#else
#define LLONG long int
#endif /* HAVE_LONG_LONG_INT */
#endif /* !defined(LLONG) */
/* Support for intmax_t. */
#ifndef INTMAX_T
#if HAVE_INTMAX_T || defined(intmax_t)
#define INTMAX_T intmax_t
#else
#define INTMAX_T LLONG
#endif /* HAVE_INTMAX_T || defined(intmax_t) */
#endif /* !defined(INTMAX_T) */
/* Support for uintptr_t. */
#ifndef UINTPTR_T
#if HAVE_UINTPTR_T || defined(uintptr_t)
#define UINTPTR_T uintptr_t
#else
#define UINTPTR_T unsigned long int
#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
#endif /* !defined(UINTPTR_T) */
/* Support for ptrdiff_t. */
#ifndef PTRDIFF_T
#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
#define PTRDIFF_T ptrdiff_t
#else
#define PTRDIFF_T long int
#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
#endif /* !defined(PTRDIFF_T) */
/*
* We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
* 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an
* unsigned type if necessary. This should work just fine in practice.
*/
#ifndef UPTRDIFF_T
#define UPTRDIFF_T PTRDIFF_T
#endif /* !defined(UPTRDIFF_T) */
/*
* We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
* However, we'll simply use size_t and convert it to a signed type if
* necessary. This should work just fine in practice.
*/
#ifndef SSIZE_T
#define SSIZE_T size_t
#endif /* !defined(SSIZE_T) */
/* Either ERANGE or E2BIG should be available everywhere. */
#ifndef ERANGE
#define ERANGE E2BIG
#endif /* !defined(ERANGE) */
#ifndef EOVERFLOW
#define EOVERFLOW ERANGE
#endif /* !defined(EOVERFLOW) */
/*
* Buffer size to hold the octal string representation of UINT128_MAX without
* nul-termination ("3777777777777777777777777777777777777777777").
*/
#ifdef MAX_CONVERT_LENGTH
#undef MAX_CONVERT_LENGTH
#endif /* defined(MAX_CONVERT_LENGTH) */
#define MAX_CONVERT_LENGTH 43
/* Format read states. */
#define PRINT_S_DEFAULT 0
#define PRINT_S_FLAGS 1
#define PRINT_S_WIDTH 2
#define PRINT_S_DOT 3
#define PRINT_S_PRECISION 4
#define PRINT_S_MOD 5
#define PRINT_S_CONV 6
/* Format flags. */
#define PRINT_F_MINUS (1 << 0)
#define PRINT_F_PLUS (1 << 1)
#define PRINT_F_SPACE (1 << 2)
#define PRINT_F_NUM (1 << 3)
#define PRINT_F_ZERO (1 << 4)
#define PRINT_F_QUOTE (1 << 5)
#define PRINT_F_UP (1 << 6)
#define PRINT_F_UNSIGNED (1 << 7)
#define PRINT_F_TYPE_G (1 << 8)
#define PRINT_F_TYPE_E (1 << 9)
/* Conversion flags. */
#define PRINT_C_CHAR 1
#define PRINT_C_SHORT 2
#define PRINT_C_LONG 3
#define PRINT_C_LLONG 4
#define PRINT_C_LDOUBLE 5
#define PRINT_C_SIZE 6
#define PRINT_C_PTRDIFF 7
#define PRINT_C_INTMAX 8
#ifndef MAX
#define MAX(x, y) ((x >= y) ? x : y)
#endif /* !defined(MAX) */
#ifndef CHARTOINT
#define CHARTOINT(ch) (ch - '0')
#endif /* !defined(CHARTOINT) */
#ifndef ISDIGIT
#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
#endif /* !defined(ISDIGIT) */
#ifndef ISNAN
#define ISNAN(x) (x != x)
#endif /* !defined(ISNAN) */
#ifndef ISINF
#define ISINF(x) ((x < -1 || x > 1) && x + x == x)
#endif /* !defined(ISINF) */
#ifdef OUTCHAR
#undef OUTCHAR
#endif /* defined(OUTCHAR) */
#define OUTCHAR(str, len, size, ch) \
do { \
if (len + 1 < size) \
str[len] = ch; \
(len)++; \
} while (/* CONSTCOND */ 0)
static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
static void printsep(char *, size_t *, size_t);
static int getnumsep(int);
static int getexponent(LDOUBLE);
static int convert(UINTMAX_T, char *, size_t, int, int);
static UINTMAX_T cast(LDOUBLE);
static UINTMAX_T myround(LDOUBLE);
static LDOUBLE mypow10(int);
extern int errno;
int
rpl_vsnprintf(char *str, size_t size, const char *format, va_list args)
{
LDOUBLE fvalue;
INTMAX_T value;
unsigned char cvalue;
const char *strvalue;
INTMAX_T *intmaxptr;
PTRDIFF_T *ptrdiffptr;
SSIZE_T *sizeptr;
LLONG *llongptr;
long int *longptr;
int *intptr;
short int *shortptr;
signed char *charptr;
size_t len = 0;
int overflow = 0;
int base = 0;
int cflags = 0;
int flags = 0;
int width = 0;
int precision = -1;
int state = PRINT_S_DEFAULT;
char ch = *format++;
/*
* C99 says: "If `n' is zero, nothing is written, and `s' may be a null
* pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer
* even if a size larger than zero was specified. At least NetBSD's
* snprintf(3) does the same, as well as other versions of this file.
* (Though some of these versions will write to a non-NULL buffer even
* if a size of zero was specified, which violates the standard.)
*/
if (str == NULL && size != 0)
size = 0;
while (ch != '\0')
switch (state) {
case PRINT_S_DEFAULT:
if (ch == '%')
state = PRINT_S_FLAGS;
else
OUTCHAR(str, len, size, ch);
ch = *format++;
break;
case PRINT_S_FLAGS:
switch (ch) {
case '-':
flags |= PRINT_F_MINUS;
ch = *format++;
break;
case '+':
flags |= PRINT_F_PLUS;
ch = *format++;
break;
case ' ':
flags |= PRINT_F_SPACE;
ch = *format++;
break;
case '#':
flags |= PRINT_F_NUM;
ch = *format++;
break;
case '0':
flags |= PRINT_F_ZERO;
ch = *format++;
break;
case '\'': /* SUSv2 flag (not in C99). */
flags |= PRINT_F_QUOTE;
ch = *format++;
break;
default:
state = PRINT_S_WIDTH;
break;
}
break;
case PRINT_S_WIDTH:
if (ISDIGIT(ch)) {
ch = CHARTOINT(ch);
if (width > (INT_MAX - ch) / 10) {
overflow = 1;
goto out;
}
width = 10 * width + ch;
ch = *format++;
} else if (ch == '*') {
/*
* C99 says: "A negative field width argument is
* taken as a `-' flag followed by a positive
* field width." (7.19.6.1, 5)
*/
if ((width = va_arg(args, int)) < 0) {
flags |= PRINT_F_MINUS;
width = -width;
}
ch = *format++;
state = PRINT_S_DOT;
} else
state = PRINT_S_DOT;
break;
case PRINT_S_DOT:
if (ch == '.') {
state = PRINT_S_PRECISION;
ch = *format++;
} else
state = PRINT_S_MOD;
break;
case PRINT_S_PRECISION:
if (precision == -1)
precision = 0;
if (ISDIGIT(ch)) {
ch = CHARTOINT(ch);
if (precision > (INT_MAX - ch) / 10) {
overflow = 1;
goto out;
}
precision = 10 * precision + ch;
ch = *format++;
} else if (ch == '*') {
/*
* C99 says: "A negative precision argument is
* taken as if the precision were omitted."
* (7.19.6.1, 5)
*/
if ((precision = va_arg(args, int)) < 0)
precision = -1;
ch = *format++;
state = PRINT_S_MOD;
} else
state = PRINT_S_MOD;
break;
case PRINT_S_MOD:
switch (ch) {
case 'h':
ch = *format++;
if (ch == 'h') { /* It's a char. */
ch = *format++;
cflags = PRINT_C_CHAR;
} else
cflags = PRINT_C_SHORT;
break;
case 'l':
ch = *format++;
if (ch == 'l') { /* It's a long long. */
ch = *format++;
cflags = PRINT_C_LLONG;
} else
cflags = PRINT_C_LONG;
break;
case 'L':
cflags = PRINT_C_LDOUBLE;
ch = *format++;
break;
case 'j':
cflags = PRINT_C_INTMAX;
ch = *format++;
break;
case 't':
cflags = PRINT_C_PTRDIFF;
ch = *format++;
break;
case 'z':
cflags = PRINT_C_SIZE;
ch = *format++;
break;
}
state = PRINT_S_CONV;
break;
case PRINT_S_CONV:
switch (ch) {
case 'd':
/* FALLTHROUGH */
case 'i':
switch (cflags) {
case PRINT_C_CHAR:
value = (signed char)va_arg(args, int);
break;
case PRINT_C_SHORT:
value = (short int)va_arg(args, int);
break;
case PRINT_C_LONG:
value = va_arg(args, long int);
break;
case PRINT_C_LLONG:
value = va_arg(args, LLONG);
break;
case PRINT_C_SIZE:
value = va_arg(args, SSIZE_T);
break;
case PRINT_C_INTMAX:
value = va_arg(args, INTMAX_T);
break;
case PRINT_C_PTRDIFF:
value = va_arg(args, PTRDIFF_T);
break;
default:
value = va_arg(args, int);
break;
}
fmtint(str, &len, size, value, 10, width,
precision, flags);
break;
case 'X':
flags |= PRINT_F_UP;
/* FALLTHROUGH */
case 'x':
base = 16;
/* FALLTHROUGH */
case 'o':
if (base == 0)
base = 8;
/* FALLTHROUGH */
case 'u':
if (base == 0)
base = 10;
flags |= PRINT_F_UNSIGNED;
switch (cflags) {
case PRINT_C_CHAR:
value = (unsigned char)va_arg(args,
unsigned int);
break;
case PRINT_C_SHORT:
value = (unsigned short int)va_arg(args,
unsigned int);
break;
case PRINT_C_LONG:
value = va_arg(args, unsigned long int);
break;
case PRINT_C_LLONG:
value = va_arg(args, ULLONG);
break;
case PRINT_C_SIZE:
value = va_arg(args, size_t);
break;
case PRINT_C_INTMAX:
value = va_arg(args, UINTMAX_T);
break;
case PRINT_C_PTRDIFF:
value = va_arg(args, UPTRDIFF_T);
break;
default:
value = va_arg(args, unsigned int);
break;
}
fmtint(str, &len, size, value, base, width,
precision, flags);
break;
case 'A':
/* Not yet supported, we'll use "%F". */
/* FALLTHROUGH */
case 'E':
if (ch == 'E')
flags |= PRINT_F_TYPE_E;
/* FALLTHROUGH */
case 'G':
if (ch == 'G')
flags |= PRINT_F_TYPE_G;
/* FALLTHROUGH */
case 'F':
flags |= PRINT_F_UP;
/* FALLTHROUGH */
case 'a':
/* Not yet supported, we'll use "%f". */
/* FALLTHROUGH */
case 'e':
if (ch == 'e')
flags |= PRINT_F_TYPE_E;
/* FALLTHROUGH */
case 'g':
if (ch == 'g')
flags |= PRINT_F_TYPE_G;
/* FALLTHROUGH */
case 'f':
if (cflags == PRINT_C_LDOUBLE)
fvalue = va_arg(args, LDOUBLE);
else
fvalue = va_arg(args, double);
fmtflt(str, &len, size, fvalue, width,
precision, flags, &overflow);
if (overflow)
goto out;
break;
case 'c':
cvalue = va_arg(args, int);
OUTCHAR(str, len, size, cvalue);
break;
case 's':
strvalue = va_arg(args, char *);
fmtstr(str, &len, size, strvalue, width,
precision, flags);
break;
case 'p':
/*
* C99 says: "The value of the pointer is
* converted to a sequence of printing
* characters, in an implementation-defined
* manner." (C99: 7.19.6.1, 8)
*/
if ((strvalue = va_arg(args, void *)) == NULL)
/*
* We use the glibc format. BSD prints
* "0x0", SysV "0".
*/
fmtstr(str, &len, size, "(nil)", width,
-1, flags);
else {
/*
* We use the BSD/glibc format. SysV
* omits the "0x" prefix (which we emit
* using the PRINT_F_NUM flag).
*/
flags |= PRINT_F_NUM;
flags |= PRINT_F_UNSIGNED;
fmtint(str, &len, size,
(UINTPTR_T)strvalue, 16, width,
precision, flags);
}
break;
case 'n':
switch (cflags) {
case PRINT_C_CHAR:
charptr = va_arg(args, signed char *);
*charptr = len;
break;
case PRINT_C_SHORT:
shortptr = va_arg(args, short int *);
*shortptr = len;
break;
case PRINT_C_LONG:
longptr = va_arg(args, long int *);
*longptr = len;
break;
case PRINT_C_LLONG:
llongptr = va_arg(args, LLONG *);
*llongptr = len;
break;
case PRINT_C_SIZE:
/*
* C99 says that with the "z" length
* modifier, "a following `n' conversion
* specifier applies to a pointer to a
* signed integer type corresponding to
* size_t argument." (7.19.6.1, 7)
*/
sizeptr = va_arg(args, SSIZE_T *);
*sizeptr = len;
break;
case PRINT_C_INTMAX:
intmaxptr = va_arg(args, INTMAX_T *);
*intmaxptr = len;
break;
case PRINT_C_PTRDIFF:
ptrdiffptr = va_arg(args, PTRDIFF_T *);
*ptrdiffptr = len;
break;
default:
intptr = va_arg(args, int *);
*intptr = len;
break;
}
break;
case '%': /* Print a "%" character verbatim. */
OUTCHAR(str, len, size, ch);
break;
default: /* Skip other characters. */
break;
}
ch = *format++;
state = PRINT_S_DEFAULT;
base = cflags = flags = width = 0;
precision = -1;
break;
}
out:
if (len < size)
str[len] = '\0';
else if (size > 0)
str[size - 1] = '\0';
if (overflow || len > INT_MAX) {
errno = EOVERFLOW;
return -1;
}
return (int)len;
}
static void
fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
int precision, int flags)
{
int padlen, strln; /* Amount to pad. */
int noprecision = (precision == -1);
if (value == NULL) /* We're forgiving. */
value = "(null)";
/* If a precision was specified, don't read the string past it. */
for (strln = 0; value[strln] != '\0' &&
(noprecision || strln < precision); strln++)
continue;
if ((padlen = width - strln) < 0)
padlen = 0;
if (flags & PRINT_F_MINUS) /* Left justify. */
padlen = -padlen;
while (padlen > 0) { /* Leading spaces. */
OUTCHAR(str, *len, size, ' ');
padlen--;
}
while (*value != '\0' && (noprecision || precision-- > 0)) {
OUTCHAR(str, *len, size, *value);
value++;
}
while (padlen < 0) { /* Trailing spaces. */
OUTCHAR(str, *len, size, ' ');
padlen++;
}
}
static void
fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
int precision, int flags)
{
UINTMAX_T uvalue;
char iconvert[MAX_CONVERT_LENGTH];
char sign = 0;
char hexprefix = 0;
int spadlen = 0; /* Amount to space pad. */
int zpadlen = 0; /* Amount to zero pad. */
int pos;
int separators = (flags & PRINT_F_QUOTE);
int noprecision = (precision == -1);
if (flags & PRINT_F_UNSIGNED)
uvalue = value;
else {
uvalue = (value >= 0) ? value : -value;
if (value < 0)
sign = '-';
else if (flags & PRINT_F_PLUS) /* Do a sign. */
sign = '+';
else if (flags & PRINT_F_SPACE)
sign = ' ';
}
pos = convert(uvalue, iconvert, sizeof(iconvert), base,
flags & PRINT_F_UP);
if (flags & PRINT_F_NUM && uvalue != 0) {
/*
* C99 says: "The result is converted to an `alternative form'.
* For `o' conversion, it increases the precision, if and only
* if necessary, to force the first digit of the result to be a
* zero (if the value and precision are both 0, a single 0 is
* printed). For `x' (or `X') conversion, a nonzero result has
* `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
*/
switch (base) {
case 8:
if (precision <= pos)
precision = pos + 1;
break;
case 16:
hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
break;
}
}
if (separators) /* Get the number of group separators we'll print. */
separators = getnumsep(pos);
zpadlen = precision - pos - separators;
spadlen = width /* Minimum field width. */
- separators /* Number of separators. */
- MAX(precision, pos) /* Number of integer digits. */
- ((sign != 0) ? 1 : 0) /* Will we print a sign? */
- ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */
if (zpadlen < 0)
zpadlen = 0;
if (spadlen < 0)
spadlen = 0;
/*
* C99 says: "If the `0' and `-' flags both appear, the `0' flag is
* ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a
* precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
*/
if (flags & PRINT_F_MINUS) /* Left justify. */
spadlen = -spadlen;
else if (flags & PRINT_F_ZERO && noprecision) {
zpadlen += spadlen;
spadlen = 0;
}
while (spadlen > 0) { /* Leading spaces. */
OUTCHAR(str, *len, size, ' ');
spadlen--;
}
if (sign != 0) /* Sign. */
OUTCHAR(str, *len, size, sign);
if (hexprefix != 0) { /* A "0x" or "0X" prefix. */
OUTCHAR(str, *len, size, '0');
OUTCHAR(str, *len, size, hexprefix);
}
while (zpadlen > 0) { /* Leading zeros. */
OUTCHAR(str, *len, size, '0');
zpadlen--;
}
while (pos > 0) { /* The actual digits. */
pos--;
OUTCHAR(str, *len, size, iconvert[pos]);
if (separators > 0 && pos > 0 && pos % 3 == 0)
printsep(str, len, size);
}
while (spadlen < 0) { /* Trailing spaces. */
OUTCHAR(str, *len, size, ' ');
spadlen++;
}
}
static void
fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width,
int precision, int flags, int *overflow)
{
LDOUBLE ufvalue;
UINTMAX_T intpart;
UINTMAX_T fracpart;
UINTMAX_T mask;
const char *infnan = NULL;
char iconvert[MAX_CONVERT_LENGTH];
char fconvert[MAX_CONVERT_LENGTH];
char econvert[5]; /* "e-300" (without nul-termination). */
char esign = 0;
char sign = 0;
int leadfraczeros = 0;
int exponent = 0;
int emitpoint = 0;
int omitzeros = 0;
int omitcount = 0;
int padlen = 0;
int epos = 0;
int fpos = 0;
int ipos = 0;
int separators = (flags & PRINT_F_QUOTE);
int estyle = (flags & PRINT_F_TYPE_E);
#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
struct lconv *lc = localeconv();
#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
/*
* AIX' man page says the default is 0, but C99 and at least Solaris'
* and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
* defaults to 6.
*/
if (precision == -1)
precision = 6;
if (fvalue < 0.0)
sign = '-';
else if (flags & PRINT_F_PLUS) /* Do a sign. */
sign = '+';
else if (flags & PRINT_F_SPACE)
sign = ' ';
if (ISNAN(fvalue))
infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
else if (ISINF(fvalue))
infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
if (infnan != NULL) {
if (sign != 0)
iconvert[ipos++] = sign;
while (*infnan != '\0')
iconvert[ipos++] = *infnan++;
fmtstr(str, len, size, iconvert, width, ipos, flags);
return;
}
/* "%e" (or "%E") or "%g" (or "%G") conversion. */
if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
if (flags & PRINT_F_TYPE_G) {
/*
* If the precision is zero, it is treated as one (cf.
* C99: 7.19.6.1, 8).
*/
if (precision == 0)
precision = 1;
/*
* For "%g" (and "%G") conversions, the precision
* specifies the number of significant digits, which
* includes the digits in the integer part. The
* conversion will or will not be using "e-style" (like
* "%e" or "%E" conversions) depending on the precision
* and on the exponent. However, the exponent can be
* affected by rounding the converted value, so we'll
* leave this decision for later. Until then, we'll
* assume that we're going to do an "e-style" conversion
* (in order to get the exponent calculated). For
* "e-style", the precision must be decremented by one.
*/
precision--;
/*
* For "%g" (and "%G") conversions, trailing zeros are
* removed from the fractional portion of the result
* unless the "#" flag was specified.
*/
if (!(flags & PRINT_F_NUM))
omitzeros = 1;
}
exponent = getexponent(fvalue);
estyle = 1;
}
again:
/*
* Sorry, we only support 9, 19, or 38 digits (that is, the number of
* digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
* minus one) past the decimal point due to our conversion method.
*/
switch (sizeof(UINTMAX_T)) {
case 16:
if (precision > 38)
precision = 38;
break;
case 8:
if (precision > 19)
precision = 19;
break;
default:
if (precision > 9)
precision = 9;
break;
}
ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
if (estyle) /* We want exactly one integer digit. */
ufvalue /= mypow10(exponent);
if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
*overflow = 1;
return;
}
/*
* Factor of ten with the number of digits needed for the fractional
* part. For example, if the precision is 3, the mask will be 1000.
*/
mask = mypow10(precision);
/*
* We "cheat" by converting the fractional part to integer by
* multiplying by a factor of ten.
*/
if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
/*
* For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
* (because precision = 3). Now, myround(1000 * 0.99962) will
* return 1000. So, the integer part must be incremented by one
* and the fractional part must be set to zero.
*/
intpart++;
fracpart = 0;
if (estyle && intpart == 10) {
/*
* The value was rounded up to ten, but we only want one
* integer digit if using "e-style". So, the integer
* part must be set to one and the exponent must be
* incremented by one.
*/
intpart = 1;
exponent++;
}
}
/*
* Now that we know the real exponent, we can check whether or not to
* use "e-style" for "%g" (and "%G") conversions. If we don't need
* "e-style", the precision must be adjusted and the integer and
* fractional parts must be recalculated from the original value.
*
* C99 says: "Let P equal the precision if nonzero, 6 if the precision
* is omitted, or 1 if the precision is zero. Then, if a conversion
* with style `E' would have an exponent of X:
*
* - if P > X >= -4, the conversion is with style `f' (or `F') and
* precision P - (X + 1).
*
* - otherwise, the conversion is with style `e' (or `E') and precision
* P - 1." (7.19.6.1, 8)
*
* Note that we had decremented the precision by one.
*/
if (flags & PRINT_F_TYPE_G && estyle &&
precision + 1 > exponent && exponent >= -4) {
precision -= exponent;
estyle = 0;
goto again;
}
if (estyle) {
if (exponent < 0) {
exponent = -exponent;
esign = '-';
} else
esign = '+';
/*
* Convert the exponent. The sizeof(econvert) is 5. So, the
* econvert buffer can hold e.g. "e+999" and "e-999". We don't
* support an exponent which contains more than three digits.
* Therefore, the following stores are safe.
*/
epos = convert(exponent, econvert, 3, 10, 0);
/*
* C99 says: "The exponent always contains at least two digits,
* and only as many more digits as necessary to represent the
* exponent." (7.19.6.1, 8)
*/
if (epos == 1)
econvert[epos++] = '0';
econvert[epos++] = esign;
econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
}
/* Convert the integer part and the fractional part. */
ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
if (fracpart != 0) /* convert() would return 1 if fracpart == 0. */
fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
leadfraczeros = precision - fpos;
if (omitzeros) {
if (fpos > 0) /* Omit trailing fractional part zeros. */
while (omitcount < fpos && fconvert[omitcount] == '0')
omitcount++;
else { /* The fractional part is zero, omit it completely. */
omitcount = precision;
leadfraczeros = 0;
}
precision -= omitcount;
}
/*
* Print a decimal point if either the fractional part is non-zero
* and/or the "#" flag was specified.
*/
if (precision > 0 || flags & PRINT_F_NUM)
emitpoint = 1;
if (separators) /* Get the number of group separators we'll print. */
separators = getnumsep(ipos);
padlen = width /* Minimum field width. */
- ipos /* Number of integer digits. */
- epos /* Number of exponent characters. */
- precision /* Number of fractional digits. */
- separators /* Number of group separators. */
- (emitpoint ? 1 : 0) /* Will we print a decimal point? */
- ((sign != 0) ? 1 : 0); /* Will we print a sign character? */
if (padlen < 0)
padlen = 0;
/*
* C99 says: "If the `0' and `-' flags both appear, the `0' flag is
* ignored." (7.19.6.1, 6)
*/
if (flags & PRINT_F_MINUS) /* Left justifty. */
padlen = -padlen;
else if (flags & PRINT_F_ZERO && padlen > 0) {
if (sign != 0) { /* Sign. */
OUTCHAR(str, *len, size, sign);
sign = 0;
}
while (padlen > 0) { /* Leading zeros. */
OUTCHAR(str, *len, size, '0');
padlen--;
}
}
while (padlen > 0) { /* Leading spaces. */
OUTCHAR(str, *len, size, ' ');
padlen--;
}
if (sign != 0) /* Sign. */
OUTCHAR(str, *len, size, sign);
while (ipos > 0) { /* Integer part. */
ipos--;
OUTCHAR(str, *len, size, iconvert[ipos]);
if (separators > 0 && ipos > 0 && ipos % 3 == 0)
printsep(str, len, size);
}
if (emitpoint) { /* Decimal point. */
#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
OUTCHAR(str, *len, size, *lc->decimal_point);
else /* We'll always print some decimal point character. */
#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
OUTCHAR(str, *len, size, '.');
}
while (leadfraczeros > 0) { /* Leading fractional part zeros. */
OUTCHAR(str, *len, size, '0');
leadfraczeros--;
}
while (fpos > omitcount) { /* The remaining fractional part. */
fpos--;
OUTCHAR(str, *len, size, fconvert[fpos]);
}
while (epos > 0) { /* Exponent. */
epos--;
OUTCHAR(str, *len, size, econvert[epos]);
}
while (padlen < 0) { /* Trailing spaces. */
OUTCHAR(str, *len, size, ' ');
padlen++;
}
}
static void
printsep(char *str, size_t *len, size_t size)
{
#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
struct lconv *lc = localeconv();
int i;
if (lc->thousands_sep != NULL)
for (i = 0; lc->thousands_sep[i] != '\0'; i++)
OUTCHAR(str, *len, size, lc->thousands_sep[i]);
else
#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
OUTCHAR(str, *len, size, ',');
}
static int
getnumsep(int digits)
{
int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
int strln;
struct lconv *lc = localeconv();
/* We support an arbitrary separator length (including zero). */
if (lc->thousands_sep != NULL) {
for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
continue;
separators *= strln;
}
#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
return separators;
}
static int
getexponent(LDOUBLE value)
{
LDOUBLE tmp = (value >= 0.0) ? value : -value;
int exponent = 0;
/*
* We check for LDOUBLE_MAX_10_EXP >= exponent >= LDOUBLE_MIN_10_EXP in
* order to work around possible endless loops which could happen (at
* least) in the second loop (at least) if we're called with an infinite
* value. However, we checked for infinity before calling this function
* using our ISINF() macro, so this might be somewhat paranoid.
*/
while (tmp < 1.0 && tmp > 0.0 && --exponent >= LDOUBLE_MIN_10_EXP)
tmp *= 10;
while (tmp >= 10.0 && ++exponent <= LDOUBLE_MAX_10_EXP)
tmp /= 10;
return exponent;
}
static int
convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
{
const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
size_t pos = 0;
/* We return an unterminated buffer with the digits in reverse order. */
do {
buf[pos++] = digits[value % base];
value /= base;
} while (value != 0 && pos < size);
return (int)pos;
}
static UINTMAX_T
cast(LDOUBLE value)
{
UINTMAX_T result;
/*
* We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
* represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
* it may be increased to the nearest higher representable value for the
* comparison (cf. C99: 6.3.1.4, 2). It might then equal the LDOUBLE
* value although converting the latter to UINTMAX_T would overflow.
*/
if (value >= UINTMAX_MAX)
return UINTMAX_MAX;
result = value;
/*
* At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
* an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
* the standard). Sigh.
*/
return (result <= value) ? result : result - 1;
}
static UINTMAX_T
myround(LDOUBLE value)
{
UINTMAX_T intpart = cast(value);
return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
}
static LDOUBLE
mypow10(int exponent)
{
LDOUBLE result = 1;
while (exponent > 0) {
result *= 10;
exponent--;
}
while (exponent < 0) {
result /= 10;
exponent++;
}
return result;
}
#endif /* !HAVE_VSNPRINTF */
#if !HAVE_VASPRINTF
#if NEED_MYMEMCPY
void *
mymemcpy(void *dst, void *src, size_t len)
{
const char *from = src;
char *to = dst;
/* No need for optimization, we use this only to replace va_copy(3). */
while (len-- > 0)
*to++ = *from++;
return dst;
}
#endif /* NEED_MYMEMCPY */
int
rpl_vasprintf(char **ret, const char *format, va_list ap)
{
size_t size;
int len;
va_list aq;
VA_COPY(aq, ap);
len = vsnprintf(NULL, 0, format, aq);
VA_END_COPY(aq);
if (len < 0 || (*ret = malloc(size = len + 1)) == NULL)
return -1;
return vsnprintf(*ret, size, format, ap);
}
#endif /* !HAVE_VASPRINTF */
#if !HAVE_SNPRINTF
#if HAVE_STDARG_H
int
rpl_snprintf(char *str, size_t size, const char *format, ...)
#else
int
rpl_snprintf(va_alist) va_dcl
#endif /* HAVE_STDARG_H */
{
#if !HAVE_STDARG_H
char *str;
size_t size;
char *format;
#endif /* HAVE_STDARG_H */
va_list ap;
int len;
VA_START(ap, format);
VA_SHIFT(ap, str, char *);
VA_SHIFT(ap, size, size_t);
VA_SHIFT(ap, format, const char *);
len = vsnprintf(str, size, format, ap);
va_end(ap);
return len;
}
#endif /* !HAVE_SNPRINTF */
#if !HAVE_ASPRINTF
#if HAVE_STDARG_H
int
rpl_asprintf(char **ret, const char *format, ...)
#else
int
rpl_asprintf(va_alist) va_dcl
#endif /* HAVE_STDARG_H */
{
#if !HAVE_STDARG_H
char **ret;
char *format;
#endif /* HAVE_STDARG_H */
va_list ap;
int len;
VA_START(ap, format);
VA_SHIFT(ap, ret, char **);
VA_SHIFT(ap, format, const char *);
len = vasprintf(ret, format, ap);
va_end(ap);
return len;
}
#endif /* !HAVE_ASPRINTF */
#else /* Dummy declaration to avoid empty translation unit warnings. */
int main(int argc, char **argv);
#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || [...] */
int sprintf(char *s, const char *fmt, ...)
{
int n;
va_list arg;
va_start(arg, fmt);
n = vsnprintf(s, 1024, fmt, arg);
va_end(arg);
return n;
}
int vsprintf (char *d, const char *s, va_list ap)
{
return vsnprintf(d, 1024, s, ap);
}
#if TEST_SNPRINTF
int
main(void)
{
const char *float_fmt[] = {
/* "%E" and "%e" formats. */
#if HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX
"%.16e",
"%22.16e",
"%022.16e",
"%-22.16e",
"%#+'022.16e",
#endif /* HAVE_LONG_LONG_INT && !OS_BSD && !OS_IRIX */
"foo|%#+0123.9E|bar",
"%-123.9e",
"%123.9e",
"%+23.9e",
"%+05.8e",
"%-05.8e",
"%05.8e",
"%+5.8e",
"%-5.8e",
"% 5.8e",
"%5.8e",
"%+4.9e",
#if !OS_LINUX /* glibc sometimes gets these wrong. */
"%+#010.0e",
"%#10.1e",
"%10.5e",
"% 10.5e",
"%5.0e",
"%5.e",
"%#5.0e",
"%#5.e",
"%3.2e",
"%3.1e",
"%-1.5e",
"%1.5e",
"%01.3e",
"%1.e",
"%.1e",
"%#.0e",
"%+.0e",
"% .0e",
"%.0e",
"%#.e",
"%+.e",
"% .e",
"%.e",
"%4e",
"%e",
"%E",
#endif /* !OS_LINUX */
/* "%F" and "%f" formats. */
#if !OS_BSD && !OS_IRIX
"% '022f",
"%+'022f",
"%-'22f",
"%'22f",
#if HAVE_LONG_LONG_INT
"%.16f",
"%22.16f",
"%022.16f",
"%-22.16f",
"%#+'022.16f",
#endif /* HAVE_LONG_LONG_INT */
#endif /* !OS_BSD && !OS_IRIX */
"foo|%#+0123.9F|bar",
"%-123.9f",
"%123.9f",
"%+23.9f",
"%+#010.0f",
"%#10.1f",
"%10.5f",
"% 10.5f",
"%+05.8f",
"%-05.8f",
"%05.8f",
"%+5.8f",
"%-5.8f",
"% 5.8f",
"%5.8f",
"%5.0f",
"%5.f",
"%#5.0f",
"%#5.f",
"%+4.9f",
"%3.2f",
"%3.1f",
"%-1.5f",
"%1.5f",
"%01.3f",
"%1.f",
"%.1f",
"%#.0f",
"%+.0f",
"% .0f",
"%.0f",
"%#.f",
"%+.f",
"% .f",
"%.f",
"%4f",
"%f",
"%F",
/* "%G" and "%g" formats. */
#if !OS_BSD && !OS_IRIX && !OS_LINUX
"% '022g",
"%+'022g",
"%-'22g",
"%'22g",
#if HAVE_LONG_LONG_INT
"%.16g",
"%22.16g",
"%022.16g",
"%-22.16g",
"%#+'022.16g",
#endif /* HAVE_LONG_LONG_INT */
#endif /* !OS_BSD && !OS_IRIX && !OS_LINUX */
"foo|%#+0123.9G|bar",
"%-123.9g",
"%123.9g",
"%+23.9g",
"%+05.8g",
"%-05.8g",
"%05.8g",
"%+5.8g",
"%-5.8g",
"% 5.8g",
"%5.8g",
"%+4.9g",
#if !OS_LINUX /* glibc sometimes gets these wrong. */
"%+#010.0g",
"%#10.1g",
"%10.5g",
"% 10.5g",
"%5.0g",
"%5.g",
"%#5.0g",
"%#5.g",
"%3.2g",
"%3.1g",
"%-1.5g",
"%1.5g",
"%01.3g",
"%1.g",
"%.1g",
"%#.0g",
"%+.0g",
"% .0g",
"%.0g",
"%#.g",
"%+.g",
"% .g",
"%.g",
"%4g",
"%g",
"%G",
#endif /* !OS_LINUX */
NULL
};
double float_val[] = {
-4.136,
-134.52,
-5.04030201,
-3410.01234,
-999999.999999,
-913450.29876,
-913450.2,
-91345.2,
-9134.2,
-913.2,
-91.2,
-9.2,
-9.9,
4.136,
134.52,
5.04030201,
3410.01234,
999999.999999,
913450.29876,
913450.2,
91345.2,
9134.2,
913.2,
91.2,
9.2,
9.9,
9.96,
9.996,
9.9996,
9.99996,
9.999996,
9.9999996,
9.99999996,
0.99999996,
0.99999999,
0.09999999,
0.00999999,
0.00099999,
0.00009999,
0.00000999,
0.00000099,
0.00000009,
0.00000001,
0.0000001,
0.000001,
0.00001,
0.0001,
0.001,
0.01,
0.1,
1.0,
1.5,
-1.5,
-1.0,
-0.1,
#if !OS_BSD /* BSD sometimes gets these wrong. */
#ifdef INFINITY
INFINITY,
-INFINITY,
#endif /* defined(INFINITY) */
#ifdef NAN
NAN,
#endif /* defined(NAN) */
#endif /* !OS_BSD */
0
};
const char *long_fmt[] = {
"foo|%0123ld|bar",
#if !OS_IRIX
"% '0123ld",
"%+'0123ld",
"%-'123ld",
"%'123ld",
#endif /* !OS_IRiX */
"%123.9ld",
"% 123.9ld",
"%+123.9ld",
"%-123.9ld",
"%0123ld",
"% 0123ld",
"%+0123ld",
"%-0123ld",
"%10.5ld",
"% 10.5ld",
"%+10.5ld",
"%-10.5ld",
"%010ld",
"% 010ld",
"%+010ld",
"%-010ld",
"%4.2ld",
"% 4.2ld",
"%+4.2ld",
"%-4.2ld",
"%04ld",
"% 04ld",
"%+04ld",
"%-04ld",
"%5.5ld",
"%+22.33ld",
"%01.3ld",
"%1.5ld",
"%-1.5ld",
"%44ld",
"%4ld",
"%4.0ld",
"%4.ld",
"%.44ld",
"%.4ld",
"%.0ld",
"%.ld",
"%ld",
NULL
};
long int long_val[] = {
#ifdef LONG_MAX
LONG_MAX,
#endif /* LONG_MAX */
#ifdef LONG_MIN
LONG_MIN,
#endif /* LONG_MIN */
-91340,
91340,
341,
134,
0203,
-1,
1,
0
};
const char *ulong_fmt[] = {
/* "%u" formats. */
"foo|%0123lu|bar",
#if !OS_IRIX
"% '0123lu",
"%+'0123lu",
"%-'123lu",
"%'123lu",
#endif /* !OS_IRiX */
"%123.9lu",
"% 123.9lu",
"%+123.9lu",
"%-123.9lu",
"%0123lu",
"% 0123lu",
"%+0123lu",
"%-0123lu",
"%5.5lu",
"%+22.33lu",
"%01.3lu",
"%1.5lu",
"%-1.5lu",
"%44lu",
"%lu",
/* "%o" formats. */
"foo|%#0123lo|bar",
"%#123.9lo",
"%# 123.9lo",
"%#+123.9lo",
"%#-123.9lo",
"%#0123lo",
"%# 0123lo",
"%#+0123lo",
"%#-0123lo",
"%#5.5lo",
"%#+22.33lo",
"%#01.3lo",
"%#1.5lo",
"%#-1.5lo",
"%#44lo",
"%#lo",
"%123.9lo",
"% 123.9lo",
"%+123.9lo",
"%-123.9lo",
"%0123lo",
"% 0123lo",
"%+0123lo",
"%-0123lo",
"%5.5lo",
"%+22.33lo",
"%01.3lo",
"%1.5lo",
"%-1.5lo",
"%44lo",
"%lo",
/* "%X" and "%x" formats. */
"foo|%#0123lX|bar",
"%#123.9lx",
"%# 123.9lx",
"%#+123.9lx",
"%#-123.9lx",
"%#0123lx",
"%# 0123lx",
"%#+0123lx",
"%#-0123lx",
"%#5.5lx",
"%#+22.33lx",
"%#01.3lx",
"%#1.5lx",
"%#-1.5lx",
"%#44lx",
"%#lx",
"%#lX",
"%123.9lx",
"% 123.9lx",
"%+123.9lx",
"%-123.9lx",
"%0123lx",
"% 0123lx",
"%+0123lx",
"%-0123lx",
"%5.5lx",
"%+22.33lx",
"%01.3lx",
"%1.5lx",
"%-1.5lx",
"%44lx",
"%lx",
"%lX",
NULL
};
unsigned long int ulong_val[] = {
#ifdef ULONG_MAX
ULONG_MAX,
#endif /* ULONG_MAX */
91340,
341,
134,
0203,
1,
0
};
const char *llong_fmt[] = {
"foo|%0123lld|bar",
"%123.9lld",
"% 123.9lld",
"%+123.9lld",
"%-123.9lld",
"%0123lld",
"% 0123lld",
"%+0123lld",
"%-0123lld",
"%5.5lld",
"%+22.33lld",
"%01.3lld",
"%1.5lld",
"%-1.5lld",
"%44lld",
"%lld",
NULL
};
LLONG llong_val[] = {
#ifdef LLONG_MAX
LLONG_MAX,
#endif /* LLONG_MAX */
#ifdef LLONG_MIN
LLONG_MIN,
#endif /* LLONG_MIN */
-91340,
91340,
341,
134,
0203,
-1,
1,
0
};
const char *string_fmt[] = {
"foo|%10.10s|bar",
"%-10.10s",
"%10.10s",
"%10.5s",
"%5.10s",
"%10.1s",
"%1.10s",
"%10.0s",
"%0.10s",
"%-42.5s",
"%2.s",
"%.10s",
"%.1s",
"%.0s",
"%.s",
"%4s",
"%s",
NULL
};
const char *string_val[] = {
"Hello",
"Hello, world!",
"Sound check: One, two, three.",
"This string is a little longer than the other strings.",
"1",
"",
NULL
};
#if !OS_SYSV /* SysV uses a different format than we do. */
const char *pointer_fmt[] = {
"foo|%p|bar",
"%42p",
"%p",
NULL
};
const char *pointer_val[] = {
*pointer_fmt,
*string_fmt,
*string_val,
NULL
};
#endif /* !OS_SYSV */
char buf1[1024], buf2[1024];
double value, digits = 9.123456789012345678901234567890123456789;
int i, j, r1, r2, failed = 0, num = 0;
/*
* Use -DTEST_NILS in order to also test the conversion of nil values. Might
* segfault on systems which don't support converting a NULL pointer with "%s"
* and lets some test cases fail against BSD and glibc due to bugs in their
* implementations.
*/
#ifndef TEST_NILS
#define TEST_NILS 0
#elif TEST_NILS
#undef TEST_NILS
#define TEST_NILS 1
#endif /* !defined(TEST_NILS) */
#ifdef TEST
#undef TEST
#endif /* defined(TEST) */
#define TEST(fmt, val) \
do { \
for (i = 0; fmt[i] != NULL; i++) \
for (j = 0; j == 0 || val[j - TEST_NILS] != 0; j++) { \
r1 = sprintf(buf1, fmt[i], val[j]); \
r2 = snprintf(buf2, sizeof(buf2), fmt[i], val[j]); \
if (strcmp(buf1, buf2) != 0 || r1 != r2) { \
(void)printf("Results don't match, " \
"format string: %s\n" \
"\t sprintf(3): [%s] (%d)\n" \
"\tsnprintf(3): [%s] (%d)\n", \
fmt[i], buf1, r1, buf2, r2); \
failed++; \
} \
num++; \
} \
} while (/* CONSTCOND */ 0)
#if HAVE_LOCALE_H
(void)setlocale(LC_ALL, "");
#endif /* HAVE_LOCALE_H */
(void)puts("Testing our snprintf(3) against your system's sprintf(3).");
TEST(float_fmt, float_val);
TEST(long_fmt, long_val);
TEST(ulong_fmt, ulong_val);
TEST(llong_fmt, llong_val);
TEST(string_fmt, string_val);
#if !OS_SYSV /* SysV uses a different format than we do. */
TEST(pointer_fmt, pointer_val);
#endif /* !OS_SYSV */
(void)printf("Result: %d out of %d tests failed.\n", failed, num);
(void)fputs("Checking how many digits we support: ", stdout);
for (i = 0; i < 100; i++) {
value = pow(10, i) * digits;
(void)sprintf(buf1, "%.1f", value);
(void)snprintf(buf2, sizeof(buf2), "%.1f", value);
if (strcmp(buf1, buf2) != 0) {
(void)printf("apparently %d.\n", i);
break;
}
}
return (failed == 0) ? 0 : 1;
}
#endif /* TEST_SNPRINTF */
/* vim: set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */
#include <stdio.h>
#if defined( LUA_NUMBER_INTEGRAL )
#include <stdarg.h>
int sprintf(char *s, const char *fmt, ...)
{
int n;
va_list arg;
va_start(arg, fmt);
n = ets_vsprintf(s, fmt, arg);
va_end(arg);
return n;
}
int vsprintf (char *d, const char *s, va_list ap)
{
return ets_vsprintf(d, s, ap);
}
#else
#define FLOATINGPT 1
#define NEWFP 1
#define ENDIAN_LITTLE 1234
#define ENDIAN_BIG 4321
#define ENDIAN_PDP 3412
#define ENDIAN ENDIAN_LITTLE
/* $Id: strichr.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
/*
* Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Opsycon AB.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <string.h>
char *
strichr(char *p, int c)
{
char *t;
if (p != NULL) {
for(t = p; *t; t++);
for (; t >= p; t--) {
*(t + 1) = *t;
}
*p = c;
}
return (p);
}
/* $Id: str_fmt.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
/*
* Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Opsycon AB.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <string.h>
#define FMT_RJUST 0
#define FMT_LJUST 1
#define FMT_RJUST0 2
#define FMT_CENTER 3
/*
* Format string by inserting blanks.
*/
void
str_fmt(char *p, int size, int fmt)
{
int n, m, len;
len = strlen (p);
switch (fmt) {
case FMT_RJUST:
for (n = size - len; n > 0; n--)
strichr (p, ' ');
break;
case FMT_LJUST:
for (m = size - len; m > 0; m--)
strcat (p, " ");
break;
case FMT_RJUST0:
for (n = size - len; n > 0; n--)
strichr (p, '0');
break;
case FMT_CENTER:
m = (size - len) / 2;
n = size - (len + m);
for (; m > 0; m--)
strcat (p, " ");
for (; n > 0; n--)
strichr (p, ' ');
break;
}
}
/* $Id: strtoupp.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
/*
* Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Opsycon AB.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <string.h>
#include <ctype.h>
void
strtoupper(char *p)
{
if(!p)
return;
for (; *p; p++)
*p = toupper (*p);
}
/* $Id: atob.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
/*
* Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Opsycon AB.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
//#include <sys/types.h>
#include <string.h>
#include <stdint.h>
//#include <pmon.h>
typedef unsigned int u_int;
typedef unsigned long u_long;
typedef int32_t register_t;
typedef long long quad_t;
typedef unsigned long long u_quad_t;
typedef double rtype;
#ifndef __P
#define __P(args) args
#endif
static char * _getbase __P((char *, int *));
static int _atob __P((unsigned long long *, char *p, int));
static char *
_getbase(char *p, int *basep)
{
if (p[0] == '0') {
switch (p[1]) {
case 'x':
*basep = 16;
break;
case 't': case 'n':
*basep = 10;
break;
case 'o':
*basep = 8;
break;
default:
*basep = 10;
return (p);
}
return (p + 2);
}
*basep = 10;
return (p);
}
/*
* _atob(vp,p,base)
*/
static int
_atob (u_quad_t *vp, char *p, int base)
{
u_quad_t value, v1, v2;
char *q, tmp[20];
int digit;
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
base = 16;
p += 2;
}
if (base == 16 && (q = strchr (p, '.')) != 0) {
if (q - p > sizeof(tmp) - 1)
return (0);
strncpy (tmp, p, q - p);
tmp[q - p] = '\0';
if (!_atob (&v1, tmp, 16))
return (0);
q++;
if (strchr (q, '.'))
return (0);
if (!_atob (&v2, q, 16))
return (0);
*vp = (v1 << 16) + v2;
return (1);
}
value = *vp = 0;
for (; *p; p++) {
if (*p >= '0' && *p <= '9')
digit = *p - '0';
else if (*p >= 'a' && *p <= 'f')
digit = *p - 'a' + 10;
else if (*p >= 'A' && *p <= 'F')
digit = *p - 'A' + 10;
else
return (0);
if (digit >= base)
return (0);
value *= base;
value += digit;
}
*vp = value;
return (1);
}
/*
* atob(vp,p,base)
* converts p to binary result in vp, rtn 1 on success
*/
int
atob(uint32_t *vp, char *p, int base)
{
u_quad_t v;
if (base == 0)
p = _getbase (p, &base);
if (_atob (&v, p, base)) {
*vp = v;
return (1);
}
return (0);
}
/*
* llatob(vp,p,base)
* converts p to binary result in vp, rtn 1 on success
*/
int
llatob(u_quad_t *vp, char *p, int base)
{
if (base == 0)
p = _getbase (p, &base);
return _atob(vp, p, base);
}
/*
* char *btoa(dst,value,base)
* converts value to ascii, result in dst
*/
char *
btoa(char *dst, u_int value, int base, int plussgn)
{
char buf[34], digit;
int i, j, rem, neg;
if (value == 0) {
dst[0] = '0';
dst[1] = 0;
return (dst);
}
neg = 0;
if (base == -10) {
base = 10;
if (value & (1L << 31)) {
value = (~value) + 1;
neg = 1;
}
}
for (i = 0; value != 0; i++) {
rem = value % base;
value /= base;
if (rem >= 0 && rem <= 9)
digit = rem + '0';
else if (rem >= 10 && rem <= 36)
digit = (rem - 10) + 'a';
buf[i] = digit;
}
buf[i] = 0;
if (neg)
strcat (buf, "-");
else if (plussgn)
strcat (buf, "+");
/* reverse the string */
for (i = 0, j = strlen (buf) - 1; j >= 0; i++, j--)
dst[i] = buf[j];
dst[i] = 0;
return (dst);
}
/*
* char *btoa(dst,value,base)
* converts value to ascii, result in dst
*/
char *
llbtoa(char *dst, u_quad_t value, int base, int plussgn)
{
char buf[66], digit;
int i, j, rem, neg;
if (value == 0) {
dst[0] = '0';
dst[1] = 0;
return (dst);
}
neg = 0;
if (base == -10) {
base = 10;
if (value & (1LL << 63)) {
value = (~value) + 1;
neg = 1;
}
}
for (i = 0; value != 0; i++) {
rem = value % base;
value /= base;
if (rem >= 0 && rem <= 9)
digit = rem + '0';
else if (rem >= 10 && rem <= 36)
digit = (rem - 10) + 'a';
buf[i] = digit;
}
buf[i] = 0;
if (neg)
strcat (buf, "-");
else if (plussgn)
strcat (buf, "+");
/* reverse the string */
for (i = 0, j = strlen (buf) - 1; j >= 0; i++, j--)
dst[i] = buf[j];
dst[i] = 0;
return (dst);
}
/*
* gethex(vp,p,n)
* convert n hex digits from p to binary, result in vp,
* rtn 1 on success
*/
int
gethex(int32_t *vp, char *p, int n)
{
u_long v;
int digit;
for (v = 0; n > 0; n--) {
if (*p == 0)
return (0);
if (*p >= '0' && *p <= '9')
digit = *p - '0';
else if (*p >= 'a' && *p <= 'f')
digit = *p - 'a' + 10;
else if (*p >= 'A' && *p <= 'F')
digit = *p - 'A' + 10;
else
return (0);
v <<= 4;
v |= digit;
p++;
}
*vp = v;
return (1);
}
/* $Id: vsprintf.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $ */
/*
* Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Opsycon AB.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
//#include <stdio.h>
//#include <pmon.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
/*
* int vsprintf(d,s,ap)
*/
int
vsprintf (char *d, const char *s, va_list ap)
{
const char *t;
char *p, *dst, tmp[40];
unsigned int n;
int fmt, trunc, haddot, width, base, longlong, plussgn;
#ifdef FLOATINGPT
double dbl;
#ifndef NEWFP
EP ex;
#endif
#endif
dst = d;
for (; *s;) {
if (*s == '%') {
s++;
fmt = FMT_RJUST;
width = trunc = haddot = longlong = plussgn = 0;
for (; *s; s++) {
if (strchr("bcdefgilopPrRsuxX%", *s))
break;
else if (*s == '-')
fmt = FMT_LJUST;
else if (*s == '+')
plussgn = 1;
else if (*s == '0')
fmt = FMT_RJUST0;
else if (*s == '~')
fmt = FMT_CENTER;
else if (*s == '*') {
if (haddot)
trunc = va_arg(ap, int);
else
width = va_arg(ap, int);
} else if (*s >= '1' && *s <= '9') {
for (t = s; isdigit(*s); s++);
strncpy(tmp, t, s - t);
tmp[s - t] = '\0';
atob(&n, tmp, 10);
if (haddot)
trunc = n;
else
width = n;
s--;
} else if (*s == '.')
haddot = 1;
}
if (*s == '%') {
*d++ = '%';
*d = 0;
} else if (*s == 's') {
p = va_arg(ap, char *);
if (p)
strcpy(d, p);
else
strcpy(d, "(null)");
} else if (*s == 'c') {
n = va_arg (ap, int);
*d = n;
d[1] = 0;
} else {
if (*s == 'l') {
if (*++s == 'l') {
longlong = 1;
++s;
}
}
if (strchr("bdiopPrRxXu", *s)) {
if (*s == 'd' || *s == 'i')
base = -10;
else if (*s == 'u')
base = 10;
else if (*s == 'x' || *s == 'X')
base = 16;
else if(*s == 'p' || *s == 'P') {
base = 16;
if (*s == 'p') {
*d++ = '0';
*d++ = 'x';
}
fmt = FMT_RJUST0;
if (sizeof(long) > 4) {
width = 16;
longlong = 1;
} else {
width = 8;
}
}
else if(*s == 'r' || *s == 'R') {
base = 16;
if (*s == 'r') {
*d++ = '0';
*d++ = 'x';
}
fmt = FMT_RJUST0;
if (sizeof(register_t) > 4) {
width = 16;
longlong = 1;
} else {
width = 8;
}
}
else if (*s == 'o')
base = 8;
else if (*s == 'b')
base = 2;
if (longlong)
llbtoa(d, va_arg (ap, quad_t),
base, plussgn);
else
btoa(d, va_arg (ap, int), base, plussgn);
if (*s == 'X')
strtoupper(d);
}
#ifdef FLOATINGPT
else if (strchr ("eEfgG", *s)) {
//static void dtoa (char *, double, int, int, int, int);
void dtoa (char *dbuf, rtype arg, int fmtch, int width, int prec, int plussgn);
dbl = va_arg(ap, double);
if (!haddot) {
trunc = 6;
}
dtoa(d, dbl, *s, width, trunc, plussgn);
trunc = 0;
}
#endif
}
if (trunc)
d[trunc] = 0;
if (width)
str_fmt (d, width, fmt);
for (; *d; d++);
s++;
} else
*d++ = *s++;
}
*d = 0;
return (d - dst);
}
#ifdef FLOATINGPT
/*
* Floating point output, cvt() onward lifted from BSD sources:
*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define MAX_FCONVERSION 512 /* largest possible real conversion */
#define MAX_EXPT 5 /* largest possible exponent field */
#define MAX_FRACT 39 /* largest possible fraction field */
#define TESTFLAG(x) 0
typedef double rtype;
extern double modf(double, double *);
#define to_char(n) ((n) + '0')
#define to_digit(c) ((c) - '0')
#define _isNan(arg) ((arg) != (arg))
static int cvt (rtype arg, int prec, char *signp, int fmtch,
char *startp, char *endp, int plussgn);
static char *c_round (double fract, int *exp, char *start, char *end,
char ch, char *signp);
static char *exponent(char *p, int exp, int fmtch);
/*
* _finite arg not Infinity or Nan
*/
static int _finite(rtype d)
{
#if ENDIAN == ENDIAN_LITTLE
struct IEEEdp {
unsigned manl:32;
unsigned manh:20;
unsigned exp:11;
unsigned sign:1;
} *ip;
#else
struct IEEEdp {
unsigned sign:1;
unsigned exp:11;
unsigned manh:20;
unsigned manl:32;
} *ip;
#endif
ip = (struct IEEEdp *)&d;
return (ip->exp != 0x7ff);
}
void dtoa (char *dbuf, rtype arg, int fmtch, int width, int prec, int plussgn)
{
char buf[MAX_FCONVERSION+1], *cp;
char sign;
int size;
if( !_finite(arg) ) {
if( _isNan(arg) )
strcpy (dbuf, "NaN");
else if( arg < 0)
strcpy (dbuf, "-Infinity");
else
strcpy (dbuf, "Infinity");
return;
}
if (prec > MAX_FRACT)
prec = MAX_FRACT;
/* leave room for sign at start of buffer */
cp = buf + 1;
/*
* cvt may have to round up before the "start" of
* its buffer, i.e. ``intf("%.2f", (double)9.999);'';
* if the first character is still NUL, it did.
* softsign avoids negative 0 if _double < 0 but
* no significant digits will be shown.
*/
*cp = '\0';
size = cvt (arg, prec, &sign, fmtch, cp, buf + sizeof(buf), plussgn);
if (*cp == '\0')
cp++;
if (sign)
*--cp = sign, size++;
cp[size] = 0;
memcpy (dbuf, cp, size + 1);
}
static int
cvt(rtype number, int prec, char *signp, int fmtch, char *startp, char *endp, int plussgn)
{
register char *p, *t;
register double fract;
double integer, tmp;
int dotrim, expcnt, gformat;
dotrim = expcnt = gformat = 0;
if (number < 0) {
number = -number;
*signp = '-';
} else if (plussgn) {
*signp = '+';
} else
*signp = 0;
fract = modf(number, &integer);
/* get an extra slot for rounding. */
t = ++startp;
/*
* get integer portion of number; put into the end of the buffer; the
* .01 is added for modf(356.0 / 10, &integer) returning .59999999...
*/
for (p = endp - 1; integer; ++expcnt) {
tmp = modf(integer / 10, &integer);
*p-- = to_char((int)((tmp + .01) * 10));
}
switch (fmtch) {
case 'f':
/* reverse integer into beginning of buffer */
if (expcnt)
for (; ++p < endp; *t++ = *p);
else
*t++ = '0';
/*
* if precision required or alternate flag set, add in a
* decimal point.
*/
if (prec || TESTFLAG(ALTERNATE_FORM))
*t++ = '.';
/* if requires more precision and some fraction left */
if (fract) {
if (prec)
do {
fract = modf(fract * 10, &tmp);
*t++ = to_char((int)tmp);
} while (--prec && fract);
if (fract)
startp = c_round(fract, (int *)NULL, startp,
t - 1, (char)0, signp);
}
for (; prec--; *t++ = '0');
break;
case 'e':
case 'E':
eformat: if (expcnt) {
*t++ = *++p;
if (prec || TESTFLAG(ALTERNATE_FORM))
*t++ = '.';
/* if requires more precision and some integer left */
for (; prec && ++p < endp; --prec)
*t++ = *p;
/*
* if done precision and more of the integer component,
* round using it; adjust fract so we don't re-round
* later.
*/
if (!prec && ++p < endp) {
fract = 0;
startp = c_round((double)0, &expcnt, startp,
t - 1, *p, signp);
}
/* adjust expcnt for digit in front of decimal */
--expcnt;
}
/* until first fractional digit, decrement exponent */
else if (fract) {
/* adjust expcnt for digit in front of decimal */
for (expcnt = -1;; --expcnt) {
fract = modf(fract * 10, &tmp);
if (tmp)
break;
}
*t++ = to_char((int)tmp);
if (prec || TESTFLAG(ALTERNATE_FORM))
*t++ = '.';
}
else {
*t++ = '0';
if (prec || TESTFLAG(ALTERNATE_FORM))
*t++ = '.';
}
/* if requires more precision and some fraction left */
if (fract) {
if (prec)
do {
fract = modf(fract * 10, &tmp);
*t++ = to_char((int)tmp);
} while (--prec && fract);
if (fract)
startp = c_round(fract, &expcnt, startp,
t - 1, (char)0, signp);
}
/* if requires more precision */
for (; prec--; *t++ = '0');
/* unless alternate flag, trim any g/G format trailing 0's */
if (gformat && !TESTFLAG(ALTERNATE_FORM)) {
while (t > startp && *--t == '0');
if (*t == '.')
--t;
++t;
}
t = exponent(t, expcnt, fmtch);
break;
case 'g':
case 'G':
/* a precision of 0 is treated as a precision of 1. */
if (!prec)
++prec;
/*
* ``The style used depends on the value converted; style e
* will be used only if the exponent resulting from the
* conversion is less than -4 or greater than the precision.''
* -- ANSI X3J11
*/
if (expcnt > prec || (!expcnt && fract && fract < .0001)) {
/*
* g/G format counts "significant digits, not digits of
* precision; for the e/E format, this just causes an
* off-by-one problem, i.e. g/G considers the digit
* before the decimal point significant and e/E doesn't
* count it as precision.
*/
--prec;
fmtch -= 2; /* G->E, g->e */
gformat = 1;
goto eformat;
}
/*
* reverse integer into beginning of buffer,
* note, decrement precision
*/
if (expcnt)
for (; ++p < endp; *t++ = *p, --prec);
else
*t++ = '0';
/*
* if precision required or alternate flag set, add in a
* decimal point. If no digits yet, add in leading 0.
*/
if (prec || TESTFLAG(ALTERNATE_FORM)) {
dotrim = 1;
*t++ = '.';
}
else
dotrim = 0;
/* if requires more precision and some fraction left */
if (fract) {
if (prec) {
do {
fract = modf(fract * 10, &tmp);
*t++ = to_char((int)tmp);
} while(!tmp && !expcnt);
while (--prec && fract) {
fract = modf(fract * 10, &tmp);
*t++ = to_char((int)tmp);
}
}
if (fract)
startp = c_round(fract, (int *)NULL, startp,
t - 1, (char)0, signp);
}
/* alternate format, adds 0's for precision, else trim 0's */
if (TESTFLAG(ALTERNATE_FORM))
for (; prec--; *t++ = '0');
else if (dotrim) {
while (t > startp && *--t == '0');
if (*t != '.')
++t;
}
}
return (t - startp);
}
static char *
c_round(double fract, int *exp, char *start, char *end, char ch, char *signp)
{
double tmp;
if (fract)
(void)modf(fract * 10, &tmp);
else
tmp = to_digit(ch);
if (tmp > 4)
for (;; --end) {
if (*end == '.')
--end;
if (++*end <= '9')
break;
*end = '0';
if (end == start) {
if (exp) { /* e/E; increment exponent */
*end = '1';
++*exp;
}
else { /* f; add extra digit */
*--end = '1';
--start;
}
break;
}
}
/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
else if (*signp == '-' || *signp == '+')
for (;; --end) {
if (*end == '.')
--end;
if (*end != '0')
break;
if (end == start)
*signp = 0;
}
return (start);
}
static char *
exponent(char *p, int exp, int fmtch)
{
char *t;
char expbuf[MAX_FCONVERSION];
*p++ = fmtch;
if (exp < 0) {
exp = -exp;
*p++ = '-';
}
else
*p++ = '+';
t = expbuf + MAX_FCONVERSION;
if (exp > 9) {
do {
*--t = to_char(exp % 10);
} while ((exp /= 10) > 9);
*--t = to_char(exp);
for (; t < expbuf + MAX_FCONVERSION; *p++ = *t++);
}
else {
*p++ = '0';
*p++ = to_char(exp);
}
return (p);
}
int sprintf(char *s, const char *fmt, ...)
{
int n;
va_list arg;
va_start(arg, fmt);
n = vsprintf(s, fmt, arg);
va_end(arg);
return n;
}
#endif /* FLOATINGPT */
#endif
......@@ -14,8 +14,9 @@ extern void output_redirect(const char *str, size_t l);
#define printf(...) do { \
char __printf_buf[BUFSIZ]; \
sprintf(__printf_buf, __VA_ARGS__); \
snprintf(__printf_buf, BUFSIZ, __VA_ARGS__); \
puts(__printf_buf); \
} while(0)
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment