xref: /minix/external/bsd/top/dist/ap_snprintf.c (revision 08cbf5a0)
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * This code is based on, and used with the permission of, the
19  * SIO stdio-replacement strx_* functions by Panos Tsirigotis
20  * <panos@alumni.cs.colorado.edu> for xinetd.
21  */
22 
23 #include "config.h"
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <sys/types.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <math.h>
31 #include <netinet/in.h>
32 
33 #ifdef HAVE_LIMITS_H
34 #include <limits.h>
35 #endif
36 
37 typedef struct {
38     char *curpos;
39     char *endpos;
40 } ap_vformatter_buff;
41 
42 
43 #define API_EXPORT(type) type
44 #define API_EXPORT_NONSTD(type) type
45 
46 #define ap_isalnum(c) (isalnum(((unsigned char)(c))))
47 #define ap_isalpha(c) (isalpha(((unsigned char)(c))))
48 #define ap_iscntrl(c) (iscntrl(((unsigned char)(c))))
49 #define ap_isdigit(c) (isdigit(((unsigned char)(c))))
50 #define ap_isgraph(c) (isgraph(((unsigned char)(c))))
51 #define ap_islower(c) (islower(((unsigned char)(c))))
52 #define ap_isprint(c) (isprint(((unsigned char)(c))))
53 #define ap_ispunct(c) (ispunct(((unsigned char)(c))))
54 #define ap_isspace(c) (isspace(((unsigned char)(c))))
55 #define ap_isupper(c) (isupper(((unsigned char)(c))))
56 #define ap_isxdigit(c) (isxdigit(((unsigned char)(c))))
57 #define ap_tolower(c) (tolower(((unsigned char)(c))))
58 #define ap_toupper(c) (toupper(((unsigned char)(c))))
59 
60 
61 typedef enum {
62     NO = 0, YES = 1
63 } boolean_e;
64 
65 #ifndef FALSE
66 #define FALSE			0
67 #endif
68 #ifndef TRUE
69 #define TRUE			1
70 #endif
71 #ifndef AP_LONGEST_LONG
72 #define AP_LONGEST_LONG		long
73 #endif
74 #define NUL			'\0'
75 #define WIDE_INT		long
76 #define WIDEST_INT		AP_LONGEST_LONG
77 
78 typedef WIDE_INT wide_int;
79 typedef unsigned WIDE_INT u_wide_int;
80 typedef WIDEST_INT widest_int;
81 #ifdef __TANDEM
82 /* Although Tandem supports "long long" there is no unsigned variant. */
83 typedef unsigned long       u_widest_int;
84 #else
85 typedef unsigned WIDEST_INT u_widest_int;
86 #endif
87 typedef int bool_int;
88 
89 #define S_NULL			"(null)"
90 #define S_NULL_LEN		6
91 
92 #define FLOAT_DIGITS		6
93 #define EXPONENT_LENGTH		10
94 
95 /*
96  * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
97  *
98  * XXX: this is a magic number; do not decrease it
99  */
100 #define NUM_BUF_SIZE		512
101 
102 /*
103  * cvt.c - IEEE floating point formatting routines for FreeBSD
104  * from GNU libc-4.6.27.  Modified to be thread safe.
105  */
106 
107 /*
108  *    ap_ecvt converts to decimal
109  *      the number of digits is specified by ndigit
110  *      decpt is set to the position of the decimal point
111  *      sign is set to 0 for positive, 1 for negative
112  */
113 
114 #define	NDIG	80
115 
116 /* buf must have at least NDIG bytes */
117 static char *ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag, char *buf)
118 {
119     register int r2;
120     double fi, fj;
121     register char *p, *p1;
122 
123     if (ndigits >= NDIG - 1)
124 	ndigits = NDIG - 2;
125     r2 = 0;
126     *sign = 0;
127     p = &buf[0];
128     if (arg < 0) {
129 	*sign = 1;
130 	arg = -arg;
131     }
132     arg = modf(arg, &fi);
133     p1 = &buf[NDIG];
134     /*
135      * Do integer part
136      */
137     if (fi != 0) {
138 	p1 = &buf[NDIG];
139 	while (p1 > &buf[0] && fi != 0) {
140 	    fj = modf(fi / 10, &fi);
141 	    *--p1 = (int) ((fj + .03) * 10) + '0';
142 	    r2++;
143 	}
144 	while (p1 < &buf[NDIG])
145 	    *p++ = *p1++;
146     }
147     else if (arg > 0) {
148 	while ((fj = arg * 10) < 1) {
149 	    arg = fj;
150 	    r2--;
151 	}
152     }
153     p1 = &buf[ndigits];
154     if (eflag == 0)
155 	p1 += r2;
156     *decpt = r2;
157     if (p1 < &buf[0]) {
158 	buf[0] = '\0';
159 	return (buf);
160     }
161     while (p <= p1 && p < &buf[NDIG]) {
162 	arg *= 10;
163 	arg = modf(arg, &fj);
164 	*p++ = (int) fj + '0';
165     }
166     if (p1 >= &buf[NDIG]) {
167 	buf[NDIG - 1] = '\0';
168 	return (buf);
169     }
170     p = p1;
171     *p1 += 5;
172     while (*p1 > '9') {
173 	*p1 = '0';
174 	if (p1 > buf)
175 	    ++ * --p1;
176 	else {
177 	    *p1 = '1';
178 	    (*decpt)++;
179 	    if (eflag == 0) {
180 		if (p > buf)
181 		    *p = '0';
182 		p++;
183 	    }
184 	}
185     }
186     *p = '\0';
187     return (buf);
188 }
189 
190 static char *ap_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
191 {
192     return (ap_cvt(arg, ndigits, decpt, sign, 1, buf));
193 }
194 
195 static char *ap_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
196 {
197     return (ap_cvt(arg, ndigits, decpt, sign, 0, buf));
198 }
199 
200 /*
201  * ap_gcvt  - Floating output conversion to
202  * minimal length string
203  */
204 
205 static char *ap_gcvt(double number, int ndigit, char *buf, boolean_e altform)
206 {
207     int sign, decpt;
208     register char *p1, *p2;
209     register int i;
210     char buf1[NDIG];
211 
212     p1 = ap_ecvt(number, ndigit, &decpt, &sign, buf1);
213     p2 = buf;
214     if (sign)
215 	*p2++ = '-';
216     for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
217 	ndigit--;
218     if ((decpt >= 0 && decpt - ndigit > 4)
219 	|| (decpt < 0 && decpt < -3)) {		/* use E-style */
220 	decpt--;
221 	*p2++ = *p1++;
222 	*p2++ = '.';
223 	for (i = 1; i < ndigit; i++)
224 	    *p2++ = *p1++;
225 	*p2++ = 'e';
226 	if (decpt < 0) {
227 	    decpt = -decpt;
228 	    *p2++ = '-';
229 	}
230 	else
231 	    *p2++ = '+';
232 	if (decpt / 100 > 0)
233 	    *p2++ = decpt / 100 + '0';
234 	if (decpt / 10 > 0)
235 	    *p2++ = (decpt % 100) / 10 + '0';
236 	*p2++ = decpt % 10 + '0';
237     }
238     else {
239 	if (decpt <= 0) {
240 	    if (*p1 != '0')
241 		*p2++ = '.';
242 	    while (decpt < 0) {
243 		decpt++;
244 		*p2++ = '0';
245 	    }
246 	}
247 	for (i = 1; i <= ndigit; i++) {
248 	    *p2++ = *p1++;
249 	    if (i == decpt)
250 		*p2++ = '.';
251 	}
252 	if (ndigit < decpt) {
253 	    while (ndigit++ < decpt)
254 		*p2++ = '0';
255 	    *p2++ = '.';
256 	}
257     }
258     if (p2[-1] == '.' && !altform)
259 	p2--;
260     *p2 = '\0';
261     return (buf);
262 }
263 
264 /*
265  * The INS_CHAR macro inserts a character in the buffer and writes
266  * the buffer back to disk if necessary
267  * It uses the char pointers sp and bep:
268  *      sp points to the next available character in the buffer
269  *      bep points to the end-of-buffer+1
270  * While using this macro, note that the nextb pointer is NOT updated.
271  *
272  * NOTE: Evaluation of the c argument should not have any side-effects
273  */
274 #define INS_CHAR(c, sp, bep, cc)				\
275 	    {							\
276 		if (sp >= bep) {				\
277 		    vbuff->curpos = sp;                         \
278 		    if (flush_func(vbuff))			\
279 			return -1;				\
280 		    sp = vbuff->curpos;				\
281 		    bep = vbuff->endpos;			\
282 		} 						\
283 		*sp++ = (c);					\
284 		cc++; 						\
285 	    }
286 
287 #define NUM( c )			( c - '0' )
288 
289 #define STR_TO_DEC( str, num )		\
290     num = NUM( *str++ ) ;		\
291     while ( ap_isdigit( *str ) )		\
292     {					\
293 	num *= 10 ;			\
294 	num += NUM( *str++ ) ;		\
295     }
296 
297 /*
298  * This macro does zero padding so that the precision
299  * requirement is satisfied. The padding is done by
300  * adding '0's to the left of the string that is going
301  * to be printed. We don't allow precision to be large
302  * enough that we continue past the start of s.
303  *
304  * NOTE: this makes use of the magic info that s is
305  * always based on num_buf with a size of NUM_BUF_SIZE.
306  */
307 #define FIX_PRECISION( adjust, precision, s, s_len )	\
308     if ( adjust ) {					\
309         int p = precision < NUM_BUF_SIZE - 1 ? precision : NUM_BUF_SIZE - 1; \
310 	while ( s_len < p )				\
311 	{						\
312 	    *--s = '0' ;				\
313 	    s_len++ ;					\
314 	}						\
315     }
316 
317 /*
318  * Macro that does padding. The padding is done by printing
319  * the character ch.
320  */
321 #define PAD( width, len, ch )	do		\
322 	{					\
323 	    INS_CHAR( ch, sp, bep, cc ) ;	\
324 	    width-- ;				\
325 	}					\
326 	while ( width > len )
327 
328 /*
329  * Prefix the character ch to the string str
330  * Increase length
331  * Set the has_prefix flag
332  */
333 #define PREFIX( str, length, ch )	 *--str = ch ; length++ ; has_prefix = YES
334 
335 
336 /*
337  * Convert num to its decimal format.
338  * Return value:
339  *   - a pointer to a string containing the number (no sign)
340  *   - len contains the length of the string
341  *   - is_negative is set to TRUE or FALSE depending on the sign
342  *     of the number (always set to FALSE if is_unsigned is TRUE)
343  *
344  * The caller provides a buffer for the string: that is the buf_end argument
345  * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
346  * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
347  *
348  * Note: we have 2 versions. One is used when we need to use quads
349  * (conv_10_quad), the other when we don't (conv_10). We're assuming the
350  * latter is faster.
351  */
352 static char *conv_10(register wide_int num, register bool_int is_unsigned,
353 		     register bool_int *is_negative, char *buf_end,
354 		     register int *len)
355 {
356     register char *p = buf_end;
357     register u_wide_int magnitude;
358 
359     if (is_unsigned) {
360 	magnitude = (u_wide_int) num;
361 	*is_negative = FALSE;
362     }
363     else {
364 	*is_negative = (num < 0);
365 
366 	/*
367 	 * On a 2's complement machine, negating the most negative integer
368 	 * results in a number that cannot be represented as a signed integer.
369 	 * Here is what we do to obtain the number's magnitude:
370 	 *      a. add 1 to the number
371 	 *      b. negate it (becomes positive)
372 	 *      c. convert it to unsigned
373 	 *      d. add 1
374 	 */
375 	if (*is_negative) {
376 	    wide_int t = num + 1;
377 
378 	    magnitude = ((u_wide_int) -t) + 1;
379 	}
380 	else
381 	    magnitude = (u_wide_int) num;
382     }
383 
384     /*
385      * We use a do-while loop so that we write at least 1 digit
386      */
387     do {
388 	register u_wide_int new_magnitude = magnitude / 10;
389 
390 	*--p = (char) (magnitude - new_magnitude * 10 + '0');
391 	magnitude = new_magnitude;
392     }
393     while (magnitude);
394 
395     *len = buf_end - p;
396     return (p);
397 }
398 
399 static char *conv_10_quad(widest_int num, register bool_int is_unsigned,
400 		     register bool_int *is_negative, char *buf_end,
401 		     register int *len)
402 {
403     register char *p = buf_end;
404     u_widest_int magnitude;
405 
406     /*
407      * We see if we can use the faster non-quad version by checking the
408      * number against the largest long value it can be. If <=, we
409      * punt to the quicker version.
410      */
411     if ((num <= ULONG_MAX && is_unsigned) || (num <= LONG_MAX && !is_unsigned))
412     	return(conv_10( (wide_int)num, is_unsigned, is_negative,
413 	       buf_end, len));
414 
415     if (is_unsigned) {
416 	magnitude = (u_widest_int) num;
417 	*is_negative = FALSE;
418     }
419     else {
420 	*is_negative = (num < 0);
421 
422 	/*
423 	 * On a 2's complement machine, negating the most negative integer
424 	 * results in a number that cannot be represented as a signed integer.
425 	 * Here is what we do to obtain the number's magnitude:
426 	 *      a. add 1 to the number
427 	 *      b. negate it (becomes positive)
428 	 *      c. convert it to unsigned
429 	 *      d. add 1
430 	 */
431 	if (*is_negative) {
432 	    widest_int t = num + 1;
433 
434 	    magnitude = ((u_widest_int) -t) + 1;
435 	}
436 	else
437 	    magnitude = (u_widest_int) num;
438     }
439 
440     /*
441      * We use a do-while loop so that we write at least 1 digit
442      */
443     do {
444 	u_widest_int new_magnitude = magnitude / 10;
445 
446 	*--p = (char) (magnitude - new_magnitude * 10 + '0');
447 	magnitude = new_magnitude;
448     }
449     while (magnitude);
450 
451     *len = buf_end - p;
452     return (p);
453 }
454 
455 
456 
457 static char *conv_in_addr(struct in_addr *ia, char *buf_end, int *len)
458 {
459     unsigned addr = ntohl(ia->s_addr);
460     char *p = buf_end;
461     bool_int is_negative;
462     int sub_len;
463 
464     p = conv_10((addr & 0x000000FF)      , TRUE, &is_negative, p, &sub_len);
465     *--p = '.';
466     p = conv_10((addr & 0x0000FF00) >>  8, TRUE, &is_negative, p, &sub_len);
467     *--p = '.';
468     p = conv_10((addr & 0x00FF0000) >> 16, TRUE, &is_negative, p, &sub_len);
469     *--p = '.';
470     p = conv_10((addr & 0xFF000000) >> 24, TRUE, &is_negative, p, &sub_len);
471 
472     *len = buf_end - p;
473     return (p);
474 }
475 
476 
477 
478 static char *conv_sockaddr_in(struct sockaddr_in *si, char *buf_end, int *len)
479 {
480     char *p = buf_end;
481     bool_int is_negative;
482     int sub_len;
483 
484     p = conv_10(ntohs(si->sin_port), TRUE, &is_negative, p, &sub_len);
485     *--p = ':';
486     p = conv_in_addr(&si->sin_addr, p, &sub_len);
487 
488     *len = buf_end - p;
489     return (p);
490 }
491 
492 
493 
494 /*
495  * Convert a floating point number to a string formats 'f', 'e' or 'E'.
496  * The result is placed in buf, and len denotes the length of the string
497  * The sign is returned in the is_negative argument (and is not placed
498  * in buf).
499  */
500 static char *conv_fp(register char format, register double num,
501     boolean_e add_dp, int precision, bool_int *is_negative,
502     char *buf, int *len)
503 {
504     register char *s = buf;
505     register char *p;
506     int decimal_point;
507     char buf1[NDIG];
508 
509     if (format == 'f')
510 	p = ap_fcvt(num, precision, &decimal_point, is_negative, buf1);
511     else			/* either e or E format */
512 	p = ap_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
513 
514     /*
515      * Check for Infinity and NaN
516      */
517     if (ap_isalpha(*p)) {
518 	*len = strlen(strcpy(buf, p));
519 	*is_negative = FALSE;
520 	return (buf);
521     }
522 
523     if (format == 'f') {
524 	if (decimal_point <= 0) {
525 	    *s++ = '0';
526 	    if (precision > 0) {
527 		*s++ = '.';
528 		while (decimal_point++ < 0)
529 		    *s++ = '0';
530 	    }
531 	    else if (add_dp)
532 		*s++ = '.';
533 	}
534 	else {
535 	    while (decimal_point-- > 0)
536 		*s++ = *p++;
537 	    if (precision > 0 || add_dp)
538 		*s++ = '.';
539 	}
540     }
541     else {
542 	*s++ = *p++;
543 	if (precision > 0 || add_dp)
544 	    *s++ = '.';
545     }
546 
547     /*
548      * copy the rest of p, the NUL is NOT copied
549      */
550     while (*p)
551 	*s++ = *p++;
552 
553     if (format != 'f') {
554 	char temp[EXPONENT_LENGTH];	/* for exponent conversion */
555 	int t_len;
556 	bool_int exponent_is_negative;
557 
558 	*s++ = format;		/* either e or E */
559 	decimal_point--;
560 	if (decimal_point != 0) {
561 	    p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative,
562 			&temp[EXPONENT_LENGTH], &t_len);
563 	    *s++ = exponent_is_negative ? '-' : '+';
564 
565 	    /*
566 	     * Make sure the exponent has at least 2 digits
567 	     */
568 	    if (t_len == 1)
569 		*s++ = '0';
570 	    while (t_len--)
571 		*s++ = *p++;
572 	}
573 	else {
574 	    *s++ = '+';
575 	    *s++ = '0';
576 	    *s++ = '0';
577 	}
578     }
579 
580     *len = s - buf;
581     return (buf);
582 }
583 
584 
585 /*
586  * Convert num to a base X number where X is a power of 2. nbits determines X.
587  * For example, if nbits is 3, we do base 8 conversion
588  * Return value:
589  *      a pointer to a string containing the number
590  *
591  * The caller provides a buffer for the string: that is the buf_end argument
592  * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
593  * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
594  *
595  * As with conv_10, we have a faster version which is used when
596  * the number isn't quad size.
597  */
598 static char *conv_p2(register u_wide_int num, register int nbits,
599 		     char format, char *buf_end, register int *len)
600 {
601     register int mask = (1 << nbits) - 1;
602     register char *p = buf_end;
603     static const char low_digits[] = "0123456789abcdef";
604     static const char upper_digits[] = "0123456789ABCDEF";
605     register const char *digits = (format == 'X') ? upper_digits : low_digits;
606 
607     do {
608 	*--p = digits[num & mask];
609 	num >>= nbits;
610     }
611     while (num);
612 
613     *len = buf_end - p;
614     return (p);
615 }
616 
617 static char *conv_p2_quad(u_widest_int num, register int nbits,
618 		     char format, char *buf_end, register int *len)
619 {
620     register int mask = (1 << nbits) - 1;
621     register char *p = buf_end;
622     static const char low_digits[] = "0123456789abcdef";
623     static const char upper_digits[] = "0123456789ABCDEF";
624     register const char *digits = (format == 'X') ? upper_digits : low_digits;
625 
626     if (num <= ULONG_MAX)
627     	return(conv_p2( (u_wide_int)num, nbits, format, buf_end, len));
628 
629     do {
630 	*--p = digits[num & mask];
631 	num >>= nbits;
632     }
633     while (num);
634 
635     *len = buf_end - p;
636     return (p);
637 }
638 
639 
640 /*
641  * Do format conversion placing the output in buffer
642  */
643 API_EXPORT(int) ap_vformatter(int (*flush_func)(ap_vformatter_buff *),
644     ap_vformatter_buff *vbuff, const char *fmt, va_list ap)
645 {
646     register char *sp;
647     register char *bep;
648     register int cc = 0;
649     register int i;
650 
651     register char *s = NULL;
652     char *q;
653     int s_len;
654 
655     register int min_width = 0;
656     int precision = 0;
657     enum {
658 	LEFT, RIGHT
659     } adjust;
660     char pad_char;
661     char prefix_char;
662 
663     double fp_num;
664     widest_int i_quad = (widest_int) 0;
665     u_widest_int ui_quad;
666     wide_int i_num = (wide_int) 0;
667     u_wide_int ui_num;
668 
669     char num_buf[NUM_BUF_SIZE];
670     char char_buf[2];		/* for printing %% and %<unknown> */
671 
672     enum var_type_enum {
673     	IS_QUAD, IS_LONG, IS_SHORT, IS_INT
674     };
675     enum var_type_enum var_type = IS_INT;
676 
677     /*
678      * Flag variables
679      */
680     boolean_e alternate_form;
681     boolean_e print_sign;
682     boolean_e print_blank;
683     boolean_e adjust_precision;
684     boolean_e adjust_width;
685     bool_int is_negative;
686 
687     sp = vbuff->curpos;
688     bep = vbuff->endpos;
689 
690     while (*fmt) {
691 	if (*fmt != '%') {
692 	    INS_CHAR(*fmt, sp, bep, cc);
693 	}
694 	else {
695 	    /*
696 	     * Default variable settings
697 	     */
698 	    adjust = RIGHT;
699 	    alternate_form = print_sign = print_blank = NO;
700 	    pad_char = ' ';
701 	    prefix_char = NUL;
702 
703 	    fmt++;
704 
705 	    /*
706 	     * Try to avoid checking for flags, width or precision
707 	     */
708 	    if (!ap_islower(*fmt)) {
709 		/*
710 		 * Recognize flags: -, #, BLANK, +
711 		 */
712 		for (;; fmt++) {
713 		    if (*fmt == '-')
714 			adjust = LEFT;
715 		    else if (*fmt == '+')
716 			print_sign = YES;
717 		    else if (*fmt == '#')
718 			alternate_form = YES;
719 		    else if (*fmt == ' ')
720 			print_blank = YES;
721 		    else if (*fmt == '0')
722 			pad_char = '0';
723 		    else
724 			break;
725 		}
726 
727 		/*
728 		 * Check if a width was specified
729 		 */
730 		if (ap_isdigit(*fmt)) {
731 		    STR_TO_DEC(fmt, min_width);
732 		    adjust_width = YES;
733 		}
734 		else if (*fmt == '*') {
735 		    min_width = va_arg(ap, int);
736 		    fmt++;
737 		    adjust_width = YES;
738 		    if (min_width < 0) {
739 			adjust = LEFT;
740 			min_width = -min_width;
741 		    }
742 		}
743 		else
744 		    adjust_width = NO;
745 
746 		/*
747 		 * Check if a precision was specified
748 		 */
749 		if (*fmt == '.') {
750 		    adjust_precision = YES;
751 		    fmt++;
752 		    if (ap_isdigit(*fmt)) {
753 			STR_TO_DEC(fmt, precision);
754 		    }
755 		    else if (*fmt == '*') {
756 			precision = va_arg(ap, int);
757 			fmt++;
758 			if (precision < 0)
759 			    precision = 0;
760 		    }
761 		    else
762 			precision = 0;
763 		}
764 		else
765 		    adjust_precision = NO;
766 	    }
767 	    else
768 		adjust_precision = adjust_width = NO;
769 
770 	    /*
771 	     * Modifier check
772 	     */
773 	    if (*fmt == 'q') {
774 		var_type = IS_QUAD;
775 		fmt++;
776 	    }
777 	    else if (*fmt == 'l') {
778 		var_type = IS_LONG;
779 		fmt++;
780 	    }
781 	    else if (*fmt == 'h') {
782 		var_type = IS_SHORT;
783 		fmt++;
784 	    }
785 	    else {
786 		var_type = IS_INT;
787 	    }
788 
789 	    /*
790 	     * Argument extraction and printing.
791 	     * First we determine the argument type.
792 	     * Then, we convert the argument to a string.
793 	     * On exit from the switch, s points to the string that
794 	     * must be printed, s_len has the length of the string
795 	     * The precision requirements, if any, are reflected in s_len.
796 	     *
797 	     * NOTE: pad_char may be set to '0' because of the 0 flag.
798 	     *   It is reset to ' ' by non-numeric formats
799 	     */
800 	    switch (*fmt) {
801 	    case 'u':
802 	    	if (var_type == IS_QUAD) {
803 		    i_quad = va_arg(ap, u_widest_int);
804 		    s = conv_10_quad(i_quad, 1, &is_negative,
805 			    &num_buf[NUM_BUF_SIZE], &s_len);
806 		}
807 		else {
808 		    if (var_type == IS_LONG)
809 			i_num = (wide_int) va_arg(ap, u_wide_int);
810 		    else if (var_type == IS_SHORT)
811 			i_num = (wide_int) (unsigned short) va_arg(ap, unsigned int);
812 		    else
813 			i_num = (wide_int) va_arg(ap, unsigned int);
814 		    s = conv_10(i_num, 1, &is_negative,
815 			    &num_buf[NUM_BUF_SIZE], &s_len);
816 		}
817 		FIX_PRECISION(adjust_precision, precision, s, s_len);
818 		break;
819 
820 	    case 'd':
821 	    case 'i':
822 	    	if (var_type == IS_QUAD) {
823 		    i_quad = va_arg(ap, widest_int);
824 		    s = conv_10_quad(i_quad, 0, &is_negative,
825 			    &num_buf[NUM_BUF_SIZE], &s_len);
826 		}
827 		else {
828 		    if (var_type == IS_LONG)
829 			i_num = (wide_int) va_arg(ap, wide_int);
830 		    else if (var_type == IS_SHORT)
831 			i_num = (wide_int) (short) va_arg(ap, int);
832 		    else
833 			i_num = (wide_int) va_arg(ap, int);
834 		    s = conv_10(i_num, 0, &is_negative,
835 			    &num_buf[NUM_BUF_SIZE], &s_len);
836 		}
837 		FIX_PRECISION(adjust_precision, precision, s, s_len);
838 
839 		if (is_negative)
840 		    prefix_char = '-';
841 		else if (print_sign)
842 		    prefix_char = '+';
843 		else if (print_blank)
844 		    prefix_char = ' ';
845 		break;
846 
847 
848 	    case 'o':
849 		if (var_type == IS_QUAD) {
850 		    ui_quad = va_arg(ap, u_widest_int);
851 		    s = conv_p2_quad(ui_quad, 3, *fmt,
852 			    &num_buf[NUM_BUF_SIZE], &s_len);
853 		}
854 		else {
855 		    if (var_type == IS_LONG)
856 			ui_num = (u_wide_int) va_arg(ap, u_wide_int);
857 		    else if (var_type == IS_SHORT)
858 			ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int);
859 		    else
860 			ui_num = (u_wide_int) va_arg(ap, unsigned int);
861 		    s = conv_p2(ui_num, 3, *fmt,
862 			    &num_buf[NUM_BUF_SIZE], &s_len);
863 		}
864 		FIX_PRECISION(adjust_precision, precision, s, s_len);
865 		if (alternate_form && *s != '0') {
866 		    *--s = '0';
867 		    s_len++;
868 		}
869 		break;
870 
871 
872 	    case 'x':
873 	    case 'X':
874 		if (var_type == IS_QUAD) {
875 		    ui_quad = va_arg(ap, u_widest_int);
876 		    s = conv_p2_quad(ui_quad, 4, *fmt,
877 			    &num_buf[NUM_BUF_SIZE], &s_len);
878 		}
879 		else {
880 		    if (var_type == IS_LONG)
881 			ui_num = (u_wide_int) va_arg(ap, u_wide_int);
882 		    else if (var_type == IS_SHORT)
883 			ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int);
884 		    else
885 			ui_num = (u_wide_int) va_arg(ap, unsigned int);
886 		    s = conv_p2(ui_num, 4, *fmt,
887 			    &num_buf[NUM_BUF_SIZE], &s_len);
888 		}
889 		FIX_PRECISION(adjust_precision, precision, s, s_len);
890 		if (alternate_form && i_num != 0) {
891 		    *--s = *fmt;	/* 'x' or 'X' */
892 		    *--s = '0';
893 		    s_len += 2;
894 		}
895 		break;
896 
897 
898 	    case 's':
899 		s = va_arg(ap, char *);
900 		if (s != NULL) {
901 		    s_len = strlen(s);
902 		    if (adjust_precision && precision < s_len)
903 			s_len = precision;
904 		}
905 		else {
906 		    s = S_NULL;
907 		    s_len = S_NULL_LEN;
908 		}
909 		pad_char = ' ';
910 		break;
911 
912 
913 	    case 'f':
914 	    case 'e':
915 	    case 'E':
916 		fp_num = va_arg(ap, double);
917 		/*
918 		 * * We use &num_buf[ 1 ], so that we have room for the sign
919 		 */
920 #ifdef HAVE_ISNAN
921 		if (isnan(fp_num)) {
922 		    s = "nan";
923 		    s_len = 3;
924 		}
925 		else
926 #endif
927 #ifdef HAVE_ISINF
928 		if (isinf(fp_num)) {
929 		    s = "inf";
930 		    s_len = 3;
931 		}
932 		else
933 #endif
934 		{
935 		    s = conv_fp(*fmt, fp_num, alternate_form,
936 			    (adjust_precision == NO) ? FLOAT_DIGITS : precision,
937 				&is_negative, &num_buf[1], &s_len);
938 		    if (is_negative)
939 			prefix_char = '-';
940 		    else if (print_sign)
941 			prefix_char = '+';
942 		    else if (print_blank)
943 			prefix_char = ' ';
944 		}
945 	        break;
946 
947 
948 	    case 'g':
949 	    case 'G':
950 		if (adjust_precision == NO)
951 		    precision = FLOAT_DIGITS;
952 		else if (precision == 0)
953 		    precision = 1;
954 		/*
955 		 * * We use &num_buf[ 1 ], so that we have room for the sign
956 		 */
957 		s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1],
958 		            alternate_form);
959 		if (*s == '-')
960 		    prefix_char = *s++;
961 		else if (print_sign)
962 		    prefix_char = '+';
963 		else if (print_blank)
964 		    prefix_char = ' ';
965 
966 		s_len = strlen(s);
967 
968 		if (alternate_form && (q = strchr(s, '.')) == NULL) {
969 		    s[s_len++] = '.';
970 		    s[s_len] = '\0'; /* delimit for following strchr() */
971 		}
972 		if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
973 		    *q = 'E';
974 		break;
975 
976 
977 	    case 'c':
978 		char_buf[0] = (char) (va_arg(ap, int));
979 		s = &char_buf[0];
980 		s_len = 1;
981 		pad_char = ' ';
982 		break;
983 
984 
985 	    case '%':
986 		char_buf[0] = '%';
987 		s = &char_buf[0];
988 		s_len = 1;
989 		pad_char = ' ';
990 		break;
991 
992 
993 	    case 'n':
994 	    	if (var_type == IS_QUAD)
995 		    *(va_arg(ap, widest_int *)) = cc;
996 		else if (var_type == IS_LONG)
997 		    *(va_arg(ap, long *)) = cc;
998 		else if (var_type == IS_SHORT)
999 		    *(va_arg(ap, short *)) = cc;
1000 		else
1001 		    *(va_arg(ap, int *)) = cc;
1002 		break;
1003 
1004 		/*
1005 		 * This is where we extend the printf format, with a second
1006 		 * type specifier
1007 		 */
1008 	    case 'p':
1009 		switch(*++fmt) {
1010 		    /*
1011 		     * If the pointer size is equal to or smaller than the size
1012 		     * of the largest unsigned int, we convert the pointer to a
1013 		     * hex number, otherwise we print "%p" to indicate that we
1014 		     * don't handle "%p".
1015 		     */
1016 		case 'p':
1017 #ifdef AP_VOID_P_IS_QUAD
1018 		    if (sizeof(void *) <= sizeof(u_widest_int)) {
1019 		    	ui_quad = (u_widest_int) va_arg(ap, void *);
1020 			s = conv_p2_quad(ui_quad, 4, 'x',
1021 				&num_buf[NUM_BUF_SIZE], &s_len);
1022 		    }
1023 #else
1024 		    if (sizeof(void *) <= sizeof(u_wide_int)) {
1025 		    	ui_num = (u_wide_int) va_arg(ap, void *);
1026 			s = conv_p2(ui_num, 4, 'x',
1027 				&num_buf[NUM_BUF_SIZE], &s_len);
1028 		    }
1029 #endif
1030 		    else {
1031 			s = "%p";
1032 			s_len = 2;
1033 			prefix_char = NUL;
1034 		    }
1035 		    pad_char = ' ';
1036 		    break;
1037 
1038 		    /* print a struct sockaddr_in as a.b.c.d:port */
1039 		case 'I':
1040 		    {
1041 			struct sockaddr_in *si;
1042 
1043 			si = va_arg(ap, struct sockaddr_in *);
1044 			if (si != NULL) {
1045 			    s = conv_sockaddr_in(si, &num_buf[NUM_BUF_SIZE], &s_len);
1046 			    if (adjust_precision && precision < s_len)
1047 				s_len = precision;
1048 			}
1049 			else {
1050 			    s = S_NULL;
1051 			    s_len = S_NULL_LEN;
1052 			}
1053 			pad_char = ' ';
1054 		    }
1055 		    break;
1056 
1057 		    /* print a struct in_addr as a.b.c.d */
1058 		case 'A':
1059 		    {
1060 			struct in_addr *ia;
1061 
1062 			ia = va_arg(ap, struct in_addr *);
1063 			if (ia != NULL) {
1064 			    s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len);
1065 			    if (adjust_precision && precision < s_len)
1066 				s_len = precision;
1067 			}
1068 			else {
1069 			    s = S_NULL;
1070 			    s_len = S_NULL_LEN;
1071 			}
1072 			pad_char = ' ';
1073 		    }
1074 		    break;
1075 
1076 		case NUL:
1077 		    /* if %p ends the string, oh well ignore it */
1078 		    continue;
1079 
1080 		default:
1081 		    s = "bogus %p";
1082 		    s_len = 8;
1083 		    prefix_char = NUL;
1084 		    break;
1085 		}
1086 		break;
1087 
1088 	    case NUL:
1089 		/*
1090 		 * The last character of the format string was %.
1091 		 * We ignore it.
1092 		 */
1093 		continue;
1094 
1095 
1096 		/*
1097 		 * The default case is for unrecognized %'s.
1098 		 * We print %<char> to help the user identify what
1099 		 * option is not understood.
1100 		 * This is also useful in case the user wants to pass
1101 		 * the output of format_converter to another function
1102 		 * that understands some other %<char> (like syslog).
1103 		 * Note that we can't point s inside fmt because the
1104 		 * unknown <char> could be preceded by width etc.
1105 		 */
1106 	    default:
1107 		char_buf[0] = '%';
1108 		char_buf[1] = *fmt;
1109 		s = char_buf;
1110 		s_len = 2;
1111 		pad_char = ' ';
1112 		break;
1113 	    }
1114 
1115 	    if (prefix_char != NUL && s != S_NULL && s != char_buf) {
1116 		*--s = prefix_char;
1117 		s_len++;
1118 	    }
1119 
1120 	    if (adjust_width && adjust == RIGHT && min_width > s_len) {
1121 		if (pad_char == '0' && prefix_char != NUL) {
1122 		    INS_CHAR(*s, sp, bep, cc);
1123 		    s++;
1124 		    s_len--;
1125 		    min_width--;
1126 		}
1127 		PAD(min_width, s_len, pad_char);
1128 	    }
1129 
1130 	    /*
1131 	     * Print the string s.
1132 	     */
1133 	    for (i = s_len; i != 0; i--) {
1134 		INS_CHAR(*s, sp, bep, cc);
1135 		s++;
1136 	    }
1137 
1138 	    if (adjust_width && adjust == LEFT && min_width > s_len)
1139 		PAD(min_width, s_len, pad_char);
1140 	}
1141 	fmt++;
1142     }
1143     vbuff->curpos = sp;
1144 
1145     return cc;
1146 }
1147 
1148 
1149 static int snprintf_flush(ap_vformatter_buff *vbuff)
1150 {
1151     /* if the buffer fills we have to abort immediately, there is no way
1152      * to "flush" an ap_snprintf... there's nowhere to flush it to.
1153      */
1154     return -1;
1155 }
1156 
1157 
1158 API_EXPORT_NONSTD(int) ap_snprintf(char *buf, size_t len, const char *format,...)
1159 {
1160     int cc;
1161     va_list ap;
1162     ap_vformatter_buff vbuff;
1163 
1164     if (len == 0)
1165 	return 0;
1166 
1167     /* save one byte for nul terminator */
1168     vbuff.curpos = buf;
1169     vbuff.endpos = buf + len - 1;
1170     va_start(ap, format);
1171     cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
1172     va_end(ap);
1173     *vbuff.curpos = '\0';
1174     return (cc == -1) ? len : cc;
1175 }
1176 
1177 
1178 API_EXPORT(int) ap_vsnprintf(char *buf, size_t len, const char *format,
1179 			     va_list ap)
1180 {
1181     int cc;
1182     ap_vformatter_buff vbuff;
1183 
1184     if (len == 0)
1185 	return 0;
1186 
1187     /* save one byte for nul terminator */
1188     vbuff.curpos = buf;
1189     vbuff.endpos = buf + len - 1;
1190     cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
1191     *vbuff.curpos = '\0';
1192     return (cc == -1) ? len : cc;
1193 }
1194