home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 329_01 / dprintf.c < prev    next >
Text File  |  1979-12-31  |  16KB  |  588 lines

  1. /*  >  DPRINTF.C
  2.  *
  3.  *  dprintf -- Source Code
  4.  *  (C)  April 4  1990  Asaf Arkin
  5.  *  All rights reserved
  6.  */
  7.  
  8.  
  9.  
  10. /*  Include files:
  11.  */
  12.  
  13. #include  <ctype.h>
  14. #include  <setjmp.h>
  15. #include  <stdarg.h>
  16. #include  <stdio.h>
  17. #include  <stdlib.h>
  18. #include  <string.h>
  19.  
  20.  
  21.  
  22. /*  Macro constants:- TRUE/FALSE, Flags mask bits, and argument size types.
  23.  *  Macros:- Maximum and Minimum expand to yield the maximum or minimum of
  24.  *  two expressions; ToValue gives the decimal value of an ASCII digit and
  25.  *  ToDigit returns a digit from a value in any radix.
  26.  *  N.B.: It goes without saying that the macro parameters must have no side
  27.  *  effects, as they will be carried out more than once.
  28.  */
  29.  
  30. #define  TRUE  1
  31. #define  FALSE 0
  32.  
  33. #define  MaskJustify   0x01  /*  -  Left justify value within field    */
  34. #define  MaskPlusSign  0x02  /*  +  Precede positive value with plus   */
  35. #define  MaskSpace     0x04  /*  sp Precede positive value with space  */
  36. #define  MaskZeros     0x08  /*  0  Justify value with zeros           */
  37. #define  MaskVariant   0x10  /*  #  Output value in variant format     */
  38.  
  39. #define  TypeNormal  1  /*  int/double           */
  40. #define  TypeShort   2  /*  short (meaningless)  */
  41. #define  TypeLong    3  /*  long int             */
  42. #define  TypeDouble  4  /*  long double          */
  43.  
  44. #define  Maximum(a,b)  ((a)>(b)?(a):(b))
  45. #define  Minimum(a,b)  ((a)<(b)?(a):(b))
  46. #define  ToValue(a)    ((a)-'0')
  47. #define  ToDigit(a)    ((a)<10?(a)+'0':(a)-10+'A')
  48.  
  49.  
  50.  
  51. /*  OutFunc (of type dprintf_fp) points to a putchar-like function, which
  52.  *  performs all output. Called with a character int as parameter, the
  53.  *  function returns EOF only if an output error occured.
  54.  */
  55.  
  56. typedef  int (*dprintf_fp)(int);
  57. static dprintf_fp  OutFunc;
  58.  
  59.  
  60. /*  Function declarations:
  61.  */
  62.  
  63. int  dprintf(dprintf_fp, const char *, ...);
  64. int  vdprintf(dprintf_fp, const char *, va_list);
  65. static void  PrintDecimal(long, int, int, char, int *);
  66. static void  PrintRadix(unsigned long, int, int, char, char, int *);
  67. static void  PrintFloat(long double, int, int, char, char, int *);
  68. static void  Print(char *, char *, int, int, char, int *);
  69. static int  ToInteger(char **, unsigned long, int, int);
  70. static void  dputc(int);
  71.  
  72.  
  73. /*  dputc employs this longjmp buffer in the event of an output error.
  74.  */
  75.  
  76. jmp_buf  dputc_Buf;
  77.  
  78.  
  79.  
  80. /*  int  dprintf(dprintf_fp, const char *, ...)
  81.  *
  82.  *  dprintf accepts pointers to a putchar-like function and a format string.
  83.  *  It then passes them to vdprintf, along with a pointer to the variable
  84.  *  arguments list.
  85.  */
  86.  
  87. int  dprintf(dprintf_fp Func, const char *Format, ...)
  88. {
  89.   int  Return;
  90.   va_list  Args;
  91.  
  92.   va_start(Args,Format);
  93.   Return=vdprintf(Func,Format,Args);
  94.   va_end(Args);
  95.   return  Return;
  96. }
  97.  
  98.  
  99.  
  100. /*  int  vdprintf(dprintf_fp, const char *, va_list)
  101.  *
  102.  *  vdprintf is an implementation of vprintf, as defined in the ANSI
  103.  *  standard, with an additional pointer-to-function as its first parameter.
  104.  *  On exit, vdprintf returns the number of characters successfully printed,
  105.  *  EOF if an error occured.
  106.  */
  107.  
  108. int  vdprintf(dprintf_fp Func, const char *Format, va_list Args)
  109. {
  110.   char  Flags,  Size,  *Ptr;
  111.   int  Width,  Precis,  OutCnt = 0;
  112.   long  Int;
  113.   unsigned long  UnsgInt;
  114.   long double  Float;
  115.   char  *FlagsList = "-+ 0#",  *TypesList = "hlL";
  116.  
  117.   /*  The pointer-to-function assigns to static variable OutFunc, rather than
  118.    *  being passed as parameter through three layers of functions. The
  119.    *  longjmp buffer is then initialized, so dputc can return in the event of
  120.    *  an output error.
  121.    */
  122.   OutFunc=Func;
  123.   if (setjmp(dputc_Buf))
  124.     return  EOF;
  125.   /*  The format string is scanned a character at a time: %'s are processed,
  126.    *  all other characters are merely echoed to the output.
  127.    */
  128.   for (; *Format; ++Format)
  129.   {
  130.     if (*Format!='%')
  131.     {
  132.       dputc(*Format);
  133.       ++OutCnt;
  134.       continue;
  135.     }
  136.     /*  An output format can start with a combination of five flags: - + spc
  137.      *  0 #. Flags is set accordingly.
  138.      */
  139.     if (!*++Format)
  140.       return  EOF;
  141.     Flags=0;
  142.     while ((Ptr=strchr(FlagsList,*Format))!=NULL)
  143.     {
  144.       Flags|=1<<(Ptr-FlagsList);
  145.       ++Format;
  146.     }
  147.     /*  Read width (zero assumed, if absent) and precision (-1 assumed, if
  148.      *  absent): width must not start with a zero; precision precedes with a
  149.      *  period -- if no precision follows the period, zero is assumed; an int
  150.      *  argument is consumed for the width or precision, if an * replaces the
  151.      *  value of either.
  152.      */
  153.     Width=0;
  154.     if (*Format=='*')
  155.     {
  156.       Width=va_arg(Args,int);
  157.       if (Width<0)
  158.       {
  159.     Width=-Width;
  160.     Flags|=MaskJustify;
  161.       }
  162.       ++Format;
  163.     }
  164.     else
  165.     while (isdigit(*Format))
  166.       Width=Width*10+ToValue(*Format++);
  167.     if (*Format=='.')
  168.     {
  169.       Precis=0;
  170.       if (*++Format=='*')
  171.       {
  172.         Precis=va_arg(Args,int);
  173.         ++Format;
  174.       }
  175.       else
  176.       while (isdigit(*Format))
  177.         Precis=Precis*10+ToValue(*Format++);
  178.     }
  179.     else
  180.       Precis=-1;
  181.     /*  An argument is either int (default), short int ('h' -- meaningless),
  182.      *  long int ('l'), double (default float), or long double ('L').
  183.      */
  184.      if ((Ptr=strchr(TypesList,*Format))!=NULL)
  185.      {
  186.        Size=Ptr-TypesList+TypeShort;
  187.        ++Format;
  188.      }
  189.      else
  190.        Size=TypeNormal;
  191.     /*  Consume one output format letter.
  192.      *  Auxiliary functions process most formats, keeping vdprintf short, or
  193.      *  else it may fail to compile.
  194.      */
  195.     switch (*Format)
  196.     {
  197.       case 'd':
  198.       case 'i':
  199.         if (Size==TypeLong)
  200.           Int=va_arg(Args,long);
  201.         else
  202.           Int=va_arg(Args,int);
  203.         PrintDecimal(Int,Width,Precis,Flags,&OutCnt);
  204.         break;
  205.       case 'u':
  206.       case 'o':
  207.       case 'x': case 'X':
  208.         if (Size==TypeLong)
  209.           UnsgInt=va_arg(Args,unsigned long);
  210.         else
  211.           UnsgInt=va_arg(Args,unsigned);
  212.         PrintRadix(UnsgInt,Width,Precis,Flags,*Format,&OutCnt);
  213.         break;
  214.       case 'c':
  215.       {
  216.         static char  Char[2]= {0,0};
  217.  
  218.     /*  chars are promoted to int when passed to functions.
  219.      */
  220.     Char[0]=va_arg(Args,int);
  221.         Print(Char,NULL,Width,-1,Flags,&OutCnt);
  222.         break;
  223.       }
  224.       case 's':
  225.     /*  If pointer is null, print string "{NULL}".
  226.      */
  227.     Ptr=va_arg(Args,char *);
  228.     if (Ptr==NULL)
  229.       Ptr="{NULL}";
  230.         Print(Ptr,NULL,Width,Precis,Flags,&OutCnt);
  231.         break;
  232.       case 'f':
  233.       case 'e': case 'E':
  234.       case 'g': case 'G':
  235.         if (Size==TypeDouble)
  236.           Float=va_arg(Args,long double);
  237.         else
  238.           Float=va_arg(Args,double);
  239.         PrintFloat(Float,Width,Precis,Flags,*Format,&OutCnt);
  240.         break;
  241.       case 'p':
  242.         /*  The pointer-to-void argument is cast to long unsigned, assuming
  243.          *  pointer representation to remain intact.
  244.          */
  245.         UnsgInt=(unsigned long) va_arg(Args,void *);
  246.         PrintRadix(UnsgInt,Width,Precis,Flags,*Format,&OutCnt);
  247.         break;
  248.       case 'n':
  249.         *(va_arg(Args,int *))=OutCnt;
  250.         break;
  251.       case '%':
  252.         Print("%",NULL,Width,-1,Flags,&OutCnt);
  253.         break;
  254.       default:
  255.         return EOF;
  256.     }
  257.   }
  258.   return  OutCnt;
  259. }
  260.  
  261.  
  262.  
  263. /*  void  PrintDecimal(long, int, int, char, int *)
  264.  *
  265.  *  Print a decimal value (%d or %i) with a sign prefix.
  266.  */
  267.  
  268. static void  PrintDecimal(long Int, int Width, int Precis, char Flags,
  269.                           int *OutCnt)
  270. {
  271.   char  *Prefix;
  272.   char  *Buffer;
  273.  
  274.   if(Int<0)
  275.   {
  276.     Int=-Int;
  277.     Prefix="-";
  278.   }
  279.   else
  280.   {
  281.     if (Flags&MaskPlusSign)
  282.       Prefix="+";
  283.     else
  284.     if (Flags&MaskSpace)
  285.       Prefix=" ";
  286.     else
  287.       Prefix=NULL;
  288.   }
  289.   ToInteger(&Buffer,Int,10,Precis);
  290.   Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
  291.   free(Buffer);
  292. }
  293.  
  294.  
  295. /*  void  PrintRadix(unsigned long, int, int, char, char, int *)
  296.  *
  297.  *  Print an unsigned int in decimal (%u), octal (%o), hexadecimal (%x or
  298.  *  %X), or pointer (%p) representation. In the variant format octals prefix
  299.  *  with a 0, hexadecimals with a 0x, and pointers with an @. (Hexadecimal
  300.  *  letters are in the same case as is the format letter.)
  301.  */
  302.  
  303. static void  PrintRadix(unsigned long Int, int Width, int Precis, char Flags,
  304.                         char Format, int *OutCnt)
  305. {
  306.   char  *Prefix = NULL;
  307.   char  *Buffer;
  308.   int  Length;
  309.  
  310.   if (Format=='u')
  311.   {
  312.     ToInteger(&Buffer,Int,10,Precis);
  313.     Print(Buffer,NULL,Width,-1,Flags,OutCnt);
  314.   }
  315.   else
  316.   if (Format=='o')
  317.   {
  318.     if (Flags&MaskVariant && Int>0)
  319.     {
  320.        Prefix="0";
  321.        Precis-=1;
  322.     }
  323.     ToInteger(&Buffer,Int,8,Precis);
  324.     Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
  325.   }
  326.   else
  327.   if (Format=='p')
  328.   {
  329.     /*  Various architectures impose different pointer representations, both
  330.      *  in memory and in writing. Default format is:  HHHH:HHHH
  331.      *  (two words, upper case hexadecimal digits), prefixed with an @ in
  332.      *  the variant format.
  333.      */
  334.     if (Flags&MaskVariant)
  335.       Prefix="@";
  336.     ToInteger(&Buffer,Int>>16,   16,4);
  337.     Print(Buffer,Prefix,Width-5,-1,Flags,OutCnt);
  338.     ToInteger(&Buffer,Int&0xffff,16,4);
  339.     Print(Buffer,":",5,-1,0,OutCnt);
  340.   }
  341.   else
  342.   {
  343.     if (Flags&MaskVariant)
  344.     {
  345.       Prefix=(Format=='x')?"0x":"0X";
  346.       Precis-=2;
  347.     }
  348.     Length=ToInteger(&Buffer,Int,16,Precis);
  349.     if (Format=='x')
  350.     {
  351.       for (; Length>0; --Length)
  352.         Buffer[Length-1]=tolower(Buffer[Length-1]);
  353.     }
  354.     Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
  355.   }
  356.   free(Buffer);
  357. }
  358.  
  359.  
  360. /*  void  PrintFloat(long double, int, int, char, char, int *)
  361.  *
  362.  *  Print a floating point number in standard (%f) or engineering (%e) form;
  363.  *  the %g format requires that the shortest of the two be selected. The
  364.  *  number divides into integer, fraction and exponent parts: each is
  365.  *  cast into a long int, stringized with ToInteger, and Print-ed.
  366.  */
  367.  
  368. static void  PrintFloat(long double Float, int Width, int Precis, char Flags,
  369.                         char Format, int *OutCnt)
  370. {
  371.   char  *Prefix;
  372.   char  *BufferI,  *BufferF = NULL,  *BufferE = NULL;
  373.   int  LengthI,  LengthF,  LengthE = 0;
  374.   int  Short = (Format=='g' || Format=='G');
  375.   int  Exponent = 0;
  376.   unsigned long  Int;
  377.  
  378.   /*  Determine prefix according to sign and format flags. If no precision
  379.    *  was given, a precision six is assumed.
  380.    */
  381.   if(Float<0)
  382.   {
  383.     Float=-Float;
  384.     Prefix="-";
  385.   }
  386.     else
  387.   {
  388.     if (Flags&MaskPlusSign)
  389.       Prefix="+";
  390.     else
  391.     if (Flags&MaskSpace)
  392.       Prefix=" ";
  393.     else
  394.       Prefix=NULL;
  395.   }
  396.   if (Precis<0)
  397.     Precis=6;
  398.   if (Format=='e' || Format=='E' || Short)
  399.   {
  400.     long double  TempFloat = Float;
  401.  
  402.     /*  For %e and %g formats, establish the exponent: Float is divided and
  403.      *  multiplied by ten, until it's value rests between zero and one.
  404.      *  Exponent totals all divisions minus all multiplications.
  405.      */
  406.     if (Float!=0)
  407.     {
  408.       while (Float>10)
  409.       {
  410.         Float/=10;
  411.         ++Exponent;
  412.       }
  413.       while (Float<1)
  414.       {
  415.         Float*=10;
  416.         --Exponent;
  417.       }
  418.     }
  419.     LengthE=ToInteger(&BufferE,( Exponent<0?-Exponent:Exponent) ,10,2)+2;
  420.     /*  If the %f format is shorter, %g requires that the exponent be
  421.      *  cancelled and that amount of precision be lost; It states that one
  422.      *  precision digit be lost in any case.
  423.      */
  424.     if (Short)
  425.     {
  426.       if (Precis>0)
  427.         --Precis;
  428.       if (Exponent>=-4 && Exponent<=Precis)
  429.       {
  430.         LengthE=0;
  431.         Precis-=Exponent;
  432.         Float=TempFloat;
  433.       }
  434.     }
  435.   }
  436.   /*  The mantissa divides into integer and fraction parts, stringized by
  437.    *  ToInteger: the last digit always rounds up; a period is printed only
  438.    *  before a fraction, or in the variant format; the %g format allows
  439.    *  trailing zeros in the fraction to be lost.
  440.    *  N.B.: Too long floating numbers may raise an exception on conversion to
  441.    *  long int, or otherwise fail to convert properly.
  442.    */
  443.   Int=(unsigned long) Float;
  444.   Float-=(long double) Int;
  445.   if (Precis<=0 && Float>=.5)
  446.     ++Int;
  447.   LengthI=ToInteger(&BufferI,Int,10,-1);
  448.   if (Precis>0)
  449.   {
  450.     for (LengthF=0; LengthF<Precis; ++LengthF)
  451.       Float*=10;
  452.     Int=(unsigned long) Float;
  453.     Float-=(long double) Int;
  454.     if (Float>=.5)
  455.       ++Int;
  456.     LengthF=ToInteger(&BufferF,Int,10,Precis);
  457.   }
  458.   else
  459.     LengthF=0;
  460.   if (Short && !(Flags&MaskVariant))
  461.     while (LengthF>0 && BufferF[LengthF-1]=='0')
  462.       --LengthF;
  463.  
  464.   if (Flags&MaskVariant || LengthF>0)
  465.     --Width;
  466.   Width-=LengthF+LengthE;
  467.   Print(BufferI,Prefix,Width,-1,Flags,OutCnt);
  468.   if (Flags&MaskVariant || LengthF>0)
  469.   {
  470.     dputc('.');
  471.     ++*OutCnt;
  472.   }
  473.   if (LengthF>0)
  474.     Print(BufferF,NULL,LengthF,LengthF,MaskZeros,OutCnt);
  475.   /*  Print exponent part of number, with an 'e' in the same case as is the
  476.    *  format letter. Exponents have a sign and at least two digits.
  477.    */
  478.   if (LengthE>0)
  479.   {
  480.     if (Format=='g')
  481.       Format='e';
  482.     else
  483.     if (Format=='G')
  484.       Format='E';
  485.     dputc(Format);
  486.     if (Exponent<0)
  487.     {
  488.       Exponent=-Exponent;
  489.       Prefix="-";
  490.     }
  491.     else
  492.       Prefix="+";
  493.     Print(BufferE,Prefix,3,-1,MaskZeros,OutCnt);
  494.   }
  495.   free(BufferI);
  496.   free(BufferF);
  497.   free(BufferE);
  498. }
  499.  
  500.  
  501.  
  502. /*  int  ToInteger(char **, unsigned long, int, int)
  503.  *
  504.  *  Convert an unsigned int to a NULL-terminated string of digits in the
  505.  *  given radix. If the string has less digits than the precision, additional
  506.  *  zeros are inserted at the start of it. ToInteger allocates a memory block
  507.  *  in which it stores the string -- Buffer returns its address.
  508.  */
  509.  
  510. static int  ToInteger(char **Buffer, unsigned long Int, int Radix, int Precis)
  511. {
  512.   int  Cnt,  Length;
  513.   unsigned long  TempInt = Int;
  514.  
  515.   if (Precis<0)
  516.     Precis=1;
  517.   for (Cnt=0; TempInt!=0; TempInt/=Radix, ++Cnt)  ;
  518.   *Buffer=malloc(Maximum(Precis,Cnt)+1);
  519.   if (*Buffer==NULL)
  520.     return  0;
  521.   for (Length=0; Length+Cnt<Precis; )
  522.     (*Buffer)[Length++]='0';
  523.   Cnt= Length=Maximum(Length+Cnt,Precis);
  524.   (*Buffer)[Length]='\0';
  525.   for (; Int>0; Int/=Radix)
  526.     (*Buffer)[--Cnt]=ToDigit(Int%Radix);
  527.   return  Length;
  528. }
  529.  
  530.  
  531. /*  void  Print(char *, char *, int, int, char, int *)
  532.  *
  533.  *  Print prefix followed by value, incrementing OutCnt by the total number
  534.  *  of characters printed: by default, the string is right justified within
  535.  *  the field with spaces; the 0 flag places zeros between prefix and value;
  536.  *  and the - flag left justifies by appending spaces at the end. Note that
  537.  *  if Maximum is not -1, no more than that number of characters are printed.
  538.  */
  539.  
  540. static void  Print(char *String, char *Prefix, int Width, int Maximum,
  541.                    char Flags, int *OutCnt)
  542. {
  543.   int  Length = strlen(String);
  544.  
  545.   if (Prefix)
  546.     Length+=strlen(Prefix);
  547.   if (Maximum>=0 && Length>Maximum)
  548.     Length=Maximum;
  549.   *OutCnt+=Maximum(Length,Width);
  550.  
  551.   if (!(Flags&MaskJustify || Flags&MaskZeros))
  552.     for (; Length<Width; --Width)
  553.       dputc(' ');
  554.   while (Prefix && *Prefix!='\0')
  555.   {
  556.     dputc(*Prefix++);
  557.     --Length;
  558.     --Width;
  559.   }
  560.   if (!(Flags&MaskJustify))
  561.     for (; Length<Width; --Width)
  562.       dputc('0');
  563.   while (*String!='\0' && Length>0)
  564.   {
  565.     dputc(*String++);
  566.     --Length;
  567.     --Width;
  568.   }
  569.   for (; Width>0; --Width)
  570.     dputc(' ');
  571. }
  572.  
  573.  
  574. /*  void  dputc(int)
  575.  *
  576.  *  Perform character output through the putchar-like function provided. If
  577.  *  it returns EOF, a longjmp to vdprintf will cause the latter to return EOF
  578.  *  to its caller.
  579.  */
  580.  
  581. static void  dputc(int Char)
  582. {
  583.   if (OutFunc(Char)==EOF)
  584.     longjmp(dputc_Buf,EOF);
  585. }
  586.  
  587.  
  588.