Cleanup in elf.c with .bss section clean; adm command mounts cdrom instead of floppy...
[ZeXOS.git] / kernel / lib / stdio / doprintf.c
blob10ad744a4829bdd920a5d006ec0664499cfe3d73
1 /*
2 * ZeX/OS
3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <_printf.h> /* fnptr_t */
21 #include <string.h> /* strlen() */
22 #include <stdarg.h> /* va_list, va_arg() */
23 /*****************************************************************************
24 Revised Jan 28, 2002
25 - changes to make characters 0x80-0xFF display properly
27 Revised June 10, 2001
28 - changes to make vsprintf() terminate string with '\0'
30 Revised May 12, 2000
31 - math in DO_NUM is now unsigned, as it should be
32 - %0 flag (pad left with zeroes) now works
33 - actually did some TESTING, maybe fixed some other bugs
35 name: do_printf
36 action: minimal subfunction for ?printf, calls function
37 'fn' with arg 'ptr' for each character to be output
38 returns:total number of characters output
40 %[flag][width][.prec][mod][conv]
41 flag: - left justify, pad right w/ blanks DONE
42 0 pad left w/ 0 for numerics DONE
43 + always print sign, + or - no
44 ' ' (blank) no
45 # (???) no
47 width: (field width) DONE
49 prec: (precision) no
51 conv: d,i decimal int DONE
52 u decimal unsigned DONE
53 o octal DONE
54 x,X hex DONE
55 f,e,g,E,G float no
56 c char DONE
57 s string DONE
58 p ptr DONE
60 mod: N near ptr DONE
61 F far ptr no
62 h short (16-bit) int DONE
63 l long (32-bit) int DONE
64 L long long (64-bit) int no
65 *****************************************************************************/
66 /* flags used in processing format string */
67 #define PR_LJ 0x01 /* left justify */
68 #define PR_CA 0x02 /* use A-F instead of a-f for hex */
69 #define PR_SG 0x04 /* signed numeric conversion (%d vs. %u) */
70 #define PR_32 0x08 /* long (32-bit) numeric conversion */
71 #define PR_16 0x10 /* short (16-bit) numeric conversion */
72 #define PR_WS 0x20 /* PR_SG set and num was < 0 */
73 #define PR_LZ 0x40 /* pad left with '0' instead of ' ' */
74 #define PR_FP 0x80 /* pointers are far */
76 /* largest number handled is 2^32-1, lowest radix handled is 8.
77 2^32-1 in base 8 has 11 digits (add 5 for trailing NUL and for slop) */
78 #define PR_BUFLEN 16
80 int do_printf(const char *fmt, va_list args, fnptr_t fn, void *ptr)
82 unsigned state, flags, radix, actual_wd, count, given_wd;
83 unsigned char *where, buf[PR_BUFLEN];
84 long num;
86 state = flags = count = given_wd = 0;
87 /* begin scanning format specifier list */
88 for(; *fmt; fmt++)
90 switch(state)
92 /* STATE 0: AWAITING % */
93 case 0:
94 if(*fmt != '%') /* not %... */
96 fn(*fmt, &ptr); /* ...just echo it */
97 count++;
98 break;
100 /* found %, get next char and advance state to check if next char is a flag */
101 state++;
102 fmt++;
103 /* FALL THROUGH */
104 /* STATE 1: AWAITING FLAGS (%-0) */
105 case 1:
106 if(*fmt == '%') /* %% */
108 fn(*fmt, &ptr);
109 count++;
110 state = flags = given_wd = 0;
111 break;
113 if(*fmt == '-')
115 if(flags & PR_LJ)/* %-- is illegal */
116 state = flags = given_wd = 0;
117 else
118 flags |= PR_LJ;
119 break;
121 /* not a flag char: advance state to check if it's field width */
122 state++;
123 /* check now for '%0...' */
124 if(*fmt == '0')
126 flags |= PR_LZ;
127 fmt++;
129 /* FALL THROUGH */
130 /* STATE 2: AWAITING (NUMERIC) FIELD WIDTH */
131 case 2:
132 if(*fmt >= '0' && *fmt <= '9')
134 given_wd = 10 * given_wd +
135 (*fmt - '0');
136 break;
138 /* not field width: advance state to check if it's a modifier */
139 state++;
140 /* FALL THROUGH */
141 /* STATE 3: AWAITING MODIFIER CHARS (FNlh) */
142 case 3:
143 if(*fmt == 'F')
145 flags |= PR_FP;
146 break;
148 if(*fmt == 'N')
149 break;
150 if(*fmt == 'l')
152 flags |= PR_32;
153 break;
155 if(*fmt == 'h')
157 flags |= PR_16;
158 break;
160 /* not modifier: advance state to check if it's a conversion char */
161 state++;
162 /* FALL THROUGH */
163 /* STATE 4: AWAITING CONVERSION CHARS (Xxpndiuocs) */
164 case 4:
165 where = buf + PR_BUFLEN - 1;
166 *where = '\0';
167 switch(*fmt)
169 case 'X':
170 flags |= PR_CA;
171 /* FALL THROUGH */
172 /* xxx - far pointers (%Fp, %Fn) not yet supported */
173 case 'x':
174 case 'p':
175 case 'n':
176 radix = 16;
177 goto DO_NUM;
178 case 'd':
179 case 'i':
180 flags |= PR_SG;
181 /* FALL THROUGH */
182 case 'u':
183 radix = 10;
184 goto DO_NUM;
185 case 'o':
186 radix = 8;
187 /* load the value to be printed. l=long=32 bits: */
188 DO_NUM: if(flags & PR_32)
189 num = va_arg(args, unsigned long);
190 /* h=short=16 bits (signed or unsigned) */
191 else if(flags & PR_16)
193 if(flags & PR_SG)
194 num = va_arg(args, short);
195 else
196 num = va_arg(args, unsigned short);
198 /* no h nor l: sizeof(int) bits (signed or unsigned) */
199 else
201 if(flags & PR_SG)
202 num = va_arg(args, int);
203 else
204 num = va_arg(args, unsigned int);
206 /* take care of sign */
207 if(flags & PR_SG)
209 if(num < 0)
211 flags |= PR_WS;
212 num = -num;
215 /* convert binary to octal/decimal/hex ASCII
216 OK, I found my mistake. The math here is _always_ unsigned */
219 unsigned long temp;
221 temp = (unsigned long)num % radix;
222 where--;
223 if(temp < 10)
224 *where = temp + '0';
225 else if(flags & PR_CA)
226 *where = temp - 10 + 'A';
227 else
228 *where = temp - 10 + 'a';
229 num = (unsigned long)num / radix;
231 while(num != 0);
232 goto EMIT;
233 case 'c':
234 /* disallow pad-left-with-zeroes for %c */
235 flags &= ~PR_LZ;
236 where--;
237 *where = (unsigned char)va_arg(args,
238 unsigned char);
239 actual_wd = 1;
240 goto EMIT2;
241 case 's':
242 /* disallow pad-left-with-zeroes for %s */
243 flags &= ~PR_LZ;
244 where = va_arg (args, unsigned char *);
245 EMIT:
246 actual_wd = strlen ((char *) where);
247 if(flags & PR_WS)
248 actual_wd++;
249 /* if we pad left with ZEROES, do the sign now */
250 if((flags & (PR_WS | PR_LZ)) ==
251 (PR_WS | PR_LZ))
253 fn('-', &ptr);
254 count++;
256 /* pad on left with spaces or zeroes (for right justify) */
257 EMIT2: if((flags & PR_LJ) == 0)
259 while(given_wd > actual_wd)
261 fn(flags & PR_LZ ? '0' :
262 ' ', &ptr);
263 count++;
264 given_wd--;
267 /* if we pad left with SPACES, do the sign now */
268 if((flags & (PR_WS | PR_LZ)) == PR_WS)
270 fn('-', &ptr);
271 count++;
273 /* emit string/char/converted number */
274 while(*where != '\0')
276 fn(*where++, &ptr);
277 count++;
279 /* pad on right with spaces (for left justify) */
280 if(given_wd < actual_wd)
281 given_wd = 0;
282 else given_wd -= actual_wd;
283 for(; given_wd; given_wd--)
285 fn(' ', &ptr);
286 count++;
288 break;
289 default:
290 break;
292 default:
293 state = flags = given_wd = 0;
294 break;
297 return count;
300 /*****************************************************************************
301 SPRINTF
302 *****************************************************************************/
303 int vsprintf_help(unsigned c, void **ptr)
305 char *dst;
307 dst = *ptr;
308 *dst++ = c;
309 *ptr = dst;
310 return 0 ;
312 /*****************************************************************************
313 *****************************************************************************/
314 int vsprintf(char *buffer, const char *fmt, va_list args)
316 int ret_val;
318 ret_val = do_printf(fmt, args, vsprintf_help, (void *)buffer);
319 buffer[ret_val] = '\0';
320 return ret_val;
323 #if 0 /* testing */
324 /*****************************************************************************
325 *****************************************************************************/
326 int sprintf(char *buffer, const char *fmt, ...)
328 va_list args;
329 int ret_val;
331 va_start(args, fmt);
332 ret_val = vsprintf(buffer, fmt, args);
333 va_end(args);
334 return ret_val;
336 /*****************************************************************************
337 PRINTF
338 You must write your own putchar()
339 *****************************************************************************/
340 int vprintf_help(unsigned c, void **ptr)
342 putchar(c);
343 return 0 ;
345 /*****************************************************************************
346 *****************************************************************************/
347 int vprintf(const char *fmt, va_list args)
349 return do_printf(fmt, args, vprintf_help, NULL);
351 /*****************************************************************************
352 *****************************************************************************/
353 int printf(const char *fmt, ...)
355 va_list args;
356 int ret_val;
358 va_start(args, fmt);
359 ret_val = vprintf(fmt, args);
360 va_end(args);
361 return ret_val;
363 /*****************************************************************************
364 *****************************************************************************/
365 int main(void)
367 char buf[64];
369 sprintf(buf, "%u score and %i years ago...\n", 4, -7);
370 puts(buf);
372 sprintf(buf, "-1L == 0x%lX == octal %lo\n", -1L, -1L);
373 puts(buf);
375 printf("<%-08s> and <%08s> justified strings\n", "left", "right");
376 return 0;
378 #endif