[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[SCM] gawk branch, gawk-5.3-stable, updated. gawk-4.1.0-5497-g21b4a113
From: |
Arnold Robbins |
Subject: |
[SCM] gawk branch, gawk-5.3-stable, updated. gawk-4.1.0-5497-g21b4a113 |
Date: |
Thu, 8 Aug 2024 00:07:41 -0400 (EDT) |
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, gawk-5.3-stable has been updated
via 21b4a1139494fd22530d42ce8dfa784cd7c7c8d2 (commit)
from 891341aadec38fe62f7f4224ca53e20a36614386 (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=21b4a1139494fd22530d42ce8dfa784cd7c7c8d2
commit 21b4a1139494fd22530d42ce8dfa784cd7c7c8d2
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Aug 8 07:06:46 2024 +0300
Squashed commit of the following:
commit 8a88c089d0aab4cb416b36a985cb4fd3367cf6ae
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Aug 6 21:42:45 2024 +0300
Fix some additional printf corner cases and add tests.
commit f76ca80261ac84e129c1b553ff35a0ae0959f524
Merge: c8bfdff2 891341aa
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Mon Aug 5 21:18:07 2024 +0300
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit c8bfdff2b370d7b98c95894ef5290adb416ebedf
Merge: 267d7fce d60aa167
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 30 19:45:26 2024 +0300
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit 267d7fcedb22f44f346777367ac3f6162ebcf01e
Merge: c2a59fef 84d2a184
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sun Jul 28 18:41:17 2024 +0300
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit c2a59fef9a5b8cc315050bfb56305e58d358f325
Merge: e7e229d9 16284502
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Fri Jul 26 11:58:46 2024 +0300
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit e7e229d94e0b7f31f53faa8956888f82bb4dbb97
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 23 22:30:48 2024 +0300
Printf refactoring finished!
commit 7731d9fd9fa4d0a27e15c8da5cbd1e4d3a515b0f
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sun Jul 21 21:37:21 2024 +0300
Start on simplifying the code.
commit 35f3fd8439e6a7d168c3221a4326e0eaef594c70
Merge: 8700b5dd f4b9d38f
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sat Jul 20 22:51:36 2024 +0300
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit 8700b5dd514cc70fc29c3df5c40d2a8935c43307
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sat Jul 20 22:41:52 2024 +0300
More printf fixes.
commit 8d1495bf9b1b502a69e3ce593c4e1bbf30ccff1f
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Wed Jul 17 19:13:18 2024 +0300
Start on simplification.
commit 7fdd89f3b30bd6158d7e5e19966e8b0cb72e0077
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Wed Jul 17 12:00:56 2024 +0300
Update NEWS.
commit 6cb660788af1d2e1a04b00f84c5c4af638a85ab3
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 16 20:51:33 2024 +0300
Continue cleaning up printf code.
commit 74f3b30538985e5d66cf2932cec33ff044170127
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 16 08:08:41 2024 +0300
Considerable code cleanup.
commit 057d4307ed06d0247c8756e1d18312f37e22fe90
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 16 06:17:49 2024 +0300
Passing tests. Cleanup still needed.
commit 3575846a3d015fa7132a398e5c4779d6e623f2d6
Merge: 964a4516 8d55806d
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sun Jul 14 16:04:05 2024 +0300
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit 964a4516d5cf3e7ec4191f3559b0358d6562c762
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sun Jul 14 15:56:05 2024 +0300
Checkpoint commit. -M and all but printf-corners passing.
commit 75f7cf551720c1a240626c10f37972cf3563ddc3
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sun Jul 14 14:37:37 2024 +0300
Checkpiont commit, passing all tests!
commit fd33a7cd1eb2c5372e30d5e79a3164cd2fdc133e
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sat Jul 13 22:21:40 2024 +0300
More printf progress.
commit 8f14d054736a4d1b5daee636dbfebac923ba7fdf
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Jul 11 18:13:10 2024 +0300
Continuing on printf, good progress.
commit 61a8624fdf8827582b00d962f6d6a8ad5a5f5264
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Jul 11 17:47:38 2024 +0300
Update pc/Makefile.tst.
commit 60699f3c1a830a8ba4d5c41a69b7e25639ed27e3
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Jul 11 16:43:19 2024 +0300
More test cases in printf-corners test.
commit a7f5d4361defc621dc32242f4135e5573bcb360d
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Jul 11 13:32:08 2024 +0300
Add new test case, printf-corners.
commit 1cd00fb6721736922f01c19d2455ebe1d5b3950c
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Jul 11 13:25:30 2024 +0300
Continuing to improve printf.
commit a6726a26cc61aaee4043cd3cae3237f46df27c48
Merge: dd7bd9eb d998e324
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Jul 11 12:22:54 2024 +0300
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit dd7bd9eb8b92426bd6134935a20361211f1de3c2
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 9 20:58:41 2024 +0300
More fixes in printf.
commit e7028c03c5ff4dbe05cb83d02706f4752622f600
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 9 20:17:32 2024 +0300
More printf fixes. Checkpoint commit.
commit 7b9e9bca203c92acaa6b3cd107764be319994038
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Sun Jul 7 22:21:31 2024 +0300
Continuing to refactor printf.
commit dde70cc78f2cfc69439c35713575158d14366567
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Fri Jul 5 16:56:21 2024 +0300
Progress with printf. Checkpoint commit.
commit 8d18169d7124cee926d1755e64c6eb5ae3edef20
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 2 21:50:10 2024 +0300
Start refactoring format_tree.
commit 95fc5822f3f0951df7371000e6cf255df0138643
Merge: ecf1b719 888281ff
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Tue Jul 2 03:01:21 2024 -0600
Merge branch 'gawk-5.3-stable' into stable/printf-rework
commit ecf1b719040196b391d0aadae276036c893d97c4
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Thu Jun 27 11:13:37 2024 +0300
Improvement in hsprint test.
commit e3de4360243b7842c849f26d9e9093b25a0fa1fa
Author: Arnold D. Robbins <arnold@skeeve.com>
Date: Wed Jun 26 19:04:24 2024 +0300
Split printf functions into a new file.
diff --git a/ChangeLog b/ChangeLog
index 24d29e4a..358d3274 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,16 +1,122 @@
+2024-08-06 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c (format_unsigned_integer, format_signed_integer):
+ Additional fixes. New added tests pass.
+
2024-07-26 Arnold D. Robbins <arnold@skeeve.com>
* re.c (check_bracket_exp): Make the code actually work.
+2024-07-23 Arnold D. Robbins <arnold@skeeve.com>
+
+ Finish cleaning up the printf refactoring. All tests run, both
+ regular and -M. Test suite is valgrind clean!
+
+ Summary of changes:
+ * printf.c: New file.
+ (format_args): Moved here and renamed from format_tree. Considerably
+ simplified by moving code out into new routines.
+ (struct flags): New struct.
+ (add_thousands, do_printf, do_sprintf, format_nan_inf, mbc_byte_count,
+ mbc_char_count, out_of_range, printf_common, reverse): Moved to here.
+ (add_alt_format, add_plus_or_space_and_fill, adjust_flags,
compute_zero_flag,
+ fill_to_field_width, format_float, format_integer_digits,
+ format_mpg_integer_digits, format_out_of_range, format_signed_integer,
+ format_unsigned_integer, zero_fill_to_precision): New routines.
+
+2024-07-21 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Simplify printing of signed integers; move several
+ common tasks out into separate routines, and use char * instead
+ of const char * to avoid a lot of casting.
+
2024-07-20 Tim Rice <trice@posteo.net>
* io.c (csvscan): Set the sentinel if we found CR-LF.
+2024-07-20 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Continuing fixes and cleanups.
+
+2024-07-17 Arnold D. Robbins <arnold@skeeve.com>
+
+ * NEWS: Updated.
+ * printf.c (fill_to_field_width): New function.
+ (format_out_of_range, format_unsigned_integer): Start to use it.
+
+2024-07-16 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Continue cleaning up code and removing now-dead code.
+
+2024-07-16 Arnold D. Robbins <arnold@skeeve.com>
+
+ * awk.h (format_args): Renamed from format_tree. Remove
+ format_tree_old.
+ * debug.c, main.c, mpfr.c, node.c: Adjusted.
+ * printf.c (format_args): Renamed. Removed all old, factored-out
+ code.
+ (format_tree_old): Removed.
+
+2024-07-16 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Use mpfr_sprintf() just to generate digits.
+ All tests now passing, even with -M.
+
+2024-07-13 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Continued progress against the printf-corners test.
+
+2024-07-11 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Signed and unsigned integer printing and float printing
+ working for current tests. MPFR integer printing also for current
+ tests. A few tests still to go in printf-corners.
+
+2024-07-09 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Signed printing I think is working. Unsigned is better.
+ Checkpoint commit.
+
+2024-07-07 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Signed and unsigned integer printing mostly working now.
+ Still to go: Finish that up, get GMP/MPFR integer printing working.
+
+2024-07-05 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf.c: Lots of cleanup. Floating point format pretty
+ much working. Signed integer printing getting there.
+ Unsigned integer printing still to do.
+
+2024-07-02 Arnold D. Robbins <arnold@skeeve.com>
+
+ * awk.h (format_tree_new, format_tree_old): Add declarations.
+ (format_tree): Temporarily make into a function.
+ * main.c (format_tree): Declare it.
+ (main): Check PRINTF_NEW environment variable and if so,
+ use the new version. Otherwise use the old one.
+ * printf.c (struct flags): Declare.
+ (format_tree_old): The original version.
+ (format_tree_new): The new one, being refactored.
+ (add_thousands): Use the global `loc' variable, not a parameter.
+ static const char *format_integer(NODE *arg, struct flags *flags);
+ (format_mpg_integer, format_float, format_out_of_range,
+ compute_zero_flag): New functions.
+
2024-07-02 Arnold D. Robbins <arnold@skeeve.com>
* re.c (make_regexp): \u escapes also now treated literally
for do_traditional.
+2024-06-26 Arnold D. Robbins <arnold@skeeve.com>
+
+ Start on printf rework.
+
+ * printf.c: New file, with stuff for printf in it.
+ * builtin.c: Move printf functions into printf.c.
+ * awk.h (efwrite): Add declaration.
+ * Makefile.am (base_source): Added printf.c.
+
2024-06-21 Arnold D. Robbins <arnold@skeeve.com>
* io.c (csvscan): Add a missing buffer overrun check. Restores
diff --git a/Makefile.am b/Makefile.am
index 9f5e40c6..a9149e9a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -102,6 +102,7 @@ base_sources = \
msg.c \
node.c \
nonposix.h \
+ printf.c \
profile.c \
protos.h \
re.c \
diff --git a/Makefile.in b/Makefile.in
index 6c92f48a..67b47bb0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -152,9 +152,9 @@ am__objects_1 = array.$(OBJEXT) awkgram.$(OBJEXT)
builtin.$(OBJEXT) \
eval.$(OBJEXT) ext.$(OBJEXT) field.$(OBJEXT) \
floatcomp.$(OBJEXT) gawkapi.$(OBJEXT) gawkmisc.$(OBJEXT) \
int_array.$(OBJEXT) io.$(OBJEXT) main.$(OBJEXT) mpfr.$(OBJEXT) \
- msg.$(OBJEXT) node.$(OBJEXT) profile.$(OBJEXT) re.$(OBJEXT) \
- replace.$(OBJEXT) str_array.$(OBJEXT) symbol.$(OBJEXT) \
- version.$(OBJEXT)
+ msg.$(OBJEXT) node.$(OBJEXT) printf.$(OBJEXT) \
+ profile.$(OBJEXT) re.$(OBJEXT) replace.$(OBJEXT) \
+ str_array.$(OBJEXT) symbol.$(OBJEXT) version.$(OBJEXT)
am_gawk_OBJECTS = $(am__objects_1)
gawk_OBJECTS = $(am_gawk_OBJECTS)
gawk_LDADD = $(LDADD)
@@ -212,10 +212,10 @@ am__depfiles_remade = ./$(DEPDIR)/array.Po
./$(DEPDIR)/awkgram.Po \
./$(DEPDIR)/floatcomp.Po ./$(DEPDIR)/gawkapi.Po \
./$(DEPDIR)/gawkmisc.Po ./$(DEPDIR)/int_array.Po \
./$(DEPDIR)/io.Po ./$(DEPDIR)/main.Po ./$(DEPDIR)/mpfr.Po \
- ./$(DEPDIR)/msg.Po ./$(DEPDIR)/node.Po ./$(DEPDIR)/profile.Po \
- ./$(DEPDIR)/re.Po ./$(DEPDIR)/replace.Po \
- ./$(DEPDIR)/str_array.Po ./$(DEPDIR)/symbol.Po \
- ./$(DEPDIR)/version.Po
+ ./$(DEPDIR)/msg.Po ./$(DEPDIR)/node.Po ./$(DEPDIR)/printf.Po \
+ ./$(DEPDIR)/profile.Po ./$(DEPDIR)/re.Po \
+ ./$(DEPDIR)/replace.Po ./$(DEPDIR)/str_array.Po \
+ ./$(DEPDIR)/symbol.Po ./$(DEPDIR)/version.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -542,6 +542,7 @@ base_sources = \
msg.c \
node.c \
nonposix.h \
+ printf.c \
profile.c \
protos.h \
re.c \
@@ -740,6 +741,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpfr.Po@am__quote@ #
am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msg.Po@am__quote@ #
am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node.Po@am__quote@ #
am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printf.Po@am__quote@ #
am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile.Po@am__quote@ #
am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/re.Po@am__quote@ #
am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/replace.Po@am__quote@ #
am--include-marker
@@ -1166,6 +1168,7 @@ distclean: distclean-recursive
-rm -f ./$(DEPDIR)/mpfr.Po
-rm -f ./$(DEPDIR)/msg.Po
-rm -f ./$(DEPDIR)/node.Po
+ -rm -f ./$(DEPDIR)/printf.Po
-rm -f ./$(DEPDIR)/profile.Po
-rm -f ./$(DEPDIR)/re.Po
-rm -f ./$(DEPDIR)/replace.Po
@@ -1238,6 +1241,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f ./$(DEPDIR)/mpfr.Po
-rm -f ./$(DEPDIR)/msg.Po
-rm -f ./$(DEPDIR)/node.Po
+ -rm -f ./$(DEPDIR)/printf.Po
-rm -f ./$(DEPDIR)/profile.Po
-rm -f ./$(DEPDIR)/re.Po
-rm -f ./$(DEPDIR)/replace.Po
diff --git a/NEWS b/NEWS
index 01f25825..900f06a5 100644
--- a/NEWS
+++ b/NEWS
@@ -26,7 +26,11 @@ Changes from 5.3.0 to 5.3.x
7. The never-documented --nostalgia option has been removed. It was
causing bug reports.
-8. As usual, there have been several minor code cleanups and bug fixes.
+8. The implementation of printf/sprintf has been thoroughly reworked
+ in order to make the code more maintainable and to fix a goodly
+ number of corner cases.
+
+9. As usual, there have been several minor code cleanups and bug fixes.
See the ChangeLog for details.
Changes from 5.2.x to 5.3.0
diff --git a/awk.h b/awk.h
index 7a07f1ef..9997534d 100644
--- a/awk.h
+++ b/awk.h
@@ -1483,6 +1483,8 @@ extern NODE *make_regnode(NODETYPE type, NODE *exp);
extern bool validate_qualified_name(char *token);
/* builtin.c */
extern void efflush(FILE *fp, const char *from, struct redirect *rp);
+extern void efwrite(const void *ptr, size_t size, size_t count, FILE *fp,
const char *from,
+ struct redirect *rp, bool flush);
extern double double_to_int(double d);
extern NODE *do_exp(int nargs);
extern NODE *do_fflush(int nargs);
@@ -1514,7 +1516,7 @@ extern NODE *do_sub(int nargs, unsigned int flags);
extern NODE *call_sub(const char *name, int nargs);
extern NODE *call_match(int nargs);
extern NODE *call_split_func(const char *name, int nargs);
-extern NODE *format_tree(const char *, size_t, NODE **, long);
+extern NODE *format_args(const char *, size_t, NODE **, long);
extern NODE *do_lshift(int nargs);
extern NODE *do_rshift(int nargs);
extern NODE *do_and(int nargs);
diff --git a/builtin.c b/builtin.c
index 275456b3..3c4c8f91 100644
--- a/builtin.c
+++ b/builtin.c
@@ -61,11 +61,6 @@
#define SIZE_MAX ((size_t) -1)
#endif
-#define DEFAULT_G_PRECISION 6
-
-static size_t mbc_byte_count(const char *ptr, size_t numchars);
-static size_t mbc_char_count(const char *ptr, size_t numbytes);
-
/* 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);
@@ -78,7 +73,6 @@ extern NODE **fields_arr;
extern bool output_is_tty;
extern FILE *output_fp;
-static const char *add_thousands(const char *original, struct lconv *loc);
static int gawk_system(const char *command);
#define POP_TWO_SCALARS(s1, s2) \
@@ -156,7 +150,7 @@ efflush(FILE *fp, const char *from, struct redirect *rp)
/* efwrite --- like fwrite, but with error checking */
-static void
+void
efwrite(const void *ptr,
size_t size,
size_t count,
@@ -656,1195 +650,6 @@ do_log(int nargs)
return make_number((AWKNUM) d);
}
-
-#ifdef HAVE_MPFR
-
-/*
- * mpz2mpfr --- convert an arbitrary-precision integer to a float
- * without any loss of precision. The returned value is only
- * good for temporary use.
- */
-
-
-static mpfr_ptr
-mpz2mpfr(mpz_ptr zi)
-{
- size_t prec;
- static mpfr_t mpfrval;
- static bool inited = false;
- int tval;
-
- /* estimate minimum precision for exact conversion */
- prec = mpz_sizeinbase(zi, 2); /* most significant 1 bit position
starting at 1 */
- prec -= (size_t) mpz_scan1(zi, 0); /* least significant 1 bit
index starting at 0 */
- if (prec < MPFR_PREC_MIN)
- prec = MPFR_PREC_MIN;
- else if (prec > MPFR_PREC_MAX)
- prec = MPFR_PREC_MAX;
-
- if (! inited) {
- mpfr_init2(mpfrval, prec);
- inited = true;
- } else
- mpfr_set_prec(mpfrval, prec);
- tval = mpfr_set_z(mpfrval, zi, ROUND_MODE);
- IEEE_FMT(mpfrval, tval);
- return mpfrval;
-}
-#endif
-
-/*
- * 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)
-{
-/* 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, nc;
- bool toofew = false;
- char *obuf, *obufout;
- size_t osiz, ofre, olen_final;
- const char *chbuf;
- const char *s0, *s1;
- int cs1;
- NODE *arg;
- long fw, prec, argnum;
- bool used_dollar;
- bool lj, alt, 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;
- char *nan_inf_val;
- bool magic_posix_flag;
-#ifdef HAVE_MPFR
- mpz_ptr zi;
- mpfr_ptr mf;
-#endif
- enum { MP_NONE = 0, 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";
- static const char bad_modifiers[] = "hjlLtz";
- static bool warned[sizeof(bad_modifiers)-1]; // auto-init to zero
-
- bool modifier_seen[sizeof(bad_modifiers)-1];
-#define modifier_index(c) (strchr(bad_modifiers, c) - bad_modifiers)
-
-#define INITIAL_OUT_SIZE 64
- emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree");
- obufout = obuf;
- osiz = INITIAL_OUT_SIZE;
- ofre = osiz - 1;
-
- 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;
- nan_inf_val = NULL;
-#ifdef HAVE_MPFR
- mf = NULL;
- zi = NULL;
-#endif
- fmt_type = MP_NONE;
-
- lj = alt = false;
- memset(modifier_seen, 0, sizeof(modifier_seen));
- magic_posix_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: argument index with `$'
must be > 0"));
- goto out;
- }
- if (argnum >= num_args) {
- msg(_("fatal: argument index %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 && used_dollar && !
isdigit((unsigned char) *s1)) {
- fatal(_("fatal: must use `count$' on all
formats or none"));
- break; /* silence warnings */
- } else 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--;
- }
- // val could be less than zero if someone
provides a field width
- // so large that it causes integer overflow.
Mainly fuzzers do this,
- // but let's try to be good anyway.
- if (val < 0 || 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 = true;
- }
- 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 = true; /* 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)
- quote_flag = true;
- goto check_pos;
-#else
- goto retry;
-#endif
- case 'h':
- case 'j':
- case 'l':
- case 'L':
- case 't':
- case 'z':
- if (modifier_seen[modifier_index(cs1)])
- break;
- else {
- int ind = modifier_index(cs1);
-
- if (do_lint && ! warned[ind]) {
- lintwarn(_("`%c' is meaningless in awk
formats; ignored"), cs1);
- warned[ind] = true;
- }
- if (do_posix) {
- msg(_("fatal: `%c' is not permitted in
POSIX awk formats"), cs1);
- goto out;
- }
- }
- modifier_seen[modifier_index(cs1)] = true;
- goto retry;
-
- case 'P':
- if (magic_posix_flag)
- break;
- magic_posix_flag = true;
- goto retry;
- case 'c':
- need_format = false;
- parse_next_arg();
- /* user input that looks numeric is numeric */
- fixtype(arg);
- if ((arg->flags & NUMBER) != 0) {
- uval = get_number_uj(arg);
- if (gawk_mb_cur_max > 1) {
- char buf[100];
- wchar_t wc;
- mbstate_t mbs;
- size_t count;
-
- memset(& mbs, 0, sizeof(mbs));
-
- /* handle systems with too small
wchar_t */
- if (sizeof(wchar_t) < 4 && uval >
0xffff) {
- if (do_lint)
- lintwarn(
- _("[s]printf: value %g is too
big for %%c format"),
-
arg->numbr);
-
- goto out0;
- }
-
- wc = uval;
-
- count = wcrtomb(buf, wc, & mbs);
- if (count == 0
- || count == (size_t) -1) {
- if (do_lint)
- lintwarn(
- _("[s]printf: value %g is not a
valid wide character"),
-
arg->numbr);
-
- goto out0;
- }
-
- memcpy(cpbuf, buf, count);
- prec = count;
- cp = cpbuf;
- goto pr_tail;
- }
-out0:
- ;
- /* else,
- fall through */
-
- 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;
- prec = 1;
- /*
- * 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 != (size_t) -1 && count != (size_t)
-2 && count > 0) {
- prec = count;
- /* may need to increase fw so that
padding happens, see pr_tail code */
- if (fw > 0)
- fw += count - 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);
-
- /*
- * Check for Nan or Inf.
- */
- if (out_of_range(arg))
- goto out_of_range;
-#ifdef HAVE_MPFR
- if (is_mpg_float(arg))
- goto mpf0;
- else if (is_mpg_integer(arg))
- goto mpz0;
- else
-#endif
- tmpval = double_to_int(arg->numbr);
-
- /*
- * ``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;
-#if defined(HAVE_LOCALE_H)
- quote_flag = (quote_flag && loc.thousands_sep[0] != 0);
-#endif
- 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 */
- int k;
- const char *ts =
loc.thousands_sep;
-
- for (k = strlen(ts) - 1; k >=
0; k--) {
- PREPEND(ts[k]);
- }
- }
- 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);
-
- if (out_of_range(arg))
- goto out_of_range;
-#ifdef HAVE_MPFR
- if (is_mpg_integer(arg)) {
-mpz0:
- zi = arg->mpg_i;
-
- 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);
- 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)));
-
- fmt_type = have_prec ? MP_INT_WITH_PREC :
MP_INT_WITHOUT_PREC;
- goto fmt0;
-
- } else if (is_mpg_float(arg)) {
-mpf0:
- mf = arg->mpg_numbr;
- if (! mpfr_number_p(mf)) {
- /* inf or NaN */
- cs1 = 'g';
- fmt_type = MP_FLOAT;
- goto fmt1;
- }
-
- 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;
- }
- 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;
- } else
-#endif
- 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;
- }
-#ifdef HAVE_MPFR
- int0:
-#endif
-#if defined(HAVE_LOCALE_H)
- quote_flag = (quote_flag && loc.thousands_sep[0] != 0);
-#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 */
- int k;
- const char *ts =
loc.thousands_sep;
-
- for (k = strlen(ts) - 1; k >=
0; k--) {
- PREPEND(ts[k]);
- }
- }
- 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) {
- if (cs1 == 's') {
- assert(cp == arg->stptr || cp == cpbuf);
- copy_count = mbc_byte_count(arg->stptr,
prec);
- }
- /* prec was set by code for %c */
- /* else
- copy_count = 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,
- * or format NaN and INF values.
- */
- nan_inf_val = format_nan_inf(arg, cs1);
- if (do_posix || magic_posix_flag || nan_inf_val ==
NULL) {
- if (do_lint && ! do_posix && ! magic_posix_flag)
- lintwarn(_("[s]printf: value %g is out
of range for `%%%c' format"),
- (double)
tmpval, cs1);
- tmpval = arg->numbr;
- if (strchr("aAeEfFgG", cs1) == NULL)
- cs1 = 'g';
- goto fmt1;
- } else {
- if (do_lint)
- lintwarn(_("[s]printf: value %s is out
of range for `%%%c' format"),
- nan_inf_val,
cs1);
- bchunk(nan_inf_val, strlen(nan_inf_val));
- s0 = s1;
- break;
- }
-
- 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':
-#if defined(PRINTF_HAS_A_FORMAT) && PRINTF_HAS_A_FORMAT == 1
- case 'A':
- case 'a':
- {
- static bool warned = false;
-
- if (do_lint && tolower(cs1) == 'a' && ! warned) {
- warned = true;
- lintwarn(_("%%%c format is POSIX standard but
not portable to other awks"), cs1);
- }
- }
-#endif
- need_format = false;
- parse_next_arg();
- (void) force_number(arg);
-
- if (! is_mpg_number(arg))
- tmpval = arg->numbr;
-#ifdef HAVE_MPFR
- else if (is_mpg_float(arg)) {
- mf = arg->mpg_numbr;
- fmt_type = MP_FLOAT;
- } else {
- /* arbitrary-precision integer, convert to MPFR
float */
- assert(mf == NULL);
- mf = mpz2mpfr(arg->mpg_i);
- fmt_type = MP_FLOAT;
- }
-#endif
- if (out_of_range(arg))
- goto out_of_range;
-
- 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 defined(LC_NUMERIC)
- if (quote_flag && ! use_lc_numeric)
- setlocale(LC_NUMERIC, "");
-#endif
-
- bool need_to_add_thousands = false;
- 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)) >=
(int) ofre)
- chksize(nc)
- need_to_add_thousands = true;
- break;
- case MP_INT_WITHOUT_PREC:
- sprintf(cp, "*Z%c", cs1);
- while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
- (int) fw, zi)) >= (int) ofre)
- chksize(nc)
- need_to_add_thousands = true;
- break;
- case MP_FLOAT:
- sprintf(cp, "*.*R*%c", cs1);
- while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
- (int) fw, (int) prec, ROUND_MODE,
mf)) >= (int) ofre)
- chksize(nc)
- break;
-#endif
- default:
- if (have_prec || tolower(cs1) != 'a') {
- sprintf(cp, "*.*%c", cs1);
- while ((nc = snprintf(obufout, ofre,
cpbuf,
- (int) fw, (int) prec,
- (double) tmpval)) >= (int)
ofre)
- chksize(nc)
- } else {
- // For %a and %A, use the default
precision if it
- // wasn't supplied by the user.
- sprintf(cp, "*%c", cs1);
- while ((nc = snprintf(obufout, ofre,
cpbuf,
- (int) fw,
- (double) tmpval)) >= (int)
ofre)
- chksize(nc)
- }
- }
-
-#if defined(LC_NUMERIC)
- if (quote_flag && ! use_lc_numeric)
- setlocale(LC_NUMERIC, "C");
-#endif
- len = strlen(obufout);
- if (quote_flag && need_to_add_thousands) {
- const char *new_text = add_thousands(obufout, &
loc);
-
- len = strlen(new_text);
- chksize(len)
- strcpy(obufout, new_text);
- free((void *) new_text);
- }
-
- ofre -= len;
- obufout += len;
- s0 = s1;
- break;
- default:
- if (do_lint && is_alpha(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);
- olen_final = obufout - obuf;
-#define GIVE_BACK_SIZE (INITIAL_OUT_SIZE * 2)
- if (ofre > GIVE_BACK_SIZE)
- erealloc(obuf, char *, olen_final + 1, "format_tree");
- r = make_str_node(obuf, olen_final, 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 (r == NULL)
- gawk_exit(EXIT_FATAL);
- return r;
-}
-
-
-/* printf_common --- common code for sprintf and printf */
-
-static NODE *
-printf_common(int nargs)
-{
- int i;
- NODE *r, *tmp;
-
- assert(nargs > 0 && nargs <= max_args);
- for (i = 1; i <= nargs; i++) {
- tmp = args_array[nargs - i] = POP();
- if (tmp->type == Node_var_array) {
- while (--i > 0)
- DEREF(args_array[nargs - i]);
- fatal(_("attempt to use array `%s' in a scalar
context"), array_vname(tmp));
- }
- }
-
- args_array[0] = force_string(args_array[0]);
- if (do_lint && (fixtype(args_array[0])->flags & STRING) == 0)
- lintwarn(_("%s: received non-string format string argument"),
"printf/sprintf");
- r = format_tree(args_array[0]->stptr, args_array[0]->stlen, args_array,
nargs);
- for (i = 0; i < nargs; i++)
- DEREF(args_array[i]);
- return r;
-}
-
-/* do_sprintf --- perform sprintf */
-
-NODE *
-do_sprintf(int nargs)
-{
- NODE *r;
-
- if (nargs == 0)
- fatal(_("sprintf: no arguments"));
-
- r = printf_common(nargs);
- if (r == NULL)
- gawk_exit(EXIT_FATAL);
- return r;
-}
-
-
-/* do_printf --- perform printf, including redirection */
-
-void
-do_printf(int nargs, int redirtype)
-{
- FILE *fp = NULL;
- NODE *tmp;
- struct redirect *rp = NULL;
- int errflg = 0;
- NODE *redir_exp = NULL;
-
- if (nargs == 0) {
- if (do_traditional) {
- if (do_lint)
- lintwarn(_("printf: no arguments"));
- if (redirtype != 0) {
- redir_exp = TOP();
- if (redir_exp->type != Node_val)
- fatal(_("attempt to use array `%s' in a
scalar context"), array_vname(redir_exp));
- rp = redirect(redir_exp, redirtype, & errflg,
true);
- DEREF(redir_exp);
- decr_sp();
- }
- return; /* bwk accepts it silently */
- }
- fatal(_("printf: no arguments"));
- }
-
- if (redirtype != 0) {
- redir_exp = PEEK(nargs);
- if (redir_exp->type != Node_val)
- fatal(_("attempt to use array `%s' in a scalar
context"), array_vname(redir_exp));
- rp = redirect(redir_exp, redirtype, & errflg, true);
- if (rp != NULL) {
- if ((rp->flag & RED_TWOWAY) != 0 && rp->output.fp ==
NULL) {
- if (is_non_fatal_redirect(redir_exp->stptr,
redir_exp->stlen)) {
- update_ERRNO_int(EBADF);
- return;
- }
- (void) close_rp(rp, CLOSE_ALL);
- fatal(_("printf: attempt to write to closed
write end of two-way pipe"));
- }
- fp = rp->output.fp;
- }
- else if (errflg) {
- update_ERRNO_int(errflg);
- return;
- }
- } else if (do_debug) /* only the debugger can change the default
output */
- fp = output_fp;
- else
- fp = stdout;
-
- tmp = printf_common(nargs);
- if (redir_exp != NULL) {
- DEREF(redir_exp);
- decr_sp();
- }
- if (tmp != NULL) {
- if (fp == NULL) {
- DEREF(tmp);
- return;
- }
- efwrite(tmp->stptr, sizeof(char), tmp->stlen, fp, "printf", rp,
true);
- if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
- rp->output.gawk_fflush(rp->output.fp,
rp->output.opaque);
- DEREF(tmp);
- } else
- gawk_exit(EXIT_FATAL);
-}
-
/* do_sqrt --- do the sqrt function */
NODE *
@@ -4446,63 +3251,6 @@ do_typeof(int nargs)
return make_string(res, strlen(res));
}
-/* mbc_byte_count --- return number of bytes for corresponding numchars
multibyte characters */
-
-static size_t
-mbc_byte_count(const char *ptr, size_t numchars)
-{
- 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;
-}
-
-/* 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)
-{
- 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, &cur_state);
- if (mb_len <= 0)
- return numbytes; /* no valid m.b. char */
-
- while (numbytes > 0) {
- mb_len = mbrlen(ptr, numbytes, &cur_state);
- if (mb_len <= 0)
- break;
- sum++;
- ptr += mb_len;
- numbytes -= mb_len;
- }
-
- return sum;
-}
-
/* sanitize_exit_status --- convert a 16 bit Unix exit status into something
reasonable */
int
@@ -4525,72 +3273,6 @@ sanitize_exit_status(int status)
return ret;
}
-/* out_of_range --- return true if a value is out of range */
-
-bool
-out_of_range(NODE *n)
-{
-#ifdef HAVE_MPFR
- if (is_mpg_integer(n))
- return false;
- else if (is_mpg_float(n))
- return (! mpfr_number_p(n->mpg_numbr));
- else
-#endif
- return (isnan(n->numbr) || isinf(n->numbr));
-}
-
-/* format_nan_inf --- format NaN and INF values */
-
-char *
-format_nan_inf(NODE *n, char format)
-{
- static char buf[100];
- double val = n->numbr;
-
-#ifdef HAVE_MPFR
- if (is_mpg_integer(n))
- return NULL;
- else if (is_mpg_float(n)) {
- if (mpfr_nan_p(n->mpg_numbr)) {
- strcpy(buf, mpfr_signbit(n->mpg_numbr) != 0 ? "-nan" :
"+nan");
-
- goto fmt;
- } else if (mpfr_inf_p(n->mpg_numbr)) {
- strcpy(buf, mpfr_signbit(n->mpg_numbr) ? "-inf" :
"+inf");
-
- goto fmt;
- } else
- return NULL;
- }
- /* else
- fallthrough */
-#endif
-
- if (isnan(val)) {
- strcpy(buf, signbit(val) != 0 ? "-nan" : "+nan");
-
- // fall through to end
- } else if (isinf(val)) {
- strcpy(buf, val < 0 ? "-inf" : "+inf");
-
- // fall through to end
- } else
- return NULL;
-
-#ifdef HAVE_MPFR
-fmt:
-#endif
- if (isupper(format)) {
- int i;
-
- for (i = 0; buf[i] != '\0'; i++)
- buf[i] = toupper(buf[i]);
- }
- return buf;
-}
-
-
/* check_symtab_functab --- check if dest is SYMTAB or FUNCTAB, fatal if so */
void
@@ -4617,88 +3299,6 @@ do_mkbool(int nargs)
return make_bool_node(result);
}
-/* reverse --- reverse the contents of a string in place */
-
-static void
-reverse(char *str)
-{
- int i, j;
- char tmp;
-
- for (i = 0, j = strlen(str) - 1; j > i; i++, j--) {
- tmp = str[i];
- str[i] = str[j];
- str[j] = tmp;
- }
-}
-
-/* add_thousands --- add the thousands separator. Needed for MPFR %d format */
-
-/*
- * Copy the source string into the destination string, backwards,
- * adding the thousands separator at the right points. Then reverse
- * the string when done. This gives us much cleaner code than trying
- * to work through the string backwards. (We tried it, it was yucky.)
- */
-
-static const char *
-add_thousands(const char *original, struct lconv *loc)
-{
- size_t orig_len = strlen(original);
- size_t new_len = orig_len + (orig_len * strlen(loc->thousands_sep)) +
1; // worst case
- char *newbuf;
- char decimal_point = '\0';
- const char *dec = NULL;
- const char *src;
- char *dest;
-
- emalloc(newbuf, char *, new_len, "add_thousands");
- memset(newbuf, '\0', new_len);
-
- src = original + strlen(original) - 1;
- dest = newbuf;
-
- if (loc->decimal_point[0] != '\0') {
- decimal_point = loc->decimal_point[0];
- if ((dec = strchr(original, decimal_point)) != NULL) {
- while (src >= dec)
- *dest++ = *src--;
- }
- }
-
-
- int ii = 0;
- int jj = 0;
- do {
- *dest++ = *src--;
- if (loc->grouping[ii] && ++jj == loc->grouping[ii]) {
- if (src >= original) { /* only add if more digits
coming */
- const char *ts = loc->thousands_sep;
- int k;
-
- for (k = strlen(ts) - 1; k >= 0; k--)
- *dest++ = ts[k];
- }
- if (loc->grouping[ii+1] == 0)
- jj = 0; /* keep using current val in
loc.grouping[ii] */
- else if (loc->grouping[ii+1] == CHAR_MAX) {
- // copy in the rest and be done
- while (src >= original)
- *dest++ = *src--;
- break;
- } else {
- ii++;
- jj = 0;
- }
- }
- } while (src >= original);
-
- *dest++ = '\0';
- reverse(newbuf);
-
- return newbuf;
-}
-
/* gawk_system --- specialized version for gawk */
int
diff --git a/debug.c b/debug.c
index 8c5bee6c..40c785f8 100644
--- a/debug.c
+++ b/debug.c
@@ -5227,7 +5227,7 @@ do_print_f(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
PUSH_BINDING(fatal_tag_stack, fatal_tag, fatal_tag_valid);
if (setjmp(fatal_tag) == 0)
- r = format_tree(tmp[0]->stptr, tmp[0]->stlen, tmp, i);
+ r = format_args(tmp[0]->stptr, tmp[0]->stlen, tmp, i);
else {
/* fatal error, restore exit_val of program */
exit_val = EXIT_SUCCESS;
diff --git a/mpfr.c b/mpfr.c
index 1cb02c11..370f4088 100644
--- a/mpfr.c
+++ b/mpfr.c
@@ -422,16 +422,16 @@ mpg_format_val(const char *format, int index, NODE *s)
return make_string(result, strlen(result));
}
- /* create dummy node for a sole use of format_tree */
+ /* create dummy node for a sole use of format_args */
dummy[1] = s;
oflags = s->flags;
if (is_mpg_integer(s) || mpfr_integer_p(s->mpg_numbr)) {
/* integral value, use %d */
- r = format_tree("%d", 2, dummy, 2);
+ r = format_args("%d", 2, dummy, 2);
s->stfmt = STFMT_UNUSED;
} else {
- r = format_tree(format, fmt_list[index]->stlen, dummy, 2);
+ r = format_args(format, fmt_list[index]->stlen, dummy, 2);
assert(r != NULL);
s->stfmt = index;
}
diff --git a/node.c b/node.c
index 0d8fea72..181152dd 100644
--- a/node.c
+++ b/node.c
@@ -246,16 +246,16 @@ r_format_val(const char *format, int index, NODE *s)
NODE *dummy[2], *r;
unsigned int oflags;
- /* create dummy node for a sole use of format_tree */
+ /* create dummy node for a sole use of format_args */
dummy[1] = s;
oflags = s->flags;
if (val == s->numbr) {
/* integral value, but outside range of %ld, use %.0f */
- r = format_tree("%.0f", 4, dummy, 2);
+ r = format_args("%.0f", 4, dummy, 2);
s->stfmt = STFMT_UNUSED;
} else {
- r = format_tree(format, fmt_list[index]->stlen, dummy,
2);
+ r = format_args(format, fmt_list[index]->stlen, dummy,
2);
assert(r != NULL);
s->stfmt = index;
}
diff --git a/pc/ChangeLog b/pc/ChangeLog
index 3d819829..d8c5b983 100644
--- a/pc/ChangeLog
+++ b/pc/ChangeLog
@@ -1,3 +1,7 @@
+2024-07-11 Arnold D. Robbins <arnold@skeeve.com>
+
+ * Makefile.tst: Regenerated.
+
2024-06-13 Arnold D. Robbins <arnold@skeeve.com>
* Makefile.tst: Regenerated.
diff --git a/pc/Makefile.tst b/pc/Makefile.tst
index 2a15eaa9..3bb4ab3b 100644
--- a/pc/Makefile.tst
+++ b/pc/Makefile.tst
@@ -180,7 +180,7 @@ BASIC_TESTS = \
substr swaplns synerr1 synerr2 synerr3 tailrecurse tradanch \
trailbs tweakfld uninit2 uninit3 uninit4 uninit5 uninitialized \
unterm uparrfs uplus wideidx wideidx2 widesub widesub2 widesub3 \
- match4 strsubscript \
+ match4 strsubscript printf-corners \
widesub4 wjposer1 zero2 zeroe0 zeroflag
UNIX_TESTS = \
@@ -2613,6 +2613,13 @@ strsubscript:
@-AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE:
$$? >>_$@
@-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@
+printf-corners:
+ @echo $@
+ @-AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE:
$$? >>_$@
+ @-if echo "$$GAWK_TEST_ARGS" | egrep -s -e '-M|--bignum' > /dev/null ; \
+ then $(CMP) "$(srcdir)"/$@-mpfr.ok _$@ && rm -f _$@ ; \
+ else $(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ ; fi
+
widesub4:
@echo $@ $(ZOS_FAIL)
@-[ -z "$$GAWKLOCALE" ] && GAWKLOCALE=ENU_USA.1252; export GAWKLOCALE; \
diff --git a/printf.c b/printf.c
new file mode 100644
index 00000000..5c604a92
--- /dev/null
+++ b/printf.c
@@ -0,0 +1,1891 @@
+/*
+ * printf.c - All the code for sprintf/printf.
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991-2024,
+ * 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 "awk.h"
+
+struct flags {
+ bool left_just;
+ bool alt;
+ bool zero;
+ bool space;
+ bool plus;
+ bool quote;
+ bool have_prec;
+ bool magic_posix_flag;
+ char format;
+ int base;
+ int field_width;
+ int precision;
+ bool negative;
+};
+
+extern int max_args;
+extern NODE **args_array;
+extern FILE *output_fp;
+
+#define DEFAULT_G_PRECISION 6
+
+static size_t mbc_byte_count(const char *ptr, size_t numchars);
+static size_t mbc_char_count(const char *ptr, size_t numbytes);
+static void reverse(char *str);
+static bool compute_zero_flag(struct flags *flags);
+static void adjust_flags(struct flags *flags);
+static char *add_thousands(const char *original);
+static char *format_integer_digits(NODE *arg, struct flags *flags, bool
*used_float);
+static char *format_mpg_integer_digits(NODE *arg, struct flags *flags, bool
*used_float);
+static char *format_signed_integer(NODE *arg, struct flags *flags);
+static char *format_unsigned_integer(NODE *arg, struct flags *flags);
+static char *format_float(NODE *arg, struct flags *flags);
+static char *format_out_of_range(NODE *arg, struct flags *flags);
+static char *fill_to_field_width(char *startval, struct flags *flags, int
fill);
+static char *add_plus_or_space_and_fill(char *number_value, struct flags
*flags);
+static char *zero_fill_to_precision(char *number_value, struct flags *flags);
+static char *add_alt_format(char *number_value, struct flags *flags);
+
+#ifdef HAVE_MPFR
+
+/*
+ * mpz2mpfr --- convert an arbitrary-precision integer to a float
+ * without any loss of precision. The returned value is only
+ * good for temporary use.
+ */
+
+
+static mpfr_ptr
+mpz2mpfr(mpz_ptr zi)
+{
+ size_t prec;
+ static mpfr_t mpfrval;
+ static bool inited = false;
+ int tval;
+
+ /* estimate minimum precision for exact conversion */
+ prec = mpz_sizeinbase(zi, 2); /* most significant 1 bit position
starting at 1 */
+ prec -= (size_t) mpz_scan1(zi, 0); /* least significant 1 bit
index starting at 0 */
+ if (prec < MPFR_PREC_MIN)
+ prec = MPFR_PREC_MIN;
+ else if (prec > MPFR_PREC_MAX)
+ prec = MPFR_PREC_MAX;
+
+ if (! inited) {
+ mpfr_init2(mpfrval, prec);
+ inited = true;
+ } else
+ mpfr_set_prec(mpfrval, prec);
+ tval = mpfr_set_z(mpfrval, zi, ROUND_MODE);
+ IEEE_FMT(mpfrval, tval);
+ return mpfrval;
+}
+#endif
+
+/*
+ * format_args() formats arguments of sprintf,
+ * and according to a fmt_string providing a format like in
+ * printf family from C library. Returns a string node whose 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.
+ *
+ * July 2024: Refactored into something (more) manageable.
+ */
+
+NODE *
+format_args(
+ 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_args"); \
+ 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_args"); \
+ 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_args"); \
+ obufout = obuf + olen; \
+ ofre += delta; \
+ osiz += delta; \
+}
+
+ size_t cur_arg = 0;
+ NODE *r = NULL;
+ bool toofew = false;
+ char *obuf, *obufout;
+ size_t osiz, ofre, olen_final;
+ const char *s0, *s1;
+ int cs1;
+ NODE *arg;
+ long fw, prec, argnum;
+ bool used_dollar;
+ bool lj, alt, have_prec, need_format;
+ long *cur = NULL;
+ uintmax_t uval;
+ int base;
+ bool space_flag;
+ bool plus_flag;
+
+ struct {
+ char *buf;
+ size_t bufsize;
+ char stackbuf[30];
+ } cpbuf;
+ char *cend = &cpbuf.stackbuf[sizeof(cpbuf.stackbuf)];
+ char *cp;
+ const char *fill;
+ char signchar = '\0';
+ size_t len;
+ bool zero_flag = false;
+ bool quote_flag = false;
+ size_t copy_count, char_count;
+ bool magic_posix_flag;
+ struct flags flags;
+
+ static const char sp[] = " ";
+ static const char bad_modifiers[] = "DHhjLltwz";
+ static bool warned[sizeof(bad_modifiers)-1]; // auto-init to zero
+
+ bool modifier_seen[sizeof(bad_modifiers)-1];
+#define modifier_index(c) (strchr(bad_modifiers, c) - bad_modifiers)
+ const char *formatted = NULL;
+
+#define INITIAL_OUT_SIZE 64
+ emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_args");
+ obufout = obuf;
+ osiz = INITIAL_OUT_SIZE;
+ ofre = osiz - 1;
+
+ cur_arg = 1;
+
+ cpbuf.bufsize = sizeof(cpbuf.stackbuf);
+ cpbuf.buf = cpbuf.stackbuf;
+
+ /*
+ * 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;
+ space_flag = false;
+ plus_flag = false;
+
+ lj = alt = false;
+ memset(modifier_seen, 0, sizeof(modifier_seen));
+ magic_posix_flag = false;
+ fill = sp;
+ cp = cend;
+ s1++;
+ memset(& flags, 0, sizeof(flags));
+
+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.
+ *
+ * 27 June 2024:
+ * This is still the case. The 2023 standard says you
shouldn't
+ * have anything between the percents.
+ */
+ if (do_lint) {
+ const char *msg = NULL;
+
+ if (fw != 0 && ! 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: argument index with `$'
must be > 0"));
+ goto out;
+ }
+ if (argnum >= num_args) {
+ msg(_("fatal: argument index %ld
greater than total number of supplied arguments"), argnum);
+ goto out;
+ }
+ } else {
+ argnum = prec;
+ prec = 0;
+ used_dollar = true;
+ if (argnum <= 0) {
+ msg(_("fatal: argument index with `$'
must be > 0"));
+ goto out;
+ }
+ if (argnum >= num_args) {
+ msg(_("fatal: argument index %ld
greater than total number of supplied arguments"), argnum);
+ goto out;
+ }
+ }
+
+ goto retry;
+ case '*':
+ if (cur == NULL)
+ break;
+ if (! do_traditional && used_dollar && !
isdigit((unsigned char) *s1)) {
+ fatal(_("fatal: must use `count$' on all
formats or none"));
+ break; /* silence warnings */
+ } else 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--;
+ }
+ // val could be less than zero if someone
provides a field width
+ // so large that it causes integer overflow.
Mainly fuzzers do this,
+ // but let's try to be good anyway.
+ if (val < 0 || 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 = true;
+ }
+ 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 */
+ space_flag = true;
+ if (signchar != '\0')
+ goto check_pos;
+ signchar = cs1;
+ goto check_pos;
+ case '+': /* print '+' or '-' */
+ plus_flag = true;
+ 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 = true; /* 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)
+ quote_flag = true;
+ goto check_pos;
+#else
+ goto retry;
+#endif
+ case 'D':
+ case 'H':
+ case 'h':
+ case 'j':
+ case 'L':
+ case 'l':
+ case 't':
+ case 'w':
+ case 'z':
+ if (modifier_seen[modifier_index(cs1)])
+ break;
+ else {
+ int ind = modifier_index(cs1);
+
+ if (do_lint && ! warned[ind]) {
+ lintwarn(_("`%c' is meaningless in awk
formats; ignored"), cs1);
+ warned[ind] = true;
+ }
+ if (do_posix) {
+ msg(_("fatal: `%c' is not permitted in
POSIX awk formats"), cs1);
+ goto out;
+ }
+ }
+ modifier_seen[modifier_index(cs1)] = true;
+ goto retry;
+
+ case 'P':
+ if (magic_posix_flag)
+ break;
+ magic_posix_flag = true;
+ goto retry;
+ case 'C': // POSIX 2024 extension, undocumented
for now
+ cs1 = 'c';
+ // FALL THROUGH
+ case 'c':
+ need_format = false;
+ parse_next_arg();
+ /* user input that looks numeric is numeric */
+ fixtype(arg);
+ if ((arg->flags & NUMBER) != 0) {
+ uval = get_number_uj(arg);
+ if (gawk_mb_cur_max > 1) {
+ char buf[100];
+ wchar_t wc;
+ mbstate_t mbs;
+ size_t count;
+
+ memset(& mbs, 0, sizeof(mbs));
+
+ /* handle systems with too small
wchar_t */
+ if (sizeof(wchar_t) < 4 && uval >
0xffff) {
+ if (do_lint)
+ lintwarn(
+ _("[s]printf: value %g is too
big for %%c format"),
+
arg->numbr);
+
+ goto out0;
+ }
+
+ wc = uval;
+
+ count = wcrtomb(buf, wc, & mbs);
+ if (count == 0
+ || count == (size_t) -1) {
+ if (do_lint)
+ lintwarn(
+ _("[s]printf: value %g is not a
valid wide character"),
+
arg->numbr);
+
+ goto out0;
+ }
+
+ memcpy(cpbuf.buf, buf, count);
+ prec = count;
+ cp = cpbuf.buf;
+ goto pr_tail;
+ }
+out0:
+ ;
+ /* else,
+ fall through */
+
+ cpbuf.buf[0] = uval;
+ prec = 1;
+ cp = cpbuf.buf;
+ goto pr_tail;
+ }
+ /*
+ * As per POSIX, only output the 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;
+ prec = 1;
+ /*
+ * 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 != (size_t) -1 && count != (size_t)
-2 && count > 0) {
+ prec = count;
+ /* may need to increase fw so that
padding happens, see pr_tail code */
+ if (fw > 0)
+ fw += count - 1;
+ }
+ }
+ goto pr_tail;
+ case 'S': // POSIX 2024 extension, undocumented
for now
+ cs1 = 's';
+ // FALL THROUGH
+ 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;
+ 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) {
+ if (cs1 == 's') {
+ assert(cp == arg->stptr || cp ==
cpbuf.buf);
+ copy_count = mbc_byte_count(arg->stptr,
prec);
+ }
+ /* prec was set by code for %c */
+ /* else
+ copy_count = prec; */
+ }
+ bchunk(cp, copy_count);
+ while (fw > prec) {
+ bchunk_one(fill);
+ fw--;
+ }
+ s0 = s1;
+ break;
+
+ case 'd':
+ case 'i':
+ need_format = false;
+ parse_next_arg();
+ (void) force_number(arg);
+ base = 10;
+
+#define set_flags() \
+ flags.left_just = lj; \
+ flags.alt = alt; \
+ flags.zero = zero_flag; \
+ flags.space = space_flag; \
+ flags.plus = plus_flag; \
+ flags.quote = quote_flag; \
+ flags.have_prec = have_prec; \
+ flags.format = cs1; \
+ flags.base = base; \
+ flags.field_width = fw; \
+ flags.precision = prec; \
+ flags.negative = false
+
+ set_flags();
+ adjust_flags(& flags);
+
+ formatted = format_signed_integer(arg, & flags);
+ len = strlen(formatted);
+ chksize(len)
+ strcpy(obufout, formatted);
+ free((void *) formatted);
+
+ ofre -= len;
+ obufout += len;
+ s0 = s1;
+ break;
+
+ case 'X':
+ 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);
+
+ set_flags();
+ adjust_flags(& flags);
+
+ formatted = format_unsigned_integer(arg, & flags);
+ len = strlen(formatted);
+ chksize(len)
+ strcpy(obufout, formatted);
+ free((void *) formatted);
+
+ ofre -= len;
+ obufout += len;
+ s0 = s1;
+ break;
+
+#if defined(PRINTF_HAS_A_FORMAT) && PRINTF_HAS_A_FORMAT == 1
+ case 'A':
+ case 'a':
+ {
+ static bool warned = false;
+
+ if (do_lint && tolower(cs1) == 'a' && ! warned) {
+ warned = true;
+ lintwarn(_("%%%c format is POSIX standard but
not portable to other awks"), cs1);
+ }
+ base = 6;
+ goto format_float;
+ }
+#endif
+ 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':
+ format_float:
+ base += 10;
+ need_format = false;
+ parse_next_arg();
+ (void) force_number(arg);
+
+ set_flags();
+ adjust_flags(& flags);
+
+ formatted = format_float(arg, & flags);
+ len = strlen(formatted);
+ chksize(len)
+ strcpy(obufout, formatted);
+ free((void *) formatted);
+
+ ofre -= len;
+ obufout += len;
+ s0 = s1;
+ break;
+ default:
+ if (do_lint && is_alpha(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);
+ olen_final = obufout - obuf;
+#define GIVE_BACK_SIZE (INITIAL_OUT_SIZE * 2)
+ if (ofre > GIVE_BACK_SIZE)
+ erealloc(obuf, char *, olen_final + 1, "format_args");
+ r = make_str_node(obuf, olen_final, ALREADY_MALLOCED);
+ obuf = NULL;
+out:
+ if (cpbuf.buf != cpbuf.stackbuf)
+ efree(cpbuf.buf);
+
+ if (obuf != NULL)
+ efree(obuf);
+
+ if (r == NULL)
+ gawk_exit(EXIT_FATAL);
+ return r;
+}
+
+
+/* printf_common --- common code for sprintf and printf */
+
+static NODE *
+printf_common(int nargs)
+{
+ int i;
+ NODE *r, *tmp;
+
+ assert(nargs > 0 && nargs <= max_args);
+ for (i = 1; i <= nargs; i++) {
+ tmp = args_array[nargs - i] = POP();
+ if (tmp->type == Node_var_array) {
+ while (--i > 0)
+ DEREF(args_array[nargs - i]);
+ fatal(_("attempt to use array `%s' in a scalar
context"), array_vname(tmp));
+ }
+ }
+
+ args_array[0] = force_string(args_array[0]);
+ if (do_lint && (fixtype(args_array[0])->flags & STRING) == 0)
+ lintwarn(_("%s: received non-string format string argument"),
"printf/sprintf");
+ r = format_args(args_array[0]->stptr, args_array[0]->stlen, args_array,
nargs);
+ for (i = 0; i < nargs; i++)
+ DEREF(args_array[i]);
+ return r;
+}
+
+/* do_sprintf --- perform sprintf */
+
+NODE *
+do_sprintf(int nargs)
+{
+ NODE *r;
+
+ if (nargs == 0)
+ fatal(_("sprintf: no arguments"));
+
+ r = printf_common(nargs);
+ if (r == NULL)
+ gawk_exit(EXIT_FATAL);
+ return r;
+}
+
+
+/* do_printf --- perform printf, including redirection */
+
+void
+do_printf(int nargs, int redirtype)
+{
+ FILE *fp = NULL;
+ NODE *tmp;
+ struct redirect *rp = NULL;
+ int errflg = 0;
+ NODE *redir_exp = NULL;
+
+ if (nargs == 0) {
+ if (do_traditional) {
+ if (do_lint)
+ lintwarn(_("printf: no arguments"));
+ if (redirtype != 0) {
+ redir_exp = TOP();
+ if (redir_exp->type != Node_val)
+ fatal(_("attempt to use array `%s' in a
scalar context"), array_vname(redir_exp));
+ rp = redirect(redir_exp, redirtype, & errflg,
true);
+ DEREF(redir_exp);
+ decr_sp();
+ }
+ return; /* bwk accepts it silently */
+ }
+ fatal(_("printf: no arguments"));
+ }
+
+ if (redirtype != 0) {
+ redir_exp = PEEK(nargs);
+ if (redir_exp->type != Node_val)
+ fatal(_("attempt to use array `%s' in a scalar
context"), array_vname(redir_exp));
+ rp = redirect(redir_exp, redirtype, & errflg, true);
+ if (rp != NULL) {
+ if ((rp->flag & RED_TWOWAY) != 0 && rp->output.fp ==
NULL) {
+ if (is_non_fatal_redirect(redir_exp->stptr,
redir_exp->stlen)) {
+ update_ERRNO_int(EBADF);
+ return;
+ }
+ (void) close_rp(rp, CLOSE_ALL);
+ fatal(_("printf: attempt to write to closed
write end of two-way pipe"));
+ }
+ fp = rp->output.fp;
+ }
+ else if (errflg) {
+ update_ERRNO_int(errflg);
+ return;
+ }
+ } else if (do_debug) /* only the debugger can change the default
output */
+ fp = output_fp;
+ else
+ fp = stdout;
+
+ tmp = printf_common(nargs);
+ if (redir_exp != NULL) {
+ DEREF(redir_exp);
+ decr_sp();
+ }
+ if (tmp != NULL) {
+ if (fp == NULL) {
+ DEREF(tmp);
+ return;
+ }
+ efwrite(tmp->stptr, sizeof(char), tmp->stlen, fp, "printf", rp,
true);
+ if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
+ rp->output.gawk_fflush(rp->output.fp,
rp->output.opaque);
+ DEREF(tmp);
+ } else
+ gawk_exit(EXIT_FATAL);
+}
+
+/* format_integer_digits --- format just the actual value of an integer.
caller frees return value */
+
+static char *
+format_integer_digits(NODE *arg, struct flags *flags, bool *used_float)
+{
+#define VALUE_SIZE 40
+ char *buf = NULL;
+ size_t buflen;
+ static const char lchbuf[] = "0123456789abcdef";
+ static const char Uchbuf[] = "0123456789ABCDEF";
+ const char *chbuf;
+ char *cp;
+ bool quote_flag = false;
+ uintmax_t uval;
+ int i;
+ double tmpval;
+
+#define growbuffer(buf, buflen, cp) { \
+ erealloc(buf, char *, buflen * 2, "format_integer_xxx"); \
+ cp = buf + buflen; \
+ buflen *= 2; \
+ }
+
+#if defined(HAVE_LOCALE_H)
+ quote_flag = (flags->quote && loc.thousands_sep[0] != '\0');
+#endif
+ emalloc(buf, char *, VALUE_SIZE, "format_integer_digits");
+ buflen = VALUE_SIZE;
+ cp = buf;
+
+ *used_float = false;
+ tmpval = double_to_int(arg->numbr);
+ if (flags->base == 10 && flags->format != 'u') {
+ // signed decimal
+ if (tmpval == -0)
+ tmpval = 0;
+
+ /*
+ * Use snprintf return value to tell if there
+ * is enough room in the buffer or not.
+ */
+ while ((i = snprintf(buf, buflen, "%.0f", tmpval)) >= buflen) {
+ if (i > 0)
+ buflen += (i > buflen) ? i : buflen;
+ else
+ buflen *= 2;
+ assert(buflen > 0);
+ erealloc(buf, char *, buflen, "format_args");
+ }
+ } else {
+ // octal or hex or unsigned decimal
+ chbuf = (flags->format == 'X' ? Uchbuf : lchbuf);
+
+ if (tmpval < 0) {
+ uval = (uintmax_t) (intmax_t) tmpval;
+ if ((AWKNUM)(intmax_t)uval != double_to_int(tmpval)) {
+ flags->format = 'g';
+ free((void *) buf);
+ *used_float = true;
+
+ return format_float(arg, flags);
+ }
+ } else {
+ uval = (uintmax_t) tmpval;
+ if ((AWKNUM)uval != double_to_int(tmpval)) {
+ flags->format = 'g';
+ free((void *) buf);
+ *used_float = true;
+
+ return format_float(arg, flags);
+ }
+ }
+
+ // generate the digits backwards.
+ do {
+ if (cp >= buf + buflen)
+ growbuffer(buf, buflen, cp);
+
+ *cp++ = chbuf[uval % flags->base];
+ uval /= flags->base;
+ } while (uval > 0);
+ *cp = '\0';
+
+ // turn it back around
+ reverse(buf);
+ }
+
+ if (flags->base == 10 && quote_flag) {
+ char *with_commas = add_thousands(buf);
+
+ free((void *) buf);
+ buf = with_commas;
+ }
+
+ return buf;
+}
+
+/* format_signed_integer --- format a signed integer (decimal) value. caller
frees return value */
+
+static char *
+format_signed_integer(NODE *arg, struct flags *flags)
+{
+ char *number_value;
+ size_t val_len;
+ char *buf1 = NULL;
+ char fill = ' ';
+ bool used_float = false;
+
+ if (out_of_range(arg))
+ return format_out_of_range(arg, flags);
+
+ if (is_mpg_integer(arg) || is_mpg_float(arg))
+ number_value = format_mpg_integer_digits(arg, flags, &
used_float);
+ else
+ number_value = format_integer_digits(arg, flags, & used_float);
// just digits, possible leading '-'
+
+ if (used_float)
+ return number_value;
+
+ val_len = strlen(number_value);
+
+ // We now have the initial *integer* decimal value in hand.
+ // If it's decimal, we've added commas if appropriate. If it's negative
+ // and decimal, it has a minus sign.
+
+ // check if negative
+ flags->negative = (number_value[0] == '-');
+
+ // The next step is deal with the rest of the printf flags.
+
+ // add more output digits to match the precision
+ if (flags->have_prec && flags->precision == 0 && is_zero(arg)) {
+ /*
+ * No actual value. But if there is plus or space or wide
+ * field width or left just, we have to handle those.
+ */
+ number_value[0] = '\0'; // value is empty
+
+ return add_plus_or_space_and_fill(number_value, flags);
+ } else if (flags->have_prec &&
+ (val_len < flags->precision ||
+ flags->negative && val_len - 1 < flags->precision)) {
+ buf1 = zero_fill_to_precision(number_value, flags);
+ val_len = strlen(buf1);
+
+ if (val_len < flags->field_width)
+ buf1 = fill_to_field_width(buf1, flags, ' ');
+
+ return buf1;
+ } else if (flags->field_width > val_len) {
+ int fw = flags->field_width;
+ size_t buflen = flags->field_width +
+ (flags->negative || flags->plus || flags->space) + 1;
+ char *cp;
+
+ if (compute_zero_flag(flags))
+ fill = '0';
+
+ reverse(number_value);
+ if (flags->negative) {
+ number_value[val_len - 1] = '\0'; // will restore
later.
+ val_len--;
+ }
+
+ fw -= (flags->negative || flags->space || flags->plus);
+
+ emalloc(buf1, char *, buflen, "format_signed_integer");
+ strcpy(buf1, number_value);
+ free((void *) number_value);
+ cp = buf1 + val_len;
+
+ if (fill == '0')
+ for (; fw > val_len; fw--)
+ *cp++ = fill;
+
+ if (flags->negative)
+ *cp++ = '-';
+ else if (flags->plus)
+ *cp++ = '+';
+ else if (flags->space)
+ *cp++ = ' ';
+
+ *cp = '\0';
+
+ reverse(buf1);
+
+ return fill_to_field_width(buf1, flags, ' ');
+ } else if ((flags->plus || flags->space) && ! flags->negative) {
+ emalloc(buf1, char *, val_len + 2, "format_signed_integer");
+ if (flags->plus) {
+ sprintf(buf1, "+%s", number_value);
+ } else {
+ sprintf(buf1, " %s", number_value);
+ }
+
+ free((void *) number_value);
+ } else
+ buf1 = number_value;
+
+ return buf1;
+}
+
+/* format_unsigned_integer --- format an unsigned integer value. caller frees
return value */
+
+static char *
+format_unsigned_integer(NODE *arg, struct flags *flags)
+{
+ char *number_value;
+ char *buf1 = NULL;
+ char fill = ' ';
+ size_t val_len;
+ bool used_float = false;
+
+ if (out_of_range(arg))
+ return format_out_of_range(arg, flags);
+
+ if (is_mpg_integer(arg) || is_mpg_float(arg))
+ number_value = format_mpg_integer_digits(arg, flags, &
used_float);
+ else
+ number_value = format_integer_digits(arg, flags, & used_float);
// just digits
+
+ if (used_float)
+ return number_value;
+
+ val_len = strlen(number_value);
+
+ // We now have the initial *integer* decimal, octal, or hex value in
hand.
+ // If it's decimal, we've added commas if appropriate.
+
+ // The next step is deal with the rest of the printf flags.
+
+ if (flags->have_prec && flags->precision == 0 && is_zero(arg)
+ && (! flags->alt || flags->base != 8)) {
+ number_value[0] = '\0';
+
+ if (flags->field_width > 0)
+ number_value =
fill_to_field_width(number_value, flags, ' ');
+
+ return number_value;
+ }
+
+ // Add more output digits to match the precision
+ if (flags->have_prec && val_len < flags->precision) {
+ // we ignore flags->alt when there's a precision
+ // also plus and space
+ flags->plus = flags->space = false;
+ buf1 = zero_fill_to_precision(number_value, flags); //
frees number_value
+ val_len = strlen(buf1);
+ number_value = buf1;
+ }
+
+ if (flags->alt && flags->base != 10 && ! is_zero(arg)) {
+ buf1 = add_alt_format(number_value, flags);
+ flags->alt = false;
+ } else {
+ buf1 = number_value;
+ }
+ val_len = strlen(buf1);
+
+ // continue with buf1
+ if (flags->field_width > val_len) {
+ // pad the field if necessary
+ // when there's a precision, field width padding with zeros is
not done.
+ if (compute_zero_flag(flags))
+ fill = '0';
+
+ buf1 = fill_to_field_width(buf1, flags, fill);
+ val_len = strlen(buf1);
+ }
+
+ if (flags->alt && ! is_zero(arg)) {
+ buf1 = add_alt_format(buf1, flags);
+ val_len = strlen(buf1);
+ }
+
+ return buf1;
+}
+
+/* format_out_of_range --- format an out of range value as %g. caller frees
return value */
+
+static char *
+format_out_of_range(NODE *arg, struct flags *flags)
+{
+ /*
+ * out of range - emergency use of %g format,
+ * or format NaN and INF values.
+ */
+
+ // nan_inf_val points to a static buffer, don't free it.
+ const char *nan_inf_val = format_nan_inf(arg, flags->format);
+
+ if (do_posix || flags->magic_posix_flag || nan_inf_val == NULL) {
+ if (do_lint && ! do_posix && ! flags->magic_posix_flag)
+ lintwarn(_("[s]printf: value %g is out of range for
`%%%c' format"),
+ (double) arg->numbr,
+ flags->format);
+
+ if (strchr("aAeEfFgG", flags->format) == NULL)
+ flags->format = 'g';
+
+ return format_float(arg, flags);
+ }
+
+ if (do_lint)
+ lintwarn(_("[s]printf: value %s is out of range for `%%%c'
format"),
+ nan_inf_val, flags->format);
+
+ // A NaN or Inf, deal with a field width, if any
+ size_t len = strlen(nan_inf_val);
+ if (flags->field_width > len) {
+ char *buf = estrdup(nan_inf_val, len);
+
+ buf = fill_to_field_width(buf, flags, ' '); // frees
original
+
+ return buf;
+ }
+
+ return estrdup(nan_inf_val, len);
+}
+
+/* compute_zero_flag --- return true if we want to fill with zeros */
+
+static bool
+compute_zero_flag(struct flags *flags)
+{
+ bool zero_flag;
+
+ /*
+ * 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.
+ */
+ zero_flag = (! flags->left_just
+ && ((flags->zero && ! flags->have_prec)
+ || (flags->field_width == 0 && flags->have_prec)));
+
+ return zero_flag;
+}
+
+/* format_mpg_integer_digits --- format an MPZ or MPFR integer. caller frees
return value */
+
+/*
+ * It seems that mpfr_snprintf() doesn't exactly mimic snprintf() with respect
+ * to all the flags and width and precision. So we instead we simply generate
+ * the digits and let the higher level code handle padding and so on.
+ */
+
+static char *
+format_mpg_integer_digits(NODE *arg, struct flags *flags, bool *used_float)
+{
+#ifdef HAVE_MPFR
+ mpz_ptr zi = NULL;
+ mpfr_ptr mf = NULL;
+ char *buf;
+ size_t buflen;
+ char cpbuf[30];
+ int nc;
+ bool quote_flag = false;
+
+ *used_float = false;
+
+#if defined(HAVE_LOCALE_H)
+ quote_flag = (flags->quote && loc.thousands_sep[0] != '\0');
+#endif
+
+ if (is_zero(arg))
+ return estrdup("0", 1);
+
+ if (is_mpg_integer(arg)) {
+ zi = arg->mpg_i;
+
+ if (flags->format != 'd' && flags->format != '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);
+ goto mpf1;
+ }
+ }
+
+
+ } else if (is_mpg_float(arg)) {
+ mf = arg->mpg_numbr;
+ if (! mpfr_number_p(mf)) {
+ cant_happen("%s", "format_mpg_integer called on nan or
inf");
+ }
+
+ if (flags->format != 'd' && flags->format != '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)) {
+ *used_float = true;
+
+ return format_float(arg, flags);
+ }
+ goto fmt0;
+ }
+ }
+
+ (void) mpfr_get_z(mpzval, mf, MPFR_RNDZ); /* convert to
GMP integer */
+ zi = mpzval;
+ // fall through
+ }
+fmt0:
+ buflen = flags->field_width + flags->precision + 11; /* 11 == slop */
+ emalloc(buf, char *, buflen, "format_mpg_integer");
+
+
+#if defined(LC_NUMERIC)
+ if (quote_flag && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "");
+#endif
+
+ sprintf(cpbuf, "%%Z%c", flags->format);
+ while ((nc = mpfr_snprintf(buf, buflen, cpbuf, zi)) >= (int) buflen) {
+ buflen *= 2;
+ erealloc(buf, char *, buflen, "format_mpg_integer");
+ }
+
+#if defined(LC_NUMERIC)
+ if (quote_flag && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "C");
+#endif
+
+ if (quote_flag && flags->base == 10) {
+ char *new_text = add_thousands(buf);
+
+ free((void *) buf);
+ buf = new_text;
+ }
+
+ return buf;
+#else
+ cant_happen("%s", "trying to format GMP integer");
+#endif
+}
+
+
+/* adjust_flags --- take care of conflicts between flags */
+
+/*
+ * The flag characters and their meanings are:
+ *
+ * - The result of the conversion is left-justified within the field. (It is
+ * right-justified if this flag is not specified.)
+ *
+ * + The result of a signed conversion always begins with a plus or minus
+ * sign. (It begins with a sign only when a value with a negative sign
+ * is converted if this flag is not specified.)
+ *
+ * space If the first character of a signed conversion is not a sign,
+ * or if a signed conversion results in no characters, a space is prefixed
+ * to the result. If the space and + flags both appear, the space flag is
ignored.
+ *
+ * # The result is converted to an "alternative form". For o conversion,
+ * it increases the precision, if and only if necessary, to force the
+ * first digit of the result to be a zero (if the value and precision are
+ * both 0, a single 0 is printed). For b conversion, a nonzero result has
+ * 0b prefixed to it. For the optional B conversion as described below,
+ * a nonzero result has 0B prefixed to it. For x (or X) conversion,
+ * a nonzero result has 0x (or 0X) prefixed to it. For a, A, e, E, f,
+ * F, g, and G conversions, the result of converting a floating-point
+ * number always contains a decimal-point character, even if no digits
+ * follow it. (Normally, a decimal-point character appears in the result of
+ * these conversions only if a digit follows it.) For g and G conversions,
+ * trailing zeros are not removed from the result. For other conversions,
+ * the behavior is undefined.
+ *
+ * 0 For b, d, i, o, u, x, X, a, A, e, E, f, F, g, and G conversions,
+ * leading zeros (following any indication of sign or base) are used to
+ * pad to the field width rather than performing space padding, except
+ * when converting an infinity or NaN. If the 0 and - flags both appear,
+ * the 0 flag is ignored. For d, i, o, u, x, and X conversions, if a
+ * preision is specified, the 0 flag is ignored. For other conversions,
+ * the behavior is undefined.
+ */
+
+static void
+adjust_flags(struct flags *flags)
+{
+ if (flags->base == 0)
+ cant_happen("%s", "flags->base == 0");
+
+ if (flags->base == 10 && strchr("cdisu", flags->format) != NULL)
+ flags->alt = false;
+
+ if (flags->field_width < 0) {
+ flags->field_width = -flags->field_width;
+ flags->left_just = true;
+ }
+
+ if (flags->base != 10) {
+ flags->quote = false;
+ }
+
+ if (flags->plus)
+ flags->space = false;
+
+ if (strchr("diouxX", flags->format) != NULL && flags->have_prec)
+ flags->zero = false;
+}
+
+/* format_float --- format a floating point number. caller frees return value
*/
+
+/*
+ * We let the C and MPFR libraries handle the quote flag, since they
+ * know how to do it for the floating point formats. Thus here we do NOT
+ * set up a separate quote_flag variable based on the contents of the
+ * struct locale loc.
+ */
+
+static char *
+format_float(NODE *arg, struct flags *flags)
+{
+ char *buf;
+ size_t buflen;
+ char *cp;
+ char cpbuf[100];
+
+ double tmpval;
+#ifdef HAVE_MPFR
+ mpfr_ptr mf = NULL;
+#endif
+ int nc;
+ bool mpfr_format = false;
+
+ if (out_of_range(arg))
+ return format_out_of_range(arg, flags);
+
+ (void) force_number(arg);
+
+ if (! is_mpg_number(arg))
+ tmpval = arg->numbr;
+#ifdef HAVE_MPFR
+ else if (is_mpg_float(arg)) {
+ mf = arg->mpg_numbr;
+ mpfr_format = true;
+ } else {
+ /* arbitrary-precision integer, convert to MPFR float */
+ assert(mf == NULL);
+ mf = mpz2mpfr(arg->mpg_i);
+ mpfr_format = true;
+ }
+#endif
+
+ if (! flags->have_prec)
+ flags->precision = DEFAULT_G_PRECISION;
+
+
+ buflen = flags->field_width + flags->precision + 11; /* 11 == slop */
+ emalloc(buf, char *, buflen, "format_float");
+
+ int signchar = '\0';
+ if (flags->plus)
+ signchar = '+';
+ else if (flags->space)
+ signchar = ' ';
+
+ cp = cpbuf;
+ *cp++ = '%';
+ if (flags->left_just)
+ *cp++ = '-';
+ if (signchar)
+ *cp++ = signchar;
+ if (flags->alt)
+ *cp++ = '#';
+ if (flags->zero)
+ *cp++ = '0';
+ if (flags->quote)
+ *cp++ = '\'';
+
+#if defined(LC_NUMERIC)
+ if (flags->quote && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "");
+#endif
+
+ if (mpfr_format) {
+#ifdef HAVE_MPFR
+ sprintf(cp, "*.*R*%c", flags->format);
+ while ((nc = mpfr_snprintf(buf, buflen, cpbuf,
+ flags->field_width, flags->precision, ROUND_MODE,
mf)) >= (int) buflen) {
+ erealloc(buf, char *, buflen * 2, "format_float");
+ buflen *= 2;
+ }
+#else
+ cant_happen("trying to format GMP/MPFR number");
+#endif
+ } else {
+ if (flags->have_prec || tolower(flags->format) != 'a') {
+ sprintf(cp, "*.*%c", flags->format);
+ while ((nc = snprintf(buf, buflen, cpbuf,
+ flags->field_width, flags->precision,
+ (double) tmpval)) >= (int) buflen) {
+ erealloc(buf, char *, buflen * 2,
"format_float");
+ buflen *= 2;
+ }
+ } else {
+ // For %a and %A, use the default precision if it
+ // wasn't supplied by the user.
+ sprintf(cp, "*%c", flags->format);
+ while ((nc = snprintf(buf, buflen, cpbuf,
+ flags->field_width,
+ (double) tmpval)) >= (int) buflen) {
+ erealloc(buf, char *, buflen * 2,
"format_float");
+ buflen *= 2;
+ }
+ }
+ }
+
+#if defined(LC_NUMERIC)
+ if (flags->quote && ! use_lc_numeric)
+ setlocale(LC_NUMERIC, "C");
+#endif
+
+ return buf;
+}
+
+/* mbc_byte_count --- return number of bytes for corresponding numchars
multibyte characters */
+
+static size_t
+mbc_byte_count(const char *ptr, size_t numchars)
+{
+ 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;
+}
+
+/* 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)
+{
+ 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, &cur_state);
+ if (mb_len <= 0)
+ return numbytes; /* no valid m.b. char */
+
+ while (numbytes > 0) {
+ mb_len = mbrlen(ptr, numbytes, &cur_state);
+ if (mb_len <= 0)
+ break;
+ sum++;
+ ptr += mb_len;
+ numbytes -= mb_len;
+ }
+
+ return sum;
+}
+
+/* out_of_range --- return true if a value is out of range */
+
+bool
+out_of_range(NODE *n)
+{
+#ifdef HAVE_MPFR
+ if (is_mpg_integer(n))
+ return false;
+ else if (is_mpg_float(n))
+ return (! mpfr_number_p(n->mpg_numbr));
+ else
+#endif
+ return (isnan(n->numbr) || isinf(n->numbr));
+}
+
+/* format_nan_inf --- format NaN and INF values. return value is to a static
buffer */
+
+char *
+format_nan_inf(NODE *n, char format)
+{
+ static char buf[100];
+ double val = n->numbr;
+ enum possible { PLUS_NAN, MINUS_NAN, PLUS_INF, MINUS_INF, NONE } result
= NONE;
+ static const char *strings[] = { "+nan", "-nan", "+inf", "-inf" };
+
+#ifdef HAVE_MPFR
+ if (is_mpg_integer(n))
+ return NULL;
+ else if (is_mpg_float(n)) {
+ if (mpfr_nan_p(n->mpg_numbr)) {
+ result = (mpfr_signbit(n->mpg_numbr) != 0 ? MINUS_NAN :
PLUS_NAN);
+ } else if (mpfr_inf_p(n->mpg_numbr)) {
+ result = (mpfr_signbit(n->mpg_numbr) != 0 ? MINUS_INF :
PLUS_INF);
+ } else
+ return NULL;
+ }
+ /* else
+ fallthrough */
+#endif
+
+ if (result == NONE) {
+ if (isnan(val)) {
+ result = (signbit(val) != 0 ? MINUS_NAN : PLUS_NAN);
+ } else if (isinf(val)) {
+ result = (val < 0 ? MINUS_INF : PLUS_INF);
+ } else
+ return NULL;
+ }
+
+ strcpy(buf, strings[(int) result]);
+
+ if (isupper(format)) {
+ int i;
+
+ for (i = 0; buf[i] != '\0'; i++)
+ buf[i] = toupper(buf[i]);
+ }
+
+ return buf;
+}
+
+
+/* reverse --- reverse the contents of a string in place */
+
+static void
+reverse(char *str)
+{
+ int i, j;
+ char tmp;
+
+ for (i = 0, j = strlen(str) - 1; j > i; i++, j--) {
+ tmp = str[i];
+ str[i] = str[j];
+ str[j] = tmp;
+ }
+}
+
+/* add_thousands --- add the thousands separator. caller frees the return
value */
+
+/*
+ * Copy the source string into the destination string, backwards,
+ * adding the thousands separator at the right points. Then reverse
+ * the string when done. This gives us much cleaner code than trying
+ * to work through the string backwards. (We tried it, it was yucky.)
+ */
+
+static char *
+add_thousands(const char *original)
+{
+ size_t orig_len = strlen(original);
+ size_t new_len = orig_len + 1;
+ char *newbuf;
+ const char *src;
+ char *dest;
+
+ emalloc(newbuf, char *, new_len, "add_thousands");
+ memset(newbuf, '\0', new_len);
+
+#if defined(HAVE_LOCALE_H)
+ new_len = orig_len + (orig_len * strlen(loc.thousands_sep)) + 1;
// worst case
+ erealloc(newbuf, char *, new_len, "add_thousands");
+ memset(newbuf, '\0', new_len);
+
+ src = original + strlen(original) - 1;
+ dest = newbuf;
+
+ if (loc.decimal_point[0] != '\0') {
+ const char *dec = NULL;
+
+ if ((dec = strchr(original, loc.decimal_point[0])) != NULL) {
+ while (src >= dec)
+ *dest++ = *src--;
+ }
+ }
+
+
+ int ii = 0;
+ int jj = 0;
+ do {
+ *dest++ = *src--;
+ if (loc.grouping[ii] && ++jj == loc.grouping[ii]) {
+ if (src >= original) { /* only add if more digits
coming */
+ const char *ts = loc.thousands_sep;
+ int k;
+
+ for (k = strlen(ts) - 1; k >= 0; k--)
+ *dest++ = ts[k];
+ }
+ if (loc.grouping[ii+1] == 0)
+ jj = 0; /* keep using current val in
loc.grouping[ii] */
+ else if (loc.grouping[ii+1] == CHAR_MAX) {
+ // copy in the rest and be done
+ while (src >= original)
+ *dest++ = *src--;
+ break;
+ } else {
+ ii++;
+ jj = 0;
+ }
+ }
+ } while (src >= original);
+
+ *dest++ = '\0';
+ reverse(newbuf);
+#else
+ strcpy(newbuf, original);
+#endif
+
+ return newbuf;
+}
+
+/* fill_to_field_width --- handle justification and padding, frees startval if
necessary, caller frees return */
+
+static char *
+fill_to_field_width(char *startval, struct flags *flags, int fill)
+{
+ size_t l = strlen(startval);
+ char *buf;
+ char *cp;
+ int fw = flags->field_width;
+
+ if (l >= fw) // nothing to do
+ return startval;
+
+ emalloc(buf, char *, fw + 1, "fill_to_field_width");
+ cp = buf;
+
+ if (flags->left_just) {
+ strcpy(buf, startval);
+ cp += l;
+ fw -= l;
+ for (; fw > 0; fw--)
+ *cp++ = fill;
+ *cp = '\0';
+ } else {
+ for (; fw > l; fw--)
+ *cp++ = fill;
+ strcpy(cp, startval);
+ }
+
+ free((void *) startval);
+
+ return buf;
+}
+
+/* add_plus_or_space_and_fill --- what it says */
+
+static char *
+add_plus_or_space_and_fill(char *number_value, struct flags *flags)
+{
+ char *buf1;
+ char *cp;
+ size_t buflen;
+ int fw = flags->field_width;
+
+ buflen = flags->field_width + strlen(number_value) +
+ (flags->space || flags->plus || flags->negative) + 1;
+
+ emalloc(buf1, char *, buflen, "add_plus_or_space_and_fill");
+ cp = buf1;
+
+ if (flags->left_just) {
+ if (flags->negative) {
+ *cp++ = '-';
+ fw--;
+ } else if (flags->plus) {
+ *cp++ = '+';
+ fw--;
+ } else if (flags->space) {
+ *cp++ = ' ';
+ fw--;
+ }
+ for (; fw > 0; fw--)
+ *cp++ = ' ';
+ *cp = '\0';
+ } else if (fw > 0 || flags->plus || flags->space) {
+ char final = ' ';
+ if (flags->negative)
+ final = '-';
+ else if (flags->plus)
+ final = '+';
+
+ for (fw--; fw > 0; fw--)
+ *cp++ = ' ';
+ *cp++ = final;
+ *cp = '\0';
+ } else
+ buf1[0] = '\0';
+
+ strcat(buf1, number_value);
+
+ free((void *) number_value);
+
+ return buf1;
+
+}
+
+/* zero_fill_to_precision --- what it says */
+
+static char *
+zero_fill_to_precision(char *number_value, struct flags *flags)
+{
+ char *buf1;
+ char *cp, *src;
+ size_t buflen;
+ int prec = flags->precision;
+ size_t val_len = strlen(number_value);
+
+ buflen = flags->precision + 1; // we know val_len < precision
+
+ emalloc(buf1, char *, buflen, "zero_fill_to_precision");
+ cp = buf1;
+ src = number_value;
+
+ if (flags->negative) {
+ *cp++ = '-';
+ src++;
+ val_len--;
+ } else if (flags->plus) {
+ *cp++ = '+';
+ } else if (flags->space) {
+ *cp++ = ' ';
+ }
+
+ for (; prec > val_len; prec--)
+ *cp++ = '0';
+
+ strcpy(cp, src);
+ free((void *) number_value);
+
+ return buf1;
+}
+
+/* add_alt_format --- deal with zero padding and the alt flag */
+
+static char *
+add_alt_format(char *number_value, struct flags *flags)
+{
+ int fw;
+ bool zero_flag;
+ char *buf, *cp;
+ size_t buflen;
+ size_t val_len = strlen(number_value);
+
+ assert(flags->alt);
+ assert(flags->base != 10);
+
+ zero_flag = compute_zero_flag(flags);
+
+ buflen = flags->field_width;
+ if (buflen < val_len)
+ buflen = val_len;
+ buflen += 3;
+
+ emalloc(buf, char *, buflen, "add_alt_format");
+ cp = buf;
+
+ fw = flags->field_width;
+ if (flags->base == 16) {
+ *cp++ = '0';
+ *cp++ = flags->format;
+ fw -= 2;
+
+ if (zero_flag) {
+ fw -= val_len;
+ for (; fw > 0; fw--)
+ *cp++ = '0';
+ // fall through
+ }
+
+ strcpy(cp, number_value);
+ } else if (number_value[0] != '0') {
+ sprintf(buf, "0%s", number_value);
+ } else {
+ strcpy(buf, number_value);
+ }
+
+ free((void *) number_value);
+
+ return buf;
+}
diff --git a/test/ChangeLog b/test/ChangeLog
index 47ac9661..963bd31e 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,31 @@
+2024-08-06 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf-corners.awk, printf-corners.ok, printf-corners-mpfr: Add
+ six additional test cases. Thanks to Maciej W. Rozycki
+ <macro@redhat.com>.
+
+2024-07-23 Arnold D. Robbins <arnold@skeeve.com>
+
+ * printf-corners.awk, printf-corners.ok, printf-corners-mpfr: Add
+ an additional test case.
+
+2024-07-05 Maciej W. Rozycki <macro@redhat.com>
+
+ * printf-corners.awk, printf-corners.ok, printf-corners-mpfr: Remove
+ cases with "inf" input. Add further cases.
+
+2024-07-11 Arnold D. Robbins <arnold@skeeve.com>
+
+ * Makefile.am (EXTRA_DIST): New test, printf-corners.
+ * printf-corners.awk, printf-corners.ok, printf-corners-mpfr.ok: New
+ files.
+
+2024-06-27 Arnold D. Robbins <arnold@skeeve.com>
+
+ * hsprint.awk: Add headers to output, fix printing of
+ format string.
+ * hsprint.ok: Updated.
+
2024-06-13 Arnold D. Robbins <arnold@skeeve.com>
* Makefile.am (EXTRA_DIST): Add asortsymtab-mpfr.ok.
diff --git a/test/Makefile.am b/test/Makefile.am
index 0f1d5178..878404fc 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1051,6 +1051,9 @@ EXTRA_DIST = \
prdupval.ok \
prec.awk \
prec.ok \
+ printf-corners.awk \
+ printf-corners.ok \
+ printf-corners-mpfr.ok \
printf0.awk \
printf0.ok \
printf1.awk \
@@ -1549,7 +1552,7 @@ BASIC_TESTS = \
substr swaplns synerr1 synerr2 synerr3 tailrecurse tradanch \
trailbs tweakfld uninit2 uninit3 uninit4 uninit5 uninitialized \
unterm uparrfs uplus wideidx wideidx2 widesub widesub2 widesub3 \
- match4 strsubscript \
+ match4 strsubscript printf-corners \
widesub4 wjposer1 zero2 zeroe0 zeroflag
UNIX_TESTS = \
diff --git a/test/Makefile.in b/test/Makefile.in
index 69649f08..c4a054e4 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -1315,6 +1315,9 @@ EXTRA_DIST = \
prdupval.ok \
prec.awk \
prec.ok \
+ printf-corners.awk \
+ printf-corners.ok \
+ printf-corners-mpfr.ok \
printf0.awk \
printf0.ok \
printf1.awk \
@@ -1813,7 +1816,7 @@ BASIC_TESTS = \
substr swaplns synerr1 synerr2 synerr3 tailrecurse tradanch \
trailbs tweakfld uninit2 uninit3 uninit4 uninit5 uninitialized \
unterm uparrfs uplus wideidx wideidx2 widesub widesub2 widesub3 \
- match4 strsubscript \
+ match4 strsubscript printf-corners \
widesub4 wjposer1 zero2 zeroe0 zeroflag
UNIX_TESTS = \
@@ -4430,6 +4433,13 @@ strsubscript:
@-AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE:
$$? >>_$@
@-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@
+printf-corners:
+ @echo $@
+ @-AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE:
$$? >>_$@
+ @-if echo "$$GAWK_TEST_ARGS" | egrep -s -e '-M|--bignum' > /dev/null ; \
+ then $(CMP) "$(srcdir)"/$@-mpfr.ok _$@ && rm -f _$@ ; \
+ else $(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ ; fi
+
widesub4:
@echo $@ $(ZOS_FAIL)
@-[ -z "$$GAWKLOCALE" ] && GAWKLOCALE=en_US.UTF-8; export GAWKLOCALE; \
diff --git a/test/Maketests b/test/Maketests
index 3538d141..cb6992a1 100644
--- a/test/Maketests
+++ b/test/Maketests
@@ -1298,6 +1298,13 @@ strsubscript:
@-AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE:
$$? >>_$@
@-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@
+printf-corners:
+ @echo $@
+ @-AWKPATH="$(srcdir)" $(AWK) -f $@.awk >_$@ 2>&1 || echo EXIT CODE:
$$? >>_$@
+ @-if echo "$$GAWK_TEST_ARGS" | egrep -s -e '-M|--bignum' > /dev/null ; \
+ then $(CMP) "$(srcdir)"/$@-mpfr.ok _$@ && rm -f _$@ ; \
+ else $(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ ; fi
+
widesub4:
@echo $@ $(ZOS_FAIL)
@-[ -z "$$GAWKLOCALE" ] && GAWKLOCALE=en_US.UTF-8; export GAWKLOCALE; \
diff --git a/test/hsprint.awk b/test/hsprint.awk
index facc109d..471d9a89 100644
--- a/test/hsprint.awk
+++ b/test/hsprint.awk
@@ -10,62 +10,73 @@
BEGIN {
- zero = "0";
- alt = "#";
- spc = " ";
- plus = "+";
- just = "-";
- value[0] = 45;
- value[1] = 45;
- value[2] = 45;
- value[3] = 12.678;
- value[4] = 12.678;
- value[5] = 12.678;
- value[6] = "zap";
- value[7] = "*";
- value[8] = -3.4567;
- value[9] = -3.4567;
- value[10]= -3.4567;
- value[11]= -3.4567;
- oper[0] = "5d";
- oper[1] = "5o";
- oper[2] = "5x";
- oper[3] = "7.2f";
- oper[4] = "10.2e";
- oper[5] = "10.4g";
- oper[6] = "5s";
- oper[7] = "5c";
- oper[8] = "7.1G";
- oper[9] = "7.2f";
- oper[10] = "10.2e";
- oper[11] = "10.4g";
+ zero = "0"
+ alt = "#"
+ spc = " "
+ plus = "+"
+ just = "-"
+ value[0] = 45
+ value[1] = 45
+ value[2] = 45
+ value[3] = 12.678
+ value[4] = 12.678
+ value[5] = 12.678
+ value[6] = "zap"
+ value[7] = "*"
+ value[8] = -3.4567
+ value[9] = -3.4567
+ value[10]= -3.4567
+ value[11]= -3.4567
+ oper[0] = "5d"
+ oper[1] = "5o"
+ oper[2] = "5x"
+ oper[3] = "7.2f"
+ oper[4] = "10.2e"
+ oper[5] = "10.4g"
+ oper[6] = "5s"
+ oper[7] = "5c"
+ oper[8] = "7.1G"
+ oper[9] = "7.2f"
+ oper[10] = "10.2e"
+ oper[11] = "10.4g"
+
+ header_fmt[0] = "fl\\fmt|%5s|%5s|%5s|%7s|%10s|%10s|\n"
+ header_fmt[1] = "fl\\fmt|%5s|%5s|%7s|%7s|%10s|%10s|\n"
+ header_sep[0] = "------+-----+-----+-----+-------+----------+----------+\n"
+ header_sep[1] =
"------+-----+-----+-------+-------+----------+----------+\n"
for (r = 0; r < 12; r += 6) {
+ printf(header_fmt[int(r / 6)],
+ oper[r], oper[r+1], oper[r+2], oper[r+3], oper[r+4],
oper[r+5])
+ printf(header_sep[int(r / 6)])
for (j = 2; j > 0; --j) {
for (p = 2; p > 0; --p) {
for (s = 2; s > 0; --s) {
for (a = 2; a > 0; --a) {
for (z = 2; z > 0; --z) {
fmt = "%" substr(just,j,1) substr(plus,p,1) \
- substr(spc,s,1) substr(alt,a,1) substr(zero,z,1);
+ substr(spc,s,1) substr(alt,a,1) substr(zero,z,1)
fstr = sprintf(\
"%6s|%s%s|%s%s|%s%s|%s%s|%s%s|%s%s|\n",
- "%" fmt,
+ # "%" fmt,
+ fmt,
fmt, oper[r],
fmt, oper[r+1],
fmt, oper[r+2],
fmt, oper[r+3],
fmt, oper[r+4],
- fmt, oper[r+5]);
+ fmt, oper[r+5])
+# xstr = fstr; sub("\n", " +++\n", xstr)
+# printf("%s", xstr)
printf(fstr, value[r], value[r+1],
value[r+2], value[r+3],
- value[r+4], value[r+5]);
+ value[r+4], value[r+5])
}
}
}
}
}
- print "";
+ print ""
}
}
diff --git a/test/hsprint.ok b/test/hsprint.ok
index f0a9e9cb..bc68cf41 100644
--- a/test/hsprint.ok
+++ b/test/hsprint.ok
@@ -1,66 +1,70 @@
- %| 45| 55| 2d| 12.68| 1.27e+01| 12.68|
- %0|00045|00055|0002d|0012.68|001.27e+01|0000012.68|
- %#| 45| 055| 0x2d| 12.68| 1.27e+01| 12.68|
- %#0|00045|00055|0x02d|0012.68|001.27e+01|0000012.68|
- % | 45| 55| 2d| 12.68| 1.27e+01| 12.68|
- % 0| 0045|00055|0002d| 012.68| 01.27e+01| 000012.68|
- % #| 45| 055| 0x2d| 12.68| 1.27e+01| 12.68|
- % #0| 0045|00055|0x02d| 012.68| 01.27e+01| 000012.68|
- %+| +45| 55| 2d| +12.68| +1.27e+01| +12.68|
- %+0|+0045|00055|0002d|+012.68|+01.27e+01|+000012.68|
- %+#| +45| 055| 0x2d| +12.68| +1.27e+01| +12.68|
- %+#0|+0045|00055|0x02d|+012.68|+01.27e+01|+000012.68|
- %+ | +45| 55| 2d| +12.68| +1.27e+01| +12.68|
- %+ 0|+0045|00055|0002d|+012.68|+01.27e+01|+000012.68|
- %+ #| +45| 055| 0x2d| +12.68| +1.27e+01| +12.68|
-%+ #0|+0045|00055|0x02d|+012.68|+01.27e+01|+000012.68|
- %-|45 |55 |2d |12.68 |1.27e+01 |12.68 |
- %-0|45 |55 |2d |12.68 |1.27e+01 |12.68 |
- %-#|45 |055 |0x2d |12.68 |1.27e+01 |12.68 |
- %-#0|45 |055 |0x2d |12.68 |1.27e+01 |12.68 |
- %- | 45 |55 |2d | 12.68 | 1.27e+01 | 12.68 |
- %- 0| 45 |55 |2d | 12.68 | 1.27e+01 | 12.68 |
- %- #| 45 |055 |0x2d | 12.68 | 1.27e+01 | 12.68 |
-%- #0| 45 |055 |0x2d | 12.68 | 1.27e+01 | 12.68 |
- %-+|+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
- %-+0|+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
- %-+#|+45 |055 |0x2d |+12.68 |+1.27e+01 |+12.68 |
-%-+#0|+45 |055 |0x2d |+12.68 |+1.27e+01 |+12.68 |
- %-+ |+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
-%-+ 0|+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
-%-+ #|+45 |055 |0x2d |+12.68 |+1.27e+01 |+12.68 |
+fl\fmt| 5d| 5o| 5x| 7.2f| 10.2e| 10.4g|
+------+-----+-----+-----+-------+----------+----------+
+ %| 45| 55| 2d| 12.68| 1.27e+01| 12.68|
+ %0|00045|00055|0002d|0012.68|001.27e+01|0000012.68|
+ %#| 45| 055| 0x2d| 12.68| 1.27e+01| 12.68|
+ %#0|00045|00055|0x02d|0012.68|001.27e+01|0000012.68|
+ % | 45| 55| 2d| 12.68| 1.27e+01| 12.68|
+ % 0| 0045|00055|0002d| 012.68| 01.27e+01| 000012.68|
+ % #| 45| 055| 0x2d| 12.68| 1.27e+01| 12.68|
+ % #0| 0045|00055|0x02d| 012.68| 01.27e+01| 000012.68|
+ %+| +45| 55| 2d| +12.68| +1.27e+01| +12.68|
+ %+0|+0045|00055|0002d|+012.68|+01.27e+01|+000012.68|
+ %+#| +45| 055| 0x2d| +12.68| +1.27e+01| +12.68|
+ %+#0|+0045|00055|0x02d|+012.68|+01.27e+01|+000012.68|
+ %+ | +45| 55| 2d| +12.68| +1.27e+01| +12.68|
+ %+ 0|+0045|00055|0002d|+012.68|+01.27e+01|+000012.68|
+ %+ #| +45| 055| 0x2d| +12.68| +1.27e+01| +12.68|
+ %+ #0|+0045|00055|0x02d|+012.68|+01.27e+01|+000012.68|
+ %-|45 |55 |2d |12.68 |1.27e+01 |12.68 |
+ %-0|45 |55 |2d |12.68 |1.27e+01 |12.68 |
+ %-#|45 |055 |0x2d |12.68 |1.27e+01 |12.68 |
+ %-#0|45 |055 |0x2d |12.68 |1.27e+01 |12.68 |
+ %- | 45 |55 |2d | 12.68 | 1.27e+01 | 12.68 |
+ %- 0| 45 |55 |2d | 12.68 | 1.27e+01 | 12.68 |
+ %- #| 45 |055 |0x2d | 12.68 | 1.27e+01 | 12.68 |
+ %- #0| 45 |055 |0x2d | 12.68 | 1.27e+01 | 12.68 |
+ %-+|+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
+ %-+0|+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
+ %-+#|+45 |055 |0x2d |+12.68 |+1.27e+01 |+12.68 |
+ %-+#0|+45 |055 |0x2d |+12.68 |+1.27e+01 |+12.68 |
+ %-+ |+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
+ %-+ 0|+45 |55 |2d |+12.68 |+1.27e+01 |+12.68 |
+ %-+ #|+45 |055 |0x2d |+12.68 |+1.27e+01 |+12.68 |
%-+ #0|+45 |055 |0x2d |+12.68 |+1.27e+01 |+12.68 |
- %| zap| *| -3| -3.46| -3.46e+00| -3.457|
- %0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
- %#| zap| *| -3.| -3.46| -3.46e+00| -3.457|
- %#0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
- % | zap| *| -3| -3.46| -3.46e+00| -3.457|
- % 0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
- % #| zap| *| -3.| -3.46| -3.46e+00| -3.457|
- % #0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
- %+| zap| *| -3| -3.46| -3.46e+00| -3.457|
- %+0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
- %+#| zap| *| -3.| -3.46| -3.46e+00| -3.457|
- %+#0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
- %+ | zap| *| -3| -3.46| -3.46e+00| -3.457|
- %+ 0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
- %+ #| zap| *| -3.| -3.46| -3.46e+00| -3.457|
-%+ #0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
- %-|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
- %-0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
- %-#|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
- %-#0|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
- %- |zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
- %- 0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
- %- #|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
-%- #0|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
- %-+|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
- %-+0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
- %-+#|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
-%-+#0|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
- %-+ |zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
-%-+ 0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
-%-+ #|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
+fl\fmt| 5s| 5c| 7.1G| 7.2f| 10.2e| 10.4g|
+------+-----+-----+-------+-------+----------+----------+
+ %| zap| *| -3| -3.46| -3.46e+00| -3.457|
+ %0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
+ %#| zap| *| -3.| -3.46| -3.46e+00| -3.457|
+ %#0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
+ % | zap| *| -3| -3.46| -3.46e+00| -3.457|
+ % 0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
+ % #| zap| *| -3.| -3.46| -3.46e+00| -3.457|
+ % #0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
+ %+| zap| *| -3| -3.46| -3.46e+00| -3.457|
+ %+0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
+ %+#| zap| *| -3.| -3.46| -3.46e+00| -3.457|
+ %+#0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
+ %+ | zap| *| -3| -3.46| -3.46e+00| -3.457|
+ %+ 0| zap| *|-000003|-003.46|-03.46e+00|-00003.457|
+ %+ #| zap| *| -3.| -3.46| -3.46e+00| -3.457|
+ %+ #0| zap| *|-00003.|-003.46|-03.46e+00|-00003.457|
+ %-|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %-0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %-#|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
+ %-#0|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
+ %- |zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %- 0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %- #|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
+ %- #0|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
+ %-+|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %-+0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %-+#|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
+ %-+#0|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
+ %-+ |zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %-+ 0|zap |* |-3 |-3.46 |-3.46e+00 |-3.457 |
+ %-+ #|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
%-+ #0|zap |* |-3. |-3.46 |-3.46e+00 |-3.457 |
diff --git a/test/printf-corners-mpfr.ok b/test/printf-corners-mpfr.ok
new file mode 100644
index 00000000..5eb24699
--- /dev/null
+++ b/test/printf-corners-mpfr.ok
@@ -0,0 +1,86 @@
+<0>
+<0>
+<00>
+<000>
+<01>
+<01>
+<01>
+<001>
+<0x01>
+<>
+<+>
+<+>
+<>
+<>
+<>
+<>
+<>
+< >
+< >
+<>
+<>
+<>
+<>
+<>
+<+>
+<+>
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+<0.00000>
+<0.00000>
+<0.e+00>
+<0.E+00>
+<0.>
+<0.>
+<0.>
+<0.>
+<-nan>
+<-NAN>
+<-nan>
+<-NAN>
+<-nan>
+<-NAN>
+<-nan>
+<-NAN>
+<-inf >
+<-INF >
+<-inf >
+<-INF >
+<-inf >
+<-INF >
+<-inf >
+<-INF >
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< 00045>
+<-01>
+<-012>
+<-001>
+<01>
+<01>
+<012>
+<12>
diff --git a/test/printf-corners.awk b/test/printf-corners.awk
new file mode 100644
index 00000000..2dee4abb
--- /dev/null
+++ b/test/printf-corners.awk
@@ -0,0 +1,88 @@
+BEGIN {
+ printf "<%#.o>\n", 0 # 0
+ printf "<%#.1o>\n", 0 # 0
+ printf "<%#.2o>\n", 0 # 00
+ printf "<%#.3o>\n", 0 # 000
+ printf "<%#.o>\n", 1 # 01
+ printf "<%#.1o>\n", 1 # 01
+ printf "<%#.2o>\n", 1 # 01
+ printf "<%#.3o>\n", 1 # 001
+ printf "<%#.2x>\n", 1 # 0x01
+ printf "<%#.x>\n", 0 # ""
+ printf "<%+.d>\n", 0 # "+"
+ printf "<%+.i>\n", 0 # "+"
+ printf "<%+.u>\n", 0 # ""
+ printf "<%+.o>\n", 0 # ""
+ printf "<%+.x>\n", 0 # ""
+ printf "<%+.X>\n", 0 # ""
+ printf "<%+.s>\n", 0 # ""
+ printf "<% .d>\n", 0 # " "
+ printf "<% .i>\n", 0 # " "
+ printf "<% .u>\n", 0 # ""
+ printf "<% .o>\n", 0 # ""
+ printf "<% .x>\n", 0 # ""
+ printf "<% .X>\n", 0 # ""
+ printf "<% .s>\n", 0 # ""
+ printf "<%+1.d>\n", 0 # "+"
+ printf "<%+1.i>\n", 0 # "+"
+ printf "<%+1.u>\n", 0 # " "
+ printf "<%+1.o>\n", 0 # " "
+ printf "<%+1.x>\n", 0 # " "
+ printf "<%+1.X>\n", 0 # " "
+ printf "<%+1.s>\n", 0 # " "
+ printf "<% 1.d>\n", 0 # " "
+ printf "<% 1.i>\n", 0 # " "
+ printf "<% 1.u>\n", 0 # " "
+ printf "<% 1.o>\n", 0 # " "
+ printf "<% 1.x>\n", 0 # " "
+ printf "<% 1.X>\n", 0 # " "
+ printf "<% 1.s>\n", 0 # " "
+ printf "<%#g>\n", "0" # "0.00000"
+ printf "<%#G>\n", "0" # "0.00000"
+ printf "<%#.e>\n", "0" # "0.e+00"
+ printf "<%#.E>\n", "0" # "0.E+00"
+ printf "<%#.f>\n", "0" # "0."
+ printf "<%#.F>\n", "0" # "0."
+ printf "<%#.g>\n", "0" # "0."
+ printf "<%#.G>\n", "0" # "0."
+ printf "<%e>\n", "-nan" # "-nan"
+ printf "<%E>\n", "-nan" # "-NAN"
+ printf "<%f>\n", "-nan" # "-nan"
+ printf "<%F>\n", "-nan" # "-NAN"
+ printf "<%g>\n", "-nan" # "-nan"
+ printf "<%G>\n", "-nan" # "-NAN"
+ printf "<%a>\n", "-nan" # "-nan"
+ printf "<%A>\n", "-nan" # "-NAN"
+ printf "<%-20e>\n", "-inf" # "-inf "
+ printf "<%-20E>\n", "-inf" # "-INF "
+ printf "<%-20f>\n", "-inf" # "-inf "
+ printf "<%-20F>\n", "-inf" # "-INF "
+ printf "<%-20g>\n", "-inf" # "-inf "
+ printf "<%-20G>\n", "-inf" # "-INF "
+ printf "<%-20a>\n", "-inf" # "-inf "
+ printf "<%-20A>\n", "-inf" # "-INF "
+ printf "<%20e>\n", "-inf" # " -inf"
+ printf "<%20E>\n", "-inf" # " -INF"
+ printf "<%20f>\n", "-inf" # " -inf"
+ printf "<%20F>\n", "-inf" # " -INF"
+ printf "<%20g>\n", "-inf" # " -inf"
+ printf "<%20G>\n", "-inf" # " -INF"
+ printf "<%20a>\n", "-inf" # " -inf"
+ printf "<%20A>\n", "-inf" # " -INF"
+ printf "<%20.20e>\n", "-inf" # " -inf"
+ printf "<%20.20E>\n", "-inf" # " -INF"
+ printf "<%20.20f>\n", "-inf" # " -inf"
+ printf "<%20.20F>\n", "-inf" # " -INF"
+ printf "<%20.20g>\n", "-inf" # " -inf"
+ printf "<%20.20G>\n", "-inf" # " -INF"
+ printf "<%20.20a>\n", "-inf" # " -inf"
+ printf "<%20.20A>\n", "-inf" # " -INF"
+ printf "<%3$*2$.*1$d>\n", 5, 8, 45 # " 00045"
+ printf "<%.2i>\n", -1 # "-01"
+ printf "<%.3i>\n", -12 # "-012"
+ printf "<%.3i>\n", -1 # "-001"
+ printf "<%.2i>\n", 1 # "01"
+ printf "<%+.2u>\n", 1 # "01"
+ printf "<% .3u>\n", 12 # "012
+ printf "<%+.2u>\n", 12 # "12"
+}
diff --git a/test/printf-corners.ok b/test/printf-corners.ok
new file mode 100644
index 00000000..5eb24699
--- /dev/null
+++ b/test/printf-corners.ok
@@ -0,0 +1,86 @@
+<0>
+<0>
+<00>
+<000>
+<01>
+<01>
+<01>
+<001>
+<0x01>
+<>
+<+>
+<+>
+<>
+<>
+<>
+<>
+<>
+< >
+< >
+<>
+<>
+<>
+<>
+<>
+<+>
+<+>
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+< >
+<0.00000>
+<0.00000>
+<0.e+00>
+<0.E+00>
+<0.>
+<0.>
+<0.>
+<0.>
+<-nan>
+<-NAN>
+<-nan>
+<-NAN>
+<-nan>
+<-NAN>
+<-nan>
+<-NAN>
+<-inf >
+<-INF >
+<-inf >
+<-INF >
+<-inf >
+<-INF >
+<-inf >
+<-INF >
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< -inf>
+< -INF>
+< 00045>
+<-01>
+<-012>
+<-001>
+<01>
+<01>
+<012>
+<12>
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 106 +++
Makefile.am | 1 +
Makefile.in | 18 +-
NEWS | 6 +-
awk.h | 4 +-
builtin.c | 1402 +-------------------------------
debug.c | 2 +-
mpfr.c | 6 +-
node.c | 6 +-
pc/ChangeLog | 4 +
pc/Makefile.tst | 9 +-
printf.c | 1891 +++++++++++++++++++++++++++++++++++++++++++
test/ChangeLog | 28 +
test/Makefile.am | 5 +-
test/Makefile.in | 12 +-
test/Maketests | 7 +
test/hsprint.awk | 79 +-
test/hsprint.ok | 128 +--
test/printf-corners-mpfr.ok | 86 ++
test/printf-corners.awk | 88 ++
test/printf-corners.ok | 86 ++
21 files changed, 2458 insertions(+), 1516 deletions(-)
create mode 100644 printf.c
create mode 100644 test/printf-corners-mpfr.ok
create mode 100644 test/printf-corners.awk
create mode 100644 test/printf-corners.ok
hooks/post-receive
--
gawk
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [SCM] gawk branch, gawk-5.3-stable, updated. gawk-4.1.0-5497-g21b4a113,
Arnold Robbins <=