diff options
author | Damien Miller <djm@mindrot.org> | 2005-11-24 19:58:19 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2005-11-24 19:58:19 +1100 |
commit | 57f3915b5513495b11e7052df0260c7896b7b612 (patch) | |
tree | 88e15971a1f626cb8ef3ff18d8e9697fef8c2cef | |
parent | efc17470e0548dae4b6ffc34370ad15562d83239 (diff) |
- (djm) [configure.ac openbsd-compat/Makefile.in openbsd-compat/bsd-asprintf.c
openbsd-compat/bsd-snprintf.c openbsd-compat/openbsd-compat.h] Add an
asprintf() implementation, after syncing our {v,}snprintf() implementation
with some extra fixes from Samba's version. With help and debugging from
dtucker and tim; ok dtucker@
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | configure.ac | 70 | ||||
-rw-r--r-- | openbsd-compat/Makefile.in | 4 | ||||
-rw-r--r-- | openbsd-compat/bsd-asprintf.c | 95 | ||||
-rw-r--r-- | openbsd-compat/bsd-snprintf.c | 610 | ||||
-rw-r--r-- | openbsd-compat/openbsd-compat.h | 10 |
6 files changed, 562 insertions, 236 deletions
@@ -1,4 +1,11 @@ 20051122 + - (djm) [configure.ac openbsd-compat/Makefile.in openbsd-compat/bsd-asprintf.c + openbsd-compat/bsd-snprintf.c openbsd-compat/openbsd-compat.h] Add an + asprintf() implementation, after syncing our {v,}snprintf() implementation + with some extra fixes from Samba's version. With help and debugging from + dtucker and tim; ok dtucker@ + +20051122 - (dtucker) OpenBSD CVS Sync - deraadt@cvs.openbsd.org 2005/11/12 18:37:59 [ssh-add.c] @@ -3333,4 +3340,4 @@ - (djm) Trim deprecated options from INSTALL. Mention UsePAM - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu -$Id: ChangeLog,v 1.4002 2005/11/22 08:55:13 dtucker Exp $ +$Id: ChangeLog,v 1.4003 2005/11/24 08:58:19 djm Exp $ diff --git a/configure.ac b/configure.ac index 2885a69f..39655288 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# $Id: configure.ac,v 1.307 2005/11/12 07:42:37 dtucker Exp $ +# $Id: configure.ac,v 1.308 2005/11/24 08:58:20 djm Exp $ # # Copyright (c) 1999-2004 Damien Miller # @@ -426,6 +426,7 @@ mips-sony-bsd|mips-sony-newsos4) ;; # UnixWare 1.x, UnixWare 2.x, and others based on code from Univel. *-*-sysv4.2*) + CFLAGS="$CFLAGS -Dva_list=_VA_LIST" AC_DEFINE(USE_PIPES) AC_DEFINE(SETEUID_BREAKS_SETUID) AC_DEFINE(BROKEN_SETREUID) @@ -1106,6 +1107,7 @@ AC_ARG_WITH(audit, dnl Checks for library functions. Please keep in alphabetical order AC_CHECK_FUNCS( \ arc4random \ + asprintf \ b64_ntop \ __b64_ntop \ b64_pton \ @@ -1181,6 +1183,7 @@ AC_CHECK_FUNCS( \ truncate \ unsetenv \ updwtmpx \ + vasprintf \ vhangup \ vsnprintf \ waitpid \ @@ -1299,6 +1302,40 @@ int main(void){char b[5];snprintf(b,5,"123456789");exit(b[4]!='\0');} ) fi +# If we don't have a working asprintf, then we strongly depend on vsnprintf +# returning the right thing on overflow: the number of characters it tried to +# create (as per SUSv3) +if test "x$ac_cv_func_asprintf" != "xyes" && \ + test "x$ac_cv_func_vsnprintf" = "xyes" ; then + AC_MSG_CHECKING([whether vsnprintf returns correct values on overflow]) + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> + +int x_snprintf(char *str,size_t count,const char *fmt,...) +{ + size_t ret; va_list ap; + va_start(ap, fmt); ret = vsnprintf(str, count, fmt, ap); va_end(ap); + return ret; +} +int main(void) +{ + char x[1]; + exit(x_snprintf(x, 1, "%s %d", "hello", 12345) == 11 ? 0 : 1); +} ]])], + [AC_MSG_RESULT(yes)], + [ + AC_MSG_RESULT(no) + AC_DEFINE(BROKEN_SNPRINTF, 1, + [Define if your snprintf is busted]) + AC_MSG_WARN([****** Your vsnprintf() function is broken, complain to your vendor]) + ], + [ AC_MSG_WARN([cross compiling: Assuming working vsnprintf()]) ] + ) +fi + # Check for missing getpeereid (or equiv) support NO_PEERCHECK="" if test "x$ac_cv_func_getpeereid" != "xyes" ; then @@ -1978,7 +2015,10 @@ if test ! -z "$SONY" ; then LIBS="$LIBS -liberty"; fi -# Checks for data types +# Check for long long datatypes +AC_CHECK_TYPES([long long, unsigned long long, long double]) + +# Check datatype sizes AC_CHECK_SIZEOF(char, 1) AC_CHECK_SIZEOF(short int, 2) AC_CHECK_SIZEOF(int, 4) @@ -2669,6 +2709,32 @@ if test "x$ac_cv_cc_implements___func__" = "xyes" ; then AC_DEFINE(HAVE___func__, 1, [Define if compiler implements __func__]) fi +AC_CACHE_CHECK([whether va_copy exists], ac_cv_have_va_copy, [ + AC_TRY_LINK( + [#include <stdarg.h> + va_list x,y;], + [va_copy(x,y);], + [ ac_cv_have_va_copy="yes" ], + [ ac_cv_have_va_copy="no" ] + ) +]) +if test "x$ac_cv_have_va_copy" = "xyes" ; then + AC_DEFINE(HAVE_VA_COPY, 1, [Define if va_copy exists]) +fi + +AC_CACHE_CHECK([whether __va_copy exists], ac_cv_have___va_copy, [ + AC_TRY_LINK( + [#include <stdarg.h> + va_list x,y;], + [__va_copy(x,y);], + [ ac_cv_have___va_copy="yes" ], + [ ac_cv_have___va_copy="no" ] + ) +]) +if test "x$ac_cv_have___va_copy" = "xyes" ; then + AC_DEFINE(HAVE___VA_COPY, 1, [Define if __va_copy exists]) +fi + AC_CACHE_CHECK([whether getopt has optreset support], ac_cv_have_getopt_optreset, [ AC_TRY_LINK( diff --git a/openbsd-compat/Makefile.in b/openbsd-compat/Makefile.in index 6f5ee284..89ac6cda 100644 --- a/openbsd-compat/Makefile.in +++ b/openbsd-compat/Makefile.in @@ -1,4 +1,4 @@ -# $Id: Makefile.in,v 1.35 2005/08/26 20:15:20 tim Exp $ +# $Id: Makefile.in,v 1.36 2005/11/24 08:58:21 djm Exp $ sysconfdir=@sysconfdir@ piddir=@piddir@ @@ -18,7 +18,7 @@ LDFLAGS=-L. @LDFLAGS@ OPENBSD=base64.o basename.o bindresvport.o daemon.o dirname.o getcwd.o getgrouplist.o getopt.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o readpassphrase.o realpath.o rresvport.o setenv.o setproctitle.o sigact.o strlcat.o strlcpy.o strmode.o strsep.o strtonum.o strtoll.o strtoul.o vis.o -COMPAT=bsd-arc4random.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-snprintf.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xmmap.o xcrypt.o +COMPAT=bsd-arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-snprintf.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xmmap.o xcrypt.o PORTS=port-irix.o port-aix.o port-uw.o diff --git a/openbsd-compat/bsd-asprintf.c b/openbsd-compat/bsd-asprintf.c new file mode 100644 index 00000000..5ca01f80 --- /dev/null +++ b/openbsd-compat/bsd-asprintf.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2004 Darren Tucker. + * + * Based originally on asprintf.c from OpenBSD: + * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifndef HAVE_VASPRINTF + +#ifndef VA_COPY +# ifdef HAVE_VA_COPY +# define VA_COPY(dest, src) va_copy(dest, src) +# else +# ifdef HAVE___VA_COPY +# define VA_COPY(dest, src) __va_copy(dest, src) +# else +# define VA_COPY(dest, src) (dest) = (src) +# endif +# endif +#endif + +#define INIT_SZ 128 + +int vasprintf(char **str, const char *fmt, va_list ap) +{ + int ret = -1; + va_list ap2; + char *string, *newstr; + size_t len; + + VA_COPY(ap2, ap); + if ((string = malloc(INIT_SZ)) == NULL) + goto fail; + + ret = vsnprintf(string, INIT_SZ, fmt, ap2); + if (ret >= 0 && ret < INIT_SZ) { /* succeeded with initial alloc */ + *str = string; + } else if (ret == INT_MAX) { /* shouldn't happen */ + goto fail; + } else { /* bigger than initial, realloc allowing for nul */ + len = (size_t)ret + 1; + if ((newstr = realloc(string, len)) == NULL) { + free(string); + goto fail; + } else { + va_end(ap2); + VA_COPY(ap2, ap); + ret = vsnprintf(newstr, len, fmt, ap2); + if (ret >= 0 && (size_t)ret < len) { + *str = newstr; + } else { /* failed with realloc'ed string, give up */ + free(newstr); + goto fail; + } + } + } + va_end(ap2); + return (ret); + +fail: + *str = NULL; + errno = ENOMEM; + va_end(ap2); + return (-1); +} +#endif + +#ifndef HAVE_ASPRINTF +int asprintf(char **str, const char *fmt, ...) +{ + va_list ap; + int ret; + + *str = NULL; + va_start(ap, fmt); + ret = vasprintf(str, fmt, ap); + va_end(ap); + + return ret; +} +#endif diff --git a/openbsd-compat/bsd-snprintf.c b/openbsd-compat/bsd-snprintf.c index b5a7ef7a..ca275abd 100644 --- a/openbsd-compat/bsd-snprintf.c +++ b/openbsd-compat/bsd-snprintf.c @@ -45,45 +45,82 @@ * missing. Some systems only have snprintf() but not vsnprintf(), so * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. * - * Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH - * Welcome to the world of %lld and %qd support. With other - * long long support. This is needed for sftp-server to work - * right. + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE * - * Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH - * Removed all hint of VARARGS stuff and banished it to the void, - * and did a bit of KNF style work to make things a bit more - * acceptable. Consider stealing from mutt or enlightenment. + * tridge@samba.org, idra@samba.org, April 2001 + * got rid of fcvt code (twas buggy and made testing harder) + * added C99 semantics + * + * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0 + * actually print args for %g and %e + * + * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0 + * Since includes.h isn't included here, VA_COPY has to be defined here. I don't + * see any include file that is guaranteed to be here, so I'm defining it + * locally. Fixes AIX and Solaris builds. + * + * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13 + * put the ifdef for HAVE_VA_COPY in one place rather than in lots of + * functions + * + * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4 + * Fix usage of va_list passed as an arg. Use __va_copy before using it + * when it exists. + * + * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14 + * Fix incorrect zpadlen handling in fmtfp. + * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it. + * few mods to make it easier to compile the tests. + * addedd the "Ollie" test to the floating point ones. + * + * Martin Pool (mbp@samba.org) April 2003 + * Remove NO_CONFIG_H so that the test case can be built within a source + * tree with less trouble. + * Remove unnecessary SAFE_FREE() definition. + * + * Martin Pool (mbp@samba.org) May 2003 + * Put in a prototype for dummy_snprintf() to quiet compiler warnings. + * + * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even + * if the C library has some snprintf functions already. **************************************************************/ #include "includes.h" -RCSID("$Id: bsd-snprintf.c,v 1.9 2004/09/23 11:35:09 dtucker Exp $"); +RCSID("$Id: bsd-snprintf.c,v 1.10 2005/11/24 08:58:21 djm Exp $"); #if defined(BROKEN_SNPRINTF) /* For those with broken snprintf() */ # undef HAVE_SNPRINTF # undef HAVE_VSNPRINTF #endif -#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) - -static void -dopr(char *buffer, size_t maxlen, const char *format, va_list args); - -static void -fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, int flags, - int min, int max); +#ifndef VA_COPY +# ifdef HAVE_VA_COPY +# define VA_COPY(dest, src) va_copy(dest, src) +# else +# ifdef HAVE___VA_COPY +# define VA_COPY(dest, src) __va_copy(dest, src) +# else +# define VA_COPY(dest, src) (dest) = (src) +# endif +# endif +#endif -static void -fmtint(char *buffer, size_t *currlen, size_t maxlen, long value, int base, - int min, int max, int flags); +#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) -static void -fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue, - int min, int max, int flags); +#ifdef HAVE_LONG_DOUBLE +# define LDOUBLE long double +#else +# define LDOUBLE double +#endif -static void -dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); +#ifdef HAVE_LONG_LONG +# define LLONG long long +#else +# define LLONG long +#endif /* * dopr(): poor man's version of doprintf @@ -109,28 +146,49 @@ dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); #define DP_F_UNSIGNED (1 << 6) /* Conversion Flags */ -#define DP_C_SHORT 1 -#define DP_C_LONG 2 -#define DP_C_LDOUBLE 3 -#define DP_C_LONG_LONG 4 - -#define char_to_int(p) (p - '0') -#define abs_val(p) (p < 0 ? -p : p) - +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LDOUBLE 3 +#define DP_C_LLONG 4 + +#define char_to_int(p) ((p)- '0') +#ifndef MAX +# define MAX(p,q) (((p) >= (q)) ? (p) : (q)) +#endif -static void -dopr(char *buffer, size_t maxlen, const char *format, va_list args) +static size_t dopr(char *buffer, size_t maxlen, const char *format, + va_list args_in); +static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static void fmtint(char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags); +static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); +static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); + +static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in) { - char *strvalue, ch; - long value; - long double fvalue; - int min = 0, max = -1, state = DP_S_DEFAULT, flags = 0, cflags = 0; - size_t currlen = 0; - + char ch; + LLONG value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + va_list args; + + VA_COPY(args, args_in); + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; ch = *format++; - + while (state != DP_S_DONE) { - if ((ch == '\0') || (currlen >= maxlen)) + if (ch == '\0') state = DP_S_DONE; switch(state) { @@ -138,7 +196,7 @@ dopr(char *buffer, size_t maxlen, const char *format, va_list args) if (ch == '%') state = DP_S_FLAGS; else - dopr_outch(buffer, &currlen, maxlen, ch); + dopr_outch (buffer, &currlen, maxlen, ch); ch = *format++; break; case DP_S_FLAGS: @@ -170,34 +228,37 @@ dopr(char *buffer, size_t maxlen, const char *format, va_list args) break; case DP_S_MIN: if (isdigit((unsigned char)ch)) { - min = 10 * min + char_to_int (ch); + min = 10*min + char_to_int (ch); ch = *format++; } else if (ch == '*') { min = va_arg (args, int); ch = *format++; state = DP_S_DOT; - } else + } else { state = DP_S_DOT; + } break; case DP_S_DOT: if (ch == '.') { state = DP_S_MAX; ch = *format++; - } else + } else { state = DP_S_MOD; + } break; case DP_S_MAX: if (isdigit((unsigned char)ch)) { if (max < 0) max = 0; - max = 10 * max + char_to_int(ch); + max = 10*max + char_to_int (ch); ch = *format++; } else if (ch == '*') { max = va_arg (args, int); ch = *format++; state = DP_S_MOD; - } else + } else { state = DP_S_MOD; + } break; case DP_S_MOD: switch (ch) { @@ -208,15 +269,11 @@ dopr(char *buffer, size_t maxlen, const char *format, va_list args) case 'l': cflags = DP_C_LONG; ch = *format++; - if (ch == 'l') { - cflags = DP_C_LONG_LONG; + if (ch == 'l') { /* It's a long long */ + cflags = DP_C_LLONG; ch = *format++; } break; - case 'q': - cflags = DP_C_LONG_LONG; - ch = *format++; - break; case 'L': cflags = DP_C_LDOUBLE; ch = *format++; @@ -231,37 +288,37 @@ dopr(char *buffer, size_t maxlen, const char *format, va_list args) case 'd': case 'i': if (cflags == DP_C_SHORT) - value = va_arg(args, int); + value = va_arg (args, int); else if (cflags == DP_C_LONG) - value = va_arg(args, long int); - else if (cflags == DP_C_LONG_LONG) - value = va_arg (args, long long); + value = va_arg (args, long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, LLONG); else value = va_arg (args, int); - fmtint(buffer, &currlen, maxlen, value, 10, min, max, flags); + fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'o': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) - value = va_arg(args, unsigned int); + value = va_arg (args, unsigned int); else if (cflags == DP_C_LONG) - value = va_arg(args, unsigned long int); - else if (cflags == DP_C_LONG_LONG) - value = va_arg(args, unsigned long long); + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (long)va_arg (args, unsigned LLONG); else - value = va_arg(args, unsigned int); - fmtint(buffer, &currlen, maxlen, value, 8, min, max, flags); + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); break; case 'u': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) - value = va_arg(args, unsigned int); + value = va_arg (args, unsigned int); else if (cflags == DP_C_LONG) - value = va_arg(args, unsigned long int); - else if (cflags == DP_C_LONG_LONG) - value = va_arg(args, unsigned long long); + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); else - value = va_arg(args, unsigned int); + value = (long)va_arg (args, unsigned int); fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); break; case 'X': @@ -269,79 +326,86 @@ dopr(char *buffer, size_t maxlen, const char *format, va_list args) case 'x': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) - value = va_arg(args, unsigned int); + value = va_arg (args, unsigned int); else if (cflags == DP_C_LONG) - value = va_arg(args, unsigned long int); - else if (cflags == DP_C_LONG_LONG) - value = va_arg(args, unsigned long long); + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); else - value = va_arg(args, unsigned int); - fmtint(buffer, &currlen, maxlen, value, 16, min, max, flags); + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); break; case 'f': if (cflags == DP_C_LDOUBLE) - fvalue = va_arg(args, long double); + fvalue = va_arg (args, LDOUBLE); else - fvalue = va_arg(args, double); + fvalue = va_arg (args, double); /* um, floating point? */ - fmtfp(buffer, &currlen, maxlen, fvalue, min, max, flags); + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'E': flags |= DP_F_UP; case 'e': if (cflags == DP_C_LDOUBLE) - fvalue = va_arg(args, long double); + fvalue = va_arg (args, LDOUBLE); else - fvalue = va_arg(args, double); + fvalue = va_arg (args, double); + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'G': flags |= DP_F_UP; case 'g': if (cflags == DP_C_LDOUBLE) - fvalue = va_arg(args, long double); + fvalue = va_arg (args, LDOUBLE); else - fvalue = va_arg(args, double); + fvalue = va_arg (args, double); + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); break; case 'c': - dopr_outch(buffer, &currlen, maxlen, va_arg(args, int)); + dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); break; case 's': - strvalue = va_arg(args, char *); - if (max < 0) - max = maxlen; /* ie, no max */ - fmtstr(buffer, &currlen, maxlen, strvalue, flags, min, max); + strvalue = va_arg (args, char *); + if (!strvalue) strvalue = "(NULL)"; + if (max == -1) { + max = strlen(strvalue); + } + if (min > 0 && max >= 0 && min > max) max = min; + fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); break; case 'p': - strvalue = va_arg(args, void *); - fmtint(buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); + strvalue = va_arg (args, void *); + fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); break; case 'n': if (cflags == DP_C_SHORT) { short int *num; - num = va_arg(args, short int *); + num = va_arg (args, short int *); *num = currlen; } else if (cflags == DP_C_LONG) { long int *num; - num = va_arg(args, long int *); - *num = currlen; - } else if (cflags == DP_C_LONG_LONG) { - long long *num; - num = va_arg(args, long long *); - *num = currlen; + num = va_arg (args, long int *); + *num = (long int)currlen; + } else if (cflags == DP_C_LLONG) { + LLONG *num; + num = va_arg (args, LLONG *); + *num = (LLONG)currlen; } else { int *num; - num = va_arg(args, int *); + num = va_arg (args, int *); *num = currlen; } break; case '%': - dopr_outch(buffer, &currlen, maxlen, ch); + dopr_outch (buffer, &currlen, maxlen, ch); break; - case 'w': /* not supported yet, treat as next char */ + case 'w': + /* not supported yet, treat as next char */ ch = *format++; break; - default: /* Unknown, skip */ - break; + default: + /* Unknown, skip */ + break; } ch = *format++; state = DP_S_DEFAULT; @@ -350,24 +414,33 @@ dopr(char *buffer, size_t maxlen, const char *format, va_list args) break; case DP_S_DONE: break; - default: /* hmm? */ + default: + /* hmm? */ break; /* some picky compilers need this */ } } - if (currlen < maxlen - 1) - buffer[currlen] = '\0'; - else - buffer[maxlen - 1] = '\0'; + if (maxlen != 0) { + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else if (maxlen > 0) + buffer[maxlen - 1] = '\0'; + } + + return currlen; } -static void -fmtstr(char *buffer, size_t *currlen, size_t maxlen, - char *value, int flags, int min, int max) +static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) { - int cnt = 0, padlen, strln; /* amount to pad */ - - if (value == 0) + int padlen, strln; /* amount to pad */ + int cnt = 0; + +#ifdef DEBUG_SNPRINTF + printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); +#endif + if (value == 0) { value = "<NULL>"; + } for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ padlen = min - strln; @@ -375,18 +448,18 @@ fmtstr(char *buffer, size_t *currlen, size_t maxlen, padlen = 0; if (flags & DP_F_MINUS) padlen = -padlen; /* Left Justify */ - + while ((padlen > 0) && (cnt < max)) { - dopr_outch(buffer, currlen, maxlen, ' '); + dopr_outch (buffer, currlen, maxlen, ' '); --padlen; ++cnt; } while (*value && (cnt < max)) { - dopr_outch(buffer, currlen, maxlen, *value++); + dopr_outch (buffer, currlen, maxlen, *value++); ++cnt; } while ((padlen < 0) && (cnt < max)) { - dopr_outch(buffer, currlen, maxlen, ' '); + dopr_outch (buffer, currlen, maxlen, ' '); ++padlen; ++cnt; } @@ -394,49 +467,49 @@ fmtstr(char *buffer, size_t *currlen, size_t maxlen, /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ -static void -fmtint(char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags) +static void fmtint(char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags) { + int signvalue = 0; unsigned long uvalue; char convert[20]; - int signvalue = 0, place = 0, caps = 0; + int place = 0; int spadlen = 0; /* amount to space pad */ int zpadlen = 0; /* amount to zero pad */ - + int caps = 0; + if (max < 0) max = 0; - + uvalue = value; - - if (!(flags & DP_F_UNSIGNED)) { - if (value < 0) { + + if(!(flags & DP_F_UNSIGNED)) { + if( value < 0 ) { signvalue = '-'; uvalue = -value; - } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else if (flags & DP_F_SPACE) - signvalue = ' '; + } else { + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } } - if (flags & DP_F_UP) - caps = 1; /* Should characters be upper case? */ + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ + do { convert[place++] = - (caps ? "0123456789ABCDEF" : "0123456789abcdef") - [uvalue % (unsigned)base]; + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; uvalue = (uvalue / (unsigned)base ); - } while (uvalue && (place < 20)); - if (place == 20) - place--; + } while(uvalue && (place < 20)); + if (place == 20) place--; convert[place] = 0; zpadlen = max - place; spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); - if (zpadlen < 0) - zpadlen = 0; - if (spadlen < 0) - spadlen = 0; + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; if (flags & DP_F_ZERO) { zpadlen = MAX(zpadlen, spadlen); spadlen = 0; @@ -444,27 +517,32 @@ fmtint(char *buffer, size_t *currlen, size_t maxlen, if (flags & DP_F_MINUS) spadlen = -spadlen; /* Left Justifty */ +#ifdef DEBUG_SNPRINTF + printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place); +#endif + /* Spaces */ while (spadlen > 0) { - dopr_outch(buffer, currlen, maxlen, ' '); + dopr_outch (buffer, currlen, maxlen, ' '); --spadlen; } /* Sign */ if (signvalue) - dopr_outch(buffer, currlen, maxlen, signvalue); + dopr_outch (buffer, currlen, maxlen, signvalue); /* Zeros */ if (zpadlen > 0) { while (zpadlen > 0) { - dopr_outch(buffer, currlen, maxlen, '0'); + dopr_outch (buffer, currlen, maxlen, '0'); --zpadlen; } } /* Digits */ while (place > 0) - dopr_outch(buffer, currlen, maxlen, convert[--place]); + dopr_outch (buffer, currlen, maxlen, convert[--place]); /* Left Justified spaces */ while (spadlen < 0) { @@ -473,11 +551,20 @@ fmtint(char *buffer, size_t *currlen, size_t maxlen, } } -static long double -pow10(int exp) +static LDOUBLE abs_val(LDOUBLE value) { - long double result = 1; + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} +static LDOUBLE POW10(int exp) +{ + LDOUBLE result = 1; + while (exp) { result *= 10; exp--; @@ -486,28 +573,69 @@ pow10(int exp) return result; } -static long -round(long double value) +static LLONG ROUND(LDOUBLE value) { - long intpart = value; - - value -= intpart; - if (value >= 0.5) - intpart++; + LLONG intpart; + intpart = (LLONG)value; + value = value - intpart; + if (value >= 0.5) intpart++; + return intpart; } -static void -fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue, - int min, int max, int flags) +/* a replacement for modf that doesn't need the math library. Should + be portable, but slow */ +static double my_modf(double x0, double *iptr) { - char iconvert[20], fconvert[20]; - int signvalue = 0, iplace = 0, fplace = 0; + int i; + long l; + double x = x0; + double f = 1.0; + + for (i=0;i<100;i++) { + l = (long)x; + if (l <= (x+1) && l >= (x-1)) break; + x *= 0.1; + f *= 10.0; + } + + if (i == 100) { + /* yikes! the number is beyond what we can handle. What do we do? */ + (*iptr) = 0; + return 0; + } + + if (i != 0) { + double i2; + double ret; + + ret = my_modf(x0-l*f, &i2); + (*iptr) = l*f + i2; + return ret; + } + + (*iptr) = l; + return x - (*iptr); +} + + +static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags) +{ + int signvalue = 0; + double ufvalue; + char iconvert[311]; + char fconvert[311]; + int iplace = 0; + int fplace = 0; int padlen = 0; /* amount to pad */ - int zpadlen = 0, caps = 0; - long intpart, fracpart; - long double ufvalue; + int zpadlen = 0; + int caps = 0; + int idx; + double intpart; + double fracpart; + double temp; /* * AIX manpage says the default is 0, but Solaris says the default @@ -516,137 +644,159 @@ fmtfp(char *buffer, size_t *currlen, size_t maxlen, long double fvalue, if (max < 0) max = 6; - ufvalue = abs_val(fvalue); + ufvalue = abs_val (fvalue); - if (fvalue < 0) + if (fvalue < 0) { signvalue = '-'; - else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ - signvalue = '+'; - else if (flags & DP_F_SPACE) - signvalue = ' '; + } else { + if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ + signvalue = '+'; + } else { + if (flags & DP_F_SPACE) + signvalue = ' '; + } + } - intpart = ufvalue; +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + +#if 0 + if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ +#endif /* - * Sorry, we only support 9 digits past the decimal because of our + * Sorry, we only support 16 digits past the decimal because of our * conversion method */ - if (max > 9) - max = 9; + if (max > 16) + max = 16; /* We "cheat" by converting the fractional part to integer by * multiplying by a factor of 10 */ - fracpart = round((pow10 (max)) * (ufvalue - intpart)); - if (fracpart >= pow10 (max)) { + temp = ufvalue; + my_modf(temp, &intpart); + + fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); + + if (fracpart >= POW10(max)) { intpart++; - fracpart -= pow10 (max); + fracpart -= POW10(max); } /* Convert integer part */ do { + temp = intpart*0.1; + my_modf(temp, &intpart); + idx = (int) ((temp -intpart +0.05)* 10.0); + /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ + /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ iconvert[iplace++] = - (caps ? "0123456789ABCDEF" : "0123456789abcdef") - [intpart % 10]; - intpart = (intpart / 10); - } while(intpart && (iplace < 20)); - if (iplace == 20) - iplace--; + (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; + } while (intpart && (iplace < 311)); + if (iplace == 311) iplace--; iconvert[iplace] = 0; /* Convert fractional part */ - do { - fconvert[fplace++] = - (caps ? "0123456789ABCDEF" : "0123456789abcdef") - [fracpart % 10]; - fracpart = (fracpart / 10); - } while(fracpart && (fplace < 20)); - if (fplace == 20) - fplace--; |