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 10298937Sdes#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) 10398937Sdes 104162852Sdes#include <ctype.h> 105162852Sdes#include <stdarg.h> 106162852Sdes#include <stdlib.h> 107162852Sdes#include <string.h> 108181111Sdes#include <limits.h> 109181111Sdes#include <errno.h> 110162852Sdes 111157016Sdes#ifdef HAVE_LONG_DOUBLE 112157016Sdes# define LDOUBLE long double 113157016Sdes#else 114157016Sdes# define LDOUBLE double 115157016Sdes#endif 11698937Sdes 117157016Sdes#ifdef HAVE_LONG_LONG 118157016Sdes# define LLONG long long 119157016Sdes#else 120157016Sdes# define LLONG long 121157016Sdes#endif 12298937Sdes 12398937Sdes/* 12498937Sdes * dopr(): poor man's version of doprintf 12598937Sdes */ 12698937Sdes 12798937Sdes/* format read states */ 12898937Sdes#define DP_S_DEFAULT 0 12998937Sdes#define DP_S_FLAGS 1 13098937Sdes#define DP_S_MIN 2 13198937Sdes#define DP_S_DOT 3 13298937Sdes#define DP_S_MAX 4 13398937Sdes#define DP_S_MOD 5 13498937Sdes#define DP_S_CONV 6 13598937Sdes#define DP_S_DONE 7 13698937Sdes 13798937Sdes/* format flags - Bits */ 13898937Sdes#define DP_F_MINUS (1 << 0) 13998937Sdes#define DP_F_PLUS (1 << 1) 14098937Sdes#define DP_F_SPACE (1 << 2) 14198937Sdes#define DP_F_NUM (1 << 3) 14298937Sdes#define DP_F_ZERO (1 << 4) 14398937Sdes#define DP_F_UP (1 << 5) 14498937Sdes#define DP_F_UNSIGNED (1 << 6) 14598937Sdes 14698937Sdes/* Conversion Flags */ 147157016Sdes#define DP_C_SHORT 1 148157016Sdes#define DP_C_LONG 2 149157016Sdes#define DP_C_LDOUBLE 3 150157016Sdes#define DP_C_LLONG 4 151262566Sdes#define DP_C_SIZE 5 152262566Sdes#define DP_C_INTMAX 6 15398937Sdes 154157016Sdes#define char_to_int(p) ((p)- '0') 155157016Sdes#ifndef MAX 156157016Sdes# define MAX(p,q) (((p) >= (q)) ? (p) : (q)) 157157016Sdes#endif 15898937Sdes 159181111Sdes#define DOPR_OUTCH(buf, pos, buflen, thechar) \ 160181111Sdes do { \ 161181111Sdes if (pos + 1 >= INT_MAX) { \ 162181111Sdes errno = ERANGE; \ 163181111Sdes return -1; \ 164181111Sdes } \ 165181111Sdes if (pos < buflen) \ 166181111Sdes buf[pos] = thechar; \ 167181111Sdes (pos)++; \ 168181111Sdes } while (0) 16998937Sdes 170181111Sdesstatic int dopr(char *buffer, size_t maxlen, const char *format, 171181111Sdes va_list args_in); 172181111Sdesstatic int fmtstr(char *buffer, size_t *currlen, size_t maxlen, 173181111Sdes char *value, int flags, int min, int max); 174181111Sdesstatic int fmtint(char *buffer, size_t *currlen, size_t maxlen, 175262566Sdes intmax_t value, int base, int min, int max, int flags); 176181111Sdesstatic int fmtfp(char *buffer, size_t *currlen, size_t maxlen, 177181111Sdes LDOUBLE fvalue, int min, int max, int flags); 178181111Sdes 179181111Sdesstatic int 180181111Sdesdopr(char *buffer, size_t maxlen, const char *format, va_list args_in) 18198937Sdes{ 182157016Sdes char ch; 183262566Sdes intmax_t value; 184157016Sdes LDOUBLE fvalue; 185157016Sdes char *strvalue; 186157016Sdes int min; 187157016Sdes int max; 188157016Sdes int state; 189157016Sdes int flags; 190157016Sdes int cflags; 191157016Sdes size_t currlen; 192157016Sdes va_list args; 193157016Sdes 194157016Sdes VA_COPY(args, args_in); 195157016Sdes 196157016Sdes state = DP_S_DEFAULT; 197157016Sdes currlen = flags = cflags = min = 0; 198157016Sdes max = -1; 19998937Sdes ch = *format++; 200157016Sdes 20198937Sdes while (state != DP_S_DONE) { 202157016Sdes if (ch == '\0') 20398937Sdes state = DP_S_DONE; 20498937Sdes 20598937Sdes switch(state) { 206124208Sdes case DP_S_DEFAULT: 207124208Sdes if (ch == '%') 208124208Sdes state = DP_S_FLAGS; 209181111Sdes else 210181111Sdes DOPR_OUTCH(buffer, currlen, maxlen, ch); 211124208Sdes ch = *format++; 212124208Sdes break; 213124208Sdes case DP_S_FLAGS: 214124208Sdes switch (ch) { 215124208Sdes case '-': 216124208Sdes flags |= DP_F_MINUS; 21798937Sdes ch = *format++; 21898937Sdes break; 219124208Sdes case '+': 220124208Sdes flags |= DP_F_PLUS; 221124208Sdes ch = *format++; 22298937Sdes break; 223124208Sdes case ' ': 224124208Sdes flags |= DP_F_SPACE; 225124208Sdes ch = *format++; 22698937Sdes break; 227124208Sdes case '#': 228124208Sdes flags |= DP_F_NUM; 229124208Sdes ch = *format++; 23098937Sdes break; 231124208Sdes case '0': 232124208Sdes flags |= DP_F_ZERO; 233124208Sdes ch = *format++; 234124208Sdes break; 235124208Sdes default: 236124208Sdes state = DP_S_MIN; 237124208Sdes break; 238124208Sdes } 239124208Sdes break; 240124208Sdes case DP_S_MIN: 241124208Sdes if (isdigit((unsigned char)ch)) { 242157016Sdes min = 10*min + char_to_int (ch); 243124208Sdes ch = *format++; 244124208Sdes } else if (ch == '*') { 245124208Sdes min = va_arg (args, int); 246124208Sdes ch = *format++; 247124208Sdes state = DP_S_DOT; 248157016Sdes } else { 249124208Sdes state = DP_S_DOT; 250157016Sdes } 251124208Sdes break; 252124208Sdes case DP_S_DOT: 253124208Sdes if (ch == '.') { 254124208Sdes state = DP_S_MAX; 255124208Sdes ch = *format++; 256157016Sdes } else { 257124208Sdes state = DP_S_MOD; 258157016Sdes } 259124208Sdes break; 260124208Sdes case DP_S_MAX: 261124208Sdes if (isdigit((unsigned char)ch)) { 262124208Sdes if (max < 0) 263124208Sdes max = 0; 264157016Sdes max = 10*max + char_to_int (ch); 265124208Sdes ch = *format++; 266124208Sdes } else if (ch == '*') { 267124208Sdes max = va_arg (args, int); 268124208Sdes ch = *format++; 269124208Sdes state = DP_S_MOD; 270157016Sdes } else { 271124208Sdes state = DP_S_MOD; 272157016Sdes } 273124208Sdes break; 274124208Sdes case DP_S_MOD: 275124208Sdes switch (ch) { 276124208Sdes case 'h': 277124208Sdes cflags = DP_C_SHORT; 278124208Sdes ch = *format++; 279124208Sdes break; 280262566Sdes case 'j': 281262566Sdes cflags = DP_C_INTMAX; 282262566Sdes ch = *format++; 283262566Sdes break; 284124208Sdes case 'l': 285124208Sdes cflags = DP_C_LONG; 286124208Sdes ch = *format++; 287157016Sdes if (ch == 'l') { /* It's a long long */ 288157016Sdes cflags = DP_C_LLONG; 28998937Sdes ch = *format++; 290124208Sdes } 29198937Sdes break; 292124208Sdes case 'L': 293124208Sdes cflags = DP_C_LDOUBLE; 294124208Sdes ch = *format++; 295124208Sdes break; 296262566Sdes case 'z': 297262566Sdes cflags = DP_C_SIZE; 298262566Sdes ch = *format++; 299262566Sdes break; 300124208Sdes default: 301124208Sdes break; 302124208Sdes } 303124208Sdes state = DP_S_CONV; 304124208Sdes break; 305124208Sdes case DP_S_CONV: 306124208Sdes switch (ch) { 307124208Sdes case 'd': 308124208Sdes case 'i': 309124208Sdes if (cflags == DP_C_SHORT) 310157016Sdes value = va_arg (args, int); 311124208Sdes else if (cflags == DP_C_LONG) 312157016Sdes value = va_arg (args, long int); 313157016Sdes else if (cflags == DP_C_LLONG) 314157016Sdes value = va_arg (args, LLONG); 315262566Sdes else if (cflags == DP_C_SIZE) 316262566Sdes value = va_arg (args, ssize_t); 317262566Sdes else if (cflags == DP_C_INTMAX) 318262566Sdes value = va_arg (args, intmax_t); 319124208Sdes else 320124208Sdes value = va_arg (args, int); 321181111Sdes if (fmtint(buffer, &currlen, maxlen, 322181111Sdes value, 10, min, max, flags) == -1) 323181111Sdes return -1; 324124208Sdes break; 325124208Sdes case 'o': 326124208Sdes flags |= DP_F_UNSIGNED; 327124208Sdes if (cflags == DP_C_SHORT) 328157016Sdes value = va_arg (args, unsigned int); 329124208Sdes else if (cflags == DP_C_LONG) 330157016Sdes value = (long)va_arg (args, unsigned long int); 331157016Sdes else if (cflags == DP_C_LLONG) 332157016Sdes value = (long)va_arg (args, unsigned LLONG); 333262566Sdes else if (cflags == DP_C_SIZE) 334262566Sdes value = va_arg (args, size_t); 335262566Sdes#ifdef notyet 336262566Sdes else if (cflags == DP_C_INTMAX) 337262566Sdes value = va_arg (args, uintmax_t); 338262566Sdes#endif 339124208Sdes else 340157016Sdes value = (long)va_arg (args, unsigned int); 341181111Sdes if (fmtint(buffer, &currlen, maxlen, value, 342181111Sdes 8, min, max, flags) == -1) 343181111Sdes return -1; 344124208Sdes break; 345124208Sdes case 'u': 346124208Sdes flags |= DP_F_UNSIGNED; 347124208Sdes if (cflags == DP_C_SHORT) 348157016Sdes value = va_arg (args, unsigned int); 349124208Sdes else if (cflags == DP_C_LONG) 350157016Sdes value = (long)va_arg (args, unsigned long int); 351157016Sdes else if (cflags == DP_C_LLONG) 352157016Sdes value = (LLONG)va_arg (args, unsigned LLONG); 353262566Sdes else if (cflags == DP_C_SIZE) 354262566Sdes value = va_arg (args, size_t); 355262566Sdes#ifdef notyet 356262566Sdes else if (cflags == DP_C_INTMAX) 357262566Sdes value = va_arg (args, uintmax_t); 358262566Sdes#endif 359124208Sdes else 360157016Sdes value = (long)va_arg (args, unsigned int); 361181111Sdes if (fmtint(buffer, &currlen, maxlen, value, 362181111Sdes 10, min, max, flags) == -1) 363181111Sdes return -1; 364124208Sdes break; 365124208Sdes case 'X': 366124208Sdes flags |= DP_F_UP; 367124208Sdes case 'x': 368124208Sdes flags |= DP_F_UNSIGNED; 369124208Sdes if (cflags == DP_C_SHORT) 370157016Sdes value = va_arg (args, unsigned int); 371124208Sdes else if (cflags == DP_C_LONG) 372157016Sdes value = (long)va_arg (args, unsigned long int); 373157016Sdes else if (cflags == DP_C_LLONG) 374157016Sdes value = (LLONG)va_arg (args, unsigned LLONG); 375262566Sdes else if (cflags == DP_C_SIZE) 376262566Sdes value = va_arg (args, size_t); 377262566Sdes#ifdef notyet 378262566Sdes else if (cflags == DP_C_INTMAX) 379262566Sdes value = va_arg (args, uintmax_t); 380262566Sdes#endif 381124208Sdes else 382157016Sdes value = (long)va_arg (args, unsigned int); 383181111Sdes if (fmtint(buffer, &currlen, maxlen, value, 384181111Sdes 16, min, max, flags) == -1) 385181111Sdes return -1; 386124208Sdes break; 387124208Sdes case 'f': 388124208Sdes if (cflags == DP_C_LDOUBLE) 389157016Sdes fvalue = va_arg (args, LDOUBLE); 390124208Sdes else 391157016Sdes fvalue = va_arg (args, double); 392181111Sdes if (fmtfp(buffer, &currlen, maxlen, fvalue, 393181111Sdes min, max, flags) == -1) 394181111Sdes return -1; 395124208Sdes break; 396124208Sdes case 'E': 397124208Sdes flags |= DP_F_UP; 398124208Sdes case 'e': 399124208Sdes if (cflags == DP_C_LDOUBLE) 400157016Sdes fvalue = va_arg (args, LDOUBLE); 401124208Sdes else 402157016Sdes fvalue = va_arg (args, double); 403181111Sdes if (fmtfp(buffer, &currlen, maxlen, fvalue, 404181111Sdes min, max, flags) == -1) 405181111Sdes return -1; 406124208Sdes break; 407124208Sdes case 'G': 408124208Sdes flags |= DP_F_UP; 409124208Sdes case 'g': 410124208Sdes if (cflags == DP_C_LDOUBLE) 411157016Sdes fvalue = va_arg (args, LDOUBLE); 412124208Sdes else 413157016Sdes fvalue = va_arg (args, double); 414181111Sdes if (fmtfp(buffer, &currlen, maxlen, fvalue, 415181111Sdes min, max, flags) == -1) 416181111Sdes return -1; 417124208Sdes break; 418124208Sdes case 'c': 419181111Sdes DOPR_OUTCH(buffer, currlen, maxlen, 420181111Sdes va_arg (args, int)); 421124208Sdes break; 422124208Sdes case 's': 423157016Sdes strvalue = va_arg (args, char *); 424157016Sdes if (!strvalue) strvalue = "(NULL)"; 425157016Sdes if (max == -1) { 426157016Sdes max = strlen(strvalue); 427157016Sdes } 428157016Sdes if (min > 0 && max >= 0 && min > max) max = min; 429181111Sdes if (fmtstr(buffer, &currlen, maxlen, 430181111Sdes strvalue, flags, min, max) == -1) 431181111Sdes return -1; 432124208Sdes break; 433124208Sdes case 'p': 434157016Sdes strvalue = va_arg (args, void *); 435181111Sdes if (fmtint(buffer, &currlen, maxlen, 436181111Sdes (long) strvalue, 16, min, max, flags) == -1) 437181111Sdes return -1; 438124208Sdes break; 439262566Sdes#if we_dont_want_this_in_openssh 440124208Sdes case 'n': 441124208Sdes if (cflags == DP_C_SHORT) { 442124208Sdes short int *num; 443157016Sdes num = va_arg (args, short int *); 444124208Sdes *num = currlen; 445124208Sdes } else if (cflags == DP_C_LONG) { 446124208Sdes long int *num; 447157016Sdes num = va_arg (args, long int *); 448157016Sdes *num = (long int)currlen; 449157016Sdes } else if (cflags == DP_C_LLONG) { 450157016Sdes LLONG *num; 451157016Sdes num = va_arg (args, LLONG *); 452157016Sdes *num = (LLONG)currlen; 453262566Sdes } else if (cflags == DP_C_SIZE) { 454262566Sdes ssize_t *num; 455262566Sdes num = va_arg (args, ssize_t *); 456262566Sdes *num = (ssize_t)currlen; 457262566Sdes } else if (cflags == DP_C_INTMAX) { 458262566Sdes intmax_t *num; 459262566Sdes num = va_arg (args, intmax_t *); 460262566Sdes *num = (intmax_t)currlen; 461124208Sdes } else { 462124208Sdes int *num; 463157016Sdes num = va_arg (args, int *); 464124208Sdes *num = currlen; 46598937Sdes } 46698937Sdes break; 467262566Sdes#endif 468124208Sdes case '%': 469181111Sdes DOPR_OUTCH(buffer, currlen, maxlen, ch); 470124208Sdes break; 471157016Sdes case 'w': 472157016Sdes /* not supported yet, treat as next char */ 47398937Sdes ch = *format++; 47498937Sdes break; 475157016Sdes default: 476157016Sdes /* Unknown, skip */ 477157016Sdes break; 478124208Sdes } 479124208Sdes ch = *format++; 480124208Sdes state = DP_S_DEFAULT; 481124208Sdes flags = cflags = min = 0; 482124208Sdes max = -1; 483124208Sdes break; 484124208Sdes case DP_S_DONE: 485124208Sdes break; 486157016Sdes default: 487157016Sdes /* hmm? */ 488124208Sdes break; /* some picky compilers need this */ 48998937Sdes } 49098937Sdes } 491157016Sdes if (maxlen != 0) { 492157016Sdes if (currlen < maxlen - 1) 493157016Sdes buffer[currlen] = '\0'; 494157016Sdes else if (maxlen > 0) 495157016Sdes buffer[maxlen - 1] = '\0'; 496157016Sdes } 497157016Sdes 498181111Sdes return currlen < INT_MAX ? (int)currlen : -1; 49998937Sdes} 50098937Sdes 501181111Sdesstatic int 502181111Sdesfmtstr(char *buffer, size_t *currlen, size_t maxlen, 503181111Sdes char *value, int flags, int min, int max) 50498937Sdes{ 505157016Sdes int padlen, strln; /* amount to pad */ 506157016Sdes int cnt = 0; 507157016Sdes 508157016Sdes#ifdef DEBUG_SNPRINTF 509157016Sdes printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); 510157016Sdes#endif 511157016Sdes if (value == 0) { 51298937Sdes value = "<NULL>"; 513157016Sdes } 51498937Sdes 515146998Sdes for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */ 51698937Sdes padlen = min - strln; 51798937Sdes if (padlen < 0) 51898937Sdes padlen = 0; 51998937Sdes if (flags & DP_F_MINUS) 52098937Sdes padlen = -padlen; /* Left Justify */ 521157016Sdes 52298937Sdes while ((padlen > 0) && (cnt < max)) { 523181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 52498937Sdes --padlen; 52598937Sdes ++cnt; 52698937Sdes } 52798937Sdes while (*value && (cnt < max)) { 528181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, *value); 529295367Sdes value++; 53098937Sdes ++cnt; 53198937Sdes } 53298937Sdes while ((padlen < 0) && (cnt < max)) { 533181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 53498937Sdes ++padlen; 53598937Sdes ++cnt; 53698937Sdes } 537181111Sdes return 0; 53898937Sdes} 53998937Sdes 54098937Sdes/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ 54198937Sdes 542181111Sdesstatic int 543181111Sdesfmtint(char *buffer, size_t *currlen, size_t maxlen, 544295367Sdes intmax_t value, int base, int min, int max, int flags) 54598937Sdes{ 546157016Sdes int signvalue = 0; 547162852Sdes unsigned LLONG uvalue; 54898937Sdes char convert[20]; 549157016Sdes int place = 0; 55098937Sdes int spadlen = 0; /* amount to space pad */ 55198937Sdes int zpadlen = 0; /* amount to zero pad */ 552157016Sdes int caps = 0; 553157016Sdes 55498937Sdes if (max < 0) 55598937Sdes max = 0; 556157016Sdes 55798937Sdes uvalue = value; 558157016Sdes 559157016Sdes if(!(flags & DP_F_UNSIGNED)) { 560157016Sdes if( value < 0 ) { 56198937Sdes signvalue = '-'; 56298937Sdes uvalue = -value; 563157016Sdes } else { 564157016Sdes if (flags & DP_F_PLUS) /* Do a sign (+/i) */ 565157016Sdes signvalue = '+'; 566157016Sdes else if (flags & DP_F_SPACE) 567157016Sdes signvalue = ' '; 568157016Sdes } 56998937Sdes } 57098937Sdes 571157016Sdes if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 572157016Sdes 57398937Sdes do { 57498937Sdes convert[place++] = 575157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef") 576157016Sdes [uvalue % (unsigned)base ]; 57798937Sdes uvalue = (uvalue / (unsigned)base ); 578157016Sdes } while(uvalue && (place < 20)); 579157016Sdes if (place == 20) place--; 58098937Sdes convert[place] = 0; 58198937Sdes 58298937Sdes zpadlen = max - place; 58398937Sdes spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); 584157016Sdes if (zpadlen < 0) zpadlen = 0; 585157016Sdes if (spadlen < 0) spadlen = 0; 58698937Sdes if (flags & DP_F_ZERO) { 58798937Sdes zpadlen = MAX(zpadlen, spadlen); 58898937Sdes spadlen = 0; 58998937Sdes } 59098937Sdes if (flags & DP_F_MINUS) 59198937Sdes spadlen = -spadlen; /* Left Justifty */ 59298937Sdes 593157016Sdes#ifdef DEBUG_SNPRINTF 594157016Sdes printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", 595157016Sdes zpadlen, spadlen, min, max, place); 596157016Sdes#endif 597157016Sdes 59898937Sdes /* Spaces */ 59998937Sdes while (spadlen > 0) { 600181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 60198937Sdes --spadlen; 60298937Sdes } 60398937Sdes 60498937Sdes /* Sign */ 60598937Sdes if (signvalue) 606181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); 60798937Sdes 60898937Sdes /* Zeros */ 60998937Sdes if (zpadlen > 0) { 61098937Sdes while (zpadlen > 0) { 611181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '0'); 61298937Sdes --zpadlen; 61398937Sdes } 61498937Sdes } 61598937Sdes 61698937Sdes /* Digits */ 617181111Sdes while (place > 0) { 618181111Sdes --place; 619181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, convert[place]); 620181111Sdes } 62198937Sdes 62298937Sdes /* Left Justified spaces */ 62398937Sdes while (spadlen < 0) { 624181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 62598937Sdes ++spadlen; 62698937Sdes } 627181111Sdes return 0; 62898937Sdes} 62998937Sdes 630157016Sdesstatic LDOUBLE abs_val(LDOUBLE value) 63198937Sdes{ 632157016Sdes LDOUBLE result = value; 63398937Sdes 634157016Sdes if (value < 0) 635157016Sdes result = -value; 636157016Sdes 637157016Sdes return result; 638157016Sdes} 639157016Sdes 640181111Sdesstatic LDOUBLE POW10(int val) 641157016Sdes{ 642157016Sdes LDOUBLE result = 1; 643157016Sdes 644181111Sdes while (val) { 64598937Sdes result *= 10; 646181111Sdes val--; 64798937Sdes } 64898937Sdes 64998937Sdes return result; 65098937Sdes} 65198937Sdes 652157016Sdesstatic LLONG ROUND(LDOUBLE value) 65398937Sdes{ 654157016Sdes LLONG intpart; 65598937Sdes 656157016Sdes intpart = (LLONG)value; 657157016Sdes value = value - intpart; 658157016Sdes if (value >= 0.5) intpart++; 659157016Sdes 66098937Sdes return intpart; 66198937Sdes} 66298937Sdes 663157016Sdes/* a replacement for modf that doesn't need the math library. Should 664157016Sdes be portable, but slow */ 665157016Sdesstatic double my_modf(double x0, double *iptr) 66698937Sdes{ 667157016Sdes int i; 668157016Sdes long l; 669157016Sdes double x = x0; 670157016Sdes double f = 1.0; 671157016Sdes 672157016Sdes for (i=0;i<100;i++) { 673157016Sdes l = (long)x; 674157016Sdes if (l <= (x+1) && l >= (x-1)) break; 675157016Sdes x *= 0.1; 676157016Sdes f *= 10.0; 677157016Sdes } 678157016Sdes 679157016Sdes if (i == 100) { 680181111Sdes /* 681181111Sdes * yikes! the number is beyond what we can handle. 682181111Sdes * What do we do? 683181111Sdes */ 684157016Sdes (*iptr) = 0; 685157016Sdes return 0; 686157016Sdes } 687157016Sdes 688157016Sdes if (i != 0) { 689157016Sdes double i2; 690157016Sdes double ret; 691157016Sdes 692157016Sdes ret = my_modf(x0-l*f, &i2); 693157016Sdes (*iptr) = l*f + i2; 694157016Sdes return ret; 695157016Sdes } 696157016Sdes 697157016Sdes (*iptr) = l; 698157016Sdes return x - (*iptr); 699157016Sdes} 700157016Sdes 701157016Sdes 702181111Sdesstatic int 703181111Sdesfmtfp (char *buffer, size_t *currlen, size_t maxlen, 704181111Sdes LDOUBLE fvalue, int min, int max, int flags) 705157016Sdes{ 706157016Sdes int signvalue = 0; 707157016Sdes double ufvalue; 708157016Sdes char iconvert[311]; 709157016Sdes char fconvert[311]; 710157016Sdes int iplace = 0; 711157016Sdes int fplace = 0; 71298937Sdes int padlen = 0; /* amount to pad */ 713157016Sdes int zpadlen = 0; 714157016Sdes int caps = 0; 715157016Sdes int idx; 716157016Sdes double intpart; 717157016Sdes double fracpart; 718157016Sdes double temp; 71998937Sdes 72098937Sdes /* 72198937Sdes * AIX manpage says the default is 0, but Solaris says the default 72298937Sdes * is 6, and sprintf on AIX defaults to 6 72398937Sdes */ 72498937Sdes if (max < 0) 72598937Sdes max = 6; 72698937Sdes 727157016Sdes ufvalue = abs_val (fvalue); 72898937Sdes 729157016Sdes if (fvalue < 0) { 73098937Sdes signvalue = '-'; 731157016Sdes } else { 732157016Sdes if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ 733157016Sdes signvalue = '+'; 734157016Sdes } else { 735157016Sdes if (flags & DP_F_SPACE) 736157016Sdes signvalue = ' '; 737157016Sdes } 738157016Sdes } 73998937Sdes 740157016Sdes#if 0 741157016Sdes if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ 742157016Sdes#endif 74398937Sdes 744157016Sdes#if 0 745157016Sdes if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ 746157016Sdes#endif 747157016Sdes 74898937Sdes /* 749157016Sdes * Sorry, we only support 16 digits past the decimal because of our 75098937Sdes * conversion method 75198937Sdes */ 752157016Sdes if (max > 16) 753157016Sdes max = 16; 75498937Sdes 75598937Sdes /* We "cheat" by converting the fractional part to integer by 75698937Sdes * multiplying by a factor of 10 75798937Sdes */ 75898937Sdes 759157016Sdes temp = ufvalue; 760157016Sdes my_modf(temp, &intpart); 761157016Sdes 762157016Sdes fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); 763157016Sdes 764157016Sdes if (fracpart >= POW10(max)) { 76598937Sdes intpart++; 766157016Sdes fracpart -= POW10(max); 76798937Sdes } 76898937Sdes 76998937Sdes /* Convert integer part */ 77098937Sdes do { 771157016Sdes temp = intpart*0.1; 772157016Sdes my_modf(temp, &intpart); 773157016Sdes idx = (int) ((temp -intpart +0.05)* 10.0); 774157016Sdes /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ 775157016Sdes /* printf ("%llf, %f, %x\n", temp, intpart, idx); */ 77698937Sdes iconvert[iplace++] = 777157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; 778157016Sdes } while (intpart && (iplace < 311)); 779157016Sdes if (iplace == 311) iplace--; 78098937Sdes iconvert[iplace] = 0; 78198937Sdes 78298937Sdes /* Convert fractional part */ 783157016Sdes if (fracpart) 784157016Sdes { 785157016Sdes do { 786157016Sdes temp = fracpart*0.1; 787157016Sdes my_modf(temp, &fracpart); 788157016Sdes idx = (int) ((temp -fracpart +0.05)* 10.0); 789157016Sdes /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */ 790157016Sdes /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */ 791157016Sdes fconvert[fplace++] = 792157016Sdes (caps? "0123456789ABCDEF":"0123456789abcdef")[idx]; 793157016Sdes } while(fracpart && (fplace < 311)); 794157016Sdes if (fplace == 311) fplace--; 795157016Sdes } 79698937Sdes fconvert[fplace] = 0; 797157016Sdes 79898937Sdes /* -1 for decimal point, another -1 if we are printing a sign */ 79998937Sdes padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 80098937Sdes zpadlen = max - fplace; 801157016Sdes if (zpadlen < 0) zpadlen = 0; 80298937Sdes if (padlen < 0) 80398937Sdes padlen = 0; 80498937Sdes if (flags & DP_F_MINUS) 80598937Sdes padlen = -padlen; /* Left Justifty */ 806157016Sdes 80798937Sdes if ((flags & DP_F_ZERO) && (padlen > 0)) { 80898937Sdes if (signvalue) { 809181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); 81098937Sdes --padlen; 81198937Sdes signvalue = 0; 81298937Sdes } 81398937Sdes while (padlen > 0) { 814181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '0'); 81598937Sdes --padlen; 81698937Sdes } 81798937Sdes } 81898937Sdes while (padlen > 0) { 819181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 82098937Sdes --padlen; 82198937Sdes } 82298937Sdes if (signvalue) 823181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, signvalue); 824157016Sdes 825181111Sdes while (iplace > 0) { 826181111Sdes --iplace; 827181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, iconvert[iplace]); 828181111Sdes } 82998937Sdes 830157016Sdes#ifdef DEBUG_SNPRINTF 831157016Sdes printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); 832157016Sdes#endif 833157016Sdes 83498937Sdes /* 835157016Sdes * Decimal point. This should probably use locale to find the correct 836157016Sdes * char to print out. 83798937Sdes */ 838157016Sdes if (max > 0) { 839181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '.'); 840157016Sdes 841157016Sdes while (zpadlen > 0) { 842181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, '0'); 843157016Sdes --zpadlen; 844157016Sdes } 84598937Sdes 846181111Sdes while (fplace > 0) { 847181111Sdes --fplace; 848181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, fconvert[fplace]); 849181111Sdes } 85098937Sdes } 85198937Sdes 85298937Sdes while (padlen < 0) { 853181111Sdes DOPR_OUTCH(buffer, *currlen, maxlen, ' '); 85498937Sdes ++padlen; 85598937Sdes } 856181111Sdes return 0; 85798937Sdes} 85898937Sdes#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */ 85998937Sdes 860157016Sdes#if !defined(HAVE_VSNPRINTF) 861181111Sdesint 862181111Sdesvsnprintf (char *str, size_t count, const char *fmt, va_list args) 86398937Sdes{ 864157016Sdes return dopr(str, count, fmt, args); 86598937Sdes} 866157016Sdes#endif 86798937Sdes 868157016Sdes#if !defined(HAVE_SNPRINTF) 869181111Sdesint 870181111Sdessnprintf(char *str, size_t count, SNPRINTF_CONST char *fmt, ...) 87198937Sdes{ 872157016Sdes size_t ret; 87398937Sdes va_list ap; 87498937Sdes 87598937Sdes va_start(ap, fmt); 876157016Sdes ret = vsnprintf(str, count, fmt, ap); 87798937Sdes va_end(ap); 878157016Sdes return ret; 87998937Sdes} 880157016Sdes#endif 881