home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 2 / goldfish_vol2_cd1.bin / files / biz / misc / asc / format.c < prev    next >
C/C++ Source or Header  |  1993-11-02  |  15KB  |  603 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. #ifndef AMIGA
  99. extern char
  100.   *strcpy(),
  101.   *strcat();
  102. #endif
  103.  
  104. static char
  105.   *fmt_int(),
  106.   *fmt_frac(),
  107.   *fmt_exp();
  108.  
  109. static void
  110.   reverse();
  111.  
  112. /*****************************************************************************/
  113.  
  114. bool
  115. format(fmt, val, buf, buflen)
  116.   char *fmt;
  117.   double val;
  118.   char *buf;
  119.   int buflen;
  120. {
  121.   register char *cp;
  122.   char *tmp, *tp;
  123.   bool comma = false, negative = false;
  124.   char *integer = NULL, *decimal = NULL;
  125.   char *exponent = NULL;
  126.   int exp_val, width;
  127.   char prtfmt[32];
  128.   static char        *mantissa = NULL;
  129.   static char        *tmpfmt1 = NULL, *tmpfmt2 = NULL, *exptmp = NULL;
  130.   static unsigned    mantlen = 0, fmtlen = 0;
  131.   char *fraction = NULL;
  132.   int zero_pad = 0;
  133.  
  134.   if (fmt == NULL)
  135.     return(true);
  136.  
  137.   if (strlen(fmt) + 1 > fmtlen)
  138.   {    fmtlen = strlen(fmt) + 40;
  139.     tmpfmt1 = scxrealloc(tmpfmt1, fmtlen);
  140.     tmpfmt2 = scxrealloc(tmpfmt2, fmtlen);
  141.     exptmp = scxrealloc(exptmp, fmtlen);
  142.   }
  143.   fmt = strcpy(tmpfmt1, fmt);
  144.   if (buflen + 1 > mantlen)
  145.   {    mantlen = buflen + 40;
  146.     mantissa = scxrealloc(mantissa, mantlen);
  147.   }
  148.  
  149.   /*
  150.    * select positive or negative format if necessary
  151.    */
  152.   for (cp = fmt; *cp != ';' && *cp != EOS; cp++)
  153.   {
  154.     if (*cp == '\\')
  155.       cp++;
  156.   }
  157.   if (*cp == ';')
  158.   {
  159.     if (val < 0.0)
  160.     {
  161.       val = -val;     /* format should provide sign if desired */
  162.       fmt = cp + 1;
  163.     }
  164.     else
  165.     {
  166.       *cp = EOS;
  167.     }
  168.   }
  169.   
  170.   /*
  171.    * extract other information from format and produce a
  172.    * format string stored in tmpfmt2 also scxmalloc()'d above
  173.    */
  174.   tmp = tmpfmt2;
  175.   for (cp = fmt, tp = tmp; *cp != EOS; cp++)
  176.   {
  177.     switch (*cp)
  178.     {
  179.       case '\\':
  180.         *tp++ = *cp++;
  181.         *tp++ = *cp;
  182.     break;
  183.  
  184.       case ',':
  185.         comma = true;
  186.     break;
  187.  
  188.       case '.':
  189.         if (decimal == NULL)
  190.       decimal = tp;
  191.     *tp++ = *cp;
  192.     break;
  193.     
  194.       case '%':
  195.         val *= 100.0;
  196.     *tp++ = *cp;
  197.     break;
  198.     
  199.       default:
  200.         *tp++ = *cp;
  201.     break;
  202.     }
  203.   }
  204.   *tp = EOS;
  205.   fmt = tmpfmt2;
  206.  
  207.   if (val < 0.0)
  208.   {    negative = true;
  209.     val = -val;
  210.   }
  211.   /*
  212.    * extract the exponent from the format if present
  213.    */
  214.   for (cp = fmt; *cp != EOS; cp++)
  215.   { if (*cp == '\\')
  216.     {
  217.       cp++;
  218.     }
  219.     else if (*cp == 'e' || *cp == 'E')
  220.     {
  221.       if (cp[1] == '+' || cp[1] == '-')
  222.       {
  223.     exponent = strcpy(exptmp, cp);
  224.     *cp = EOS;
  225.     exp_val = 0;
  226.     if (val!=0.0) {
  227.       while (val < 1.0)
  228.       {
  229.         val *= 10.0;
  230.         exp_val--;
  231.       }
  232.       while (val >= 10.0)
  233.       {
  234.         val /= 10.0;
  235.         exp_val++;
  236.       }
  237.     }
  238.     break;
  239.       }
  240.     }
  241.   }
  242.  
  243.   /*
  244.    * determine maximum decimal places and use sprintf
  245.    * to build initial character form of formatted value.
  246.    */
  247.   width = 0;
  248.   if (decimal)
  249.   {
  250.     *decimal++ = EOS;
  251.     for (cp = decimal; *cp != EOS; cp++)
  252.     {
  253.       switch (*cp)
  254.       {
  255.         case '\\':
  256.           cp++;
  257.       break;
  258.  
  259.         case '#':
  260.           width++;
  261.       break;
  262.  
  263.     case '0':
  264.       zero_pad = ++width;
  265.       break;
  266.       }
  267.     }
  268.     zero_pad = strlen(decimal) - zero_pad;
  269.   }
  270.   (void) sprintf(prtfmt, "%%.%dlf", width);
  271.   (void) sprintf(mantissa, prtfmt, val);
  272.   for (cp = integer = mantissa; *cp != '.' && *cp != EOS; cp++)
  273.   {
  274.     if (*integer == '0')
  275.       integer++;
  276.   }
  277.   if (*cp == '.')
  278.   {
  279.     fraction = cp + 1;
  280.     *cp = EOS;
  281.     cp = fraction + strlen(fraction) - 1;
  282.     for (; zero_pad > 0; zero_pad--, cp--)
  283.     {
  284.       if (*cp == '0')
  285.         *cp = EOS;
  286.     }
  287.   }
  288.  
  289.   /*
  290.    * format the puppy
  291.    */
  292.   {
  293.     static    char *citmp = NULL, *cftmp = NULL;
  294.     static    unsigned cilen = 0, cflen = 0;
  295.     char *ci, *cf, *ce;
  296.     int len_ci, len_cf, len_ce;
  297.     bool ret = false;
  298.     
  299.     ci = fmt_int(integer, fmt, comma, negative);
  300.     len_ci = strlen(ci);
  301.     if (len_ci >= cilen)
  302.     {    cilen = len_ci + 40;
  303.     citmp = scxrealloc(citmp, cilen);
  304.     }
  305.     ci = strcpy(citmp, ci);
  306.  
  307.     cf = (fraction) ? fmt_frac(fraction, decimal) : "";
  308.     len_cf = strlen(cf);
  309.     if (len_cf >= cflen)
  310.     {    cflen = len_cf + 40;
  311.     cftmp = scxrealloc(cftmp, cilen);
  312.     }
  313.     cf = strcpy(cftmp, cf);
  314.  
  315.     ce = (exponent) ? fmt_exp(exp_val, exponent) : "";
  316.     len_ce = strlen(ce);
  317. /*
  318.  * Skip copy assuming sprintf doesn't call our format functions
  319.  *   ce = strcpy(scxmalloc((unsigned)((len_ce = strlen(ce)) + 1)), ce);
  320.  */
  321.     if (len_ci + len_cf + len_ce < buflen)
  322.     {
  323.       (void) sprintf(buf, "%s%s%s", ci, cf, ce);
  324.       ret = true;
  325.     }
  326.  
  327.     return (ret);
  328.   }
  329. }
  330.  
  331. /*****************************************************************************/
  332.  
  333. static char *
  334. fmt_int(val, fmt, comma, negative)
  335.   char *val;        /* integer part of the value to be formatted */
  336.   char *fmt;        /* integer part of the format */
  337.   bool comma;        /* true if we should comma-ify the value */
  338.   bool negative;    /* true if the value is actually negative */
  339. {
  340.   int digit, f, v;
  341.   int thousands = 0;
  342.   char *cp;
  343.   static char buf[MAXBUF];
  344.   char *bufptr = buf;
  345.  
  346.   /*
  347.    * locate the leftmost digit placeholder
  348.    */
  349.   for (cp = fmt; *cp != EOS; cp++)
  350.   {
  351.     if (*cp == '\\')
  352.       cp++;
  353.     else if (*cp == '#' || *cp == '0')
  354.       break;
  355.   }
  356.   digit = (*cp == EOS) ? -1 : cp - fmt;
  357.  
  358.   /*
  359.    * format the value
  360.    */
  361.   f = strlen(fmt) - 1;
  362.   v = (digit >= 0) ? strlen(val) - 1 : -1;
  363.   while (f >= 0 || v >= 0)
  364.   {
  365.     if (f > 0 && fmt[f-1] == '\\')
  366.     {
  367.       *bufptr++ = fmt[f--];
  368.     }
  369.     else if (f >= 0 && (fmt[f] == '#' || fmt[f] == '0'))
  370.     {
  371.       if (v >= 0 || fmt[f] == '0')
  372.       {
  373.         *bufptr++ = v < 0 ? '0' : val[v];
  374.     if (comma && (thousands = (thousands + 1) % 3) == 0 && v > 0)
  375.     {
  376.       *bufptr++ = ',';
  377.     }
  378.     v--;
  379.       }
  380.     }
  381.     else if (f >= 0)
  382.     {
  383.       *bufptr++ = fmt[f];
  384.     }
  385.     if (v >= 0 && f == digit)
  386.     {
  387.       continue;
  388.     }
  389.     f--;
  390.   }
  391.     
  392.   if (negative && digit >= 0)
  393.     *bufptr++ = '-';
  394.   *bufptr = EOS;
  395.   reverse(buf);
  396.  
  397.   return (buf);
  398. }
  399.  
  400. /*****************************************************************************/
  401.  
  402. static char *
  403. fmt_frac(val, fmt)
  404.   char *val;        /* fractional part of the value to be formatted */
  405.   char *fmt;        /* fractional portion of format */
  406. {
  407.   static char buf[MAXBUF];
  408.   register char *bufptr = buf;
  409.   register char *fmtptr = fmt, *valptr = val;
  410.  
  411.   *bufptr++ = '.';
  412.   while (*fmtptr != EOS)
  413.   {
  414.     if (*fmtptr == '\\')
  415.     {
  416.       *bufptr++ = *++fmtptr;
  417.     }
  418.     else if (*fmtptr == '#' || *fmtptr == '0')
  419.     {
  420.       if (*valptr != EOS || *fmtptr == '0')
  421.       {
  422.         *bufptr++ = (*valptr != EOS) ? *valptr++ : *fmtptr;
  423.       }
  424.     }
  425.     else
  426.     {
  427.       *bufptr++ = *fmtptr;
  428.     }
  429.     fmtptr++;
  430.   }
  431.   *bufptr = EOS;
  432.  
  433.   return (buf);
  434. }
  435.  
  436. /*****************************************************************************/
  437.  
  438. static char *
  439. fmt_exp(val, fmt)
  440.   int val;        /* value of the exponent */
  441.   char *fmt;        /* exponent part of the format */
  442. {
  443.   static char buf[MAXBUF];
  444.   register char *bufptr = buf;
  445.   char valbuf[64];
  446.   bool negative = false;
  447.   
  448.   *bufptr++ = *fmt++;
  449.   if (*fmt == '+')
  450.     *bufptr++ = (val < 0) ? '-' : '+';
  451.   else if (val < 0)
  452.     *bufptr++ = '-';
  453.   fmt++;
  454.   *bufptr = EOS;
  455.  
  456.   if (val < 0)
  457.   {
  458.     val = -val;
  459.     negative = false;
  460.   }
  461.   (void) sprintf(valbuf, "%d", val);
  462.   
  463.   (void) strcat(buf, fmt_int(valbuf, fmt, false, negative));
  464.   return (buf);
  465. }
  466.  
  467. /*****************************************************************************/
  468.  
  469. static void
  470. reverse(buf)
  471.   register char *buf;
  472. {
  473.   register char *cp = buf + strlen(buf) - 1;
  474.   register char tmp;
  475.  
  476.   while (buf < cp)
  477.   {
  478.     tmp = *cp;
  479.     *cp-- = *buf;
  480.     *buf++ = tmp;
  481.   }
  482. }
  483.  
  484. /*****************************************************************************/
  485. /*  
  486.  * Tom Anderson    <toma@hpsad.hp.com>
  487.  * 10/14/90
  488.  *
  489.  * This routine takes a value and formats it using fixed, scientific,
  490.  * or engineering notation.  The format command 'f' determines which
  491.  * format is used.  The formats are:         example
  492.  *    0:   Fixed point (default)             0.00010
  493.  *    1:   Scientific                        1.00E-04
  494.  *    2:   Engineering                       100.00u
  495.  *
  496.  * The format command 'f' now uses three values.  The first two are the
  497.  * width and precision, and the last one is the format value 0, 1, or 2 as
  498.  * described above.  The format value is passed in the variable fmt.
  499.  *
  500.  * This formatted value is written into the passed buffer.  if the
  501.  * resulting string is too long to fit into the passed buffer, the
  502.  * function returns false.  Otherwise the function returns true.
  503.  *
  504.  * When a number is formatted as engineering and is outside of the range
  505.  * of typically used engineering exponents, the format reverts to
  506.  * 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. char engmult[] = "afpnum kMGT";
  524.  
  525. bool
  526. engformat(fmt, width, lprecision, val, buf, buflen)
  527. int fmt; 
  528. int width; 
  529. int lprecision;
  530. double val;
  531. char *buf;
  532. int buflen;
  533. {
  534.   int engind = 0;
  535.   double engmant, pow(), engabs, engexp;
  536.   if (buflen < width) return (false);
  537.   if (fmt == REFMTFIX)
  538.   (void) sprintf(buf,"%*.*f", width, lprecision, val);
  539.   if (fmt == REFMTFLT)
  540.   (void) sprintf(buf,"%*.*E", width, lprecision, val);
  541.   if (fmt == REFMTENG)
  542.   {
  543.     if (val == 0e0)    /* Hack to get zeroes to line up in engr fmt */
  544.     {
  545.       (void) sprintf((buf-1),"%*.*f ", width, lprecision, val);
  546.     }
  547.     else
  548.     {
  549.       engabs=(val);
  550.       if (engabs < 0e0) engabs= -engabs;
  551.       if ((engabs >= 1e-18) && (engabs < 1e-15 )) engind=0;
  552.       if ((engabs >= 1e-15) && (engabs < 1e-12 )) engind=1;
  553.       if ((engabs >= 1e-12) && (engabs < 1e-9 )) engind=2;
  554.       if ((engabs >= 1e-9) && (engabs < 1e-6 )) engind=3;
  555.       if ((engabs >= 1e-6) && (engabs < 1e-3 )) engind=4;
  556.       if ((engabs >= 1e-3) && (engabs < 1 )) engind=5;
  557.       if ((engabs >= 1) && (engabs < 1e3 )) engind=6;
  558.       if ((engabs >= 1e3) && (engabs < 1e6 )) engind=7;
  559.       if ((engabs >= 1e6) && (engabs < 1e9 )) engind=8;
  560.       if ((engabs >= 1e9) && (engabs < 1e12 )) engind=9;
  561.       if ((engabs >= 1e12) && (engabs < 1e15 )) engind=10;
  562.       if ((engabs <1e-18) || (engabs >=1e15))
  563.       {
  564.       /* Revert to floating point */
  565.         (void) sprintf(buf,"%*.*E", width, lprecision, val);
  566.       }
  567.       else
  568.       {
  569.         engexp= (double) (engind-6)*3;
  570.         engmant= val/pow(10.0e0,engexp);
  571.         (void) sprintf(buf,"%*.*f%c", width-1,
  572.                       lprecision, engmant, engmult[engind]);
  573.       }
  574.     }
  575.   }
  576.   if (fmt == REFMTDATE) {
  577.     int i;
  578.     char *time;
  579.     long int secs;
  580.  
  581.     if (buflen < 9) {
  582.       for (i = 0; i < width; i++) buf[i] = '*';
  583.       buf[i] = '\0';
  584.     }
  585.     else {
  586.       secs = (time_t)val;
  587.       time = ctime(&secs);
  588.       buf[0] = time[8];
  589.       buf[1] = time[9];
  590.       buf[2] = ' ';
  591.       buf[3] = time[4];
  592.       buf[4] = time[5];
  593.       buf[5] = time[6];
  594.       buf[6] = ' ';
  595.       buf[7] = time[22];
  596.       buf[8] = time[23];
  597.       for (i = 9; i < width; i++) buf[i] = ' ';
  598.       buf[i] = '\0';
  599.     }
  600.   }
  601.   return (true);
  602. }
  603.