Mercurial > hg
annotate mcabber/libjabber/snprintf.c @ 798:bbaab5692b0e
hgignore: Ignore hgcset.h
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Sun, 09 Apr 2006 10:15:44 +0200 |
parents | c3ae9251c197 |
children |
rev | line source |
---|---|
25 | 1 /* ==================================================================== |
2 * Copyright (c) 1995-1998 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 | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
9 * notice, this list of conditions and the following disclaimer. |
25 | 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 | |
417
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
56 /** |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
57 * @file snprintf.c |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
58 * @brief implement snprintf if not present in the libc |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
59 * |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
60 * snprintf is not implemented by all libc implementations, this file implements this |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
61 * function, if it is not already present. You should not call any of the functions |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
62 * in this file directly! |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
63 */ |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
64 |
25 | 65 #include <libxode.h> |
66 | |
67 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) | |
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 | |
77 | |
78 #ifdef HAVE_GCVT | |
79 | |
80 #define ap_ecvt ecvt | |
81 #define ap_fcvt fcvt | |
82 #define ap_gcvt gcvt | |
83 | |
84 #else | |
85 | |
86 /* | |
87 * cvt.c - IEEE floating point formatting routines for FreeBSD | |
88 * from GNU libc-4.6.27 | |
89 */ | |
90 | |
91 /* | |
92 * ap_ecvt converts to decimal | |
93 * the number of digits is specified by ndigit | |
94 * decpt is set to the position of the decimal point | |
95 * sign is set to 0 for positive, 1 for negative | |
96 */ | |
97 | |
98 #define NDIG 80 | |
99 | |
100 static char * | |
101 ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag) | |
102 { | |
103 register int r2; | |
104 double fi, fj; | |
105 register char *p, *p1; | |
106 static char buf[NDIG]; | |
107 | |
108 if (ndigits >= NDIG - 1) | |
109 ndigits = NDIG - 2; | |
110 r2 = 0; | |
111 *sign = 0; | |
112 p = &buf[0]; | |
113 if (arg < 0) { | |
114 *sign = 1; | |
115 arg = -arg; | |
116 } | |
117 arg = modf(arg, &fi); | |
118 p1 = &buf[NDIG]; | |
119 /* | |
120 * Do integer part | |
121 */ | |
122 if (fi != 0) { | |
123 p1 = &buf[NDIG]; | |
124 while (fi != 0) { | |
125 fj = modf(fi / 10, &fi); | |
126 *--p1 = (int) ((fj + .03) * 10) + '0'; | |
127 r2++; | |
128 } | |
129 while (p1 < &buf[NDIG]) | |
130 *p++ = *p1++; | |
131 } else if (arg > 0) { | |
132 while ((fj = arg * 10) < 1) { | |
133 arg = fj; | |
134 r2--; | |
135 } | |
136 } | |
137 p1 = &buf[ndigits]; | |
138 if (eflag == 0) | |
139 p1 += r2; | |
140 *decpt = r2; | |
141 if (p1 < &buf[0]) { | |
142 buf[0] = '\0'; | |
143 return (buf); | |
144 } | |
145 while (p <= p1 && p < &buf[NDIG]) { | |
146 arg *= 10; | |
147 arg = modf(arg, &fj); | |
148 *p++ = (int) fj + '0'; | |
149 } | |
150 if (p1 >= &buf[NDIG]) { | |
151 buf[NDIG - 1] = '\0'; | |
152 return (buf); | |
153 } | |
154 p = p1; | |
155 *p1 += 5; | |
156 while (*p1 > '9') { | |
157 *p1 = '0'; | |
158 if (p1 > buf) | |
159 ++ * --p1; | |
160 else { | |
161 *p1 = '1'; | |
162 (*decpt)++; | |
163 if (eflag == 0) { | |
164 if (p > buf) | |
165 *p = '0'; | |
166 p++; | |
167 } | |
168 } | |
169 } | |
170 *p = '\0'; | |
171 return (buf); | |
172 } | |
173 | |
174 static char * | |
175 ap_ecvt(double arg, int ndigits, int *decpt, int *sign) | |
176 { | |
177 return (ap_cvt(arg, ndigits, decpt, sign, 1)); | |
178 } | |
179 | |
180 static char * | |
181 ap_fcvt(double arg, int ndigits, int *decpt, int *sign) | |
182 { | |
183 return (ap_cvt(arg, ndigits, decpt, sign, 0)); | |
184 } | |
185 | |
186 /* | |
187 * ap_gcvt - Floating output conversion to | |
188 * minimal length string | |
189 */ | |
190 | |
191 static char * | |
192 ap_gcvt(double number, int ndigit, char *buf) | |
193 { | |
194 int sign, decpt; | |
195 register char *p1, *p2; | |
196 int i; | |
197 | |
198 p1 = ap_ecvt(number, ndigit, &decpt, &sign); | |
199 p2 = buf; | |
200 if (sign) | |
201 *p2++ = '-'; | |
202 for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) | |
203 ndigit--; | |
204 if ((decpt >= 0 && decpt - ndigit > 4) | |
205 || (decpt < 0 && decpt < -3)) { /* use E-style */ | |
206 decpt--; | |
207 *p2++ = *p1++; | |
208 *p2++ = '.'; | |
209 for (i = 1; i < ndigit; i++) | |
210 *p2++ = *p1++; | |
211 *p2++ = 'e'; | |
212 if (decpt < 0) { | |
213 decpt = -decpt; | |
214 *p2++ = '-'; | |
215 } else | |
216 *p2++ = '+'; | |
217 if (decpt / 100 > 0) | |
218 *p2++ = decpt / 100 + '0'; | |
219 if (decpt / 10 > 0) | |
220 *p2++ = (decpt % 100) / 10 + '0'; | |
221 *p2++ = decpt % 10 + '0'; | |
222 } else { | |
223 if (decpt <= 0) { | |
224 if (*p1 != '0') | |
225 *p2++ = '.'; | |
226 while (decpt < 0) { | |
227 decpt++; | |
228 *p2++ = '0'; | |
229 } | |
230 } | |
231 for (i = 1; i <= ndigit; i++) { | |
232 *p2++ = *p1++; | |
233 if (i == decpt) | |
234 *p2++ = '.'; | |
235 } | |
236 if (ndigit < decpt) { | |
237 while (ndigit++ < decpt) | |
238 *p2++ = '0'; | |
239 *p2++ = '.'; | |
240 } | |
241 } | |
242 if (p2[-1] == '.') | |
243 p2--; | |
244 *p2 = '\0'; | |
245 return (buf); | |
246 } | |
247 | |
248 #endif /* HAVE_CVT */ | |
249 | |
250 typedef enum { | |
251 NO = 0, YES = 1 | |
252 } boolean_e; | |
253 | |
417
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
254 #ifndef FALSE |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
255 # define FALSE 0 |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
256 #endif |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
257 #ifndef TRUE |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
258 # define TRUE 1 |
c3ae9251c197
Sync libjabber with upstream
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
259 #endif |
25 | 260 #define NUL '\0' |
261 #define INT_NULL ((int *)0) | |
262 #define WIDE_INT long | |
263 | |
264 typedef WIDE_INT wide_int; | |
265 typedef unsigned WIDE_INT u_wide_int; | |
266 typedef int bool_int; | |
267 | |
268 #define S_NULL "(null)" | |
269 #define S_NULL_LEN 6 | |
270 | |
271 #define FLOAT_DIGITS 6 | |
272 #define EXPONENT_LENGTH 10 | |
273 | |
274 /* | |
275 * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions | |
276 * | |
277 * XXX: this is a magic number; do not decrease it | |
278 */ | |
279 #define NUM_BUF_SIZE 512 | |
280 | |
281 | |
282 /* | |
283 * Descriptor for buffer area | |
284 */ | |
285 struct buf_area { | |
286 char *buf_end; | |
287 char *nextb; /* pointer to next byte to read/write */ | |
288 }; | |
289 | |
290 typedef struct buf_area buffy; | |
291 | |
292 /* | |
293 * The INS_CHAR macro inserts a character in the buffer and writes | |
294 * the buffer back to disk if necessary | |
295 * It uses the char pointers sp and bep: | |
296 * sp points to the next available character in the buffer | |
297 * bep points to the end-of-buffer+1 | |
298 * While using this macro, note that the nextb pointer is NOT updated. | |
299 * | |
300 * NOTE: Evaluation of the c argument should not have any side-effects | |
301 */ | |
302 #define INS_CHAR( c, sp, bep, cc ) \ | |
303 { \ | |
304 if ( sp < bep ) \ | |
305 { \ | |
306 *sp++ = c ; \ | |
307 cc++ ; \ | |
308 } \ | |
309 } | |
310 | |
311 #define NUM( c ) ( c - '0' ) | |
312 | |
313 #define STR_TO_DEC( str, num ) \ | |
314 num = NUM( *str++ ) ; \ | |
315 while ( isdigit((int)*str ) ) \ | |
316 { \ | |
317 num *= 10 ; \ | |
318 num += NUM( *str++ ) ; \ | |
319 } | |
320 | |
321 /* | |
322 * This macro does zero padding so that the precision | |
323 * requirement is satisfied. The padding is done by | |
324 * adding '0's to the left of the string that is going | |
325 * to be printed. | |
326 */ | |
327 #define FIX_PRECISION( adjust, precision, s, s_len ) \ | |
328 if ( adjust ) \ | |
329 while ( s_len < precision ) \ | |
330 { \ | |
331 *--s = '0' ; \ | |
332 s_len++ ; \ | |
333 } | |
334 | |
335 /* | |
336 * Macro that does padding. The padding is done by printing | |
337 * the character ch. | |
338 */ | |
339 #define PAD( width, len, ch ) do \ | |
340 { \ | |
341 INS_CHAR( ch, sp, bep, cc ) ; \ | |
342 width-- ; \ | |
343 } \ | |
344 while ( width > len ) | |
345 | |
346 /* | |
347 * Prefix the character ch to the string str | |
348 * Increase length | |
349 * Set the has_prefix flag | |
350 */ | |
351 #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES | |
352 | |
353 | |
354 /* | |
355 * Convert num to its decimal format. | |
356 * Return value: | |
357 * - a pointer to a string containing the number (no sign) | |
358 * - len contains the length of the string | |
359 * - is_negative is set to TRUE or FALSE depending on the sign | |
360 * of the number (always set to FALSE if is_unsigned is TRUE) | |
361 * | |
362 * The caller provides a buffer for the string: that is the buf_end argument | |
363 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer | |
364 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) | |
365 */ | |
366 static char * | |
367 conv_10(register wide_int num, register bool_int is_unsigned, | |
368 register bool_int * is_negative, char *buf_end, register int *len) | |
369 { | |
370 register char *p = buf_end; | |
371 register u_wide_int magnitude; | |
372 | |
373 if (is_unsigned) { | |
374 magnitude = (u_wide_int) num; | |
375 *is_negative = FALSE; | |
376 } else { | |
377 *is_negative = (num < 0); | |
378 | |
379 /* | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
380 * On a 2's complement machine, negating the most negative integer |
25 | 381 * results in a number that cannot be represented as a signed integer. |
382 * Here is what we do to obtain the number's magnitude: | |
383 * a. add 1 to the number | |
384 * b. negate it (becomes positive) | |
385 * c. convert it to unsigned | |
386 * d. add 1 | |
387 */ | |
388 if (*is_negative) { | |
389 wide_int t = num + 1; | |
390 | |
391 magnitude = ((u_wide_int) - t) + 1; | |
392 } else | |
393 magnitude = (u_wide_int) num; | |
394 } | |
395 | |
396 /* | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
397 * We use a do-while loop so that we write at least 1 digit |
25 | 398 */ |
399 do { | |
400 register u_wide_int new_magnitude = magnitude / 10; | |
401 | |
402 *--p = magnitude - new_magnitude * 10 + '0'; | |
403 magnitude = new_magnitude; | |
404 } | |
405 while (magnitude); | |
406 | |
407 *len = buf_end - p; | |
408 return (p); | |
409 } | |
410 | |
411 | |
412 | |
413 /* | |
414 * Convert a floating point number to a string formats 'f', 'e' or 'E'. | |
415 * The result is placed in buf, and len denotes the length of the string | |
416 * The sign is returned in the is_negative argument (and is not placed | |
417 * in buf). | |
418 */ | |
419 static char * | |
420 conv_fp(register char format, register double num, | |
421 boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) | |
422 { | |
423 register char *s = buf; | |
424 register char *p; | |
425 int decimal_point; | |
426 | |
427 if (format == 'f') | |
428 p = ap_fcvt(num, precision, &decimal_point, is_negative); | |
429 else /* either e or E format */ | |
430 p = ap_ecvt(num, precision + 1, &decimal_point, is_negative); | |
431 | |
432 /* | |
433 * Check for Infinity and NaN | |
434 */ | |
435 if (isalpha((int)*p)) { | |
436 *len = strlen(strcpy(buf, p)); | |
437 *is_negative = FALSE; | |
438 return (buf); | |
439 } | |
440 if (format == 'f') { | |
441 if (decimal_point <= 0) { | |
442 *s++ = '0'; | |
443 if (precision > 0) { | |
444 *s++ = '.'; | |
445 while (decimal_point++ < 0) | |
446 *s++ = '0'; | |
447 } else if (add_dp) { | |
448 *s++ = '.'; | |
449 } | |
450 } else { | |
451 while (decimal_point-- > 0) { | |
452 *s++ = *p++; | |
453 } | |
454 if (precision > 0 || add_dp) { | |
455 *s++ = '.'; | |
456 } | |
457 } | |
458 } else { | |
459 *s++ = *p++; | |
460 if (precision > 0 || add_dp) | |
461 *s++ = '.'; | |
462 } | |
463 | |
464 /* | |
465 * copy the rest of p, the NUL is NOT copied | |
466 */ | |
467 while (*p) | |
468 *s++ = *p++; | |
469 | |
470 if (format != 'f') { | |
471 char temp[EXPONENT_LENGTH]; /* for exponent conversion */ | |
472 int t_len; | |
473 bool_int exponent_is_negative; | |
474 | |
475 *s++ = format; /* either e or E */ | |
476 decimal_point--; | |
477 if (decimal_point != 0) { | |
478 p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, | |
479 &temp[EXPONENT_LENGTH], &t_len); | |
480 *s++ = exponent_is_negative ? '-' : '+'; | |
481 | |
482 /* | |
483 * Make sure the exponent has at least 2 digits | |
484 */ | |
485 if (t_len == 1) | |
486 *s++ = '0'; | |
487 while (t_len--) | |
488 *s++ = *p++; | |
489 } else { | |
490 *s++ = '+'; | |
491 *s++ = '0'; | |
492 *s++ = '0'; | |
493 } | |
494 } | |
495 *len = s - buf; | |
496 return (buf); | |
497 } | |
498 | |
499 | |
500 /* | |
501 * Convert num to a base X number where X is a power of 2. nbits determines X. | |
502 * For example, if nbits is 3, we do base 8 conversion | |
503 * Return value: | |
504 * a pointer to a string containing the number | |
505 * | |
506 * The caller provides a buffer for the string: that is the buf_end argument | |
507 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer | |
508 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) | |
509 */ | |
510 static char * | |
511 conv_p2(register u_wide_int num, register int nbits, | |
512 char format, char *buf_end, register int *len) | |
513 { | |
514 register int mask = (1 << nbits) - 1; | |
515 register char *p = buf_end; | |
516 static char low_digits[] = "0123456789abcdef"; | |
517 static char upper_digits[] = "0123456789ABCDEF"; | |
518 register char *digits = (format == 'X') ? upper_digits : low_digits; | |
519 | |
520 do { | |
521 *--p = digits[num & mask]; | |
522 num >>= nbits; | |
523 } | |
524 while (num); | |
525 | |
526 *len = buf_end - p; | |
527 return (p); | |
528 } | |
529 | |
530 | |
531 /* | |
532 * Do format conversion placing the output in buffer | |
533 */ | |
534 static int format_converter(register buffy * odp, const char *fmt, | |
535 va_list ap) | |
536 { | |
537 register char *sp; | |
538 register char *bep; | |
539 register int cc = 0; | |
540 register int i; | |
541 | |
542 register char *s = NULL; | |
543 char *q; | |
544 int s_len; | |
545 | |
546 register int min_width = 0; | |
547 int precision = 0; | |
548 enum { | |
549 LEFT, RIGHT | |
550 } adjust; | |
551 char pad_char; | |
552 char prefix_char; | |
553 | |
554 double fp_num; | |
555 wide_int i_num = (wide_int) 0; | |
556 u_wide_int ui_num; | |
557 | |
558 char num_buf[NUM_BUF_SIZE]; | |
559 char char_buf[2]; /* for printing %% and %<unknown> */ | |
560 | |
561 /* | |
562 * Flag variables | |
563 */ | |
564 boolean_e is_long; | |
565 boolean_e alternate_form; | |
566 boolean_e print_sign; | |
567 boolean_e print_blank; | |
568 boolean_e adjust_precision; | |
569 boolean_e adjust_width; | |
570 bool_int is_negative; | |
571 | |
572 sp = odp->nextb; | |
573 bep = odp->buf_end; | |
574 | |
575 while (*fmt) { | |
576 if (*fmt != '%') { | |
577 INS_CHAR(*fmt, sp, bep, cc); | |
578 } else { | |
579 /* | |
580 * Default variable settings | |
581 */ | |
582 adjust = RIGHT; | |
583 alternate_form = print_sign = print_blank = NO; | |
584 pad_char = ' '; | |
585 prefix_char = NUL; | |
586 | |
587 fmt++; | |
588 | |
589 /* | |
590 * Try to avoid checking for flags, width or precision | |
591 */ | |
592 if (isascii((int)*fmt) && !islower((int)*fmt)) { | |
593 /* | |
594 * Recognize flags: -, #, BLANK, + | |
595 */ | |
596 for (;; fmt++) { | |
597 if (*fmt == '-') | |
598 adjust = LEFT; | |
599 else if (*fmt == '+') | |
600 print_sign = YES; | |
601 else if (*fmt == '#') | |
602 alternate_form = YES; | |
603 else if (*fmt == ' ') | |
604 print_blank = YES; | |
605 else if (*fmt == '0') | |
606 pad_char = '0'; | |
607 else | |
608 break; | |
609 } | |
610 | |
611 /* | |
612 * Check if a width was specified | |
613 */ | |
614 if (isdigit((int)*fmt)) { | |
615 STR_TO_DEC(fmt, min_width); | |
616 adjust_width = YES; | |
617 } else if (*fmt == '*') { | |
618 min_width = va_arg(ap, int); | |
619 fmt++; | |
620 adjust_width = YES; | |
621 if (min_width < 0) { | |
622 adjust = LEFT; | |
623 min_width = -min_width; | |
624 } | |
625 } else | |
626 adjust_width = NO; | |
627 | |
628 /* | |
629 * Check if a precision was specified | |
630 * | |
631 * XXX: an unreasonable amount of precision may be specified | |
632 * resulting in overflow of num_buf. Currently we | |
633 * ignore this possibility. | |
634 */ | |
635 if (*fmt == '.') { | |
636 adjust_precision = YES; | |
637 fmt++; | |
638 if (isdigit((int)*fmt)) { | |
639 STR_TO_DEC(fmt, precision); | |
640 } else if (*fmt == '*') { | |
641 precision = va_arg(ap, int); | |
642 fmt++; | |
643 if (precision < 0) | |
644 precision = 0; | |
645 } else | |
646 precision = 0; | |
647 } else | |
648 adjust_precision = NO; | |
649 } else | |
650 adjust_precision = adjust_width = NO; | |
651 | |
652 /* | |
653 * Modifier check | |
654 */ | |
655 if (*fmt == 'l') { | |
656 is_long = YES; | |
657 fmt++; | |
658 } else | |
659 is_long = NO; | |
660 | |
661 /* | |
662 * Argument extraction and printing. | |
663 * First we determine the argument type. | |
664 * Then, we convert the argument to a string. | |
665 * On exit from the switch, s points to the string that | |
666 * must be printed, s_len has the length of the string | |
667 * The precision requirements, if any, are reflected in s_len. | |
668 * | |
669 * NOTE: pad_char may be set to '0' because of the 0 flag. | |
670 * It is reset to ' ' by non-numeric formats | |
671 */ | |
672 switch (*fmt) { | |
673 case 'u': | |
674 if (is_long) | |
675 i_num = va_arg(ap, u_wide_int); | |
676 else | |
677 i_num = (wide_int) va_arg(ap, unsigned int); | |
678 /* | |
679 * The rest also applies to other integer formats, so fall | |
680 * into that case. | |
681 */ | |
682 case 'd': | |
683 case 'i': | |
684 /* | |
685 * Get the arg if we haven't already. | |
686 */ | |
687 if ((*fmt) != 'u') { | |
688 if (is_long) | |
689 i_num = va_arg(ap, wide_int); | |
690 else | |
691 i_num = (wide_int) va_arg(ap, int); | |
692 }; | |
693 s = conv_10(i_num, (*fmt) == 'u', &is_negative, | |
694 &num_buf[NUM_BUF_SIZE], &s_len); | |
695 FIX_PRECISION(adjust_precision, precision, s, s_len); | |
696 | |
697 if (*fmt != 'u') { | |
698 if (is_negative) | |
699 prefix_char = '-'; | |
700 else if (print_sign) | |
701 prefix_char = '+'; | |
702 else if (print_blank) | |
703 prefix_char = ' '; | |
704 } | |
705 break; | |
706 | |
707 | |
708 case 'o': | |
709 if (is_long) | |
710 ui_num = va_arg(ap, u_wide_int); | |
711 else | |
712 ui_num = (u_wide_int) va_arg(ap, unsigned int); | |
713 s = conv_p2(ui_num, 3, *fmt, | |
714 &num_buf[NUM_BUF_SIZE], &s_len); | |
715 FIX_PRECISION(adjust_precision, precision, s, s_len); | |
716 if (alternate_form && *s != '0') { | |
717 *--s = '0'; | |
718 s_len++; | |
719 } | |
720 break; | |
721 | |
722 | |
723 case 'x': | |
724 case 'X': | |
725 if (is_long) | |
726 ui_num = (u_wide_int) va_arg(ap, u_wide_int); | |
727 else | |
728 ui_num = (u_wide_int) va_arg(ap, unsigned int); | |
729 s = conv_p2(ui_num, 4, *fmt, | |
730 &num_buf[NUM_BUF_SIZE], &s_len); | |
731 FIX_PRECISION(adjust_precision, precision, s, s_len); | |
732 if (alternate_form && i_num != 0) { | |
733 *--s = *fmt; /* 'x' or 'X' */ | |
734 *--s = '0'; | |
735 s_len += 2; | |
736 } | |
737 break; | |
738 | |
739 | |
740 case 's': | |
741 s = va_arg(ap, char *); | |
742 if (s != NULL) { | |
743 s_len = strlen(s); | |
744 if (adjust_precision && precision < s_len) | |
745 s_len = precision; | |
746 } else { | |
747 s = S_NULL; | |
748 s_len = S_NULL_LEN; | |
749 } | |
750 pad_char = ' '; | |
751 break; | |
752 | |
753 | |
754 case 'f': | |
755 case 'e': | |
756 case 'E': | |
757 fp_num = va_arg(ap, double); | |
758 | |
759 s = conv_fp(*fmt, fp_num, alternate_form, | |
760 (adjust_precision == NO) ? FLOAT_DIGITS : precision, | |
761 &is_negative, &num_buf[1], &s_len); | |
762 if (is_negative) | |
763 prefix_char = '-'; | |
764 else if (print_sign) | |
765 prefix_char = '+'; | |
766 else if (print_blank) | |
767 prefix_char = ' '; | |
768 break; | |
769 | |
770 | |
771 case 'g': | |
772 case 'G': | |
773 if (adjust_precision == NO) | |
774 precision = FLOAT_DIGITS; | |
775 else if (precision == 0) | |
776 precision = 1; | |
777 /* | |
778 * * We use &num_buf[ 1 ], so that we have room for the sign | |
779 */ | |
780 s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1]); | |
781 if (*s == '-') | |
782 prefix_char = *s++; | |
783 else if (print_sign) | |
784 prefix_char = '+'; | |
785 else if (print_blank) | |
786 prefix_char = ' '; | |
787 | |
788 s_len = strlen(s); | |
789 | |
790 if (alternate_form && (q = strchr(s, '.')) == NULL) | |
791 s[s_len++] = '.'; | |
792 if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) | |
793 *q = 'E'; | |
794 break; | |
795 | |
796 | |
797 case 'c': | |
798 char_buf[0] = (char) (va_arg(ap, int)); | |
799 s = &char_buf[0]; | |
800 s_len = 1; | |
801 pad_char = ' '; | |
802 break; | |
803 | |
804 | |
805 case '%': | |
806 char_buf[0] = '%'; | |
807 s = &char_buf[0]; | |
808 s_len = 1; | |
809 pad_char = ' '; | |
810 break; | |
811 | |
812 | |
813 case 'n': | |
814 *(va_arg(ap, int *)) = cc; | |
815 break; | |
816 | |
817 /* | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
818 * Always extract the argument as a "char *" pointer. We |
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
819 * should be using "void *" but there are still machines |
25 | 820 * that don't understand it. |
821 * If the pointer size is equal to the size of an unsigned | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
822 * integer we convert the pointer to a hex number, otherwise |
25 | 823 * we print "%p" to indicate that we don't handle "%p". |
824 */ | |
825 case 'p': | |
826 ui_num = (u_wide_int) va_arg(ap, char *); | |
827 | |
828 if (sizeof(char *) <= sizeof(u_wide_int)) | |
829 s = conv_p2(ui_num, 4, 'x', | |
830 &num_buf[NUM_BUF_SIZE], &s_len); | |
831 else { | |
832 s = "%p"; | |
833 s_len = 2; | |
834 } | |
835 pad_char = ' '; | |
836 break; | |
837 | |
838 | |
839 case NUL: | |
840 /* | |
841 * The last character of the format string was %. | |
842 * We ignore it. | |
843 */ | |
844 continue; | |
845 | |
846 | |
847 /* | |
848 * The default case is for unrecognized %'s. | |
849 * We print %<char> to help the user identify what | |
850 * option is not understood. | |
851 * This is also useful in case the user wants to pass | |
852 * the output of format_converter to another function | |
853 * that understands some other %<char> (like syslog). | |
854 * Note that we can't point s inside fmt because the | |
855 * unknown <char> could be preceded by width etc. | |
856 */ | |
857 default: | |
858 char_buf[0] = '%'; | |
859 char_buf[1] = *fmt; | |
860 s = char_buf; | |
861 s_len = 2; | |
862 pad_char = ' '; | |
863 break; | |
864 } | |
865 | |
866 if (prefix_char != NUL) { | |
867 *--s = prefix_char; | |
868 s_len++; | |
869 } | |
870 if (adjust_width && adjust == RIGHT && min_width > s_len) { | |
871 if (pad_char == '0' && prefix_char != NUL) { | |
872 INS_CHAR(*s, sp, bep, cc) | |
873 s++; | |
874 s_len--; | |
875 min_width--; | |
876 } | |
877 PAD(min_width, s_len, pad_char); | |
878 } | |
879 /* | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
25
diff
changeset
|
880 * Print the string s. |
25 | 881 */ |
882 for (i = s_len; i != 0; i--) { | |
883 INS_CHAR(*s, sp, bep, cc); | |
884 s++; | |
885 } | |
886 | |
887 if (adjust_width && adjust == LEFT && min_width > s_len) | |
888 PAD(min_width, s_len, pad_char); | |
889 } | |
890 fmt++; | |
891 } | |
892 odp->nextb = sp; | |
893 return (cc); | |
894 } | |
895 | |
896 | |
897 /* | |
898 * This is the general purpose conversion function. | |
899 */ | |
900 static void strx_printv(int *ccp, char *buf, size_t len, const char *format, | |
901 va_list ap) | |
902 { | |
903 buffy od; | |
904 int cc; | |
905 | |
906 /* | |
907 * First initialize the descriptor | |
908 * Notice that if no length is given, we initialize buf_end to the | |
909 * highest possible address. | |
910 */ | |
911 od.buf_end = len ? &buf[len] : (char *) ~0; | |
912 od.nextb = buf; | |
913 | |
914 /* | |
915 * Do the conversion | |
916 */ | |
917 cc = format_converter(&od, format, ap); | |
918 if (len == 0 || od.nextb <= od.buf_end) | |
919 *(od.nextb) = '\0'; | |
920 if (ccp) | |
921 *ccp = cc; | |
922 } | |
923 | |
924 | |
925 int ap_snprintf(char *buf, size_t len, const char *format,...) | |
926 { | |
927 int cc; | |
928 va_list ap; | |
929 | |
930 va_start(ap, format); | |
931 strx_printv(&cc, buf, (len - 1), format, ap); | |
932 va_end(ap); | |
933 return (cc); | |
934 } | |
935 | |
936 | |
937 int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap) | |
938 { | |
939 int cc; | |
940 | |
941 strx_printv(&cc, buf, (len - 1), format, ap); | |
942 return (cc); | |
943 } | |
944 | |
945 #endif /* HAVE_SNPRINTF */ |