source: trunk/third/nmh/sbr/snprintf.c @ 12455

Revision 12455, 24.3 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12454, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * snprintf.c -- formatted output to a string
3 *
4 * This is an implementation of snprintf() and vsnprintf()
5 * taken from the Apache web server.  This is only used on
6 * systems which do not have a native version.
7 */
8
9/* ====================================================================
10 * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in
21 *    the documentation and/or other materials provided with the
22 *    distribution.
23 *
24 * 3. All advertising materials mentioning features or use of this
25 *    software must display the following acknowledgment:
26 *    "This product includes software developed by the Apache Group
27 *    for use in the Apache HTTP server project (http://www.apache.org/)."
28 *
29 * 4. The names "Apache Server" and "Apache Group" must not be used to
30 *    endorse or promote products derived from this software without
31 *    prior written permission. For written permission, please contact
32 *    apache@apache.org.
33 *
34 * 5. Products derived from this software may not be called "Apache"
35 *    nor may "Apache" appear in their names without prior written
36 *    permission of the Apache Group.
37 *
38 * 6. Redistributions of any form whatsoever must retain the following
39 *    acknowledgment:
40 *    "This product includes software developed by the Apache Group
41 *    for use in the Apache HTTP server project (http://www.apache.org/)."
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
44 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
46 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
47 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
48 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
49 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
50 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
52 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
53 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
54 * OF THE POSSIBILITY OF SUCH DAMAGE.
55 * ====================================================================
56 *
57 * This software consists of voluntary contributions made by many
58 * individuals on behalf of the Apache Group and was originally based
59 * on public domain software written at the National Center for
60 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
61 * For more information on the Apache Group and the Apache HTTP server
62 * project, please see <http://www.apache.org/>.
63 *
64 * This code is based on, and used with the permission of, the
65 * SIO stdio-replacement strx_* functions by Panos Tsirigotis
66 * <panos@alumni.cs.colorado.edu> for xinetd.
67 */
68
69#include <stdio.h>
70#include <ctype.h>
71#include <sys/types.h>
72#include <stdarg.h>
73#include <string.h>
74#include <stdlib.h>
75#include <math.h>
76#include <netinet/in.h>
77
78typedef enum {
79    NO = 0, YES = 1
80} boolean_e;
81
82#ifndef FALSE
83#define FALSE                   0
84#endif
85#ifndef TRUE
86#define TRUE                    1
87#endif
88#define NUL                     '\0'
89#define INT_NULL                ((int *)0)
90#define WIDE_INT                long
91
92typedef struct {
93    char *curpos;
94    char *endpos;
95} ap_vformatter_buff;
96
97typedef WIDE_INT wide_int;
98typedef unsigned WIDE_INT u_wide_int;
99typedef int bool_int;
100
101#define S_NULL                  "(null)"
102#define S_NULL_LEN              6
103
104#define FLOAT_DIGITS            6
105#define EXPONENT_LENGTH         10
106
107/* These macros allow correct support of 8-bit characters on systems which
108 * support 8-bit characters.  Pretty dumb how the cast is required, but
109 * that's legacy libc for ya.  These new macros do not support EOF like
110 * the standard macros do.  Tough.
111 */
112#define ap_isalpha(c) (isalpha(((unsigned char)(c))))
113#define ap_isdigit(c) (isdigit(((unsigned char)(c))))
114#define ap_islower(c) (islower(((unsigned char)(c))))
115
116/*
117 * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
118 *
119 * XXX: this is a magic number; do not decrease it
120 */
121#define NUM_BUF_SIZE            512
122
123/*
124 * cvt.c - IEEE floating point formatting routines for FreeBSD
125 * from GNU libc-4.6.27.  Modified to be thread safe.
126 */
127
128/*
129 *    ap_ecvt converts to decimal
130 *      the number of digits is specified by ndigit
131 *      decpt is set to the position of the decimal point
132 *      sign is set to 0 for positive, 1 for negative
133 */
134
135#define NDIG    80
136
137/* buf must have at least NDIG bytes */
138static char *ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag, char *buf)
139{
140    register int r2;
141    double fi, fj;
142    register char *p, *p1;
143
144    if (ndigits >= NDIG - 1)
145        ndigits = NDIG - 2;
146    r2 = 0;
147    *sign = 0;
148    p = &buf[0];
149    if (arg < 0) {
150        *sign = 1;
151        arg = -arg;
152    }
153    arg = modf(arg, &fi);
154    p1 = &buf[NDIG];
155    /*
156     * Do integer part
157     */
158    if (fi != 0) {
159        p1 = &buf[NDIG];
160        while (fi != 0) {
161            fj = modf(fi / 10, &fi);
162            *--p1 = (int) ((fj + .03) * 10) + '0';
163            r2++;
164        }
165        while (p1 < &buf[NDIG])
166            *p++ = *p1++;
167    }
168    else if (arg > 0) {
169        while ((fj = arg * 10) < 1) {
170            arg = fj;
171            r2--;
172        }
173    }
174    p1 = &buf[ndigits];
175    if (eflag == 0)
176        p1 += r2;
177    *decpt = r2;
178    if (p1 < &buf[0]) {
179        buf[0] = '\0';
180        return (buf);
181    }
182    while (p <= p1 && p < &buf[NDIG]) {
183        arg *= 10;
184        arg = modf(arg, &fj);
185        *p++ = (int) fj + '0';
186    }
187    if (p1 >= &buf[NDIG]) {
188        buf[NDIG - 1] = '\0';
189        return (buf);
190    }
191    p = p1;
192    *p1 += 5;
193    while (*p1 > '9') {
194        *p1 = '0';
195        if (p1 > buf)
196            ++ * --p1;
197        else {
198            *p1 = '1';
199            (*decpt)++;
200            if (eflag == 0) {
201                if (p > buf)
202                    *p = '0';
203                p++;
204            }
205        }
206    }
207    *p = '\0';
208    return (buf);
209}
210
211static char *ap_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
212{
213    return (ap_cvt(arg, ndigits, decpt, sign, 1, buf));
214}
215
216static char *ap_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
217{
218    return (ap_cvt(arg, ndigits, decpt, sign, 0, buf));
219}
220
221/*
222 * ap_gcvt  - Floating output conversion to
223 * minimal length string
224 */
225
226static char *ap_gcvt(double number, int ndigit, char *buf, boolean_e altform)
227{
228    int sign, decpt;
229    register char *p1, *p2;
230    register int i;
231    char buf1[NDIG];
232
233    p1 = ap_ecvt(number, ndigit, &decpt, &sign, buf1);
234    p2 = buf;
235    if (sign)
236        *p2++ = '-';
237    for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
238        ndigit--;
239    if ((decpt >= 0 && decpt - ndigit > 4)
240        || (decpt < 0 && decpt < -3)) {         /* use E-style */
241        decpt--;
242        *p2++ = *p1++;
243        *p2++ = '.';
244        for (i = 1; i < ndigit; i++)
245            *p2++ = *p1++;
246        *p2++ = 'e';
247        if (decpt < 0) {
248            decpt = -decpt;
249            *p2++ = '-';
250        }
251        else
252            *p2++ = '+';
253        if (decpt / 100 > 0)
254            *p2++ = decpt / 100 + '0';
255        if (decpt / 10 > 0)
256            *p2++ = (decpt % 100) / 10 + '0';
257        *p2++ = decpt % 10 + '0';
258    }
259    else {
260        if (decpt <= 0) {
261            if (*p1 != '0')
262                *p2++ = '.';
263            while (decpt < 0) {
264                decpt++;
265                *p2++ = '0';
266            }
267        }
268        for (i = 1; i <= ndigit; i++) {
269            *p2++ = *p1++;
270            if (i == decpt)
271                *p2++ = '.';
272        }
273        if (ndigit < decpt) {
274            while (ndigit++ < decpt)
275                *p2++ = '0';
276            *p2++ = '.';
277        }
278    }
279    if (p2[-1] == '.' && !altform)
280        p2--;
281    *p2 = '\0';
282    return (buf);
283}
284
285/*
286 * The INS_CHAR macro inserts a character in the buffer and writes
287 * the buffer back to disk if necessary
288 * It uses the char pointers sp and bep:
289 *      sp points to the next available character in the buffer
290 *      bep points to the end-of-buffer+1
291 * While using this macro, note that the nextb pointer is NOT updated.
292 *
293 * NOTE: Evaluation of the c argument should not have any side-effects
294 */
295#define INS_CHAR(c, sp, bep, cc)                                \
296            {                                                   \
297                if (sp >= bep) {                                \
298                    vbuff->curpos = sp;                         \
299                    if (flush_func(vbuff))                      \
300                        return -1;                              \
301                    sp = vbuff->curpos;                         \
302                    bep = vbuff->endpos;                        \
303                }                                               \
304                *sp++ = (c);                                    \
305                cc++;                                           \
306            }
307
308#define NUM( c )                        ( c - '0' )
309
310#define STR_TO_DEC( str, num )          \
311    num = NUM( *str++ ) ;               \
312    while ( ap_isdigit( *str ) )                \
313    {                                   \
314        num *= 10 ;                     \
315        num += NUM( *str++ ) ;          \
316    }
317
318/*
319 * This macro does zero padding so that the precision
320 * requirement is satisfied. The padding is done by
321 * adding '0's to the left of the string that is going
322 * to be printed.
323 */
324#define FIX_PRECISION( adjust, precision, s, s_len )    \
325    if ( adjust )                                       \
326        while ( s_len < precision )                     \
327        {                                               \
328            *--s = '0' ;                                \
329            s_len++ ;                                   \
330        }
331
332/*
333 * Macro that does padding. The padding is done by printing
334 * the character ch.
335 */
336#define PAD( width, len, ch )   do              \
337        {                                       \
338            INS_CHAR( ch, sp, bep, cc ) ;       \
339            width-- ;                           \
340        }                                       \
341        while ( width > len )
342
343/*
344 * Prefix the character ch to the string str
345 * Increase length
346 * Set the has_prefix flag
347 */
348#define PREFIX( str, length, ch )        *--str = ch ; length++ ; has_prefix = YES
349
350
351/*
352 * Convert num to its decimal format.
353 * Return value:
354 *   - a pointer to a string containing the number (no sign)
355 *   - len contains the length of the string
356 *   - is_negative is set to TRUE or FALSE depending on the sign
357 *     of the number (always set to FALSE if is_unsigned is TRUE)
358 *
359 * The caller provides a buffer for the string: that is the buf_end argument
360 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
361 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
362 */
363static char *conv_10(register wide_int num, register bool_int is_unsigned,
364                     register bool_int *is_negative, char *buf_end,
365                     register int *len)
366{
367    register char *p = buf_end;
368    register u_wide_int magnitude;
369
370    if (is_unsigned) {
371        magnitude = (u_wide_int) num;
372        *is_negative = FALSE;
373    }
374    else {
375        *is_negative = (num < 0);
376
377        /*
378         * On a 2's complement machine, negating the most negative integer
379         * results in a number that cannot be represented as a signed integer.
380         * Here is what we do to obtain the number's magnitude:
381         *      a. add 1 to the number
382         *      b. negate it (becomes positive)
383         *      c. convert it to unsigned
384         *      d. add 1
385         */
386        if (*is_negative) {
387            wide_int t = num + 1;
388
389            magnitude = ((u_wide_int) -t) + 1;
390        }
391        else
392            magnitude = (u_wide_int) num;
393    }
394
395    /*
396     * We use a do-while loop so that we write at least 1 digit
397     */
398    do {
399        register u_wide_int new_magnitude = magnitude / 10;
400
401        *--p = (char) (magnitude - new_magnitude * 10 + '0');
402        magnitude = new_magnitude;
403    }
404    while (magnitude);
405
406    *len = buf_end - p;
407    return (p);
408}
409
410
411
412static char *conv_in_addr(struct in_addr *ia, char *buf_end, int *len)
413{
414    unsigned addr = ntohl(ia->s_addr);
415    char *p = buf_end;
416    bool_int is_negative;
417    int sub_len;
418
419    p = conv_10((addr & 0x000000FF)      , TRUE, &is_negative, p, &sub_len);
420    *--p = '.';
421    p = conv_10((addr & 0x0000FF00) >>  8, TRUE, &is_negative, p, &sub_len);
422    *--p = '.';
423    p = conv_10((addr & 0x00FF0000) >> 16, TRUE, &is_negative, p, &sub_len);
424    *--p = '.';
425    p = conv_10((addr & 0xFF000000) >> 24, TRUE, &is_negative, p, &sub_len);
426
427    *len = buf_end - p;
428    return (p);
429}
430
431
432
433static char *conv_sockaddr_in(struct sockaddr_in *si, char *buf_end, int *len)
434{
435    char *p = buf_end;
436    bool_int is_negative;
437    int sub_len;
438
439    p = conv_10(ntohs(si->sin_port), TRUE, &is_negative, p, &sub_len);
440    *--p = ':';
441    p = conv_in_addr(&si->sin_addr, p, &sub_len);
442
443    *len = buf_end - p;
444    return (p);
445}
446
447
448
449/*
450 * Convert a floating point number to a string formats 'f', 'e' or 'E'.
451 * The result is placed in buf, and len denotes the length of the string
452 * The sign is returned in the is_negative argument (and is not placed
453 * in buf).
454 */
455static char *conv_fp(register char format, register double num,
456    boolean_e add_dp, int precision, bool_int *is_negative,
457    char *buf, int *len)
458{
459    register char *s = buf;
460    register char *p;
461    int decimal_point;
462    char buf1[NDIG];
463
464    if (format == 'f')
465        p = ap_fcvt(num, precision, &decimal_point, is_negative, buf1);
466    else                        /* either e or E format */
467        p = ap_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
468
469    /*
470     * Check for Infinity and NaN
471     */
472    if (ap_isalpha(*p)) {
473        *len = strlen(strcpy(buf, p));
474        *is_negative = FALSE;
475        return (buf);
476    }
477
478    if (format == 'f') {
479        if (decimal_point <= 0) {
480            *s++ = '0';
481            if (precision > 0) {
482                *s++ = '.';
483                while (decimal_point++ < 0)
484                    *s++ = '0';
485            }
486            else if (add_dp)
487                *s++ = '.';
488        }
489        else {
490            while (decimal_point-- > 0)
491                *s++ = *p++;
492            if (precision > 0 || add_dp)
493                *s++ = '.';
494        }
495    }
496    else {
497        *s++ = *p++;
498        if (precision > 0 || add_dp)
499            *s++ = '.';
500    }
501
502    /*
503     * copy the rest of p, the NUL is NOT copied
504     */
505    while (*p)
506        *s++ = *p++;
507
508    if (format != 'f') {
509        char temp[EXPONENT_LENGTH];     /* for exponent conversion */
510        int t_len;
511        bool_int exponent_is_negative;
512
513        *s++ = format;          /* either e or E */
514        decimal_point--;
515        if (decimal_point != 0) {
516            p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative,
517                        &temp[EXPONENT_LENGTH], &t_len);
518            *s++ = exponent_is_negative ? '-' : '+';
519
520            /*
521             * Make sure the exponent has at least 2 digits
522             */
523            if (t_len == 1)
524                *s++ = '0';
525            while (t_len--)
526                *s++ = *p++;
527        }
528        else {
529            *s++ = '+';
530            *s++ = '0';
531            *s++ = '0';
532        }
533    }
534
535    *len = s - buf;
536    return (buf);
537}
538
539
540/*
541 * Convert num to a base X number where X is a power of 2. nbits determines X.
542 * For example, if nbits is 3, we do base 8 conversion
543 * Return value:
544 *      a pointer to a string containing the number
545 *
546 * The caller provides a buffer for the string: that is the buf_end argument
547 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
548 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
549 */
550static char *conv_p2(register u_wide_int num, register int nbits,
551                     char format, char *buf_end, register int *len)
552{
553    register int mask = (1 << nbits) - 1;
554    register char *p = buf_end;
555    static const char low_digits[] = "0123456789abcdef";
556    static const char upper_digits[] = "0123456789ABCDEF";
557    register const char *digits = (format == 'X') ? upper_digits : low_digits;
558
559    do {
560        *--p = digits[num & mask];
561        num >>= nbits;
562    }
563    while (num);
564
565    *len = buf_end - p;
566    return (p);
567}
568
569
570/*
571 * Do format conversion placing the output in buffer
572 */
573int ap_vformatter(int (*flush_func)(ap_vformatter_buff *),
574    ap_vformatter_buff *vbuff, const char *fmt, va_list ap)
575{
576    register char *sp;
577    register char *bep;
578    register int cc = 0;
579    register int i;
580
581    register char *s = NULL;
582    char *q;
583    int s_len;
584
585    register int min_width = 0;
586    int precision = 0;
587    enum {
588        LEFT, RIGHT
589    } adjust;
590    char pad_char;
591    char prefix_char;
592
593    double fp_num;
594    wide_int i_num = (wide_int) 0;
595    u_wide_int ui_num;
596
597    char num_buf[NUM_BUF_SIZE];
598    char char_buf[2];           /* for printing %% and %<unknown> */
599
600    /*
601     * Flag variables
602     */
603    boolean_e is_long;
604    boolean_e alternate_form;
605    boolean_e print_sign;
606    boolean_e print_blank;
607    boolean_e adjust_precision;
608    boolean_e adjust_width;
609    bool_int is_negative;
610
611    sp = vbuff->curpos;
612    bep = vbuff->endpos;
613
614    while (*fmt) {
615        if (*fmt != '%') {
616            INS_CHAR(*fmt, sp, bep, cc);
617        }
618        else {
619            /*
620             * Default variable settings
621             */
622            adjust = RIGHT;
623            alternate_form = print_sign = print_blank = NO;
624            pad_char = ' ';
625            prefix_char = NUL;
626
627            fmt++;
628
629            /*
630             * Try to avoid checking for flags, width or precision
631             */
632            if (!ap_islower(*fmt)) {
633                /*
634                 * Recognize flags: -, #, BLANK, +
635                 */
636                for (;; fmt++) {
637                    if (*fmt == '-')
638                        adjust = LEFT;
639                    else if (*fmt == '+')
640                        print_sign = YES;
641                    else if (*fmt == '#')
642                        alternate_form = YES;
643                    else if (*fmt == ' ')
644                        print_blank = YES;
645                    else if (*fmt == '0')
646                        pad_char = '0';
647                    else
648                        break;
649                }
650
651                /*
652                 * Check if a width was specified
653                 */
654                if (ap_isdigit(*fmt)) {
655                    STR_TO_DEC(fmt, min_width);
656                    adjust_width = YES;
657                }
658                else if (*fmt == '*') {
659                    min_width = va_arg(ap, int);
660                    fmt++;
661                    adjust_width = YES;
662                    if (min_width < 0) {
663                        adjust = LEFT;
664                        min_width = -min_width;
665                    }
666                }
667                else
668                    adjust_width = NO;
669
670                /*
671                 * Check if a precision was specified
672                 *
673                 * XXX: an unreasonable amount of precision may be specified
674                 * resulting in overflow of num_buf. Currently we
675                 * ignore this possibility.
676                 */
677                if (*fmt == '.') {
678                    adjust_precision = YES;
679                    fmt++;
680                    if (ap_isdigit(*fmt)) {
681                        STR_TO_DEC(fmt, precision);
682                    }
683                    else if (*fmt == '*') {
684                        precision = va_arg(ap, int);
685                        fmt++;
686                        if (precision < 0)
687                            precision = 0;
688                    }
689                    else
690                        precision = 0;
691                }
692                else
693                    adjust_precision = NO;
694            }
695            else
696                adjust_precision = adjust_width = NO;
697
698            /*
699             * Modifier check
700             */
701            if (*fmt == 'l') {
702                is_long = YES;
703                fmt++;
704            }
705            else {
706                if (*fmt == 'h')  /* "short" backward compatibility */
707                    ++fmt;
708                is_long = NO;
709            }
710
711            /*
712             * Argument extraction and printing.
713             * First we determine the argument type.
714             * Then, we convert the argument to a string.
715             * On exit from the switch, s points to the string that
716             * must be printed, s_len has the length of the string
717             * The precision requirements, if any, are reflected in s_len.
718             *
719             * NOTE: pad_char may be set to '0' because of the 0 flag.
720             *   It is reset to ' ' by non-numeric formats
721             */
722            switch (*fmt) {
723            case 'u':
724                if (is_long)
725                    i_num = va_arg(ap, u_wide_int);
726                else
727                    i_num = (wide_int) va_arg(ap, unsigned int);
728                s = conv_10(i_num, 1, &is_negative,
729                            &num_buf[NUM_BUF_SIZE], &s_len);
730                FIX_PRECISION(adjust_precision, precision, s, s_len);
731                break;
732
733            case 'd':
734            case 'i':
735                if (is_long)
736                    i_num = va_arg(ap, wide_int);
737                else
738                    i_num = (wide_int) va_arg(ap, int);
739                s = conv_10(i_num, 0, &is_negative,
740                            &num_buf[NUM_BUF_SIZE], &s_len);
741                FIX_PRECISION(adjust_precision, precision, s, s_len);
742
743                if (is_negative)
744                    prefix_char = '-';
745                else if (print_sign)
746                    prefix_char = '+';
747                else if (print_blank)
748                    prefix_char = ' ';
749                break;
750
751
752            case 'o':
753                if (is_long)
754                    ui_num = va_arg(ap, u_wide_int);
755                else
756                    ui_num = (u_wide_int) va_arg(ap, unsigned int);
757                s = conv_p2(ui_num, 3, *fmt,
758                            &num_buf[NUM_BUF_SIZE], &s_len);
759                FIX_PRECISION(adjust_precision, precision, s, s_len);
760                if (alternate_form && *s != '0') {
761                    *--s = '0';
762                    s_len++;
763                }
764                break;
765
766
767            case 'x':
768            case 'X':
769                if (is_long)
770                    ui_num = (u_wide_int) va_arg(ap, u_wide_int);
771                else
772                    ui_num = (u_wide_int) va_arg(ap, unsigned int);
773                s = conv_p2(ui_num, 4, *fmt,
774                            &num_buf[NUM_BUF_SIZE], &s_len);
775                FIX_PRECISION(adjust_precision, precision, s, s_len);
776                if (alternate_form && i_num != 0) {
777                    *--s = *fmt;        /* 'x' or 'X' */
778                    *--s = '0';
779                    s_len += 2;
780                }
781                break;
782
783
784            case 's':
785                s = va_arg(ap, char *);
786                if (s != NULL) {
787                    s_len = strlen(s);
788                    if (adjust_precision && precision < s_len)
789                        s_len = precision;
790                }
791                else {
792                    s = S_NULL;
793                    s_len = S_NULL_LEN;
794                }
795                pad_char = ' ';
796                break;
797
798
799            case 'f':
800            case 'e':
801            case 'E':
802                fp_num = va_arg(ap, double);
803                /*
804                 * * We use &num_buf[ 1 ], so that we have room for the sign
805                 */
806                s = conv_fp(*fmt, fp_num, alternate_form,
807                        (adjust_precision == NO) ? FLOAT_DIGITS : precision,
808                            &is_negative, &num_buf[1], &s_len);
809                if (is_negative)
810                    prefix_char = '-';
811                else if (print_sign)
812                    prefix_char = '+';
813                else if (print_blank)
814                    prefix_char = ' ';
815                break;
816
817
818            case 'g':
819            case 'G':
820                if (adjust_precision == NO)
821                    precision = FLOAT_DIGITS;
822                else if (precision == 0)
823                    precision = 1;
824                /*
825                 * * We use &num_buf[ 1 ], so that we have room for the sign
826                 */
827                s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1],
828                            alternate_form);
829                if (*s == '-')
830                    prefix_char = *s++;
831                else if (print_sign)
832                    prefix_char = '+';
833                else if (print_blank)
834                    prefix_char = ' ';
835
836                s_len = strlen(s);
837
838                if (alternate_form && (q = strchr(s, '.')) == NULL) {
839                    s[s_len++] = '.';
840                    s[s_len] = '\0'; /* delimit for following strchr() */
841                }
842                if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
843                    *q = 'E';
844                break;
845
846
847            case 'c':
848                char_buf[0] = (char) (va_arg(ap, int));
849                s = &char_buf[0];
850                s_len = 1;
851                pad_char = ' ';
852                break;
853
854
855            case '%':
856                char_buf[0] = '%';
857                s = &char_buf[0];
858                s_len = 1;
859                pad_char = ' ';
860                break;
861
862
863            case 'n':
864                *(va_arg(ap, int *)) = cc;
865                break;
866
867                /*
868                 * This is where we extend the printf format, with a second
869                 * type specifier
870                 */
871            case 'p':
872                switch(*++fmt) {
873                    /*
874                     * If the pointer size is equal to the size of an unsigned
875                     * integer we convert the pointer to a hex number, otherwise
876                     * we print "%p" to indicate that we don't handle "%p".
877                     */
878                case 'p':
879                    ui_num = (u_wide_int) va_arg(ap, void *);
880
881                    if (sizeof(char *) <= sizeof(u_wide_int))
882                                s = conv_p2(ui_num, 4, 'x',
883                                            &num_buf[NUM_BUF_SIZE], &s_len);
884                    else {
885                        s = "%p";
886                        s_len = 2;
887                        prefix_char = NUL;
888                    }
889                    pad_char = ' ';
890                    break;
891
892                    /* print a struct sockaddr_in as a.b.c.d:port */
893                case 'I':
894                    {
895                        struct sockaddr_in *si;
896
897                        si = va_arg(ap, struct sockaddr_in *);
898                        if (si != NULL) {
899                            s = conv_sockaddr_in(si, &num_buf[NUM_BUF_SIZE], &s_len);
900                            if (adjust_precision && precision < s_len)
901                                s_len = precision;
902                        }
903                        else {
904                            s = S_NULL;
905                            s_len = S_NULL_LEN;
906                        }
907                        pad_char = ' ';
908                    }
909                    break;
910
911                    /* print a struct in_addr as a.b.c.d */
912                case 'A':
913                    {
914                        struct in_addr *ia;
915
916                        ia = va_arg(ap, struct in_addr *);
917                        if (ia != NULL) {
918                            s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len);
919                            if (adjust_precision && precision < s_len)
920                                s_len = precision;
921                        }
922                        else {
923                            s = S_NULL;
924                            s_len = S_NULL_LEN;
925                        }
926                        pad_char = ' ';
927                    }
928                    break;
929
930                case NUL:
931                    /* if %p ends the string, oh well ignore it */
932                    continue;
933
934                default:
935                    s = "bogus %p";
936                    s_len = 8;
937                    prefix_char = NUL;
938                    break;
939                }
940                break;
941
942            case NUL:
943                /*
944                 * The last character of the format string was %.
945                 * We ignore it.
946                 */
947                continue;
948
949
950                /*
951                 * The default case is for unrecognized %'s.
952                 * We print %<char> to help the user identify what
953                 * option is not understood.
954                 * This is also useful in case the user wants to pass
955                 * the output of format_converter to another function
956                 * that understands some other %<char> (like syslog).
957                 * Note that we can't point s inside fmt because the
958                 * unknown <char> could be preceded by width etc.
959                 */
960            default:
961                char_buf[0] = '%';
962                char_buf[1] = *fmt;
963                s = char_buf;
964                s_len = 2;
965                pad_char = ' ';
966                break;
967            }
968
969            if (prefix_char != NUL && s != S_NULL && s != char_buf) {
970                *--s = prefix_char;
971                s_len++;
972            }
973
974            if (adjust_width && adjust == RIGHT && min_width > s_len) {
975                if (pad_char == '0' && prefix_char != NUL) {
976                    INS_CHAR(*s, sp, bep, cc);
977                    s++;
978                    s_len--;
979                    min_width--;
980                }
981                PAD(min_width, s_len, pad_char);
982            }
983
984            /*
985             * Print the string s.
986             */
987            for (i = s_len; i != 0; i--) {
988                INS_CHAR(*s, sp, bep, cc);
989                s++;
990            }
991
992            if (adjust_width && adjust == LEFT && min_width > s_len)
993                PAD(min_width, s_len, pad_char);
994        }
995        fmt++;
996    }
997    vbuff->curpos = sp;
998    return cc;
999}
1000
1001
1002static int snprintf_flush(ap_vformatter_buff *vbuff)
1003{
1004    /* if the buffer fills we have to abort immediately, there is no way
1005     * to "flush" a snprintf... there's nowhere to flush it to.
1006     */
1007    return -1;
1008}
1009
1010
1011int snprintf(char *buf, size_t len, const char *format,...)
1012{
1013    int cc;
1014    va_list ap;
1015    ap_vformatter_buff vbuff;
1016
1017    if (len == 0)
1018        return 0;
1019
1020    /* save one byte for nul terminator */
1021    vbuff.curpos = buf;
1022    vbuff.endpos = buf + len - 1;
1023    va_start(ap, format);
1024    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
1025    va_end(ap);
1026    *vbuff.curpos = '\0';
1027    return (cc == -1) ? len : cc;
1028}
1029
1030
1031int vsnprintf(char *buf, size_t len, const char *format, va_list ap)
1032{
1033    int cc;
1034    ap_vformatter_buff vbuff;
1035
1036    if (len == 0)
1037        return 0;
1038
1039    /* save one byte for nul terminator */
1040    vbuff.curpos = buf;
1041    vbuff.endpos = buf + len - 1;
1042    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
1043    *vbuff.curpos = '\0';
1044    return (cc == -1) ? len : cc;
1045}
Note: See TracBrowser for help on using the repository browser.