#include <stdio.h>
#include <stdarg.h> //va_list, va_start, va_arg, va_end
#include <string.h> //strchr
//compile with: gcc -Wall -Wextra cc-c.c -o cc-c
//run with: ./cc-c
//K&R2 exercise 7-3: minprintf revised to handle more of printf.
//Instead of re-implementing every conversion by hand, each %-spec is
//copied into a small local format string ("%-8.3f", "%lx", ...) and
//handed to the real printf together with ONE argument, fetched with
//va_arg as the right type. minprintf's job is now just the parsing:
//which characters make up the spec, and which type the conversion eats.
//
//Handled: flags - + space # 0, width, .precision, * for width or
//precision taken from the argument list, the l (long) modifier, and
//conversions d i o x X u c s f e E g G p %.
#define LOCALFMT 100 //room for one conversion spec
void minprintf(char *fmt, ...);
int main(void)
{
//each line: real printf first, then minprintf with the same args
printf( "[%d] [%5d] [%-5d] [%05d] [%+d]\n", 42, 42, 42, 42, 42);
minprintf( "[%d] [%5d] [%-5d] [%05d] [%+d]\n", 42, 42, 42, 42, 42);
printf( "[%o] [%#o] [%x] [%#X] [%u]\n", 255, 255, 255, 255, 255u);
minprintf( "[%o] [%#o] [%x] [%#X] [%u]\n", 255, 255, 255, 255, 255u);
printf( "[%ld] [%lx]\n", 1234567890123L, 0xdeadbeefcafeUL);
minprintf( "[%ld] [%lx]\n", 1234567890123L, 0xdeadbeefcafeUL);
printf( "[%f] [%.2f] [%10.3f] [%e] [%g]\n", 3.14159, 3.14159, 3.14159, 31415.9, 0.0001);
minprintf( "[%f] [%.2f] [%10.3f] [%e] [%g]\n", 3.14159, 3.14159, 3.14159, 31415.9, 0.0001);
printf( "[%c] [%s] [%10s] [%-10s] [%.3s]\n", 'A', "hello", "hello", "hello", "hello");
minprintf( "[%c] [%s] [%10s] [%-10s] [%.3s]\n", 'A', "hello", "hello", "hello", "hello");
printf( "[%*d] [%-*.*f] [100%%]\n", 6, 42, 9, 2, 2.71828);
minprintf( "[%*d] [%-*.*f] [100%%]\n", 6, 42, 9, 2, 2.71828);
return 0;
}
//minprintf: minimal printf with variable argument list
void minprintf(char *fmt, ...)
{
va_list ap; //points to each unnamed arg in turn
char *p;
char localfmt[LOCALFMT];
int i;
int islong;
va_start(ap, fmt); //make ap point to 1st unnamed arg
for (p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
i = 0;
localfmt[i++] = '%';
//flags, width, precision: copy them through as they are.
//A '*' means "the next int argument", so fetch it now and
//write its digits into localfmt in place of the '*'.
//The bound on i leaves room for the digits of one int plus
//the modifier, the conversion, and the '\0'
while (*(p+1) && strchr("-+ #0123456789.*", *(p+1))
&& i < LOCALFMT - 16)
if (*++p == '*')
i += sprintf(localfmt + i, "%d", va_arg(ap, int));
else
localfmt[i++] = *p;
islong = 0;
if (*(p+1) == 'l') {
localfmt[i++] = *++p;
islong = 1;
}
localfmt[i++] = *++p; //the conversion character
localfmt[i] = '\0';
//IDIOM: case labels stacked with no break -- several values
//share one body. Each group is "conversions that eat this type"
switch (*p) {
case 'd': case 'i':
if (islong)
printf(localfmt, va_arg(ap, long));
else
printf(localfmt, va_arg(ap, int));
break;
case 'o': case 'x': case 'X': case 'u':
if (islong)
printf(localfmt, va_arg(ap, unsigned long));
else
printf(localfmt, va_arg(ap, unsigned int));
break;
case 'c': //a char arrives promoted to int
printf(localfmt, va_arg(ap, int));
break;
case 'f': case 'e': case 'E': case 'g': case 'G':
printf(localfmt, va_arg(ap, double)); //floats arrive as double
break;
case 's':
printf(localfmt, va_arg(ap, char *));
break;
case 'p':
printf(localfmt, va_arg(ap, void *));
break;
case '%':
putchar('%');
break;
case '\0': //fmt ended inside a spec, e.g. "50%":
localfmt[i-1] = '\0'; //print what was collected, and stop
fputs(localfmt, stdout); //before p++ steps past the '\0'
va_end(ap);
return;
default: //unknown conversion: print it as text
fputs(localfmt, stdout);
break;
}
}
va_end(ap); //clean up when done
}
win@DESKTOP-MEIH88T:~/webdev-projects$ gcc -Wall -Wextra cc-c.c -o cc-c
win@DESKTOP-MEIH88T:~/webdev-projects$ ./cc-c
[42] [ 42] [42 ] [00042] [+42]
[42] [ 42] [42 ] [00042] [+42]
[377] [0377] [ff] [0XFF] [255]
[377] [0377] [ff] [0XFF] [255]
[1234567890123] [deadbeefcafe]
[1234567890123] [deadbeefcafe]
[3.141590] [3.14] [ 3.142] [3.141590e+04] [0.0001]
[3.141590] [3.14] [ 3.142] [3.141590e+04] [0.0001]
[A] [hello] [ hello] [hello ] [hel]
[A] [hello] [ hello] [hello ] [hel]
[ 42] [2.72 ] [100%]
[ 42] [2.72 ] [100%]