bsd-snprintf.c revision 157016
1113908Sdes/* 2113908Sdes * Copyright Patrick Powell 1995 3113908Sdes * This code is based on code written by Patrick Powell (papowell@astart.com) 4113908Sdes * It may be used for any purpose as long as this notice remains intact 5113908Sdes * on all source code distributions 6113908Sdes */ 7113908Sdes 898937Sdes/************************************************************** 998937Sdes * Original: 1098937Sdes * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 1198937Sdes * A bombproof version of doprnt (dopr) included. 1298937Sdes * Sigh. This sort of thing is always nasty do deal with. Note that 1398937Sdes * the version here does not include floating point... 1498937Sdes * 1598937Sdes * snprintf() is used instead of sprintf() as it does limit checks 1698937Sdes * for string length. This covers a nasty loophole. 1798937Sdes * 1898937Sdes * The other functions are there to prevent NULL pointers from 1998937Sdes * causing nast effects. 2098937Sdes * 2198937Sdes * More Recently: 2298937Sdes * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 2398937Sdes * This was ugly. It is still ugly. I opted out of floating point 2498937Sdes * numbers, but the formatter understands just about everything 2598937Sdes * from the normal C string format, at least as far as I can tell from 2698937Sdes * the Solaris 2.5 printf(3S) man page. 2798937Sdes * 2898937Sdes * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 2998937Sdes * Ok, added some minimal floating point support, which means this 3098937Sdes * probably requires libm on most operating systems. Don't yet 3198937Sdes * support the exponent (e,E) and sigfig (g,G). Also, fmtint() 3298937Sdes * was pretty badly broken, it just wasn't being exercised in ways 3398937Sdes * which showed it, so that's been fixed. Also, formated the code 3498937Sdes * to mutt conventions, and removed dead code left over from the 3598937Sdes * original. Also, there is now a builtin-test, just compile with: 3698937Sdes * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm 3798937Sdes * and run snprintf for results. 3898937Sdes * 3998937Sdes * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i 4098937Sdes * The PGP code was using unsigned hexadecimal formats. 4198937Sdes * Unfortunately, unsigned formats simply didn't work. 4298937Sdes * 4398937Sdes * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 4498937Sdes * The original code assumed that both snprintf() and vsnprintf() were 4598937Sdes * missing. Some systems only have snprintf() but not vsnprintf(), so 4698937Sdes * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. 4798937Sdes * 48157016Sdes * Andrew Tridgell (tridge@samba.org) Oct 1998 49157016Sdes * fixed handling of %.0f 50157016Sdes * added test for HAVE_LONG_DOUBLE 5198937Sdes * 52157016Sdes * tridge@samba.org, idra@samba.org, April 2001 53157016Sdes * got rid of fcvt code (twas buggy and made testing harder) 54157016Sdes * added C99 semantics 55157016Sdes * 56157016Sdes * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0 57157016Sdes * actually print args for %g and %e 58157016Sdes * 59157016Sdes * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0 60157016Sdes * Since includes.h isn't included here, VA_COPY has to be defined here. I don't 61157016Sdes * see any include file that is guaranteed to be here, so I'm defining it 62157016Sdes * locally. Fixes AIX and Solaris builds. 63157016Sdes * 64157016Sdes * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13 65157016Sdes * put the ifdef for HAVE_VA_COPY in one place rather than in lots of 66157016Sdes * functions 67157016Sdes * 68157016Sdes * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4 69157016Sdes * Fix usage of va_list passed as an arg. Use __va_copy before using it 70157016Sdes * when it exists. 71157016Sdes * 72157016Sdes * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14 73157016Sdes * Fix incorrect zpadlen handling in fmtfp. 74157016Sdes * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it. 75157016Sdes * few mods to make it easier to compile the tests. 76157016Sdes * addedd the "Ollie" test to the floating point ones. 77157016Sdes * 78157016Sdes * Martin Pool (mbp@samba.org) April 2003 79157016Sdes * Remove NO_CONFIG_H so that the test case can be built within a source 80157016Sdes * tree with less trouble. 81157016Sdes * Remove unnecessary SAFE_FREE() definition. 82157016Sdes * 83157016Sdes * Martin Pool (mbp@samba.org) May 2003 84157016Sdes * Put in a prototype for dummy_snprintf() to quiet compiler warnings. 85157016Sdes * 86157016Sdes * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even 87157016Sdes * if the C library has some snprintf functions already. 8898937Sdes **************************************************************/ 8998937Sdes 9098937Sdes#include "includes.h" 9198937Sdes 92157016SdesRCSID("$Id: bsd-snprintf.c,v 1.11 2005/12/17 11:32:04 dtucker Exp $"); 9398937Sdes 9498937Sdes#if defined(BROKEN_SNPRINTF) /* For those with broken snprintf() */ 9598937Sdes# undef HAVE_SNPRINTF 9698937Sdes# undef HAVE_VSNPRINTF 9798937Sdes#endif 9898937Sdes 99157016Sdes#ifndef VA_COPY 100157016Sdes# ifdef HAVE_VA_COPY 101157016Sdes# define VA_COPY(dest, src) va_copy(dest, src) 102157016Sdes# else 103157016Sdes# ifdef HAVE___VA_COPY 104157016Sdes# define VA_COPY(dest, src) __va_copy(dest, src) 105157016Sdes# else 106157016Sdes# define VA_COPY(dest, src) (dest) = (src) 107157016Sdes# endif 108157016Sdes# endif 109157016Sdes#endif 110157016Sdes 11198937Sdes#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) 11298937Sdes 113157016Sdes#ifdef HAVE_LONG_DOUBLE 114157016Sdes# define LDOUBLE long double 115157016Sdes#else 116157016Sdes# define LDOUBLE double 117157016Sdes#endif 11898937Sdes 119157016Sdes#ifdef HAVE_LONG_LONG 120157016Sdes# define LLONG long long 121157016Sdes#else 122157016Sdes# define LLONG long 123157016Sdes#endif 12498937Sdes 12598937Sdes/* 12698937Sdes * dopr(): poor man's version of doprintf 12798937Sdes */ 12898937Sdes 12998937Sdes/* format read states */ 13098937Sdes#define DP_S_DEFAULT 0 13198937Sdes#define DP_S_FLAGS 1 13298937Sdes#define DP_S_MIN 2 13398937Sdes#define DP_S_DOT 3 13498937Sdes#define DP_S_MAX 4 13598937Sdes#define DP_S_MOD 5 13698937Sdes#define DP_S_CONV 6 13798937Sdes#define DP_S_DONE 7 13898937Sdes 13998937Sdes/* format flags - Bits */ 14098937Sdes#define DP_F_MINUS (1 << 0) 14198937Sdes#define DP_F_PLUS (1 << 1) 14298937Sdes#define DP_F_SPACE (1 << 2) 14398937Sdes#define DP_F_NUM (1 << 3) 14498937Sdes#define DP_F_ZERO (1 << 4) 14598937Sdes#define DP_F_UP (1 << 5) 14698937Sdes#define DP_F_UNSIGNED (1 << 6) 14798937Sdes 14898937Sdes/* Conversion Flags */ 149157016Sdes#define DP_C_SHORT 1 150157016Sdes#define DP_C_LONG 2 151157016Sdes#define DP_C_LDOUBLE 3 152157016Sdes#define DP_C_LLONG 4 15398937Sdes 154157016Sdes#define char_to_int(p) ((p)- '0') 155157016Sdes#ifndef MAX 156157016Sdes# define MAX(p,q) (((p) >= (q)) ? (p) : (q)) 157157016Sdes#endif 15898937Sdes 159157016Sdesstatic size_t dopr(char *buffer, size_t maxlen, const char *format, 160157016Sdes va_list args_in); 161157016Sdesstatic void fmtstr(char *buffer, size_t *currlen, size_t maxlen, 162157016Sdes char *value, int flags, int min, int max); 163157016Sdesstatic void fmtint(char *buffer, size_t *currlen, size_t maxlen, 164157016Sdes long value, int base, int min, int max, int flags); 165157016Sdesstatic void fmtfp(char *buffer, size_t *currlen, size_t maxlen, 166157016Sdes LDOUBLE fvalue, int min, int max, int flags); 167157016Sdesstatic void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); 16898937Sdes 169157016Sdesstatic size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in) 17098937Sdes{ 171157016Sdes char ch; 172157016Sdes LLONG value; 173157016Sdes LDOUBLE fvalue; 174157016Sdes char *strvalue; 175157016Sdes int min; 176157016Sdes int max; 177157016Sdes int state; 178157016Sdes int flags; 179157016Sdes int cflags; 180157016Sdes size_t currlen; 181157016Sdes va_list args; 182157016Sdes 183157016Sdes VA_COPY(args, args_in); 184157016Sdes 185157016Sdes state = DP_S_DEFAULT; 186157016Sdes currlen = flags = cflags = min = 0; 187157016Sdes max = -1; 18898937Sdes ch = *format++; 189157016Sdes 19098937Sdes while (state != DP_S_DONE) { 191157016Sdes if (ch == '\0') 19298937Sdes state = DP_S_DONE; 19398937Sdes 19498937Sdes switch(state) { 195124208Sdes case DP_S_DEFAULT: 196124208Sdes if (ch == '%') 197124208Sdes state = DP_S_FLAGS; 198124208Sdes else 199157016Sdes dopr_outch (buffer, &currlen, maxlen, ch); 200124208Sdes ch = *format++; 201124208Sdes break; 202124208Sdes case DP_S_FLAGS: 203124208Sdes switch (ch) { 204124208Sdes case '-': 205124208Sdes flags |= DP_F_MINUS; 20698937Sdes ch = *format++; 20798937Sdes break; 208124208Sdes case '+': 209124208Sdes flags |= DP_F_PLUS; 210124208Sdes ch = *format++; 21198937Sdes break; 212124208Sdes case ' ': 213124208Sdes flags |= DP_F_SPACE; 214124208Sdes ch = *format++; 21598937Sdes break; 216124208Sdes case '#': 217124208Sdes flags |= DP_F_NUM; 218124208Sdes ch = *format++; 21998937Sdes break; 220124208Sdes case '0': 221124208Sdes flags |= DP_F_ZERO; 222124208Sdes ch = *format++; 223124208Sdes break; 224124208Sdes default: 225124208Sdes state = DP_S_MIN; 226124208Sdes break; 227124208Sdes } 228124208Sdes break; 229124208Sdes case DP_S_MIN: 230124208Sdes if (isdigit((unsigned char)ch)) { 231157016Sdes min = 10*min + char_to_int (ch); 232124208Sdes ch = *format++; 233124208Sdes } else if (ch == '*') { 234124208Sdes min = va_arg (args, int); 235124208Sdes ch = *format++; 236124208Sdes state = DP_S_DOT; 237157016Sdes } else { 238124208Sdes state = DP_S_DOT; 239157016Sdes } 240124208Sdes break; 241124208Sdes case DP_S_DOT: 242124208Sdes if (ch == '.') { 243124208Sdes state = DP_S_MAX; 244124208Sdes ch = *format++; 245157016Sdes } else { 246124208Sdes state = DP_S_MOD; 247157016Sdes } 248124208Sdes break; 249124208Sdes case DP_S_MAX: 250124208Sdes if (isdigit((unsigned char)ch)) { 251124208Sdes if (max < 0) 252124208Sdes max = 0; 253157016Sdes max = 10*max + char_to_int (ch); 254124208Sdes ch = *format++; 255124208Sdes } else if (ch == '*') { 256124208Sdes max = va_arg (args, int); 257124208Sdes ch = *format++; 258124208Sdes state = DP_S_MOD; 259157016Sdes } else { 260124208Sdes state = DP_S_MOD; 261157016Sdes } 262124208Sdes break; 263124208Sdes case DP_S_MOD: 264124208Sdes switch (ch) { 265124208Sdes case 'h': 266124208Sdes cflags = DP_C_SHORT; 267124208Sdes ch = *format++; 268124208Sdes break; 269124208Sdes case 'l': 270124208Sdes cflags = DP_C_LONG; 271124208Sdes ch = *format++; 272157016Sdes if (ch == 'l') { /* It's a long long */ 273157016Sdes cflags = DP_C_LLONG; 27498937Sdes ch = *format++; 275124208Sdes } 27698937Sdes break; 277124208Sdes case 'L': 278124208Sdes cflags = DP_C_LDOUBLE; 279124208Sdes ch = *format++; 280124208Sdes break; 281124208Sdes default: 282124208Sdes break; 283124208Sdes } 284124208Sdes state = DP_S_CONV; 285124208Sdes break; 286124208Sdes case DP_S_CONV: 287124208Sdes switch (ch) { 288124208Sdes case 'd': 289124208Sdes case 'i': 290124208Sdes if (cflags == DP_C_SHORT) 291157016Sdes value = va_arg (args, int); 292124208Sdes else if (cflags == DP_C_LONG) 293157016Sdes value = va_arg (args, long int); 294157016Sdes else if (cflags == DP_C_LLONG) 295157016Sdes value = va_arg (args, LLONG); 296124208Sdes else 297124208Sdes value = va_arg (args, int); 298157016Sdes fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); 299124208Sdes break; 300124208Sdes case 'o': 301124208Sdes flags |= DP_F_UNSIGNED; 302124208Sdes if (cflags == DP_C_SHORT) 303157016Sdes value = va_arg (args, unsigned int); 304124208Sdes else if (cflags == DP_C_LONG) 305157016Sdes value = (long)va_arg (args, unsigned long int); 306157016Sdes else if (cflags == DP_C_LLONG) 307157016Sdes value = (long)va_arg (args, unsigned LLONG); 308124208Sdes else 309157016Sdes value = (long)va_arg (args, unsigned int); 310157016Sdes fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); 311124208Sdes break; 312124208Sdes case 'u': 313124208Sdes flags |= DP_F_UNSIGNED; 314124208Sdes if (cflags == DP_C_SHORT) 315157016Sdes value = va_arg (args, unsigned int); 316124208Sdes else if (cflags == DP_C_LONG) 317157016Sdes value = (long)va_arg (args, unsigned long int); 318157016Sdes else if (cflags == DP_C_LLONG) 319157016Sdes value = (LLONG)va_arg (args, unsigned LLONG); 320124208Sdes else 321157016Sdes value = (long)va_arg (args, unsigned int); 322124208Sdes fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); 323124208Sdes break; 324124208Sdes case 'X': 325124208Sdes flags |= DP_F_UP; 326124208Sdes case 'x': 327124208Sdes flags |= DP_F_UNSIGNED; 328124208Sdes if (cflags == DP_C_SHORT) 329157016Sdes value = va_arg (args, unsigned int); 330124208Sdes else if (cflags == DP_C_LONG) 331157016Sdes value = (long)va_arg (args, unsigned long int); 332157016Sdes else if (cflags == DP_C_LLONG) 333157016Sdes value = (LLONG)va_arg (args, unsigned LLONG); 334124208Sdes else 335157016Sdes value = (long)va_arg (args, unsigned int); 336157016Sdes fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); 337124208Sdes break; 338124208Sdes case 'f': 339124208Sdes if (cflags == DP_C_LDOUBLE) 340157016Sdes fvalue = va_arg (args, LDOUBLE); 341124208Sdes else 342157016Sdes fvalue = va_arg (args, double); 343124208Sdes /* um, floating point? */ 344157016Sdes fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); 345124208Sdes break; 346124208Sdes case 'E': 347124208Sdes flags |= DP_F_UP; 348124208Sdes case 'e': 349124208Sdes if (cflags == DP_C_LDOUBLE) 350157016Sdes fvalue = va_arg (args, LDOUBLE); 351124208Sdes else 352157016Sdes fvalue = va_arg (args, double); 353157016Sdes fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); 354124208Sdes break; 355124208Sdes case 'G': 356124208Sdes flags |= DP_F_UP; 357124208Sdes case 'g': 358124208Sdes if (cflags == DP_C_LDOUBLE) 359157016Sdes fvalue = va_arg (args, LDOUBLE); 360124208Sdes else 361157016Sdes fvalue = va_arg (args, double); 362157016Sdes fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); 363124208Sdes break; 364124208Sdes case 'c': 365157016Sdes dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); 366124208Sdes break; 367124208Sdes case 's': 368157016Sdes strvalue = va_arg (args, char *); 369157016Sdes if (!strvalue) strvalue = "(NULL)"; 370157016Sdes if (max == -1) { 371157016Sdes max = strlen(strvalue); 372157016Sdes } 373157016Sdes if (min > 0 && max >= 0 && min > max) max = min; 374157016Sdes fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); 375124208Sdes break; 376124208Sdes case 'p': 377157016Sdes strvalue = va_arg (args, void *); 378157016Sdes fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); 379124208Sdes break; 380124208Sdes case 'n': 381124208Sdes if (cflags == DP_C_SHORT) { 382124208Sdes short int *num; 383157016Sdes num = va_arg (args, short int *); 384124208Sdes *num = currlen; 385124208Sdes } else if (cflags == DP_C_LONG) { 386124208Sdes long int *num; 387157016Sdes num = va_arg (args, long int *); 388157016Sdes *num = (long int)currlen; 389157016Sdes } else if (cflags == DP_C_LLONG) { 390157016Sdes LLONG *num; 391157016Sdes num = va_arg (args, LLONG *); 392157016Sdes *num = (LLONG)currlen; 393124208Sdes } else { 394124208Sdes int *num; 395157016Sdes num = va_arg (args, int *); 396124208Sdes *num = currlen; 39798937Sdes } 39898937Sdes break; 399124208Sdes case '%': 400157016Sdes dopr_outch (buffer, &currlen, maxlen, ch); 401124208Sdes break; 402157016Sdes case 'w': 403157016Sdes /* not supported yet, treat as next char */ 40498937Sdes ch = *format++; 40598937Sdes break; 406157016Sdes default: 407157016Sdes /* Unknown, skip */ 408157016Sdes break; 409124208Sdes } 410124208Sdes ch = *format++; 411124208Sdes state = DP_S_DEFAULT; 412124208Sdes flags = cflags = min = 0; 413124208Sdes max = -1; 414124208Sdes break; 415124208Sdes case DP_S_DONE: 416124208Sdes break; 417157016Sdes default: 418157016Sdes /* hmm? */ 419124208Sdes break; /* some picky compilers need this */ 42098937Sdes } 42198937Sdes } 422157016Sdes if (maxlen != 0) { 423157016Sdes if (currlen < maxlen - 1) 424157016Sdes buffer[currlen] = '\0'; 425157016Sdes else if (maxlen > 0) 426157016Sdes buffer[maxlen - 1] = '\0'; 427157016Sdes } 428157016Sdes 429157016Sdes return currlen; 43098937Sdes} 43198937Sdes 432157016Sdesstatic void fmtstr(char *buffer, size_t *currlen, size_t maxlen, 433157016Sdes char *value, int flags, int min, int max) 43498937Sdes{ 435157016Sdes int padlen, strln; /* amount to pad */ 436157016Sdes int cnt = 0; 437157016Sdes 438157016Sdes#ifdef DEBUG_SNPRINTF 439157016Sdes printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); 440157016Sdes#endif 441157016Sdes if (value == 0) { 44298937Sdes value = "<NULL>"; 443157016Sdes } 44498937Sdes 445146998Sdes for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ 44698937Sdes padlen = min - strln; 44798937Sdes if (padlen < 0) 44898937Sdes padlen = 0; 44998937Sdes if (flags & DP_F_MINUS) 45098937Sdes padlen = -padlen; /* Left Justify */ 451157016Sdes 45298937Sdes while ((padlen > 0) && (cnt < max)) { 453157016Sdes dopr_outch (buffer, currlen, maxlen, ' '); 45498937Sdes --padlen; 45598937Sdes ++cnt; 45698937Sdes } 45798937Sdes while (*value && (cnt < max)) { 458157016Sdes dopr_outch (buffer, currlen, maxlen, *value++); 45998937Sdes ++cnt; 46098937Sdes } 46198937Sdes while ((padlen < 0) && (cnt < max)) { 462157016Sdes dopr_outch (buffer, currlen, maxlen, ' '); 46398937Sdes ++padlen; 46498937Sdes ++cnt; 46598937Sdes } 46698937Sdes} 46798937Sdes 46898937Sdes/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ 46998937Sdes 470157016Sdesstatic void fmtint(char *buffer, size_t *currlen, size_t maxlen, 471157016Sdes long value, int base, int min, int max, int flags) 47298937Sdes{ 473157016Sdes int signvalue = 0; 47498937Sdes unsigned long uvalue; 47598937Sdes char convert[20]; 476157016Sdes int place = 0; 47798937Sdes int spadlen = 0; /* amount to space pad */ 47898937Sdes int zpadlen = 0; /* amount to zero pad */ 479157016Sdes int caps = 0; 480157016Sdes 48198937Sdes if (max < 0) 48298937Sdes max = 0; 483157016Sdes 48498937Sdes uvalue = value; 485157016Sdes 486157016Sdes if(!(flags & DP_F_UNSIGNED)) { 487157016Sdes if( value < 0 ) { 48898937Sdes signvalue = '-'; 48998937Sdes uvalue = -value; 490157016Sdes } else { 491157016Sdes if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 492157016Sdes signvalue = '+'; 493157016Sdes else if (flags & DP_F_SPACE) 494157016Sdes signvalue = ' '; 495157016Sdes } 49698937Sdes } 49798937Sdes 498157016Sdes if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 499157016Sdes 50098937Sdes do { 50198937Sdes convert[place++] = 502157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef") 503157016Sdes [uvalue % (unsigned)base ]; 50498937Sdes uvalue = (uvalue / (unsigned)base ); 505157016Sdes } while(uvalue && (place < 20)); 506157016Sdes if (place == 20) place--; 50798937Sdes convert[place] = 0; 50898937Sdes 50998937Sdes zpadlen = max - place; 51098937Sdes spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); 511157016Sdes if (zpadlen < 0) zpadlen = 0; 512157016Sdes if (spadlen < 0) spadlen = 0; 51398937Sdes if (flags & DP_F_ZERO) { 51498937Sdes zpadlen = MAX(zpadlen, spadlen); 51598937Sdes spadlen = 0; 51698937Sdes } 51798937Sdes if (flags & DP_F_MINUS) 51898937Sdes spadlen = -spadlen; /* Left Justifty */ 51998937Sdes 520157016Sdes#ifdef DEBUG_SNPRINTF 521157016Sdes printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", 522157016Sdes zpadlen, spadlen, min, max, place); 523157016Sdes#endif 524157016Sdes 52598937Sdes /* Spaces */ 52698937Sdes while (spadlen > 0) { 527157016Sdes dopr_outch (buffer, currlen, maxlen, ' '); 52898937Sdes --spadlen; 52998937Sdes } 53098937Sdes 53198937Sdes /* Sign */ 53298937Sdes if (signvalue) 533157016Sdes dopr_outch (buffer, currlen, maxlen, signvalue); 53498937Sdes 53598937Sdes /* Zeros */ 53698937Sdes if (zpadlen > 0) { 53798937Sdes while (zpadlen > 0) { 538157016Sdes dopr_outch (buffer, currlen, maxlen, '0'); 53998937Sdes --zpadlen; 54098937Sdes } 54198937Sdes } 54298937Sdes 54398937Sdes /* Digits */ 54498937Sdes while (place > 0) 545157016Sdes dopr_outch (buffer, currlen, maxlen, convert[--place]); 54698937Sdes 54798937Sdes /* Left Justified spaces */ 54898937Sdes while (spadlen < 0) { 54998937Sdes dopr_outch (buffer, currlen, maxlen, ' '); 55098937Sdes ++spadlen; 55198937Sdes } 55298937Sdes} 55398937Sdes 554157016Sdesstatic LDOUBLE abs_val(LDOUBLE value) 55598937Sdes{ 556157016Sdes LDOUBLE result = value; 55798937Sdes 558157016Sdes if (value < 0) 559157016Sdes result = -value; 560157016Sdes 561157016Sdes return result; 562157016Sdes} 563157016Sdes 564157016Sdesstatic LDOUBLE POW10(int exp) 565157016Sdes{ 566157016Sdes LDOUBLE result = 1; 567157016Sdes 56898937Sdes while (exp) { 56998937Sdes result *= 10; 57098937Sdes exp--; 57198937Sdes } 57298937Sdes 57398937Sdes return result; 57498937Sdes} 57598937Sdes 576157016Sdesstatic LLONG ROUND(LDOUBLE value) 57798937Sdes{ 578157016Sdes LLONG intpart; 57998937Sdes 580157016Sdes intpart = (LLONG)value; 581157016Sdes value = value - intpart; 582157016Sdes if (value >= 0.5) intpart++; 583157016Sdes 58498937Sdes return intpart; 58598937Sdes} 58698937Sdes 587157016Sdes/* a replacement for modf that doesn't need the math library. Should 588157016Sdes be portable, but slow */ 589157016Sdesstatic double my_modf(double x0, double *iptr) 59098937Sdes{ 591157016Sdes int i; 592157016Sdes long l; 593157016Sdes double x = x0; 594157016Sdes double f = 1.0; 595157016Sdes 596157016Sdes for (i=0;i<100;i++) { 597157016Sdes l = (long)x; 598157016Sdes if (l <= (x+1) && l >= (x-1)) break; 599157016Sdes x *= 0.1; 600157016Sdes f *= 10.0; 601157016Sdes } 602157016Sdes 603157016Sdes if (i == 100) { 604157016Sdes /* yikes! the number is beyond what we can handle. What do we do? */ 605157016Sdes (*iptr) = 0; 606157016Sdes return 0; 607157016Sdes } 608157016Sdes 609157016Sdes if (i != 0) { 610157016Sdes double i2; 611157016Sdes double ret; 612157016Sdes 613157016Sdes ret = my_modf(x0-l*f, &i2); 614157016Sdes (*iptr) = l*f + i2; 615157016Sdes return ret; 616157016Sdes } 617157016Sdes 618157016Sdes (*iptr) = l; 619157016Sdes return x - (*iptr); 620157016Sdes} 621157016Sdes 622157016Sdes 623157016Sdesstatic void fmtfp (char *buffer, size_t *currlen, size_t maxlen, 624157016Sdes LDOUBLE fvalue, int min, int max, int flags) 625157016Sdes{ 626157016Sdes int signvalue = 0; 627157016Sdes double ufvalue; 628157016Sdes char iconvert[311]; 629157016Sdes char fconvert[311]; 630157016Sdes int iplace = 0; 631157016Sdes int fplace = 0; 63298937Sdes int padlen = 0; /* amount to pad */ 633157016Sdes int zpadlen = 0; 634157016Sdes int caps = 0; 635157016Sdes int idx; 636157016Sdes double intpart; 637157016Sdes double fracpart; 638157016Sdes double temp; 63998937Sdes 64098937Sdes /* 64198937Sdes * AIX manpage says the default is 0, but Solaris says the default 64298937Sdes * is 6, and sprintf on AIX defaults to 6 64398937Sdes */ 64498937Sdes if (max < 0) 64598937Sdes max = 6; 64698937Sdes 647157016Sdes ufvalue = abs_val (fvalue); 64898937Sdes 649157016Sdes if (fvalue < 0) { 65098937Sdes signvalue = '-'; 651157016Sdes } else { 652157016Sdes if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ 653157016Sdes signvalue = '+'; 654157016Sdes } else { 655157016Sdes if (flags & DP_F_SPACE) 656157016Sdes signvalue = ' '; 657157016Sdes } 658157016Sdes } 65998937Sdes 660157016Sdes#if 0 661157016Sdes if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 662157016Sdes#endif 66398937Sdes 664157016Sdes#if 0 665157016Sdes if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ 666157016Sdes#endif 667157016Sdes 66898937Sdes /* 669157016Sdes * Sorry, we only support 16 digits past the decimal because of our 67098937Sdes * conversion method 67198937Sdes */ 672157016Sdes if (max > 16) 673157016Sdes max = 16; 67498937Sdes 67598937Sdes /* We "cheat" by converting the fractional part to integer by 67698937Sdes * multiplying by a factor of 10 67798937Sdes */ 67898937Sdes 679157016Sdes temp = ufvalue; 680157016Sdes my_modf(temp, &intpart); 681157016Sdes 682157016Sdes fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); 683157016Sdes 684157016Sdes if (fracpart >= POW10(max)) { 68598937Sdes intpart++; 686157016Sdes fracpart -= POW10(max); 68798937Sdes } 68898937Sdes 68998937Sdes /* Convert integer part */ 69098937Sdes do { 691157016Sdes temp = intpart*0.1; 692157016Sdes my_modf(temp, &intpart); 693157016Sdes idx = (int) ((temp -intpart +0.05)* 10.0); 694157016Sdes /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ 695157016Sdes /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ 69698937Sdes iconvert[iplace++] = 697157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; 698157016Sdes } while (intpart && (iplace < 311)); 699157016Sdes if (iplace == 311) iplace--; 70098937Sdes iconvert[iplace] = 0; 70198937Sdes 70298937Sdes /* Convert fractional part */ 703157016Sdes if (fracpart) 704157016Sdes { 705157016Sdes do { 706157016Sdes temp = fracpart*0.1; 707157016Sdes my_modf(temp, &fracpart); 708157016Sdes idx = (int) ((temp -fracpart +0.05)* 10.0); 709157016Sdes /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */ 710157016Sdes /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */ 711157016Sdes fconvert[fplace++] = 712157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; 713157016Sdes } while(fracpart && (fplace < 311)); 714157016Sdes if (fplace == 311) fplace--; 715157016Sdes } 71698937Sdes fconvert[fplace] = 0; 717157016Sdes 71898937Sdes /* -1 for decimal point, another -1 if we are printing a sign */ 71998937Sdes padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 72098937Sdes zpadlen = max - fplace; 721157016Sdes if (zpadlen < 0) zpadlen = 0; 72298937Sdes if (padlen < 0) 72398937Sdes padlen = 0; 72498937Sdes if (flags & DP_F_MINUS) 72598937Sdes padlen = -padlen; /* Left Justifty */ 726157016Sdes 72798937Sdes if ((flags & DP_F_ZERO) && (padlen > 0)) { 72898937Sdes if (signvalue) { 729157016Sdes dopr_outch (buffer, currlen, maxlen, signvalue); 73098937Sdes --padlen; 73198937Sdes signvalue = 0; 73298937Sdes } 73398937Sdes while (padlen > 0) { 734157016Sdes dopr_outch (buffer, currlen, maxlen, '0'); 73598937Sdes --padlen; 73698937Sdes } 73798937Sdes } 73898937Sdes while (padlen > 0) { 739157016Sdes dopr_outch (buffer, currlen, maxlen, ' '); 74098937Sdes --padlen; 74198937Sdes } 74298937Sdes if (signvalue) 743157016Sdes dopr_outch (buffer, currlen, maxlen, signvalue); 744157016Sdes 74598937Sdes while (iplace > 0) 746157016Sdes dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); 74798937Sdes 748157016Sdes#ifdef DEBUG_SNPRINTF 749157016Sdes printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); 750157016Sdes#endif 751157016Sdes 75298937Sdes /* 753157016Sdes * Decimal point. This should probably use locale to find the correct 754157016Sdes * char to print out. 75598937Sdes */ 756157016Sdes if (max > 0) { 757157016Sdes dopr_outch (buffer, currlen, maxlen, '.'); 758157016Sdes 759157016Sdes while (zpadlen > 0) { 760157016Sdes dopr_outch (buffer, currlen, maxlen, '0'); 761157016Sdes --zpadlen; 762157016Sdes } 76398937Sdes 764157016Sdes while (fplace > 0) 765157016Sdes dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); 76698937Sdes } 76798937Sdes 76898937Sdes while (padlen < 0) { 769157016Sdes dopr_outch (buffer, currlen, maxlen, ' '); 77098937Sdes ++padlen; 77198937Sdes } 77298937Sdes} 77398937Sdes 774157016Sdesstatic void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) 77598937Sdes{ 776157016Sdes if (*currlen < maxlen) { 777157016Sdes buffer[(*currlen)] = c; 778157016Sdes } 779157016Sdes (*currlen)++; 78098937Sdes} 78198937Sdes#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ 78298937Sdes 783157016Sdes#if !defined(HAVE_VSNPRINTF) 784157016Sdesint vsnprintf (char *str, size_t count, const char *fmt, va_list args) 78598937Sdes{ 786157016Sdes return dopr(str, count, fmt, args); 78798937Sdes} 788157016Sdes#endif 78998937Sdes 790157016Sdes#if !defined(HAVE_SNPRINTF) 791157016Sdesint snprintf(char *str, size_t count, SNPRINTF_CONST char *fmt, ...) 79298937Sdes{ 793157016Sdes size_t ret; 79498937Sdes va_list ap; 79598937Sdes 79698937Sdes va_start(ap, fmt); 797157016Sdes ret = vsnprintf(str, count, fmt, ap); 79898937Sdes va_end(ap); 799157016Sdes return ret; 80098937Sdes} 801157016Sdes#endif 80298937Sdes 803