Add a new --eval=32|64 command line option, to make it easy to set the
size of eval. If not present, then in --gnu mode, eval remains 32
bits (since eval64 is easy to access and install as an override);
while in -G mode, eval is sized to match 'signed long' as per POSIX
(either 32- or 64-bit, depending on the machine m4 was compiled for).
Testing this ought to be interesting, since this is one case where m4
intentionally behaves differently based on the platform.
* src/m4.h (eval_size): New global.
(define_builtin): Alter signature.
* src/m4.c (usage): Document --eval.
(eval_size, EVAL_SIZE_OPTION, long_options, main): Handle new option.
* src/builtin.c (define_builtin): Add return type.
(builtin_init): Use this to alter default mode of eval.
* doc/m4.texi (Limits control): Document --eval, and its interactions
with --gnu and --traditional.
(Eval): Mention more on default sizing of eval, and add test.
* NEWS: Mention this.
---
NEWS | 7 ++++-
doc/m4.texi | 76 +++++++++++++++++++++++++++++++++++++++++++++++++--
src/builtin.c | 13 +++++++--
src/m4.c | 16 +++++++++++
src/m4.h | 5 ++--
5 files changed, 108 insertions(+), 9 deletions(-)
diff --git a/NEWS b/NEWS
index dd3ea04b..45750173 100644
--- a/NEWS
+++ b/NEWS
@@ -132,7 +132,12 @@ GNU M4 NEWS - User visible changes.
builtin now refuses to recognize `=' as a synonym for `==' (this had
emitted a warning since 1.4.8b).
-** Add a new `eval64' builtin that operates on 64-bit integers.
+** Add a new `eval64' builtin that operates on 64-bit integers. By
+ default, both `eval' and `eval64' are available with `eval' in 32-bit
+ mode; when `m4 -G' is used to hide GNU extensions, only `eval' is
+ available but using the size of `signed long'. The new command-line
+ option `--eval=32' or `--eval=64' can also force the integer size used
+ by `eval'.
** A number of portability improvements inherited from gnulib.
diff --git a/doc/m4.texi b/doc/m4.texi
index b0a35eae..c642a9eb 100644
--- a/doc/m4.texi
+++ b/doc/m4.texi
@@ -807,6 +807,12 @@ Limits control
@code{m4}.
@table @code
+@item --eval @var{size}
+This option forces @code{eval} (@pxref{Eval}) to use the given
+@var{size} of 32 or 64, rather than defaulting to 32 bits under
+@option{--gnu} or to the size of @code{signed long} under
+@option{--traditional}.
+
@item -g
@itemx --gnu
Enable all the extensions in this implementation. In this release of
@@ -827,7 +833,11 @@ Limits control
@item -G
@itemx --traditional
Suppress all the extensions made in this implementation, compared to the
-System V version. @xref{Compatibility}, for a list of these.
+System V version; this mode focuses on POSIX compatibility.
+@xref{Compatibility}, for a list of items that are suppressed. Since
+this mode suppresses @code{eval64}, the @code{eval} macro defaults to
+the size of @code{signed long} unless @option{--eval} is also used to
+pick a specific size.
@item -H @var{num}
@itemx --hashsize=@var{num}
@@ -7371,8 +7381,11 @@ Eval
@result{}
@end example
-If your script wants to use larger math by default, it is easy to
-arrange for that up front with @code{defn}:
+When using M4 with GNU extensions (the default, or when @option{--gnu}
+was specified), @code{eval} starts in 32-bit mode. If your script wants
+to use larger math by default, it is easy to arrange for that up front
+with @code{defn}, while 32-bit mode is still accessible using
+@code{builtin}:
@example
eval(`90000 * 90000')
@@ -7381,8 +7394,65 @@ Eval
@result{}
eval(`90000 * 90000')
@result{}8100000000
+builtin(`eval', `90000 * 90000')
+@result{}-489934592
@end example
+But when using @option{--traditional} mode for maximum POSIX compliance,
+there is no access to either of the extension macros @code{eval64} or
+@code{builtin}, so @code{eval} defaults to using the size of the
+platform's @code{signed long}. If this default needs to be tuned, M4
+also has @option{--eval=32} or @option{--eval=64} to force the size used
+by the initial definition of @code{eval} @pxref{Limits control, ,
+Invoking m4}). For example, if you are testing on a 64-bit platform:
+
+@comment This output is platform-specific, see below for a better unit test.
+@comment ignore
+@example
+$ @kbd{m4 --gnu}
+ifelse(format(`%lu', `-1'), `18446744073709551615',
+`long is 64 bits', `long is 32 bits')
+@result{}long is 64 bits
+dumpdef(`eval')dnl
+@error{}eval:@tabchar{}<eval>
+m4exit
+$ @kbd{m4 -G}
+64-bit default: eval(`1 << 33')
+@result{}64-bit default: 8589934592
+dumpdef(`eval')dnl
+@error{}eval:@tabchar{}<eval64>
+m4exit
+$ @kbd{m4 -G --eval=32}
+forced 32-bit: eval(`1 << 33')
+@result{}forced 32-bit: 2
+dumpdef(`eval')dnl
+@error{}eval:@tabchar{}<eval>
+m4exit
+@end example
+
+@ignore
+@comment This is a more reliable test of the above example.
+@example
+ifdef(`__unix__', ,
+ `errprint(` skipping: syscmd does not have unix semantics
+')m4exit(`77')')dnl
+changequote(`[', `]')dnl
+ifelse(esyscmd([printf 'format(%%lx,4294967296)' | ]__program__[ --gnu])
+.0.sysval,
+ esyscmd([printf 'eval(0x100000000,16)' | ]__program__[ -G])
+.sysval.0,
+ [match], [oops])
+@result{}match
+define([probe], [eval(1<<33)dumpdef(eval)])dnl
+syscmd([printf ']defn([probe])[' | ]__program__[ -G --eval=32]) sysval
+@result{}2 0
+@error{}eval:@tabchar{}<eval>
+syscmd([printf ']defn([probe])[' | ]__program__[ --gnu --eval=64]) sysval
+@result{}8589934592 0
+@error{}eval:@tabchar{}<eval64>
+@end example
+@end ignore
+
If @var{radix} is specified, it specifies the radix to be used in the
expansion. The default radix is 10; this is also the case if
@var{radix} is the empty string. A warning results if the radix is
diff --git a/src/builtin.c b/src/builtin.c
index bba80e33..e7a82c4a 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -224,7 +224,7 @@ func_print (struct obstack *obs, const builtin *func, bool
flatten,
/* Install a builtin macro with name NAME and length LEN, bound to the
C function given in BP. MODE is SYMBOL_INSERT or
SYMBOL_PUSHDEF. */
-void
+symbol *
define_builtin (const char *name, size_t len, const builtin *bp,
symbol_lookup mode)
{
@@ -235,6 +235,7 @@ define_builtin (const char *name, size_t len, const builtin
*bp,
SYMBOL_MACRO_ARGS (sym) = bp->groks_macro_args;
SYMBOL_BLIND_NO_ARGS (sym) = bp->blind_if_no_args;
SYMBOL_FUNC (sym) = bp->func;
+ return sym;
}
/* Storage for the compiled regular expression of
@@ -487,6 +488,7 @@ builtin_init (void)
const builtin *bp;
const predefined *pp;
char *string;
+ symbol *sym;
for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
if (!no_gnu_extensions || !bp->gnu_extension)
@@ -495,11 +497,16 @@ builtin_init (void)
if (prefix_all_builtins)
{
string = xasprintf ("m4_%s", bp->name);
- define_builtin (string, len + 3, bp, SYMBOL_INSERT);
+ sym = define_builtin (string, len + 3, bp, SYMBOL_INSERT);
free (string);
}
else
- define_builtin (bp->name, len, bp, SYMBOL_INSERT);
+ sym = define_builtin (bp->name, len, bp, SYMBOL_INSERT);
+ /* Special case the initial width of eval. */
+ if (bp->func == m4_eval
+ && (eval_size == 64 || (eval_size == 0 && no_gnu_extensions
+ && sizeof (long) * CHAR_BIT == 64)))
+ SYMBOL_FUNC (sym) = m4_eval64;
}
for (pp = &predefined_tab[0]; pp->func != NULL; pp++)
diff --git a/src/m4.c b/src/m4.c
index 89a2ec2f..a136029e 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -47,6 +47,9 @@ int debug_level = 0;
/* Disable GNU extensions (-G). */
int no_gnu_extensions = 0;
+/* Default eval size (--eval): One of 0, 32, or 64. */
+int eval_size = 0;
+
/* Prefix all builtin functions by `m4_'. */
int prefix_all_builtins = 0;
@@ -270,6 +273,7 @@ Preprocessor features:\n\
puts ("");
xprintf (_("\
Limits control:\n\
+ --eval=SIZE force eval to SIZE 32 or 64\n\
-g, --gnu override -G to re-enable GNU extensions\n\
-G, --traditional suppress all GNU extensions\n\
-H, --hashsize=PRIME set symbol lookup hash table size [%d]\n\
@@ -332,6 +336,7 @@ mismatch, or whatever value was passed to the m4exit
macro.\n\
enum
{
DEBUGFILE_OPTION = CHAR_MAX + 1, /* no short opt */
+ EVAL_SIZE_OPTION, /* no short opt */
WARN_MACRO_SEQUENCE_OPTION, /* no short opt */
HELP_OPTION, /* no short opt */
@@ -362,6 +367,7 @@ static const struct option long_options[] = {
{"undefine", required_argument, NULL, 'U'},
{"debugfile", optional_argument, NULL, DEBUGFILE_OPTION},
+ {"eval-size", required_argument, NULL, EVAL_SIZE_OPTION},
{"warn-macro-sequence", optional_argument, NULL,
WARN_MACRO_SEQUENCE_OPTION},
@@ -634,6 +640,16 @@ main (int argc, char *const *argv, char *const *envp
MAYBE_UNUSED)
debugfile = optarg;
break;
+ case EVAL_SIZE_OPTION:
+ if (STREQ (optarg, "32"))
+ eval_size = 32;
+ else if (STREQ (optarg, "64"))
+ eval_size = 64;
+ else
+ error (0, 0, _("invalid eval size: %s"),
+ quotearg_style (locale_quoting_style, optarg));
+ break;
+
case WARN_MACRO_SEQUENCE_OPTION:
/* Don't call set_macro_sequence here, as it can exit.
--warn-macro-sequence sets optarg to NULL (which uses the
diff --git a/src/m4.h b/src/m4.h
index c1735318..239103b0 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -134,6 +134,7 @@ typedef unsigned int bool_bitfield;
extern int sync_output; /* -s */
extern int debug_level; /* -d */
extern int no_gnu_extensions; /* -G */
+extern int eval_size; /* --eval */
extern int prefix_all_builtins; /* -P */
extern size_t max_debug_argument_length; /* -l */
extern int suppress_warnings; /* -Q */
@@ -521,8 +522,8 @@ struct re_registers;
extern void builtin_init (void);
extern bool bad_argc (const call_info *, int, unsigned int, unsigned int);
-extern void define_builtin (const char *, size_t, const builtin *,
- symbol_lookup);
+extern symbol *define_builtin (const char *, size_t, const builtin *,
+ symbol_lookup);
extern void set_macro_sequence (const char *);
extern void free_regex (void);
extern void define_user_macro (const char *, size_t, const char *, size_t,
--
2.49.0
_______________________________________________
M4-patches mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/m4-patches