update for HEAD-2003091401
[reactos.git] / lib / ntdll / stdio / swprintf.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            lib/ntdll/stdio/swprintf.c
6  * PURPOSE:         unicode sprintf functions
7  * PROGRAMMERS:     David Welch
8  *                  Eric Kohl
9  *
10  * TODO:
11  *   - Verify the implementation of '%Z'.
12  */
13
14 /*
15  *  linux/lib/vsprintf.c
16  *
17  *  Copyright (C) 1991, 1992  Linus Torvalds
18  */
19
20 /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
21 /*
22  * Wirzenius wrote this portably, Torvalds fucked it up :-)
23  */
24
25 #include <ddk/ntddk.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <wchar.h>
30 #include <limits.h>
31
32 #define NDEBUG
33 #include <ntdll/ntdll.h>
34
35
36 #define ZEROPAD 1               /* pad with zero */
37 #define SIGN    2               /* unsigned/signed long */
38 #define PLUS    4               /* show plus */
39 #define SPACE   8               /* space if plus */
40 #define LEFT    16              /* left justified */
41 #define SPECIAL 32              /* 0x */
42 #define LARGE   64              /* use 'ABCDEF' instead of 'abcdef' */
43
44
45 #define do_div(n,base) ({ \
46 int __res; \
47 __res = ((unsigned long long) n) % (unsigned) base; \
48 n = ((unsigned long long) n) / (unsigned) base; \
49 __res; })
50
51
52 static int skip_atoi(const wchar_t **s)
53 {
54         int i=0;
55
56         while (iswdigit(**s))
57                 i = i*10 + *((*s)++) - L'0';
58         return i;
59 }
60
61
62 static wchar_t *
63 number(wchar_t * buf, wchar_t * end, long long num, int base, int size, int precision, int type)
64 {
65         wchar_t c, sign, tmp[66];
66         const wchar_t *digits;
67         const wchar_t *small_digits = L"0123456789abcdefghijklmnopqrstuvwxyz";
68         const wchar_t *large_digits = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
69         int i;
70
71         digits = (type & LARGE) ? large_digits : small_digits;
72         if (type & LEFT)
73                 type &= ~ZEROPAD;
74         if (base < 2 || base > 36)
75                 return 0;
76         c = (type & ZEROPAD) ? L'0' : L' ';
77         sign = 0;
78         if (type & SIGN) {
79                 if (num < 0) {
80                         sign = L'-';
81                         num = -num;
82                         size--;
83                 } else if (type & PLUS) {
84                         sign = L'+';
85                         size--;
86                 } else if (type & SPACE) {
87                         sign = ' ';
88                         size--;
89                 }
90         }
91         if (type & SPECIAL) {
92                 if (base == 16)
93                         size -= 2;
94                 else if (base == 8)
95                         size--;
96         }
97         i = 0;
98         if (num == 0)
99                 tmp[i++] = L'0';
100         else while (num != 0)
101                 tmp[i++] = digits[do_div(num,base)];
102         if (i > precision)
103                 precision = i;
104         size -= precision;
105         if (!(type&(ZEROPAD+LEFT))) {
106                 while(size-->0) {
107                         if (buf <= end)
108                                 *buf = L' ';
109                         ++buf;
110                 }
111         }
112         if (sign) {
113                 if (buf <= end)
114                         *buf = sign;
115                 ++buf;
116         }
117         if (type & SPECIAL) {
118                 if (base==8) {
119                         if (buf <= end)
120                                 *buf = L'0';
121                         ++buf;
122                 } else if (base==16) {
123                         if (buf <= end)
124                                 *buf = L'0';
125                         ++buf;
126                         if (buf <= end)
127                                 *buf = digits[33];
128                         ++buf;
129                 }
130         }
131         if (!(type & LEFT)) {
132                 while (size-- > 0) {
133                         if (buf <= end)
134                                 *buf = c;
135                         ++buf;
136                 }
137         }
138         while (i < precision--) {
139                 if (buf <= end)
140                         *buf = L'0';
141                 ++buf;
142         }
143         while (i-- > 0) {
144                 if (buf <= end)
145                         *buf = tmp[i];
146                 ++buf;
147         }
148         while (size-- > 0) {
149                 if (buf <= end)
150                         *buf = L' ';
151                 ++buf;
152         }
153         return buf;
154 }
155
156 static wchar_t*
157 string(wchar_t* buf, wchar_t* end, const char* s, int len, int field_width, int precision, int flags)
158 {
159         int i;
160         if (s == NULL)
161         {
162                 s = "<NULL>";
163                 len = 6;
164         }
165         else
166         {
167                 if (len == -1)
168                 {
169                         len = 0;
170                         while (s[len] && (unsigned int)len < (unsigned int)precision)
171                                 len++;
172                 }
173                 else
174                 {
175                         if ((unsigned int)len > (unsigned int)precision)
176                                 len = precision;
177                 }
178         }
179         if (!(flags & LEFT))
180                 while (len < field_width--)
181                 {
182                         if (buf <= end)
183                                 *buf = L' ';
184                         ++buf;
185                 }
186         for (i = 0; i < len; ++i)
187         {
188                 if (buf <= end)
189                         *buf = *s++;
190                 ++buf;
191         }
192         while (len < field_width--)
193         {
194                 if (buf <= end)
195                         *buf = L' ';
196                 ++buf;
197         }
198         return buf;
199 }
200
201 static wchar_t* 
202 stringw(wchar_t* buf, wchar_t* end, const wchar_t* sw, int len, int field_width, int precision, int flags)
203 {
204         int i;
205         if (sw == NULL)
206         {
207                 sw = L"<NULL>";
208                 len = 6;
209         }
210         else
211         {
212                 if (len == -1)
213                 {
214                         len = 0;
215                         while (sw[len] && (unsigned int)len < (unsigned int)precision)
216                                 len++;
217                 }
218                 else
219                 {
220                         if ((unsigned int)len > (unsigned int)precision)
221                                 len = precision;
222                 }
223         }
224         if (!(flags & LEFT))
225                 while (len < field_width--)
226                 {
227                         if (buf <= end)
228                                 *buf = L' ';
229                         buf++;
230                 }
231         for (i = 0; i < len; ++i)
232         {
233                 if (buf <= end)
234                         *buf = *sw++;
235                 buf++;
236         }
237         while (len < field_width--)
238         {
239                 if (buf <= end)
240                         *buf = L' ';
241                 buf++;
242         }
243         return buf;
244 }
245
246
247 int _vsnwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, va_list args)
248 {
249         int len;
250         unsigned long long num;
251         int base;
252         wchar_t * str, * end;
253         const char *s;
254         const wchar_t *sw;
255
256         int flags;              /* flags to number() */
257
258         int field_width;        /* width of output field */
259         int precision;          /* min. # of digits for integers; max
260                                    number of chars for from string */
261         int qualifier;          /* 'h', 'l', 'L', 'w' or 'I' for integer fields */
262
263         str = buf;
264         end = buf + cnt - 1;
265         if (end < buf - 1) {
266                 end = ((void *) -1);
267                 cnt = end - buf + 1;
268         }
269
270         for ( ; *fmt ; ++fmt) {
271                 if (*fmt != L'%') {
272                         if (str <= end)
273                                 *str = *fmt;
274                         ++str;
275                         continue;
276                 }
277
278                 /* process flags */
279                 flags = 0;
280                 repeat:
281                         ++fmt;          /* this also skips first '%' */
282                         switch (*fmt) {
283                                 case L'-': flags |= LEFT; goto repeat;
284                                 case L'+': flags |= PLUS; goto repeat;
285                                 case L' ': flags |= SPACE; goto repeat;
286                                 case L'#': flags |= SPECIAL; goto repeat;
287                                 case L'0': flags |= ZEROPAD; goto repeat;
288                         }
289
290                 /* get field width */
291                 field_width = -1;
292                 if (iswdigit(*fmt))
293                         field_width = skip_atoi(&fmt);
294                 else if (*fmt == L'*') {
295                         ++fmt;
296                         /* it's the next argument */
297                         field_width = va_arg(args, int);
298                         if (field_width < 0) {
299                                 field_width = -field_width;
300                                 flags |= LEFT;
301                         }
302                 }
303
304                 /* get the precision */
305                 precision = -1;
306                 if (*fmt == L'.') {
307                         ++fmt;
308                         if (iswdigit(*fmt))
309                                 precision = skip_atoi(&fmt);
310                         else if (*fmt == L'*') {
311                                 ++fmt;
312                                 /* it's the next argument */
313                                 precision = va_arg(args, int);
314                         }
315                         if (precision < 0)
316                                 precision = 0;
317                 }
318
319                 /* get the conversion qualifier */
320                 qualifier = -1;
321                 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'w') {
322                         qualifier = *fmt;
323                         ++fmt;
324                 } else if (*fmt == 'I' && *(fmt+1) == '6' && *(fmt+2) == '4') {
325                         qualifier = *fmt;
326                         fmt += 3;
327                 }
328
329                 /* default base */
330                 base = 10;
331
332                 switch (*fmt) {
333                 case L'c':
334                         if (!(flags & LEFT))
335                                 while (--field_width > 0) {
336                                         if (str <= end)
337                                                 *str = L' ';
338                                         ++str;
339                                 }
340                         if (qualifier == 'h') {
341                                 if (str <= end)
342                                         *str = (wchar_t) va_arg(args, int);
343                                 ++str;
344                         } else {
345                                 if (str <= end)
346                                         *str = (wchar_t) va_arg(args, int);
347                                 ++str;
348                         }
349                         while (--field_width > 0) {
350                                 if (str <= end)
351                                         *str = L' ';
352                                 ++str;
353                         }
354                         continue;
355
356                 case L'C':
357                         if (!(flags & LEFT))
358                                 while (--field_width > 0) {
359                                         if (str <= end)
360                                                 *str = L' ';
361                                         ++str;
362                                 }
363                         if (qualifier == 'l' || qualifier == 'w') {
364                                 if (str <= end)
365                                         *str = (wchar_t) va_arg(args, int);
366                                 ++str;
367                         } else {
368                                 if (str <= end)
369                                         *str = (wchar_t) va_arg(args, int);
370                                 ++str;
371                         }
372                         while (--field_width > 0) {
373                                 if (str <= end)
374                                         *str = L' ';
375                                 ++str;
376                         }
377                         continue;
378
379                 case L's':
380                         if (qualifier == 'h') {
381                                 /* print ascii string */
382                                 s = va_arg(args, char *);
383                                 str = string(str, end, s, -1,  field_width, precision, flags);
384                         } else {
385                                 /* print unicode string */
386                                 sw = va_arg(args, wchar_t *);
387                                 str = stringw(str, end, sw, -1, field_width, precision, flags);
388                         }
389                         continue;
390
391                 case L'S':
392                         if (qualifier == 'l' || qualifier == 'w') {
393                                 /* print unicode string */
394                                 sw = va_arg(args, wchar_t *);
395                                 str = stringw(str, end, sw, -1, field_width, precision, flags);
396                         } else {
397                                 /* print ascii string */
398                                 s = va_arg(args, char *);
399                                 str = string(str, end, s, -1,  field_width, precision, flags);
400                         }
401                         continue;
402
403                 case L'Z':
404                         if (qualifier == 'h') {
405                                 /* print counted ascii string */
406                                 PANSI_STRING pus = va_arg(args, PANSI_STRING);
407                                 if ((pus == NULL) || (pus->Buffer == NULL)) {
408                                         s = NULL;
409                                         len = -1;
410                                 } else {
411                                         s = pus->Buffer;
412                                         len = pus->Length;
413                                 }
414                                 str = string(str, end, s, len,  field_width, precision, flags);
415                         } else {
416                                 /* print counted unicode string */
417                                 PUNICODE_STRING pus = va_arg(args, PUNICODE_STRING);
418                                 if ((pus == NULL) || (pus->Buffer == NULL)) {
419                                         sw = NULL;
420                                         len = -1;
421                                 } else {
422                                         sw = pus->Buffer;
423                                         len = pus->Length / sizeof(WCHAR);
424                                 }
425                                 str = stringw(str, end, sw, len,  field_width, precision, flags);
426                         }
427                         continue;
428
429                 case L'p':
430                         if (field_width == -1) {
431                                 field_width = 2*sizeof(void *);
432                                 flags |= ZEROPAD;
433                         }
434                         str = number(str, end,
435                                 (unsigned long) va_arg(args, void *), 16,
436                                 field_width, precision, flags);
437                         continue;
438
439                 case L'n':
440                         /* FIXME: What does C99 say about the overflow case here? */
441                         if (qualifier == 'l') {
442                                 long * ip = va_arg(args, long *);
443                                 *ip = (str - buf);
444                         } else {
445                                 int * ip = va_arg(args, int *);
446                                 *ip = (str - buf);
447                         }
448                         continue;
449
450                 /* integer number formats - set up the flags and "break" */
451                 case L'o':
452                         base = 8;
453                         break;
454
455                 case L'b':
456                         base = 2;
457                         break;
458
459                 case L'X':
460                         flags |= LARGE;
461                 case L'x':
462                         base = 16;
463                         break;
464
465                 case L'd':
466                 case L'i':
467                         flags |= SIGN;
468                 case L'u':
469                         break;
470
471                 default:
472                         if (*fmt != L'%') {
473                                 if (str <= end)
474                                         *str = L'%';
475                                 ++str;
476                         }
477                         if (*fmt) {
478                                 if (str <= end)
479                                         *str = *fmt;
480                                 ++str;
481                         } else
482                                 --fmt;
483                         continue;
484                 }
485
486                 if (qualifier == 'I')
487                         num = va_arg(args, unsigned long long);
488                 else if (qualifier == 'l')
489                         num = va_arg(args, unsigned long);
490                 else if (qualifier == 'h') {
491                         if (flags & SIGN)
492                                 num = va_arg(args, int);
493                         else
494                                 num = va_arg(args, unsigned int);
495                 }
496                 else {
497                         if (flags & SIGN)
498                                 num = va_arg(args, int);
499                         else
500                                 num = va_arg(args, unsigned int);
501                 }
502                 str = number(str, end, num, base, field_width, precision, flags);
503         }
504         if (str <= end)
505                 *str = L'\0';
506         else if (cnt > 0)
507                 /* don't write out a null byte if the buf size is zero */
508                 *end = L'\0';
509         return str-buf;
510 }
511
512
513 /*
514  * @implemented
515  */
516 int swprintf(wchar_t *buf, const wchar_t *fmt, ...)
517 {
518         va_list args;
519         int i;
520
521         va_start(args, fmt);
522         i=_vsnwprintf(buf,INT_MAX,fmt,args);
523         va_end(args);
524         return i;
525 }
526
527
528 /*
529  * @implemented
530  */
531 int _snwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, ...)
532 {
533         va_list args;
534         int i;
535
536         va_start(args, fmt);
537         i=_vsnwprintf(buf,cnt,fmt,args);
538         va_end(args);
539         return i;
540 }
541
542
543 int vswprintf(wchar_t *buf, const wchar_t *fmt, va_list args)
544 {
545         return _vsnwprintf(buf,INT_MAX,fmt,args);
546 }
547
548 /* EOF */