home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Product / Product.zip / sc621_3.zip / src / format.c < prev    next >
C/C++ Source or Header  |  1993-11-09  |  15KB  |  609 lines

  1. /*****************************************************************************
  2.  *
  3.  * Mark Nagel <nagel@ics.uci.edu>
  4.  * 20 July 1989
  5.  *
  6.  * $Revision: 6.21 $
  7.  *
  8.  * bool
  9.  * format(fmt, num, buf, buflen)
  10.  *  char *fmt;
  11.  *  double num;
  12.  *  char buf[];
  13.  *  int buflen;
  14.  *
  15.  * The format function will produce a string representation of a number
  16.  * given a _format_ (described below) and a double value.  The result is
  17.  * written into the passed buffer -- if the resulting string is too
  18.  * long to fit into the passed buffer, the function returns false.
  19.  * Otherwise the function returns true.
  20.  *
  21.  * The fmt parameter contains the format to use to convert the number.
  22.  *
  23.  *  #    Digit placeholder.  If the number has fewer digits on either
  24.  *      side of the decimal point than  there are '#' characters in
  25.  *      the format, the extra '#' characters are ignored.  The number
  26.  *      is rounded to the number of digit placeholders as there are
  27.  *      to the right of the decimal point.  If there are more digits
  28.  *      in the number than there are digit placeholders on the left
  29.  *      side of the decimal point, then those digits are displayed.
  30.  *
  31.  *  0    Digit placeholder.  Same as for '#' except that the number
  32.  *      is padded with zeroes on either side of the decimal point.
  33.  *      The number of zeroes used in padding is determined by the
  34.  *      number of digit placeholders after the '0' for digits on
  35.  *      the left side of the decimal point and by the number of
  36.  *      digit placeholders before the '0' for digits on the right
  37.  *      side of the decimal point.
  38.  *
  39.  *  .    Decimal point.  Determines how many digits are placed on
  40.  *      the right and left sides of the decimal point in the number.
  41.  *      Note that numbers smaller than 1 will begin with a decimal
  42.  *      point if the left side of the decimal point contains only
  43.  *      a '#' digit placeholder.  Use a '0' placeholder to get a
  44.  *      leading zero in decimal formats.
  45.  *
  46.  *  %    Percentage.  For each '%' character in the format, the actual
  47.  *      number gets multiplied by 100 (only for purposes of formatting
  48.  *      -- the original number is left unmodified) and the '%' character
  49.  *      is placed in the same position as it is in the format.
  50.  *
  51.  *  ,    Thousands separator.  The presence of a ',' in the format
  52.  *      (multiple commas are treated as one) will cause the number
  53.  *      to be formatted with a ',' separating each set of three digits
  54.  *      in the integer part of the number with numbering beginning
  55.  *      from the right end of the integer.
  56.  *
  57.  *  \    Quote.  This character causes the next character to be
  58.  *      inserted into the formatted string directly with no
  59.  *      special interpretation.
  60.  *
  61.  *  E- E+ e- e+
  62.  *    Scientific format.  Causes the number to formatted in scientific
  63.  *    notation.  The case of the 'E' or 'e' given is preserved.  If
  64.  *      the format uses a '+', then the sign is always given for the
  65.  *    exponent value.  If the format uses a '-', then the sign is
  66.  *    only given when the exponent value is negative.  Note that if
  67.  *    there is no digit placeholder following the '+' or '-', then
  68.  *    that part of the formatted number is left out.  In general,
  69.  *    there should be one or more digit placeholders after the '+'
  70.  *    or '-'.
  71.  *
  72.  *  ;    Format selector.  Use this character to separate the format
  73.  *    into two distinct formats.  The format to the left of the
  74.  *    ';' character will be used if the number given is zero or
  75.  *    positive.  The format to the right of the ';' character is
  76.  *      used if the number given is negative.
  77.  *    
  78.  *  Any
  79.  *    Self insert.  Any other character will be inserted directly
  80.  *    into the formatted number with no change made to the actual
  81.  *      number.
  82.  *
  83.  *****************************************************************************/
  84.  
  85. /*****************************************************************************/
  86.  
  87. #include <stdio.h>
  88. #include <sys/types.h>
  89. #include <time.h>
  90. #include "sc.h"
  91.  
  92. #define bool    int
  93. #define true    1
  94. #define false    0
  95. #define EOS    '\0'
  96. #define MAXBUF    256
  97.  
  98. #if defined(__OS2__)
  99. # include <string.h>
  100. #else
  101. extern char *strcpy();
  102. extern char *strcat();
  103. #endif
  104.  
  105. static char
  106.   *fmt_int(),
  107.   *fmt_frac(),
  108.   *fmt_exp();
  109.  
  110. static void
  111.   reverse();
  112.  
  113. /*****************************************************************************/
  114.  
  115. bool
  116. format(fmt, val, buf, buflen)
  117.   char *fmt;
  118.   double val;
  119.   char *buf;
  120.   int buflen;
  121. {
  122.   register char *cp;
  123.   char *tmp, *tp;
  124.   bool comma = false, negative = false;
  125.   char *integer = NULL, *decimal = NULL;
  126.   char *exponent = NULL;
  127.   int exp_val, width;
  128.   char prtfmt[32];
  129.   static char        *mantissa = NULL;
  130.   static char        *tmpfmt1 = NULL, *tmpfmt2 = NULL, *exptmp = NULL;
  131.   static unsigned    mantlen = 0, fmtlen = 0;
  132.   char *fraction = NULL;
  133.   int zero_pad = 0;
  134.  
  135.   if (fmt == NULL)
  136.     return(true);
  137.  
  138.   if (strlen(fmt) + 1 > fmtlen)
  139.   {    fmtlen = strlen(fmt) + 40;
  140.     tmpfmt1 = scxrealloc(tmpfmt1, fmtlen);
  141.     tmpfmt2 = scxrealloc(tmpfmt2, fmtlen);
  142.     exptmp = scxrealloc(exptmp, fmtlen);
  143.   }
  144.   fmt = strcpy(tmpfmt1, fmt);
  145.   if (buflen + 1 > mantlen)
  146.   {    mantlen = buflen + 40;
  147.     mantissa = scxrealloc(mantissa, mantlen);
  148.   }
  149.  
  150.   /*
  151.    * select positive or negative format if necessary
  152.    */
  153.   for (cp = fmt; *cp != ';' && *cp != EOS; cp++)
  154.   {
  155.     if (*cp == '\\')
  156.       cp++;
  157.   }
  158.   if (*cp == ';')
  159.   {
  160.     if (val < 0.0)
  161.     {
  162.       val = -val;     /* format should provide sign if desired */
  163.       fmt = cp + 1;
  164.     }
  165.     else
  166.     {
  167.       *cp = EOS;
  168.     }
  169.   }
  170.   
  171.   /*
  172.    * extract other information from format and produce a
  173.    * format string stored in tmpfmt2 also scxmalloc()'d above
  174.    */
  175.   tmp = tmpfmt2;
  176.   for (cp = fmt, tp = tmp; *cp != EOS; cp++)
  177.   {
  178.     switch (*cp)
  179.     {
  180.       case '\\':
  181.         *tp++ = *cp++;
  182.         *tp++ = *cp;
  183.     break;
  184.  
  185.       case ',':
  186.         comma = true;
  187.     break;
  188.  
  189.       case '.':
  190.         if (decimal == NULL)
  191.       decimal = tp;
  192.     *tp++ = *cp;
  193.     break;
  194.     
  195.       case '%':
  196.         val *= 100.0;
  197.     *tp++ = *cp;
  198.     break;
  199.     
  200.       default:
  201.         *tp++ = *cp;
  202.     break;
  203.     }
  204.   }
  205.   *tp = EOS;
  206.   fmt = tmpfmt2;
  207.  
  208.   if (val < 0.0)
  209.   {    negative = true;
  210.     val = -val;
  211.   }
  212.   /*
  213.    * extract the exponent from the format if present
  214.    */
  215.   for (cp = fmt; *cp != EOS; cp++)
  216.   { if (*cp == '\\')
  217.     {
  218.       cp++;
  219.     }
  220.     else if (*cp == 'e' || *cp == 'E')
  221.     {
  222.       if (cp[1] == '+' || cp[1] == '-')
  223.       {
  224.     exponent = strcpy(exptmp, cp);
  225.     *cp = EOS;
  226.     exp_val = 0;
  227.     if (val!=0.0) {
  228.       while (val < 1.0)
  229.       {
  230.         val *= 10.0;
  231.         exp_val--;
  232.       }
  233.       while (val >= 10.0)
  234.       {
  235.         val /= 10.0;
  236.         exp_val++;
  237.       }
  238.     }
  239.     break;
  240.       }
  241.     }
  242.   }
  243.  
  244.   /*
  245.    * determine maximum decimal places and use sprintf
  246.    * to build initial character form of formatted value.
  247.    */
  248.   width = 0;
  249.   if (decimal)
  250.   {
  251.     *decimal++ = EOS;
  252.     for (cp = decimal; *cp != EOS; cp++)
  253.     {
  254.       switch (*cp)
  255.       {
  256.         case '\\':
  257.           cp++;
  258.       break;
  259.  
  260.         case '#':
  261.           width++;
  262.       break;
  263.  
  264.     case '0':
  265.       zero_pad = ++width;
  266.       break;
  267.       }
  268.     }
  269.     zero_pad = strlen(decimal) - zero_pad;
  270.   }
  271.   (void) sprintf(prtfmt, "%%.%dlf", width);
  272.   (void) sprintf(mantissa, prtfmt, val);
  273.   for (cp = integer = mantissa; *cp != '.' && *cp != EOS; cp++)
  274.   {
  275.     if (*integer == '0')
  276.       integer++;
  277.   }
  278.   if (*cp == '.')
  279.   {
  280.     fraction = cp + 1;
  281.     *cp = EOS;
  282.     cp = fraction + strlen(fraction) - 1;
  283.     for (; zero_pad > 0; zero_pad--, cp--)
  284.     {
  285.       if (*cp == '0')
  286.         *cp = EOS;
  287.     }
  288.   }
  289.  
  290.   /*
  291.    * format the puppy
  292.    */
  293.   {
  294.     static    char *citmp = NULL, *cftmp = NULL;
  295.     static    unsigned cilen = 0, cflen = 0;
  296.     char *ci, *cf, *ce;
  297.     int len_ci, len_cf, len_ce;
  298.     bool ret = false;
  299.     
  300.     ci = fmt_int(integer, fmt, comma, negative);
  301.     len_ci = strlen(ci);
  302.     if (len_ci >= cilen)
  303.     {    cilen = len_ci + 40;
  304.     citmp = scxrealloc(citmp, cilen);
  305.     }
  306.     ci = strcpy(citmp, ci);
  307.  
  308.     cf = (fraction) ? fmt_frac(fraction, decimal) : "";
  309.     len_cf = strlen(cf);
  310.     if (len_cf >= cflen)
  311.     {    cflen = len_cf + 40;
  312.     cftmp = scxrealloc(cftmp, cilen);
  313.     }
  314.     cf = strcpy(cftmp, cf);
  315.  
  316.     ce = (exponent) ? fmt_exp(exp_val, exponent) : "";
  317.     len_ce = strlen(ce);
  318. /*
  319.  * Skip copy assuming sprintf doesn't call our format functions
  320.  *   ce = strcpy(scxmalloc((unsigned)((len_ce = strlen(ce)) + 1)), ce);
  321.  */
  322.     if (len_ci + len_cf + len_ce < buflen)
  323.     {
  324.       (void) sprintf(buf, "%s%s%s", ci, cf, ce);
  325.       ret = true;
  326.     }
  327.  
  328.     return (ret);
  329.   }
  330. }
  331.  
  332. /*****************************************************************************/
  333.  
  334. static char *
  335. fmt_int(val, fmt, comma, negative)
  336.   char *val;        /* integer part of the value to be formatted */
  337.   char *fmt;        /* integer part of the format */
  338.   bool comma;        /* true if we should comma-ify the value */
  339.   bool negative;    /* true if the value is actually negative */
  340. {
  341.   int digit, f, v;
  342.   int thousands = 0;
  343.   char *cp;
  344.   static char buf[MAXBUF];
  345.   char *bufptr = buf;
  346.  
  347.   /*
  348.    * locate the leftmost digit placeholder
  349.    */
  350.   for (cp = fmt; *cp != EOS; cp++)
  351.   {
  352.     if (*cp == '\\')
  353.       cp++;
  354.     else if (*cp == '#' || *cp == '0')
  355.       break;
  356.   }
  357.   digit = (*cp == EOS) ? -1 : cp - fmt;
  358.  
  359.   /*
  360.    * format the value
  361.    */
  362.   f = strlen(fmt) - 1;
  363.   v = (digit >= 0) ? strlen(val) - 1 : -1;
  364.   while (f >= 0 || v >= 0)
  365.   {
  366.     if (f > 0 && fmt[f-1] == '\\')
  367.     {
  368.       *bufptr++ = fmt[f--];
  369.     }
  370.     else if (f >= 0 && (fmt[f] == '#' || fmt[f] == '0'))
  371.     {
  372.       if (v >= 0 || fmt[f] == '0')
  373.       {
  374.         *bufptr++ = v < 0 ? '0' : val[v];
  375.     if (comma && (thousands = (thousands + 1) % 3) == 0 && v > 0)
  376.     {
  377.       *bufptr++ = ',';
  378.     }
  379.     v--;
  380.       }
  381.     }
  382.     else if (f >= 0)
  383.     {
  384.       *bufptr++ = fmt[f];
  385.     }
  386.     if (v >= 0 && f == digit)
  387.     {
  388.       continue;
  389.     }
  390.     f--;
  391.   }
  392.     
  393.   if (negative && digit >= 0)
  394.     *bufptr++ = '-';
  395.   *bufptr = EOS;
  396.   reverse(buf);
  397.  
  398.   return (buf);
  399. }
  400.  
  401. /*****************************************************************************/
  402.  
  403. static char *
  404. fmt_frac(val, fmt)
  405.   char *val;        /* fractional part of the value to be formatted */
  406.   char *fmt;        /* fractional portion of format */
  407. {
  408.   static char buf[MAXBUF];
  409.   register char *bufptr = buf;
  410.   register char *fmtptr = fmt, *valptr = val;
  411.  
  412.   *bufptr++ = '.';
  413.   while (*fmtptr != EOS)
  414.   {
  415.     if (*fmtptr == '\\')
  416.     {
  417.       *bufptr++ = *++fmtptr;
  418.     }
  419.     else if (*fmtptr == '#' || *fmtptr == '0')
  420.     {
  421.       if (*valptr != EOS || *fmtptr == '0')
  422.       {
  423.         *bufptr++ = (*valptr != EOS) ? *valptr++ : *fmtptr;
  424.       }
  425.     }
  426.     else
  427.     {
  428.       *bufptr++ = *fmtptr;
  429.     }
  430.     fmtptr++;
  431.   }
  432.   *bufptr = EOS;
  433.  
  434.   return (buf);
  435. }
  436.  
  437. /*****************************************************************************/
  438.  
  439. static char *
  440. fmt_exp(val, fmt)
  441.   int val;        /* value of the exponent */
  442.   char *fmt;        /* exponent part of the format */
  443. {
  444.   static char buf[MAXBUF];
  445.   register char *bufptr = buf;
  446.   char valbuf[64];
  447.   bool negative = false;
  448.   
  449.   *bufptr++ = *fmt++;
  450.   if (*fmt == '+')
  451.     *bufptr++ = (val < 0) ? '-' : '+';
  452.   else if (val < 0)
  453.     *bufptr++ = '-';
  454.   fmt++;
  455.   *bufptr = EOS;
  456.  
  457.   if (val < 0)
  458.   {
  459.     val = -val;
  460.     negative = false;
  461.   }
  462.   (void) sprintf(valbuf, "%d", val);
  463.   
  464.   (void) strcat(buf, fmt_int(valbuf, fmt, false, negative));
  465.   return (buf);
  466. }
  467.  
  468. /*****************************************************************************/
  469.  
  470. static void
  471. reverse(buf)
  472.   register char *buf;
  473. {
  474.   register char *cp = buf + strlen(buf) - 1;
  475.   register char tmp;
  476.  
  477.   while (buf < cp)
  478.   {
  479.     tmp = *cp;
  480.     *cp-- = *buf;
  481.     *buf++ = tmp;
  482.   }
  483. }
  484.  
  485. /*****************************************************************************/
  486. /*  
  487.  * Tom Anderson    <toma@hpsad.hp.com>
  488.  * 10/14/90
  489.  *
  490.  * This routine takes a value and formats it using fixed, scientific,
  491.  * or engineering notation.  The format command 'f' determines which
  492.  * format is used.  The formats are:         example
  493.  *    0:   Fixed point (default)             0.00010
  494.  *    1:   Scientific                        1.00E-04
  495.  *    2:   Engineering                       100.00e-06
  496.  *
  497.  * The format command 'f' now uses three values.  The first two are the
  498.  * width and precision, and the last one is the format value 0, 1, or 2 as
  499.  * described above.  The format value is passed in the variable fmt.
  500.  *
  501.  * This formatted value is written into the passed buffer.  if the
  502.  * resulting string is too long to fit into the passed buffer, the
  503.  * function returns false.  Otherwise the function returns true.
  504.  *
  505.  * When a number is formatted as engineering and is outside of the range,
  506.  * the format reverts to scientific.
  507.  *
  508.  * To preserve compatability with old spreadsheet files, the third value
  509.  * may be missing, and the default will be fixed point (format 0).
  510.  *
  511.  * When an old style sheet is saved, the third value will be stored.
  512.  *
  513.  */
  514.  
  515. /* defined in sc.h */
  516. #ifndef REFMTFIX
  517. #define REFMTFIX    0
  518. #define REFMTFLT    1
  519. #define REFMTENG    2
  520. #define REFMTDATE    3
  521. #endif
  522.  
  523. bool
  524. engformat(fmt, width, lprecision, val, buf, buflen)
  525. int fmt; 
  526. int width; 
  527. int lprecision;
  528. double val;
  529. char *buf;
  530. int buflen;
  531. {
  532.  
  533.   static char *engmult[] = {
  534.       "-18", "-15", "-12", "-09", "-06", "-03",
  535.       "+00",
  536.       "+03", "+06", "+09", "+12", "+15", "+18"
  537.   };
  538.   int engind = 0;
  539.   double engmant, pow(), engabs, engexp;
  540.   if (buflen < width) return (false);
  541.   if (fmt == REFMTFIX)
  542.   (void) sprintf(buf,"%*.*f", width, lprecision, val);
  543.   if (fmt == REFMTFLT)
  544.   (void) sprintf(buf,"%*.*E", width, lprecision, val);
  545.   if (fmt == REFMTENG)
  546.   {
  547.     if (val == 0e0)    /* Hack to get zeroes to line up in engr fmt */
  548.     {
  549.       (void) sprintf((buf-1),"%*.*f ", width, lprecision, val);
  550.     }
  551.     else
  552.     {
  553.       engabs=(val);
  554.       if (engabs < 0e0) engabs= -engabs;
  555.       if ((engabs >= 1e-18) && (engabs < 1e-15 )) engind=0;
  556.       if ((engabs >= 1e-15) && (engabs < 1e-12 )) engind=1;
  557.       if ((engabs >= 1e-12) && (engabs < 1e-9 )) engind=2;
  558.       if ((engabs >= 1e-9) && (engabs < 1e-6 )) engind=3;
  559.       if ((engabs >= 1e-6) && (engabs < 1e-3 )) engind=4;
  560.       if ((engabs >= 1e-3) && (engabs < 1 )) engind=5;
  561.       if ((engabs >= 1) && (engabs < 1e3 )) engind=6;
  562.       if ((engabs >= 1e3) && (engabs < 1e6 )) engind=7;
  563.       if ((engabs >= 1e6) && (engabs < 1e9 )) engind=8;
  564.       if ((engabs >= 1e9) && (engabs < 1e12 )) engind=9;
  565.       if ((engabs >= 1e12) && (engabs < 1e15 )) engind=10;
  566.       if ((engabs >= 1e15) && (engabs < 1e18 )) engind=11;
  567.       if ((engabs >= 1e18) && (engabs < 1e21 )) engind=12;
  568.       if ((engabs <1e-18) || (engabs >=1e21))
  569.       {
  570.       /* Revert to floating point */
  571.         (void) sprintf(buf,"%*.*E", width, lprecision, val);
  572.       }
  573.       else
  574.       {
  575.         engexp= (double) (engind-6)*3;
  576.         engmant= val/pow(10.0e0,engexp);
  577.         (void) sprintf(buf,"%*.*fe%s", width-4,
  578.                       lprecision, engmant, engmult[engind]);
  579.       }
  580.     }
  581.   }
  582.   if (fmt == REFMTDATE) {
  583.     int i;
  584.     char *time_of_day;
  585.     long int secs;
  586.  
  587.     if (buflen < 9) {
  588.       for (i = 0; i < width; i++) buf[i] = '*';
  589.       buf[i] = '\0';
  590.     }
  591.     else {
  592.       secs = (time_t)val;
  593.       time_of_day = ctime(&secs);
  594.       buf[0] = time_of_day[8];
  595.       buf[1] = time_of_day[9];
  596.       buf[2] = ' ';
  597.       buf[3] = time_of_day[4];
  598.       buf[4] = time_of_day[5];
  599.       buf[5] = time_of_day[6];
  600.       buf[6] = ' ';
  601.       buf[7] = time_of_day[22];
  602.       buf[8] = time_of_day[23];
  603.       for (i = 9; i < width; i++) buf[i] = ' ';
  604.       buf[i] = '\0';
  605.     }
  606.   }
  607.   return (true);
  608. }
  609.