From bcef427e185102ad6469614eed402ace2dd9aee8 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 19 Nov 2008 09:00:55 -0700 Subject: [PATCH] Improve m4_expand robustness, part 1. * lib/m4sugar/m4sugar.m4 (_m4_expand): Tolerate unquoted unbalanced `)'. * tests/m4sugar.at (m4@&t@_expand): New test. Signed-off-by: Eric Blake --- ChangeLog | 7 +++++++ lib/m4sugar/m4sugar.m4 | 43 +++++++++++++++++++++++++-------------- tests/m4sugar.at | 46 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index d2d2f7bcc..380f7c78c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2008-11-20 Eric Blake + + Improve m4_expand robustness, part 1. + * lib/m4sugar/m4sugar.m4 (_m4_expand): Tolerate unquoted + unbalanced `)'. + * tests/m4sugar.at (m4@&t@_expand): New test. + 2008-11-20 Eric Blake Add m4_chomp, m4_esyscmd_s. diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4 index c9a7f9893..7074d90c8 100644 --- a/lib/m4sugar/m4sugar.m4 +++ b/lib/m4sugar/m4sugar.m4 @@ -814,9 +814,10 @@ m4_define([m4_echo], [$@]) # m4_expand(ARG) # -------------- -# Return the expansion of ARG as a single string. Unlike m4_quote($1), this -# correctly preserves whitespace following single-quoted commas that appeared -# within ARG. +# Return the expansion of ARG as a single string. Unlike +# m4_quote($1), this preserves whitespace following single-quoted +# commas that appear within ARG. It also deals with shell case +# statements. # # m4_define([active], [ACT, IVE]) # m4_define([active2], [[ACT, IVE]]) @@ -825,19 +826,31 @@ m4_define([m4_echo], [$@]) # m4_expand([active, active2]) # => ACT, IVE, ACT, IVE # -# Unfortunately, due to limitations in m4, ARG must expand to something -# with balanced quotes (use quadrigraphs to get around this). The input -# is not likely to have unbalanced -=<{(/)}>=- quotes, and it is possible -# to have unbalanced (), provided it was specified with proper [] quotes. -# Likewise, ARG must either avoid unquoted comments, or must be sure -# to include the trailing newline to end the comment. -# -# Exploit that extra () will group unquoted commas and the following -# whitespace, then convert () to []. m4_bpatsubst can't handle newlines -# inside $1, and m4_substr strips quoting. So we (ab)use m4_changequote. -m4_define([m4_expand], [_$0(-=<{($1)}>=-)]) +# Unfortunately, due to limitations in m4, ARG must expand to +# something with balanced quotes (use quadrigraphs to get around +# this), and should not contain the unlikely delimiters -=<{( or +# )}>=-. It is possible to have unbalanced quoted `(' or `)', as well +# as unbalanced unquoted `)'. +# +# Exploit that extra unquoted () will group unquoted commas and the +# following whitespace. m4_bpatsubst can't handle newlines inside $1, +# and m4_substr strips quoting. So we (ab)use m4_changequote, using +# temporary quotes to remove the delimiters that conveniently included +# the unquoted () that were added prior to the changequote. +# +# Thanks to shell case statements, too many people are prone to pass +# underquoted `)', so we try to detect that by passing a marker as a +# fourth argument; if the marker is not present, then we assume that +# we encountered an early `)', and re-expand the first argument, but +# this time with one more `(' in the second argument and in the +# open-quote delimiter. We must also ignore the slop from the +# previous try. The final macro is thus half line-noise, half art. +m4_define([m4_expand], [_$0([$1], [(], -=<{($1)}>=-, [}>=-])]) + m4_define([_m4_expand], -[m4_changequote([-=<{(], [)}>=-])$1m4_changequote([, ])]) +[m4_if([$4], [}>=-], + [m4_changequote([-=<{$2], [)}>=-])$3m4_changequote([, ])], + [$0([$1], [($2], -=<{($2$1)}>=-, [}>=-])m4_ignore$2])]) # m4_ignore(ARGS) diff --git a/tests/m4sugar.at b/tests/m4sugar.at index d1ee1fe6a..89e0fde6c 100644 --- a/tests/m4sugar.at +++ b/tests/m4sugar.at @@ -676,6 +676,52 @@ one AT_CLEANUP +## ----------- ## +## m4_expand. ## +## ----------- ## + +AT_SETUP([m4@&t@_expand]) + +AT_CHECK_M4SUGAR_TEXT( +[[m4_define([active], [ACTIVE])dnl +m4_expand([#active +active]) +m4_expand([[active]]) +dnl properly quoted case statements +m4_expand([case a in @%:@( + *) echo active, ;; +esac +case b in + *[)] echo active, ;; +esac]) +dnl unbalanced underquoted `)', but we manage anyway (gasp!) +m4_expand([case c in #( + *) echo active, ;; +esac +case d in + *) echo active, ;; +esac]) +]], +[[#active +ACTIVE +active +case a in #( + *) echo ACTIVE, ;; +esac +case b in + *) echo ACTIVE, ;; +esac +case c in #( + *) echo ACTIVE, ;; +esac +case d in + *) echo ACTIVE, ;; +esac +]]) + +AT_CLEANUP + + ## -------------- ## ## m4_text_wrap. ## ## -------------- ## -- 2.47.3