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