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