[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gawk-diffs] [SCM] gawk branch, num-handler, updated. 390353f51f36ca5351
From: |
John Haque |
Subject: |
[gawk-diffs] [SCM] gawk branch, num-handler, updated. 390353f51f36ca53515630d38b63d6bbb1c4f43d |
Date: |
Thu, 03 Jan 2013 10:50:21 +0000 |
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "gawk".
The branch, num-handler has been updated
via 390353f51f36ca53515630d38b63d6bbb1c4f43d (commit)
from 7c017f175f74131de421563b127f9a554cc8fa07 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
http://git.sv.gnu.org/cgit/gawk.git/commit/?id=390353f51f36ca53515630d38b63d6bbb1c4f43d
commit 390353f51f36ca53515630d38b63d6bbb1c4f43d
Author: John Haque <address@hidden>
Date: Wed Jan 2 18:58:01 2013 -0600
Refactoring format_tree() routine.
diff --git a/ChangeLog b/ChangeLog
index 01829c7..389c61f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2013-01-03 John Haque <address@hidden>
+
+ Refactor format_tree() to seperate number formatting code.
+
+ * format.h: New file.
+ (format_spec, print_fmt_buf): Definitions.
+ (chksize, bchunk, bchunk_one, buf_adjust, buf2node,
+ tmpbuf_prepend, pr_fill, pr_num_tail, free_print_fmt_buf):
+ Inline routines.
+ * awk.h (num_handler_t): New fields gawk_format_printf,
+ gawk_isnan, gawk_isinf. Removed field gawk_format_nodes.
+ * builtin.c (chksize__internal, cpbuf_chksize__internal,
+ get_fmt_buf, format_nondecimal): New routines.
+ (format_tree): Restore function format_tree(). Adjusted to call
+ the current number formatting routine.
+ * double.c (awknum_isnan, awknum_isinf, format_awknum_printf):
+ New routines.
+ (format_nodes_awknum): Removed.
+ * mpfr.c (mpfp_isnan, mpfp_isinf, mpfp_format_printf):
+ New routines.
+ (mpfp_format_nodes): Removed.
+
2012-12-28 John Haque <address@hidden>
* double.c: Use make_awknum everywhere instead of make_number
diff --git a/awk.h b/awk.h
index 89f2682..e459a95 100644
--- a/awk.h
+++ b/awk.h
@@ -868,6 +868,8 @@ typedef struct {
NODE *(*ptr)(int); /* function that implements this built-in */
} bltin_t;
+struct format_spec;
+struct print_fmt_buf;
typedef struct {
bool (*init)(bltin_t **); /* initialization */
@@ -884,17 +886,20 @@ typedef struct {
NODE *(*gawk_force_number)(NODE *); /* force a NODE value to be
numeric */
void (*gawk_negate_number)(NODE *); /* in place negation of a
number */
int (*gawk_cmp_numbers)(const NODE *, const NODE *); /* compare two
numbers */
+
int (*gawk_sgn_number)(const NODE *); /* test if a numeric node is
zero,
positive or negative */
- bool (*gawk_is_integer)(const NODE *); /* test if a number is an
integer */
+ bool (*gawk_isinteger)(const NODE *); /* test if a number is an
integer */
+ bool (*gawk_isnan)(const NODE *); /* test if NaN */
+ bool (*gawk_isinf)(const NODE *); /* test if infinity */
NODE *(*gawk_fmt_number)(const char *, int, NODE *); /* stringify a
numeric value
based on awk
input/output format */
- NODE *(*gawk_format_nodes)(const char *, size_t, NODE **, long); /*
format NODES according to
-
user-specified FORMAT */
+
+ int (*gawk_format_printf)(NODE *, struct format_spec *, struct
print_fmt_buf *); /* (s)printf format */
/* conversion to C types */
- AWKNUM (*gawk_todouble)(const NODE *); /* number to AWKNUM */
+ double (*gawk_todouble)(const NODE *); /* number to double */
long (*gawk_tolong)(const NODE *); /* number to long */
unsigned long (*gawk_toulong)(const NODE *); /* number to unsigned
long */
uintmax_t (*gawk_touintmax_t)(const NODE *); /* number to uintmax_t */
@@ -1104,7 +1109,8 @@ extern long (*get_number_si)(const NODE *);
extern double (*get_number_d)(const NODE *);
extern uintmax_t (*get_number_uj)(const NODE *);
extern int (*sgn_number)(const NODE *);
-extern NODE *(*format_tree)(const char *, size_t, NODE **, long);
+extern int (*format_number_printf)(NODE *, struct format_spec *, struct
print_fmt_buf *);
+
/* built-in array types */
extern afunc_t str_array_func[];
@@ -1234,7 +1240,7 @@ DEREF(NODE *r)
/* ------------------------- Pseudo-functions ------------------------- */
#define iszero(n) (sgn_number(n) == 0)
-#define isinteger(n) numbr_hndlr->gawk_is_integer(n)
+#define isinteger(n) numbr_hndlr->gawk_isinteger(n)
#define var_uninitialized(n) ((n)->var_value == Nnull_string)
@@ -1401,6 +1407,7 @@ extern NODE *do_bindtextdomain(int nargs);
extern int strncasecmpmbs(const unsigned char *,
const unsigned char *, size_t);
#endif
+extern NODE *format_tree(const char *, size_t, NODE **, long);
/* eval.c */
extern void PUSH_CODE(INSTRUCTION *cp);
diff --git a/builtin.c b/builtin.c
index f6c80d3..720a12c 100644
--- a/builtin.c
+++ b/builtin.c
@@ -2061,3 +2061,640 @@ mbc_char_count(const char *ptr, size_t numbytes)
return numbytes;
#endif
}
+
+
+#include "format.h"
+
+char lchbuf[] = "0123456789abcdef";
+char Uchbuf[] = "0123456789ABCDEF";
+char space_string[] = " ";
+char zero_string[] = "0";
+
+
+#define OUTBUF_INIT_SIZE 510
+
+/* chksize__internal --- make room for something LEN big in the output buffer
*/
+
+static void
+chksize__internal(struct print_fmt_buf *outb, size_t len)
+{
+ size_t delta, newsize;
+
+ assert(outb->buf != NULL);
+ delta = outb->dataend - outb->buf;
+ newsize = delta + len + OUTBUF_INIT_SIZE;
+ erealloc(outb->buf, char *, newsize + 2, "chksize__internal");
+ outb->dataend = outb->buf + delta;
+ outb->bufsize = newsize;
+ outb->room_left = len + OUTBUF_INIT_SIZE;
+}
+
+/* cpbuf_chksize__internal --- enlarge the temporary buffer */
+
+static void
+cpbuf_chksize__internal(struct print_fmt_buf *outb)
+{
+ char *cp, *prev = outb->cpbuf.buf;
+ size_t oldsize = outb->cpbuf.bufsize;
+ size_t newsize;
+
+ newsize = outb->cpbuf.bufsize = 2 * oldsize;
+ emalloc(outb->cpbuf.buf, char *, newsize + 2,
"cpbuf_chksize__internal");
+ memcpy((cp = outb->cpbuf.buf + oldsize), prev, oldsize);
+ efree(prev);
+ outb->cpbuf.bufend = outb->cpbuf.buf + newsize;
+ outb->cpbuf.databegin = cp;
+}
+
+static struct print_fmt_buf static_outb;
+
+/* get_fmt_buf --- buffer(s) to manage (s)printf formatting */
+
+struct print_fmt_buf *
+get_fmt_buf()
+{
+ struct print_fmt_buf *outb;
+
+ if (static_outb.buf == NULL)
+ outb = & static_outb;
+ else {
+ emalloc(outb, struct print_fmt_buf *, sizeof (struct
print_fmt_buf), "get_fmt_buf");
+ outb->is_malloced = true;
+ }
+
+ emalloc(outb->buf, char *, OUTBUF_INIT_SIZE + 2, "get_fmt_buf");
+ outb->bufsize = OUTBUF_INIT_SIZE;
+ outb->room_left = outb->bufsize;
+ outb->dataend = outb->buf;
+ outb->chksize = chksize__internal;
+
+ emalloc(outb->cpbuf.buf, char *, 64, "get_fmt_buf");
+ outb->cpbuf.bufsize = 62;
+ outb->cpbuf.databegin = outb->cpbuf.bufend = outb->cpbuf.buf +
outb->cpbuf.bufsize;
+ outb->cpbuf_chksize = cpbuf_chksize__internal;
+ return outb;
+}
+
+
+# define CP cpbuf_start(outb)
+# define CEND cpbuf_end(outb)
+# define CPBUF cpbuf(outb)
+
+
+/* format_nondecimal --- output a nondecimal number according to a format */
+
+void
+format_nondecimal(uintmax_t val, struct format_spec *spec, struct
print_fmt_buf *outb)
+{
+ uintmax_t uval = val;
+ int ii, jj;
+ const char *chbuf = spec->chbuf;
+
+ /*
+ * 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 (! spec->lj
+ && ((spec->zero_flag && ! spec->have_prec)
+ || (spec->fw == 0 && spec->have_prec))
+ )
+ spec->fill = zero_string;
+
+ ii = jj = 0;
+ do {
+ tmpbuf_prepend(outb, chbuf[uval % spec->base]);
+ uval /= spec->base;
+#if defined(HAVE_LOCALE_H)
+ if (spec->base == 10 && spec->quote_flag && loc.grouping[ii] &&
++jj == loc.grouping[ii]) {
+ if (uval) /* only add if more digits coming */
+ tmpbuf_prepend(outb, 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)
+ spec->quote_flag= false;
+ else {
+ ii++;
+ jj = 0;
+ }
+ }
+#endif
+ } while (uval > 0);
+
+ /* add more output digits to match the precision */
+ if (spec->have_prec) {
+ while (CEND - CP < spec->prec)
+ tmpbuf_prepend(outb, '0');
+ }
+
+ if (spec->alt && val != 0) {
+ if (spec->base == 16) {
+ tmpbuf_prepend(outb, spec->fmtchar);
+ tmpbuf_prepend(outb, '0');
+ if (spec->fill != space_string) {
+ bchunk(outb, CP, 2);
+ CP += 2;
+ spec->fw -= 2;
+ }
+ } else if (spec->base == 8)
+ tmpbuf_prepend(outb, '0');
+ }
+
+ spec->base = 0;
+ if (spec->prec > spec->fw)
+ spec->fw = spec->prec;
+ spec->prec = CEND - CP;
+ pr_num_tail(CP, spec->prec, spec, outb);
+}
+
+
+/*
+ * format_tree() 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.
+ */
+
+
+NODE *
+format_tree(
+ const char *fmt_string,
+ size_t n0,
+ NODE **the_args,
+ long num_args)
+{
+ size_t cur_arg = 0;
+ NODE *r = NULL;
+ bool toofew = false;
+ const char *s0, *s1;
+ int cs1;
+ NODE *arg;
+ long argnum;
+
+ bool used_dollar;
+ bool big_flag, bigbig_flag, small_flag, need_format;
+ long *cur = NULL;
+ uintmax_t uval;
+ char *cp;
+ size_t copy_count, char_count;
+
+ struct print_fmt_buf *outb;
+ struct format_spec spec;
+
+
+ /*
+ * 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) { \
+ msg(_("fatal: must use `count$' on all formats or
none")); \
+ goto out; \
+ } \
+ arg = the_args[argnum]; \
+ } else if (used_dollar) { \
+ 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++; \
+ } \
+}
+
+ outb = get_fmt_buf();
+ cur_arg = 1;
+
+ need_format = false;
+ used_dollar = false;
+
+ s0 = s1 = fmt_string;
+
+ while (n0-- > 0) {
+ if (*s1 != '%') {
+ s1++;
+ continue;
+ }
+ need_format = true;
+ bchunk(outb, s0, s1 - s0);
+ s0 = s1;
+ argnum = 0;
+
+ memset(& spec, '\0', sizeof (spec));
+ cur = & spec.fw;
+ spec.fill = space_string; /* always space for string */
+
+ big_flag = bigbig_flag = small_flag = false;
+ CP = CEND;
+ s1++;
+
+retry:
+ if (n0-- == 0) /* ran out early! */
+ break;
+
+ switch (cs1 = *s1++) {
+ case (-1): /* dummy case to allow for checking */
+check_pos:
+ if (cur != & spec.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 (spec.fw && ! spec.have_prec)
+ msg = _("field width is ignored for
`%%' specifier");
+ else if (spec.fw == 0 && spec.have_prec)
+ msg = _("precision is ignored for `%%'
specifier");
+ else if (spec.fw && spec.have_prec)
+ msg = _("field width and precision are
ignored for `%%' specifier");
+
+ if (msg != NULL)
+ lintwarn("%s", msg);
+ }
+ bchunk_one(outb, "%");
+ 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 == & spec.fw)
+ spec.zero_flag = true;
+ if (spec.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 (spec.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 (spec.prec < 0) /* negative precision is
discarded */
+ spec.have_prec = false;
+ if (cur == & spec.prec)
+ cur = NULL;
+ if (n0 == 0) /* badly formatted control string */
+ continue;
+ goto retry;
+ case '$':
+ if (do_traditional) {
+ msg(_("fatal: `$' is not permitted in awk
formats"));
+ goto out;
+ }
+
+ if (cur == & spec.fw) {
+ argnum = spec.fw;
+ spec.fw = 0;
+ used_dollar = true;
+ if (argnum <= 0) {
+ msg(_("fatal: arg count with `$' must
be > 0"));
+ goto out;
+ }
+ if (argnum >= num_args) {
+ msg(_("fatal: arg count %ld greater
than total number of supplied arguments"), argnum);
+ goto out;
+ }
+ } else {
+ 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 != '$') {
+ 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();
+ }
+
+ (void) force_number(arg);
+ *cur = get_number_si(arg);
+ if (*cur < 0 && cur == & spec.fw) {
+ *cur = -*cur;
+ spec.lj++;
+ }
+ if (cur == & spec.prec) {
+ if (*cur >= 0)
+ spec.have_prec = true;
+ else
+ spec.have_prec = false;
+ cur = NULL;
+ }
+ goto retry;
+ case ' ': /* print ' ' or '-' */
+ /* 'space' flag is ignored */
+ /* if '+' already present */
+ if (spec.signchar != false)
+ goto check_pos;
+ /* FALL THROUGH */
+ case '+': /* print '+' or '-' */
+ spec.signchar = cs1;
+ goto check_pos;
+ case '-':
+ if (spec.prec < 0)
+ break;
+ if (cur == & spec.prec) {
+ spec.prec = -1;
+ goto retry;
+ }
+ spec.lj++; /* filling is ignored */
+ goto check_pos;
+ case '.':
+ if (cur != & spec.fw)
+ break;
+ cur = & spec.prec;
+ spec.have_prec = true;
+ goto retry;
+ case '#':
+ spec.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')
+ spec.quote_flag= true;
+ goto check_pos;
+#else
+ goto retry;
+#endif
+ case 'l':
+ if (big_flag)
+ break;
+ else {
+ static bool warned = false;
+
+ if (do_lint && ! warned) {
+ lintwarn(_("`l' is meaningless in awk
formats; ignored"));
+ warned = true;
+ }
+ if (do_posix) {
+ msg(_("fatal: `l' is not permitted in
POSIX awk formats"));
+ goto out;
+ }
+ }
+ big_flag = true;
+ goto retry;
+ case 'L':
+ if (bigbig_flag)
+ break;
+ else {
+ static bool warned = false;
+
+ if (do_lint && ! warned) {
+ lintwarn(_("`L' is meaningless in awk
formats; ignored"));
+ warned = true;
+ }
+ if (do_posix) {
+ msg(_("fatal: `L' is not permitted in
POSIX awk formats"));
+ goto out;
+ }
+ }
+ bigbig_flag = true;
+ goto retry;
+ case 'h':
+ if (small_flag)
+ break;
+ else {
+ static bool warned = false;
+
+ if (do_lint && ! warned) {
+ lintwarn(_("`h' is meaningless in awk
formats; ignored"));
+ warned = true;
+ }
+ if (do_posix) {
+ msg(_("fatal: `h' is not permitted in
POSIX awk formats"));
+ goto out;
+ }
+ }
+ small_flag = 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) != 0) {
+ uval = get_number_uj(arg);
+#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);
+ spec.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",
+ get_number_d(arg));
+ }
+ CPBUF[0] = uval;
+ spec.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;
+#if 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;
+ spec.prec = count;
+ goto pr_tail;
+ }
+out2:
+ ;
+#endif
+ spec.prec = 1;
+ goto pr_tail;
+ case 's':
+ need_format = false;
+ parse_next_arg();
+ arg = force_string(arg);
+ if (spec.fw == 0 && ! spec.have_prec)
+ spec.prec = arg->stlen;
+ else {
+ char_count = mbc_char_count(arg->stptr,
arg->stlen);
+ if (! spec.have_prec || spec.prec > char_count)
+ spec.prec = char_count;
+ }
+ cp = arg->stptr;
+ pr_tail:
+ if (! spec.lj)
+ pr_fill(& spec, outb);
+
+ copy_count = spec.prec;
+ if (spec.fw == 0 && ! spec.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,
spec.prec);
+ }
+
+ bchunk(outb, cp, copy_count);
+ pr_fill(& spec, outb);
+
+ s0 = s1;
+ break;
+
+ case 'X':
+ /* FALL THROUGH */
+ case 'x':
+ spec.base += 6;
+ /* FALL THROUGH */
+ case 'u':
+ spec.base += 2;
+ /* FALL THROUGH */
+ case 'o':
+ spec.base += 8;
+ goto fmt1;
+ 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':
+ case 'd':
+ case 'i':
+ fmt1:
+ need_format = false;
+ parse_next_arg();
+ (void) force_number(arg);
+ spec.fmtchar = cs1;
+ if (format_number_printf(arg, & spec, outb) < 0)
+ goto out;
+
+ s0 = s1;
+ break;
+
+ default:
+ if (do_lint && isalpha(cs1))
+ lintwarn(_("ignoring unknown format specifier character `%c': no
argument converted"), cs1);
+ break;
+ }
+ if (toofew) {
+ 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(outb, s0, s1 - s0);
+ r = buf2node(outb);
+out:
+ free_fmt_buf(outb);
+
+ if (r == NULL)
+ gawk_exit(EXIT_FATAL);
+ return r;
+}
+
diff --git a/double.c b/double.c
index 985d4f4..b5b7458 100644
--- a/double.c
+++ b/double.c
@@ -28,6 +28,8 @@
#include "random.h"
#include "floatmagic.h" /* definition of isnan */
+#include "format.h"
+
/* Can declare these, since we always use the random shipped with gawk */
extern char *initstate(unsigned long seed, char *state, long n);
extern char *setstate(char *state);
@@ -52,12 +54,14 @@ static NODE *force_awknum(NODE *);
static NODE *format_awknum_val(const char *, int, NODE *);
static unsigned long awknum_toulong(const NODE *);
static long awknum_tolong(const NODE *);
-static AWKNUM awknum_todouble(const NODE *);
+static double awknum_todouble(const NODE *);
static uintmax_t awknum_touintmax_t(const NODE *);
static int awknum_sgn(const NODE *);
-static bool awknum_is_integer(const NODE *);
+static bool awknum_isinteger(const NODE *);
+static bool awknum_isnan(const NODE *);
+static bool awknum_isinf(const NODE *);
static NODE *awknum_copy(const NODE *);
-static NODE *format_nodes_awknum(const char *, size_t, NODE **, long);
+static int format_awknum_printf(NODE *, struct format_spec *, struct
print_fmt_buf *);
static bool awknum_init(bltin_t **);
static NODE *awknum_add(const NODE *, const NODE *);
static NODE *awknum_sub(const NODE *, const NODE *);
@@ -107,9 +111,11 @@ numbr_handler_t awknum_hndlr = {
negate_awknum,
cmp_awknums,
awknum_sgn,
- awknum_is_integer,
+ awknum_isinteger,
+ awknum_isnan,
+ awknum_isinf,
format_awknum_val,
- format_nodes_awknum,
+ format_awknum_printf,
awknum_todouble,
awknum_tolong,
awknum_toulong,
@@ -184,7 +190,7 @@ awknum_tolong(const NODE *n)
/* awknum_todouble --- conversion to AWKNUM */
-static AWKNUM
+static double
awknum_todouble(const NODE *n)
{
return n->numbr;
@@ -206,16 +212,33 @@ awknum_sgn(const NODE *n)
return (n->numbr < 0.0 ? -1 : n->numbr > 0.0);
}
-/* awknum_is_integer --- check if a number is an integer */
+/* awknum_isinteger --- check if a number is an integer */
static bool
-awknum_is_integer(const NODE *n)
+awknum_isinteger(const NODE *n)
{
if (isnan(n->numbr) || isinf(n->numbr))
return false;
return double_to_int(n->numbr) == n->numbr;
}
+/* awknum_isnan --- check if number is NaN */
+
+static bool
+awknum_isnan(const NODE *n)
+{
+ return isnan(n->numbr);
+}
+
+/* awknum_isinf --- check if number is infinity */
+
+static bool
+awknum_isinf(const NODE *n)
+{
+ return isinf(n->numbr);
+}
+
+
/* negate_awknum --- negate AWKNUM in NODE */
static void
@@ -1256,835 +1279,232 @@ do_strtonum(int nargs)
return make_awknum(d);
}
+/* format_awknum_printf --- format a number for (s)printf */
-/*
- * format_tree() 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.
- */
-
-static NODE *
-format_nodes_awknum(
- const char *fmt_string,
- size_t n0,
- NODE **the_args,
- long num_args)
+static int
+format_awknum_printf(NODE *arg, struct format_spec *spec, struct print_fmt_buf
*outb)
{
-/* 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); \
-}
+ AWKNUM tmpval;
+ uintmax_t uval;
+ bool sgn;
+ int i, ii, jj;
+ char *chp, *cp;
+ char cs1;
+ int nc;
+
+ static char stackbuf[64]; /* temporary buffer for integer
formatting */
+ static char *intbuf = stackbuf;
+ size_t intbuf_size = 64;
+
+# define CP cpbuf_start(outb)
+# define CEND cpbuf_end(outb)
+# define CPBUF cpbuf(outb)
+
+
+ tmpval = arg->numbr;
+ spec->fill = space_string;
+ spec->chbuf = lchbuf;
+
+ cp = CP;
+ cs1 = spec->fmtchar;
+ switch (cs1) {
+ case 'd':
+ case 'i':
+ if (isnan(tmpval) || isinf(tmpval))
+ goto out_of_range;
+ else
+ tmpval = double_to_int(tmpval);
-/* 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; \
-}
+ /*
+ * ``The result of converting a zero value with a
+ * precision of zero is no characters.''
+ */
+ if (spec->have_prec && spec->prec == 0 && tmpval == 0) {
+ pr_num_tail(cp, spec->prec, spec, outb);
+ return 0;
+ }
-/* 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; \
-}
+ if (tmpval < 0) {
+ tmpval = -tmpval;
+ sgn = true;
+ } else {
+ if (tmpval == -0.0) /* avoid printing -0 */
+ tmpval = 0.0;
+ sgn = false;
+ }
- size_t cur_arg = 0;
- NODE *r = NULL;
- int i, nc;
- bool toofew = false;
- char *obuf, *obufout;
- size_t osiz, ofre;
- const char *chbuf;
- const char *s0, *s1;
- int cs1;
- NODE *arg;
- long fw, prec, argnum;
- bool used_dollar;
- bool lj, alt, big_flag, bigbig_flag, small_flag, have_prec, need_format;
- long *cur = NULL;
- uintmax_t uval;
- bool sgn;
- int base;
- /*
- * 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 = 0.0;
- char signchar = '\0';
- size_t len;
- bool zero_flag = false;
- bool 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;
+ /*
+ * Use snprintf return value to tell if there
+ * is enough room in the buffer or not.
+ */
+ while ((i = snprintf(intbuf, intbuf_size, "%.0f", tmpval)) >=
intbuf_size) {
+ if (intbuf == stackbuf)
+ intbuf = NULL;
+ intbuf_size = i + 1;
+ erealloc(intbuf, char *, intbuf_size, "format_tree");
}
- }
- /*
- * 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)
+ if (i < 1)
+ goto out_of_range;
- /*
- * 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) { \
- msg(_("fatal: must use `count$' on all formats or
none")); \
- goto out; \
- } \
- arg = the_args[argnum]; \
- } else if (used_dollar) { \
- 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++; \
- } \
-}
+ chp = & intbuf[i-1];
+ ii = jj = 0;
+ do {
+ tmpbuf_prepend(outb, *chp);
+ chp--; i--;
+#if defined(HAVE_LOCALE_H)
+ if (spec->quote_flag && loc.grouping[ii] && ++jj ==
loc.grouping[ii]) {
+ if (i) /* only add if more digits coming */
+ tmpbuf_prepend(outb,
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)
+ spec->quote_flag= false;
+ else {
+ ii++;
+ jj = 0;
+ }
+ }
+#endif
+ } while (i > 0);
- need_format = false;
- used_dollar = false;
- s0 = s1 = fmt_string;
- while (n0-- > 0) {
- if (*s1 != '%') {
- s1++;
- continue;
+ /* add more output digits to match the precision */
+ if (spec->have_prec) {
+ while (CEND - CP < spec->prec)
+ tmpbuf_prepend(outb, '0');
}
- need_format = true;
- bchunk(s0, s1 - s0);
- s0 = s1;
- cur = &fw;
- fw = 0;
- prec = 0;
- base = 0;
- argnum = 0;
- base = 0;
- have_prec = false;
- signchar = '\0';
- zero_flag = false;
- quote_flag = false;
- lj = alt = big_flag = bigbig_flag = small_flag = 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) {
- msg(_("fatal: `$' is not permitted in awk
formats"));
- goto out;
- }
- if (cur == &fw) {
- argnum = fw;
- fw = 0;
- used_dollar = true;
- if (argnum <= 0) {
- msg(_("fatal: arg count with `$' must
be > 0"));
- goto out;
- }
- if (argnum >= num_args) {
- msg(_("fatal: arg count %ld greater
than total number of supplied arguments"), argnum);
- goto out;
- }
- } else {
- msg(_("fatal: `$' not permitted after period in
format"));
- goto out;
- }
+ if (sgn)
+ tmpbuf_prepend(outb, '-');
+ else if (spec->signchar)
+ tmpbuf_prepend(outb, spec->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 (! spec->lj
+ && ((spec->zero_flag && ! spec->have_prec)
+ || (spec->fw == 0 && spec->have_prec)))
+ spec->fill = zero_string;
+ if (spec->prec > spec->fw)
+ spec->fw = spec->prec;
+ spec->prec = CEND - CP;
+ if (spec->fw > spec->prec && ! spec->lj && spec->fill !=
space_string
+ && (*CP == '-' || spec->signchar)) {
+ bchunk_one(outb, CP);
+ CP++;
+ spec->prec--;
+ spec->fw--;
+ }
+ cp = CP;
- goto retry;
- case '*':
- if (cur == NULL)
- break;
- if (! do_traditional && isdigit((unsigned char) *s1)) {
- int val = 0;
+ pr_num_tail(CP, spec->prec, spec, outb);
+ return 0;
- for (; n0 > 0 && *s1 && isdigit((unsigned char)
*s1); s1++, n0--) {
- val *= 10;
- val += *s1 - '0';
- }
- if (*s1 != '$') {
- 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();
- }
- (void) force_number(arg);
- *cur = get_number_si(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_flag)
- break;
- else {
- static bool warned = false;
-
- if (do_lint && ! warned) {
- lintwarn(_("`l' is meaningless in awk
formats; ignored"));
- warned = true;
- }
- if (do_posix) {
- msg(_("fatal: `l' is not permitted in
POSIX awk formats"));
- goto out;
- }
- }
- big_flag = true;
- goto retry;
- case 'L':
- if (bigbig_flag)
- break;
- else {
- static bool warned = false;
-
- if (do_lint && ! warned) {
- lintwarn(_("`L' is meaningless in awk
formats; ignored"));
- warned = true;
- }
- if (do_posix) {
- msg(_("fatal: `L' is not permitted in
POSIX awk formats"));
- goto out;
- }
- }
- bigbig_flag = true;
- goto retry;
- case 'h':
- if (small_flag)
- break;
- else {
- static bool warned = false;
-
- if (do_lint && ! warned) {
- lintwarn(_("`h' is meaningless in awk
formats; ignored"));
- warned = true;
- }
- if (do_posix) {
- msg(_("fatal: `h' is not permitted in
POSIX awk formats"));
- goto out;
- }
- }
- small_flag = 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) != 0) {
- uval = get_number_uj(arg);
-#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;
-#if 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();
- (void) force_number(arg);
- tmpval = arg->numbr;
-
- /*
- * Check for Nan or Inf.
- */
- if (isnan(tmpval) || isinf(tmpval))
+ case 'X':
+ spec->chbuf = Uchbuf;
+ /* FALL THROUGH */
+ case 'x':
+ /* FALL THROUGH */
+ case 'u':
+ /* FALL THROUGH */
+ case 'o':
+ /*
+ * ``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 (! spec->alt && spec->have_prec && spec->prec == 0 && tmpval
== 0) {
+ pr_num_tail(cp, spec->prec, spec, outb);
+ return 0;
+ }
+
+ if (tmpval < 0) {
+ uval = (uintmax_t) (intmax_t) tmpval;
+ if ((AWKNUM)(intmax_t) uval != double_to_int(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)
+ } else {
+ uval = (uintmax_t) tmpval;
+ if ((AWKNUM) uval != double_to_int(tmpval))
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();
- (void) force_number(arg);
- tmpval = arg->numbr;
-
- /*
- * ``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;
- }
+ /* spec->fmtchar = cs1; */
+ format_nondecimal(uval, spec, outb);
+ return 0;
- /*
- * 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);
+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 fmt1;
- /* 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 fmt1;
-
- case 'F':
+ case 'F':
#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1
- cs1 = 'f';
- /* FALL THROUGH */
+ cs1 = 'f';
+ /* FALL THROUGH */
#endif
- case 'g':
- case 'G':
- case 'e':
- case 'f':
- case 'E':
- need_format = false;
- parse_next_arg();
- (void) force_number(arg);
- tmpval = arg->numbr;
- fmt1:
- if (! have_prec)
- prec = DEFAULT_G_PRECISION;
-
- chksize(fw + prec + 11); /* 11 == slop */
- cp = cpbuf;
- *cp++ = '%';
- if (lj)
- *cp++ = '-';
- if (signchar)
- *cp++ = signchar;
- if (alt)
- *cp++ = '#';
- if (zero_flag)
- *cp++ = '0';
- if (quote_flag)
- *cp++ = '\'';
+ case 'g':
+ case 'G':
+ case 'e':
+ case 'f':
+ case 'E':
+fmt1:
+ if (! spec->have_prec)
+ spec->prec = DEFAULT_G_PRECISION;
+
+ chksize(outb, spec->fw + spec->prec + 11); /* 11 == slop */
+ cp = CPBUF; /* XXX --- using the temporary prepend-buffer
and
+ * we know it has enough room (>=11).
+ */
+ *cp++ = '%';
+ if (spec->lj)
+ *cp++ = '-';
+ if (spec->signchar)
+ *cp++ = spec->signchar;
+ if (spec->alt)
+ *cp++ = '#';
+ if (spec->zero_flag)
+ *cp++ = '0';
+ if (spec->quote_flag)
+ *cp++ = '\'';
#if defined(LC_NUMERIC)
- if (quote_flag && ! use_lc_numeric)
- setlocale(LC_NUMERIC, "");
+ if (spec->quote_flag && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "");
#endif
- sprintf(cp, "*.*%c", cs1);
- while ((nc = snprintf(obufout, ofre, cpbuf,
- (int) fw, (int) prec,
- (double) tmpval)) >= ofre)
- chksize(nc)
+ sprintf(cp, "*.*%c", cs1);
+ while ((nc = snprintf(buf_end(outb), buf_space(outb), CPBUF,
+ (int) spec->fw, (int) spec->prec, tmpval)) >=
buf_space(outb))
+ chksize(outb, nc + 1);
#if defined(LC_NUMERIC)
- if (quote_flag && ! use_lc_numeric)
- setlocale(LC_NUMERIC, "C");
+ if (spec->quote_flag && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "C");
#endif
- 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) {
- 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);
+ buf_adjust(outb, nc); /* adjust data and free space in output
buffer */
+ return 0;
+
+ default:
+ cant_happen();
}
- if (r == NULL)
- gawk_exit(EXIT_FATAL);
- return r;
+ return -1;
}
diff --git a/format.h b/format.h
new file mode 100644
index 0000000..1418a8b
--- /dev/null
+++ b/format.h
@@ -0,0 +1,155 @@
+/* format specification */
+
+struct format_spec {
+ int base;
+ long fw;
+ long prec;
+ const char *fill;
+ const char *chbuf;
+ bool lj;
+ bool alt;
+ bool have_prec;
+ bool zero_flag;
+ bool quote_flag;
+ char signchar;
+ char fmtchar;
+};
+
+
+/* struct to manage awk (s)printf formatted string */
+
+struct print_fmt_buf {
+ char *buf; /* beginning of buffer */
+ char *dataend; /* end of current data */
+ size_t bufsize;
+ size_t room_left;
+ bool is_malloced; /* true if this struct is malloc-ed */
+ void (*chksize)(struct print_fmt_buf *, size_t);
+ void (*cpbuf_chksize)(struct print_fmt_buf *);
+
+ /*
+ * temporary buffer: can be used to prepend one character at a time
+ * without overflowing the buffer; used primarily to format
integers.
+ */
+ struct {
+ char *buf; /* beginning of buffer */
+ char *bufend; /* end of buffer */
+ size_t bufsize;
+ char *databegin; /* start of current data */
+ } cpbuf;
+};
+
+extern struct print_fmt_buf *get_fmt_buf(void);
+extern void format_nondecimal(uintmax_t, struct format_spec *, struct
print_fmt_buf *);
+
+# define buf_start(ob) ((ob)->buf)
+# define buf_end(ob) ((ob)->dataend)
+# define buf_space(ob) ((ob)->room_left)
+# define cpbuf_start(ob) ((ob)->cpbuf.databegin)
+# define cpbuf_end(ob) ((ob)->cpbuf.bufend)
+# define cpbuf(ob) ((ob)->cpbuf.buf)
+
+extern char lchbuf[];
+extern char Uchbuf[];
+extern char space_string[];
+extern char zero_string[];
+
+/* chksize --- make room for something LEN big in the output buffer */
+
+static inline void
+chksize(struct print_fmt_buf *outb, size_t len)
+{
+ if (len > outb->room_left)
+ outb->chksize(outb, len);
+}
+
+/* bchunk --- copy LEN bytes from STR checking for space in the process */
+
+static inline void
+bchunk(struct print_fmt_buf *outb, const char *str, size_t len)
+{
+ if (len > 0) {
+ if (len > outb->room_left)
+ outb->chksize(outb, len);
+ outb->dataend = (char *) memcpy(outb->dataend, str, len) + len;
+ outb->room_left -= len;
+ }
+}
+
+/* bchunk_one --- copy one byte from STR checking for space in the process */
+
+static inline void
+bchunk_one(struct print_fmt_buf *outb, const char *str)
+{
+ if (1 > outb->room_left)
+ outb->chksize(outb, 1);
+ *outb->dataend++ = *str;
+ --outb->room_left;
+}
+
+/* buf_adjust --- adjust buffer after appending LEN bytes */
+
+static inline void
+buf_adjust(struct print_fmt_buf *outb, size_t len)
+{
+ assert(len <= outb->room_left);
+ outb->dataend += len;
+ outb->room_left -= len;
+}
+
+/* buf2node --- convert bytes to string NODE */
+
+static inline NODE *
+buf2node(struct print_fmt_buf *outb)
+{
+ NODE *node;
+ node = make_str_node(outb->buf, outb->dataend - outb->buf,
ALREADY_MALLOCED);
+ outb->buf = NULL;
+ return node;
+}
+
+
+/* tmpbuf_prepend --- prepend one byte to temporary buffer */
+
+static inline void
+tmpbuf_prepend(struct print_fmt_buf *outb, char ch)
+{
+ if (outb->cpbuf.databegin == outb->cpbuf.buf)
+ outb->cpbuf_chksize(outb);
+ *--outb->cpbuf.databegin = ch;
+}
+
+/* pr_fill --- fill buffer with current fill character */
+
+static inline void
+pr_fill(struct format_spec *spec, struct print_fmt_buf *outb)
+{
+ while (spec->fw > spec->prec) {
+ bchunk_one(outb, spec->fill);
+ spec->fw--;
+ }
+}
+
+static inline void
+pr_num_tail(const char *cp, size_t copy_count, struct format_spec *spec,
struct print_fmt_buf *outb)
+{
+ if (! spec->lj)
+ pr_fill(spec, outb);
+ bchunk(outb, cp, copy_count);
+ pr_fill(spec, outb);
+}
+
+
+/* free_print_fmt_buf --- free buffer */
+
+static inline void
+free_fmt_buf(struct print_fmt_buf *outb)
+{
+ if (outb->buf != NULL) {
+ efree(outb->buf);
+ outb->buf = NULL;
+ }
+ efree(outb->cpbuf.buf);
+ if (outb->is_malloced)
+ efree(outb);
+}
diff --git a/main.c b/main.c
index 24dbb56..c7e9a89 100644
--- a/main.c
+++ b/main.c
@@ -1549,7 +1549,7 @@ init_numbr_handler(bltin_t **bltins)
cmp_numbers = numbr_hndlr->gawk_cmp_numbers;
str2node = numbr_hndlr->gawk_str2number;
free_number = numbr_hndlr->gawk_free_number;
- format_tree = numbr_hndlr->gawk_format_nodes;
+ format_number_printf = numbr_hndlr->gawk_format_printf;
get_number_d = numbr_hndlr->gawk_todouble;
get_number_si = numbr_hndlr->gawk_tolong;
get_number_ui = numbr_hndlr->gawk_toulong;
diff --git a/mpfr.c b/mpfr.c
index 4697a9f..e3215c9 100644
--- a/mpfr.c
+++ b/mpfr.c
@@ -29,6 +29,8 @@
#include <gmp.h>
#include <mpfr.h>
+#include "format.h"
+
#ifndef MPFR_RNDN
/* for compatibility with MPFR 2.X */
#define MPFR_RNDN GMP_RNDN
@@ -56,12 +58,14 @@ static void mpfp_free_num(NODE *);
static NODE *mpfp_format_val(const char *, int, NODE *);
static unsigned long mpfp_toulong(const NODE *);
static long mpfp_tolong(const NODE *);
-static AWKNUM mpfp_todouble(const NODE *);
+static double mpfp_todouble(const NODE *);
static uintmax_t mpfp_touintmax_t(const NODE *);
static int mpfp_sgn(const NODE *);
-static bool mpfp_is_integer(const NODE *n);
+static bool mpfp_isinteger(const NODE *);
+static bool mpfp_isnan(const NODE *);
+static bool mpfp_isinf(const NODE *);
static NODE *mpfp_copy_number(const NODE *);
-static NODE *mpfp_format_nodes(const char *, size_t, NODE **, long);
+static int mpfp_format_printf(NODE *, struct format_spec *, struct
print_fmt_buf *);
static bool mpfp_init(bltin_t **);
static NODE *mpfp_add(const NODE *, const NODE *);
static NODE *mpfp_sub(const NODE *, const NODE *);
@@ -162,9 +166,11 @@ numbr_handler_t mpfp_hndlr = {
mpfp_negate_num,
mpfp_compare,
mpfp_sgn,
- mpfp_is_integer,
+ mpfp_isinteger,
+ mpfp_isnan,
+ mpfp_isinf,
mpfp_format_val,
- mpfp_format_nodes,
+ mpfp_format_printf,
mpfp_todouble,
mpfp_tolong,
mpfp_toulong,
@@ -275,7 +281,7 @@ mpfp_tolong(const NODE *n)
/* mpfp_todouble --- conversion to AWKNUM */
-static AWKNUM
+static double
mpfp_todouble(const NODE *n)
{
return (n->flags & MPFN) != 0 ? mpfr_get_d(n->qnumbr, ROUND_MODE) :
mpz_get_d(n->qnumbr);
@@ -299,14 +305,31 @@ mpfp_sgn(const NODE *n)
: mpz_sgn(MPZ_T(n->qnumbr));
}
-/* mpfp_is_integer --- check if a number is an integer */
+/* mpfp_isinteger --- check if a number is an integer */
static bool
-mpfp_is_integer(const NODE *n)
+mpfp_isinteger(const NODE *n)
{
return is_mpfp_integer(n) ? true : mpfr_integer_p(n->qnumbr);
}
+/* mpfp_isnan --- check if a number is NaN */
+
+static bool
+mpfp_isnan(const NODE *n)
+{
+ return is_mpfp_float(n) && mpfr_nan_p(MPFR_T(n->qnumbr));
+}
+
+/* mpfp_isinf --- check if a number is infinity */
+
+static bool
+mpfp_isinf(const NODE *n)
+{
+ return is_mpfp_float(n) && mpfr_inf_p(MPFR_T(n->qnumbr));
+}
+
+
/* mpfp_make_node --- allocate a node to store MPFR float or GMP integer */
static NODE *
@@ -573,6 +596,7 @@ mpfp_force_number(NODE *n)
return n;
}
+
/* mpfp_format_val --- format a numeric value based on format */
static NODE *
@@ -587,10 +611,10 @@ mpfp_format_val(const char *format, int index, NODE *s)
if (is_mpfp_integer(s) || mpfr_integer_p(s->qnumbr)) {
/* integral value, use %d */
- r = mpfp_format_nodes("%d", 2, dummy, 2);
+ r = format_tree("%d", 2, dummy, 2);
s->stfmt = -1;
} else {
- r = mpfp_format_nodes(format, fmt_list[index]->stlen, dummy, 2);
+ r = format_tree(format, fmt_list[index]->stlen, dummy, 2);
assert(r != NULL);
s->stfmt = (char) index;
}
@@ -606,6 +630,7 @@ mpfp_format_val(const char *format, int index, NODE *s)
return s;
}
+
/* mpfp_str2node --- create an arbitrary-pecision number from string */
static NODE *
@@ -1752,845 +1777,223 @@ finish:
}
-
extern size_t mbc_byte_count(const char *ptr, size_t numchars);
extern size_t mbc_char_count(const char *ptr, size_t numbytes);
-/*
- * mpfp_format_nodes() 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.
- */
-
-static NODE *
-mpfp_format_nodes(
- 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; \
-}
+/* mpfp_format_prinf --- format a number for (s)printf */
-/* 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; \
-}
+static int
+mpfp_format_printf(NODE *arg, struct format_spec *spec, struct print_fmt_buf
*outb)
+{
+ mpz_ptr zi;
+ mpfr_ptr mf;
+ enum { MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } mpfmt_spec;
- size_t cur_arg = 0;
- NODE *r = NULL;
- int i, nc;
- bool toofew = false;
- char *obuf, *obufout;
- size_t osiz, ofre;
- const char *chbuf;
- const char *s0, *s1;
- int cs1;
- NODE *arg;
- long fw, prec, argnum;
- bool used_dollar;
- bool lj, alt, big_flag, bigbig_flag, small_flag, have_prec, need_format;
- long *cur = NULL;
uintmax_t uval;
bool sgn;
- int base;
- /*
- * 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)];
+ int i, ii, jj;
char *cp;
- const char *fill;
- AWKNUM tmpval = 0.0;
- char signchar = '\0';
- size_t len;
- bool zero_flag = false;
- bool quote_flag = false;
- int ii, jj;
- char *chp;
- size_t copy_count, char_count;
-#ifdef HAVE_MPFR
- mpz_ptr zi;
- mpfr_ptr mf;
-#endif
- enum { MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } fmt_type;
-
- 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) { \
- msg(_("fatal: must use `count$' on all formats or
none")); \
- goto out; \
- } \
- arg = the_args[argnum]; \
- } else if (used_dollar) { \
- 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;
- base = 0;
- argnum = 0;
- base = 0;
- have_prec = false;
- signchar = '\0';
- zero_flag = false;
- quote_flag = false;
-#ifdef HAVE_MPFR
- mf = NULL;
- zi = NULL;
-#endif
- fmt_type = 0;
+ char cs1;
+ int nc;
+ /* const char *chbuf; */
- lj = alt = big_flag = bigbig_flag = small_flag = false;
- fill = sp;
- cp = cend;
- chbuf = lchbuf;
- s1++;
+# define CP cpbuf_start(outb)
+# define CEND cpbuf_end(outb)
+# define CPBUF cpbuf(outb)
-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;
+ spec->fill = space_string;
+ spec->chbuf = lchbuf;
- 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) {
- msg(_("fatal: `$' is not permitted in awk
formats"));
- goto out;
- }
+ cs1 = spec->fmtchar;
+ cp = CP;
- if (cur == &fw) {
- argnum = fw;
- fw = 0;
- used_dollar = true;
- if (argnum <= 0) {
- msg(_("fatal: arg count with `$' must
be > 0"));
- goto out;
- }
- if (argnum >= num_args) {
- msg(_("fatal: arg count %ld greater
than total number of supplied arguments"), argnum);
- goto out;
- }
- } else {
- 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;
+ switch (cs1) {
+ case 'd':
+ case 'i':
+ if (is_mpfp_float(arg))
+ goto mpf0;
+ goto mpz0;
+ case 'X':
+ spec->chbuf = Uchbuf;
+ /* FALL THROUGH */
+ case 'x':
+ /* FALL THROUGH */
+ case 'u':
+ /* FALL THROUGH */
+ case 'o':
+ if (is_mpfp_integer(arg)) {
+mpz0:
+ zi = arg->qnumbr;
- for (; n0 > 0 && *s1 && isdigit((unsigned char)
*s1); s1++, n0--) {
- val *= 10;
- val += *s1 - '0';
- }
- if (*s1 != '$') {
- 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();
- }
- (void) force_number(arg);
- *cur = get_number_si(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_flag)
- break;
- else {
- static bool warned = false;
-
- if (do_lint && ! warned) {
- lintwarn(_("`l' is meaningless in awk
formats; ignored"));
- warned = true;
- }
- if (do_posix) {
- msg(_("fatal: `l' is not permitted in
POSIX awk formats"));
- goto out;
- }
- }
- big_flag = true;
- goto retry;
- case 'L':
- if (bigbig_flag)
- break;
- else {
- static bool warned = false;
-
- if (do_lint && ! warned) {
- lintwarn(_("`L' is meaningless in awk
formats; ignored"));
- warned = true;
- }
- if (do_posix) {
- msg(_("fatal: `L' is not permitted in
POSIX awk formats"));
- goto out;
- }
- }
- bigbig_flag = true;
- goto retry;
- case 'h':
- if (small_flag)
- break;
- else {
- static bool warned = false;
-
- if (do_lint && ! warned) {
- lintwarn(_("`h' is meaningless in awk
formats; ignored"));
- warned = true;
- }
- if (do_posix) {
- msg(_("fatal: `h' is not permitted in
POSIX awk formats"));
- goto out;
- }
- }
- small_flag = 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) != 0) {
- uval = get_number_uj(arg);
-#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);
+ if (cs1 != 'd' && cs1 != 'i') {
+ if (mpz_sgn(zi) <= 0) {
+ /*
+ * Negative value or 0 requires special
handling.
+ * Unlike MPFR, GMP does not allow
conversion
+ * to (u)intmax_t. So we first convert
GMP type to
+ * a MPFR type.
+ */
+ mf = mpz2mpfr(zi, _mpfrval);
+ goto mpf1;
}
- 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;
-#if 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();
- (void) force_number(arg);
-#ifdef HAVE_MPFR
- if (is_mpfp_float(arg))
- goto mpf0;
- else {
- assert(is_mpfp_integer(arg) == true);
- goto mpz0;
+ spec->signchar = '\0'; /* Don't print '+' */
}
-#endif
- 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();
- (void) force_number(arg);
-#ifdef HAVE_MPFR
- if (is_mpfp_integer(arg)) {
-mpz0:
- zi = arg->qnumbr;
-
- if (cs1 != 'd' && cs1 != 'i') {
- if (mpz_sgn(zi) <= 0) {
- /*
- * Negative value or 0 requires
special handling.
- * Unlike MPFR, GMP does not
allow conversion
- * to (u)intmax_t. So we first
convert GMP type to
- * a MPFR type.
- */
- mf = mpz2mpfr(zi, _mpfrval);
- goto mpf1;
- }
- signchar = '\0'; /* Don't print
'+' */
- }
- /* See comments above about when to fill with
zeros */
- zero_flag = (! lj
- && ((zero_flag && !
have_prec)
- || (fw == 0 &&
have_prec)));
+ /* See comments above about when to fill with zeros */
+ spec->zero_flag = (! spec->lj
+ && ((spec->zero_flag && !
spec->have_prec)
+ || (spec->fw == 0 &&
spec->have_prec)));
- fmt_type = have_prec ? MP_INT_WITH_PREC :
MP_INT_WITHOUT_PREC;
- goto fmt0;
+ mpfmt_spec = spec->have_prec ? MP_INT_WITH_PREC :
MP_INT_WITHOUT_PREC;
+ goto fmt0;
- } else {
- assert(is_mpfp_float(arg) == true);
+ } else {
+ assert(is_mpfp_float(arg) == true);
mpf0:
- mf = arg->qnumbr;
- if (! mpfr_number_p(mf)) {
- /* inf or NaN */
- cs1 = 'g';
- fmt_type = MP_FLOAT;
- goto fmt1;
- }
+ mf = arg->qnumbr;
+ if (! mpfr_number_p(mf)) {
+ /* inf or NaN */
+ cs1 = 'g';
+ mpfmt_spec = MP_FLOAT;
+ goto fmt1;
+ }
- if (cs1 != 'd' && cs1 != 'i') {
+ if (cs1 != 'd' && cs1 != 'i') {
mpf1:
- /*
- * The output of printf("%#.0x", 0) is
0 instead of 0x, hence <= in
- * the comparison below.
- */
- if (mpfr_sgn(mf) <= 0) {
- if (! mpfr_fits_intmax_p(mf,
ROUND_MODE)) {
- /* -ve number is too
large */
- cs1 = 'g';
- fmt_type = MP_FLOAT;
- goto fmt1;
- }
-
- tmpval = uval = (uintmax_t)
mpfr_get_sj(mf, ROUND_MODE);
- if (! alt && have_prec && prec
== 0 && tmpval == 0)
- goto pr_tail; /*
printf("%.0x", 0) is no characters */
- goto int0;
+ /*
+ * The output of printf("%#.0x", 0) is 0
instead of 0x, hence <= in
+ * the comparison below.
+ */
+ if (mpfr_sgn(mf) <= 0) {
+ if (! mpfr_fits_intmax_p(mf,
ROUND_MODE)) {
+ /* -ve number is too large */
+ cs1 = 'g';
+ mpfmt_spec = MP_FLOAT;
+ goto fmt1;
}
- signchar = '\0'; /* Don't print
'+' */
- }
- /* See comments above about when to fill with
zeros */
- zero_flag = (! lj
- && ((zero_flag && !
have_prec)
- || (fw == 0 &&
have_prec)));
-
- (void) mpfr_get_z(_mpzval, mf, MPFR_RNDZ);
/* convert to GMP integer */
- fmt_type = have_prec ? MP_INT_WITH_PREC :
MP_INT_WITHOUT_PREC;
- zi = _mpzval;
- goto fmt0;
- }
-#endif
-
-#ifdef HAVE_MPFR
- int0:
-#endif
- /*
- * 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;
+ uval = (uintmax_t) mpfr_get_sj(mf,
ROUND_MODE);
+ if (! spec->alt && spec->have_prec &&
spec->prec == 0 && uval == 0) {
+ /* printf("%.0x", 0) is no
characters */
+ pr_num_tail(cp, 0, spec, outb);
+ return 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--;
+ /* spec->fmtchar = cs1; */
+ /* spec->chbuf = chbuf; */
+ format_nondecimal(uval, spec, outb);
+ return 0;
}
+ spec->signchar = '\0'; /* Don't print '+' */
}
- 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 fmt1;
- 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();
- (void) force_number(arg);
+ /* See comments above about when to fill with zeros */
+ spec->zero_flag = (! spec->lj
+ && ((spec->zero_flag && !
spec->have_prec)
+ || (spec->fw == 0 &&
spec->have_prec)));
+
+ (void) mpfr_get_z(_mpzval, mf, MPFR_RNDZ); /*
convert to GMP integer */
+ mpfmt_spec = spec->have_prec ? MP_INT_WITH_PREC :
MP_INT_WITHOUT_PREC;
+ zi = _mpzval;
+ goto fmt0;
+ }
- if (! is_mpfp_number(arg))
- tmpval = arg->numbr;
-#ifdef HAVE_MPFR
- else if (is_mpfp_float(arg)) {
- mf = arg->qnumbr;
- fmt_type = MP_FLOAT;
- } else {
- /* arbitrary-precision integer, convert to MPFR
float */
- assert(mf == NULL);
- mf = mpz2mpfr(arg->qnumbr, _mpfrval);
- fmt_type = MP_FLOAT;
- }
-#endif
- fmt1:
- if (! have_prec)
- prec = DEFAULT_G_PRECISION;
-#ifdef HAVE_MPFR
- fmt0:
-#endif
- chksize(fw + prec + 11); /* 11 == slop */
- cp = cpbuf;
- *cp++ = '%';
- if (lj)
- *cp++ = '-';
- if (signchar)
- *cp++ = signchar;
- if (alt)
- *cp++ = '#';
- if (zero_flag)
- *cp++ = '0';
- if (quote_flag)
- *cp++ = '\'';
+#if 0
+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 fmt1;
-#if defined(LC_NUMERIC)
- if (quote_flag && ! use_lc_numeric)
- setlocale(LC_NUMERIC, "");
#endif
- switch (fmt_type) {
-#ifdef HAVE_MPFR
- case MP_INT_WITH_PREC:
- sprintf(cp, "*.*Z%c", cs1);
- while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
- (int) fw, (int) prec, zi)) >= ofre)
- chksize(nc)
- break;
- case MP_INT_WITHOUT_PREC:
- sprintf(cp, "*Z%c", cs1);
- while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
- (int) fw, zi)) >= ofre)
- chksize(nc)
- break;
- case MP_FLOAT:
- sprintf(cp, "*.*R*%c", cs1);
- while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
- (int) fw, (int) prec, ROUND_MODE,
mf)) >= ofre)
- chksize(nc)
- break;
+ case 'F':
+#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1
+ cs1 = 'f';
+ /* FALL THROUGH */
#endif
- default:
- sprintf(cp, "*.*%c", cs1);
- while ((nc = snprintf(obufout, ofre, cpbuf,
- (int) fw, (int) prec,
- (double) tmpval)) >= ofre)
- chksize(nc)
- }
+ case 'g':
+ case 'G':
+ case 'e':
+ case 'f':
+ case 'E':
+ if (is_mpfp_float(arg)) {
+ mf = arg->qnumbr;
+ mpfmt_spec = MP_FLOAT;
+ } else {
+ /* arbitrary-precision integer, convert to MPFR float */
+ assert(mf == NULL);
+ mf = mpz2mpfr(arg->qnumbr, _mpfrval);
+ mpfmt_spec = MP_FLOAT;
+ }
+fmt1:
+ if (! spec->have_prec)
+ spec->prec = DEFAULT_G_PRECISION;
+
+fmt0:
+ chksize(outb, spec->fw + spec->prec + 11); /* 11 == slop */
+ cp = CPBUF; /* XXX --- using the temporary prepend-buffer
and
+ * we know it has enough room (>=11).
+ */
+ *cp++ = '%';
+ if (spec->lj)
+ *cp++ = '-';
+ if (spec->signchar)
+ *cp++ = spec->signchar;
+ if (spec->alt)
+ *cp++ = '#';
+ if (spec->zero_flag)
+ *cp++ = '0';
+ if (spec->quote_flag)
+ *cp++ = '\'';
#if defined(LC_NUMERIC)
- if (quote_flag && ! use_lc_numeric)
- setlocale(LC_NUMERIC, "C");
+ if (spec->quote_flag && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "");
#endif
- len = strlen(obufout);
- ofre -= len;
- obufout += len;
- s0 = s1;
+ switch (mpfmt_spec) {
+ case MP_INT_WITH_PREC:
+ sprintf(cp, "*.*Z%c", cs1);
+ while ((nc = mpfr_snprintf(buf_end(outb),
buf_space(outb), CPBUF,
+ (int) spec->fw, (int) spec->prec, zi))
>= buf_space(outb))
+ chksize(outb, nc + 1);
break;
- default:
- if (do_lint && isalpha(cs1))
- lintwarn(_("ignoring unknown format specifier
character `%c': no argument converted"), cs1);
+ case MP_INT_WITHOUT_PREC:
+ sprintf(cp, "*Z%c", cs1);
+ while ((nc = mpfr_snprintf(buf_end(outb),
buf_space(outb), CPBUF,
+ (int) spec->fw, zi)) >= buf_space(outb))
+ chksize(outb, nc + 1);
break;
+ case MP_FLOAT:
+ sprintf(cp, "*.*R*%c", cs1);
+ while ((nc = mpfr_snprintf(buf_end(outb),
buf_space(outb), CPBUF,
+ (int) spec->fw, (int) spec->prec,
ROUND_MODE, mf)) >= buf_space(outb))
+ chksize(outb, nc + 1);
+ break;
+ default:
+ cant_happen();
}
- if (toofew) {
- 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);
+
+#if defined(LC_NUMERIC)
+ if (spec->quote_flag && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "C");
+#endif
+
+ buf_adjust(outb, nc); /* adjust data and free space in output
buffer */
+ return 0;
+
+ default:
+ cant_happen();
}
- if (r == NULL)
- gawk_exit(EXIT_FATAL);
- return r;
+ return -1;
}
+
#else
static bool mpfp_init(bltin_t **bltins);
diff --git a/msg.c b/msg.c
index dd83759..4d67f60 100644
--- a/msg.c
+++ b/msg.c
@@ -91,8 +91,9 @@ err(bool isfatal, const char *s, const char *emsg, va_list
argp)
/*
* fmt_number --- format a number node for use in error messages.
- * N.B: format is awk printf format. MUST NOT throw warning in format
- * tree. "%ld" is BAD, "%d" is Ok!
+ *
+ * N.B: format is awk printf format. MUST NOT generate warning
+ * in format_tree(). "%ld" is BAD, "%d" is OK!
*/
const char *
diff --git a/node.c b/node.c
index 9efd767..55d7710 100644
--- a/node.c
+++ b/node.c
@@ -26,7 +26,7 @@
#include "awk.h"
-NODE *(*format_tree)(const char *, size_t, NODE **, long);
+int (*format_number_printf)(NODE *, struct format_spec *, struct print_fmt_buf
*);
NODE *(*str2node)(char *, char **, int, bool);
NODE *(*make_number)(AWKNUM);
NODE *(*str2number)(NODE *);
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 22 ++
awk.h | 19 +-
builtin.c | 637 +++++++++++++++++++++++++++++++++++++
double.c | 1036 ++++++++++++++-----------------------------------------------
format.h | 155 +++++++++
main.c | 2 +-
mpfr.c | 1011 ++++++++++++-----------------------------------------------
msg.c | 5 +-
node.c | 2 +-
9 files changed, 1267 insertions(+), 1622 deletions(-)
create mode 100644 format.h
hooks/post-receive
--
gawk
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gawk-diffs] [SCM] gawk branch, num-handler, updated. 390353f51f36ca53515630d38b63d6bbb1c4f43d,
John Haque <=