update for HEAD-2003091401
[reactos.git] / ntoskrnl / rtl / swprintf.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/rtl/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 <internal/debug.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 int _vsnwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, va_list args)
247 {
248         int len;
249         unsigned long long num;
250         int base;
251         wchar_t * str, * end;
252         const char *s;
253         const wchar_t *sw;
254
255         int flags;              /* flags to number() */
256
257         int field_width;        /* width of output field */
258         int precision;          /* min. # of digits for integers; max
259                                    number of chars for from string */
260         int qualifier;          /* 'h', 'l', 'L', 'w' or 'I' for integer fields */
261
262         str = buf;
263         end = buf + cnt - 1;
264         if (end < buf - 1) {
265                 end = ((void *) -1);
266                 cnt = end - buf + 1;
267         }
268
269         for ( ; *fmt ; ++fmt) {
270                 if (*fmt != L'%') {
271                         if (str <= end)
272                                 *str = *fmt;
273                         ++str;
274                         continue;
275                 }
276
277                 /* process flags */
278                 flags = 0;
279                 repeat:
280                         ++fmt;          /* this also skips first '%' */
281                         switch (*fmt) {
282                                 case L'-': flags |= LEFT; goto repeat;
283                                 case L'+': flags |= PLUS; goto repeat;
284                                 case L' ': flags |= SPACE; goto repeat;
285                                 case L'#': flags |= SPECIAL; goto repeat;
286                                 case L'0': flags |= ZEROPAD; goto repeat;
287                         }
288
289                 /* get field width */
290                 field_width = -1;
291                 if (iswdigit(*fmt))
292                         field_width = skip_atoi(&fmt);
293                 else if (*fmt == L'*') {
294                         ++fmt;
295                         /* it's the next argument */
296                         field_width = va_arg(args, int);
297                         if (field_width < 0) {
298                                 field_width = -field_width;
299                                 flags |= LEFT;
300                         }
301                 }
302
303                 /* get the precision */
304                 precision = -1;
305                 if (*fmt == L'.') {
306                         ++fmt;
307                         if (iswdigit(*fmt))
308                                 precision = skip_atoi(&fmt);
309                         else if (*fmt == L'*') {
310                                 ++fmt;
311                                 /* it's the next argument */
312                                 precision = va_arg(args, int);
313                         }
314                         if (precision < 0)
315                                 precision = 0;
316                 }
317
318                 /* get the conversion qualifier */
319                 qualifier = -1;
320                 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'w') {
321                         qualifier = *fmt;
322                         ++fmt;
323                 } else if (*fmt == 'I' && *(fmt+1) == '6' && *(fmt+2) == '4') {
324                         qualifier = *fmt;
325                         fmt += 3;
326                 }
327
328                 /* default base */
329                 base = 10;
330
331                 switch (*fmt) {
332                 case L'c':
333                         if (!(flags & LEFT))
334                                 while (--field_width > 0) {
335                                         if (str <= end)
336                                                 *str = L' ';
337                                         ++str;
338                                 }
339                         if (qualifier == 'h') {
340                                 if (str <= end)
341                                         *str = (wchar_t) va_arg(args, int);
342                                 ++str;
343                         } else {
344                                 if (str <= end)
345                                         *str = (wchar_t) va_arg(args, int);
346                                 ++str;
347                         }
348                         while (--field_width > 0) {
349                                 if (str <= end)
350                                         *str = L' ';
351                                 ++str;
352                         }
353                         continue;
354
355                 case L'C':
356                         if (!(flags & LEFT))
357                                 while (--field_width > 0) {
358                                         if (str <= end)
359                                                 *str = L' ';
360                                         ++str;
361                                 }
362                         if (qualifier == 'l' || qualifier == 'w') {
363                                 if (str <= end)
364                                         *str = (wchar_t) va_arg(args, int);
365                                 ++str;
366                         } else {
367                                 if (str <= end)
368                                         *str = (wchar_t) va_arg(args, int);
369                                 ++str;
370                         }
371                         while (--field_width > 0) {
372                                 if (str <= end)
373                                         *str = L' ';
374                                 ++str;
375                         }
376                         continue;
377
378                 case L's':
379                         if (qualifier == 'h') {
380                                 /* print ascii string */
381                                 s = va_arg(args, char *);
382                                 str = string(str, end, s, -1,  field_width, precision, flags);
383                         } else {
384                                 /* print unicode string */
385                                 sw = va_arg(args, wchar_t *);
386                                 str = stringw(str, end, sw, -1, field_width, precision, flags);
387                         }
388                         continue;
389
390                 case L'S':
391                         if (qualifier == 'l' || qualifier == 'w') {
392                                 /* print unicode string */
393                                 sw = va_arg(args, wchar_t *);
394                                 str = stringw(str, end, sw, -1, field_width, precision, flags);
395                         } else {
396                                 /* print ascii string */
397                                 s = va_arg(args, char *);
398                                 str = string(str, end, s, -1,  field_width, precision, flags);
399                         }
400                         continue;
401
402                 case L'Z':
403                         if (qualifier == 'h') {
404                                 /* print counted ascii string */
405                                 PANSI_STRING pus = va_arg(args, PANSI_STRING);
406                                 if ((pus == NULL) || (pus->Buffer == NULL)) {
407                                         s = NULL;
408                                         len = -1;
409                                 } else {
410                                         s = pus->Buffer;
411                                         len = pus->Length;
412                                 }
413                                 str = string(str, end, s, len,  field_width, precision, flags);
414                         } else {
415                                 /* print counted unicode string */
416                                 PUNICODE_STRING pus = va_arg(args, PUNICODE_STRING);
417                                 if ((pus == NULL) || (pus->Buffer == NULL)) {
418                                         sw = NULL;
419                                         len = -1;
420                                 } else {
421                                         sw = pus->Buffer;
422                                         len = pus->Length / sizeof(WCHAR);
423                                 }
424                                 str = stringw(str, end, sw, len,  field_width, precision, flags);
425                         }
426                         continue;
427
428                 case L'p':
429                         if (field_width == -1) {
430                                 field_width = 2*sizeof(void *);
431                                 flags |= ZEROPAD;
432                         }
433                         str = number(str, end,
434                                 (unsigned long) va_arg(args, void *), 16,
435                                 field_width, precision, flags);
436                         continue;
437
438                 case L'n':
439                         /* FIXME: What does C99 say about the overflow case here? */
440                         if (qualifier == 'l') {
441                                 long * ip = va_arg(args, long *);
442                                 *ip = (str - buf);
443                         } else {
444                                 int * ip = va_arg(args, int *);
445                                 *ip = (str - buf);
446                         }
447                         continue;
448
449                 /* integer number formats - set up the flags and "break" */
450                 case L'o':
451                         base = 8;
452                         break;
453
454                 case L'b':
455                         base = 2;
456                         break;
457
458                 case L'X':
459                         flags |= LARGE;
460                 case L'x':
461                         base = 16;
462                         break;
463
464                 case L'd':
465                 case L'i':
466                         flags |= SIGN;
467                 case L'u':
468                         break;
469
470                 default:
471                         if (*fmt != L'%') {
472                                 if (str <= end)
473                                         *str = L'%';
474                                 ++str;
475                         }
476                         if (*fmt) {
477                                 if (str <= end)
478                                         *str = *fmt;
479                                 ++str;
480                         } else
481                                 --fmt;
482                         continue;
483                 }
484
485                 if (qualifier == 'I')
486                         num = va_arg(args, unsigned long long);
487                 else if (qualifier == 'l')
488                         num = va_arg(args, unsigned long);
489                 else if (qualifier == 'h') {
490                         if (flags & SIGN)
491                                 num = va_arg(args, int);
492                         else
493                                 num = va_arg(args, unsigned int);
494                 }
495                 else {
496                         if (flags & SIGN)
497                                 num = va_arg(args, int);
498                         else
499                                 num = va_arg(args, unsigned int);
500                 }
501                 str = number(str, end, num, base, field_width, precision, flags);
502         }
503         if (str <= end)
504                 *str = L'\0';
505         else if (cnt > 0)
506                 /* don't write out a null byte if the buf size is zero */
507                 *end = L'\0';
508         return str-buf;
509 }
510
511
512 /*
513  * @implemented
514  */
515 int swprintf(wchar_t *buf, const wchar_t *fmt, ...)
516 {
517         va_list args;
518         int i;
519
520         va_start(args, fmt);
521         i=_vsnwprintf(buf,INT_MAX,fmt,args);
522         va_end(args);
523         return i;
524 }
525
526
527 /*
528  * @implemented
529  */
530 int _snwprintf(wchar_t *buf, size_t cnt, const wchar_t *fmt, ...)
531 {
532         va_list args;
533         int i;
534
535         va_start(args, fmt);
536         i=_vsnwprintf(buf,cnt,fmt,args);
537         va_end(args);
538         return i;
539 }
540
541
542 int vswprintf(wchar_t *buf, const wchar_t *fmt, va_list args)
543 {
544         return _vsnwprintf(buf,INT_MAX,fmt,args);
545 }
546
547 /* EOF */