1+ #include "stdio.h"
2+ #include "stdarg.h"
3+
4+ typedef unsigned long size_t ;
5+ static size_t strnlen (const char * s , size_t maxlen ) {
6+ size_t len = 0 ;
7+ while (len < maxlen && s [len ] != '\0' ) {
8+ len ++ ;
9+ }
10+ return len ;
11+ }
12+
13+ /*
14+ * Print a number (base <= 16) in reverse order,
15+ * using specified putch function and associated pointer putdat.
16+ */
17+ static void
18+ printnum (void (* putch )(int , void * ), void * putdat ,
19+ unsigned long long num , unsigned base , int width , int padc )
20+ {
21+ // first recursively print all preceding (more significant) digits
22+ if (num >= base ) {
23+ printnum (putch , putdat , num / base , base , width - 1 , padc );
24+ } else {
25+ // print any needed pad characters before first digit
26+ while (-- width > 0 )
27+ putch (padc , putdat );
28+ }
29+
30+ // then print this (the least significant) digit
31+ putch ("0123456789abcdef" [num % base ], putdat );
32+ }
33+
34+ // Get an unsigned int of various possible sizes from a varargs list,
35+ // depending on the lflag parameter.
36+ static unsigned long long
37+ getuint (va_list * ap , int lflag )
38+ {
39+ if (lflag >= 2 )
40+ return va_arg (* ap , unsigned long long);
41+ else if (lflag )
42+ return va_arg (* ap , unsigned long );
43+ else
44+ return va_arg (* ap , unsigned int );
45+ }
46+
47+ // Same as getuint but signed - can't use getuint
48+ // because of sign extension
49+ static long long
50+ getint (va_list * ap , int lflag )
51+ {
52+ if (lflag >= 2 )
53+ return va_arg (* ap , long long );
54+ else if (lflag )
55+ return va_arg (* ap , long );
56+ else
57+ return va_arg (* ap , int );
58+ }
59+
60+ // Main function to format and print a string.
61+ void printfmt (void (* putch )(int , void * ), void * putdat , const char * fmt , ...);
62+
63+ void
64+ vprintfmt (void (* putch )(int , void * ), void * putdat , const char * fmt , va_list ap )
65+ {
66+ register const char * p ;
67+ register int ch ;
68+ unsigned long long num ;
69+ int base , lflag , width , precision , altflag ;
70+ char padc ;
71+
72+ while (1 ) {
73+ while ((ch = * (unsigned char * ) fmt ++ ) != '%' ) {
74+ if (ch == '\0' )
75+ return ;
76+
77+ putch (ch , putdat );
78+ }
79+
80+ // Process a %-escape sequence
81+ padc = ' ' ;
82+ width = -1 ;
83+ precision = -1 ;
84+ lflag = 0 ;
85+ altflag = 0 ;
86+ reswitch :
87+ switch (ch = * (unsigned char * ) fmt ++ ) {
88+
89+ // flag to pad on the right
90+ case '-' :
91+ padc = '-' ;
92+ goto reswitch ;
93+
94+ // flag to pad with 0's instead of spaces
95+ case '0' :
96+ padc = '0' ;
97+ goto reswitch ;
98+
99+ // width field
100+ case '1' :
101+ case '2' :
102+ case '3' :
103+ case '4' :
104+ case '5' :
105+ case '6' :
106+ case '7' :
107+ case '8' :
108+ case '9' :
109+ for (precision = 0 ; ; ++ fmt ) {
110+ precision = precision * 10 + ch - '0' ;
111+ ch = * fmt ;
112+ if (ch < '0' || ch > '9' )
113+ break ;
114+ }
115+ goto process_precision ;
116+
117+ case '*' :
118+ precision = va_arg (ap , int );
119+ goto process_precision ;
120+
121+ case '.' :
122+ if (width < 0 )
123+ width = 0 ;
124+ goto reswitch ;
125+
126+ case '#' :
127+ altflag = 1 ;
128+ goto reswitch ;
129+
130+ process_precision :
131+ if (width < 0 )
132+ width = precision , precision = -1 ;
133+ goto reswitch ;
134+
135+ // long flag (doubled for long long)
136+ case 'l' :
137+ lflag ++ ;
138+ goto reswitch ;
139+
140+ // character
141+ case 'c' :
142+ putch (va_arg (ap , int ), putdat );
143+ break ;
144+
145+ // string
146+ case 's' :
147+ if ((p = va_arg (ap , char * )) == NULL )
148+ p = "(null)" ;
149+ if (width > 0 && padc != '-' )
150+ for (width -= strnlen (p , precision ); width > 0 ; width -- )
151+ putch (padc , putdat );
152+ for (; (ch = * p ++ ) != '\0' && (precision < 0 || -- precision >= 0 ); width -- )
153+ if (altflag && (ch < ' ' || ch > '~' ))
154+ putch ('?' , putdat );
155+ else
156+ putch (ch , putdat );
157+ for (; width > 0 ; width -- )
158+ putch (' ' , putdat );
159+ break ;
160+
161+ // (signed) decimal
162+ case 'd' :
163+ num = getint (& ap , lflag );
164+ if ((long long ) num < 0 ) {
165+ putch ('-' , putdat );
166+ num = - (long long ) num ;
167+ }
168+ base = 10 ;
169+ goto number ;
170+
171+ // unsigned decimal
172+ case 'u' :
173+ num = getuint (& ap , lflag );
174+ base = 10 ;
175+ goto number ;
176+
177+ // (unsigned) octal
178+ case 'o' :
179+ num = getuint (& ap , lflag );
180+ base = 8 ;
181+ goto number ;
182+
183+ // pointer
184+ case 'p' :
185+ putch ('0' , putdat );
186+ putch ('x' , putdat );
187+ num = (unsigned long long )
188+ (uintptr_t ) va_arg (ap , void * );
189+ base = 16 ;
190+ goto number ;
191+
192+ // (unsigned) hexadecimal
193+ case 'x' :
194+ num = getuint (& ap , lflag );
195+ base = 16 ;
196+ number :
197+ printnum (putch , putdat , num , base , width , padc );
198+ break ;
199+
200+ // escaped '%' character
201+ case '%' :
202+ putch (ch , putdat );
203+ break ;
204+
205+ // unrecognized escape sequence - just print it literally
206+ default :
207+ putch ('%' , putdat );
208+ for (fmt -- ; fmt [-1 ] != '%' ; fmt -- )
209+ /* do nothing */ ;
210+ break ;
211+ }
212+ }
213+ }
214+
215+ void
216+ printfmt (void (* putch )(int , void * ), void * putdat , const char * fmt , ...)
217+ {
218+ va_list ap ;
219+
220+ va_start (ap , fmt );
221+ vprintfmt (putch , putdat , fmt , ap );
222+ va_end (ap );
223+ }
0 commit comments