home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / misc / emu / AROSdev.lha / AROS / compiler / clib / __vcformat.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-04  |  9.6 KB  |  428 lines

  1. /*
  2.     (C) 1995-96 AROS - The Amiga Replacement OS
  3.     $Id: __vcformat.c,v 1.4 1997/01/28 15:32:31 digulla Exp $
  4.  
  5.     Desc: Function to format a string like printf().
  6.     Lang: english
  7. */
  8. /* Original source from libnix */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <stdarg.h>
  12. #include <limits.h>
  13. #include <ctype.h>
  14. #include <math.h>
  15. #include <float.h>
  16.  
  17. #ifndef BITSPERBYTE
  18. #   define BITSPERBYTE 8
  19. #endif
  20.  
  21. /* a little macro to make life easier */
  22. #define OUT(c)  do                           \
  23.         { if((*outc)((c),data)==EOF)   \
  24.             return outcount;         \
  25.           outcount++;             \
  26.         }while(0)
  27.  
  28. #define MINFLOATSIZE (DBL_DIG+1) /* Why not 1 more - it's 97% reliable */
  29. #define MININTSIZE (sizeof(unsigned long)*BITSPERBYTE/3+1)
  30. #define MINPOINTSIZE (sizeof(void *)*BITSPERBYTE/4+1)
  31. #define REQUIREDBUFFER (MININTSIZE>MINPOINTSIZE? \
  32.             (MININTSIZE>MINFLOATSIZE?MININTSIZE:MINFLOATSIZE): \
  33.             (MINPOINTSIZE>MINFLOATSIZE?MINPOINTSIZE:MINFLOATSIZE))
  34.  
  35. #define ALTERNATEFLAG 1  /* '#' is set */
  36. #define ZEROPADFLAG   2  /* '0' is set */
  37. #define LALIGNFLAG    4  /* '-' is set */
  38. #define BLANKFLAG     8  /* ' ' is set */
  39. #define SIGNFLAG      16 /* '+' is set */
  40.  
  41. unsigned char *__decimalpoint = ".";
  42.  
  43. /*****************************************************************************
  44.  
  45.     NAME */
  46.  
  47.     int __vcformat (
  48.  
  49. /*  SYNOPSIS */
  50.     void       * data,
  51.     int      (* outc)(int, void *),
  52.     const char * format,
  53.     va_list      args)
  54.  
  55. /*  FUNCTION
  56.     Format a list of arguments and call a function for each char
  57.     to print.
  58.  
  59.     INPUTS
  60.     data - This is passed to the usercallback outc
  61.     outc - Call this function for every character that should be
  62.         emitted. The function should return EOF on error and
  63.         > 0 otherwise.
  64.     format - A printf() format string.
  65.     args - A list of arguments for the format string.
  66.  
  67.     RESULT
  68.     The number of characters written.
  69.  
  70.     NOTES
  71.  
  72.     EXAMPLE
  73.  
  74.     BUGS
  75.  
  76.     SEE ALSO
  77.  
  78.     INTERNALS
  79.  
  80.     HISTORY
  81.     06.12.1996 digulla copied from libnix
  82.  
  83. ******************************************************************************/
  84. {
  85.   size_t outcount=0;
  86.  
  87.   while(*format)
  88.   {
  89.     if(*format=='%')
  90.     {
  91.       static char flagc[]=
  92.       { '#','0','-',' ','+' };
  93.       static char lowertabel[]=
  94.       { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
  95.       static char uppertabel[]=
  96.       { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
  97.       size_t width=0,preci=ULONG_MAX,flags=0; /* Specifications */
  98.       char type,subtype='i';
  99.       char buffer1[2];           /* Signs and that like */
  100.       char buffer[REQUIREDBUFFER]; /* The body */
  101.       char *buffer2=buffer;       /* So we can set this to any other strings */
  102.       size_t size1=0,size2=0;       /* How many chars in buffer? */
  103.       const char *ptr=format+1;    /* pointer to format string */
  104.       size_t i,pad;           /* Some temporary variables */
  105.  
  106.       do /* read flags */
  107.     for(i=0;i<sizeof(flagc);i++)
  108.       if(flagc[i]==*ptr)
  109.       { flags|=1<<i;
  110.         ptr++;
  111.         break; }
  112.       while(i<sizeof(flagc));
  113.  
  114.       if(*ptr=='*') /* read width from arguments */
  115.       { signed int a;
  116.     ptr++;
  117.     a=va_arg(args,signed int);
  118.     if(a<0)
  119.     { flags|=LALIGNFLAG;
  120.       width=-a; }
  121.     else
  122.       width=a;
  123.       }else
  124.     while(isdigit(*ptr))
  125.       width=width*10+(*ptr++-'0');
  126.  
  127.       if(*ptr=='.')
  128.       { ptr++;
  129.     if(*ptr=='*') /* read precision from arguments */
  130.     { signed int a;
  131.       ptr++;
  132.       a=va_arg(args,signed int);
  133.       if(a>=0)
  134.         preci=a;
  135.     }else
  136.     { preci=0;
  137.       while(isdigit(*ptr))
  138.         preci=preci*10+(*ptr++-'0');
  139.     }
  140.       }
  141.  
  142.       if(*ptr=='h'||*ptr=='l'||*ptr=='L')
  143.     subtype=*ptr++;
  144.  
  145.       type=*ptr++;
  146.  
  147.       switch(type)
  148.       { case 'd':
  149.     case 'i':
  150.     case 'o':
  151.     case 'p':
  152.     case 'u':
  153.     case 'x':
  154.     case 'X':
  155.     { unsigned long v;
  156.       char *tabel;
  157.       int base;
  158.  
  159.       if(type=='p')
  160.       { subtype='l'; /* This is written as %#lx */
  161.         type='x';
  162.         flags|=ALTERNATEFLAG; }
  163.  
  164.       if(type=='d'||type=='i') /* These are signed */
  165.       { signed long v2;
  166.         if(subtype=='l')
  167.           v2=va_arg(args,signed long);
  168.         else
  169.           v2=va_arg(args,signed int);
  170.         if(v2<0)
  171.         { buffer1[size1++]='-';
  172.           v=-v2;
  173.         }else
  174.         { if(flags&SIGNFLAG)
  175.         buffer1[size1++]='+';
  176.           else if(flags&BLANKFLAG)
  177.         buffer1[size1++]=' ';
  178.           v=v2; }
  179.       }else            /* These are unsigned */
  180.       { if(subtype=='l')
  181.           v=va_arg(args,unsigned long);
  182.         else
  183.           v=va_arg(args,unsigned int);
  184.         if(flags&ALTERNATEFLAG)
  185.         { if(type=='o'&&(preci||v))
  186.         buffer1[size1++]='0';
  187.           if((type=='x'||type=='X')&&v)
  188.           { buffer1[size1++]='0';
  189.         buffer1[size1++]=type; }
  190.         }
  191.       }
  192.  
  193.       buffer2=&buffer[sizeof(buffer)]; /* Calculate body string */
  194.       base=type=='x'||type=='X'?16:(type=='o'?8:10);
  195.       tabel=type!='X'?lowertabel:uppertabel;
  196.       do
  197.       { *--buffer2=tabel[v%base];
  198.         v=v/base;
  199.         size2++;
  200.       }while(v);
  201.       if(preci==ULONG_MAX) /* default */
  202.         preci=0;
  203.       else
  204.         flags&=~ZEROPADFLAG;
  205.       break;
  206.     }
  207.     case 'c':
  208.       if(subtype=='l')
  209.         *buffer2=va_arg(args,long);
  210.       else
  211.         *buffer2=va_arg(args,int);
  212.       size2=1;
  213.       preci=0;
  214.       break;
  215.     case 's':
  216.       buffer2=va_arg(args,char *);
  217.       size2=strlen(buffer2);
  218.       size2=size2<=preci?size2:preci;
  219.       preci=0;
  220.       break;
  221. #ifdef FULL_SPECIFIERS
  222.     case 'f':
  223.     case 'e':
  224.     case 'E':
  225.     case 'g':
  226.     case 'G':
  227.     { double v;
  228.       char killzeros=0,sign=0; /* some flags */
  229.       int ex1,ex2; /* Some temporary variables */
  230.       size_t size,dnum,dreq;
  231.       char *udstr=NULL;
  232.  
  233.       v=va_arg(args,double);
  234.  
  235.       if(isinf(v))
  236.       { if(v>0)
  237.           udstr="+inf";
  238.         else
  239.           udstr="-inf";
  240.       }else if(isnan(v))
  241.         udstr="NaN";
  242.  
  243.       if(udstr!=NULL)
  244.       { size2=strlen(udstr);
  245.         preci=0;
  246.         buffer2=udstr;
  247.         break; }
  248.  
  249.       if(preci==ULONG_MAX) /* old default */
  250.         preci=6; /* new default */
  251.  
  252.       if(v<0.0)
  253.       { sign='-';
  254.         v=-v;
  255.       }else
  256.       { if(flags&SIGNFLAG)
  257.           sign='+';
  258.         else if(flags&BLANKFLAG)
  259.           sign=' ';
  260.       }
  261.  
  262.       ex1=0;
  263.       if(v!=0.0)
  264.       { ex1=log10(v);
  265.         if(v<1.0)
  266.           v=v*pow(10,- --ex1); /* Caution: (int)log10(.5)!=-1 */
  267.         else
  268.           v=v/pow(10,ex1);
  269.         if(v<1.0) /* adjust if we are too low (log10(.1)=-.999999999) */
  270.         { v*=10.0; /* luckily this cannot happen with FLT_MAX and FLT_MIN */
  271.           ex1--; } /* The case too high (log(10.)=.999999999) is done later */
  272.       }
  273.  
  274.       ex2=preci;
  275.       if(type=='f')
  276.         ex2+=ex1;
  277.       if(tolower(type)=='g')
  278.         ex2--;
  279.       v+=.5/pow(10,ex2<MINFLOATSIZE?ex2:MINFLOATSIZE); /* Round up */
  280.  
  281.       if(v>=10.0) /* Adjusts log10(10.)=.999999999 too */
  282.       { v/=10.0;
  283.         ex1++; }
  284.  
  285.       if(tolower(type)=='g') /* This changes to one of the other types */
  286.       { if(ex1<(signed long)preci&&ex1>=-4)
  287.         { type='f';
  288.           preci-=ex1;
  289.         }else
  290.           type=type=='g'?'e':'E';
  291.         preci--;
  292.         if(!(flags&ALTERNATEFLAG))
  293.           killzeros=1; /* set flag to kill trailing zeros */
  294.       }
  295.  
  296.       dreq=preci+1; /* Calculate number of decimal places required */
  297.       if(type=='f')
  298.         dreq+=ex1;     /* even more before the decimal point */
  299.  
  300.       dnum=0;
  301.       while(dnum<dreq&&dnum<MINFLOATSIZE) /* Calculate all decimal places needed */
  302.       { buffer[dnum++]=(char)v+'0';
  303.         v=(v-(double)(char)v)*10.0; }
  304.  
  305.       if(killzeros) /* Kill trailing zeros if possible */
  306.         while(preci&&(dreq-->dnum||buffer[dreq]=='0'))
  307.           preci--;
  308.  
  309.       if(type=='f')/* Calculate actual size of string (without sign) */
  310.       { size=preci+1; /* numbers after decimal point + 1 before */
  311.         if(ex1>0)
  312.           size+=ex1; /* numbers >= 10 */
  313.         if(preci||flags&ALTERNATEFLAG)
  314.           size++; /* 1 for decimal point */
  315.       }else
  316.       { size=preci+5; /* 1 for the number before the decimal point, and 4 for the exponent */
  317.         if(preci||flags&ALTERNATEFLAG)
  318.           size++;
  319.         if(ex1>99||ex1<-99)
  320.           size++; /* exponent needs an extra decimal place */
  321.       }
  322.  
  323.       pad=size+(sign!=0);
  324.       pad=pad>=width?0:width-pad;
  325.  
  326.       if(sign&&flags&ZEROPADFLAG)
  327.         OUT(sign);
  328.  
  329.       if(!(flags&LALIGNFLAG))
  330.         for(i=0;i<pad;i++)
  331.           OUT(flags&ZEROPADFLAG?'0':' ');
  332.  
  333.       if(sign&&!(flags&ZEROPADFLAG))
  334.         OUT(sign);
  335.  
  336.       dreq=0;
  337.       if(type=='f')
  338.       { if(ex1<0)
  339.           OUT('0');
  340.         else
  341.           while(ex1>=0)
  342.           { OUT(dreq<dnum?buffer[dreq++]:'0');
  343.         ex1--; }
  344.         if(preci||flags&ALTERNATEFLAG)
  345.         { OUT(__decimalpoint[0]);
  346.           while(preci--)
  347.         if(++ex1<0)
  348.           OUT('0');
  349.         else
  350.           OUT(dreq<dnum?buffer[dreq++]:'0');
  351.         }
  352.       }else
  353.       { OUT(buffer[dreq++]);
  354.         if(preci||flags&ALTERNATEFLAG)
  355.         { OUT(__decimalpoint[0]);
  356.           while(preci--)
  357.         OUT(dreq<dnum?buffer[dreq++]:'0');
  358.         }
  359.         OUT(type);
  360.         if(ex1<0)
  361.         { OUT('-');
  362.           ex1=-ex1; }
  363.         else
  364.           OUT('+');
  365.         if(ex1>99)
  366.           OUT(ex1/100+'0');
  367.         OUT(ex1/10%10+'0');
  368.         OUT(ex1%10+'0');
  369.       }
  370.  
  371.       if(flags&LALIGNFLAG)
  372.         for(i=0;i<pad;i++)
  373.           OUT(' ');
  374.  
  375.       width=preci=0; /* Everything already done */
  376.       break;
  377.     }
  378. #endif
  379.     case '%':
  380.       buffer2="%";
  381.       size2=1;
  382.       preci=0;
  383.       break;
  384.     case 'n':
  385.       *va_arg(args,int *)=outcount;
  386.       width=preci=0;
  387.       break;
  388.     default:
  389.       if(!type)
  390.         ptr--; /* We've gone too far - step one back */
  391.       buffer2=(char *)format;
  392.       size2=ptr-format;
  393.       width=preci=0;
  394.       break;
  395.       }
  396.       pad=size1+(size2>=preci?size2:preci); /* Calculate the number of characters */
  397.       pad=pad>=width?0:width-pad; /* and the number of resulting pad bytes */
  398.  
  399.       if(flags&ZEROPADFLAG) /* print sign and that like */
  400.     for(i=0;i<size1;i++)
  401.       OUT(buffer1[i]);
  402.  
  403.       if(!(flags&LALIGNFLAG)) /* Pad left */
  404.     for(i=0;i<pad;i++)
  405.       OUT(flags&ZEROPADFLAG?'0':' ');
  406.  
  407.       if(!(flags&ZEROPADFLAG)) /* print sign if not zero padded */
  408.     for(i=0;i<size1;i++)
  409.       OUT(buffer1[i]);
  410.  
  411.       for(i=size2;i<preci;i++) /* extend to precision */
  412.     OUT('0');
  413.  
  414.       for(i=0;i<size2;i++) /* print body */
  415.     OUT(buffer2[i]);
  416.  
  417.       if(flags&LALIGNFLAG) /* Pad right */
  418.     for(i=0;i<pad;i++)
  419.       OUT(' ');
  420.  
  421.       format=ptr;
  422.     }
  423.     else
  424.       OUT(*format++);
  425.   }
  426.   return outcount;
  427. }
  428.