home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <limits.h>
- #include <ctype.h>
- #include <math.h>
- #include <float.h>
-
- /* a little macro to make life easier */
-
- #define OUT(c) do \
- { if(putc((c),stream)==EOF) \
- return outcount; \
- outcount++; \
- }while(0)
-
- #define BITSPERBYTE CHAR_BIT
-
- #define MINFLOATSIZE (DBL_DIG+1) /* Why not 1 more - it's 97% reliable */
- #define MININTSIZE (sizeof(unsigned long)*BITSPERBYTE/3+1)
- #define MINPOINTSIZE (sizeof(void *)*BITSPERBYTE/4+1)
- #define REQUIREDBUFFER (MININTSIZE>MINPOINTSIZE? \
- (MININTSIZE>MINFLOATSIZE?MININTSIZE:MINFLOATSIZE): \
- (MINPOINTSIZE>MINFLOATSIZE?MINPOINTSIZE:MINFLOATSIZE))
-
- #define ALTERNATEFLAG 1 /* '#' is set */
- #define ZEROPADFLAG 2 /* '0' is set */
- #define LALIGNFLAG 4 /* '-' is set */
- #define BLANKFLAG 8 /* ' ' is set */
- #define SIGNFLAG 16 /* '+' is set */
-
-
- int vfprintf(FILE *stream,const char *format,va_list args)
- {
- size_t outcount=0;
-
- while(*format)
- {
- if(*format=='%')
- {
- static char flagc[]=
- { '#','0','-',' ','+' };
- static char lowertabel[]=
- { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
- static char uppertabel[]=
- { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
- size_t width=0,preci=ULONG_MAX,flags=0; /* Specifications */
- char type,subtype='i';
- char buffer1[2]; /* Signs and that like */
- char buffer[REQUIREDBUFFER]; /* The body */
- char *buffer2=buffer; /* So we can set this to any other strings */
- size_t size1=0,size2=0; /* How many chars in buffer? */
- const char *ptr=format+1; /* pointer to format string */
- size_t i,pad; /* Some temporary variables */
-
- do /* read flags */
- for(i=0;i<sizeof(flagc);i++)
- if(flagc[i]==*ptr)
- { flags|=1<<i;
- ptr++;
- break; }
- while(i<sizeof(flagc));
-
- if(*ptr=='*') /* read width from arguments */
- { signed int a;
- ptr++;
- a=va_arg(args,signed int);
- if(a<0)
- { flags|=LALIGNFLAG;
- width=-a; }
- else
- width=a;
- }else
- while(isdigit(*ptr))
- width=width*10+(*ptr++-'0');
-
- if(*ptr=='.')
- { ptr++;
- if(*ptr=='*') /* read precision from arguments */
- { signed int a;
- ptr++;
- a=va_arg(args,signed int);
- if(a>=0)
- preci=a;
- }else
- { preci=0;
- while(isdigit(*ptr))
- preci=preci*10+(*ptr++-'0');
- }
- }
-
- if(*ptr=='h'||*ptr=='l'||*ptr=='L')
- subtype=*ptr++;
-
- type=*ptr++;
-
- switch(type)
- { case 'd':
- case 'i':
- case 'o':
- case 'p':
- case 'u':
- case 'x':
- case 'X':
- { unsigned long v;
- char *tabel;
- int base;
-
- if(type=='p')
- { subtype='l'; /* This is written as %#lx */
- type='x';
- flags|=ALTERNATEFLAG; }
-
- if(type=='d'||type=='i') /* These are signed */
- { signed long v2;
- if(subtype=='l')
- v2=va_arg(args,signed long);
- else
- v2=va_arg(args,signed int);
- if(v2<0)
- { buffer1[size1++]='-';
- v=-v2;
- }else
- { if(flags&SIGNFLAG)
- buffer1[size1++]='+';
- else if(flags&BLANKFLAG)
- buffer1[size1++]=' ';
- v=v2; }
- }else /* These are unsigned */
- { if(subtype=='l')
- v=va_arg(args,unsigned long);
- else
- v=va_arg(args,unsigned int);
- if(flags&ALTERNATEFLAG)
- { if(type=='o'&&(preci||v))
- buffer1[size1++]='0';
- if((type=='x'||type=='X')&&v)
- { buffer1[size1++]='0';
- buffer1[size1++]=type; }
- }
- }
-
- buffer2=&buffer[sizeof(buffer)]; /* Calculate body string */
- base=type=='x'||type=='X'?16:(type=='o'?8:10);
- tabel=type!='X'?lowertabel:uppertabel;
- do
- { *--buffer2=tabel[v%base];
- v=v/base;
- size2++;
- }while(v);
- if(preci==ULONG_MAX) /* default */
- preci=0;
- else
- flags&=~ZEROPADFLAG;
- break;
- }
- case 'c':
- if(subtype=='l')
- *buffer2=va_arg(args,long);
- else
- *buffer2=va_arg(args,int);
- size2=1;
- preci=0;
- break;
- case 's':
- buffer2=va_arg(args,char *);
- { char *p=buffer2;
- size2=0;
- while((width<=0||size2<width)&&*p){size2++;p++;}
- }
- size2=size2<=preci?size2:preci;
- preci=0;
- break;
- #ifdef MATH
- case 'f':
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- { double v;
- char killzeros=0,sign=0; /* some flags */
- int ex1,ex2; /* Some temporary variables */
- size_t size,dnum,dreq;
- char *udstr=NULL;
-
- v=va_arg(args,double);
-
- if(isinf(v))
- { if(v>0)
- udstr="+inf";
- else
- udstr="-inf";
- }else if(isnan(v))
- udstr="NaN";
-
- if(udstr!=NULL)
- { size2=strlen(udstr);
- preci=0;
- buffer2=udstr;
- break; }
-
- if(preci==ULONG_MAX) /* old default */
- preci=6; /* new default */
-
- if(v<0.0)
- { sign='-';
- v=-v;
- }else
- { if(flags&SIGNFLAG)
- sign='+';
- else if(flags&BLANKFLAG)
- sign=' ';
- }
-
- ex1=0;
- if(v!=0.0)
- { ex1=log10(v);
- if(v<1.0)
- v=v*pow(10,- --ex1); /* Caution: (int)log10(.5)!=-1 */
- else
- v=v/pow(10,ex1);
- if(v<1.0) /* adjust if we are too low (log10(.1)=-.999999999) */
- { v*=10.0; /* luckily this cannot happen with FLT_MAX and FLT_MIN */
- ex1--; } /* The case too high (log(10.)=.999999999) is done later */
- }
-
- ex2=preci;
- if(type=='f')
- ex2+=ex1;
- if(tolower(type)=='g')
- ex2--;
- v+=.5/pow(10,ex2<MINFLOATSIZE?ex2:MINFLOATSIZE); /* Round up */
-
- if(v>=10.0) /* Adjusts log10(10.)=.999999999 too */
- { v/=10.0;
- ex1++; }
-
- if(tolower(type)=='g') /* This changes to one of the other types */
- { if(ex1<(signed long)preci&&ex1>=-4)
- { type='f';
- preci-=ex1;
- }else
- type=type=='g'?'e':'E';
- preci--;
- if(!(flags&ALTERNATEFLAG))
- killzeros=1; /* set flag to kill trailing zeros */
- }
-
- dreq=preci+1; /* Calculate number of decimal places required */
- if(type=='f')
- dreq+=ex1; /* even more before the decimal point */
-
- dnum=0;
- while(dnum<dreq&&dnum<MINFLOATSIZE) /* Calculate all decimal places needed */
- { buffer[dnum++]=(char)v+'0';
- v=(v-(double)(char)v)*10.0; }
-
- if(killzeros) /* Kill trailing zeros if possible */
- while(preci&&(dreq-->dnum||buffer[dreq]=='0'))
- preci--;
-
- if(type=='f')/* Calculate actual size of string (without sign) */
- { size=preci+1; /* numbers after decimal point + 1 before */
- if(ex1>0)
- size+=ex1; /* numbers >= 10 */
- if(preci||flags&ALTERNATEFLAG)
- size++; /* 1 for decimal point */
- }else
- { size=preci+5; /* 1 for the number before the decimal point, and 4 for the exponent */
- if(preci||flags&ALTERNATEFLAG)
- size++;
- if(ex1>99||ex1<-99)
- size++; /* exponent needs an extra decimal place */
- }
-
- pad=size+(sign!=0);
- pad=pad>=width?0:width-pad;
-
- if(sign&&flags&ZEROPADFLAG)
- OUT(sign);
-
- if(!(flags&LALIGNFLAG))
- for(i=0;i<pad;i++)
- OUT(flags&ZEROPADFLAG?'0':' ');
-
- if(sign&&!(flags&ZEROPADFLAG))
- OUT(sign);
-
- dreq=0;
- if(type=='f')
- { if(ex1<0)
- OUT('0');
- else
- while(ex1>=0)
- { OUT(dreq<dnum?buffer[dreq++]:'0');
- ex1--; }
- if(preci||flags&ALTERNATEFLAG)
- { OUT('.');
- while(preci--)
- if(++ex1<0)
- OUT('0');
- else
- OUT(dreq<dnum?buffer[dreq++]:'0');
- }
- }else
- { OUT(buffer[dreq++]);
- if(preci||flags&ALTERNATEFLAG)
- { OUT('.');
- while(preci--)
- OUT(dreq<dnum?buffer[dreq++]:'0');
- }
- OUT(type);
- if(ex1<0)
- { OUT('-');
- ex1=-ex1; }
- else
- OUT('+');
- if(ex1>99)
- OUT(ex1/100+'0');
- OUT(ex1/10%10+'0');
- OUT(ex1%10+'0');
- }
-
- if(flags&LALIGNFLAG)
- for(i=0;i<pad;i++)
- OUT(' ');
-
- width=preci=0; /* Everything already done */
- break;
- }
- #endif
- case '%':
- buffer2="%";
- size2=1;
- preci=0;
- break;
- case 'n':
- *va_arg(args,int *)=outcount;
- width=preci=0;
- break;
- default:
- if(!type)
- ptr--; /* We've gone too far - step one back */
- buffer2=(char *)format;
- size2=ptr-format;
- width=preci=0;
- break;
- }
- pad=size1+(size2>=preci?size2:preci); /* Calculate the number of characters */
- pad=pad>=width?0:width-pad; /* and the number of resulting pad bytes */
-
- if(flags&ZEROPADFLAG) /* print sign and that like */
- for(i=0;i<size1;i++)
- OUT(buffer1[i]);
-
- if(!(flags&LALIGNFLAG)) /* Pad left */
- for(i=0;i<pad;i++)
- OUT(flags&ZEROPADFLAG?'0':' ');
-
- if(!(flags&ZEROPADFLAG)) /* print sign if not zero padded */
- for(i=0;i<size1;i++)
- OUT(buffer1[i]);
-
- for(i=size2;i<preci;i++) /* extend to precision */
- OUT('0');
-
- for(i=0;i<size2;i++) /* print body */
- OUT(buffer2[i]);
-
- if(flags&LALIGNFLAG) /* Pad right */
- for(i=0;i<pad;i++)
- OUT(' ');
-
- format=ptr;
- }
- else
- OUT(*format++);
- }
- return outcount;
- }
-