Sync with trunk head (part 1 of 2)
[reactos.git] / lib / rtl / swprintf.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 wchar_t **s)
80 {
81 int i=0;
82
83 while (iswdigit(**s))
84 i = i*10 + *((*s)++) - L'0';
85 return i;
86 }
87
88
89 static wchar_t *
90 number(wchar_t * buf, wchar_t * end, long long num, int base, int size, int precision, int type)
91 {
92 wchar_t c, sign, tmp[66];
93 const wchar_t *digits;
94 const wchar_t *small_digits = L"0123456789abcdefghijklmnopqrstuvwxyz";
95 const wchar_t *large_digits = L"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) ? L'0' : L' ';
104 sign = 0;
105 if (type & SIGN) {
106 if (num < 0) {
107 sign = L'-';
108 num = -num;
109 size--;
110 } else if (type & PLUS) {
111 sign = L'+';
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 i = 0;
124 if ((num == 0) && (precision !=0))
125 tmp[i++] = L'0';
126 else while (num != 0)
127 tmp[i++] = digits[do_div(&num,base)];
128 if (i > precision)
129 precision = i;
130 size -= precision;
131 if (!(type&(ZEROPAD+LEFT))) {
132 while(size-->0) {
133 if (buf <= end)
134 *buf = L' ';
135 ++buf;
136 }
137 }
138 if (sign) {
139 if (buf <= end)
140 *buf = sign;
141 ++buf;
142 }
143
144 if ((type & SPECIAL) && ((type & REMOVEHEX) == 0)) {
145 if (base==16) {
146 if (buf <= end)
147 *buf = L'0';
148 ++buf;
149 if (buf <= end)
150 *buf = digits[33];
151 ++buf;
152 }
153 }
154 if (!(type & LEFT)) {
155 while (size-- > 0) {
156 if (buf <= end)
157 *buf = c;
158 ++buf;
159 }
160 }
161 while (i < precision--) {
162 if (buf <= end)
163 *buf = L'0';
164 ++buf;
165 }
166 while (i-- > 0) {
167 if (buf <= end)
168 *buf = tmp[i];
169 ++buf;
170 }
171 while (size-- > 0) {
172 if (buf <= end)
173 *buf = L' ';
174 ++buf;
175 }
176
177
178 return buf;
179 }
180
181 static wchar_t *
182 numberf(wchar_t * buf, wchar_t * end, double num, int base, int size, int precision, int type)
183 {
184 wchar_t c, sign, tmp[66];
185 const wchar_t *digits;
186 const wchar_t *small_digits = L"0123456789abcdefghijklmnopqrstuvwxyz";
187 const wchar_t *large_digits = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
188 int i;
189 long long x;
190
191 /* FIXME
192 the float version of number is direcly copy of number
193 */
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) ? L'0' : L' ';
202 sign = 0;
203 if (type & SIGN) {
204 if (num < 0) {
205 sign = L'-';
206 num = -num;
207 size--;
208 } else if (type & PLUS) {
209 sign = L'+';
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++] = L'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 = L' ';
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 = L'0';
252 ++buf;
253 } else if (base==16) {
254 if (buf <= end)
255 *buf = L'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 = L'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 = L' ';
282 ++buf;
283 }
284 return buf;
285 }
286
287 static wchar_t*
288 string(wchar_t* buf, wchar_t* end, const char* s, int len, int field_width, int precision, int flags)
289 {
290 int i;
291 wchar_t c;
292
293 c = (flags & ZEROPAD) ? L'0' : L' ';
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 = L' ';
331 ++buf;
332 }
333 return buf;
334 }
335
336 static wchar_t*
337 stringw(wchar_t* buf, wchar_t* end, const wchar_t* sw, int len, int field_width, int precision, int flags)
338 {
339 int i;
340 wchar_t c;
341
342 c = (flags & ZEROPAD) ? L'0' : L' ';
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 = *sw++;
374 buf++;
375 }
376 while (len < field_width--)
377 {
378 if (buf <= end)
379 *buf = L' ';
380 buf++;
381 }
382 return buf;
383 }
384
385 /*
386 * @implemented
387 */
388 int __cdecl _vsnwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, va_list args)
389 {
390 int len;
391 unsigned long long num;
392 int base;
393 wchar_t * str, * end;
394 const char *s;
395 const wchar_t *sw;
396 const wchar_t *ss;
397 double _double;
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', 'w' or 'I' for integer fields */
405
406 str = buf;
407 end = buf + cnt - 1;
408 if (end < buf - 1) {
409 end = ((wchar_t *) -1);
410 cnt = end - buf + 1;
411 }
412
413 for ( ; *fmt ; ++fmt) {
414 if (*fmt != L'%') {
415 if (str <= end)
416 *str = *fmt;
417 ++str;
418 continue;
419 }
420
421 /* process flags */
422 flags = 0;
423 repeat:
424 ++fmt; /* this also skips first '%' */
425 switch (*fmt) {
426 case L'-': flags |= LEFT; goto repeat;
427 case L'+': flags |= PLUS; goto repeat;
428 case L' ': flags |= SPACE; goto repeat;
429 case L'#': flags |= SPECIAL; goto repeat;
430 case L'0': flags |= ZEROPAD; goto repeat;
431 }
432
433 /* get field width */
434 field_width = -1;
435 if (iswdigit(*fmt))
436 field_width = skip_atoi(&fmt);
437 else if (*fmt == L'*') {
438 ++fmt;
439 /* it's the next argument */
440 field_width = va_arg(args, int);
441 if (field_width < 0) {
442 field_width = -field_width;
443 flags |= LEFT;
444 }
445 }
446
447 /* get the precision */
448 precision = -1;
449 if (*fmt == L'.') {
450 ++fmt;
451 if (iswdigit(*fmt))
452 precision = skip_atoi(&fmt);
453 else if (*fmt == L'*') {
454 ++fmt;
455 /* it's the next argument */
456 precision = va_arg(args, int);
457 }
458 if (precision < 0)
459 precision = 0;
460 }
461
462 /* get the conversion qualifier */
463 qualifier = -1;
464 if (*fmt == L'l' && *(fmt+1) == L'l') {
465 qualifier = L'I';
466 fmt += 2;
467 } else if (*fmt == L'h' || *fmt == L'l' || *fmt == L'L' || *fmt == L'w') {
468 qualifier = *fmt;
469 ++fmt;
470 } else if (*fmt == L'I' && *(fmt+1) == L'6' && *(fmt+2) == L'4') {
471 qualifier = *fmt;
472 fmt += 3;
473 } else if (*fmt == L'I' && *(fmt+1) == L'3' && *(fmt+2) == L'2') {
474 qualifier = L'l';
475 fmt += 3;
476 } else if (*fmt == L'F' && *(fmt+1) == L'p') {
477 fmt += 1;
478 flags |= REMOVEHEX;
479 }
480
481 /* default base */
482 base = 10;
483
484 switch (*fmt) {
485 case L'c':
486 if (qualifier == 'h' || qualifier == 'w') {
487 wchar_t sw1[2];
488 /* print unicode string */
489 sw1[0] = (wchar_t) va_arg(args, int);
490 sw1[1] = 0;
491 str = stringw(str, end, (wchar_t *)&sw1, -1, field_width, precision, flags);
492 } else {
493 char s1[2];
494 /* print ascii string */
495 s1[0] = ( unsigned char) va_arg(args, int);
496 s1[1] = 0;
497 str = string(str, end, (char *)&s1, -1, field_width, precision, flags);
498 }
499
500 continue;
501
502 case L'C':
503 if (!(flags & LEFT))
504 while (--field_width > 0) {
505 if (str <= end)
506 *str = L' ';
507 ++str;
508 }
509 if (qualifier == 'l' || qualifier == 'w') {
510 if (str <= end)
511 *str = (wchar_t) va_arg(args, int);
512 ++str;
513 } else {
514 if (str <= end)
515 *str = (wchar_t) va_arg(args, int);
516 ++str;
517 }
518 while (--field_width > 0) {
519 if (str <= end)
520 *str = L' ';
521 ++str;
522 }
523 continue;
524
525 case L's':
526 if (qualifier == 'h') {
527 /* print ascii string */
528 s = va_arg(args, char *);
529 str = string(str, end, s, -1, field_width, precision, flags);
530 } else {
531 /* print unicode string */
532 sw = va_arg(args, wchar_t *);
533 str = stringw(str, end, sw, -1, field_width, precision, flags);
534 }
535 continue;
536
537 case L'S':
538 if (qualifier == 'l' || qualifier == 'w') {
539 /* print unicode string */
540 sw = va_arg(args, wchar_t *);
541 str = stringw(str, end, sw, -1, field_width, precision, flags);
542 } else {
543 /* print ascii string */
544 s = va_arg(args, char *);
545 str = string(str, end, s, -1, field_width, precision, flags);
546 }
547 continue;
548
549 case L'Z':
550 if (qualifier == 'h') {
551 /* print counted ascii string */
552 PANSI_STRING pus = va_arg(args, PANSI_STRING);
553 if ((pus == NULL) || (pus->Buffer == NULL)) {
554 s = NULL;
555 len = -1;
556 } else {
557 s = pus->Buffer;
558 len = pus->Length;
559 }
560 str = string(str, end, s, len, field_width, precision, flags);
561 } else {
562 /* print counted unicode string */
563 PUNICODE_STRING pus = va_arg(args, PUNICODE_STRING);
564 if ((pus == NULL) || (pus->Buffer == NULL)) {
565 sw = NULL;
566 len = -1;
567 } else {
568 sw = pus->Buffer;
569 len = pus->Length / sizeof(WCHAR);
570 }
571 str = stringw(str, end, sw, len, field_width, precision, flags);
572 }
573 continue;
574
575 case L'p':
576 if ((flags & LARGE) == 0)
577 flags |= LARGE;
578 if (field_width == -1) {
579 field_width = 2*sizeof(void *);
580 flags |= ZEROPAD;
581 }
582 str = number(str, end,
583 (ULONG_PTR) va_arg(args, void *), 16,
584 field_width, precision, flags);
585 continue;
586
587 case L'n':
588 /* FIXME: What does C99 say about the overflow case here? */
589 if (qualifier == 'l') {
590 long * ip = va_arg(args, long *);
591 *ip = (str - buf);
592 } else {
593 int * ip = va_arg(args, int *);
594 *ip = (str - buf);
595 }
596 continue;
597 /* float number formats - set up the flags and "break" */
598 case 'e':
599 case 'E':
600 case 'f':
601 case 'g':
602 case 'G':
603 _double = (double)va_arg(args, double);
604
605 if ( _isnan(_double) ) {
606 ss = L"Nan";
607 len = 3;
608 while ( len > 0 ) {
609 if (str <= end)
610 *str = *ss++;
611 ++str;
612 len --;
613 }
614 } else if ( _isinf(_double) < 0 ) {
615 ss = L"-Inf";
616 len = 4;
617 while ( len > 0 ) {
618 if (str <= end)
619 *str = *ss++;
620 ++str;
621 len --;
622 }
623 } else if ( _isinf(_double) > 0 ) {
624 ss = L"+Inf";
625 len = 4;
626 while ( len > 0 ) {
627 if (str <= end)
628 *str = *ss++;
629 ++str;
630 len --;
631 }
632 } else {
633 if ( precision == -1 )
634 precision = 6;
635 str = numberf(str, end, _double, base, field_width, precision, flags);
636 }
637
638 continue;
639
640
641
642 /* integer number formats - set up the flags and "break" */
643 case L'o':
644 base = 8;
645 break;
646
647 case L'b':
648 base = 2;
649 break;
650
651 case L'X':
652 flags |= LARGE;
653 case L'x':
654 base = 16;
655 break;
656
657 case L'd':
658 case L'i':
659 flags |= SIGN;
660 case L'u':
661 break;
662
663 default:
664 if (*fmt) {
665 if (str <= end)
666 *str = *fmt;
667 ++str;
668 } else
669 --fmt;
670 continue;
671 }
672
673 if (qualifier == L'I')
674 num = va_arg(args, unsigned long long);
675 else if (qualifier == L'l') {
676 if (flags & SIGN)
677 num = va_arg(args, long);
678 else
679 num = va_arg(args, unsigned long);
680 }
681 else if (qualifier == L'h') {
682 if (flags & SIGN)
683 num = va_arg(args, int);
684 else
685 num = va_arg(args, unsigned int);
686 }
687 else {
688 if (flags & SIGN)
689 num = va_arg(args, int);
690 else
691 num = va_arg(args, unsigned int);
692 }
693 str = number(str, end, num, base, field_width, precision, flags);
694 }
695 if (str <= end)
696 *str = L'\0';
697 else if (cnt > 0)
698 /* don't write out a null byte if the buf size is zero */
699 *end = L'\0';
700 return str-buf;
701 }
702
703
704 /*
705 * @implemented
706 */
707 int swprintf(wchar_t *buf, const wchar_t *fmt, ...)
708 {
709 va_list args;
710 int i;
711
712 va_start(args, fmt);
713 i=_vsnwprintf(buf,MAXLONG,fmt,args);
714 va_end(args);
715 return i;
716 }
717
718
719 /*
720 * @implemented
721 */
722 int __cdecl _snwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, ...)
723 {
724 va_list args;
725 int i;
726
727 va_start(args, fmt);
728 i=_vsnwprintf(buf,cnt,fmt,args);
729 va_end(args);
730 return i;
731 }
732
733
734 /*
735 * @implemented
736 */
737 int __cdecl vswprintf(wchar_t *buf, const wchar_t *fmt, va_list args)
738 {
739 return _vsnwprintf(buf,MAXLONG,fmt,args);
740 }
741
742 /* EOF */