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. 88181111Sdes * 89181111Sdes * Damien Miller (djm@mindrot.org) Jan 2007 90181111Sdes * Fix integer overflows in return value. 91181111Sdes * Make formatting quite a bit faster by inlining dopr_outch() 92181111Sdes * 9398937Sdes **************************************************************/ 9498937Sdes 9598937Sdes#include "includes.h" 9698937Sdes 9798937Sdes#if defined(BROKEN_SNPRINTF) /* For those with broken snprintf() */ 9898937Sdes# undef HAVE_SNPRINTF 9998937Sdes# undef HAVE_VSNPRINTF 10098937Sdes#endif 10198937Sdes 102157016Sdes#ifndef VA_COPY 103157016Sdes# ifdef HAVE_VA_COPY 104157016Sdes# define VA_COPY(dest, src) va_copy(dest, src) 105157016Sdes# else 106157016Sdes# ifdef HAVE___VA_COPY 107157016Sdes# define VA_COPY(dest, src) __va_copy(dest, src) 108157016Sdes# else 109157016Sdes# define VA_COPY(dest, src) (dest) = (src) 110157016Sdes# endif 111157016Sdes# endif 112157016Sdes#endif 113157016Sdes 11498937Sdes#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) 11598937Sdes 116162852Sdes#include <ctype.h> 117162852Sdes#include <stdarg.h> 118162852Sdes#include <stdlib.h> 119162852Sdes#include <string.h> 120181111Sdes#include <limits.h> 121181111Sdes#include <errno.h> 122162852Sdes 123157016Sdes#ifdef HAVE_LONG_DOUBLE 124157016Sdes# define LDOUBLE long double 125157016Sdes#else 126157016Sdes# define LDOUBLE double 127157016Sdes#endif 12898937Sdes 129157016Sdes#ifdef HAVE_LONG_LONG 130157016Sdes# define LLONG long long 131157016Sdes#else 132157016Sdes# define LLONG long 133157016Sdes#endif 13498937Sdes 13598937Sdes/* 13698937Sdes * dopr(): poor man's version of doprintf 13798937Sdes */ 13898937Sdes 13998937Sdes/* format read states */ 14098937Sdes#define DP_S_DEFAULT 0 14198937Sdes#define DP_S_FLAGS 1 14298937Sdes#define DP_S_MIN 2 14398937Sdes#define DP_S_DOT 3 14498937Sdes#define DP_S_MAX 4 14598937Sdes#define DP_S_MOD 5 14698937Sdes#define DP_S_CONV 6 14798937Sdes#define DP_S_DONE 7 14898937Sdes 14998937Sdes/* format flags - Bits */ 15098937Sdes#define DP_F_MINUS (1 << 0) 15198937Sdes#define DP_F_PLUS (1 << 1) 15298937Sdes#define DP_F_SPACE (1 << 2) 15398937Sdes#define DP_F_NUM (1 << 3) 15498937Sdes#define DP_F_ZERO (1 << 4) 15598937Sdes#define DP_F_UP (1 << 5) 15698937Sdes#define DP_F_UNSIGNED (1 << 6) 15798937Sdes 15898937Sdes/* Conversion Flags */ 159157016Sdes#define DP_C_SHORT 1 160157016Sdes#define DP_C_LONG 2 161157016Sdes#define DP_C_LDOUBLE 3 162157016Sdes#define DP_C_LLONG 4 163262566Sdes#define DP_C_SIZE 5 164262566Sdes#define DP_C_INTMAX 6 16598937Sdes 166157016Sdes#define char_to_int(p) ((p)- '0') 167157016Sdes#ifndef MAX 168157016Sdes# define MAX(p,q) (((p) >= (q)) ? (p) : (q)) 169157016Sdes#endif 17098937Sdes 171181111Sdes#define DOPR_OUTCH(buf, pos, buflen, thechar) \ 172181111Sdes do { \ 173181111Sdes if (pos + 1 >= INT_MAX) { \ 174181111Sdes errno = ERANGE; \ 175181111Sdes return -1; \ 176181111Sdes } \ 177181111Sdes if (pos < buflen) \ 178181111Sdes buf[pos] = thechar; \ 179181111Sdes (pos)++; \ 180181111Sdes } while (0) 18198937Sdes 182181111Sdesstatic int dopr(char *buffer, size_t maxlen, const char *format, 183181111Sdes va_list args_in); 184181111Sdesstatic int fmtstr(char *buffer, size_t *currlen, size_t maxlen, 185181111Sdes char *value, int flags, int min, int max); 186181111Sdesstatic int fmtint(char *buffer, size_t *currlen, size_t maxlen, 187262566Sdes intmax_t value, int base, int min, int max, int flags); 188181111Sdesstatic int fmtfp(char *buffer, size_t *currlen, size_t maxlen, 189181111Sdes LDOUBLE fvalue, int min, int max, int flags); 190181111Sdes 191181111Sdesstatic int 192181111Sdesdopr(char *buffer, size_t maxlen, const char *format, va_list args_in) 19398937Sdes{ 194157016Sdes char ch; 195262566Sdes intmax_t value; 196157016Sdes LDOUBLE fvalue; 197157016Sdes char *strvalue; 198157016Sdes int min; 199157016Sdes int max; 200157016Sdes int state; 201157016Sdes int flags; 202157016Sdes int cflags; 203157016Sdes size_t currlen; 204157016Sdes va_list args; 205157016Sdes 206157016Sdes VA_COPY(args, args_in); 207157016Sdes 208157016Sdes state = DP_S_DEFAULT; 209157016Sdes currlen = flags = cflags = min = 0; 210157016Sdes max = -1; 21198937Sdes ch = *format++; 212157016Sdes 21398937Sdes while (state != DP_S_DONE) { 214157016Sdes if (ch == '\0') 21598937Sdes state = DP_S_DONE; 21698937Sdes 21798937Sdes switch(state) { 218124208Sdes case DP_S_DEFAULT: 219124208Sdes if (ch == '%') 220124208Sdes state = DP_S_FLAGS; 221181111Sdes else 222181111Sdes DOPR_OUTCH(buffer, currlen, maxlen, ch); 223124208Sdes ch = *format++; 224124208Sdes break; 225124208Sdes case DP_S_FLAGS: 226124208Sdes switch (ch) { 227124208Sdes case '-': 228124208Sdes flags |= DP_F_MINUS; 22998937Sdes ch = *format++; 23098937Sdes break; 231124208Sdes case '+': 232124208Sdes flags |= DP_F_PLUS; 233124208Sdes ch = *format++; 23498937Sdes break; 235124208Sdes case ' ': 236124208Sdes flags |= DP_F_SPACE; 237124208Sdes ch = *format++; 23898937Sdes break; 239124208Sdes case '#': 240124208Sdes flags |= DP_F_NUM; 241124208Sdes ch = *format++; 24298937Sdes break; 243124208Sdes case '0': 244124208Sdes flags |= DP_F_ZERO; 245124208Sdes ch = *format++; 246124208Sdes break; 247124208Sdes default: 248124208Sdes state = DP_S_MIN; 249124208Sdes break; 250124208Sdes } 251124208Sdes break; 252124208Sdes case DP_S_MIN: 253124208Sdes if (isdigit((unsigned char)ch)) { 254157016Sdes min = 10*min + char_to_int (ch); 255124208Sdes ch = *format++; 256124208Sdes } else if (ch == '*') { 257124208Sdes min = va_arg (args, int); 258124208Sdes ch = *format++; 259124208Sdes state = DP_S_DOT; 260157016Sdes } else { 261124208Sdes state = DP_S_DOT; 262157016Sdes } 263124208Sdes break; 264124208Sdes case DP_S_DOT: 265124208Sdes if (ch == '.') { 266124208Sdes state = DP_S_MAX; 267124208Sdes ch = *format++; 268157016Sdes } else { 269124208Sdes state = DP_S_MOD; 270157016Sdes } 271124208Sdes break; 272124208Sdes case DP_S_MAX: 273124208Sdes if (isdigit((unsigned char)ch)) { 274124208Sdes if (max < 0) 275124208Sdes max = 0; 276157016Sdes max = 10*max + char_to_int (ch); 277124208Sdes ch = *format++; 278124208Sdes } else if (ch == '*') { 279124208Sdes max = va_arg (args, int); 280124208Sdes ch = *format++; 281124208Sdes state = DP_S_MOD; 282157016Sdes } else { 283124208Sdes state = DP_S_MOD; 284157016Sdes } 285124208Sdes break; 286124208Sdes case DP_S_MOD: 287124208Sdes switch (ch) { 288124208Sdes case 'h': 289124208Sdes cflags = DP_C_SHORT; 290124208Sdes ch = *format++; 291124208Sdes break; 292262566Sdes case 'j': 293262566Sdes cflags = DP_C_INTMAX; 294262566Sdes ch = *format++; 295262566Sdes break; 296124208Sdes case 'l': 297124208Sdes cflags = DP_C_LONG; 298124208Sdes ch = *format++; 299157016Sdes if (ch == 'l') { /* It's a long long */ 300157016Sdes cflags = DP_C_LLONG; 30198937Sdes ch = *format++; 302124208Sdes } 30398937Sdes break; 304124208Sdes case 'L': 305124208Sdes cflags = DP_C_LDOUBLE; 306124208Sdes ch = *format++; 307124208Sdes break; 308262566Sdes case 'z': 309262566Sdes cflags = DP_C_SIZE; 310262566Sdes ch = *format++; 311262566Sdes break; 312124208Sdes default: 313124208Sdes break; 314124208Sdes } 315124208Sdes state = DP_S_CONV; 316124208Sdes break; 317124208Sdes case DP_S_CONV: 318124208Sdes switch (ch) { 319124208Sdes case 'd': 320124208Sdes case 'i': 321124208Sdes if (cflags == DP_C_SHORT) 322157016Sdes value = va_arg (args, int); 323124208Sdes else if (cflags == DP_C_LONG) 324157016Sdes value = va_arg (args, long int); 325157016Sdes else if (cflags == DP_C_LLONG) 326157016Sdes value = va_arg (args, LLONG); 327262566Sdes else if (cflags == DP_C_SIZE) 328262566Sdes value = va_arg (args, ssize_t); 329262566Sdes else if (cflags == DP_C_INTMAX) 330262566Sdes value = va_arg (args, intmax_t); 331124208Sdes else 332124208Sdes value = va_arg (args, int); 333181111Sdes if (fmtint(buffer, &currlen, maxlen, 334181111Sdes value, 10, min, max, flags) == -1) 335181111Sdes return -1; 336124208Sdes break; 337124208Sdes case 'o': 338124208Sdes flags |= DP_F_UNSIGNED; 339124208Sdes if (cflags == DP_C_SHORT) 340157016Sdes value = va_arg (args, unsigned int); 341124208Sdes else if (cflags == DP_C_LONG) 342157016Sdes value = (long)va_arg (args, unsigned long int); 343157016Sdes else if (cflags == DP_C_LLONG) 344157016Sdes value = (long)va_arg (args, unsigned LLONG); 345262566Sdes else if (cflags == DP_C_SIZE) 346262566Sdes value = va_arg (args, size_t); 347262566Sdes#ifdef notyet 348262566Sdes else if (cflags == DP_C_INTMAX) 349262566Sdes value = va_arg (args, uintmax_t); 350262566Sdes#endif 351124208Sdes else 352157016Sdes value = (long)va_arg (args, unsigned int); 353181111Sdes if (fmtint(buffer, &currlen, maxlen, value, 354181111Sdes 8, min, max, flags) == -1) 355181111Sdes return -1; 356124208Sdes break; 357124208Sdes case 'u': 358124208Sdes flags |= DP_F_UNSIGNED; 359124208Sdes if (cflags == DP_C_SHORT) 360157016Sdes value = va_arg (args, unsigned int); 361124208Sdes else if (cflags == DP_C_LONG) 362157016Sdes value = (long)va_arg (args, unsigned long int); 363157016Sdes else if (cflags == DP_C_LLONG) 364157016Sdes value = (LLONG)va_arg (args, unsigned LLONG); 365262566Sdes else if (cflags == DP_C_SIZE) 366262566Sdes value = va_arg (args, size_t); 367262566Sdes#ifdef notyet 368262566Sdes else if (cflags == DP_C_INTMAX) 369262566Sdes value = va_arg (args, uintmax_t); 370262566Sdes#endif 371124208Sdes else 372157016Sdes value = (long)va_arg (args, unsigned int); 373181111Sdes if (fmtint(buffer, &currlen, maxlen, value, 374181111Sdes 10, min, max, flags) == -1) 375181111Sdes return -1; 376124208Sdes break; 377124208Sdes case 'X': 378124208Sdes flags |= DP_F_UP; 379124208Sdes case 'x': 380124208Sdes flags |= DP_F_UNSIGNED; 381124208Sdes if (cflags == DP_C_SHORT) 382157016Sdes value = va_arg (args, unsigned int); 383124208Sdes else if (cflags == DP_C_LONG) 384157016Sdes value = (long)va_arg (args, unsigned long int); 385157016Sdes else if (cflags == DP_C_LLONG) 386157016Sdes value = (LLONG)va_arg (args, unsigned LLONG); 387262566Sdes else if (cflags == DP_C_SIZE) 388262566Sdes value = va_arg (args, size_t); 389262566Sdes#ifdef notyet 390262566Sdes else if (cflags == DP_C_INTMAX) 391262566Sdes value = va_arg (args, uintmax_t); 392262566Sdes#endif 393124208Sdes else 394157016Sdes value = (long)va_arg (args, unsigned int); 395181111Sdes if (fmtint(buffer, &currlen, maxlen, value, 396181111Sdes 16, min, max, flags) == -1) 397181111Sdes return -1; 398124208Sdes break; 399124208Sdes case 'f': 400124208Sdes if (cflags == DP_C_LDOUBLE) 401157016Sdes fvalue = va_arg (args, LDOUBLE); 402124208Sdes else 403157016Sdes fvalue = va_arg (args, double); 404181111Sdes if (fmtfp(buffer, &currlen, maxlen, fvalue, 405181111Sdes min, max, flags) == -1) 406181111Sdes return -1; 407124208Sdes break; 408124208Sdes case 'E': 409124208Sdes flags |= DP_F_UP; 410124208Sdes case 'e': 411124208Sdes if (cflags == DP_C_LDOUBLE) 412157016Sdes fvalue = va_arg (args, LDOUBLE); 413124208Sdes else 414157016Sdes fvalue = va_arg (args, double); 415181111Sdes if (fmtfp(buffer, &currlen, maxlen, fvalue, 416181111Sdes min, max, flags) == -1) 417181111Sdes return -1; 418124208Sdes break; 419124208Sdes case 'G': 420124208Sdes flags |= DP_F_UP; 421124208Sdes case 'g': 422124208Sdes if (cflags == DP_C_LDOUBLE) 423157016Sdes fvalue = va_arg (args, LDOUBLE); 424124208Sdes else 425157016Sdes fvalue = va_arg (args, double); 426181111Sdes if (fmtfp(buffer, &currlen, maxlen, fvalue, 427181111Sdes min, max, flags) == -1) 428181111Sdes return -1; 429124208Sdes break; 430124208Sdes case 'c': 431181111Sdes DOPR_OUTCH(buffer, currlen, maxlen, 432181111Sdes va_arg (args, int)); 433124208Sdes break; 434124208Sdes case 's': 435157016Sdes strvalue = va_arg (args, char *); 436157016Sdes if (!strvalue) strvalue = "(NULL)"; 437157016Sdes if (max == -1) { 438157016Sdes max = strlen(strvalue); 439157016Sdes } 440157016Sdes if (min > 0 && max >= 0 && min > max) max = min; 441181111Sdes if (fmtstr(buffer, &currlen, maxlen, 442181111Sdes strvalue, flags, min, max) == -1) 443181111Sdes return -1; 444124208Sdes break; 445124208Sdes case 'p': 446157016Sdes strvalue = va_arg (args, void *); 447181111Sdes if (fmtint(buffer, &currlen, maxlen, 448181111Sdes (long) strvalue, 16, min, max, flags) == -1) 449181111Sdes return -1; 450124208Sdes break; 451262566Sdes#if we_dont_want_this_in_openssh 452124208Sdes case 'n': 453124208Sdes if (cflags == DP_C_SHORT) { 454124208Sdes short int *num; 455157016Sdes num = va_arg (args, short int *); 456124208Sdes *num = currlen; 457124208Sdes } else if (cflags == DP_C_LONG) { 458124208Sdes long int *num; 459157016Sdes num = va_arg (args, long int *); 460157016Sdes *num = (long int)currlen; 461157016Sdes } else if (cflags == DP_C_LLONG) { 462157016Sdes LLONG *num; 463157016Sdes num = va_arg (args, LLONG *); 464157016Sdes *num = (LLONG)currlen; 465262566Sdes } else if (cflags == DP_C_SIZE) { 466262566Sdes ssize_t *num; 467262566Sdes num = va_arg (args, ssize_t *); 468262566Sdes *num = (ssize_t)currlen; 469262566Sdes } else if (cflags == DP_C_INTMAX) { 470262566Sdes intmax_t *num; 471262566Sdes num = va_arg (args, intmax_t *); 472262566Sdes *num = (intmax_t)currlen; 473124208Sdes } else { 474124208Sdes int *num; 475157016Sdes num = va_arg (args, int *); 476124208Sdes *num = currlen; 47798937Sdes } 47898937Sdes break; 479262566Sdes#endif 480124208Sdes case '%': 481181111Sdes DOPR_OUTCH(buffer, currlen, maxlen, ch); 482124208Sdes break; 483157016Sdes case 'w': 484157016Sdes /* not supported yet, treat as next char */ 48598937Sdes ch = *format++; 48698937Sdes break; 487157016Sdes default: 488157016Sdes /* Unknown, skip */ 489157016Sdes break; 490124208Sdes } 491124208Sdes ch = *format++; 492124208Sdes state = DP_S_DEFAULT; 493124208Sdes flags = cflags = min = 0; 494124208Sdes max = -1; 495124208Sdes break; 496124208Sdes case DP_S_DONE: 497124208Sdes break; 498157016Sdes default: 499157016Sdes /* hmm? */ 500124208Sdes break; /* some picky compilers need this */ 50198937Sdes } 50298937Sdes } 503157016Sdes if (maxlen != 0) { 504157016Sdes if (currlen < maxlen - 1) 505157016Sdes buffer[currlen] = '\0'; 506157016Sdes else if (maxlen > 0) 507157016Sdes buffer[maxlen - 1] = '\0'; 508157016Sdes } 509157016Sdes 510181111Sdes return currlen < INT_MAX ? (int)currlen : -1; 51198937Sdes} 51298937Sdes 513181111Sdesstatic int 514181111Sdesfmtstr(char *buffer, size_t *currlen, size_t maxlen, 515181111Sdes char *value, int flags, int min, int max) 51698937Sdes{ 517157016Sdes int padlen, strln; /* amount to pad */ 518157016Sdes int cnt = 0; 519157016Sdes 520157016Sdes#ifdef DEBUG_SNPRINTF 521157016Sdes printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); 522157016Sdes#endif 523157016Sdes if (value == 0) { 52498937Sdes value = "<NULL>"; 525157016Sdes } 52698937Sdes 527146998Sdes for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ 52898937Sdes padlen = min - strln; 52998937Sdes if (padlen < 0) 53098937Sdes padlen = 0; 53198937Sdes if (flags & DP_F_MINUS) 53298937Sdes padlen = -padlen; /* Left Justify */ 533157016Sdes 53498937Sdes while ((padlen > 0) && (cnt < max)) { 535181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 53698937Sdes --padlen; 53798937Sdes ++cnt; 53898937Sdes } 53998937Sdes while (*value && (cnt < max)) { 540181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, *value); 541181111Sdes *value++; 54298937Sdes ++cnt; 54398937Sdes } 54498937Sdes while ((padlen < 0) && (cnt < max)) { 545181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 54698937Sdes ++padlen; 54798937Sdes ++cnt; 54898937Sdes } 549181111Sdes return 0; 55098937Sdes} 55198937Sdes 55298937Sdes/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ 55398937Sdes 554181111Sdesstatic int 555181111Sdesfmtint(char *buffer, size_t *currlen, size_t maxlen, 556162852Sdes LLONG value, int base, int min, int max, int flags) 55798937Sdes{ 558157016Sdes int signvalue = 0; 559162852Sdes unsigned LLONG uvalue; 56098937Sdes char convert[20]; 561157016Sdes int place = 0; 56298937Sdes int spadlen = 0; /* amount to space pad */ 56398937Sdes int zpadlen = 0; /* amount to zero pad */ 564157016Sdes int caps = 0; 565157016Sdes 56698937Sdes if (max < 0) 56798937Sdes max = 0; 568157016Sdes 56998937Sdes uvalue = value; 570157016Sdes 571157016Sdes if(!(flags & DP_F_UNSIGNED)) { 572157016Sdes if( value < 0 ) { 57398937Sdes signvalue = '-'; 57498937Sdes uvalue = -value; 575157016Sdes } else { 576157016Sdes if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 577157016Sdes signvalue = '+'; 578157016Sdes else if (flags & DP_F_SPACE) 579157016Sdes signvalue = ' '; 580157016Sdes } 58198937Sdes } 58298937Sdes 583157016Sdes if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 584157016Sdes 58598937Sdes do { 58698937Sdes convert[place++] = 587157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef") 588157016Sdes [uvalue % (unsigned)base ]; 58998937Sdes uvalue = (uvalue / (unsigned)base ); 590157016Sdes } while(uvalue && (place < 20)); 591157016Sdes if (place == 20) place--; 59298937Sdes convert[place] = 0; 59398937Sdes 59498937Sdes zpadlen = max - place; 59598937Sdes spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); 596157016Sdes if (zpadlen < 0) zpadlen = 0; 597157016Sdes if (spadlen < 0) spadlen = 0; 59898937Sdes if (flags & DP_F_ZERO) { 59998937Sdes zpadlen = MAX(zpadlen, spadlen); 60098937Sdes spadlen = 0; 60198937Sdes } 60298937Sdes if (flags & DP_F_MINUS) 60398937Sdes spadlen = -spadlen; /* Left Justifty */ 60498937Sdes 605157016Sdes#ifdef DEBUG_SNPRINTF 606157016Sdes printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", 607157016Sdes zpadlen, spadlen, min, max, place); 608157016Sdes#endif 609157016Sdes 61098937Sdes /* Spaces */ 61198937Sdes while (spadlen > 0) { 612181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 61398937Sdes --spadlen; 61498937Sdes } 61598937Sdes 61698937Sdes /* Sign */ 61798937Sdes if (signvalue) 618181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); 61998937Sdes 62098937Sdes /* Zeros */ 62198937Sdes if (zpadlen > 0) { 62298937Sdes while (zpadlen > 0) { 623181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '0'); 62498937Sdes --zpadlen; 62598937Sdes } 62698937Sdes } 62798937Sdes 62898937Sdes /* Digits */ 629181111Sdes while (place > 0) { 630181111Sdes --place; 631181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, convert[place]); 632181111Sdes } 63398937Sdes 63498937Sdes /* Left Justified spaces */ 63598937Sdes while (spadlen < 0) { 636181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 63798937Sdes ++spadlen; 63898937Sdes } 639181111Sdes return 0; 64098937Sdes} 64198937Sdes 642157016Sdesstatic LDOUBLE abs_val(LDOUBLE value) 64398937Sdes{ 644157016Sdes LDOUBLE result = value; 64598937Sdes 646157016Sdes if (value < 0) 647157016Sdes result = -value; 648157016Sdes 649157016Sdes return result; 650157016Sdes} 651157016Sdes 652181111Sdesstatic LDOUBLE POW10(int val) 653157016Sdes{ 654157016Sdes LDOUBLE result = 1; 655157016Sdes 656181111Sdes while (val) { 65798937Sdes result *= 10; 658181111Sdes val--; 65998937Sdes } 66098937Sdes 66198937Sdes return result; 66298937Sdes} 66398937Sdes 664157016Sdesstatic LLONG ROUND(LDOUBLE value) 66598937Sdes{ 666157016Sdes LLONG intpart; 66798937Sdes 668157016Sdes intpart = (LLONG)value; 669157016Sdes value = value - intpart; 670157016Sdes if (value >= 0.5) intpart++; 671157016Sdes 67298937Sdes return intpart; 67398937Sdes} 67498937Sdes 675157016Sdes/* a replacement for modf that doesn't need the math library. Should 676157016Sdes be portable, but slow */ 677157016Sdesstatic double my_modf(double x0, double *iptr) 67898937Sdes{ 679157016Sdes int i; 680157016Sdes long l; 681157016Sdes double x = x0; 682157016Sdes double f = 1.0; 683157016Sdes 684157016Sdes for (i=0;i<100;i++) { 685157016Sdes l = (long)x; 686157016Sdes if (l <= (x+1) && l >= (x-1)) break; 687157016Sdes x *= 0.1; 688157016Sdes f *= 10.0; 689157016Sdes } 690157016Sdes 691157016Sdes if (i == 100) { 692181111Sdes /* 693181111Sdes * yikes! the number is beyond what we can handle. 694181111Sdes * What do we do? 695181111Sdes */ 696157016Sdes (*iptr) = 0; 697157016Sdes return 0; 698157016Sdes } 699157016Sdes 700157016Sdes if (i != 0) { 701157016Sdes double i2; 702157016Sdes double ret; 703157016Sdes 704157016Sdes ret = my_modf(x0-l*f, &i2); 705157016Sdes (*iptr) = l*f + i2; 706157016Sdes return ret; 707157016Sdes } 708157016Sdes 709157016Sdes (*iptr) = l; 710157016Sdes return x - (*iptr); 711157016Sdes} 712157016Sdes 713157016Sdes 714181111Sdesstatic int 715181111Sdesfmtfp (char *buffer, size_t *currlen, size_t maxlen, 716181111Sdes LDOUBLE fvalue, int min, int max, int flags) 717157016Sdes{ 718157016Sdes int signvalue = 0; 719157016Sdes double ufvalue; 720157016Sdes char iconvert[311]; 721157016Sdes char fconvert[311]; 722157016Sdes int iplace = 0; 723157016Sdes int fplace = 0; 72498937Sdes int padlen = 0; /* amount to pad */ 725157016Sdes int zpadlen = 0; 726157016Sdes int caps = 0; 727157016Sdes int idx; 728157016Sdes double intpart; 729157016Sdes double fracpart; 730157016Sdes double temp; 73198937Sdes 73298937Sdes /* 73398937Sdes * AIX manpage says the default is 0, but Solaris says the default 73498937Sdes * is 6, and sprintf on AIX defaults to 6 73598937Sdes */ 73698937Sdes if (max < 0) 73798937Sdes max = 6; 73898937Sdes 739157016Sdes ufvalue = abs_val (fvalue); 74098937Sdes 741157016Sdes if (fvalue < 0) { 74298937Sdes signvalue = '-'; 743157016Sdes } else { 744157016Sdes if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ 745157016Sdes signvalue = '+'; 746157016Sdes } else { 747157016Sdes if (flags & DP_F_SPACE) 748157016Sdes signvalue = ' '; 749157016Sdes } 750157016Sdes } 75198937Sdes 752157016Sdes#if 0 753157016Sdes if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 754157016Sdes#endif 75598937Sdes 756157016Sdes#if 0 757157016Sdes if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ 758157016Sdes#endif 759157016Sdes 76098937Sdes /* 761157016Sdes * Sorry, we only support 16 digits past the decimal because of our 76298937Sdes * conversion method 76398937Sdes */ 764157016Sdes if (max > 16) 765157016Sdes max = 16; 76698937Sdes 76798937Sdes /* We "cheat" by converting the fractional part to integer by 76898937Sdes * multiplying by a factor of 10 76998937Sdes */ 77098937Sdes 771157016Sdes temp = ufvalue; 772157016Sdes my_modf(temp, &intpart); 773157016Sdes 774157016Sdes fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); 775157016Sdes 776157016Sdes if (fracpart >= POW10(max)) { 77798937Sdes intpart++; 778157016Sdes fracpart -= POW10(max); 77998937Sdes } 78098937Sdes 78198937Sdes /* Convert integer part */ 78298937Sdes do { 783157016Sdes temp = intpart*0.1; 784157016Sdes my_modf(temp, &intpart); 785157016Sdes idx = (int) ((temp -intpart +0.05)* 10.0); 786157016Sdes /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ 787157016Sdes /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ 78898937Sdes iconvert[iplace++] = 789157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; 790157016Sdes } while (intpart && (iplace < 311)); 791157016Sdes if (iplace == 311) iplace--; 79298937Sdes iconvert[iplace] = 0; 79398937Sdes 79498937Sdes /* Convert fractional part */ 795157016Sdes if (fracpart) 796157016Sdes { 797157016Sdes do { 798157016Sdes temp = fracpart*0.1; 799157016Sdes my_modf(temp, &fracpart); 800157016Sdes idx = (int) ((temp -fracpart +0.05)* 10.0); 801157016Sdes /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */ 802157016Sdes /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */ 803157016Sdes fconvert[fplace++] = 804157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; 805157016Sdes } while(fracpart && (fplace < 311)); 806157016Sdes if (fplace == 311) fplace--; 807157016Sdes } 80898937Sdes fconvert[fplace] = 0; 809157016Sdes 81098937Sdes /* -1 for decimal point, another -1 if we are printing a sign */ 81198937Sdes padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 81298937Sdes zpadlen = max - fplace; 813157016Sdes if (zpadlen < 0) zpadlen = 0; 81498937Sdes if (padlen < 0) 81598937Sdes padlen = 0; 81698937Sdes if (flags & DP_F_MINUS) 81798937Sdes padlen = -padlen; /* Left Justifty */ 818157016Sdes 81998937Sdes if ((flags & DP_F_ZERO) && (padlen > 0)) { 82098937Sdes if (signvalue) { 821181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); 82298937Sdes --padlen; 82398937Sdes signvalue = 0; 82498937Sdes } 82598937Sdes while (padlen > 0) { 826181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '0'); 82798937Sdes --padlen; 82898937Sdes } 82998937Sdes } 83098937Sdes while (padlen > 0) { 831181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 83298937Sdes --padlen; 83398937Sdes } 83498937Sdes if (signvalue) 835181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); 836157016Sdes 837181111Sdes while (iplace > 0) { 838181111Sdes --iplace; 839181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, iconvert[iplace]); 840181111Sdes } 84198937Sdes 842157016Sdes#ifdef DEBUG_SNPRINTF 843157016Sdes printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); 844157016Sdes#endif 845157016Sdes 84698937Sdes /* 847157016Sdes * Decimal point. This should probably use locale to find the correct 848157016Sdes * char to print out. 84998937Sdes */ 850157016Sdes if (max > 0) { 851181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '.'); 852157016Sdes 853157016Sdes while (zpadlen > 0) { 854181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '0'); 855157016Sdes --zpadlen; 856157016Sdes } 85798937Sdes 858181111Sdes while (fplace > 0) { 859181111Sdes --fplace; 860181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, fconvert[fplace]); 861181111Sdes } 86298937Sdes } 86398937Sdes 86498937Sdes while (padlen < 0) { 865181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 86698937Sdes ++padlen; 86798937Sdes } 868181111Sdes return 0; 86998937Sdes} 87098937Sdes#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ 87198937Sdes 872157016Sdes#if !defined(HAVE_VSNPRINTF) 873181111Sdesint 874181111Sdesvsnprintf (char *str, size_t count, const char *fmt, va_list args) 87598937Sdes{ 876157016Sdes return dopr(str, count, fmt, args); 87798937Sdes} 878157016Sdes#endif 87998937Sdes 880157016Sdes#if !defined(HAVE_SNPRINTF) 881181111Sdesint 882181111Sdessnprintf(char *str, size_t count, SNPRINTF_CONST char *fmt, ...) 88398937Sdes{ 884157016Sdes size_t ret; 88598937Sdes va_list ap; 88698937Sdes 88798937Sdes va_start(ap, fmt); 888157016Sdes ret = vsnprintf(str, count, fmt, ap); 88998937Sdes va_end(ap); 890157016Sdes return ret; 89198937Sdes} 892157016Sdes#endif 893