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