Abandoning silverblade-audio branch.
[reactos.git] / reactos / 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 num = x;
230 }
231 if (i > precision)
232 precision = i;
233 size -= precision;
234 if (!(type&(ZEROPAD+LEFT))) {
235 while(size-->0) {
236 if (buf <= end)
237 *buf = L' ';
238 ++buf;
239 }
240 }
241 if (sign) {
242 if (buf <= end)
243 *buf = sign;
244 ++buf;
245 }
246 if (type & SPECIAL) {
247 if (base==8) {
248 if (buf <= end)
249 *buf = L'0';
250 ++buf;
251 } else if (base==16) {
252 if (buf <= end)
253 *buf = L'0';
254 ++buf;
255 if (buf <= end)
256 *buf = digits[33];
257 ++buf;
258 }
259 }
260 if (!(type & LEFT)) {
261 while (size-- > 0) {
262 if (buf <= end)
263 *buf = c;
264 ++buf;
265 }
266 }
267 while (i < precision--) {
268 if (buf <= end)
269 *buf = L'0';
270 ++buf;
271 }
272 while (i-- > 0) {
273 if (buf <= end)
274 *buf = tmp[i];
275 ++buf;
276 }
277 while (size-- > 0) {
278 if (buf <= end)
279 *buf = L' ';
280 ++buf;
281 }
282 return buf;
283 }
284
285 static wchar_t*
286 string(wchar_t* buf, wchar_t* end, const char* s, int len, int field_width, int precision, int flags)
287 {
288 int i;
289 wchar_t c;
290
291 c = (flags & ZEROPAD) ? L'0' : L' ';
292
293 if (s == NULL)
294 {
295 s = "<NULL>";
296 len = 6;
297 }
298 else
299 {
300 if (len == -1)
301 {
302 len = 0;
303 while ((unsigned int)len < (unsigned int)precision && s[len])
304 len++;
305 }
306 else
307 {
308 if ((unsigned int)len > (unsigned int)precision)
309 len = precision;
310 }
311 }
312 if (!(flags & LEFT))
313 while (len < field_width--)
314 {
315 if (buf <= end)
316 *buf = c;
317 ++buf;
318 }
319 for (i = 0; i < len; ++i)
320 {
321 if (buf <= end)
322 *buf = *s++;
323 ++buf;
324 }
325 while (len < field_width--)
326 {
327 if (buf <= end)
328 *buf = L' ';
329 ++buf;
330 }
331 return buf;
332 }
333
334 static wchar_t*
335 stringw(wchar_t* buf, wchar_t* end, const wchar_t* sw, int len, int field_width, int precision, int flags)
336 {
337 int i;
338 wchar_t c;
339
340 c = (flags & ZEROPAD) ? L'0' : L' ';
341
342 if (sw == NULL)
343 {
344 sw = L"<NULL>";
345 len = 6;
346 }
347 else
348 {
349 if (len == -1)
350 {
351 len = 0;
352 while ((unsigned int)len < (unsigned int)precision && sw[len])
353 len++;
354 }
355 else
356 {
357 if ((unsigned int)len > (unsigned int)precision)
358 len = precision;
359 }
360 }
361 if (!(flags & LEFT))
362 while (len < field_width--)
363 {
364 if (buf <= end)
365 *buf = c;
366 buf++;
367 }
368 for (i = 0; i < len; ++i)
369 {
370 if (buf <= end)
371 *buf = *sw++;
372 buf++;
373 }
374 while (len < field_width--)
375 {
376 if (buf <= end)
377 *buf = L' ';
378 buf++;
379 }
380 return buf;
381 }
382
383 /*
384 * @implemented
385 */
386 int __cdecl _vsnwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, va_list args)
387 {
388 int len;
389 unsigned long long num;
390 int base;
391 wchar_t * str, * end;
392 const char *s;
393 const wchar_t *sw;
394 const wchar_t *ss;
395 double _double;
396
397 int flags; /* flags to number() */
398
399 int field_width; /* width of output field */
400 int precision; /* min. # of digits for integers; max
401 number of chars for from string */
402 int qualifier; /* 'h', 'l', 'L', 'w' or 'I' for integer fields */
403
404 str = buf;
405 end = buf + cnt - 1;
406 if (end < buf - 1) {
407 end = ((wchar_t *) -1);
408 cnt = end - buf + 1;
409 }
410
411 for ( ; *fmt ; ++fmt) {
412 if (*fmt != L'%') {
413 if (str <= end)
414 *str = *fmt;
415 ++str;
416 continue;
417 }
418
419 /* process flags */
420 flags = 0;
421 repeat:
422 ++fmt; /* this also skips first '%' */
423 switch (*fmt) {
424 case L'-': flags |= LEFT; goto repeat;
425 case L'+': flags |= PLUS; goto repeat;
426 case L' ': flags |= SPACE; goto repeat;
427 case L'#': flags |= SPECIAL; goto repeat;
428 case L'0': flags |= ZEROPAD; goto repeat;
429 }
430
431 /* get field width */
432 field_width = -1;
433 if (iswdigit(*fmt))
434 field_width = skip_atoi(&fmt);
435 else if (*fmt == L'*') {
436 ++fmt;
437 /* it's the next argument */
438 field_width = va_arg(args, int);
439 if (field_width < 0) {
440 field_width = -field_width;
441 flags |= LEFT;
442 }
443 }
444
445 /* get the precision */
446 precision = -1;
447 if (*fmt == L'.') {
448 ++fmt;
449 if (iswdigit(*fmt))
450 precision = skip_atoi(&fmt);
451 else if (*fmt == L'*') {
452 ++fmt;
453 /* it's the next argument */
454 precision = va_arg(args, int);
455 }
456 if (precision < 0)
457 precision = 0;
458 }
459
460 /* get the conversion qualifier */
461 qualifier = -1;
462 if (*fmt == L'h' || *fmt == L'l' || *fmt == L'L' || *fmt == L'w') {
463 qualifier = *fmt;
464 ++fmt;
465 } else if (*fmt == L'I' && *(fmt+1) == L'6' && *(fmt+2) == L'4') {
466 qualifier = *fmt;
467 fmt += 3;
468 } else if (*fmt == L'I' && *(fmt+1) == L'3' && *(fmt+2) == L'2') {
469 qualifier = L'l';
470 fmt += 3;
471 } else if (*fmt == L'F' && *(fmt+1) == L'p') {
472 fmt += 1;
473 flags |= REMOVEHEX;
474 }
475
476 /* default base */
477 base = 10;
478
479 switch (*fmt) {
480 case L'c':
481 if (qualifier == 'h' || qualifier == 'w') {
482 wchar_t sw1[2];
483 /* print unicode string */
484 sw1[0] = (wchar_t) va_arg(args, int);
485 sw1[1] = 0;
486 str = stringw(str, end, (wchar_t *)&sw1, -1, field_width, precision, flags);
487 } else {
488 char s1[2];
489 /* print ascii string */
490 s1[0] = ( unsigned char) va_arg(args, int);
491 s1[1] = 0;
492 str = string(str, end, (char *)&s1, -1, field_width, precision, flags);
493 }
494
495 continue;
496
497 case L'C':
498 if (!(flags & LEFT))
499 while (--field_width > 0) {
500 if (str <= end)
501 *str = L' ';
502 ++str;
503 }
504 if (qualifier == 'l' || qualifier == 'w') {
505 if (str <= end)
506 *str = (wchar_t) va_arg(args, int);
507 ++str;
508 } else {
509 if (str <= end)
510 *str = (wchar_t) va_arg(args, int);
511 ++str;
512 }
513 while (--field_width > 0) {
514 if (str <= end)
515 *str = L' ';
516 ++str;
517 }
518 continue;
519
520 case L's':
521 if (qualifier == 'h') {
522 /* print ascii string */
523 s = va_arg(args, char *);
524 str = string(str, end, s, -1, field_width, precision, flags);
525 } else {
526 /* print unicode string */
527 sw = va_arg(args, wchar_t *);
528 str = stringw(str, end, sw, -1, field_width, precision, flags);
529 }
530 continue;
531
532 case L'S':
533 if (qualifier == 'l' || qualifier == 'w') {
534 /* print unicode string */
535 sw = va_arg(args, wchar_t *);
536 str = stringw(str, end, sw, -1, field_width, precision, flags);
537 } else {
538 /* print ascii string */
539 s = va_arg(args, char *);
540 str = string(str, end, s, -1, field_width, precision, flags);
541 }
542 continue;
543
544 case L'Z':
545 if (qualifier == 'h') {
546 /* print counted ascii string */
547 PANSI_STRING pus = va_arg(args, PANSI_STRING);
548 if ((pus == NULL) || (pus->Buffer == NULL)) {
549 s = NULL;
550 len = -1;
551 } else {
552 s = pus->Buffer;
553 len = pus->Length;
554 }
555 str = string(str, end, s, len, field_width, precision, flags);
556 } else {
557 /* print counted unicode string */
558 PUNICODE_STRING pus = va_arg(args, PUNICODE_STRING);
559 if ((pus == NULL) || (pus->Buffer == NULL)) {
560 sw = NULL;
561 len = -1;
562 } else {
563 sw = pus->Buffer;
564 len = pus->Length / sizeof(WCHAR);
565 }
566 str = stringw(str, end, sw, len, field_width, precision, flags);
567 }
568 continue;
569
570 case L'p':
571 if ((flags & LARGE) == 0)
572 flags |= LARGE;
573 if (field_width == -1) {
574 field_width = 2*sizeof(void *);
575 flags |= ZEROPAD;
576 }
577 str = number(str, end,
578 (ULONG_PTR) va_arg(args, void *), 16,
579 field_width, precision, flags);
580 continue;
581
582 case L'n':
583 /* FIXME: What does C99 say about the overflow case here? */
584 if (qualifier == 'l') {
585 long * ip = va_arg(args, long *);
586 *ip = (str - buf);
587 } else {
588 int * ip = va_arg(args, int *);
589 *ip = (str - buf);
590 }
591 continue;
592 /* float number formats - set up the flags and "break" */
593 case 'e':
594 case 'E':
595 case 'f':
596 case 'g':
597 case 'G':
598 _double = (double)va_arg(args, double);
599
600 if ( _isnan(_double) ) {
601 ss = L"Nan";
602 len = 3;
603 while ( len > 0 ) {
604 if (str <= end)
605 *str = *ss++;
606 ++str;
607 len --;
608 }
609 } else if ( _isinf(_double) < 0 ) {
610 ss = L"-Inf";
611 len = 4;
612 while ( len > 0 ) {
613 if (str <= end)
614 *str = *ss++;
615 ++str;
616 len --;
617 }
618 } else if ( _isinf(_double) > 0 ) {
619 ss = L"+Inf";
620 len = 4;
621 while ( len > 0 ) {
622 if (str <= end)
623 *str = *ss++;
624 ++str;
625 len --;
626 }
627 } else {
628 if ( precision == -1 )
629 precision = 6;
630 str = numberf(str, end, _double, base, field_width, precision, flags);
631 }
632
633 continue;
634
635
636
637 /* integer number formats - set up the flags and "break" */
638 case L'o':
639 base = 8;
640 break;
641
642 case L'b':
643 base = 2;
644 break;
645
646 case L'X':
647 flags |= LARGE;
648 case L'x':
649 base = 16;
650 break;
651
652 case L'd':
653 case L'i':
654 flags |= SIGN;
655 case L'u':
656 break;
657
658 default:
659 if (*fmt) {
660 if (str <= end)
661 *str = *fmt;
662 ++str;
663 } else
664 --fmt;
665 continue;
666 }
667
668 if (qualifier == L'I')
669 num = va_arg(args, unsigned long long);
670 else if (qualifier == L'l') {
671 if (flags & SIGN)
672 num = va_arg(args, long);
673 else
674 num = va_arg(args, unsigned long);
675 }
676 else if (qualifier == L'h') {
677 if (flags & SIGN)
678 num = va_arg(args, int);
679 else
680 num = va_arg(args, unsigned int);
681 }
682 else {
683 if (flags & SIGN)
684 num = va_arg(args, int);
685 else
686 num = va_arg(args, unsigned int);
687 }
688 str = number(str, end, num, base, field_width, precision, flags);
689 }
690 if (str <= end)
691 *str = L'\0';
692 else if (cnt > 0)
693 /* don't write out a null byte if the buf size is zero */
694 *end = L'\0';
695 return str-buf;
696 }
697
698
699 /*
700 * @implemented
701 */
702 int swprintf(wchar_t *buf, const wchar_t *fmt, ...)
703 {
704 va_list args;
705 int i;
706
707 va_start(args, fmt);
708 i=_vsnwprintf(buf,MAXLONG,fmt,args);
709 va_end(args);
710 return i;
711 }
712
713
714 /*
715 * @implemented
716 */
717 int __cdecl _snwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, ...)
718 {
719 va_list args;
720 int i;
721
722 va_start(args, fmt);
723 i=_vsnwprintf(buf,cnt,fmt,args);
724 va_end(args);
725 return i;
726 }
727
728
729 /*
730 * @implemented
731 */
732 int __cdecl vswprintf(wchar_t *buf, const wchar_t *fmt, va_list args)
733 {
734 return _vsnwprintf(buf,MAXLONG,fmt,args);
735 }
736
737 /* EOF */