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 M4-patches@gnu.org https://lists.gnu.org/mailman/listinfo/m4-patches