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