Annotation of embedaddon/rsync/lib/snprintf.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  * NOTE: If you change this file, please merge it into rsync, samba, etc.
                      3:  */
                      4: 
                      5: /*
                      6:  * Copyright Patrick Powell 1995
                      7:  * This code is based on code written by Patrick Powell (papowell@astart.com)
                      8:  * It may be used for any purpose as long as this notice remains intact
                      9:  * on all source code distributions
                     10:  */
                     11: 
                     12: /**************************************************************
                     13:  * Original:
                     14:  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
                     15:  * A bombproof version of doprnt (dopr) included.
                     16:  * Sigh.  This sort of thing is always nasty do deal with.  Note that
                     17:  * the version here does not include floating point...
                     18:  *
                     19:  * snprintf() is used instead of sprintf() as it does limit checks
                     20:  * for string length.  This covers a nasty loophole.
                     21:  *
                     22:  * The other functions are there to prevent NULL pointers from
                     23:  * causing nast effects.
                     24:  *
                     25:  * More Recently:
                     26:  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
                     27:  *  This was ugly.  It is still ugly.  I opted out of floating point
                     28:  *  numbers, but the formatter understands just about everything
                     29:  *  from the normal C string format, at least as far as I can tell from
                     30:  *  the Solaris 2.5 printf(3S) man page.
                     31:  *
                     32:  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
                     33:  *    Ok, added some minimal floating point support, which means this
                     34:  *    probably requires libm on most operating systems.  Don't yet
                     35:  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
                     36:  *    was pretty badly broken, it just wasn't being exercised in ways
                     37:  *    which showed it, so that's been fixed.  Also, formated the code
                     38:  *    to mutt conventions, and removed dead code left over from the
                     39:  *    original.  Also, there is now a builtin-test, just compile with:
1.1.1.2 ! misho      40:  *           gcc -I.. -DTEST_SNPRINTF -o snprintf snprintf.c -lm
1.1       misho      41:  *    and run snprintf for results.
                     42:  * 
                     43:  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
                     44:  *    The PGP code was using unsigned hexadecimal formats. 
                     45:  *    Unfortunately, unsigned formats simply didn't work.
                     46:  *
                     47:  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
                     48:  *    The original code assumed that both snprintf() and vsnprintf() were
                     49:  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
                     50:  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
                     51:  *
                     52:  *  Andrew Tridgell (tridge@samba.org) Oct 1998
                     53:  *    fixed handling of %.0f
                     54:  *    added test for HAVE_LONG_DOUBLE
                     55:  *
                     56:  * tridge@samba.org, idra@samba.org, April 2001
                     57:  *    got rid of fcvt code (twas buggy and made testing harder)
                     58:  *    added C99 semantics
                     59:  *
                     60:  * date: 2002/12/19 19:56:31;  author: herb;  state: Exp;  lines: +2 -0
                     61:  * actually print args for %g and %e
                     62:  * 
                     63:  * date: 2002/06/03 13:37:52;  author: jmcd;  state: Exp;  lines: +8 -0
                     64:  * Since includes.h isn't included here, VA_COPY has to be defined here.  I don't
                     65:  * see any include file that is guaranteed to be here, so I'm defining it
                     66:  * locally.  Fixes AIX and Solaris builds.
                     67:  * 
                     68:  * date: 2002/06/03 03:07:24;  author: tridge;  state: Exp;  lines: +5 -13
                     69:  * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
                     70:  * functions
                     71:  * 
                     72:  * date: 2002/05/17 14:51:22;  author: jmcd;  state: Exp;  lines: +21 -4
                     73:  * Fix usage of va_list passed as an arg.  Use __va_copy before using it
                     74:  * when it exists.
                     75:  * 
                     76:  * date: 2002/04/16 22:38:04;  author: idra;  state: Exp;  lines: +20 -14
                     77:  * Fix incorrect zpadlen handling in fmtfp.
                     78:  * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
                     79:  * few mods to make it easier to compile the tests.
                     80:  * addedd the "Ollie" test to the floating point ones.
                     81:  *
                     82:  * Martin Pool (mbp@samba.org) April 2003
                     83:  *    Remove NO_CONFIG_H so that the test case can be built within a source
                     84:  *    tree with less trouble.
                     85:  *    Remove unnecessary SAFE_FREE() definition.
                     86:  *
                     87:  * Martin Pool (mbp@samba.org) May 2003
                     88:  *    Put in a prototype for dummy_snprintf() to quiet compiler warnings.
                     89:  *
                     90:  *    Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
                     91:  *    if the C library has some snprintf functions already.
1.1.1.2 ! misho      92:  *
        !            93:  * Darren Tucker (dtucker@zip.com.au) 2005
        !            94:  *    Fix bug allowing read overruns of the source string with "%.*s"
        !            95:  *    Usually harmless unless the read runs outside the process' allocation
        !            96:  *    (eg if your malloc does guard pages) in which case it will segfault.
        !            97:  *    From OpenSSH.  Also added test for same.
        !            98:  *
        !            99:  * Simo Sorce (idra@samba.org) Jan 2006
        !           100:  * 
        !           101:  *    Add support for position independent parameters 
        !           102:  *    fix fmtstr now it conforms to sprintf wrt min.max
        !           103:  *
1.1       misho     104:  **************************************************************/
                    105: 
                    106: #include "config.h"
                    107: 
                    108: #ifdef TEST_SNPRINTF /* need math library headers for testing */
                    109: 
                    110: /* In test mode, we pretend that this system doesn't have any snprintf
                    111:  * functions, regardless of what config.h says. */
                    112: #  undef HAVE_SNPRINTF
                    113: #  undef HAVE_VSNPRINTF
                    114: #  undef HAVE_C99_VSNPRINTF
                    115: #  undef HAVE_ASPRINTF
                    116: #  undef HAVE_VASPRINTF
                    117: #  include <math.h>
                    118: #endif /* TEST_SNPRINTF */
                    119: 
                    120: #ifdef HAVE_STRING_H
                    121: #include <string.h>
                    122: #endif
                    123: 
                    124: #ifdef HAVE_STRINGS_H
                    125: #include <strings.h>
                    126: #endif
                    127: #ifdef HAVE_CTYPE_H
                    128: #include <ctype.h>
                    129: #endif
                    130: #include <sys/types.h>
                    131: #include <stdarg.h>
                    132: #ifdef HAVE_STDLIB_H
                    133: #include <stdlib.h>
                    134: #endif
                    135: 
                    136: #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
                    137: /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
                    138: #include <stdio.h>
                    139:  /* make the compiler happy with an empty file */
                    140:  void dummy_snprintf(void);
                    141:  void dummy_snprintf(void) {} 
                    142: #endif /* HAVE_SNPRINTF, etc */
                    143: 
1.1.1.2 ! misho     144: #ifdef STDC_HEADERS
        !           145: #include <stddef.h>
        !           146: #endif
        !           147: 
1.1       misho     148: #ifdef HAVE_LONG_DOUBLE
                    149: #define LDOUBLE long double
                    150: #else
                    151: #define LDOUBLE double
                    152: #endif
                    153: 
1.1.1.2 ! misho     154: #if !defined HAVE_LONG_LONG && SIZEOF_LONG_LONG
        !           155: #define HAVE_LONG_LONG 1
        !           156: #endif
        !           157: #ifdef HAVE_LONG_LONG
1.1       misho     158: #define LLONG long long
                    159: #else
                    160: #define LLONG long
                    161: #endif
                    162: 
                    163: #ifndef VA_COPY
                    164: #if defined HAVE_VA_COPY || defined va_copy
                    165: #define VA_COPY(dest, src) va_copy(dest, src)
                    166: #else
                    167: #ifdef HAVE___VA_COPY
                    168: #define VA_COPY(dest, src) __va_copy(dest, src)
                    169: #else
                    170: #define VA_COPY(dest, src) (dest) = (src)
                    171: #endif
                    172: #endif
1.1.1.2 ! misho     173: #endif
        !           174: 
        !           175: /* yes this really must be a ||. Don't muck with this (tridge) */
        !           176: #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
1.1       misho     177: 
                    178: /*
                    179:  * dopr(): poor man's version of doprintf
                    180:  */
                    181: 
                    182: /* format read states */
                    183: #define DP_S_DEFAULT 0
                    184: #define DP_S_FLAGS   1
                    185: #define DP_S_MIN     2
                    186: #define DP_S_DOT     3
                    187: #define DP_S_MAX     4
                    188: #define DP_S_MOD     5
                    189: #define DP_S_CONV    6
                    190: #define DP_S_DONE    7
                    191: 
                    192: /* format flags - Bits */
                    193: #define DP_F_MINUS     (1 << 0)
                    194: #define DP_F_PLUS      (1 << 1)
                    195: #define DP_F_SPACE     (1 << 2)
                    196: #define DP_F_NUM       (1 << 3)
                    197: #define DP_F_ZERO      (1 << 4)
                    198: #define DP_F_UP        (1 << 5)
                    199: #define DP_F_UNSIGNED  (1 << 6)
                    200: 
                    201: /* Conversion Flags */
1.1.1.2 ! misho     202: #define DP_C_CHAR    1
        !           203: #define DP_C_SHORT   2
        !           204: #define DP_C_LONG    3
        !           205: #define DP_C_LDOUBLE 4
        !           206: #define DP_C_LLONG   5
        !           207: #define DP_C_SIZET   6
        !           208: 
        !           209: /* Chunk types */
        !           210: #define CNK_FMT_STR 0
        !           211: #define CNK_INT     1
        !           212: #define CNK_OCTAL   2
        !           213: #define CNK_UINT    3
        !           214: #define CNK_HEX     4
        !           215: #define CNK_FLOAT   5
        !           216: #define CNK_CHAR    6
        !           217: #define CNK_STRING  7
        !           218: #define CNK_PTR     8
        !           219: #define CNK_NUM     9
        !           220: #define CNK_PRCNT   10
1.1       misho     221: 
                    222: #define char_to_int(p) ((p)- '0')
                    223: #ifndef MAX
                    224: #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
                    225: #endif
                    226: 
1.1.1.2 ! misho     227: struct pr_chunk {
        !           228:        int type; /* chunk type */
        !           229:        int num; /* parameter number */
        !           230:        int min; 
        !           231:        int max;
        !           232:        int flags;
        !           233:        int cflags;
        !           234:        int start;
        !           235:        int len;
        !           236:        LLONG value;
        !           237:        LDOUBLE fvalue;
        !           238:        char *strvalue;
        !           239:        void *pnum;
        !           240:        struct pr_chunk *min_star;
        !           241:        struct pr_chunk *max_star;
        !           242:        struct pr_chunk *next;
        !           243: };
        !           244: 
        !           245: struct pr_chunk_x {
        !           246:        struct pr_chunk **chunks;
        !           247:        int num;
        !           248: };
1.1       misho     249: 
1.1.1.2 ! misho     250: static int dopr(char *buffer, size_t maxlen, const char *format, 
1.1       misho     251:                   va_list args_in);
                    252: static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
                    253:                    char *value, int flags, int min, int max);
                    254: static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
1.1.1.2 ! misho     255:                    LLONG value, int base, int min, int max, int flags);
1.1       misho     256: static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
                    257:                   LDOUBLE fvalue, int min, int max, int flags);
                    258: static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
1.1.1.2 ! misho     259: static struct pr_chunk *new_chunk(void);
        !           260: static int add_cnk_list_entry(struct pr_chunk_x **list,
        !           261:                                int max_num, struct pr_chunk *chunk);
1.1       misho     262: 
1.1.1.2 ! misho     263: static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
1.1       misho     264: {
                    265:        char ch;
                    266:        int state;
1.1.1.2 ! misho     267:        int pflag;
        !           268:        int pnum;
        !           269:        int pfirst;
1.1       misho     270:        size_t currlen;
                    271:        va_list args;
1.1.1.2 ! misho     272:        const char *base;
        !           273:        struct pr_chunk *chunks = NULL;
        !           274:        struct pr_chunk *cnk = NULL;
        !           275:        struct pr_chunk_x *clist = NULL;
        !           276:        int max_pos;
        !           277:        int ret = -1;
1.1       misho     278: 
                    279:        VA_COPY(args, args_in);
1.1.1.2 ! misho     280: 
1.1       misho     281:        state = DP_S_DEFAULT;
1.1.1.2 ! misho     282:        pfirst = 1;
        !           283:        pflag = 0;
        !           284:        pnum = 0;
        !           285: 
        !           286:        max_pos = 0;
        !           287:        base = format;
1.1       misho     288:        ch = *format++;
                    289:        
1.1.1.2 ! misho     290:        /* retrieve the string structure as chunks */
1.1       misho     291:        while (state != DP_S_DONE) {
                    292:                if (ch == '\0') 
                    293:                        state = DP_S_DONE;
                    294: 
                    295:                switch(state) {
                    296:                case DP_S_DEFAULT:
1.1.1.2 ! misho     297:                        
        !           298:                        if (cnk) {
        !           299:                                cnk->next = new_chunk();
        !           300:                                cnk = cnk->next;
        !           301:                        } else {
        !           302:                                cnk = new_chunk();
        !           303:                        }
        !           304:                        if (!cnk) goto done;
        !           305:                        if (!chunks) chunks = cnk;
        !           306:                        
        !           307:                        if (ch == '%') {
1.1       misho     308:                                state = DP_S_FLAGS;
1.1.1.2 ! misho     309:                                ch = *format++;
        !           310:                        } else {
        !           311:                                cnk->type = CNK_FMT_STR;
        !           312:                                cnk->start = format - base -1;
        !           313:                                while ((ch != '\0') && (ch != '%')) ch = *format++;
        !           314:                                cnk->len = format - base - cnk->start -1;
        !           315:                        }
1.1       misho     316:                        break;
                    317:                case DP_S_FLAGS:
                    318:                        switch (ch) {
                    319:                        case '-':
1.1.1.2 ! misho     320:                                cnk->flags |= DP_F_MINUS;
1.1       misho     321:                                ch = *format++;
                    322:                                break;
                    323:                        case '+':
1.1.1.2 ! misho     324:                                cnk->flags |= DP_F_PLUS;
1.1       misho     325:                                ch = *format++;
                    326:                                break;
                    327:                        case ' ':
1.1.1.2 ! misho     328:                                cnk->flags |= DP_F_SPACE;
1.1       misho     329:                                ch = *format++;
                    330:                                break;
                    331:                        case '#':
1.1.1.2 ! misho     332:                                cnk->flags |= DP_F_NUM;
1.1       misho     333:                                ch = *format++;
                    334:                                break;
                    335:                        case '0':
1.1.1.2 ! misho     336:                                cnk->flags |= DP_F_ZERO;
        !           337:                                ch = *format++;
        !           338:                                break;
        !           339:                        case 'I':
        !           340:                                /* internationalization not supported yet */
1.1       misho     341:                                ch = *format++;
                    342:                                break;
                    343:                        default:
                    344:                                state = DP_S_MIN;
                    345:                                break;
                    346:                        }
                    347:                        break;
                    348:                case DP_S_MIN:
                    349:                        if (isdigit((unsigned char)ch)) {
1.1.1.2 ! misho     350:                                cnk->min = 10 * cnk->min + char_to_int (ch);
        !           351:                                ch = *format++;
        !           352:                        } else if (ch == '$') {
        !           353:                                if (!pfirst && !pflag) {
        !           354:                                        /* parameters must be all positioned or none */
        !           355:                                        goto done;
        !           356:                                }
        !           357:                                if (pfirst) {
        !           358:                                        pfirst = 0;
        !           359:                                        pflag = 1;
        !           360:                                }
        !           361:                                if (cnk->min == 0) /* what ?? */
        !           362:                                        goto done;
        !           363:                                cnk->num = cnk->min;
        !           364:                                cnk->min = 0;
1.1       misho     365:                                ch = *format++;
                    366:                        } else if (ch == '*') {
1.1.1.2 ! misho     367:                                if (pfirst) pfirst = 0;
        !           368:                                cnk->min_star = new_chunk();
        !           369:                                if (!cnk->min_star) /* out of memory :-( */
        !           370:                                        goto done;
        !           371:                                cnk->min_star->type = CNK_INT;
        !           372:                                if (pflag) {
        !           373:                                        int num;
        !           374:                                        ch = *format++;
        !           375:                                        if (!isdigit((unsigned char)ch)) {
        !           376:                                                /* parameters must be all positioned or none */
        !           377:                                                goto done;
        !           378:                                        }
        !           379:                                        for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
        !           380:                                                num = 10 * num + char_to_int(ch);
        !           381:                                        }
        !           382:                                        cnk->min_star->num = num;
        !           383:                                        if (ch != '$') /* what ?? */
        !           384:                                                goto done;
        !           385:                                } else {
        !           386:                                        cnk->min_star->num = ++pnum;
        !           387:                                }
        !           388:                                max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
        !           389:                                if (max_pos == 0) /* out of memory :-( */
        !           390:                                        goto done;
1.1       misho     391:                                ch = *format++;
                    392:                                state = DP_S_DOT;
                    393:                        } else {
1.1.1.2 ! misho     394:                                if (pfirst) pfirst = 0;
1.1       misho     395:                                state = DP_S_DOT;
                    396:                        }
                    397:                        break;
                    398:                case DP_S_DOT:
                    399:                        if (ch == '.') {
                    400:                                state = DP_S_MAX;
                    401:                                ch = *format++;
                    402:                        } else { 
                    403:                                state = DP_S_MOD;
                    404:                        }
                    405:                        break;
                    406:                case DP_S_MAX:
                    407:                        if (isdigit((unsigned char)ch)) {
1.1.1.2 ! misho     408:                                if (cnk->max < 0)
        !           409:                                        cnk->max = 0;
        !           410:                                cnk->max = 10 * cnk->max + char_to_int (ch);
        !           411:                                ch = *format++;
        !           412:                        } else if (ch == '$') {
        !           413:                                if (!pfirst && !pflag) {
        !           414:                                        /* parameters must be all positioned or none */
        !           415:                                        goto done;
        !           416:                                }
        !           417:                                if (cnk->max <= 0) /* what ?? */
        !           418:                                        goto done;
        !           419:                                cnk->num = cnk->max;
        !           420:                                cnk->max = -1;
1.1       misho     421:                                ch = *format++;
                    422:                        } else if (ch == '*') {
1.1.1.2 ! misho     423:                                cnk->max_star = new_chunk();
        !           424:                                if (!cnk->max_star) /* out of memory :-( */
        !           425:                                        goto done;
        !           426:                                cnk->max_star->type = CNK_INT;
        !           427:                                if (pflag) {
        !           428:                                        int num;
        !           429:                                        ch = *format++;
        !           430:                                        if (!isdigit((unsigned char)ch)) {
        !           431:                                                /* parameters must be all positioned or none */
        !           432:                                                goto done;
        !           433:                                        }
        !           434:                                        for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
        !           435:                                                num = 10 * num + char_to_int(ch);
        !           436:                                        }
        !           437:                                        cnk->max_star->num = num;
        !           438:                                        if (ch != '$') /* what ?? */
        !           439:                                                goto done;
        !           440:                                } else {
        !           441:                                        cnk->max_star->num = ++pnum;
        !           442:                                }
        !           443:                                max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
        !           444:                                if (max_pos == 0) /* out of memory :-( */
        !           445:                                        goto done;
        !           446: 
1.1       misho     447:                                ch = *format++;
                    448:                                state = DP_S_MOD;
                    449:                        } else {
                    450:                                state = DP_S_MOD;
                    451:                        }
                    452:                        break;
                    453:                case DP_S_MOD:
                    454:                        switch (ch) {
                    455:                        case 'h':
1.1.1.2 ! misho     456:                                cnk->cflags = DP_C_SHORT;
1.1       misho     457:                                ch = *format++;
1.1.1.2 ! misho     458:                                if (ch == 'h') {
        !           459:                                        cnk->cflags = DP_C_CHAR;
        !           460:                                        ch = *format++;
        !           461:                                }
1.1       misho     462:                                break;
                    463:                        case 'l':
1.1.1.2 ! misho     464:                                cnk->cflags = DP_C_LONG;
1.1       misho     465:                                ch = *format++;
                    466:                                if (ch == 'l') {        /* It's a long long */
1.1.1.2 ! misho     467:                                        cnk->cflags = DP_C_LLONG;
1.1       misho     468:                                        ch = *format++;
                    469:                                }
                    470:                                break;
                    471:                        case 'L':
1.1.1.2 ! misho     472:                                cnk->cflags = DP_C_LDOUBLE;
        !           473:                                ch = *format++;
        !           474:                                break;
        !           475:                        case 'z':
        !           476:                                cnk->cflags = DP_C_SIZET;
1.1       misho     477:                                ch = *format++;
                    478:                                break;
                    479:                        default:
                    480:                                break;
                    481:                        }
                    482:                        state = DP_S_CONV;
                    483:                        break;
                    484:                case DP_S_CONV:
1.1.1.2 ! misho     485:                        if (cnk->num == 0) cnk->num = ++pnum;
        !           486:                        max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
        !           487:                        if (max_pos == 0) /* out of memory :-( */
        !           488:                                goto done;
        !           489:                        
1.1       misho     490:                        switch (ch) {
                    491:                        case 'd':
                    492:                        case 'i':
1.1.1.2 ! misho     493:                                cnk->type = CNK_INT;
1.1       misho     494:                                break;
                    495:                        case 'o':
1.1.1.2 ! misho     496:                                cnk->type = CNK_OCTAL;
        !           497:                                cnk->flags |= DP_F_UNSIGNED;
1.1       misho     498:                                break;
                    499:                        case 'u':
1.1.1.2 ! misho     500:                                cnk->type = CNK_UINT;
        !           501:                                cnk->flags |= DP_F_UNSIGNED;
1.1       misho     502:                                break;
                    503:                        case 'X':
1.1.1.2 ! misho     504:                                cnk->flags |= DP_F_UP;
1.1       misho     505:                        case 'x':
1.1.1.2 ! misho     506:                                cnk->type = CNK_HEX;
        !           507:                                cnk->flags |= DP_F_UNSIGNED;
1.1       misho     508:                                break;
1.1.1.2 ! misho     509:                        case 'A':
        !           510:                                /* hex float not supported yet */
1.1       misho     511:                        case 'E':
                    512:                        case 'G':
1.1.1.2 ! misho     513:                        case 'F':
        !           514:                                cnk->flags |= DP_F_UP;
        !           515:                        case 'a':
        !           516:                                /* hex float not supported yet */
        !           517:                        case 'e':
        !           518:                        case 'f':
1.1       misho     519:                        case 'g':
1.1.1.2 ! misho     520:                                cnk->type = CNK_FLOAT;
1.1       misho     521:                                break;
                    522:                        case 'c':
1.1.1.2 ! misho     523:                                cnk->type = CNK_CHAR;
1.1       misho     524:                                break;
                    525:                        case 's':
1.1.1.2 ! misho     526:                                cnk->type = CNK_STRING;
1.1       misho     527:                                break;
                    528:                        case 'p':
1.1.1.2 ! misho     529:                                cnk->type = CNK_PTR;
        !           530:                                cnk->flags |= DP_F_UNSIGNED;
1.1       misho     531:                                break;
                    532:                        case 'n':
1.1.1.2 ! misho     533:                                cnk->type = CNK_NUM;
1.1       misho     534:                                break;
                    535:                        case '%':
1.1.1.2 ! misho     536:                                cnk->type = CNK_PRCNT;
1.1       misho     537:                                break;
                    538:                        default:
1.1.1.2 ! misho     539:                                /* Unknown, bail out*/
        !           540:                                goto done;
1.1       misho     541:                        }
                    542:                        ch = *format++;
                    543:                        state = DP_S_DEFAULT;
                    544:                        break;
                    545:                case DP_S_DONE:
                    546:                        break;
                    547:                default:
                    548:                        /* hmm? */
                    549:                        break; /* some picky compilers need this */
                    550:                }
                    551:        }
1.1.1.2 ! misho     552: 
        !           553:        /* retrieve the format arguments */
        !           554:        for (pnum = 0; pnum < max_pos; pnum++) {
        !           555:                int i;
        !           556: 
        !           557:                if (clist[pnum].num == 0) {
        !           558:                        /* ignoring a parameter should not be permitted
        !           559:                         * all parameters must be matched at least once
        !           560:                         * BUT seem some system ignore this rule ...
        !           561:                         * at least my glibc based system does --SSS
        !           562:                         */
        !           563: #ifdef DEBUG_SNPRINTF
        !           564:                        printf("parameter at position %d not used\n", pnum+1);
        !           565: #endif
        !           566:                        /* eat the parameter */
        !           567:                        va_arg (args, int);
        !           568:                        continue;
        !           569:                }
        !           570:                for (i = 1; i < clist[pnum].num; i++) {
        !           571:                        if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
        !           572:                                /* nooo noo no!
        !           573:                                 * all the references to a parameter
        !           574:                                 * must be of the same type
        !           575:                                 */
        !           576:                                goto done;
        !           577:                        }
        !           578:                }
        !           579:                cnk = clist[pnum].chunks[0];
        !           580:                switch (cnk->type) {
        !           581:                case CNK_INT:
        !           582:                        if (cnk->cflags == DP_C_SHORT) 
        !           583:                                cnk->value = va_arg (args, int);
        !           584:                        else if (cnk->cflags == DP_C_LONG)
        !           585:                                cnk->value = va_arg (args, long int);
        !           586:                        else if (cnk->cflags == DP_C_LLONG)
        !           587:                                cnk->value = va_arg (args, LLONG);
        !           588:                        else if (cnk->cflags == DP_C_SIZET)
        !           589:                                cnk->value = va_arg (args, ssize_t);
        !           590:                        else
        !           591:                                cnk->value = va_arg (args, int);
        !           592: 
        !           593:                        for (i = 1; i < clist[pnum].num; i++) {
        !           594:                                clist[pnum].chunks[i]->value = cnk->value;
        !           595:                        }
        !           596:                        break;
        !           597: 
        !           598:                case CNK_OCTAL:
        !           599:                case CNK_UINT:
        !           600:                case CNK_HEX:
        !           601:                        if (cnk->cflags == DP_C_SHORT)
        !           602:                                cnk->value = va_arg (args, unsigned int);
        !           603:                        else if (cnk->cflags == DP_C_LONG)
        !           604:                                cnk->value = (unsigned long int)va_arg (args, unsigned long int);
        !           605:                        else if (cnk->cflags == DP_C_LLONG)
        !           606:                                cnk->value = (LLONG)va_arg (args, unsigned LLONG);
        !           607:                        else if (cnk->cflags == DP_C_SIZET)
        !           608:                                cnk->value = (size_t)va_arg (args, size_t);
        !           609:                        else
        !           610:                                cnk->value = (unsigned int)va_arg (args, unsigned int);
        !           611: 
        !           612:                        for (i = 1; i < clist[pnum].num; i++) {
        !           613:                                clist[pnum].chunks[i]->value = cnk->value;
        !           614:                        }
        !           615:                        break;
        !           616: 
        !           617:                case CNK_FLOAT:
        !           618:                        if (cnk->cflags == DP_C_LDOUBLE)
        !           619:                                cnk->fvalue = va_arg (args, LDOUBLE);
        !           620:                        else
        !           621:                                cnk->fvalue = va_arg (args, double);
        !           622: 
        !           623:                        for (i = 1; i < clist[pnum].num; i++) {
        !           624:                                clist[pnum].chunks[i]->fvalue = cnk->fvalue;
        !           625:                        }
        !           626:                        break;
        !           627: 
        !           628:                case CNK_CHAR:
        !           629:                        cnk->value = va_arg (args, int);
        !           630: 
        !           631:                        for (i = 1; i < clist[pnum].num; i++) {
        !           632:                                clist[pnum].chunks[i]->value = cnk->value;
        !           633:                        }
        !           634:                        break;
        !           635: 
        !           636:                case CNK_STRING:
        !           637:                        cnk->strvalue = va_arg (args, char *);
        !           638:                        if (!cnk->strvalue) cnk->strvalue = "(NULL)";
        !           639: 
        !           640:                        for (i = 1; i < clist[pnum].num; i++) {
        !           641:                                clist[pnum].chunks[i]->strvalue = cnk->strvalue;
        !           642:                        }
        !           643:                        break;
        !           644: 
        !           645:                case CNK_PTR:
        !           646:                        cnk->strvalue = va_arg (args, void *);
        !           647:                        for (i = 1; i < clist[pnum].num; i++) {
        !           648:                                clist[pnum].chunks[i]->strvalue = cnk->strvalue;
        !           649:                        }
        !           650:                        break;
        !           651: 
        !           652:                case CNK_NUM:
        !           653:                        if (cnk->cflags == DP_C_CHAR)
        !           654:                                cnk->pnum = va_arg (args, char *);
        !           655:                        else if (cnk->cflags == DP_C_SHORT)
        !           656:                                cnk->pnum = va_arg (args, short int *);
        !           657:                        else if (cnk->cflags == DP_C_LONG)
        !           658:                                cnk->pnum = va_arg (args, long int *);
        !           659:                        else if (cnk->cflags == DP_C_LLONG)
        !           660:                                cnk->pnum = va_arg (args, LLONG *);
        !           661:                        else if (cnk->cflags == DP_C_SIZET)
        !           662:                                cnk->pnum = va_arg (args, ssize_t *);
        !           663:                        else
        !           664:                                cnk->pnum = va_arg (args, int *);
        !           665: 
        !           666:                        for (i = 1; i < clist[pnum].num; i++) {
        !           667:                                clist[pnum].chunks[i]->pnum = cnk->pnum;
        !           668:                        }
        !           669:                        break;
        !           670: 
        !           671:                case CNK_PRCNT:
        !           672:                        break;
        !           673: 
        !           674:                default:
        !           675:                        /* what ?? */
        !           676:                        goto done;
        !           677:                }
        !           678:        }
        !           679:        /* print out the actual string from chunks */
        !           680:        currlen = 0;
        !           681:        cnk = chunks;
        !           682:        while (cnk) {
        !           683:                int len, min, max;
        !           684: 
        !           685:                if (cnk->min_star) min = cnk->min_star->value;
        !           686:                else min = cnk->min;
        !           687:                if (cnk->max_star) max = cnk->max_star->value;
        !           688:                else max = cnk->max;
        !           689: 
        !           690:                switch (cnk->type) {
        !           691: 
        !           692:                case CNK_FMT_STR:
        !           693:                        if (maxlen != 0 && maxlen > currlen) {
        !           694:                                if (maxlen > (currlen + cnk->len)) len = cnk->len;
        !           695:                                else len = maxlen - currlen;
        !           696: 
        !           697:                                memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
        !           698:                        }
        !           699:                        currlen += cnk->len;
        !           700:                                
        !           701:                        break;
        !           702: 
        !           703:                case CNK_INT:
        !           704:                case CNK_UINT:
        !           705:                        fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
        !           706:                        break;
        !           707: 
        !           708:                case CNK_OCTAL:
        !           709:                        fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
        !           710:                        break;
        !           711: 
        !           712:                case CNK_HEX:
        !           713:                        fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
        !           714:                        break;
        !           715: 
        !           716:                case CNK_FLOAT:
        !           717:                        fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
        !           718:                        break;
        !           719: 
        !           720:                case CNK_CHAR:
        !           721:                        dopr_outch (buffer, &currlen, maxlen, cnk->value);
        !           722:                        break;
        !           723: 
        !           724:                case CNK_STRING:
        !           725:                        if (max == -1) {
        !           726:                                max = strlen(cnk->strvalue);
        !           727:                        }
        !           728:                        fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
        !           729:                        break;
        !           730: 
        !           731:                case CNK_PTR:
        !           732:                        fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
        !           733:                        break;
        !           734: 
        !           735:                case CNK_NUM:
        !           736:                        if (cnk->cflags == DP_C_CHAR)
        !           737:                                *((char *)(cnk->pnum)) = (char)currlen;
        !           738:                        else if (cnk->cflags == DP_C_SHORT)
        !           739:                                *((short int *)(cnk->pnum)) = (short int)currlen;
        !           740:                        else if (cnk->cflags == DP_C_LONG)
        !           741:                                *((long int *)(cnk->pnum)) = (long int)currlen;
        !           742:                        else if (cnk->cflags == DP_C_LLONG)
        !           743:                                *((LLONG *)(cnk->pnum)) = (LLONG)currlen;
        !           744:                        else if (cnk->cflags == DP_C_SIZET)
        !           745:                                *((ssize_t *)(cnk->pnum)) = (ssize_t)currlen;
        !           746:                        else
        !           747:                                *((int *)(cnk->pnum)) = (int)currlen;
        !           748:                        break;
        !           749: 
        !           750:                case CNK_PRCNT:
        !           751:                        dopr_outch (buffer, &currlen, maxlen, '%');
        !           752:                        break;
        !           753: 
        !           754:                default:
        !           755:                        /* what ?? */
        !           756:                        goto done;
        !           757:                }
        !           758:                cnk = cnk->next;
        !           759:        }
1.1       misho     760:        if (maxlen != 0) {
                    761:                if (currlen < maxlen - 1) 
                    762:                        buffer[currlen] = '\0';
                    763:                else if (maxlen > 0) 
                    764:                        buffer[maxlen - 1] = '\0';
                    765:        }
1.1.1.2 ! misho     766:        ret = currlen;
        !           767: 
        !           768: done:
        !           769:        va_end(args);
        !           770: 
        !           771:        while (chunks) {
        !           772:                cnk = chunks->next;
        !           773:                free(chunks);
        !           774:                chunks = cnk;
        !           775:        }
        !           776:        if (clist) {
        !           777:                for (pnum = 0; pnum < max_pos; pnum++) {
        !           778:                        if (clist[pnum].chunks) free(clist[pnum].chunks);
        !           779:                }
        !           780:                free(clist);
        !           781:        }
        !           782:        return ret;
1.1       misho     783: }
                    784: 
                    785: static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
                    786:                    char *value, int flags, int min, int max)
                    787: {
                    788:        int padlen, strln;     /* amount to pad */
                    789:        int cnt = 0;
                    790: 
                    791: #ifdef DEBUG_SNPRINTF
                    792:        printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
                    793: #endif
                    794:        if (value == 0) {
                    795:                value = "<NULL>";
                    796:        }
                    797: 
1.1.1.2 ! misho     798:        for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
1.1       misho     799:        padlen = min - strln;
                    800:        if (padlen < 0) 
                    801:                padlen = 0;
                    802:        if (flags & DP_F_MINUS) 
                    803:                padlen = -padlen; /* Left Justify */
                    804:        
1.1.1.2 ! misho     805:        while (padlen > 0) {
1.1       misho     806:                dopr_outch (buffer, currlen, maxlen, ' ');
                    807:                --padlen;
                    808:        }
                    809:        while (*value && (cnt < max)) {
                    810:                dopr_outch (buffer, currlen, maxlen, *value++);
                    811:                ++cnt;
                    812:        }
1.1.1.2 ! misho     813:        while (padlen < 0) {
1.1       misho     814:                dopr_outch (buffer, currlen, maxlen, ' ');
                    815:                ++padlen;
                    816:        }
                    817: }
                    818: 
                    819: /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
                    820: 
                    821: static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
1.1.1.2 ! misho     822:                    LLONG value, int base, int min, int max, int flags)
1.1       misho     823: {
                    824:        int signvalue = 0;
1.1.1.2 ! misho     825:        unsigned LLONG uvalue;
1.1       misho     826:        char convert[20];
                    827:        int place = 0;
                    828:        int spadlen = 0; /* amount to space pad */
                    829:        int zpadlen = 0; /* amount to zero pad */
                    830:        int caps = 0;
                    831:        
                    832:        if (max < 0)
                    833:                max = 0;
                    834:        
                    835:        uvalue = value;
                    836:        
                    837:        if(!(flags & DP_F_UNSIGNED)) {
                    838:                if( value < 0 ) {
                    839:                        signvalue = '-';
                    840:                        uvalue = -value;
                    841:                } else {
                    842:                        if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
                    843:                                signvalue = '+';
                    844:                        else if (flags & DP_F_SPACE)
                    845:                                signvalue = ' ';
                    846:                }
                    847:        }
                    848:   
                    849:        if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
                    850: 
                    851:        do {
                    852:                convert[place++] =
                    853:                        (caps? "0123456789ABCDEF":"0123456789abcdef")
                    854:                        [uvalue % (unsigned)base  ];
                    855:                uvalue = (uvalue / (unsigned)base );
                    856:        } while(uvalue && (place < 20));
                    857:        if (place == 20) place--;
                    858:        convert[place] = 0;
                    859: 
                    860:        zpadlen = max - place;
                    861:        spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
                    862:        if (zpadlen < 0) zpadlen = 0;
                    863:        if (spadlen < 0) spadlen = 0;
                    864:        if (flags & DP_F_ZERO) {
                    865:                zpadlen = MAX(zpadlen, spadlen);
                    866:                spadlen = 0;
                    867:        }
                    868:        if (flags & DP_F_MINUS) 
                    869:                spadlen = -spadlen; /* Left Justifty */
                    870: 
                    871: #ifdef DEBUG_SNPRINTF
                    872:        printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
                    873:               zpadlen, spadlen, min, max, place);
                    874: #endif
                    875: 
                    876:        /* Spaces */
                    877:        while (spadlen > 0) {
                    878:                dopr_outch (buffer, currlen, maxlen, ' ');
                    879:                --spadlen;
                    880:        }
                    881: 
                    882:        /* Sign */
                    883:        if (signvalue) 
                    884:                dopr_outch (buffer, currlen, maxlen, signvalue);
                    885: 
                    886:        /* Zeros */
                    887:        if (zpadlen > 0) {
                    888:                while (zpadlen > 0) {
                    889:                        dopr_outch (buffer, currlen, maxlen, '0');
                    890:                        --zpadlen;
                    891:                }
                    892:        }
                    893: 
                    894:        /* Digits */
                    895:        while (place > 0) 
                    896:                dopr_outch (buffer, currlen, maxlen, convert[--place]);
                    897:   
                    898:        /* Left Justified spaces */
                    899:        while (spadlen < 0) {
                    900:                dopr_outch (buffer, currlen, maxlen, ' ');
                    901:                ++spadlen;
                    902:        }
                    903: }
                    904: 
                    905: static LDOUBLE abs_val(LDOUBLE value)
                    906: {
                    907:        LDOUBLE result = value;
                    908: 
                    909:        if (value < 0)
                    910:                result = -value;
                    911:        
                    912:        return result;
                    913: }
                    914: 
                    915: static LDOUBLE POW10(int exp)
                    916: {
                    917:        LDOUBLE result = 1;
                    918:        
                    919:        while (exp) {
                    920:                result *= 10;
                    921:                exp--;
                    922:        }
                    923:   
                    924:        return result;
                    925: }
                    926: 
                    927: static LLONG ROUND(LDOUBLE value)
                    928: {
                    929:        LLONG intpart;
                    930: 
                    931:        intpart = (LLONG)value;
                    932:        value = value - intpart;
                    933:        if (value >= 0.5) intpart++;
                    934:        
                    935:        return intpart;
                    936: }
                    937: 
                    938: /* a replacement for modf that doesn't need the math library. Should
                    939:    be portable, but slow */
                    940: static double my_modf(double x0, double *iptr)
                    941: {
                    942:        int i;
1.1.1.2 ! misho     943:        LLONG l=0;
1.1       misho     944:        double x = x0;
                    945:        double f = 1.0;
                    946: 
                    947:        for (i=0;i<100;i++) {
                    948:                l = (long)x;
1.1.1.2 ! misho     949:                if (l <= (x+1) && l >= (x-1)) break;
1.1       misho     950:                x *= 0.1;
                    951:                f *= 10.0;
                    952:        }
                    953: 
1.1.1.2 ! misho     954:        if (i == 100) {
        !           955:                /* yikes! the number is beyond what we can handle. What do we do? */
        !           956:                (*iptr) = 0;
        !           957:                return 0;
        !           958:        }
        !           959: 
        !           960:        if (i != 0) {
        !           961:                double i2;
        !           962:                double ret;
        !           963: 
        !           964:                ret = my_modf(x0-l*f, &i2);
        !           965:                (*iptr) = l*f + i2;
        !           966:                return ret;
        !           967:        } 
        !           968: 
        !           969:        (*iptr) = l;
        !           970:        return x - (*iptr);
1.1       misho     971: }
                    972: 
                    973: 
                    974: static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
                    975:                   LDOUBLE fvalue, int min, int max, int flags)
                    976: {
                    977:        int signvalue = 0;
                    978:        double ufvalue;
                    979:        char iconvert[311];
                    980:        char fconvert[311];
                    981:        int iplace = 0;
                    982:        int fplace = 0;
                    983:        int padlen = 0; /* amount to pad */
                    984:        int zpadlen = 0; 
                    985:        int caps = 0;
                    986:        int idx;
                    987:        double intpart;
                    988:        double fracpart;
                    989:        double temp;
                    990:   
                    991:        /* 
                    992:         * AIX manpage says the default is 0, but Solaris says the default
                    993:         * is 6, and sprintf on AIX defaults to 6
                    994:         */
                    995:        if (max < 0)
                    996:                max = 6;
                    997: 
                    998:        ufvalue = abs_val (fvalue);
                    999: 
                   1000:        if (fvalue < 0) {
                   1001:                signvalue = '-';
                   1002:        } else {
                   1003:                if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
                   1004:                        signvalue = '+';
                   1005:                } else {
                   1006:                        if (flags & DP_F_SPACE)
                   1007:                                signvalue = ' ';
                   1008:                }
                   1009:        }
                   1010: 
                   1011: #if 0
                   1012:        if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
                   1013: #endif
                   1014: 
                   1015: #if 0
                   1016:         if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
                   1017: #endif
                   1018: 
                   1019:        /* 
1.1.1.2 ! misho    1020:         * Sorry, we only support 9 digits past the decimal because of our 
1.1       misho    1021:         * conversion method
                   1022:         */
1.1.1.2 ! misho    1023:        if (max > 9)
        !          1024:                max = 9;
1.1       misho    1025: 
                   1026:        /* We "cheat" by converting the fractional part to integer by
                   1027:         * multiplying by a factor of 10
                   1028:         */
                   1029: 
                   1030:        temp = ufvalue;
                   1031:        my_modf(temp, &intpart);
                   1032: 
                   1033:        fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
                   1034:        
                   1035:        if (fracpart >= POW10(max)) {
                   1036:                intpart++;
                   1037:                fracpart -= POW10(max);
                   1038:        }
                   1039: 
                   1040: 
                   1041:        /* Convert integer part */
                   1042:        do {
                   1043:                temp = intpart*0.1;
                   1044:                my_modf(temp, &intpart);
                   1045:                idx = (int) ((temp -intpart +0.05)* 10.0);
                   1046:                /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
                   1047:                /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
                   1048:                iconvert[iplace++] =
                   1049:                        (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
                   1050:        } while (intpart && (iplace < 311));
                   1051:        if (iplace == 311) iplace--;
                   1052:        iconvert[iplace] = 0;
                   1053: 
                   1054:        /* Convert fractional part */
                   1055:        if (fracpart)
                   1056:        {
                   1057:                do {
                   1058:                        temp = fracpart*0.1;
                   1059:                        my_modf(temp, &fracpart);
                   1060:                        idx = (int) ((temp -fracpart +0.05)* 10.0);
                   1061:                        /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
                   1062:                        /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
                   1063:                        fconvert[fplace++] =
                   1064:                        (caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
                   1065:                } while(fracpart && (fplace < 311));
                   1066:                if (fplace == 311) fplace--;
                   1067:        }
                   1068:        fconvert[fplace] = 0;
                   1069:   
                   1070:        /* -1 for decimal point, another -1 if we are printing a sign */
                   1071:        padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
                   1072:        zpadlen = max - fplace;
                   1073:        if (zpadlen < 0) zpadlen = 0;
                   1074:        if (padlen < 0) 
                   1075:                padlen = 0;
                   1076:        if (flags & DP_F_MINUS) 
                   1077:                padlen = -padlen; /* Left Justifty */
                   1078:        
                   1079:        if ((flags & DP_F_ZERO) && (padlen > 0)) {
                   1080:                if (signvalue) {
                   1081:                        dopr_outch (buffer, currlen, maxlen, signvalue);
                   1082:                        --padlen;
                   1083:                        signvalue = 0;
                   1084:                }
                   1085:                while (padlen > 0) {
                   1086:                        dopr_outch (buffer, currlen, maxlen, '0');
                   1087:                        --padlen;
                   1088:                }
                   1089:        }
                   1090:        while (padlen > 0) {
                   1091:                dopr_outch (buffer, currlen, maxlen, ' ');
                   1092:                --padlen;
                   1093:        }
                   1094:        if (signvalue) 
                   1095:                dopr_outch (buffer, currlen, maxlen, signvalue);
                   1096:        
                   1097:        while (iplace > 0) 
                   1098:                dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
                   1099: 
                   1100: #ifdef DEBUG_SNPRINTF
                   1101:        printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
                   1102: #endif
                   1103: 
                   1104:        /*
                   1105:         * Decimal point.  This should probably use locale to find the correct
                   1106:         * char to print out.
                   1107:         */
                   1108:        if (max > 0) {
                   1109:                dopr_outch (buffer, currlen, maxlen, '.');
                   1110:                
                   1111:                while (zpadlen > 0) {
                   1112:                        dopr_outch (buffer, currlen, maxlen, '0');
                   1113:                        --zpadlen;
                   1114:                }
                   1115: 
                   1116:                while (fplace > 0) 
                   1117:                        dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
                   1118:        }
                   1119: 
                   1120:        while (padlen < 0) {
                   1121:                dopr_outch (buffer, currlen, maxlen, ' ');
                   1122:                ++padlen;
                   1123:        }
                   1124: }
                   1125: 
                   1126: static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
                   1127: {
                   1128:        if (*currlen < maxlen) {
                   1129:                buffer[(*currlen)] = c;
                   1130:        }
                   1131:        (*currlen)++;
                   1132: }
                   1133: 
1.1.1.2 ! misho    1134: static struct pr_chunk *new_chunk(void) {
        !          1135:        struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk));
        !          1136: 
        !          1137:        if (!new_c)
        !          1138:                return NULL;
        !          1139: 
        !          1140:        new_c->type = 0;
        !          1141:        new_c->num = 0;
        !          1142:        new_c->min = 0;
        !          1143:        new_c->min_star = NULL;
        !          1144:        new_c->max = -1;
        !          1145:        new_c->max_star = NULL;
        !          1146:        new_c->flags = 0;
        !          1147:        new_c->cflags = 0;
        !          1148:        new_c->start = 0;
        !          1149:        new_c->len = 0;
        !          1150:        new_c->value = 0;
        !          1151:        new_c->fvalue = 0;
        !          1152:        new_c->strvalue = NULL;
        !          1153:        new_c->pnum = NULL;
        !          1154:        new_c->next = NULL;
        !          1155: 
        !          1156:        return new_c;
        !          1157: }
        !          1158: 
        !          1159: static int add_cnk_list_entry(struct pr_chunk_x **list,
        !          1160:                                int max_num, struct pr_chunk *chunk) {
        !          1161:        struct pr_chunk_x *l;
        !          1162:        struct pr_chunk **c;
        !          1163:        int max;
        !          1164:        int cnum;
        !          1165:        int i, pos;
        !          1166: 
        !          1167:        if (chunk->num > max_num) {
        !          1168:                max = chunk->num;
        !          1169:        
        !          1170:                if (*list == NULL) {
        !          1171:                        l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max);
        !          1172:                        pos = 0;
        !          1173:                } else {
        !          1174:                        l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max);
        !          1175:                        pos = max_num;
        !          1176:                }
        !          1177:                if (l == NULL) {
        !          1178:                        for (i = 0; i < max; i++) {
        !          1179:                                if ((*list)[i].chunks) free((*list)[i].chunks);
        !          1180:                        }
        !          1181:                        return 0;
        !          1182:                }
        !          1183:                for (i = pos; i < max; i++) {
        !          1184:                        l[i].chunks = NULL;
        !          1185:                        l[i].num = 0;
        !          1186:                }
        !          1187:        } else {
        !          1188:                l = *list;
        !          1189:                max = max_num;
        !          1190:        }
        !          1191: 
        !          1192:        i = chunk->num - 1;
        !          1193:        cnum = l[i].num + 1;
        !          1194:        if (l[i].chunks == NULL) {
        !          1195:                c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum); 
        !          1196:        } else {
        !          1197:                c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum);
        !          1198:        }
        !          1199:        if (c == NULL) {
        !          1200:                for (i = 0; i < max; i++) {
        !          1201:                        if (l[i].chunks) free(l[i].chunks);
        !          1202:                }
        !          1203:                return 0;
        !          1204:        }
        !          1205:        c[l[i].num] = chunk;
        !          1206:        l[i].chunks = c;
        !          1207:        l[i].num = cnum;
        !          1208: 
        !          1209:        *list = l;
        !          1210:        return max;
        !          1211: }
        !          1212: 
1.1       misho    1213:  int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
                   1214: {
                   1215:        return dopr(str, count, fmt, args);
                   1216: }
                   1217: #define vsnprintf rsync_vsnprintf
                   1218: #endif
                   1219: 
                   1220: /* yes this really must be a ||. Don't muck with this (tridge)
                   1221:  *
                   1222:  * The logic for these two is that we need our own definition if the
                   1223:  * OS *either* has no definition of *sprintf, or if it does have one
                   1224:  * that doesn't work properly according to the autoconf test.
                   1225:  */
                   1226: #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
                   1227: int rsync_snprintf(char *str,size_t count,const char *fmt,...)
                   1228: {
                   1229:        size_t ret;
                   1230:        va_list ap;
                   1231:     
                   1232:        va_start(ap, fmt);
                   1233:        ret = vsnprintf(str, count, fmt, ap);
                   1234:        va_end(ap);
                   1235:        return ret;
                   1236: }
                   1237: #define snprintf rsync_snprintf
                   1238: #endif
                   1239: 
                   1240: #ifndef HAVE_VASPRINTF
                   1241:  int vasprintf(char **ptr, const char *format, va_list ap)
                   1242: {
                   1243:        int ret;
                   1244:        va_list ap2;
                   1245: 
                   1246:        VA_COPY(ap2, ap);
                   1247:        ret = vsnprintf(NULL, 0, format, ap2);
1.1.1.2 ! misho    1248:        va_end(ap2);
        !          1249:        if (ret < 0) return ret;
1.1       misho    1250: 
                   1251:        (*ptr) = (char *)malloc(ret+1);
                   1252:        if (!*ptr) return -1;
                   1253: 
                   1254:        VA_COPY(ap2, ap);
                   1255:        ret = vsnprintf(*ptr, ret+1, format, ap2);
1.1.1.2 ! misho    1256:        va_end(ap2);
1.1       misho    1257: 
                   1258:        return ret;
                   1259: }
                   1260: #endif
                   1261: 
                   1262: 
                   1263: #ifndef HAVE_ASPRINTF
                   1264:  int asprintf(char **ptr, const char *format, ...)
                   1265: {
                   1266:        va_list ap;
                   1267:        int ret;
                   1268:        
                   1269:        *ptr = NULL;
                   1270:        va_start(ap, format);
                   1271:        ret = vasprintf(ptr, format, ap);
                   1272:        va_end(ap);
                   1273: 
                   1274:        return ret;
                   1275: }
                   1276: #endif
                   1277: 
                   1278: #ifdef TEST_SNPRINTF
                   1279: 
                   1280:  int sprintf(char *str,const char *fmt,...);
1.1.1.2 ! misho    1281:  int printf(const char *fmt,...);
1.1       misho    1282: 
                   1283:  int main (void)
                   1284: {
                   1285:        char buf1[1024];
                   1286:        char buf2[1024];
1.1.1.2 ! misho    1287:        char *buf3;
1.1       misho    1288:        char *fp_fmt[] = {
                   1289:                "%1.1f",
                   1290:                "%-1.5f",
                   1291:                "%1.5f",
                   1292:                "%123.9f",
                   1293:                "%10.5f",
                   1294:                "% 10.5f",
                   1295:                "%+22.9f",
                   1296:                "%+4.9f",
                   1297:                "%01.3f",
                   1298:                "%4f",
                   1299:                "%3.1f",
                   1300:                "%3.2f",
                   1301:                "%.0f",
                   1302:                "%f",
1.1.1.2 ! misho    1303:                "%-8.8f",
        !          1304:                "%-9.9f",
1.1       misho    1305:                NULL
                   1306:        };
                   1307:        double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996, 
                   1308:                             0.9996, 1.996, 4.136, 5.030201, 0.00205,
                   1309:                             /* END LIST */ 0};
                   1310:        char *int_fmt[] = {
                   1311:                "%-1.5d",
                   1312:                "%1.5d",
                   1313:                "%123.9d",
                   1314:                "%5.5d",
                   1315:                "%10.5d",
                   1316:                "% 10.5d",
                   1317:                "%+22.33d",
                   1318:                "%01.3d",
                   1319:                "%4d",
                   1320:                "%d",
                   1321:                NULL
                   1322:        };
1.1.1.2 ! misho    1323:        long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
1.1       misho    1324:        char *str_fmt[] = {
1.1.1.2 ! misho    1325:                "%10.5s",
        !          1326:                "%-10.5s",
        !          1327:                "%5.10s",
        !          1328:                "%-5.10s",
        !          1329:                "%10.1s",
        !          1330:                "%0.10s",
        !          1331:                "%10.0s",
        !          1332:                "%1.10s",
1.1       misho    1333:                "%s",
                   1334:                "%.1s",
                   1335:                "%.10s",
                   1336:                "%10s",
                   1337:                NULL
                   1338:        };
                   1339:        char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
1.1.1.2 ! misho    1340: #ifdef HAVE_LONG_LONG
        !          1341:        char *ll_fmt[] = {
        !          1342:                "%llu",
        !          1343:                NULL
        !          1344:        };
        !          1345:        LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
        !          1346: #endif
1.1       misho    1347:        int x, y;
                   1348:        int fail = 0;
                   1349:        int num = 0;
1.1.1.2 ! misho    1350:        int l1, l2;
        !          1351:        char *ss_fmt[] = {
        !          1352:                "%zd",
        !          1353:                "%zu",
        !          1354:                NULL
        !          1355:        };
        !          1356:        size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0};
1.1       misho    1357: 
                   1358:        printf ("Testing snprintf format codes against system sprintf...\n");
                   1359: 
                   1360:        for (x = 0; fp_fmt[x] ; x++) {
                   1361:                for (y = 0; fp_nums[y] != 0 ; y++) {
1.1.1.2 ! misho    1362:                        buf1[0] = buf2[0] = '\0';
        !          1363:                        l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
        !          1364:                        l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]);
        !          1365:                        buf1[1023] = buf2[1023] = '\0';
        !          1366:                        if (strcmp (buf1, buf2) || (l1 != l2)) {
        !          1367:                                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
        !          1368:                                       fp_fmt[x], l1, buf1, l2, buf2);
1.1       misho    1369:                                fail++;
                   1370:                        }
                   1371:                        num++;
                   1372:                }
                   1373:        }
                   1374: 
                   1375:        for (x = 0; int_fmt[x] ; x++) {
                   1376:                for (y = 0; int_nums[y] != 0 ; y++) {
1.1.1.2 ! misho    1377:                        buf1[0] = buf2[0] = '\0';
        !          1378:                        l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
        !          1379:                        l2 = sprintf (buf2, int_fmt[x], int_nums[y]);
        !          1380:                        buf1[1023] = buf2[1023] = '\0';
        !          1381:                        if (strcmp (buf1, buf2) || (l1 != l2)) {
        !          1382:                                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
        !          1383:                                       int_fmt[x], l1, buf1, l2, buf2);
1.1       misho    1384:                                fail++;
                   1385:                        }
                   1386:                        num++;
                   1387:                }
                   1388:        }
                   1389: 
                   1390:        for (x = 0; str_fmt[x] ; x++) {
                   1391:                for (y = 0; str_vals[y] != 0 ; y++) {
1.1.1.2 ! misho    1392:                        buf1[0] = buf2[0] = '\0';
        !          1393:                        l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
        !          1394:                        l2 = sprintf (buf2, str_fmt[x], str_vals[y]);
        !          1395:                        buf1[1023] = buf2[1023] = '\0';
        !          1396:                        if (strcmp (buf1, buf2) || (l1 != l2)) {
        !          1397:                                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
        !          1398:                                       str_fmt[x], l1, buf1, l2, buf2);
1.1       misho    1399:                                fail++;
                   1400:                        }
1.1.1.2 ! misho    1401:                        num++;
        !          1402:                }
        !          1403:        }
        !          1404: 
        !          1405: #ifdef HAVE_LONG_LONG
        !          1406:        for (x = 0; ll_fmt[x] ; x++) {
        !          1407:                for (y = 0; ll_nums[y] != 0 ; y++) {
        !          1408:                        buf1[0] = buf2[0] = '\0';
        !          1409:                        l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
        !          1410:                        l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]);
        !          1411:                        buf1[1023] = buf2[1023] = '\0';
        !          1412:                        if (strcmp (buf1, buf2) || (l1 != l2)) {
        !          1413:                                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
        !          1414:                                       ll_fmt[x], l1, buf1, l2, buf2);
1.1       misho    1415:                                fail++;
                   1416:                        }
                   1417:                        num++;
                   1418:                }
                   1419:        }
1.1.1.2 ! misho    1420: #endif
1.1       misho    1421: 
1.1.1.2 ! misho    1422: #define BUFSZ 2048
        !          1423: 
        !          1424:        buf1[0] = buf2[0] = '\0';
        !          1425:        if ((buf3 = malloc(BUFSZ)) == NULL) {
        !          1426:                fail++;
        !          1427:        } else {
        !          1428:                num++;
        !          1429:                memset(buf3, 'a', BUFSZ);
        !          1430:                snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
        !          1431:                buf1[1023] = '\0';
        !          1432:                if (strcmp(buf1, "a") != 0) {
        !          1433:                        printf("length limit buf1 '%s' expected 'a'\n", buf1);
        !          1434:                        fail++;
        !          1435:                }
        !          1436:         }
        !          1437: 
        !          1438:        buf1[0] = buf2[0] = '\0';
        !          1439:        l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
        !          1440:        l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
        !          1441:        buf1[1023] = buf2[1023] = '\0';
        !          1442:        if (strcmp(buf1, buf2) || (l1 != l2)) {
        !          1443:                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
        !          1444:                                "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
        !          1445:                fail++;
        !          1446:        }
        !          1447: 
        !          1448:        buf1[0] = buf2[0] = '\0';
        !          1449:        l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
        !          1450:        l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
        !          1451:        buf1[1023] = buf2[1023] = '\0';
        !          1452:        if (strcmp(buf1, buf2)) {
        !          1453:                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
        !          1454:                                "%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
        !          1455:                fail++;
        !          1456:        }
        !          1457: 
        !          1458:        for (x = 0; ss_fmt[x] ; x++) {
        !          1459:                for (y = 0; ss_nums[y] != 0 ; y++) {
        !          1460:                        buf1[0] = buf2[0] = '\0';
        !          1461:                        l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]);
        !          1462:                        l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]);
        !          1463:                        buf1[1023] = buf2[1023] = '\0';
        !          1464:                        if (strcmp (buf1, buf2) || (l1 != l2)) {
        !          1465:                                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n", 
        !          1466:                                       ss_fmt[x], l1, buf1, l2, buf2);
        !          1467:                                fail++;
        !          1468:                        }
        !          1469:                        num++;
        !          1470:                }
        !          1471:        }
        !          1472: #if 0
        !          1473:        buf1[0] = buf2[0] = '\0';
        !          1474:        l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
        !          1475:        l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
        !          1476:        buf1[1023] = buf2[1023] = '\0';
        !          1477:        if (strcmp(buf1, buf2)) {
        !          1478:                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
        !          1479:                                "%lld", l1, buf1, l2, buf2);
        !          1480:                fail++;
        !          1481:        }
        !          1482: 
        !          1483:        buf1[0] = buf2[0] = '\0';
        !          1484:        l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
        !          1485:        l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
        !          1486:        buf1[1023] = buf2[1023] = '\0';
        !          1487:        if (strcmp(buf1, buf2)) {
        !          1488:                printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
        !          1489:                                "%Lf", l1, buf1, l2, buf2);
        !          1490:                fail++;
        !          1491:        }
        !          1492: #endif
1.1       misho    1493:        printf ("%d tests failed out of %d.\n", fail, num);
                   1494: 
                   1495:        printf("seeing how many digits we support\n");
                   1496:        {
                   1497:                double v0 = 0.12345678901234567890123456789012345678901;
                   1498:                for (x=0; x<100; x++) {
                   1499:                        double p = pow(10, x); 
                   1500:                        double r = v0*p;
                   1501:                        snprintf(buf1, sizeof(buf1), "%1.1f", r);
                   1502:                        sprintf(buf2,                "%1.1f", r);
                   1503:                        if (strcmp(buf1, buf2)) {
                   1504:                                printf("we seem to support %d digits\n", x-1);
                   1505:                                break;
                   1506:                        }
                   1507:                }
                   1508:        }
                   1509: 
                   1510:        return 0;
                   1511: }
                   1512: #endif /* TEST_SNPRINTF */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>