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