diff options
author | mongo <mongo@iomega> | 2016-04-15 16:20:17 -0300 |
---|---|---|
committer | mongo <mongo@iomega> | 2016-04-15 16:20:17 -0300 |
commit | f686ba184e0af3fd37aa8a743631a7a376f30843 (patch) | |
tree | e9a48dc691511a2961f93163944ba0ca1a84e5b3 /src/format.c | |
parent | c0a088d7a4bc61e6e69fa5bd8964c39f68507c71 (diff) |
Renamed src.scim2 to src
Diffstat (limited to 'src/format.c')
-rwxr-xr-x | src/format.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/src/format.c b/src/format.c new file mode 100755 index 0000000..a770c64 --- /dev/null +++ b/src/format.c @@ -0,0 +1,564 @@ +/***************************************************************************** + * + * Mark Nagel <nagel@ics.uci.edu> + * 20 July 1989 + * + * $Revision: 7.16 $ + * + * bool + * format(fmt, precision, num, buf, buflen) + * char *fmt; + * double num; + * char buf[]; + * int buflen; + * + * The format function will produce a string representation of a number + * given a _format_ (described below) and a double value. The result is + * written into the passed buffer -- if the resulting string is too + * long to fit into the passed buffer, the function returns false. + * Otherwise the function returns true. + * + * The fmt parameter contains the format to use to convert the number. + * + * # Digit placeholder. If the number has fewer digits on either + * side of the decimal point than there are '#' characters in + * the format, the extra '#' characters are ignored. The number + * is rounded to the number of digit placeholders as there are + * to the right of the decimal point. If there are more digits + * in the number than there are digit placeholders on the left + * side of the decimal point, then those digits are displayed. + * + * 0 Digit placeholder. Same as for '#' except that the number + * is padded with zeroes on either side of the decimal point. + * The number of zeroes used in padding is determined by the + * number of digit placeholders after the '0' for digits on + * the left side of the decimal point and by the number of + * digit placeholders before the '0' for digits on the right + * side of the decimal point. + * + * . Decimal point. Determines how many digits are placed on + * the right and left sides of the decimal point in the number. + * Note that numbers smaller than 1 will begin with a decimal + * point if the left side of the decimal point contains only + * a '#' digit placeholder. Use a '0' placeholder to get a + * leading zero in decimal formats. + * + * % Percentage. For each '%' character in the format, the actual + * number gets multiplied by 100 (only for purposes of formatting + * -- the original number is left unmodified) and the '%' character + * is placed in the same position as it is in the format. + * + * , Thousands separator. The presence of a ',' in the format + * (multiple commas are treated as one) will cause the number + * to be formatted with a ',' separating each set of three digits + * in the integer part of the number with numbering beginning + * from the right end of the integer. + * + * & Precision. When this character is present in the fractional + * part of the number, it is equavalent to a number of 0's equal + * to the precision specified in the column format command. For + * example, if the precision is 3, "&" is equivalent to "000". + * + * \ Quote. This character causes the next character to be + * inserted into the formatted string directly with no + * special interpretation. + * + * E- E+ e- e+ + * Scientific format. Causes the number to formatted in scientific + * notation. The case of the 'E' or 'e' given is preserved. If + * the format uses a '+', then the sign is always given for the + * exponent value. If the format uses a '-', then the sign is + * only given when the exponent value is negative. Note that if + * there is no digit placeholder following the '+' or '-', then + * that part of the formatted number is left out. In general, + * there should be one or more digit placeholders after the '+' + * or '-'. + * + * ; Format selector. Use this character to separate the format + * into two distinct formats. The format to the left of the + * ';' character will be used if the number given is zero or + * positive. The format to the right of the ';' character is + * used if the number given is negative. + * + * Any + * Self insert. Any other character will be inserted directly + * into the formatted number with no change made to the actual + * number. + * + *****************************************************************************/ + +/*****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <time.h> +#include "sc.h" + +#define bool int +#define true 1 +#define false 0 +#define EOS '\0' +#define MAXBUF 256 + +static char *fmt_int(char *val, char *fmt, bool comma, bool negative); +static char *fmt_frac(char *val, char *fmt, int lprecision); +static char *fmt_exp(int val, char *fmt); + +static void reverse(register char *buf); + +char * colformat[COLFORMATS]; + +/*****************************************************************************/ + +bool format(char *fmt, int lprecision, double val, char *buf, int buflen) { + register char *cp; + char *tmp, *tp; + bool comma = false, negative = false; + char *integer = NULL, *decimal = NULL; + char *exponent = NULL; + int exp_val = 0; + int width; + char prtfmt[32]; + static char * mantissa = NULL; + static char * tmpfmt1 = NULL, * tmpfmt2 = NULL, * exptmp = NULL; + static unsigned mantlen = 0, fmtlen = 0; + char * fraction = NULL; + int zero_pad = 0; + + if (fmt == NULL) + return(true); + + if (strlen(fmt) + 1 > fmtlen) { + fmtlen = strlen(fmt) + 40; + tmpfmt1 = scxrealloc(tmpfmt1, fmtlen); + tmpfmt2 = scxrealloc(tmpfmt2, fmtlen); + exptmp = scxrealloc(exptmp, fmtlen); + } + fmt = strcpy(tmpfmt1, fmt); + if (buflen + 1 > mantlen) { + mantlen = buflen + 40; + mantissa = scxrealloc(mantissa, mantlen); + } + +/* + * select positive or negative format if necessary + */ + for (cp = fmt; *cp != ';' && *cp != EOS; cp++) { + if (*cp == '\\') + cp++; + } + if (*cp == ';') { + if (val < 0.0) { + val = -val; /* format should provide sign if desired */ + fmt = cp + 1; + } else + *cp = EOS; + } + +/* + * extract other information from format and produce a + * format string stored in tmpfmt2 also scxmalloc()'d above + */ + tmp = tmpfmt2; + for (cp = fmt, tp = tmp; *cp != EOS; cp++) { + switch (*cp) { + case '\\': + *tp++ = *cp++; + *tp++ = *cp; + break; + + case ',': + comma = true; + break; + + case '.': + if (decimal == NULL) + decimal = tp; + *tp++ = *cp; + break; + + case '%': + val *= 100.0; + *tp++ = *cp; + break; + + default: + *tp++ = *cp; + break; + } + } + *tp = EOS; + fmt = tmpfmt2; + +/* The following line was necessary due to problems with the gcc + * compiler and val being a negative zero. Thanks to Mike Novack for + * the suggestion. - CRM + */ + val = (val + 1.0) - 1.0; + if (val < 0.0) { + negative = true; + val = -val; + } +/* + * extract the exponent from the format if present + */ + for (cp = fmt; *cp != EOS; cp++) { + if (*cp == '\\') + cp++; + else if (*cp == 'e' || *cp == 'E') { + if (cp[1] == '+' || cp[1] == '-') { + exponent = strcpy(exptmp, cp); + *cp = EOS; + if (val != 0.0) { + while (val < 1.0) { + val *= 10.0; + exp_val--; + } + while (val >= 10.0) { + val /= 10.0; + exp_val++; + } + } + } + break; + } + } + +/* + * determine maximum decimal places and use sprintf + * to build initial character form of formatted value. + */ + width = 0; + if (decimal) { + *decimal++ = EOS; + for (cp = decimal; *cp != EOS; cp++) { + switch (*cp) { + case '\\': + cp++; + break; + + case '#': + width++; + break; + + case '0': + zero_pad = ++width; + break; + + case '&': + width += lprecision; + zero_pad = width; + break; + } + } + zero_pad = strlen(decimal) - zero_pad; + } + (void) sprintf(prtfmt, "%%.%dlf", width); + (void) sprintf(mantissa, prtfmt, val); + for (cp = integer = mantissa; *cp != dpoint && *cp != EOS; cp++) { + if (*integer == '0') + integer++; + } + if (*cp == dpoint) { + fraction = cp + 1; + *cp = EOS; + cp = fraction + strlen(fraction) - 1; + for (; zero_pad > 0 && *cp != EOS; zero_pad--, cp--) { // scim + //for (; zero_pad > 0; zero_pad--, cp--) { // sc + if (*cp == '0') + *cp = EOS; + else + break; + } + } else + fraction = ""; + +/* + * format the puppy + */ + { + static char * citmp = NULL, * cftmp = NULL; + static unsigned cilen = 0, cflen = 0; + char * ci, * cf, * ce; + int len_ci, len_cf, len_ce; + bool ret = false; + + ci = fmt_int(integer, fmt, comma, negative); + len_ci = strlen(ci); + if (len_ci >= cilen) { + cilen = len_ci + 40; + citmp = scxrealloc(citmp, cilen); + } + ci = strcpy(citmp, ci); + + cf = decimal ? fmt_frac(fraction, decimal, lprecision) : ""; + len_cf = strlen(cf); + if (len_cf >= cflen) { + cflen = len_cf + 40; + cftmp = scxrealloc(cftmp, cflen); // scim + //cftmp = scxrealloc(cftmp, cilen); // sc + } + cf = strcpy(cftmp, cf); + + ce = (exponent) ? fmt_exp(exp_val, exponent) : ""; + len_ce = strlen(ce); +/* + * Skip copy assuming sprintf doesn't call our format functions + * ce = strcpy(scxmalloc((unsigned)((len_ce = strlen(ce)) + 1)), ce); + */ + if (len_ci + len_cf + len_ce < buflen) { + (void) sprintf(buf, "%s%s%s", ci, cf, ce); + ret = true; + } + + return (ret); + } +} + +/*****************************************************************************/ + +static char * fmt_int(char *val, /* integer part of the value to be formatted */ + char *fmt, /* integer part of the format */ + bool comma, /* true if we should comma-ify the value */ + bool negative) { /* true if the value is actually negative */ + + int digit, f, v; + int thousands = 0; + char * cp; + static char buf[MAXBUF]; + char * bufptr = buf; + +/* + * locate the leftmost digit placeholder + */ + for (cp = fmt; *cp != EOS; cp++) { + if (*cp == '\\') + cp++; + else if (*cp == '#' || *cp == '0') + break; + } + digit = (*cp == EOS) ? -1 : cp - fmt; + +/* + * format the value + */ + f = strlen(fmt) - 1; + v = (digit >= 0) ? strlen(val) - 1 : -1; + while (f >= 0 || v >= 0) { + if (f > 0 && fmt[f-1] == '\\') { + *bufptr++ = fmt[f--]; + } else if (f >= 0 && (fmt[f] == '#' || fmt[f] == '0')) { + if (v >= 0 || fmt[f] == '0') { + *bufptr++ = v < 0 ? '0' : val[v]; + if (comma && (thousands = (thousands + 1) % 3) == 0 && + v > 0 && thsep != '\0') + *bufptr++ = thsep; + v--; + } + } else if (f >= 0) { + *bufptr++ = fmt[f]; + } + if (v >= 0 && f == digit) { + continue; + } + f--; + } + + if (negative && digit >= 0) + *bufptr++ = '-'; + *bufptr = EOS; + reverse(buf); + + return (buf); +} + +/*****************************************************************************/ + +static char * fmt_frac( + char *val, /* fractional part of the value to be formatted */ + char *fmt, /* fractional portion of format */ + int lprecision) { /* precision, for interpreting the "&" */ + + static char buf[MAXBUF]; + register char * bufptr = buf; + register char * fmtptr = fmt, *valptr = val; + + *bufptr++ = dpoint; + while (*fmtptr != EOS) { + if (*fmtptr == '&') { + int i; + for (i = 0; i < lprecision; i++) + *bufptr++ = (*valptr != EOS) ? *valptr++ : '0'; + } else if (*fmtptr == '\\') + *bufptr++ = *++fmtptr; + else if (*fmtptr == '#' || *fmtptr == '0') { + if (*valptr != EOS || *fmtptr == '0') + *bufptr++ = (*valptr != EOS) ? *valptr++ : '0'; + } else + *bufptr++ = *fmtptr; + fmtptr++; + } + *bufptr = EOS; + + if (buf[1] < '0' || buf[1] > '9') + return (buf + 1); + else + return (buf); +} + +/*****************************************************************************/ + +static char * fmt_exp(int val, /* value of the exponent */ + char *fmt) { /* exponent part of the format */ + static char buf[MAXBUF]; + register char *bufptr = buf; + char valbuf[64]; + bool negative = false; + + *bufptr++ = *fmt++; + if (*fmt == '+') + *bufptr++ = (val < 0) ? '-' : '+'; + else if (val < 0) + *bufptr++ = '-'; + fmt++; + *bufptr = EOS; + + if (val < 0) { + val = -val; + negative = false; + } + (void) sprintf(valbuf, "%d", val); + + (void) strcat(buf, fmt_int(valbuf, fmt, false, negative)); + return (buf); +} + +/*****************************************************************************/ + +static void reverse(register char *buf) { + register char *cp = buf + strlen(buf) - 1; + register char tmp; + + while (buf < cp) { + tmp = *cp; + *cp-- = *buf; + *buf++ = tmp; + } +} + +/*****************************************************************************/ +/* + * Tom Anderson <toma@hpsad.hp.com> + * 10/14/90 + * + * This routine takes a value and formats it using fixed, scientific, + * or engineering notation. The format command 'f' determines which + * format is used. The formats are: example + * 0: Fixed point (default) 0.00010 + * 1: Scientific 1.00E-04 + * 2: Engineering 100.00e-06 + * + * The format command 'f' now uses three values. The first two are the + * width and precision, and the last one is the format value 0, 1, or 2 as + * described above. The format value is passed in the variable fmt. + * + * This formatted value is written into the passed buffer. if the + * resulting string is too long to fit into the passed buffer, the + * function returns false. Otherwise the function returns true. + * + * When a number is formatted as engineering and is outside of the range, + * the format reverts to scientific. + * + * To preserve compatability with old spreadsheet files, the third value + * may be missing, and the default will be fixed point (format 0). + * + * When an old style sheet is saved, the third value will be stored. + * + */ + +/* defined in sc.h */ +#ifndef REFMTFIX +#define REFMTFIX 0 +#define REFMTFLT 1 +#define REFMTENG 2 +#define REFMTDATE 3 +#define REFMTLDATE 4 +#endif + +bool engformat(int fmt, int width, int lprecision, double val, char *buf, int buflen) { + + static char * engmult[] = { + "-18", "-15", "-12", "-09", "-06", "-03", + "+00", + "+03", "+06", "+09", "+12", "+15", "+18" + }; + int engind = 0; + double engmant, pow(), engabs, engexp; + + if (buflen < width) return (false); + if (fmt >= 0 && fmt < COLFORMATS && colformat[fmt]) + return (format(colformat[fmt], lprecision, val, buf, buflen)); + if (fmt == REFMTFIX) + (void) sprintf(buf,"%*.*f", width, lprecision, val); + if (fmt == REFMTFLT) + (void) sprintf(buf,"%*.*E", width, lprecision, val); + if (fmt == REFMTENG) { + if (val == 0e0) { /* Hack to get zeroes to line up in engr fmt */ + (void) sprintf((buf-1),"%*.*f ", width, lprecision, val); + } else { + engabs = (val); + if ( engabs < 0e0) engabs = -engabs; + if ((engabs >= 1e-18) && (engabs < 1e-15)) engind=0; + if ((engabs >= 1e-15) && (engabs < 1e-12)) engind=1; + if ((engabs >= 1e-12) && (engabs < 1e-9 )) engind=2; + if ((engabs >= 1e-9) && (engabs < 1e-6 )) engind=3; + if ((engabs >= 1e-6) && (engabs < 1e-3 )) engind=4; + if ((engabs >= 1e-3) && (engabs < 1 )) engind=5; + if ((engabs >= 1) && (engabs < 1e3 )) engind=6; + if ((engabs >= 1e3) && (engabs < 1e6 )) engind=7; + if ((engabs >= 1e6) && (engabs < 1e9 )) engind=8; + if ((engabs >= 1e9) && (engabs < 1e12 )) engind=9; + if ((engabs >= 1e12) && (engabs < 1e15 )) engind=10; + if ((engabs >= 1e15) && (engabs < 1e18 )) engind=11; + if ((engabs >= 1e18) && (engabs < 1e21 )) engind=12; + if ((engabs < 1e-18) || (engabs >= 1e21 )) { + /* Revert to floating point */ + (void) sprintf(buf,"%*.*E", width, lprecision, val); + } else { + engexp = (double) (engind-6)*3; + engmant = val/pow(10.0e0,engexp); + (void) sprintf(buf,"%*.*fe%s", width-4, lprecision, engmant, engmult[engind]); + } + } + } + if (fmt == REFMTDATE) { + int i; + time_t secs; + + if (buflen < 9) { + for (i = 0; i < width; i++) buf[i] = '*'; + buf[i] = '\0'; + } else { + secs = (time_t)val; + strftime(buf,buflen,"%e %b %y",localtime(&secs)); + for (i = 9; i < width; i++) buf[i] = ' '; + buf[i] = '\0'; + } + } + if (fmt == REFMTLDATE) { + int i; + time_t secs; + + if (buflen < 11) { + for (i = 0; i < width; i++) buf[i] = '*'; + buf[i] = '\0'; + } else { + secs = (time_t)val; + strftime(buf,buflen,"%e %b %Y",localtime(&secs)); + for (i = 11; i < width; i++) buf[i] = ' '; + buf[i] = '\0'; + } + } + return (true); +} |