| /* |
| Copyright 2001, 2002 Georges Menie (www.menie.org) |
| stdarg version contributed by Christian Ettinger |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU Lesser General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| /* |
| putchar is the only external dependency for this file, |
| if you have a working putchar, leave it commented out. |
| If not, uncomment the define below and |
| replace outbyte(c) by your own function call. |
| |
| #define putchar(c) outbyte(c) |
| */ |
| |
| #include <stdarg.h> |
| |
| static void printchar(char **str, int c) |
| { |
| extern int putchar(int c); |
| |
| if (str) { |
| **str = c; |
| ++(*str); |
| } |
| else (void)putchar(c); |
| } |
| |
| #define PAD_RIGHT 1 |
| #define PAD_ZERO 2 |
| |
| static int prints(char **out, const char *string, int width, int pad) |
| { |
| register int pc = 0, padchar = ' '; |
| |
| if (width > 0) { |
| register int len = 0; |
| register const char *ptr; |
| for (ptr = string; *ptr; ++ptr) ++len; |
| if (len >= width) width = 0; |
| else width -= len; |
| if (pad & PAD_ZERO) padchar = '0'; |
| } |
| if (!(pad & PAD_RIGHT)) { |
| for ( ; width > 0; --width) { |
| printchar (out, padchar); |
| ++pc; |
| } |
| } |
| for ( ; *string ; ++string) { |
| printchar (out, *string); |
| ++pc; |
| } |
| for ( ; width > 0; --width) { |
| printchar (out, padchar); |
| ++pc; |
| } |
| |
| return pc; |
| } |
| |
| /* the following should be enough for 32 bit int */ |
| #define PRINT_BUF_LEN 12 |
| |
| static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase) |
| { |
| char print_buf[PRINT_BUF_LEN]; |
| register char *s; |
| register int t, neg = 0, pc = 0; |
| register unsigned int u = i; |
| |
| if (i == 0) { |
| print_buf[0] = '0'; |
| print_buf[1] = '\0'; |
| return prints (out, print_buf, width, pad); |
| } |
| |
| if (sg && b == 10 && i < 0) { |
| neg = 1; |
| u = -i; |
| } |
| |
| s = print_buf + PRINT_BUF_LEN-1; |
| *s = '\0'; |
| |
| while (u) { |
| t = u % b; |
| if( t >= 10 ) |
| t += letbase - '0' - 10; |
| *--s = t + '0'; |
| u /= b; |
| } |
| |
| if (neg) { |
| if( width && (pad & PAD_ZERO) ) { |
| printchar (out, '-'); |
| ++pc; |
| --width; |
| } |
| else { |
| *--s = '-'; |
| } |
| } |
| |
| return pc + prints (out, s, width, pad); |
| } |
| |
| static int print( char **out, const char *format, va_list args ) |
| { |
| register int width, pad; |
| register int pc = 0; |
| char scr[2];
|
|
|
| for (; *format != 0; ++format) {
|
| if (*format == '%') { |
| ++format; |
| width = pad = 0; |
| if (*format == '\0') break; |
| if (*format == '%') goto out; |
| if (*format == '-') { |
| ++format; |
| pad = PAD_RIGHT; |
| } |
| while (*format == '0') { |
| ++format; |
| pad |= PAD_ZERO; |
| } |
| for ( ; *format >= '0' && *format <= '9'; ++format) { |
| width *= 10; |
| width += *format - '0'; |
| } |
| if( *format == 's' ) { |
| register char *s = (char *)va_arg( args, int ); |
| pc += prints (out, s?s:"(null)", width, pad); |
| continue; |
| } |
| if( *format == 'd' ) { |
| pc += printi (out, va_arg( args, int ), 10, 1, width, pad, 'a'); |
| continue; |
| } |
| if( *format == 'x' ) { |
| pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'a'); |
| continue; |
| } |
| if( *format == 'X' ) { |
| pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'A'); |
| continue; |
| } |
| if( *format == 'u' ) { |
| pc += printi (out, va_arg( args, int ), 10, 0, width, pad, 'a'); |
| continue; |
| } |
| if( *format == 'c' ) { |
| /* char are converted to int then pushed on the stack */ |
| scr[0] = (char)va_arg( args, int ); |
| scr[1] = '\0'; |
| pc += prints (out, scr, width, pad); |
| continue; |
| } |
| } |
| else { |
| out:
|
| printchar (out, *format); |
| ++pc;
|
| } |
| } |
| if (out) **out = '\0'; |
| va_end( args ); |
| return pc; |
| } |
| |
| int printf(const char *format, ...) |
| { |
| va_list args; |
| |
| va_start( args, format ); |
| return print( 0, format, args ); |
| } |
| |
| int sprintf(char *out, const char *format, ...) |
| { |
| va_list args; |
| |
| va_start( args, format ); |
| return print( &out, format, args ); |
| } |
|
|
|
|
| int snprintf( char *buf, unsigned int count, const char *format, ... )
|
| {
|
| va_list args; |
| |
| ( void ) count;
|
|
|
| va_start( args, format );
|
| return print( &buf, format, args );
|
| }
|
|
|
| |
| #ifdef TEST_PRINTF |
| int main(void) |
| { |
| char *ptr = "Hello world!"; |
| char *np = 0; |
| int i = 5; |
| unsigned int bs = sizeof(int)*8; |
| int mi; |
| char buf[80]; |
| |
| mi = (1 << (bs-1)) + 1; |
| printf("%s\n", ptr); |
| printf("printf test\n"); |
| printf("%s is null pointer\n", np); |
| printf("%d = 5\n", i); |
| printf("%d = - max int\n", mi); |
| printf("char %c = 'a'\n", 'a'); |
| printf("hex %x = ff\n", 0xff); |
| printf("hex %02x = 00\n", 0); |
| printf("signed %d = unsigned %u = hex %x\n", -3, -3, -3); |
| printf("%d %s(s)%", 0, "message"); |
| printf("\n"); |
| printf("%d %s(s) with %%\n", 0, "message"); |
| sprintf(buf, "justif: \"%-10s\"\n", "left"); printf("%s", buf); |
| sprintf(buf, "justif: \"%10s\"\n", "right"); printf("%s", buf); |
| sprintf(buf, " 3: %04d zero padded\n", 3); printf("%s", buf); |
| sprintf(buf, " 3: %-4d left justif.\n", 3); printf("%s", buf); |
| sprintf(buf, " 3: %4d right justif.\n", 3); printf("%s", buf); |
| sprintf(buf, "-3: %04d zero padded\n", -3); printf("%s", buf); |
| sprintf(buf, "-3: %-4d left justif.\n", -3); printf("%s", buf); |
| sprintf(buf, "-3: %4d right justif.\n", -3); printf("%s", buf); |
| |
| return 0; |
| } |
| |
| /* |
| * if you compile this file with |
| * gcc -Wall $(YOUR_C_OPTIONS) -DTEST_PRINTF -c printf.c |
| * you will get a normal warning: |
| * printf.c:214: warning: spurious trailing `%' in format |
| * this line is testing an invalid % at the end of the format string. |
| * |
| * this should display (on 32bit int machine) : |
| * |
| * Hello world! |
| * printf test |
| * (null) is null pointer |
| * 5 = 5 |
| * -2147483647 = - max int |
| * char a = 'a' |
| * hex ff = ff |
| * hex 00 = 00 |
| * signed -3 = unsigned 4294967293 = hex fffffffd |
| * 0 message(s) |
| * 0 message(s) with % |
| * justif: "left " |
| * justif: " right" |
| * 3: 0003 zero padded |
| * 3: 3 left justif. |
| * 3: 3 right justif. |
| * -3: -003 zero padded |
| * -3: -3 left justif. |
| * -3: -3 right justif. |
| */ |
| |
| #endif |
| |
| |