1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2008-2014 Travis Geiselbrecht
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7
8 #include <limits.h>
9 #include <printf.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <sys/types.h>
13
sprintf(char * str,const char * fmt,...)14 int sprintf(char *str, const char *fmt, ...)
15 {
16 int err;
17
18 va_list ap;
19 va_start(ap, fmt);
20 err = vsprintf(str, fmt, ap);
21 va_end(ap);
22
23 return err;
24 }
25
snprintf(char * str,size_t len,const char * fmt,...)26 int snprintf(char *str, size_t len, const char *fmt, ...)
27 {
28 int err;
29
30 va_list ap;
31 va_start(ap, fmt);
32 err = vsnprintf(str, len, fmt, ap);
33 va_end(ap);
34
35 return err;
36 }
37
38
39 #define LONGFLAG 0x00000001
40 #define LONGLONGFLAG 0x00000002
41 #define HALFFLAG 0x00000004
42 #define HALFHALFFLAG 0x00000008
43 #define SIZETFLAG 0x00000010
44 #define INTMAXFLAG 0x00000020
45 #define PTRDIFFFLAG 0x00000040
46 #define ALTFLAG 0x00000080
47 #define CAPSFLAG 0x00000100
48 #define SHOWSIGNFLAG 0x00000200
49 #define SIGNEDFLAG 0x00000400
50 #define LEFTFORMATFLAG 0x00000800
51 #define LEADZEROFLAG 0x00001000
52 #define BLANKPOSFLAG 0x00002000
53
longlong_to_string(char * buf,unsigned long long n,size_t len,unsigned int flag,char * signchar)54 __NO_INLINE static char *longlong_to_string(char *buf, unsigned long long n, size_t len, unsigned int flag, char *signchar)
55 {
56 size_t pos = len;
57 int negative = 0;
58
59 if ((flag & SIGNEDFLAG) && (long long)n < 0) {
60 negative = 1;
61 n = -n;
62 }
63
64 buf[--pos] = 0;
65
66 /* only do the math if the number is >= 10 */
67 while (n >= 10) {
68 int digit = n % 10;
69
70 n /= 10;
71
72 buf[--pos] = digit + '0';
73 }
74 buf[--pos] = n + '0';
75
76 if (negative)
77 *signchar = '-';
78 else if ((flag & SHOWSIGNFLAG))
79 *signchar = '+';
80 else if ((flag & BLANKPOSFLAG))
81 *signchar = ' ';
82 else
83 *signchar = '\0';
84
85 return &buf[pos];
86 }
87
88 static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
89 static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
90
longlong_to_hexstring(char * buf,unsigned long long u,size_t len,unsigned int flag)91 __NO_INLINE static char *longlong_to_hexstring(char *buf, unsigned long long u, size_t len, unsigned int flag)
92 {
93 size_t pos = len;
94 const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
95
96 buf[--pos] = 0;
97 do {
98 unsigned int digit = u % 16;
99 u /= 16;
100
101 buf[--pos] = table[digit];
102 } while (u != 0);
103
104 return &buf[pos];
105 }
106
vsprintf(char * str,const char * fmt,va_list ap)107 int vsprintf(char *str, const char *fmt, va_list ap)
108 {
109 return vsnprintf(str, INT_MAX, fmt, ap);
110 }
111
112 struct _output_args {
113 char *outstr;
114 size_t len;
115 size_t pos;
116 };
117
_vsnprintf_output(const char * str,size_t len,void * state)118 static int _vsnprintf_output(const char *str, size_t len, void *state)
119 {
120 struct _output_args *args = state;
121
122 size_t count = 0;
123 while (count < len) {
124 if (args->pos < args->len) {
125 args->outstr[args->pos++] = *str;
126 }
127
128 str++;
129 count++;
130 }
131
132 return count;
133 }
134
vsnprintf(char * str,size_t len,const char * fmt,va_list ap)135 int vsnprintf(char *str, size_t len, const char *fmt, va_list ap)
136 {
137 struct _output_args args;
138 int wlen;
139
140 args.outstr = str;
141 args.len = len;
142 args.pos = 0;
143
144 wlen = _printf_engine(&_vsnprintf_output, (void *)&args, fmt, ap);
145 if (args.pos >= len)
146 str[len-1] = '\0';
147 else
148 str[wlen] = '\0';
149 return wlen;
150 }
151
_printf_engine(_printf_engine_output_func out,void * state,const char * fmt,va_list ap)152 int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap)
153 {
154 int err = 0;
155 char c;
156 unsigned char uc;
157 const char *s;
158 size_t string_len;
159 unsigned long long n;
160 void *ptr;
161 int flags;
162 unsigned int format_num;
163 char signchar;
164 size_t chars_written = 0;
165 char num_buffer[32];
166
167 #define OUTPUT_STRING(str, len) do { err = out(str, len, state); if (err < 0) { goto exit; } else { chars_written += err; } } while(0)
168 #define OUTPUT_CHAR(c) do { char __temp[1] = { c }; OUTPUT_STRING(__temp, 1); } while (0)
169
170 for (;;) {
171 /* reset the format state */
172 flags = 0;
173 format_num = 0;
174 signchar = '\0';
175
176 /* handle regular chars that aren't format related */
177 s = fmt;
178 string_len = 0;
179 while ((c = *fmt++) != 0) {
180 if (c == '%')
181 break; /* we saw a '%', break and start parsing format */
182 string_len++;
183 }
184
185 /* output the string we've accumulated */
186 OUTPUT_STRING(s, string_len);
187
188 /* make sure we haven't just hit the end of the string */
189 if (c == 0)
190 break;
191
192 next_format:
193 /* grab the next format character */
194 c = *fmt++;
195 if (c == 0)
196 break;
197
198 switch (c) {
199 case '0'...'9':
200 if (c == '0' && format_num == 0)
201 flags |= LEADZEROFLAG;
202 format_num *= 10;
203 format_num += c - '0';
204 goto next_format;
205 case '.':
206 /* XXX for now eat numeric formatting */
207 goto next_format;
208 case '%':
209 OUTPUT_CHAR('%');
210 break;
211 case 'c':
212 uc = va_arg(ap, unsigned int);
213 OUTPUT_CHAR(uc);
214 break;
215 case 's':
216 s = va_arg(ap, const char *);
217 if (s == 0)
218 s = "<null>";
219 flags &= ~LEADZEROFLAG; /* doesn't make sense for strings */
220 goto _output_string;
221 case '-':
222 flags |= LEFTFORMATFLAG;
223 goto next_format;
224 case '+':
225 flags |= SHOWSIGNFLAG;
226 goto next_format;
227 case ' ':
228 flags |= BLANKPOSFLAG;
229 goto next_format;
230 case '#':
231 flags |= ALTFLAG;
232 goto next_format;
233 case 'l':
234 if (flags & LONGFLAG)
235 flags |= LONGLONGFLAG;
236 flags |= LONGFLAG;
237 goto next_format;
238 case 'h':
239 if (flags & HALFFLAG)
240 flags |= HALFHALFFLAG;
241 flags |= HALFFLAG;
242 goto next_format;
243 case 'z':
244 flags |= SIZETFLAG;
245 goto next_format;
246 case 'j':
247 flags |= INTMAXFLAG;
248 goto next_format;
249 case 't':
250 flags |= PTRDIFFFLAG;
251 goto next_format;
252 case 'i':
253 case 'd':
254 n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) :
255 (flags & LONGFLAG) ? va_arg(ap, long) :
256 (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) :
257 (flags & HALFFLAG) ? (short)va_arg(ap, int) :
258 (flags & SIZETFLAG) ? va_arg(ap, ssize_t) :
259 (flags & INTMAXFLAG) ? va_arg(ap, intmax_t) :
260 (flags & PTRDIFFFLAG) ? va_arg(ap, ptrdiff_t) :
261 va_arg(ap, int);
262 flags |= SIGNEDFLAG;
263 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
264 goto _output_string;
265 case 'u':
266 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
267 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
268 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
269 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
270 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
271 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
272 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
273 va_arg(ap, unsigned int);
274 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
275 goto _output_string;
276 case 'p':
277 flags |= LONGFLAG | ALTFLAG;
278 goto hex;
279 case 'X':
280 flags |= CAPSFLAG;
281 /* fallthrough */
282 hex:
283 case 'x':
284 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
285 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
286 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
287 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
288 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
289 (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
290 (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
291 va_arg(ap, unsigned int);
292 s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags);
293 if (flags & ALTFLAG) {
294 OUTPUT_CHAR('0');
295 OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x');
296 }
297 goto _output_string;
298 case 'n':
299 ptr = va_arg(ap, void *);
300 if (flags & LONGLONGFLAG)
301 *(long long *)ptr = chars_written;
302 else if (flags & LONGFLAG)
303 *(long *)ptr = chars_written;
304 else if (flags & HALFHALFFLAG)
305 *(signed char *)ptr = chars_written;
306 else if (flags & HALFFLAG)
307 *(short *)ptr = chars_written;
308 else if (flags & SIZETFLAG)
309 *(size_t *)ptr = chars_written;
310 else
311 *(int *)ptr = chars_written;
312 break;
313 default:
314 OUTPUT_CHAR('%');
315 OUTPUT_CHAR(c);
316 break;
317 }
318
319 /* move on to the next field */
320 continue;
321
322 /* shared output code */
323 _output_string:
324 string_len = strlen(s);
325
326 if (flags & LEFTFORMATFLAG) {
327 /* left justify the text */
328 OUTPUT_STRING(s, string_len);
329 unsigned int written = err;
330
331 /* pad to the right (if necessary) */
332 for (; format_num > written; format_num--)
333 OUTPUT_CHAR(' ');
334 } else {
335 /* right justify the text (digits) */
336
337 /* if we're going to print a sign digit,
338 it'll chew up one byte of the format size */
339 if (signchar != '\0' && format_num > 0)
340 format_num--;
341
342 /* output the sign char before the leading zeros */
343 if (flags & LEADZEROFLAG && signchar != '\0')
344 OUTPUT_CHAR(signchar);
345
346 /* pad according to the format string */
347 for (; format_num > string_len; format_num--)
348 OUTPUT_CHAR(flags & LEADZEROFLAG ? '0' : ' ');
349
350 /* if not leading zeros, output the sign char just before the number */
351 if (!(flags & LEADZEROFLAG) && signchar != '\0')
352 OUTPUT_CHAR(signchar);
353
354 /* output the string */
355 OUTPUT_STRING(s, string_len);
356 }
357 continue;
358 }
359
360 #undef OUTPUT_STRING
361 #undef OUTPUT_CHAR
362
363 exit:
364 return (err < 0) ? err : (int)chars_written;
365 }
366