Mercurial > hg
annotate mcabber/libjabber/snprintf.c @ 824:37ef269330f0
Show incomplete subscriptions in the roster
When the "from" subscription is missing, curly braces are used.
When the "to" subscription is missing, the status is replaced by '?'.
Thanks to pmw for the suggestion.
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Tue, 25 Apr 2006 07:13:43 +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 */ |