~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to lib/replace/snprintf.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

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