#include <stdarg.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "dietstdio.h"
#define WANT_NULL_PRINTF
#define WANT_FLOATING_POINT_IN_PRINTF
static inline unsigned int skip_to(const char *format) {
unsigned int nr;
for (nr=0; format[nr] && (format[nr]!='%'); ++nr);
return nr;
}
#define A_WRITE(fn,buf,sz) ((fn)->put((void*)(buf),(sz),(fn)->data))
static const char pad_line[2][16]= { " ", "0000000000000000", };
static inline int write_pad(struct arg_printf* fn, int len, int padwith) {
int nr=0;
for (;len>15;len-=16,nr+=16) {
A_WRITE(fn,pad_line[(padwith=='0')?1:0],16);
}
if (len>0) {
A_WRITE(fn,pad_line[(padwith=='0')?1:0],(unsigned int)len); nr+=len;
}
return nr;
}
int __v_printf(struct arg_printf* fn, const char *format, va_list arg_ptr)
{
int len=0;
#ifdef WANT_ERROR_PRINTF
int _errno = errno;
#endif
while (*format) {
unsigned int sz = skip_to(format);
if (sz) {
A_WRITE(fn,format,sz); len+=sz;
format+=sz;
}
if (*format=='%') {
char buf[128];
union { char*s; } u_str;
#define s u_str.s
int retval;
unsigned char ch, padwith=' ';
char flag_in_sign=0;
char flag_upcase=0;
char flag_hash=0;
char flag_left=0;
char flag_space=0;
char flag_sign=0;
char flag_dot=0;
signed char flag_long=0;
unsigned int base;
unsigned int width=0, preci=0;
long number=0;
#ifdef WANT_LONGLONG_PRINTF
long long llnumber=0;
#endif
++format;
inn_printf:
switch(ch=*format++) {
case 0:
return -1;
break;
case '#':
flag_hash=-1;
case 'z':
goto inn_printf;
case 'h':
--flag_long;
goto inn_printf;
case 'q':
case 'L':
++flag_long;
case 'l':
++flag_long;
goto inn_printf;
case '-':
flag_left=1;
goto inn_printf;
case ' ':
flag_space=1;
goto inn_printf;
case '+':
flag_sign=1;
goto inn_printf;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if(flag_dot) return -1;
width=strtoul(format-1,(char**)&s,10);
if (ch=='0' && !flag_left) padwith='0';
format=s;
goto inn_printf;
case '*':
width=va_arg(arg_ptr,int);
goto inn_printf;
case '.':
flag_dot=1;
if (*format=='*') {
int tmp=va_arg(arg_ptr,int);
preci=tmp<0?0:tmp;
++format;
} else {
long int tmp=strtol(format,(char**)&s,10);
preci=tmp<0?0:tmp;
format=s;
}
goto inn_printf;
case 'c':
ch=(char)va_arg(arg_ptr,int);
case '%':
A_WRITE(fn,&ch,1); ++len;
break;
#ifdef WANT_ERROR_PRINTF
case 'm':
s=strerror(_errno);
sz=strlen(s);
A_WRITE(fn,s,sz); len+=sz;
break;
#endif
case 's':
s=va_arg(arg_ptr,char *);
#ifdef WANT_NULL_PRINTF
if (!s) s="(null)";
#endif
sz = strlen(s);
if (flag_dot && sz>preci) sz=preci;
preci=0;
flag_dot^=flag_dot;
padwith=' ';
print_out:
{
char *sign=s;
int todo=0;
int vs;
if (! (width||preci) ) {
A_WRITE(fn,s,sz); len+=sz;
break;
}
if (flag_in_sign) todo=1;
if (flag_hash>0) todo=flag_hash;
if (todo) {
s+=todo;
sz-=todo;
width-=todo;
}
if (!flag_left) {
if (flag_dot) {
vs=preci>sz?preci:sz;
len+=write_pad(fn,(signed int)width-(signed int)vs,' ');
if (todo) {
A_WRITE(fn,sign,todo);
len+=todo;
}
len+=write_pad(fn,(signed int)preci-(signed int)sz,'0');
} else {
if (todo && padwith=='0') {
A_WRITE(fn,sign,todo);
len+=todo; todo=0;
}
len+=write_pad(fn,(signed int)width-(signed int)sz, padwith);
if (todo) {
A_WRITE(fn,sign,todo);
len+=todo;
}
}
A_WRITE(fn,s,sz); len+=sz;
} else if (flag_left) {
if (todo) {
A_WRITE(fn,sign,todo);
len+=todo;
}
len+=write_pad(fn,(signed int)preci-(signed int)sz, '0');
A_WRITE(fn,s,sz); len+=sz;
vs=preci>sz?preci:sz;
len+=write_pad(fn,(signed int)width-(signed int)vs, ' ');
} else {
A_WRITE(fn,s,sz); len+=sz;
}
break;
}
case 'b':
base=2;
sz=0;
goto num_printf;
case 'p':
flag_hash=2;
flag_long=1;
ch='x';
case 'X':
flag_upcase=(ch=='X');
case 'x':
base=16;
sz=0;
if (flag_hash) {
buf[1]='0';
buf[2]=ch;
flag_hash=2;
sz=2;
}
if (preci>width) width=preci;
goto num_printf;
case 'd':
case 'i':
flag_in_sign=1;
case 'u':
base=10;
sz=0;
goto num_printf;
case 'o':
base=8;
sz=0;
if (flag_hash) {
buf[1]='0';
flag_hash=1;
++sz;
}
num_printf:
s=buf+1;
if (flag_long>0) {
#ifdef WANT_LONGLONG_PRINTF
if (flag_long>1)
llnumber=va_arg(arg_ptr,long long);
else
#endif
number=va_arg(arg_ptr,long);
}
else
number=va_arg(arg_ptr,int);
if (flag_in_sign) {
#ifdef WANT_LONGLONG_PRINTF
if ((flag_long>1)&&(llnumber<0)) {
llnumber=-llnumber;
flag_in_sign=2;
} else
#endif
if (number<0) {
number=-number;
flag_in_sign=2;
}
}
if (flag_long<0) number&=0xffff;
if (flag_long<-1) number&=0xff;
#ifdef WANT_LONGLONG_PRINTF
if (flag_long>1)
retval = __lltostr(s+sz,sizeof(buf)-5,(unsigned long long) llnumber,base,flag_upcase);
else
#endif
retval = __ltostr(s+sz,sizeof(buf)-5,(unsigned long) number,base,flag_upcase);
if (flag_dot && retval == 1 && s[sz] == '0') {
if (preci == 0||flag_hash > 0) {
sz = 0;
}
flag_hash = 0;
} else sz += retval;
if (flag_in_sign==2) {
*(--s)='-';
++sz;
} else if ((flag_in_sign)&&(flag_sign || flag_space)) {
*(--s)=(flag_sign)?'+':' ';
++sz;
} else flag_in_sign=0;
goto print_out;
#ifdef WANT_FLOATING_POINT_IN_PRINTF
case 'f':
case 'g':
{
int g=(ch=='g');
double d=va_arg(arg_ptr,double);
s=buf+1;
if (width==0) width=1;
if (!flag_dot) preci=6;
if (flag_sign || d < +0.0) flag_in_sign=1;
sz=__dtostr(d,s,sizeof(buf)-1,width,preci);
if (flag_dot) {
char *tmp;
if ((tmp=strchr(s,'.'))) {
if (preci || flag_hash) ++tmp;
while (preci>0 && *++tmp) --preci;
*tmp=0;
} else if (flag_hash) {
s[sz]='.';
s[++sz]='\0';
}
}
if (g) {
char *tmp,*tmp1;
if ((tmp=strchr(s,'.'))) {
tmp1=strchr(tmp,'e');
while (*tmp) ++tmp;
if (tmp1) tmp=tmp1;
while (*--tmp=='0') ;
if (*tmp!='.') ++tmp;
*tmp=0;
if (tmp1) strcpy(tmp,tmp1);
}
}
if ((flag_sign || flag_space) && d>=0) {
*(--s)=(flag_sign)?'+':' ';
++sz;
}
sz=strlen(s);
flag_dot=0;
flag_hash=0;
goto print_out;
}
#endif
default:
break;
}
}
}
return len;
}