diff options
author | Arnold D. Robbins <arnold@skeeve.com> | 2011-04-29 10:35:31 +0300 |
---|---|---|
committer | Arnold D. Robbins <arnold@skeeve.com> | 2011-04-29 10:35:31 +0300 |
commit | 70a498e0df48c71699797024fcb4c8154599ea5d (patch) | |
tree | 544ce76df279e47303dd1deb532986b655bd2853 /awkprintf.h | |
parent | 04746bc5c0301fac55badc956225f486607dffd9 (diff) | |
download | egawk-70a498e0df48c71699797024fcb4c8154599ea5d.tar.gz egawk-70a498e0df48c71699797024fcb4c8154599ea5d.tar.bz2 egawk-70a498e0df48c71699797024fcb4c8154599ea5d.zip |
Bug fixes from John and Pat.
Diffstat (limited to 'awkprintf.h')
-rw-r--r-- | awkprintf.h | 1019 |
1 files changed, 0 insertions, 1019 deletions
diff --git a/awkprintf.h b/awkprintf.h deleted file mode 100644 index 47ee377d..00000000 --- a/awkprintf.h +++ /dev/null @@ -1,1019 +0,0 @@ -/* - * awkprintf.h -- Formatting code for gawk, used in debug.c and builtin.c. - */ - -/* - * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, Inc. - * - * This file is part of GAWK, the GNU implementation of the - * AWK Programming Language. - * - * GAWK is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * GAWK is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "floatmagic.h" - -#define DEFAULT_G_PRECISION 6 - -#ifdef GFMT_WORKAROUND -/* semi-temporary hack, mostly to gracefully handle VMS */ -static void sgfmt(char *buf, const char *format, int alt, - int fwidth, int precision, double value); -#endif /* GFMT_WORKAROUND */ -static size_t mbc_byte_count(const char *ptr, size_t numchars); -static size_t mbc_char_count(const char *ptr, size_t numbytes); - -/* - * r_format_arg() formats arguments of sprintf, - * and accordingly to a fmt_string providing a format like in - * printf family from C library. Returns a string node which value - * is a formatted string. Called by sprintf function. - * - * It is one of the uglier parts of gawk. Thanks to Michal Jaegermann - * for taming this beast and making it compatible with ANSI C. - */ - -r_format_arg( - const char *fmt_string, - size_t n0, - NODE **the_args, - long num_args) -{ -/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */ -/* difference of pointers should be of ptrdiff_t type, but let us be kind */ -#define bchunk(s, l) if (l) { \ - while ((l) > ofre) { \ - size_t olen = obufout - obuf; \ - erealloc(obuf, char *, osiz * 2, "format_tree"); \ - ofre += osiz; \ - osiz *= 2; \ - obufout = obuf + olen; \ - } \ - memcpy(obufout, s, (size_t) (l)); \ - obufout += (l); \ - ofre -= (l); \ -} - -/* copy one byte from 's' to 'obufout' checking for space in the process */ -#define bchunk_one(s) { \ - if (ofre < 1) { \ - size_t olen = obufout - obuf; \ - erealloc(obuf, char *, osiz * 2, "format_tree"); \ - ofre += osiz; \ - osiz *= 2; \ - obufout = obuf + olen; \ - } \ - *obufout++ = *s; \ - --ofre; \ -} - -/* Is there space for something L big in the buffer? */ -#define chksize(l) if ((l) >= ofre) { \ - size_t olen = obufout - obuf; \ - size_t delta = osiz+l-ofre; \ - erealloc(obuf, char *, osiz + delta, "format_tree"); \ - obufout = obuf + olen; \ - ofre += delta; \ - osiz += delta; \ -} - - size_t cur_arg = 0; - NODE *r = NULL; - int i; - int toofew = FALSE; - char *obuf, *obufout; - size_t osiz, ofre; - const char *chbuf; - const char *s0, *s1; - int cs1; - NODE *arg; - long fw, prec, argnum; - int used_dollar; - int lj, alt, big, bigbig, small, have_prec, need_format; - long *cur = NULL; - uintmax_t uval; - int sgn; - int base = 0; - /* - * Although this is an array, the elements serve two different - * purposes. The first element is the general buffer meant - * to hold the entire result string. The second one is a - * temporary buffer for large floating point values. They - * could just as easily be separate variables, and the - * code might arguably be clearer. - */ - struct { - char *buf; - size_t bufsize; - char stackbuf[30]; - } cpbufs[2]; -#define cpbuf cpbufs[0].buf - char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)]; - char *cp; - const char *fill; - AWKNUM tmpval; - char signchar = FALSE; - size_t len; - int zero_flag = FALSE; - int quote_flag = FALSE; - int ii, jj; - char *chp; - size_t copy_count, char_count; - static const char sp[] = " "; - static const char zero_string[] = "0"; - static const char lchbuf[] = "0123456789abcdef"; - static const char Uchbuf[] = "0123456789ABCDEF"; - -#define INITIAL_OUT_SIZE 512 - emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree"); - obufout = obuf; - osiz = INITIAL_OUT_SIZE; - ofre = osiz - 2; - - cur_arg = 1; - - { - size_t k; - for (k = 0; k < sizeof(cpbufs)/sizeof(cpbufs[0]); k++) { - cpbufs[k].bufsize = sizeof(cpbufs[k].stackbuf); - cpbufs[k].buf = cpbufs[k].stackbuf; - } - } - - /* - * The point of this goop is to grow the buffer - * holding the converted number, so that large - * values don't overflow a fixed length buffer. - */ -#define PREPEND(CH) do { \ - if (cp == cpbufs[0].buf) { \ - char *prev = cpbufs[0].buf; \ - emalloc(cpbufs[0].buf, char *, 2*cpbufs[0].bufsize, \ - "format_tree"); \ - memcpy((cp = cpbufs[0].buf+cpbufs[0].bufsize), prev, \ - cpbufs[0].bufsize); \ - cpbufs[0].bufsize *= 2; \ - if (prev != cpbufs[0].stackbuf) \ - efree(prev); \ - cend = cpbufs[0].buf+cpbufs[0].bufsize; \ - } \ - *--cp = (CH); \ -} while(0) - - /* - * Check first for use of `count$'. - * If plain argument retrieval was used earlier, choke. - * Otherwise, return the requested argument. - * If not `count$' now, but it was used earlier, choke. - * If this format is more than total number of args, choke. - * Otherwise, return the current argument. - */ -#define parse_next_arg() { \ - if (argnum > 0) { \ - if (cur_arg > 1) { \ - fmt_msg(_("fatal: must use `count$' on all formats or none")); \ - goto out; \ - } \ - arg = the_args[argnum]; \ - } else if (used_dollar) { \ - fmt_msg(_("fatal: must use `count$' on all formats or none")); \ - arg = 0; /* shutup the compiler */ \ - goto out; \ - } else if (cur_arg >= num_args) { \ - arg = 0; /* shutup the compiler */ \ - toofew = TRUE; \ - break; \ - } else { \ - arg = the_args[cur_arg]; \ - cur_arg++; \ - } \ -} - - need_format = FALSE; - used_dollar = FALSE; - - s0 = s1 = fmt_string; - while (n0-- > 0) { - if (*s1 != '%') { - s1++; - continue; - } - need_format = TRUE; - bchunk(s0, s1 - s0); - s0 = s1; - cur = &fw; - fw = 0; - prec = 0; - argnum = 0; - have_prec = FALSE; - signchar = FALSE; - zero_flag = FALSE; - quote_flag = FALSE; - lj = alt = big = bigbig = small = FALSE; - fill = sp; - cp = cend; - chbuf = lchbuf; - s1++; - -retry: - if (n0-- == 0) /* ran out early! */ - break; - - switch (cs1 = *s1++) { - case (-1): /* dummy case to allow for checking */ -check_pos: - if (cur != &fw) - break; /* reject as a valid format */ - goto retry; - case '%': - need_format = FALSE; - /* - * 29 Oct. 2002: - * The C99 standard pages 274 and 279 seem to imply that - * since there's no arg converted, the field width doesn't - * apply. The code already was that way, but this - * comment documents it, at least in the code. - */ - if (do_lint) { - const char *msg = NULL; - - if (fw && ! have_prec) - msg = _("field width is ignored for `%%' specifier"); - else if (fw == 0 && have_prec) - msg = _("precision is ignored for `%%' specifier"); - else if (fw && have_prec) - msg = _("field width and precision are ignored for `%%' specifier"); - - if (msg != NULL) - lintwarn("%s", msg); - } - bchunk_one("%"); - s0 = s1; - break; - - case '0': - /* - * Only turn on zero_flag if we haven't seen - * the field width or precision yet. Otherwise, - * screws up floating point formatting. - */ - if (cur == & fw) - zero_flag = TRUE; - if (lj) - goto retry; - /* FALL through */ - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (cur == NULL) - break; - if (prec >= 0) - *cur = cs1 - '0'; - /* - * with a negative precision *cur is already set - * to -1, so it will remain negative, but we have - * to "eat" precision digits in any case - */ - while (n0 > 0 && *s1 >= '0' && *s1 <= '9') { - --n0; - *cur = *cur * 10 + *s1++ - '0'; - } - if (prec < 0) /* negative precision is discarded */ - have_prec = FALSE; - if (cur == &prec) - cur = NULL; - if (n0 == 0) /* badly formatted control string */ - continue; - goto retry; - case '$': - if (do_traditional) { - fmt_msg(_("fatal: `$' is not permitted in awk formats")); - goto out; - } - - if (cur == &fw) { - argnum = fw; - fw = 0; - used_dollar = TRUE; - if (argnum <= 0) { - fmt_msg(_("fatal: arg count with `$' must be > 0")); - goto out; - } - if (argnum >= num_args) { - fmt_msg(_("fatal: arg count %ld greater than total number of supplied arguments"), argnum); - goto out; - } - } else { - fmt_msg(_("fatal: `$' not permitted after period in format")); - goto out; - } - - goto retry; - case '*': - if (cur == NULL) - break; - if (! do_traditional && isdigit((unsigned char) *s1)) { - int val = 0; - - for (; n0 > 0 && *s1 && isdigit((unsigned char) *s1); s1++, n0--) { - val *= 10; - val += *s1 - '0'; - } - if (*s1 != '$') { - fmt_msg(_("fatal: no `$' supplied for positional field width or precision")); - goto out; - } else { - s1++; - n0--; - } - if (val >= num_args) { - toofew = TRUE; - break; - } - arg = the_args[val]; - } else { - parse_next_arg(); - } - *cur = force_number(arg); - if (*cur < 0 && cur == &fw) { - *cur = -*cur; - lj++; - } - if (cur == &prec) { - if (*cur >= 0) - have_prec = TRUE; - else - have_prec = FALSE; - cur = NULL; - } - goto retry; - case ' ': /* print ' ' or '-' */ - /* 'space' flag is ignored */ - /* if '+' already present */ - if (signchar != FALSE) - goto check_pos; - /* FALL THROUGH */ - case '+': /* print '+' or '-' */ - signchar = cs1; - goto check_pos; - case '-': - if (prec < 0) - break; - if (cur == &prec) { - prec = -1; - goto retry; - } - fill = sp; /* if left justified then other */ - lj++; /* filling is ignored */ - goto check_pos; - case '.': - if (cur != &fw) - break; - cur = ≺ - have_prec = TRUE; - goto retry; - case '#': - alt = TRUE; - goto check_pos; - case '\'': -#if defined(HAVE_LOCALE_H) - /* allow quote_flag if there is a thousands separator. */ - if (loc.thousands_sep[0] != '\0') - quote_flag = TRUE; - goto check_pos; -#else - goto retry; -#endif - case 'l': - if (big) - break; - else { - static short warned = FALSE; - - if (do_lint && ! warned) { - lintwarn(_("`l' is meaningless in awk formats; ignored")); - warned = TRUE; - } - if (do_posix) { - fmt_msg(_("fatal: `l' is not permitted in POSIX awk formats")); - goto out; - } - } - big = TRUE; - goto retry; - case 'L': - if (bigbig) - break; - else { - static short warned = FALSE; - - if (do_lint && ! warned) { - lintwarn(_("`L' is meaningless in awk formats; ignored")); - warned = TRUE; - } - if (do_posix) { - fmt_msg(_("fatal: `L' is not permitted in POSIX awk formats")); - goto out; - } - } - bigbig = TRUE; - goto retry; - case 'h': - if (small) - break; - else { - static short warned = FALSE; - - if (do_lint && ! warned) { - lintwarn(_("`h' is meaningless in awk formats; ignored")); - warned = TRUE; - } - if (do_posix) { - fmt_msg(_("fatal: `h' is not permitted in POSIX awk formats")); - goto out; - } - } - small = TRUE; - goto retry; - case 'c': - need_format = FALSE; - parse_next_arg(); - /* user input that looks numeric is numeric */ - if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM) - (void) force_number(arg); - if (arg->flags & NUMBER) { - uval = (uintmax_t) arg->numbr; -#if MBS_SUPPORT - if (gawk_mb_cur_max > 1) { - char buf[100]; - wchar_t wc; - mbstate_t mbs; - size_t count; - - memset(& mbs, 0, sizeof(mbs)); - wc = uval; - - count = wcrtomb(buf, wc, & mbs); - if (count == 0 - || count == (size_t)-1 - || count == (size_t)-2) - goto out0; - - memcpy(cpbuf, buf, count); - prec = count; - cp = cpbuf; - goto pr_tail; - } -out0: - ; - /* else, - fall through */ -#endif - if (do_lint && uval > 255) { - lintwarn("[s]printf: value %g is too big for %%c format", - arg->numbr); - } - cpbuf[0] = uval; - prec = 1; - cp = cpbuf; - goto pr_tail; - } - /* - * As per POSIX, only output first character of a - * string value. Thus, we ignore any provided - * precision, forcing it to 1. (Didn't this - * used to work? 6/2003.) - */ - cp = arg->stptr; -#ifdef MBS_SUPPORT - /* - * First character can be multiple bytes if - * it's a multibyte character. Grr. - */ - if (gawk_mb_cur_max > 1) { - mbstate_t state; - size_t count; - - memset(& state, 0, sizeof(state)); - count = mbrlen(cp, arg->stlen, & state); - if (count == 0 - || count == (size_t)-1 - || count == (size_t)-2) - goto out2; - prec = count; - goto pr_tail; - } -out2: - ; -#endif - prec = 1; - goto pr_tail; - case 's': - need_format = FALSE; - parse_next_arg(); - arg = force_string(arg); - if (fw == 0 && ! have_prec) - prec = arg->stlen; - else { - char_count = mbc_char_count(arg->stptr, arg->stlen); - if (! have_prec || prec > char_count) - prec = char_count; - } - cp = arg->stptr; - goto pr_tail; - case 'd': - case 'i': - need_format = FALSE; - parse_next_arg(); - tmpval = force_number(arg); - /* - * Check for Nan or Inf. - */ - if (isnan(tmpval) || isinf(tmpval)) - goto out_of_range; - else - tmpval = double_to_int(tmpval); - - /* - * ``The result of converting a zero value with a - * precision of zero is no characters.'' - */ - if (have_prec && prec == 0 && tmpval == 0) - goto pr_tail; - - if (tmpval < 0) { - tmpval = -tmpval; - sgn = TRUE; - } else { - if (tmpval == -0.0) - /* avoid printing -0 */ - tmpval = 0.0; - sgn = FALSE; - } - /* - * Use snprintf return value to tell if there - * is enough room in the buffer or not. - */ - while ((i = snprintf(cpbufs[1].buf, - cpbufs[1].bufsize, "%.0f", - tmpval)) >= - cpbufs[1].bufsize) { - if (cpbufs[1].buf == cpbufs[1].stackbuf) - cpbufs[1].buf = NULL; - if (i > 0) { - cpbufs[1].bufsize += ((i > cpbufs[1].bufsize) ? - i : cpbufs[1].bufsize); - } - else - cpbufs[1].bufsize *= 2; - assert(cpbufs[1].bufsize > 0); - erealloc(cpbufs[1].buf, char *, - cpbufs[1].bufsize, "format_tree"); - } - if (i < 1) - goto out_of_range; - chp = &cpbufs[1].buf[i-1]; - ii = jj = 0; - do { - PREPEND(*chp); - chp--; i--; -#if defined(HAVE_LOCALE_H) - if (quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { - if (i) /* only add if more digits coming */ - PREPEND(loc.thousands_sep[0]); /* XXX - assumption it's one char */ - if (loc.grouping[ii+1] == 0) - jj = 0; /* keep using current val in loc.grouping[ii] */ - else if (loc.grouping[ii+1] == CHAR_MAX) - quote_flag = FALSE; - else { - ii++; - jj = 0; - } - } -#endif - } while (i > 0); - - /* add more output digits to match the precision */ - if (have_prec) { - while (cend - cp < prec) - PREPEND('0'); - } - - if (sgn) - PREPEND('-'); - else if (signchar) - PREPEND(signchar); - /* - * When to fill with zeroes is of course not simple. - * First: No zero fill if left-justifying. - * Next: There seem to be two cases: - * A '0' without a precision, e.g. %06d - * A precision with no field width, e.g. %.10d - * Any other case, we don't want to fill with zeroes. - */ - if (! lj - && ((zero_flag && ! have_prec) - || (fw == 0 && have_prec))) - fill = zero_string; - if (prec > fw) - fw = prec; - prec = cend - cp; - if (fw > prec && ! lj && fill != sp - && (*cp == '-' || signchar)) { - bchunk_one(cp); - cp++; - prec--; - fw--; - } - goto pr_tail; - case 'X': - chbuf = Uchbuf; /* FALL THROUGH */ - case 'x': - base += 6; /* FALL THROUGH */ - case 'u': - base += 2; /* FALL THROUGH */ - case 'o': - base += 8; - need_format = FALSE; - parse_next_arg(); - tmpval = force_number(arg); - - /* - * ``The result of converting a zero value with a - * precision of zero is no characters.'' - * - * If I remember the ANSI C standard, though, - * it says that for octal conversions - * the precision is artificially increased - * to add an extra 0 if # is supplied. - * Indeed, in C, - * printf("%#.0o\n", 0); - * prints a single 0. - */ - if (! alt && have_prec && prec == 0 && tmpval == 0) - goto pr_tail; - - if (tmpval < 0) { - uval = (uintmax_t) (intmax_t) tmpval; - if ((AWKNUM)(intmax_t)uval != - double_to_int(tmpval)) - goto out_of_range; - } else { - uval = (uintmax_t) tmpval; - if ((AWKNUM)uval != double_to_int(tmpval)) - goto out_of_range; - } - /* - * When to fill with zeroes is of course not simple. - * First: No zero fill if left-justifying. - * Next: There seem to be two cases: - * A '0' without a precision, e.g. %06d - * A precision with no field width, e.g. %.10d - * Any other case, we don't want to fill with zeroes. - */ - if (! lj - && ((zero_flag && ! have_prec) - || (fw == 0 && have_prec))) - fill = zero_string; - ii = jj = 0; - do { - PREPEND(chbuf[uval % base]); - uval /= base; -#if defined(HAVE_LOCALE_H) - if (base == 10 && quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { - if (uval) /* only add if more digits coming */ - PREPEND(loc.thousands_sep[0]); /* XXX --- assumption it's one char */ - if (loc.grouping[ii+1] == 0) - jj = 0; /* keep using current val in loc.grouping[ii] */ - else if (loc.grouping[ii+1] == CHAR_MAX) - quote_flag = FALSE; - else { - ii++; - jj = 0; - } - } -#endif - } while (uval > 0); - - /* add more output digits to match the precision */ - if (have_prec) { - while (cend - cp < prec) - PREPEND('0'); - } - - if (alt && tmpval != 0) { - if (base == 16) { - PREPEND(cs1); - PREPEND('0'); - if (fill != sp) { - bchunk(cp, 2); - cp += 2; - fw -= 2; - } - } else if (base == 8) - PREPEND('0'); - } - base = 0; - if (prec > fw) - fw = prec; - prec = cend - cp; - pr_tail: - if (! lj) { - while (fw > prec) { - bchunk_one(fill); - fw--; - } - } - copy_count = prec; - if (fw == 0 && ! have_prec) - ; - else if (gawk_mb_cur_max > 1 && (cs1 == 's' || cs1 == 'c')) { - assert(cp == arg->stptr || cp == cpbuf); - copy_count = mbc_byte_count(arg->stptr, prec); - } - bchunk(cp, copy_count); - while (fw > prec) { - bchunk_one(fill); - fw--; - } - s0 = s1; - break; - - out_of_range: - /* out of range - emergency use of %g format */ - if (do_lint) - lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"), - (double) tmpval, cs1); - cs1 = 'g'; - goto format_float; - - case 'F': -#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 - cs1 = 'f'; - /* FALL THROUGH */ -#endif - case 'g': - case 'G': - case 'e': - case 'f': - case 'E': - need_format = FALSE; - parse_next_arg(); - tmpval = force_number(arg); - format_float: - if (! have_prec) - prec = DEFAULT_G_PRECISION; - chksize(fw + prec + 9); /* 9 == slop */ -#ifdef VAXCRTL - /* pre-ANSI library doesn't handle '0' flag - correctly in many cases; reject it */ - if (zero_flag - && (lj || (signchar && signchar != '+'))) - zero_flag = FALSE; -#endif - cp = cpbuf; - *cp++ = '%'; - if (lj) - *cp++ = '-'; - if (signchar) - *cp++ = signchar; - if (alt) - *cp++ = '#'; - if (zero_flag) - *cp++ = '0'; - if (quote_flag) - *cp++ = '\''; - strcpy(cp, "*.*"); - cp += 3; - *cp++ = cs1; - *cp = '\0'; -#ifndef GFMT_WORKAROUND -#if defined(LC_NUMERIC) - if (quote_flag && ! use_lc_numeric) - setlocale(LC_NUMERIC, ""); -#endif - { - int n; - while ((n = snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, - (double) tmpval)) >= ofre) - chksize(n) - } -#if defined(LC_NUMERIC) - if (quote_flag && ! use_lc_numeric) - setlocale(LC_NUMERIC, "C"); -#endif -#else /* GFMT_WORKAROUND */ - if (cs1 == 'g' || cs1 == 'G') - sgfmt(obufout, cpbuf, (int) alt, - (int) fw, (int) prec, (double) tmpval); - else { - int n; - while ((n = snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, - (double) tmpval)) >= ofre) - chksize(n) - } -#endif /* GFMT_WORKAROUND */ - len = strlen(obufout); - ofre -= len; - obufout += len; - s0 = s1; - break; - default: - if (do_lint && isalpha(cs1)) - lintwarn(_("ignoring unknown format specifier character `%c': no argument converted"), cs1); - break; - } - if (toofew) { - fmt_msg("%s\n\t`%s'\n\t%*s%s", - _("fatal: not enough arguments to satisfy format string"), - fmt_string, (int) (s1 - fmt_string - 1), "", - _("^ ran out for this one")); - goto out; - } - } - if (do_lint) { - if (need_format) - lintwarn( - _("[s]printf: format specifier does not have control letter")); - if (cur_arg < num_args) - lintwarn( - _("too many arguments supplied for format string")); - } - bchunk(s0, s1 - s0); - r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED); - obuf = NULL; -out: - { - size_t k; - size_t count = sizeof(cpbufs)/sizeof(cpbufs[0]); - for (k = 0; k < count; k++) { - if (cpbufs[k].buf != cpbufs[k].stackbuf) - efree(cpbufs[k].buf); - } - if (obuf != NULL) - efree(obuf); - } - return r; -} - - -#ifdef GFMT_WORKAROUND -/* - * printf's %g format [can't rely on gcvt()] - * caveat: don't use as argument to *printf()! - * 'format' string HAS to be of "<flags>*.*g" kind, or we bomb! - */ -static void -sgfmt(char *buf, /* return buffer; assumed big enough to hold result */ - const char *format, - int alt, /* use alternate form flag */ - int fwidth, /* field width in a format */ - int prec, /* indicates desired significant digits, not decimal places */ - double g) /* value to format */ -{ - char dform[40]; - char *gpos; - char *d, *e, *p; - int again = FALSE; - - strncpy(dform, format, sizeof dform - 1); - dform[sizeof dform - 1] = '\0'; - gpos = strrchr(dform, '.'); - - if (g == 0.0 && ! alt) { /* easy special case */ - *gpos++ = 'd'; - *gpos = '\0'; - (void) sprintf(buf, dform, fwidth, 0); - return; - } - - /* advance to location of 'g' in the format */ - while (*gpos && *gpos != 'g' && *gpos != 'G') - gpos++; - - if (prec <= 0) /* negative precision is ignored */ - prec = (prec < 0 ? DEFAULT_G_PRECISION : 1); - - if (*gpos == 'G') - again = TRUE; - /* start with 'e' format (it'll provide nice exponent) */ - *gpos = 'e'; - prec--; - (void) sprintf(buf, dform, fwidth, prec, g); - if ((e = strrchr(buf, 'e')) != NULL) { /* find exponent */ - int expn = atoi(e+1); /* fetch exponent */ - if (expn >= -4 && expn <= prec) { /* per K&R2, B1.2 */ - /* switch to 'f' format and re-do */ - *gpos = 'f'; - prec -= expn; /* decimal precision */ - (void) sprintf(buf, dform, fwidth, prec, g); - e = buf + strlen(buf); - while (*--e == ' ') - continue; - e++; - } - else if (again) - *gpos = 'E'; - - /* if 'alt' in force, then trailing zeros are not removed */ - if (! alt && (d = strrchr(buf, '.')) != NULL) { - /* throw away an excess of precision */ - for (p = e; p > d && *--p == '0'; ) - prec--; - if (d == p) - prec--; - if (prec < 0) - prec = 0; - /* and do that once again */ - again = TRUE; - } - if (again) - (void) sprintf(buf, dform, fwidth, prec, g); - } -} -#endif /* GFMT_WORKAROUND */ - - -/* mbc_byte_count --- return number of bytes for corresponding numchars multibyte characters */ - -static size_t -mbc_byte_count(const char *ptr, size_t numchars) -{ -#ifdef MBS_SUPPORT - mbstate_t cur_state; - size_t sum = 0; - int mb_len; - - memset(& cur_state, 0, sizeof(cur_state)); - - assert(gawk_mb_cur_max > 1); - mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - return numchars; /* no valid m.b. char */ - - for (; numchars > 0; numchars--) { - mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - break; - sum += mb_len; - ptr += mb_len; - } - - return sum; -#else - return numchars; -#endif -} - -/* mbc_char_count --- return number of m.b. chars in string, up to numbytes bytes */ - -static size_t -mbc_char_count(const char *ptr, size_t numbytes) -{ -#ifdef MBS_SUPPORT - mbstate_t cur_state; - size_t sum = 0; - int mb_len; - - if (gawk_mb_cur_max == 1) - return numbytes; - - memset(& cur_state, 0, sizeof(cur_state)); - - mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - return numbytes; /* no valid m.b. char */ - - for (; numbytes > 0; numbytes--) { - mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - break; - sum++; - ptr += mb_len; - } - - return sum; -#else - return numbytes; -#endif -} |