]>
Commit | Line | Data |
---|---|---|
b57f37fb WP |
1 | |
2 | /* | |
3 | * Modified and hacked into libratbox by Aaron Sethman <androsyn@ratbox.org> | |
4 | * The original headers are below.. | |
5 | * Note that this implementation does not process floating point numbers so | |
6 | * you will likely need to fall back to using sprintf yourself to do those... | |
b57f37fb WP |
7 | */ |
8 | ||
9 | /* | |
10 | * linux/lib/vsprintf.c | |
11 | * | |
12 | * Copyright (C) 1991, 1992 Linus Torvalds | |
13 | */ | |
14 | ||
15 | /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ | |
16 | /* | |
17 | * Wirzenius wrote this portably, Torvalds fucked it up :-) | |
18 | */ | |
19 | ||
20 | /* | |
21 | * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com> | |
22 | * - changed to provide snprintf and vsnprintf functions | |
23 | * So Feb 1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de> | |
24 | * - scnprintf and vscnprintf | |
25 | */ | |
26 | #include <libratbox_config.h> | |
27 | #include <ratbox_lib.h> | |
28 | ||
94b4fbf9 VY |
29 | static int |
30 | skip_atoi(const char **s) | |
b57f37fb | 31 | { |
94b4fbf9 | 32 | int i = 0; |
b57f37fb | 33 | |
94b4fbf9 VY |
34 | while(isdigit(**s)) |
35 | i = i * 10 + *((*s)++) - '0'; | |
b57f37fb WP |
36 | return i; |
37 | } | |
38 | ||
39 | /* Decimal conversion is by far the most typical, and is used | |
40 | * for /proc and /sys data. This directly impacts e.g. top performance | |
41 | * with many processes running. We optimize it for speed | |
42 | * using code from | |
43 | * http://www.cs.uiowa.edu/~jones/bcd/decimal.html | |
44 | * (with permission from the author, Douglas W. Jones). */ | |
45 | ||
46 | /* Formats correctly any integer in [0,99999]. | |
47 | * Outputs from one to five digits depending on input. | |
48 | * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */ | |
94b4fbf9 VY |
49 | static char * |
50 | put_dec_trunc(char *buf, unsigned q) | |
b57f37fb WP |
51 | { |
52 | unsigned d3, d2, d1, d0; | |
94b4fbf9 VY |
53 | d1 = (q >> 4) & 0xf; |
54 | d2 = (q >> 8) & 0xf; | |
55 | d3 = (q >> 12); | |
b57f37fb | 56 | |
94b4fbf9 | 57 | d0 = 6 * (d3 + d2 + d1) + (q & 0xf); |
b57f37fb | 58 | q = (d0 * 0xcd) >> 11; |
94b4fbf9 VY |
59 | d0 = d0 - 10 * q; |
60 | *buf++ = d0 + '0'; /* least significant digit */ | |
61 | d1 = q + 9 * d3 + 5 * d2 + d1; | |
62 | if(d1 != 0) | |
63 | { | |
b57f37fb | 64 | q = (d1 * 0xcd) >> 11; |
94b4fbf9 VY |
65 | d1 = d1 - 10 * q; |
66 | *buf++ = d1 + '0'; /* next digit */ | |
b57f37fb | 67 | |
94b4fbf9 VY |
68 | d2 = q + 2 * d2; |
69 | if((d2 != 0) || (d3 != 0)) | |
70 | { | |
b57f37fb | 71 | q = (d2 * 0xd) >> 7; |
94b4fbf9 VY |
72 | d2 = d2 - 10 * q; |
73 | *buf++ = d2 + '0'; /* next digit */ | |
b57f37fb | 74 | |
94b4fbf9 VY |
75 | d3 = q + 4 * d3; |
76 | if(d3 != 0) | |
77 | { | |
b57f37fb | 78 | q = (d3 * 0xcd) >> 11; |
94b4fbf9 VY |
79 | d3 = d3 - 10 * q; |
80 | *buf++ = d3 + '0'; /* next digit */ | |
81 | if(q != 0) | |
82 | *buf++ = q + '0'; /* most sign. digit */ | |
b57f37fb WP |
83 | } |
84 | } | |
85 | } | |
86 | return buf; | |
87 | } | |
94b4fbf9 | 88 | |
b57f37fb | 89 | /* Same with if's removed. Always emits five digits */ |
94b4fbf9 VY |
90 | static char * |
91 | put_dec_full(char *buf, unsigned q) | |
b57f37fb WP |
92 | { |
93 | /* BTW, if q is in [0,9999], 8-bit ints will be enough, */ | |
94 | /* but anyway, gcc produces better code with full-sized ints */ | |
95 | unsigned d3, d2, d1, d0; | |
94b4fbf9 VY |
96 | d1 = (q >> 4) & 0xf; |
97 | d2 = (q >> 8) & 0xf; | |
98 | d3 = (q >> 12); | |
b57f37fb WP |
99 | |
100 | /* Possible ways to approx. divide by 10 */ | |
101 | /* gcc -O2 replaces multiply with shifts and adds */ | |
102 | // (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386) | |
103 | // (x * 0x67) >> 10: 1100111 | |
104 | // (x * 0x34) >> 9: 110100 - same | |
105 | // (x * 0x1a) >> 8: 11010 - same | |
106 | // (x * 0x0d) >> 7: 1101 - same, shortest code (on i386) | |
107 | ||
94b4fbf9 | 108 | d0 = 6 * (d3 + d2 + d1) + (q & 0xf); |
b57f37fb | 109 | q = (d0 * 0xcd) >> 11; |
94b4fbf9 | 110 | d0 = d0 - 10 * q; |
b57f37fb | 111 | *buf++ = d0 + '0'; |
94b4fbf9 VY |
112 | d1 = q + 9 * d3 + 5 * d2 + d1; |
113 | q = (d1 * 0xcd) >> 11; | |
114 | d1 = d1 - 10 * q; | |
115 | *buf++ = d1 + '0'; | |
116 | ||
117 | d2 = q + 2 * d2; | |
118 | q = (d2 * 0xd) >> 7; | |
119 | d2 = d2 - 10 * q; | |
120 | *buf++ = d2 + '0'; | |
121 | ||
122 | d3 = q + 4 * d3; | |
123 | q = (d3 * 0xcd) >> 11; /* - shorter code */ | |
124 | /* q = (d3 * 0x67) >> 10; - would also work */ | |
125 | d3 = d3 - 10 * q; | |
126 | *buf++ = d3 + '0'; | |
127 | *buf++ = q + '0'; | |
b57f37fb WP |
128 | return buf; |
129 | } | |
130 | ||
94b4fbf9 VY |
131 | static char * |
132 | put_dec(char *buf, unsigned long long int num) | |
b57f37fb | 133 | { |
94b4fbf9 VY |
134 | while(1) |
135 | { | |
b57f37fb | 136 | unsigned rem; |
94b4fbf9 | 137 | if(num < 100000) |
b57f37fb WP |
138 | return put_dec_trunc(buf, num); |
139 | rem = num % 100000; | |
140 | num = num / 100000; | |
141 | buf = put_dec_full(buf, rem); | |
142 | } | |
143 | } | |
144 | ||
145 | #define ZEROPAD 1 /* pad with zero */ | |
146 | #define SIGN 2 /* unsigned/signed long */ | |
147 | #define PLUS 4 /* show plus */ | |
148 | #define SPACE 8 /* space if plus */ | |
149 | #define LEFT 16 /* left justified */ | |
150 | #define SPECIAL 32 /* 0x */ | |
151 | #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ | |
152 | ||
94b4fbf9 VY |
153 | static char * |
154 | number(char *buf, char *end, unsigned long long int num, int base, int size, int precision, | |
155 | int type) | |
b57f37fb | 156 | { |
94b4fbf9 | 157 | char sign, tmp[66]; |
b57f37fb WP |
158 | const char *digits; |
159 | /* we are called with base 8, 10 or 16, only, thus don't need "g..." */ | |
94b4fbf9 VY |
160 | static const char small_digits[] = "0123456789abcdefx"; /* "ghijklmnopqrstuvwxyz"; */ |
161 | static const char large_digits[] = "0123456789ABCDEFX"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ | |
b57f37fb WP |
162 | int need_pfx = ((type & SPECIAL) && base != 10); |
163 | int i; | |
164 | ||
165 | digits = (type & LARGE) ? large_digits : small_digits; | |
94b4fbf9 | 166 | if(type & LEFT) |
b57f37fb | 167 | type &= ~ZEROPAD; |
94b4fbf9 | 168 | if(base < 2 || base > 36) |
b57f37fb WP |
169 | return NULL; |
170 | sign = 0; | |
94b4fbf9 VY |
171 | if(type & SIGN) |
172 | { | |
173 | if((signed long long int)num < 0) | |
174 | { | |
b57f37fb | 175 | sign = '-'; |
94b4fbf9 | 176 | num = -(signed long long int)num; |
b57f37fb | 177 | size--; |
94b4fbf9 VY |
178 | } |
179 | else if(type & PLUS) | |
180 | { | |
b57f37fb WP |
181 | sign = '+'; |
182 | size--; | |
94b4fbf9 VY |
183 | } |
184 | else if(type & SPACE) | |
185 | { | |
b57f37fb WP |
186 | sign = ' '; |
187 | size--; | |
188 | } | |
189 | } | |
94b4fbf9 VY |
190 | if(need_pfx) |
191 | { | |
b57f37fb | 192 | size--; |
94b4fbf9 | 193 | if(base == 16) |
b57f37fb WP |
194 | size--; |
195 | } | |
196 | ||
197 | /* generate full string in tmp[], in reverse order */ | |
198 | i = 0; | |
94b4fbf9 | 199 | if(num == 0) |
b57f37fb WP |
200 | tmp[i++] = '0'; |
201 | /* Generic code, for any base: | |
94b4fbf9 VY |
202 | else do { |
203 | tmp[i++] = digits[do_div(num,base)]; | |
204 | } while (num != 0); | |
205 | */ | |
206 | else if(base != 10) | |
207 | { /* 8 or 16 */ | |
b57f37fb WP |
208 | int mask = base - 1; |
209 | int shift = 3; | |
94b4fbf9 VY |
210 | if(base == 16) |
211 | shift = 4; | |
212 | do | |
213 | { | |
b57f37fb WP |
214 | tmp[i++] = digits[((unsigned char)num) & mask]; |
215 | num >>= shift; | |
94b4fbf9 VY |
216 | } |
217 | while(num); | |
218 | } | |
219 | else | |
220 | { /* base 10 */ | |
b57f37fb WP |
221 | i = put_dec(tmp, num) - tmp; |
222 | } | |
223 | ||
224 | /* printing 100 using %2d gives "100", not "00" */ | |
94b4fbf9 | 225 | if(i > precision) |
b57f37fb WP |
226 | precision = i; |
227 | /* leading space padding */ | |
228 | size -= precision; | |
94b4fbf9 VY |
229 | if(!(type & (ZEROPAD + LEFT))) |
230 | { | |
231 | while(--size >= 0) | |
232 | { | |
233 | if(buf < end) | |
b57f37fb WP |
234 | *buf = ' '; |
235 | ++buf; | |
236 | } | |
237 | } | |
238 | /* sign */ | |
94b4fbf9 VY |
239 | if(sign) |
240 | { | |
241 | if(buf < end) | |
b57f37fb WP |
242 | *buf = sign; |
243 | ++buf; | |
244 | } | |
245 | /* "0x" / "0" prefix */ | |
94b4fbf9 VY |
246 | if(need_pfx) |
247 | { | |
248 | if(buf < end) | |
b57f37fb WP |
249 | *buf = '0'; |
250 | ++buf; | |
94b4fbf9 VY |
251 | if(base == 16) |
252 | { | |
253 | if(buf < end) | |
254 | *buf = digits[16]; /* for arbitrary base: digits[33]; */ | |
b57f37fb WP |
255 | ++buf; |
256 | } | |
257 | } | |
258 | /* zero or space padding */ | |
94b4fbf9 VY |
259 | if(!(type & LEFT)) |
260 | { | |
b57f37fb | 261 | char c = (type & ZEROPAD) ? '0' : ' '; |
94b4fbf9 VY |
262 | while(--size >= 0) |
263 | { | |
264 | if(buf < end) | |
b57f37fb WP |
265 | *buf = c; |
266 | ++buf; | |
267 | } | |
268 | } | |
269 | /* hmm even more zero padding? */ | |
94b4fbf9 VY |
270 | while(i <= --precision) |
271 | { | |
272 | if(buf < end) | |
b57f37fb WP |
273 | *buf = '0'; |
274 | ++buf; | |
275 | } | |
276 | /* actual digits of result */ | |
94b4fbf9 VY |
277 | while(--i >= 0) |
278 | { | |
279 | if(buf < end) | |
b57f37fb WP |
280 | *buf = tmp[i]; |
281 | ++buf; | |
282 | } | |
283 | /* trailing space padding */ | |
94b4fbf9 VY |
284 | while(--size >= 0) |
285 | { | |
286 | if(buf < end) | |
b57f37fb WP |
287 | *buf = ' '; |
288 | ++buf; | |
289 | } | |
290 | return buf; | |
291 | } | |
292 | ||
293 | /** | |
294 | * vsnprintf - Format a string and place it in a buffer | |
295 | * @buf: The buffer to place the result into | |
296 | * @size: The size of the buffer, including the trailing null space | |
297 | * @fmt: The format string to use | |
298 | * @args: Arguments for the format string | |
299 | * | |
300 | * The return value is the number of characters which would | |
301 | * be generated for the given input, excluding the trailing | |
302 | * '\0', as per ISO C99. If you want to have the exact | |
303 | * number of characters written into @buf as return value | |
304 | * (not including the trailing '\0'), use vscnprintf(). If the | |
305 | * return is greater than or equal to @size, the resulting | |
306 | * string is truncated. | |
307 | * | |
308 | * Call this function if you are already dealing with a va_list. | |
309 | * You probably want snprintf() instead. | |
310 | */ | |
94b4fbf9 VY |
311 | int |
312 | rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) | |
b57f37fb WP |
313 | { |
314 | int len; | |
315 | unsigned long long int num; | |
316 | int i, base; | |
317 | char *str, *end, c; | |
318 | const char *s; | |
319 | ||
320 | int flags; /* flags to number() */ | |
321 | ||
322 | int field_width; /* width of output field */ | |
323 | int precision; /* min. # of digits for integers; max | |
324 | number of chars for from string */ | |
325 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ | |
94b4fbf9 VY |
326 | /* 'z' support added 23/7/1999 S.H. */ |
327 | /* 'z' changed to 'Z' --davidm 1/25/99 */ | |
328 | /* 't' added for ptrdiff_t */ | |
b57f37fb WP |
329 | |
330 | /* Reject out-of-range values early. Large positive sizes are | |
331 | used for unknown buffer sizes. */ | |
94b4fbf9 VY |
332 | if(rb_unlikely((int)size < 0)) |
333 | { | |
b57f37fb WP |
334 | return 0; |
335 | } | |
336 | ||
337 | str = buf; | |
338 | end = buf + size; | |
339 | ||
340 | /* Make sure end is always >= buf */ | |
94b4fbf9 VY |
341 | if(end < buf) |
342 | { | |
b57f37fb WP |
343 | end = ((void *)-1); |
344 | size = end - buf; | |
345 | } | |
346 | ||
94b4fbf9 VY |
347 | for(; *fmt; ++fmt) |
348 | { | |
349 | if(*fmt != '%') | |
350 | { | |
351 | if(str < end) | |
b57f37fb WP |
352 | *str = *fmt; |
353 | ++str; | |
354 | continue; | |
355 | } | |
356 | ||
357 | /* process flags */ | |
358 | flags = 0; | |
94b4fbf9 VY |
359 | repeat: |
360 | ++fmt; /* this also skips first '%' */ | |
361 | switch (*fmt) | |
362 | { | |
363 | case '-': | |
364 | flags |= LEFT; | |
365 | goto repeat; | |
366 | case '+': | |
367 | flags |= PLUS; | |
368 | goto repeat; | |
369 | case ' ': | |
370 | flags |= SPACE; | |
371 | goto repeat; | |
372 | case '#': | |
373 | flags |= SPECIAL; | |
374 | goto repeat; | |
375 | case '0': | |
376 | flags |= ZEROPAD; | |
377 | goto repeat; | |
378 | } | |
b57f37fb WP |
379 | |
380 | /* get field width */ | |
381 | field_width = -1; | |
94b4fbf9 | 382 | if(isdigit(*fmt)) |
b57f37fb | 383 | field_width = skip_atoi(&fmt); |
94b4fbf9 VY |
384 | else if(*fmt == '*') |
385 | { | |
b57f37fb WP |
386 | ++fmt; |
387 | /* it's the next argument */ | |
388 | field_width = va_arg(args, int); | |
94b4fbf9 VY |
389 | if(field_width < 0) |
390 | { | |
b57f37fb WP |
391 | field_width = -field_width; |
392 | flags |= LEFT; | |
393 | } | |
394 | } | |
395 | ||
396 | /* get the precision */ | |
397 | precision = -1; | |
94b4fbf9 VY |
398 | if(*fmt == '.') |
399 | { | |
400 | ++fmt; | |
401 | if(isdigit(*fmt)) | |
b57f37fb | 402 | precision = skip_atoi(&fmt); |
94b4fbf9 VY |
403 | else if(*fmt == '*') |
404 | { | |
b57f37fb WP |
405 | ++fmt; |
406 | /* it's the next argument */ | |
407 | precision = va_arg(args, int); | |
408 | } | |
94b4fbf9 | 409 | if(precision < 0) |
b57f37fb WP |
410 | precision = 0; |
411 | } | |
412 | ||
413 | /* get the conversion qualifier */ | |
414 | qualifier = -1; | |
94b4fbf9 VY |
415 | if(*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
416 | *fmt == 'Z' || *fmt == 'z' || *fmt == 't') | |
417 | { | |
b57f37fb WP |
418 | qualifier = *fmt; |
419 | ++fmt; | |
94b4fbf9 VY |
420 | if(qualifier == 'l' && *fmt == 'l') |
421 | { | |
b57f37fb WP |
422 | qualifier = 'L'; |
423 | ++fmt; | |
424 | } | |
425 | } | |
426 | ||
427 | /* default base */ | |
428 | base = 10; | |
429 | ||
94b4fbf9 VY |
430 | switch (*fmt) |
431 | { | |
432 | case 'c': | |
433 | if(!(flags & LEFT)) | |
434 | { | |
435 | while(--field_width > 0) | |
436 | { | |
437 | if(str < end) | |
b57f37fb WP |
438 | *str = ' '; |
439 | ++str; | |
440 | } | |
94b4fbf9 VY |
441 | } |
442 | c = (unsigned char)va_arg(args, int); | |
443 | if(str < end) | |
444 | *str = c; | |
445 | ++str; | |
446 | while(--field_width > 0) | |
447 | { | |
448 | if(str < end) | |
449 | *str = ' '; | |
450 | ++str; | |
451 | } | |
452 | continue; | |
b57f37fb | 453 | |
94b4fbf9 VY |
454 | case 's': |
455 | s = va_arg(args, char *); | |
456 | if(s == NULL) | |
457 | { | |
458 | abort(); /* prefer blowing up vs corrupt data */ | |
459 | } | |
460 | len = rb_strnlen(s, precision); | |
461 | ||
462 | if(!(flags & LEFT)) | |
463 | { | |
464 | while(len < field_width--) | |
465 | { | |
466 | if(str < end) | |
b57f37fb WP |
467 | *str = ' '; |
468 | ++str; | |
469 | } | |
94b4fbf9 VY |
470 | } |
471 | for(i = 0; i < len; ++i) | |
472 | { | |
473 | if(str < end) | |
474 | *str = *s; | |
475 | ++str; | |
476 | ++s; | |
477 | } | |
478 | while(len < field_width--) | |
479 | { | |
480 | if(str < end) | |
481 | *str = ' '; | |
482 | ++str; | |
483 | } | |
484 | continue; | |
b57f37fb | 485 | |
94b4fbf9 VY |
486 | case 'p': |
487 | if(field_width == -1) | |
488 | { | |
489 | field_width = 2 * sizeof(void *); | |
490 | flags |= ZEROPAD; | |
491 | } | |
492 | str = number(str, end, | |
493 | (unsigned long)va_arg(args, void *), | |
494 | 16, field_width, precision, flags); | |
495 | continue; | |
b57f37fb | 496 | |
94b4fbf9 VY |
497 | |
498 | case 'n': | |
499 | /* FIXME: | |
500 | * What does C99 say about the overflow case here? */ | |
501 | if(qualifier == 'l') | |
502 | { | |
503 | long *ip = va_arg(args, long *); | |
504 | *ip = (str - buf); | |
505 | } | |
506 | else if(qualifier == 'Z' || qualifier == 'z') | |
507 | { | |
508 | size_t *ip = va_arg(args, size_t *); | |
509 | *ip = (str - buf); | |
510 | } | |
511 | else | |
512 | { | |
513 | int *ip = va_arg(args, int *); | |
514 | *ip = (str - buf); | |
515 | } | |
516 | continue; | |
517 | ||
518 | case '%': | |
519 | if(str < end) | |
520 | *str = '%'; | |
521 | ++str; | |
522 | continue; | |
523 | ||
524 | /* integer number formats - set up the flags and "break" */ | |
525 | case 'o': | |
526 | base = 8; | |
527 | break; | |
528 | ||
529 | case 'X': | |
530 | flags |= LARGE; | |
531 | case 'x': | |
532 | base = 16; | |
533 | break; | |
534 | ||
535 | case 'd': | |
536 | case 'i': | |
537 | flags |= SIGN; | |
538 | case 'u': | |
539 | break; | |
540 | ||
541 | default: | |
542 | if(str < end) | |
543 | *str = '%'; | |
544 | ++str; | |
545 | if(*fmt) | |
546 | { | |
547 | if(str < end) | |
548 | *str = *fmt; | |
b57f37fb | 549 | ++str; |
94b4fbf9 VY |
550 | } |
551 | else | |
552 | { | |
553 | --fmt; | |
554 | } | |
555 | continue; | |
b57f37fb | 556 | } |
94b4fbf9 | 557 | if(qualifier == 'L') |
b57f37fb | 558 | num = va_arg(args, long long int); |
94b4fbf9 VY |
559 | else if(qualifier == 'l') |
560 | { | |
b57f37fb | 561 | num = va_arg(args, unsigned long); |
94b4fbf9 VY |
562 | if(flags & SIGN) |
563 | num = (signed long)num; | |
564 | } | |
565 | else if(qualifier == 'Z' || qualifier == 'z') | |
566 | { | |
b57f37fb | 567 | num = va_arg(args, size_t); |
94b4fbf9 VY |
568 | } |
569 | else if(qualifier == 't') | |
570 | { | |
b57f37fb | 571 | num = va_arg(args, ptrdiff_t); |
94b4fbf9 VY |
572 | } |
573 | else if(qualifier == 'h') | |
574 | { | |
575 | num = (unsigned short)va_arg(args, int); | |
576 | if(flags & SIGN) | |
577 | num = (signed short)num; | |
578 | } | |
579 | else | |
580 | { | |
b57f37fb | 581 | num = va_arg(args, unsigned int); |
94b4fbf9 VY |
582 | if(flags & SIGN) |
583 | num = (signed int)num; | |
b57f37fb | 584 | } |
94b4fbf9 | 585 | str = number(str, end, num, base, field_width, precision, flags); |
b57f37fb | 586 | } |
94b4fbf9 VY |
587 | if(size > 0) |
588 | { | |
589 | if(str < end) | |
b57f37fb WP |
590 | *str = '\0'; |
591 | else | |
592 | end[-1] = '\0'; | |
593 | } | |
594 | /* the trailing null byte doesn't count towards the total */ | |
94b4fbf9 | 595 | return str - buf; |
b57f37fb WP |
596 | } |
597 | ||
598 | /** | |
599 | * snprintf - Format a string and place it in a buffer | |
600 | * @buf: The buffer to place the result into | |
601 | * @size: The size of the buffer, including the trailing null space | |
602 | * @fmt: The format string to use | |
603 | * @...: Arguments for the format string | |
604 | * | |
605 | * The return value is the number of characters which would be | |
606 | * generated for the given input, excluding the trailing null, | |
607 | * as per ISO C99. If the return is greater than or equal to | |
608 | * @size, the resulting string is truncated. | |
609 | */ | |
94b4fbf9 VY |
610 | int |
611 | rb_snprintf(char *buf, size_t size, const char *fmt, ...) | |
b57f37fb WP |
612 | { |
613 | va_list args; | |
614 | int i; | |
615 | ||
616 | va_start(args, fmt); | |
94b4fbf9 | 617 | i = rb_vsnprintf(buf, size, fmt, args); |
b57f37fb WP |
618 | va_end(args); |
619 | return i; | |
620 | } | |
621 | ||
622 | /** | |
623 | * vsprintf - Format a string and place it in a buffer | |
624 | * @buf: The buffer to place the result into | |
625 | * @fmt: The format string to use | |
626 | * @args: Arguments for the format string | |
627 | * | |
628 | * The function returns the number of characters written | |
629 | * into @buf. Use vsnprintf() or vscnprintf() in order to avoid | |
630 | * buffer overflows. | |
631 | * | |
632 | * Call this function if you are already dealing with a va_list. | |
633 | * You probably want sprintf() instead. | |
634 | */ | |
94b4fbf9 VY |
635 | int |
636 | rb_vsprintf(char *buf, const char *fmt, va_list args) | |
b57f37fb WP |
637 | { |
638 | return rb_vsnprintf(buf, INT_MAX, fmt, args); | |
639 | } | |
640 | ||
641 | /** | |
642 | * sprintf - Format a string and place it in a buffer | |
643 | * @buf: The buffer to place the result into | |
644 | * @fmt: The format string to use | |
645 | * @...: Arguments for the format string | |
646 | * | |
647 | * The function returns the number of characters written | |
648 | * into @buf. Use snprintf() or scnprintf() in order to avoid | |
649 | * buffer overflows. | |
650 | */ | |
94b4fbf9 VY |
651 | int |
652 | rb_sprintf(char *buf, const char *fmt, ...) | |
b57f37fb WP |
653 | { |
654 | va_list args; | |
655 | int i; | |
656 | ||
657 | va_start(args, fmt); | |
94b4fbf9 | 658 | i = rb_vsnprintf(buf, INT_MAX, fmt, args); |
b57f37fb WP |
659 | va_end(args); |
660 | return i; | |
661 | } | |
662 | ||
663 | /* | |
664 | * rb_vsprintf_append() | |
665 | * appends sprintf formatted string to the end of the buffer | |
666 | */ | |
94b4fbf9 | 667 | |
b57f37fb WP |
668 | int |
669 | rb_vsprintf_append(char *str, const char *format, va_list ap) | |
670 | { | |
94b4fbf9 VY |
671 | size_t x = strlen(str); |
672 | return (rb_vsprintf(str + x, format, ap) + x); | |
b57f37fb WP |
673 | } |
674 | ||
675 | /* | |
676 | * rb_sprintf_append() | |
677 | * appends sprintf formatted string to the end of the buffer | |
678 | */ | |
679 | int | |
680 | rb_sprintf_append(char *str, const char *format, ...) | |
681 | { | |
94b4fbf9 VY |
682 | int x; |
683 | va_list ap; | |
684 | va_start(ap, format); | |
685 | x = rb_vsprintf_append(str, format, ap); | |
686 | va_end(ap); | |
687 | return (x); | |
b57f37fb WP |
688 | } |
689 | ||
690 | /* | |
691 | * rb_vsnprintf_append() | |
692 | * appends sprintf formatted string to the end of the buffer but not | |
693 | * exceeding len | |
694 | */ | |
695 | ||
696 | int | |
697 | rb_vsnprintf_append(char *str, size_t len, const char *format, va_list ap) | |
698 | { | |
94b4fbf9 VY |
699 | size_t x; |
700 | if(len == 0) | |
701 | return 0; | |
702 | x = strlen(str); | |
703 | ||
704 | if(len < x) | |
705 | { | |
706 | str[len - 1] = '\0'; | |
707 | return len - 1; | |
708 | } | |
709 | return (rb_vsnprintf(str + x, len - x, format, ap) + x); | |
b57f37fb WP |
710 | } |
711 | ||
712 | /* | |
713 | * rb_snprintf_append() | |
714 | * appends snprintf formatted string to the end of the buffer but not | |
715 | * exceeding len | |
716 | */ | |
717 | ||
718 | int | |
719 | rb_snprintf_append(char *str, size_t len, const char *format, ...) | |
720 | { | |
94b4fbf9 VY |
721 | int x; |
722 | va_list ap; | |
723 | va_start(ap, format); | |
724 | x = rb_vsnprintf_append(str, len, format, ap); | |
725 | va_end(ap); | |
726 | return (x); | |
b57f37fb | 727 | } |