d0421f64ace265e4393f2c223f01022db4d0b538
[reactos.git] / reactos / lib / user32 / misc / sprintf.c
1 /* $Id: sprintf.c,v 1.5 2001/11/05 20:59:57 jimtabor Exp $
2 *
3 * user32.dll
4 *
5 * wsprintf functions
6 *
7 * Copyright 1996 Alexandre Julliard
8 *
9 * 1999-05-01 (Emanuele Aliberti)
10 * Adapted from Wine to ReactOS
11 */
12
13 #include <stdarg.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <windows.h>
17
18 #define WPRINTF_LEFTALIGN 0x0001 /* Align output on the left ('-' prefix) */
19 #define WPRINTF_PREFIX_HEX 0x0002 /* Prefix hex with 0x ('#' prefix) */
20 #define WPRINTF_ZEROPAD 0x0004 /* Pad with zeros ('0' prefix) */
21 #define WPRINTF_LONG 0x0008 /* Long arg ('l' prefix) */
22 #define WPRINTF_SHORT 0x0010 /* Short arg ('h' prefix) */
23 #define WPRINTF_UPPER_HEX 0x0020 /* Upper-case hex ('X' specifier) */
24 #define WPRINTF_WIDE 0x0040 /* Wide arg ('w' prefix) */
25
26 typedef enum
27 {
28 WPR_UNKNOWN,
29 WPR_CHAR,
30 WPR_WCHAR,
31 WPR_STRING,
32 WPR_WSTRING,
33 WPR_SIGNED,
34 WPR_UNSIGNED,
35 WPR_HEXA
36
37 } WPRINTF_TYPE;
38
39
40 typedef struct
41 {
42 UINT flags;
43 UINT width;
44 UINT precision;
45 WPRINTF_TYPE type;
46
47 } WPRINTF_FORMAT;
48
49
50 static const LPSTR null_stringA = "(null)";
51 static const LPWSTR null_stringW = L"(null)";
52
53
54 /* === COMMON === */
55
56
57 /***********************************************************************
58 * NAME PRIVATE
59 * WPRINTF_GetLen
60 *
61 * DESCRIPTION
62 * ?
63 *
64 * ARGUMENTS
65 * format
66 * ?
67 * arg
68 * ?
69 * number
70 * ?
71 * maxlen
72 * ?
73 *
74 * RETURN VALUE
75 * ?
76 */
77 static
78 UINT
79 WPRINTF_GetLen(
80 WPRINTF_FORMAT *format,
81 LPCVOID arg,
82 LPSTR number,
83 UINT maxlen
84 )
85 {
86 UINT len;
87
88 if (format->flags & WPRINTF_LEFTALIGN)
89 {
90 format->flags &= ~WPRINTF_ZEROPAD;
91 }
92 if (format->width > maxlen)
93 {
94 format->width = maxlen;
95 }
96 switch(format->type)
97 {
98 case WPR_CHAR:
99 case WPR_WCHAR:
100 return (format->precision = 1);
101
102 case WPR_STRING:
103 if (!*(LPCSTR *)arg)
104 {
105 *(LPCSTR *)arg = null_stringA;
106 }
107 for ( len = 0;
108 (!format->precision || (len < format->precision));
109 len++
110 )
111 {
112 if (!*(*(LPCSTR *)arg + len))
113 {
114 break;
115 }
116 }
117 if (len > maxlen)
118 {
119 len = maxlen;
120 }
121 return (format->precision = len);
122
123 case WPR_WSTRING:
124 if (!*(LPCWSTR *)arg)
125 {
126 *(LPCWSTR *)arg = null_stringW;
127 }
128 for ( len = 0;
129 (!format->precision || (len < format->precision));
130 len++
131 )
132 {
133 if (!*(*(LPCWSTR *)arg + len))
134 break;
135 }
136 if (len > maxlen)
137 {
138 len = maxlen;
139 }
140 return (format->precision = len);
141
142 case WPR_SIGNED:
143 len = sprintf(
144 number,
145 "%d",
146 *(INT *) arg
147 );
148 break;
149
150 case WPR_UNSIGNED:
151 len = sprintf(
152 number,
153 "%u",
154 *(UINT *) arg
155 );
156 break;
157
158 case WPR_HEXA:
159 len = sprintf(
160 number,
161 ((format->flags & WPRINTF_UPPER_HEX)
162 ? "%X"
163 : "%x"),
164 *(UINT *) arg
165 );
166 if (format->flags & WPRINTF_PREFIX_HEX)
167 {
168 len += 2;
169 }
170 break;
171
172 default:
173 return 0;
174 }
175 if (len > maxlen)
176 {
177 len = maxlen;
178 }
179 if (format->precision < len)
180 {
181 format->precision = len;
182 }
183 if (format->precision > maxlen)
184 {
185 format->precision = maxlen;
186 }
187 if ( (format->flags & WPRINTF_ZEROPAD)
188 && (format->width > format->precision)
189 )
190 {
191 format->precision = format->width;
192 }
193 return len;
194 }
195
196
197 /* === ANSI VERSION === */
198
199
200 /***********************************************************************
201 * NAME PRIVATE
202 * WPRINTF_ParseFormatA
203 *
204 * DESCRIPTION
205 * Parse a format specification. A format specification has the
206 * form:
207 *
208 * [-][#][0][width][.precision]type
209 *
210 * ARGUMENTS
211 * format
212 * ?
213 * res
214 * ?
215 *
216 * RETURN VALUE
217 * The length of the format specification in characters.
218 */
219 static
220 INT
221 WPRINTF_ParseFormatA(
222 LPCSTR format,
223 WPRINTF_FORMAT *res
224 )
225 {
226 LPCSTR p = format;
227
228 res->flags = 0;
229 res->width = 0;
230 res->precision = 0;
231 if (*p == '-')
232 {
233 res->flags |= WPRINTF_LEFTALIGN;
234 p++;
235 }
236 if (*p == '#')
237 {
238 res->flags |= WPRINTF_PREFIX_HEX;
239 p++;
240 }
241 if (*p == '0')
242 {
243 res->flags |= WPRINTF_ZEROPAD;
244 p++;
245 }
246 while ((*p >= '0') && (*p <= '9')) /* width field */
247 {
248 res->width =
249 (res->width * 10)
250 + (*p - '0');
251 p++;
252 }
253 if (*p == '.') /* precision field */
254 {
255 p++;
256 while ((*p >= '0') && (*p <= '9'))
257 {
258 res->precision =
259 (res->precision * 10)
260 + (*p - '0');
261 p++;
262 }
263 }
264 if (*p == 'l')
265 {
266 res->flags |= WPRINTF_LONG;
267 p++;
268 }
269 else if (*p == 'h')
270 {
271 res->flags |= WPRINTF_SHORT;
272 p++;
273 }
274 else if (*p == 'w')
275 {
276 res->flags |= WPRINTF_WIDE;
277 p++;
278 }
279
280 switch (*p)
281 {
282 case 'c':
283 res->type =
284 (res->flags & WPRINTF_LONG)
285 ? WPR_WCHAR
286 : WPR_CHAR;
287 break;
288
289 case 'C':
290 res->type =
291 (res->flags & WPRINTF_SHORT)
292 ? WPR_CHAR
293 : WPR_WCHAR;
294 break;
295
296 case 'd':
297 case 'i':
298 res->type = WPR_SIGNED;
299 break;
300
301 case 's':
302 res->type =
303 (res->flags & (WPRINTF_LONG |WPRINTF_WIDE))
304 ? WPR_WSTRING
305 : WPR_STRING;
306 break;
307
308 case 'S':
309 res->type =
310 (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE))
311 ? WPR_STRING
312 : WPR_WSTRING;
313 break;
314
315 case 'u':
316 res->type = WPR_UNSIGNED;
317 break;
318
319 case 'X':
320 res->flags |= WPRINTF_UPPER_HEX;
321 /* fall through */
322
323 case 'x':
324 res->type = WPR_HEXA;
325 break;
326
327 default: /* unknown format char */
328 res->type = WPR_UNKNOWN;
329 p--; /* print format as normal char */
330 break;
331
332 } /* switch */
333
334 return (INT) (p - format) + 1;
335 }
336
337
338 /***********************************************************************
339 * NAME PRIVATE
340 * wvsnprintfA (Not a Windows API)
341 */
342 INT
343 STDCALL
344 wvsnprintfA(
345 LPSTR buffer,
346 UINT maxlen,
347 LPCSTR spec,
348 va_list args
349 )
350 {
351 WPRINTF_FORMAT format;
352 LPSTR p = buffer;
353 UINT i;
354 UINT len;
355 CHAR number [20];
356
357 while (*spec && (maxlen > 1))
358 {
359 if (*spec != '%')
360 {
361 *p++ = *spec++;
362 maxlen--;
363 continue;
364 }
365 spec++;
366 if (*spec == '%')
367 {
368 *p++ = *spec++;
369 maxlen--;
370 continue;
371 }
372 spec += WPRINTF_ParseFormatA(
373 spec,
374 & format
375 );
376 len = WPRINTF_GetLen(
377 & format,
378 args,
379 number,
380 (maxlen - 1)
381 );
382 if (!(format.flags & WPRINTF_LEFTALIGN))
383 {
384 for ( i = format.precision;
385 (i < format.width);
386 i++, maxlen--
387 )
388 {
389 *p++ = ' ';
390 }
391 }
392 switch (format.type)
393 {
394 case WPR_WCHAR:
395 if ((*p = (CHAR) (WCHAR) va_arg( args, int)))
396 {
397 p++;
398 }
399 else if (format.width > 1)
400 {
401 *p++ = ' ';
402 }
403 else
404 {
405 len = 0;
406 }
407 break;
408
409 case WPR_CHAR:
410 if ((*p = (CHAR) va_arg( args, int )))
411 {
412 p++;
413 }
414 else if (format.width > 1)
415 {
416 *p++ = ' ';
417 }
418 else
419 {
420 len = 0;
421 }
422 break;
423
424 case WPR_STRING:
425 memcpy(
426 p,
427 va_arg( args, LPCSTR ),
428 len
429 );
430 p += len;
431 break;
432
433 case WPR_WSTRING:
434 {
435 LPCWSTR ptr = va_arg( args, LPCWSTR );
436
437 for ( i = 0;
438 (i < len);
439 i++
440 )
441 {
442 *p++ = (CHAR) *ptr++;
443 }
444 }
445 break;
446
447 case WPR_HEXA:
448 if ( (format.flags & WPRINTF_PREFIX_HEX)
449 && (maxlen > 3)
450 )
451 {
452 *p++ = '0';
453 *p++ = (format.flags & WPRINTF_UPPER_HEX)
454 ? 'X'
455 : 'x';
456 maxlen -= 2;
457 len -= 2;
458 format.precision -= 2;
459 format.width -= 2;
460 }
461 /* fall through */
462
463 case WPR_SIGNED:
464 case WPR_UNSIGNED:
465 for ( i = len;
466 (i < format.precision);
467 i++, maxlen--
468 )
469 {
470 *p++ = '0';
471 }
472 memcpy(
473 p,
474 number,
475 len
476 );
477 p += len;
478 (void) va_arg( args, INT ); /* Go to the next arg */
479 break;
480
481 case WPR_UNKNOWN:
482 continue;
483 } /* switch */
484
485 if (format.flags & WPRINTF_LEFTALIGN)
486 {
487 for ( i = format.precision;
488 (i < format.width);
489 i++, maxlen--
490 )
491 {
492 *p++ = ' ';
493 }
494 }
495 maxlen -= len;
496 } /* while */
497
498 *p = '\0';
499 return (maxlen > 1)
500 ? (INT) (p - buffer)
501 : -1;
502 }
503
504
505 /***********************************************************************
506 * NAME PUBLIC
507 * wsprintfA (USER32.585)
508 *
509 * DESCRIPTION
510 *
511 * ARGUMENTS
512 *
513 * RETURN VALUE
514 */
515 INT
516 CDECL
517 wsprintfA(
518 LPSTR buffer,
519 LPCSTR spec,
520 ...
521 )
522 {
523 va_list valist;
524 INT res;
525
526 va_start( valist, spec );
527 res = wvsnprintfA(
528 buffer,
529 0xffffffff,
530 spec,
531 valist
532 );
533 va_end( valist );
534 return res;
535 }
536
537
538 /***********************************************************************
539 * NAME PUBLIC
540 * wvsprintfA (USER32.587)
541 *
542 * DESCRIPTION
543 *
544 * ARGUMENTS
545 *
546 * RETURN VALUE
547 */
548 INT
549 STDCALL
550 wvsprintfA(
551 LPSTR buffer,
552 LPCSTR spec,
553 va_list args
554 )
555 {
556 return wvsnprintfA(
557 buffer,
558 0xffffffff,
559 spec,
560 args
561 );
562 }
563
564
565 /* === UNICODE VERSION === */
566
567
568 /***********************************************************************
569 * NAME PRIVATE
570 * WPRINTF_ParseFormatW
571 *
572 * DESCRIPTION
573 * Parse a format specification. A format specification has
574 * the form:
575 *
576 * [-][#][0][width][.precision]type
577 *
578 * ARGUMENTS
579 * format
580 * ?
581 * res
582 * ?
583 *
584 * RETURN VALUE
585 * The length of the format specification in characters.
586 */
587 static
588 INT
589 WPRINTF_ParseFormatW(
590 LPCWSTR format,
591 WPRINTF_FORMAT *res
592 )
593 {
594 LPCWSTR p = format;
595
596 res->flags = 0;
597 res->width = 0;
598 res->precision = 0;
599 if (*p == L'-')
600 {
601 res->flags |= WPRINTF_LEFTALIGN;
602 p++;
603 }
604 if (*p == L'#')
605 {
606 res->flags |= WPRINTF_PREFIX_HEX;
607 p++;
608 }
609 if (*p == L'0')
610 {
611 res->flags |= WPRINTF_ZEROPAD;
612 p++;
613 }
614
615 while ((*p >= L'0') && (*p <= L'9')) /* width field */
616 {
617 res->width =
618 (res->width * 10)
619 + (*p - L'0');
620 p++;
621 }
622
623 if (*p == L'.') /* precision field */
624 {
625 p++;
626 while ((*p >= L'0') && (*p <= L'9'))
627 {
628 res->precision =
629 (res->precision * 10)
630 + (*p - '0');
631 p++;
632 }
633 }
634 if (*p == L'l')
635 {
636 res->flags |= WPRINTF_LONG;
637 p++;
638 }
639 else if (*p == L'h')
640 {
641 res->flags |= WPRINTF_SHORT;
642 p++;
643 }
644 else if (*p == L'w')
645 {
646 res->flags |= WPRINTF_WIDE;
647 p++;
648 }
649
650 switch ((CHAR)*p)
651 {
652 case L'c':
653 res->type =
654 (res->flags & WPRINTF_SHORT)
655 ? WPR_CHAR
656 : WPR_WCHAR;
657 break;
658
659 case L'C':
660 res->type =
661 (res->flags & WPRINTF_LONG)
662 ? WPR_WCHAR
663 : WPR_CHAR;
664 break;
665
666 case L'd':
667 case L'i':
668 res->type = WPR_SIGNED;
669 break;
670
671 case L's':
672 res->type =
673 ((res->flags & WPRINTF_SHORT)
674 && !(res->flags & WPRINTF_WIDE)
675 )
676 ? WPR_STRING
677 : WPR_WSTRING;
678 break;
679
680 case L'S':
681 res->type =
682 (res->flags & (WPRINTF_LONG | WPRINTF_WIDE))
683 ? WPR_WSTRING
684 : WPR_STRING;
685 break;
686
687 case L'u':
688 res->type = WPR_UNSIGNED;
689 break;
690
691 case L'X':
692 res->flags |= WPRINTF_UPPER_HEX;
693 /* fall through */
694
695 case L'x':
696 res->type = WPR_HEXA;
697 break;
698
699 default:
700 res->type = WPR_UNKNOWN;
701 p--; /* print format as normal char */
702 break;
703 } /* switch */
704
705 return (INT) (p - format) + 1;
706 }
707
708
709 /***********************************************************************
710 * NAME PRIVATE
711 * wvsnprintfW (Not a Windows API)
712 */
713 INT
714 wvsnprintfW(
715 LPWSTR buffer,
716 UINT maxlen,
717 LPCWSTR spec,
718 va_list args
719 )
720 {
721 WPRINTF_FORMAT format;
722 LPWSTR p = buffer;
723 UINT i;
724 UINT len;
725 CHAR number [20];
726
727 while (*spec && (maxlen > 1))
728 {
729 if (*spec != L'%')
730 {
731 *p++ = *spec++;
732 maxlen--;
733 continue;
734 }
735 spec++;
736 if (*spec == L'%')
737 {
738 *p++ = *spec++;
739 maxlen--;
740 continue;
741 }
742 spec += WPRINTF_ParseFormatW(
743 spec,
744 & format
745 );
746 len = WPRINTF_GetLen(
747 & format,
748 args,
749 number,
750 (maxlen - 1)
751 );
752 if (!(format.flags & WPRINTF_LEFTALIGN))
753 {
754 for ( i = format.precision;
755 (i < format.width);
756 i++, maxlen--
757 )
758 {
759 *p++ = L' ';
760 }
761 }
762 switch (format.type)
763 {
764 case WPR_WCHAR:
765 if ((*p = (WCHAR) va_arg( args, int)))
766 {
767 p++;
768 }
769 else if (format.width > 1)
770 {
771 *p++ = L' ';
772 }
773 else
774 {
775 len = 0;
776 }
777 break;
778
779 case WPR_CHAR:
780 if ((*p = (WCHAR)(CHAR) va_arg( args, int )))
781 {
782 p++;
783 }
784 else if (format.width > 1)
785 {
786 *p++ = L' ';
787 }
788 else
789 {
790 len = 0;
791 }
792 break;
793
794 case WPR_STRING:
795 {
796 LPCSTR ptr = va_arg( args, LPCSTR );
797
798 for ( i = 0;
799 (i < len);
800 i++
801 )
802 {
803 *p++ = (WCHAR) *ptr++;
804 }
805 }
806 break;
807
808 case WPR_WSTRING:
809 if (len)
810 {
811 memcpy(
812 p,
813 va_arg( args, LPCWSTR ),
814 (len * sizeof (WCHAR))
815 );
816 }
817 p += len;
818 break;
819
820 case WPR_HEXA:
821 if ( (format.flags & WPRINTF_PREFIX_HEX)
822 && (maxlen > 3)
823 )
824 {
825 *p++ = L'0';
826 *p++ = (format.flags & WPRINTF_UPPER_HEX)
827 ? L'X'
828 : L'x';
829 maxlen -= 2;
830 len -= 2;
831 format.precision -= 2;
832 format.width -= 2;
833 }
834 /* fall through */
835
836 case WPR_SIGNED:
837 case WPR_UNSIGNED:
838 for ( i = len;
839 (i < format.precision);
840 i++, maxlen--
841 )
842 {
843 *p++ = L'0';
844 }
845 for ( i = 0;
846 (i < len);
847 i++
848 )
849 {
850 *p++ = (WCHAR) number[i];
851 }
852 (void) va_arg( args, INT ); /* Go to the next arg */
853 break;
854
855 case WPR_UNKNOWN:
856 continue;
857 } /* switch */
858
859 if (format.flags & WPRINTF_LEFTALIGN)
860 {
861 for ( i = format.precision;
862 (i < format.width);
863 i++, maxlen--
864 )
865 {
866 *p++ = L' ';
867 }
868 }
869 maxlen -= len;
870 } /* while */
871
872 *p = L'\0';
873 return (maxlen > 1)
874 ? (INT) (p - buffer)
875 : -1;
876 }
877
878
879 /***********************************************************************
880 * NAME PUBLIC
881 * wsprintfW (USER32.586)
882 *
883 * DESCRIPTION
884 *
885 * ARGUMENTS
886 *
887 * RETURN VALUE
888 */
889 INT
890 CDECL
891 wsprintfW(
892 LPWSTR buffer,
893 LPCWSTR spec,
894 ...
895 )
896 {
897 va_list valist;
898 INT res;
899
900 va_start( valist, spec );
901 res = wvsnprintfW(
902 buffer,
903 0xffffffff,
904 spec,
905 valist
906 );
907 va_end( valist );
908 return res;
909 }
910
911
912 /***********************************************************************
913 * NAME PUBLIC
914 * wvsprintfW (USER32.588)
915 *
916 * DESCRIPTION
917 *
918 * ARGUMENTS
919 *
920 * RETURN VALUE
921 */
922 INT
923 STDCALL
924 wvsprintfW(
925 LPWSTR buffer,
926 LPCWSTR spec,
927 va_list args
928 )
929 {
930 return wvsnprintfW(
931 buffer,
932 0xffffffff,
933 spec,
934 args
935 );
936 }
937
938
939 /* EOF */