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