ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/vsnprintf.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (9 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 18801 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* An implementation of vsnprintf() for systems that don't have it.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include <stdarg.h>
11 #include <string.h>
12 #include <ctype.h>
13
14 typedef int
15 (*_pfmt_writefunc_t)(const char *buf, size_t len, void *arg1, void *arg2);
16
17 int my_vsnprintf(char *string, size_t size, const char *format, va_list args);
18 int my_snprintf(char *string, size_t size, const char *format, ...);
19
20 /*************************************************************************/
21
22 /* Basic format routine for *printf() interfaces. Takes a writing function
23 * and two (optional) pointer parameters for that function which are passed
24 * on unmodified. The function should return the number of bytes written,
25 * or 0 (not -1!) on write failure.
26 */
27
28 static int _pfmt(const char *format, va_list args,
29 _pfmt_writefunc_t writefunc, void *arg1, void *arg2)
30 {
31 int total = 0; /* Total bytes written */
32 const char *startptr;/* Beginning of non-token text in format string.
33 * Used for writing in bulk instead of
34 * character-at-a-time. */
35 int n; /* Bytes written in last writefunc() call */
36 int valid; /* Was this a valid %-token? */
37 int alt_form; /* "Alternate form"? (# flag) */
38 int zero_pad; /* Zero-pad value? */
39 int left_justify; /* Left-justify? (0 means right-justify) */
40 int always_sign; /* Always add sign value? */
41 int insert_blank; /* Insert blank before positive values for %d/%i? */
42 int width; /* Field width */
43 int precision; /* Precision */
44 int argsize; /* Size of argument: 0=normal, 1=short, 2=long,
45 * 3=long long */
46 int what; /* What are we working on? 0=flags, 1=width,
47 * 2=precision, 3=argsize, 4=argtype */
48 long intval; /* Integer value */
49 char *strval; /* String value */
50 void *ptrval; /* Pointer value */
51 char numbuf[64]; /* Temporary buffer for printing numbers; this MUST
52 * be large enough to hold the longest possible
53 * number (size is not checked in processing) */
54 char *numptr; /* Pointer to start of printed number in `numbuf' */
55
56
57 intval = 0;
58 strval = NULL;
59 ptrval = NULL;
60
61 startptr = format;
62 while (*format) {
63 if (*format != '%') {
64 format++;
65 continue;
66 }
67 if (startptr != format) {
68 /* Write out accumulated text */
69 n = writefunc(startptr, format-startptr, arg1, arg2);
70 total += n;
71 /* Abort on short write */
72 if (n != format-startptr)
73 break;
74 /* Point to this token, in case it's a bad one */
75 startptr = format;
76 }
77
78 /* Begin %-token processing */
79 valid = 0; /* 1 if valid, -1 if known not valid (syntax error) */
80 alt_form = 0;
81 left_justify = 0;
82 always_sign = 0;
83 insert_blank = 0;
84 zero_pad = 0;
85 width = -1;
86 precision = -1;
87 argsize = 0;
88 what = 0;
89
90 while (!valid && *++format) { /* Broken out of by terminal chars */
91 switch (*format) {
92
93 /* Flags */
94 case '#':
95 if (what != 0) {
96 valid = -1;
97 break;
98 }
99 alt_form = 1;
100 break;
101 case '-':
102 if (what != 0) {
103 valid = -1;
104 break;
105 }
106 left_justify = 1;
107 break;
108 case '+':
109 if (what != 0) {
110 valid = -1;
111 break;
112 }
113 always_sign = 1;
114 break;
115 case ' ':
116 if (what != 0) {
117 valid = -1;
118 break;
119 }
120 insert_blank = 1;
121 break;
122 case '\'':
123 /* Comma-grouping by locale; not supported */
124 valid = -1;
125 break;
126 case '0':
127 if (what == 0) {
128 zero_pad = 1;
129 break;
130 }
131 /* else fall through */
132
133 /* Field widths */
134 case '1':
135 case '2':
136 case '3':
137 case '4':
138 case '5':
139 case '6':
140 case '7':
141 case '8':
142 case '9':
143 if (what == 0)
144 what = 1;
145 else if (what > 2) {
146 valid = -1;
147 break;
148 }
149 if (what == 1) {
150 if (width < 0)
151 width = 0;
152 width = width*10 + (*format)-'0';
153 } else {
154 if (precision < 0)
155 precision = 0;
156 precision = precision*10 + (*format)-'0';
157 }
158 break;
159 case '*':
160 if (what == 0)
161 what = 1;
162 else if (what > 2) {
163 valid = -1;
164 break;
165 }
166 if (what == 1) {
167 width = va_arg(args, int);
168 if (width < 0) {
169 width = -width;
170 left_justify = 1;
171 }
172 } else {
173 precision = va_arg(args, int);
174 }
175 break;
176 case '.':
177 if (what >= 2) {
178 valid = -1;
179 break;
180 }
181 what = 2;
182 break;
183
184 /* Argument sizes */
185 case 'h':
186 if (what > 3) {
187 valid = -1;
188 break;
189 }
190 argsize = 1;
191 what = 4;
192 break;
193 case 'l':
194 if (what > 3) {
195 valid = -1;
196 break;
197 }
198 argsize = 2;
199 what = 4;
200 break;
201 case 'L':
202 /* Long long (64 bits); not supported */
203 valid = -1;
204 break;
205 /* Argument types */
206 case 'd':
207 case 'i':
208 case 'o':
209 case 'u':
210 case 'x':
211 case 'X':
212 if (argsize == 1)
213 intval = va_arg(args, int);
214 else if (argsize == 2)
215 intval = va_arg(args, long);
216 else
217 intval = va_arg(args, int);
218 valid = 1;
219 break;
220 case 'c':
221 intval = va_arg(args, int);
222 valid = 1;
223 break;
224 case 's':
225 strval = va_arg(args, char *);
226 valid = 1;
227 break;
228 case 'p':
229 ptrval = va_arg(args, void *);
230 valid = 1;
231 break;
232 case 'n':
233 *((int *)va_arg(args, int *)) = total;
234 valid = 1;
235 break;
236 case '%':
237 valid = 1;
238 break;
239
240 /* All other characters */
241 default:
242 valid = -1;
243 break;
244 } /* switch (*format) */
245 }
246 if (valid != 1) {
247 /* Not a valid %-token; start loop over (token will get printed
248 * out next time through). */
249 continue;
250 }
251
252 /* Don't zero-pad if a precision was given or left-justifying */
253 if (precision != -1 || left_justify)
254 zero_pad = 0;
255
256 /* For numbers, limit precision to the size of the print buffer */
257 if ((*format=='d' || *format=='i' || *format=='o' || *format=='u'
258 || *format=='x' || *format=='X')
259 && precision > (signed) sizeof(numbuf))
260 {
261 precision = sizeof(numbuf);
262 }
263
264 switch (*format++) { /* Do something with this token */
265 case '%':
266 (*writefunc)(format-1, 1, arg1, arg2);
267 total++;
268 break;
269
270 case 'p':
271 /* Print the NULL value specially */
272 if (ptrval == NULL) {
273 strval = "(null)";
274 goto handle_string;
275 }
276 /* For all other values, pretend it's really %#.8x */
277 alt_form = 1;
278 zero_pad = 0;
279 precision = 8;
280 intval = (long) ptrval;
281 /* Fall through */
282
283 case 'x':
284 case 'X': {
285 static const char x_chars[] = "0123456789abcdef0x";
286 static const char X_chars[] = "0123456789ABCDEF0X";
287 const char *chars = (format[-1]=='X') ? X_chars : x_chars;
288 const char *padstr = zero_pad ? "0" : " ";
289 unsigned long uintval;
290 int len;
291
292 uintval = (unsigned long) intval;
293 if (alt_form && uintval != 0) {
294 n = writefunc(chars+16, 2, arg1, arg2);
295 total += n;
296 if (n != 2)
297 break;
298 width -= 2;
299 }
300 if (precision < 1)
301 precision = 1;
302 numptr = numbuf + sizeof(numbuf);
303 for (len = 0; len < precision || uintval != 0; len++) {
304 *--numptr = chars[uintval%16];
305 uintval /= 16;
306 }
307 if (left_justify) {
308 n = writefunc(numptr, len, arg1, arg2);
309 total += n;
310 if (n != len)
311 break;
312 }
313 while (len < width) {
314 if (1 != writefunc(padstr, 1, arg1, arg2))
315 break;
316 total++;
317 width--;
318 }
319 if (!left_justify)
320 total += writefunc(numptr, len, arg1, arg2);
321 break;
322 } /* case 'x', 'X' */
323
324 case 'o': {
325 const char *padstr = zero_pad ? "0" : " ";
326 unsigned long uintval;
327 int len;
328
329 uintval = (unsigned long) intval;
330 if (precision < 1)
331 precision = 1;
332 numptr = numbuf + sizeof(numbuf);
333 for (len = 0; len < precision || uintval != 0; len++) {
334 *--numptr = '0' + uintval%8;
335 uintval /= 8;
336 }
337 if (alt_form && *numptr != '0') {
338 *--numptr = '0';
339 len++;
340 }
341 if (left_justify) {
342 n = writefunc(numptr, len, arg1, arg2);
343 total += n;
344 if (n != len)
345 break;
346 }
347 while (len < width) {
348 if (1 != writefunc(padstr, 1, arg1, arg2))
349 break;
350 total++;
351 width--;
352 }
353 if (!left_justify)
354 total += writefunc(numptr, len, arg1, arg2);
355 break;
356 } /* case 'o' */
357
358 case 'u': {
359 const char *padstr = zero_pad ? "0" : " ";
360 unsigned long uintval;
361 int len;
362
363 uintval = (unsigned long) intval;
364 if (precision < 1)
365 precision = 1;
366 numptr = numbuf + sizeof(numbuf);
367 for (len = 0; len < precision || uintval != 0; len++) {
368 *--numptr = '0' + uintval%10;
369 uintval /= 10;
370 }
371 if (left_justify) {
372 n = writefunc(numptr, len, arg1, arg2);
373 total += n;
374 if (n != len)
375 break;
376 }
377 while (len < width) {
378 if (1 != writefunc(padstr, 1, arg1, arg2))
379 break;
380 total++;
381 width--;
382 }
383 if (!left_justify)
384 total += writefunc(numptr, len, arg1, arg2);
385 break;
386 } /* case 'u' */
387
388 case 'd':
389 case 'i': {
390 const char *padstr = zero_pad ? "0" : " ";
391 int len;
392 char sign_char;
393
394 numptr = numbuf + sizeof(numbuf);
395 len = 0;
396 sign_char = 0;
397 if (intval < 0) {
398 sign_char = '-';
399 intval = -intval;
400 if (intval < 0) { /* true for 0x800...0 */
401 *numptr-- = '0' - intval%10;
402 len++;
403 intval /= 10;
404 intval = -intval;
405 }
406 }
407 if (precision < 1)
408 precision = 1;
409 for (; len < precision || intval != 0; len++) {
410 *--numptr = '0' + intval%10;
411 intval /= 10;
412 }
413 if (!sign_char) {
414 if (always_sign)
415 sign_char = '+';
416 else if (insert_blank)
417 sign_char = ' ';
418 }
419 if (sign_char) {
420 if (zero_pad) {
421 if (1 != writefunc(&sign_char, 1, arg1, arg2))
422 break;
423 total++;
424 width--;
425 } else {
426 *--numptr = sign_char;
427 len = 0;
428 }
429 }
430 if (left_justify) {
431 n = writefunc(numptr, len, arg1, arg2);
432 total += n;
433 if (n != len)
434 break;
435 }
436 while (len < width) {
437 if (1 != writefunc(padstr, 1, arg1, arg2))
438 break;
439 total++;
440 width--;
441 }
442 if (!left_justify)
443 total += writefunc(numptr, len, arg1, arg2);
444 break;
445 } /* case 'd', 'i' */
446
447 case 'c': {
448 const char *padstr = zero_pad ? "0" : " ";
449 unsigned char c = (unsigned char) intval;
450
451 if (left_justify) {
452 if (1 != writefunc((char *)&c, 1, arg1, arg2))
453 break;
454 total++;
455 }
456 while (width > 1) {
457 if (1 != writefunc(padstr, 1, arg1, arg2))
458 break;
459 total++;
460 width--;
461 }
462 if (!left_justify)
463 total += writefunc((char *)&c, 1, arg1, arg2);
464 break;
465 } /* case 'c' */
466
467 case 's': {
468 const char *padstr;
469 int len;
470
471 /* Catch null strings */
472 if (strval == NULL) {
473 strval = "(null)";
474 }
475 handle_string: /* NULL pointers for %p come here */
476 padstr = zero_pad ? "0" : " ";
477 len = strlen(strval);
478 if (precision < 0 || precision > len)
479 precision = len;
480 if (left_justify && precision > 0) {
481 n = writefunc(strval, precision, arg1, arg2);
482 total += n;
483 if (n != precision)
484 break;
485 }
486 while (width > precision) {
487 if (1 != writefunc(padstr, 1, arg1, arg2))
488 break;
489 total++;
490 width--;
491 }
492 if (!left_justify && precision > 0)
493 total += writefunc(strval, precision, arg1, arg2);
494 break;
495 } /* case 's' */
496
497 } /* switch (*format++) */
498
499 startptr = format; /* Start again after this %-token */
500 } /* while (*format) */
501
502 /* Write anything left over. */
503 if (startptr != format)
504 total += writefunc(startptr, format-startptr, arg1, arg2);
505
506 /* Return total bytes written. */
507 return total;
508 }
509
510 /*************************************************************************/
511
512 static int writefunc(const char *buf, size_t len, char **string, size_t *size)
513 {
514 if ((*size) <= 0)
515 return 0;
516 if (len > (*size)-1)
517 len = (*size)-1;
518 if (len > 0)
519 memcpy((*string), buf, len);
520 (*string) += len;
521 (*string)[0] = 0;
522 (*size) -= len;
523 return len;
524 }
525
526 int my_vsnprintf(char *string, size_t size, const char *format, va_list args)
527 {
528 int ret;
529
530 if (size <= 0)
531 return 0;
532 ret = _pfmt(format, args, (_pfmt_writefunc_t) writefunc, &string, &size);
533 return ret;
534 }
535
536 int my_snprintf(char *string, size_t size, const char *format, ...)
537 {
538 va_list args;
539 int ret;
540
541 va_start(args, format);
542 ret = my_vsnprintf(string, size, format, args);
543 va_end(args);
544 return ret;
545 }
546
547 /*************************************************************************/
548
549 /*
550 * Local variables:
551 * c-file-style: "stroustrup"
552 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
553 * indent-tabs-mode: nil
554 * End:
555 *
556 * vim: expandtab shiftwidth=4:
557 */