blob: af9379e018f872aac60034755e29c7e6e990ea61 [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{
42 return dgetc(c);
43}
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
69
70#define LONGFLAG 0x00000001
71#define LONGLONGFLAG 0x00000002
72#define HALFFLAG 0x00000004
73#define HALFHALFFLAG 0x00000008
74#define SIZETFLAG 0x00000010
75#define ALTFLAG 0x00000020
76#define CAPSFLAG 0x00000040
77#define SHOWSIGNFLAG 0x00000080
78#define SIGNEDFLAG 0x00000100
79#define LEFTFORMATFLAG 0x00000200
80#define LEADZEROFLAG 0x00000400
81
82static char *longlong_to_string(char *buf, unsigned long long n, int len, uint flag)
83{
84 int pos = len;
85 int negative = 0;
86
87 if((flag & SIGNEDFLAG) && (long long)n < 0) {
88 negative = 1;
89 n = -n;
90 }
91
92 buf[--pos] = 0;
93
94 /* only do the math if the number is >= 10 */
95 while(n >= 10) {
96 int digit = n % 10;
97
98 n /= 10;
99
100 buf[--pos] = digit + '0';
101 }
102 buf[--pos] = n + '0';
103
104 if(negative)
105 buf[--pos] = '-';
106 else if((flag & SHOWSIGNFLAG))
107 buf[--pos] = '+';
108
109 return &buf[pos];
110}
111
112static char *longlong_to_hexstring(char *buf, unsigned long long u, int len, uint flag)
113{
114 int pos = len;
115 static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
116 static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
117 const char *table;
118
119 if((flag & CAPSFLAG))
120 table = hextable_caps;
121 else
122 table = hextable;
123
124 buf[--pos] = 0;
125 do {
126 unsigned int digit = u % 16;
127 u /= 16;
128
129 buf[--pos] = table[digit];
130 } while(u != 0);
131
132 return &buf[pos];
133}
134
135int vsprintf(char *str, const char *fmt, va_list ap)
136{
137 char c;
138 unsigned char uc;
139 const char *s;
140 unsigned long long n;
141 void *ptr;
142 int flags;
143 unsigned int format_num;
144 int chars_written = 0;
145 char num_buffer[32];
146
147#define OUTPUT_CHAR(c) do { (*str++ = c); chars_written++; } while(0)
148
149 for(;;) {
150 /* handle regular chars that aren't format related */
151 while((c = *fmt++) != 0) {
152 if(c == '%')
153 break; /* we saw a '%', break and start parsing format */
154 OUTPUT_CHAR(c);
155 }
156
157 /* make sure we haven't just hit the end of the string */
158 if(c == 0)
159 break;
160
161 /* reset the format state */
162 flags = 0;
163 format_num = 0;
164
165next_format:
166 /* grab the next format character */
167 c = *fmt++;
168 if(c == 0)
169 break;
170
171 switch(c) {
172 case '0'...'9':
173 if (c == '0' && format_num == 0)
174 flags |= LEADZEROFLAG;
175 format_num *= 10;
176 format_num += c - '0';
177 goto next_format;
178 case '.':
179 /* XXX for now eat numeric formatting */
180 goto next_format;
181 case '%':
182 OUTPUT_CHAR('%');
183 break;
184 case 'c':
185 uc = va_arg(ap, unsigned int);
186 OUTPUT_CHAR(uc);
187 break;
188 case 's':
189 s = va_arg(ap, const char *);
190 if(s == 0)
191 s = "<null>";
192 goto _output_string;
193 case '-':
194 flags |= LEFTFORMATFLAG;
195 goto next_format;
196 case '+':
197 flags |= SHOWSIGNFLAG;
198 goto next_format;
199 case '#':
200 flags |= ALTFLAG;
201 goto next_format;
202 case 'l':
203 if(flags & LONGFLAG)
204 flags |= LONGLONGFLAG;
205 flags |= LONGFLAG;
206 goto next_format;
207 case 'h':
208 if(flags & HALFFLAG)
209 flags |= HALFHALFFLAG;
210 flags |= HALFFLAG;
211 goto next_format;
212 case 'z':
213 flags |= SIZETFLAG;
214 goto next_format;
215 case 'D':
216 flags |= LONGFLAG;
217 /* fallthrough */
218 case 'i':
219 case 'd':
220 n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) :
221 (flags & LONGFLAG) ? va_arg(ap, long) :
222 (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) :
223 (flags & HALFFLAG) ? (short)va_arg(ap, int) :
224 (flags & SIZETFLAG) ? va_arg(ap, ssize_t) :
225 va_arg(ap, int);
226 flags |= SIGNEDFLAG;
227 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags);
228 goto _output_string;
229 case 'U':
230 flags |= LONGFLAG;
231 /* fallthrough */
232 case 'u':
233 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
234 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
235 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
236 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
237 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
238 va_arg(ap, unsigned int);
239 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags);
240 goto _output_string;
241 case 'p':
242 flags |= LONGFLAG | ALTFLAG;
243 goto hex;
244 case 'X':
245 flags |= CAPSFLAG;
246 /* fallthrough */
247hex:
248 case 'x':
249 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
250 (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
251 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
252 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
253 (flags & SIZETFLAG) ? va_arg(ap, size_t) :
254 va_arg(ap, unsigned int);
255 s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags);
256 if(flags & ALTFLAG) {
257 OUTPUT_CHAR('0');
258 OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x');
259 }
260 goto _output_string;
261 case 'n':
262 ptr = va_arg(ap, void *);
263 if(flags & LONGLONGFLAG)
264 *(long long *)ptr = chars_written;
265 else if(flags & LONGFLAG)
266 *(long *)ptr = chars_written;
267 else if(flags & HALFHALFFLAG)
268 *(signed char *)ptr = chars_written;
269 else if(flags & HALFFLAG)
270 *(short *)ptr = chars_written;
271 else if(flags & SIZETFLAG)
272 *(size_t *)ptr = chars_written;
273 else
274 *(int *)ptr = chars_written;
275 break;
276 default:
277 OUTPUT_CHAR('%');
278 OUTPUT_CHAR(c);
279 break;
280 }
281
282 /* move on to the next field */
283 continue;
284
285 /* shared output code */
286_output_string:
287 if (flags & LEFTFORMATFLAG) {
288 /* left justify the text */
289 uint count = 0;
290 while(*s != 0) {
291 OUTPUT_CHAR(*s++);
292 count++;
293 }
294
295 /* pad to the right (if necessary) */
296 for (; format_num > count; format_num--)
297 OUTPUT_CHAR(' ');
298 } else {
299 /* right justify the text (digits) */
300 size_t string_len = strlen(s);
301 char outchar = (flags & LEADZEROFLAG) ? '0' : ' ';
302 for (; format_num > string_len; format_num--)
303 OUTPUT_CHAR(outchar);
304
305 /* output the string */
306 while(*s != 0)
307 OUTPUT_CHAR(*s++);
308 }
309 continue;
310 }
311
312 /* null terminate */
313 OUTPUT_CHAR('\0');
314 chars_written--; /* don't count the null */
315
316#undef OUTPUT_CHAR
317
318 return chars_written;
319}
320
321