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