Sync to trunk (r46918)
[reactos.git] / lib / rtl / sprintf.c
1 /*
2 * PROGRAMMERS: David Welch
3 * Eric Kohl
4 *
5 * TODO:
6 * - Verify the implementation of '%Z'.
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 #include <rtl.h>
21
22 #define ZEROPAD 1 /* pad with zero */
23 #define SIGN 2 /* unsigned/signed long */
24 #define PLUS 4 /* show plus */
25 #define SPACE 8 /* space if plus */
26 #define LEFT 16 /* left justified */
27 #define SPECIAL 32 /* 0x */
28 #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
29 #define REMOVEHEX 256 /* use 256 as remve 0x frim BASE 16 */
30 typedef struct {
31 unsigned int mantissal:32;
32 unsigned int mantissah:20;
33 unsigned int exponent:11;
34 unsigned int sign:1;
35 } double_t;
36
37 static
38 __inline
39 int
40 _isinf(double __x)
41 {
42 union
43 {
44 double* __x;
45 double_t* x;
46 } x;
47
48 x.__x = &__x;
49 return ( x.x->exponent == 0x7ff && ( x.x->mantissah == 0 && x.x->mantissal == 0 ));
50 }
51
52 static
53 __inline
54 int
55 _isnan(double __x)
56 {
57 union
58 {
59 double* __x;
60 double_t* x;
61 } x;
62 x.__x = &__x;
63 return ( x.x->exponent == 0x7ff && ( x.x->mantissah != 0 || x.x->mantissal != 0 ));
64 }
65
66
67 static
68 __inline
69 int
70 do_div(long long *n, int base)
71 {
72 int a;
73 a = ((unsigned long long) *n) % (unsigned) base;
74 *n = ((unsigned long long) *n) / (unsigned) base;
75 return a;
76 }
77
78
79 static int skip_atoi(const char **s)
80 {
81 int i=0;
82
83 while (isdigit(**s))
84 i = i*10 + *((*s)++) - '0';
85 return i;
86 }
87
88
89 static char *
90 number(char * buf, char * end, long long num, int base, int size, int precision, int type)
91 {
92 char c,sign,tmp[66];
93 const char *digits;
94 const char *small_digits = "0123456789abcdefghijklmnopqrstuvwxyz";
95 const char *large_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
96 int i;
97
98 digits = (type & LARGE) ? large_digits : small_digits;
99 if (type & LEFT)
100 type &= ~ZEROPAD;
101 if (base < 2 || base > 36)
102 return 0;
103 c = (type & ZEROPAD) ? '0' : ' ';
104 sign = 0;
105 if (type & SIGN) {
106 if (num < 0) {
107 sign = '-';
108 num = -num;
109 size--;
110 } else if (type & PLUS) {
111 sign = '+';
112 size--;
113 } else if (type & SPACE) {
114 sign = ' ';
115 size--;
116 }
117 }
118
119 if ((type & SPECIAL) && ((type & REMOVEHEX) == 0)) {
120 if (base == 16)
121 size -= 2;
122
123 }
124 i = 0;
125 if ((num == 0) && (precision !=0))
126 tmp[i++] = '0';
127 else while (num != 0)
128 tmp[i++] = digits[do_div(&num,base)];
129 if (i > precision)
130 precision = i;
131 size -= precision;
132 if (!(type&(ZEROPAD+LEFT))) {
133 while(size-->0) {
134 if (buf <= end)
135 *buf = ' ';
136 ++buf;
137 }
138 }
139 if (sign) {
140 if (buf <= end)
141 *buf = sign;
142 ++buf;
143 }
144
145 if ((type & SPECIAL) && ((type & REMOVEHEX) == 0)) {
146 if (base==16) {
147 if (buf <= end)
148 *buf = '0';
149 ++buf;
150 if (buf <= end)
151 *buf = digits[33];
152 ++buf;
153 }
154 }
155
156 if (!(type & LEFT)) {
157 while (size-- > 0) {
158 if (buf <= end)
159 *buf = c;
160 ++buf;
161 }
162 }
163 while (i < precision--) {
164 if (buf <= end)
165 *buf = '0';
166 ++buf;
167 }
168 while (i-- > 0) {
169 if (buf <= end)
170 *buf = tmp[i];
171 ++buf;
172 }
173 while (size-- > 0) {
174 if (buf <= end)
175 *buf = ' ';
176 ++buf;
177 }
178
179 return buf;
180 }
181
182 static char *
183 numberf(char * buf, char * end, double num, int base, int size, int precision, int type)
184 {
185 char c,sign,tmp[66];
186 const char *digits;
187 const char *small_digits = "0123456789abcdefghijklmnopqrstuvwxyz";
188 const char *large_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
189 int i;
190 long long x;
191
192 /* FIXME
193 the float version of number is direcly copy of number
194 */
195
196 digits = (type & LARGE) ? large_digits : small_digits;
197 if (type & LEFT)
198 type &= ~ZEROPAD;
199 if (base < 2 || base > 36)
200 return 0;
201 c = (type & ZEROPAD) ? '0' : ' ';
202 sign = 0;
203 if (type & SIGN) {
204 if (num < 0) {
205 sign = '-';
206 num = -num;
207 size--;
208 } else if (type & PLUS) {
209 sign = '+';
210 size--;
211 } else if (type & SPACE) {
212 sign = ' ';
213 size--;
214 }
215 }
216 if (type & SPECIAL) {
217 if (base == 16)
218 size -= 2;
219 else if (base == 8)
220 size--;
221 }
222 i = 0;
223 if (num == 0)
224 tmp[i++] = '0';
225 else while (num != 0)
226 {
227 x = num;
228 tmp[i++] = digits[do_div(&x,base)];
229 #ifndef _M_ARM // Missing __floatdidf in CeGCC 0.55 -- GCC 4.4
230 num=x;
231 #endif
232 }
233 if (i > precision)
234 precision = i;
235 size -= precision;
236 if (!(type&(ZEROPAD+LEFT))) {
237 while(size-->0) {
238 if (buf <= end)
239 *buf = ' ';
240 ++buf;
241 }
242 }
243 if (sign) {
244 if (buf <= end)
245 *buf = sign;
246 ++buf;
247 }
248 if (type & SPECIAL) {
249 if (base==8) {
250 if (buf <= end)
251 *buf = '0';
252 ++buf;
253 } else if (base==16) {
254 if (buf <= end)
255 *buf = '0';
256 ++buf;
257 if (buf <= end)
258 *buf = digits[33];
259 ++buf;
260 }
261 }
262 if (!(type & LEFT)) {
263 while (size-- > 0) {
264 if (buf <= end)
265 *buf = c;
266 ++buf;
267 }
268 }
269 while (i < precision--) {
270 if (buf <= end)
271 *buf = '0';
272 ++buf;
273 }
274 while (i-- > 0) {
275 if (buf <= end)
276 *buf = tmp[i];
277 ++buf;
278 }
279 while (size-- > 0) {
280 if (buf <= end)
281 *buf = ' ';
282 ++buf;
283 }
284 return buf;
285 }
286
287 static char*
288 string(char* buf, char* end, const char* s, int len, int field_width, int precision, int flags)
289 {
290 int i;
291 char c;
292
293 c = (flags & ZEROPAD) ? '0' : ' ';
294
295 if (s == NULL)
296 {
297 s = "<NULL>";
298 len = 6;
299 }
300 else
301 {
302 if (len == -1)
303 {
304 len = 0;
305 while ((unsigned int)len < (unsigned int)precision && s[len])
306 len++;
307 }
308 else
309 {
310 if ((unsigned int)len > (unsigned int)precision)
311 len = precision;
312 }
313 }
314 if (!(flags & LEFT))
315 while (len < field_width--)
316 {
317 if (buf <= end)
318 *buf = c;
319 ++buf;
320 }
321 for (i = 0; i < len; ++i)
322 {
323 if (buf <= end)
324 *buf = *s++;
325 ++buf;
326 }
327 while (len < field_width--)
328 {
329 if (buf <= end)
330 *buf = ' ';
331 ++buf;
332 }
333 return buf;
334 }
335
336 static char*
337 stringw(char* buf, char* end, const wchar_t* sw, int len, int field_width, int precision, int flags)
338 {
339 int i;
340 char c;
341
342 c = (flags & ZEROPAD) ? '0' : ' ';
343
344 if (sw == NULL)
345 {
346 sw = L"<NULL>";
347 len = 6;
348 }
349 else
350 {
351 if (len == -1)
352 {
353 len = 0;
354 while ((unsigned int)len < (unsigned int)precision && sw[len])
355 len++;
356 }
357 else
358 {
359 if ((unsigned int)len > (unsigned int)precision)
360 len = precision;
361 }
362 }
363 if (!(flags & LEFT))
364 while (len < field_width--)
365 {
366 if (buf <= end)
367 *buf = c;
368 buf++;
369 }
370 for (i = 0; i < len; ++i)
371 {
372 if (buf <= end)
373 *buf = (unsigned char)(*sw++);
374 buf++;
375 }
376 while (len < field_width--)
377 {
378 if (buf <= end)
379 *buf = ' ';
380 buf++;
381 }
382 return buf;
383 }
384
385 /*
386 * @implemented
387 */
388 int __cdecl _vsnprintf(char *buf, size_t cnt, const char *fmt, va_list args)
389 {
390 int len;
391 unsigned long long num;
392 double _double;
393
394 int base;
395 char *str, *end;
396 const char *s;
397 const wchar_t *sw;
398
399 int flags; /* flags to number() */
400
401 int field_width; /* width of output field */
402 int precision; /* min. # of digits for integers; max
403 number of chars for from string */
404 int qualifier; /* 'h', 'l', 'L', 'I' or 'w' for integer fields */
405
406 /* clear the string buffer with zero so we do not need NULL terment it at end */
407
408 str = buf;
409 end = buf + cnt - 1;
410 if (end < buf - 1) {
411 end = ((char *) -1);
412 cnt = end - buf + 1;
413 }
414
415 for ( ; *fmt ; ++fmt) {
416 if (*fmt != '%') {
417 if (str <= end)
418 *str = *fmt;
419 ++str;
420 continue;
421 }
422
423 /* process flags */
424 flags = 0;
425 repeat:
426 ++fmt; /* this also skips first '%' */
427 switch (*fmt) {
428 case '-': flags |= LEFT; goto repeat;
429 case '+': flags |= PLUS; goto repeat;
430 case ' ': flags |= SPACE; goto repeat;
431 case '#': flags |= SPECIAL; goto repeat;
432 case '0': flags |= ZEROPAD; goto repeat;
433 }
434
435 /* get field width */
436 field_width = -1;
437 if (isdigit(*fmt))
438 field_width = skip_atoi(&fmt);
439 else if (*fmt == '*') {
440 ++fmt;
441 /* it's the next argument */
442 field_width = va_arg(args, int);
443 if (field_width < 0) {
444 field_width = -field_width;
445 flags |= LEFT;
446 }
447 }
448
449 /* get the precision */
450 precision = -1;
451 if (*fmt == '.') {
452 ++fmt;
453 if (isdigit(*fmt))
454 precision = skip_atoi(&fmt);
455 else if (*fmt == '*') {
456 ++fmt;
457 /* it's the next argument */
458 precision = va_arg(args, int);
459 }
460 if (precision < 0)
461 precision = 0;
462 }
463
464 /* get the conversion qualifier */
465 qualifier = -1;
466 if (*fmt == 'l' && *(fmt+1) == 'l') {
467 qualifier = 'I';
468 fmt += 2;
469 } else if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'w') {
470 qualifier = *fmt;
471 ++fmt;
472 } else if (*fmt == 'I' && *(fmt+1) == '6' && *(fmt+2) == '4') {
473 qualifier = *fmt;
474 fmt += 3;
475 } else if (*fmt == 'I' && *(fmt+1) == '3' && *(fmt+2) == '2') {
476 qualifier = 'l';
477 fmt += 3;
478 } else if (*fmt == 'F' && *(fmt+1) == 'p') {
479 fmt += 1;
480 flags |= REMOVEHEX;
481 }
482
483 /* default base */
484 base = 10;
485
486 switch (*fmt) {
487 case 'c': /* finished */
488 if (qualifier == 'l' || qualifier == 'w') {
489 wchar_t sw1[2];
490 /* print unicode string */
491 sw1[0] = (wchar_t) va_arg(args, int);
492 sw1[1] = 0;
493 str = stringw(str, end, (wchar_t *)&sw1, -1, field_width, precision, flags);
494 } else {
495 char s1[2];
496 /* print ascii string */
497 s1[0] = ( unsigned char) va_arg(args, int);
498 s1[1] = 0;
499 str = string(str, end, (char *)&s1, -1, field_width, precision, flags);
500 }
501 continue;
502
503 case 'C': /* finished */
504 if (!(flags & LEFT))
505 while (--field_width > 0) {
506 if (str <= end)
507 *str = ' ';
508 ++str;
509 }
510 if (qualifier == 'h') {
511 if (str <= end)
512 *str = (unsigned char) va_arg(args, int);
513 ++str;
514 } else {
515 if (str <= end)
516 *str = (unsigned char)(wchar_t) va_arg(args, int);
517 ++str;
518 }
519 while (--field_width > 0) {
520 if (str <= end)
521 *str = ' ';
522 ++str;
523 }
524 continue;
525
526 case 's': /* finished */
527 if (qualifier == 'l' || qualifier == 'w') {
528 /* print unicode string */
529 sw = va_arg(args, wchar_t *);
530 str = stringw(str, end, sw, -1, field_width, precision, flags);
531 } else {
532 /* print ascii string */
533 s = va_arg(args, char *);
534 str = string(str, end, s, -1, field_width, precision, flags);
535 }
536 continue;
537
538 case 'S':
539 if (qualifier == 'h') {
540 /* print ascii string */
541 s = va_arg(args, char *);
542 str = string(str, end, s, -1, field_width, precision, flags);
543 } else {
544 /* print unicode string */
545 sw = va_arg(args, wchar_t *);
546 str = stringw(str, end, sw, -1, field_width, precision, flags);
547 }
548 continue;
549
550 case 'Z':
551 if (qualifier == 'w') {
552 /* print counted unicode string */
553 PUNICODE_STRING pus = va_arg(args, PUNICODE_STRING);
554 if ((pus == NULL) || (pus->Buffer == NULL)) {
555 sw = NULL;
556 len = -1;
557 } else {
558 sw = pus->Buffer;
559 len = pus->Length / sizeof(WCHAR);
560 }
561 str = stringw(str, end, sw, len, field_width, precision, flags);
562 } else {
563 /* print counted ascii string */
564 PANSI_STRING pus = va_arg(args, PANSI_STRING);
565 if ((pus == NULL) || (pus->Buffer == NULL)) {
566 s = NULL;
567 len = -1;
568 } else {
569 s = pus->Buffer;
570 len = pus->Length;
571 }
572 str = string(str, end, s, len, field_width, precision, flags);
573 }
574 continue;
575
576 case 'p':
577 if ((flags & LARGE) == 0)
578 flags |= LARGE;
579
580 if (field_width == -1) {
581 field_width = 2 * sizeof(void *);
582 flags |= ZEROPAD;
583 }
584 str = number(str, end,
585 (ULONG_PTR) va_arg(args, void *), 16,
586 field_width, precision, flags);
587 continue;
588
589 case 'n':
590 /* FIXME: What does C99 say about the overflow case here? */
591 if (qualifier == 'l') {
592 long * ip = va_arg(args, long *);
593 *ip = (str - buf);
594 } else {
595 int * ip = va_arg(args, int *);
596 *ip = (str - buf);
597 }
598 continue;
599
600 /* float number formats - set up the flags and "break" */
601 case 'e':
602 case 'E':
603 case 'f':
604 case 'g':
605 case 'G':
606 _double = (double)va_arg(args, double);
607 if ( _isnan(_double) ) {
608 s = "Nan";
609 len = 3;
610 while ( len > 0 ) {
611 if (str <= end)
612 *str = *s++;
613 ++str;
614 len --;
615 }
616 } else if ( _isinf(_double) < 0 ) {
617 s = "-Inf";
618 len = 4;
619 while ( len > 0 ) {
620 if (str <= end)
621 *str = *s++;
622 ++str;
623 len --;
624 }
625 } else if ( _isinf(_double) > 0 ) {
626 s = "+Inf";
627 len = 4;
628 while ( len > 0 ) {
629 if (str <= end)
630 *str = *s++;
631 ++str;
632 len --;
633 }
634 } else {
635 if ( precision == -1 )
636 precision = 6;
637 str = numberf(str, end, (int)_double, base, field_width, precision, flags);
638 }
639
640 continue;
641
642
643 /* integer number formats - set up the flags and "break" */
644 case 'o':
645 base = 8;
646 break;
647
648 case 'b':
649 base = 2;
650 break;
651
652 case 'X':
653 flags |= LARGE;
654 case 'x':
655 base = 16;
656 break;
657
658 case 'd':
659 case 'i':
660 flags |= SIGN;
661 case 'u':
662 break;
663
664 default:
665 if (*fmt) {
666 if (str <= end)
667 *str = *fmt;
668 ++str;
669 } else
670 --fmt;
671 continue;
672 }
673
674 if (qualifier == 'I')
675 num = va_arg(args, unsigned long long);
676 else if (qualifier == 'l') {
677 if (flags & SIGN)
678 num = va_arg(args, long);
679 else
680 num = va_arg(args, unsigned long);
681 }
682 else if (qualifier == 'h') {
683 if (flags & SIGN)
684 num = va_arg(args, int);
685 else
686 num = va_arg(args, unsigned int);
687 }
688 else {
689 if (flags & SIGN)
690 num = va_arg(args, int);
691 else
692 num = va_arg(args, unsigned int);
693 }
694 str = number(str, end, num, base, field_width, precision, flags);
695 }
696 if (str <= end)
697 *str = '\0';
698 else if (cnt > 0)
699 /* don't write out a null byte if the buf size is zero */
700 *end = '\0';
701 return str-buf;
702 }
703
704
705 /*
706 * @implemented
707 */
708 int sprintf(char * buf, const char *fmt, ...)
709 {
710 va_list args;
711 int i;
712
713 va_start(args, fmt);
714 i=_vsnprintf(buf,MAXLONG,fmt,args);
715 va_end(args);
716 return i;
717 }
718
719
720 /*
721 * @implemented
722 */
723 int _snprintf(char * buf, size_t cnt, const char *fmt, ...)
724 {
725 va_list args;
726 int i;
727
728 va_start(args, fmt);
729 i=_vsnprintf(buf,cnt,fmt,args);
730 va_end(args);
731 return i;
732 }
733
734
735 /*
736 * @implemented
737 */
738 int __cdecl vsprintf(char *buf, const char *fmt, va_list args)
739 {
740 return _vsnprintf(buf,MAXLONG,fmt,args);
741 }
742
743 /* EOF */