blob: 8a3aeb68ae7aff2e05f19b9b1eab3fa1fa9d45e5 [file] [log] [blame]
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07001/*
2 * Copyright (c) 2008 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
Travis Geiselbrechteb946052008-09-07 22:32:49 -070023#include <debug.h>
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070024#include <limits.h>
25#include <stdarg.h>
26#include <sys/types.h>
27#include <printf.h>
28#include <string.h>
29
Travis Geiselbrechteb946052008-09-07 22:32:49 -070030void putc(char c)
31{
32 return _dputc(c);
33}
34
35int puts(const char *str)
36{
37 return _dputs(str);
38}
39
40int getc(char *c)
41{
Travis Geiselbrecht28da92a2009-06-28 11:12:40 -070042 return dgetc(c, true);
Travis Geiselbrechteb946052008-09-07 22:32:49 -070043}
44
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070045int printf(const char *fmt, ...)
46{
47 int err;
48
49 va_list ap;
50 va_start(ap, fmt);
Travis Geiselbrechteb946052008-09-07 22:32:49 -070051 err = _dvprintf(fmt, ap);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070052 va_end(ap);
53
54 return err;
55}
56
57int sprintf(char *str, const char *fmt, ...)
58{
59 int err;
60
61 va_list ap;
62 va_start(ap, fmt);
63 err = vsprintf(str, fmt, ap);
64 va_end(ap);
65
66 return err;
67}
68
travis geiselbrecht9d564f12008-12-29 22:47:11 +000069int snprintf(char *str, size_t len, const char *fmt, ...)
70{
71 int err;
72
73 va_list ap;
74 va_start(ap, fmt);
75 err = vsnprintf(str, len, fmt, ap);
76 va_end(ap);
77
78 return err;
79}
80
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070081
82#define LONGFLAG 0x00000001
83#define LONGLONGFLAG 0x00000002
84#define HALFFLAG 0x00000004
85#define HALFHALFFLAG 0x00000008
86#define SIZETFLAG 0x00000010
87#define ALTFLAG 0x00000020
88#define CAPSFLAG 0x00000040
89#define SHOWSIGNFLAG 0x00000080
90#define SIGNEDFLAG 0x00000100
91#define LEFTFORMATFLAG 0x00000200
92#define LEADZEROFLAG 0x00000400
93
94static char *longlong_to_string(char *buf, unsigned long long n, int len, uint flag)
95{
96 int pos = len;
97 int negative = 0;
98
99 if((flag & SIGNEDFLAG) && (long long)n < 0) {
100 negative = 1;
101 n = -n;
102 }
103
104 buf[--pos] = 0;
105
106 /* only do the math if the number is >= 10 */
107 while(n >= 10) {
108 int digit = n % 10;
109
110 n /= 10;
111
112 buf[--pos] = digit + '0';
113 }
114 buf[--pos] = n + '0';
115
116 if(negative)
117 buf[--pos] = '-';
118 else if((flag & SHOWSIGNFLAG))
119 buf[--pos] = '+';
120
121 return &buf[pos];
122}
123
124static char *longlong_to_hexstring(char *buf, unsigned long long u, int len, uint flag)
125{
126 int pos = len;
127 static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
128 static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
129 const char *table;
130
131 if((flag & CAPSFLAG))
132 table = hextable_caps;
133 else
134 table = hextable;
135
136 buf[--pos] = 0;
137 do {
138 unsigned int digit = u % 16;
139 u /= 16;
140
141 buf[--pos] = table[digit];
142 } while(u != 0);
143
144 return &buf[pos];
145}
146
147int vsprintf(char *str, const char *fmt, va_list ap)
148{
travis geiselbrecht9d564f12008-12-29 22:47:11 +0000149 return vsnprintf(str, INT_MAX, fmt, ap);
150}
151
152int vsnprintf(char *str, size_t len, const char *fmt, va_list ap)
153{
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700154 char c;
155 unsigned char uc;
156 const char *s;
157 unsigned long long n;
158 void *ptr;
159 int flags;
160 unsigned int format_num;
travis geiselbrecht9d564f12008-12-29 22:47:11 +0000161 size_t chars_written = 0;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700162 char num_buffer[32];
163
travis geiselbrecht9d564f12008-12-29 22:47:11 +0000164#define OUTPUT_CHAR(c) do { (*str++ = c); chars_written++; if (chars_written + 1 == len) goto done; } while(0)
165#define OUTPUT_CHAR_NOLENCHECK(c) do { (*str++ = c); chars_written++; } while(0)
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700166
167 for(;;) {
168 /* handle regular chars that aren't format related */
169 while((c = *fmt++) != 0) {
170 if(c == '%')
171 break; /* we saw a '%', break and start parsing format */
172 OUTPUT_CHAR(c);
173 }
174
175 /* make sure we haven't just hit the end of the string */
176 if(c == 0)
177 break;
178
179 /* reset the format state */
180 flags = 0;
181 format_num = 0;
182
183next_format:
184 /* grab the next format character */
185 c = *fmt++;
186 if(c == 0)
187 break;
188
189 switch(c) {
190 case '0'...'9':
191 if (c == '0' && format_num == 0)
192 flags |= LEADZEROFLAG;
193 format_num *= 10;
194 format_num += c - '0';
195 goto next_format;
196 case '.':
197 /* XXX for now eat numeric formatting */
198 goto next_format;
199 case '%':
200 OUTPUT_CHAR('%');
201 break;
202 case 'c':
203 uc = va_arg(ap, unsigned int);
204 OUTPUT_CHAR(uc);
205 break;
206 case 's':
207 s = va_arg(ap, const char *);
208 if(s == 0)
209 s = "<null>";
210 goto _output_string;
211 case '-':
212 flags |= LEFTFORMATFLAG;
213 goto next_format;
214 case '+':
215 flags |= SHOWSIGNFLAG;
216 goto next_format;
217 case '#':
218 flags |= ALTFLAG;
219 goto next_format;
220 case 'l':
221 if(flags & LONGFLAG)
222 flags |= LONGLONGFLAG;
223 flags |= LONGFLAG;
224 goto next_format;
225 case 'h':
226 if(flags & HALFFLAG)
227 flags |= HALFHALFFLAG;
228 flags |= HALFFLAG;
229 goto next_format;
230 case 'z':
231 flags |= SIZETFLAG;
232 goto next_format;
233 case 'D':
234 flags |= LONGFLAG;
235 /* fallthrough */
236 case 'i':
237 case 'd':
238 n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) :
239 (flags & LONGFLAG) ? va_arg(ap, long) :
240 (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) :
241 (flags & HALFFLAG) ? (short)va_arg(ap, int) :
242 (flags & SIZETFLAG) ? va_arg(ap, ssize_t) :
243 va_arg(ap, int);
244 flags |= SIGNEDFLAG;
245 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags);
246 goto _output_string;
247 case 'U':
248 flags |= LONGFLAG;
249 /* fallthrough */
250 case 'u':
251 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
252 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
253 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
254 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
255 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
256 va_arg(ap, unsigned int);
257 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags);
258 goto _output_string;
259 case 'p':
260 flags |= LONGFLAG | ALTFLAG;
261 goto hex;
262 case 'X':
263 flags |= CAPSFLAG;
264 /* fallthrough */
265hex:
266 case 'x':
267 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
268 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
269 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
270 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
271 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
272 va_arg(ap, unsigned int);
273 s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags);
274 if(flags & ALTFLAG) {
275 OUTPUT_CHAR('0');
276 OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x');
277 }
278 goto _output_string;
279 case 'n':
280 ptr = va_arg(ap, void *);
281 if(flags & LONGLONGFLAG)
282 *(long long *)ptr = chars_written;
283 else if(flags & LONGFLAG)
284 *(long *)ptr = chars_written;
285 else if(flags & HALFHALFFLAG)
286 *(signed char *)ptr = chars_written;
287 else if(flags & HALFFLAG)
288 *(short *)ptr = chars_written;
289 else if(flags & SIZETFLAG)
290 *(size_t *)ptr = chars_written;
291 else
292 *(int *)ptr = chars_written;
293 break;
294 default:
295 OUTPUT_CHAR('%');
296 OUTPUT_CHAR(c);
297 break;
298 }
299
300 /* move on to the next field */
301 continue;
302
303 /* shared output code */
304_output_string:
305 if (flags & LEFTFORMATFLAG) {
306 /* left justify the text */
307 uint count = 0;
308 while(*s != 0) {
309 OUTPUT_CHAR(*s++);
310 count++;
311 }
312
313 /* pad to the right (if necessary) */
314 for (; format_num > count; format_num--)
315 OUTPUT_CHAR(' ');
316 } else {
317 /* right justify the text (digits) */
318 size_t string_len = strlen(s);
319 char outchar = (flags & LEADZEROFLAG) ? '0' : ' ';
320 for (; format_num > string_len; format_num--)
321 OUTPUT_CHAR(outchar);
322
323 /* output the string */
324 while(*s != 0)
325 OUTPUT_CHAR(*s++);
326 }
327 continue;
328 }
329
travis geiselbrecht9d564f12008-12-29 22:47:11 +0000330done:
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700331 /* null terminate */
travis geiselbrecht9d564f12008-12-29 22:47:11 +0000332 OUTPUT_CHAR_NOLENCHECK('\0');
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700333 chars_written--; /* don't count the null */
334
335#undef OUTPUT_CHAR
travis geiselbrecht9d564f12008-12-29 22:47:11 +0000336#undef OUTPUT_CHAR_NOLENCHECK
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700337
338 return chars_written;
339}
340
341