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