home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_08_09 / 8n09039a < prev    next >
Text File  |  1990-07-21  |  16KB  |  546 lines

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