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

Reply via email to