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