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