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 /*****************************************************************************
25 - changes to make characters 0x80-0xFF display properly
28 - changes to make vsprintf() terminate string with '\0'
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
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
47 width: (field width) DONE
51 conv: d,i decimal int DONE
52 u decimal unsigned DONE
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) */
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
];
86 state
= flags
= count
= given_wd
= 0;
87 /* begin scanning format specifier list */
92 /* STATE 0: AWAITING % */
94 if(*fmt
!= '%') /* not %... */
96 fn(*fmt
, &ptr
); /* ...just echo it */
100 /* found %, get next char and advance state to check if next char is a flag */
104 /* STATE 1: AWAITING FLAGS (%-0) */
106 if(*fmt
== '%') /* %% */
110 state
= flags
= given_wd
= 0;
115 if(flags
& PR_LJ
)/* %-- is illegal */
116 state
= flags
= given_wd
= 0;
121 /* not a flag char: advance state to check if it's field width */
123 /* check now for '%0...' */
130 /* STATE 2: AWAITING (NUMERIC) FIELD WIDTH */
132 if(*fmt
>= '0' && *fmt
<= '9')
134 given_wd
= 10 * given_wd
+
138 /* not field width: advance state to check if it's a modifier */
141 /* STATE 3: AWAITING MODIFIER CHARS (FNlh) */
160 /* not modifier: advance state to check if it's a conversion char */
163 /* STATE 4: AWAITING CONVERSION CHARS (Xxpndiuocs) */
165 where
= buf
+ PR_BUFLEN
- 1;
172 /* xxx - far pointers (%Fp, %Fn) not yet supported */
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
)
194 num
= va_arg(args
, short);
196 num
= va_arg(args
, unsigned short);
198 /* no h nor l: sizeof(int) bits (signed or unsigned) */
202 num
= va_arg(args
, int);
204 num
= va_arg(args
, unsigned int);
206 /* take care of sign */
215 /* convert binary to octal/decimal/hex ASCII
216 OK, I found my mistake. The math here is _always_ unsigned */
221 temp
= (unsigned long)num
% radix
;
225 else if(flags
& PR_CA
)
226 *where
= temp
- 10 + 'A';
228 *where
= temp
- 10 + 'a';
229 num
= (unsigned long)num
/ radix
;
234 /* disallow pad-left-with-zeroes for %c */
237 *where
= (unsigned char)va_arg(args
,
242 /* disallow pad-left-with-zeroes for %s */
244 where
= va_arg (args
, unsigned char *);
246 actual_wd
= strlen ((char *) where
);
249 /* if we pad left with ZEROES, do the sign now */
250 if((flags
& (PR_WS
| PR_LZ
)) ==
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' :
267 /* if we pad left with SPACES, do the sign now */
268 if((flags
& (PR_WS
| PR_LZ
)) == PR_WS
)
273 /* emit string/char/converted number */
274 while(*where
!= '\0')
279 /* pad on right with spaces (for left justify) */
280 if(given_wd
< actual_wd
)
282 else given_wd
-= actual_wd
;
283 for(; given_wd
; given_wd
--)
293 state
= flags
= given_wd
= 0;
300 /*****************************************************************************
302 *****************************************************************************/
303 int vsprintf_help(unsigned c
, void **ptr
)
312 /*****************************************************************************
313 *****************************************************************************/
314 int vsprintf(char *buffer
, const char *fmt
, va_list args
)
318 ret_val
= do_printf(fmt
, args
, vsprintf_help
, (void *)buffer
);
319 buffer
[ret_val
] = '\0';
324 /*****************************************************************************
325 *****************************************************************************/
326 int sprintf(char *buffer
, const char *fmt
, ...)
332 ret_val
= vsprintf(buffer
, fmt
, args
);
336 /*****************************************************************************
338 You must write your own putchar()
339 *****************************************************************************/
340 int vprintf_help(unsigned c
, void **ptr
)
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
, ...)
359 ret_val
= vprintf(fmt
, args
);
363 /*****************************************************************************
364 *****************************************************************************/
369 sprintf(buf
, "%u score and %i years ago...\n", 4, -7);
372 sprintf(buf
, "-1L == 0x%lX == octal %lo\n", -1L, -1L);
375 printf("<%-08s> and <%08s> justified strings\n", "left", "right");