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