Re: multiline output variables.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Here's what I hope is a final solution to multiline output variables and
the related rework of _AC_OUTPUT_FILES.

I needed to change the delimeter to not contain @, since I now escape all
@ in output variable values as |#@!@#|.  No, there's no really good reason
that I chose this string, except that it's ugly enough to be rare, and it
doesn't have @s on its ends, which means that it can be used to escape
itself if need be (ick!).  Also, note that I escape _all_ @s in output
variables so that if I have $fish='@foo', $bait='bar@', and
$foobar='whoops', @fish@@bait@ doesn't become "whoops"  (which would only
happen if I AC_SUBST([fish]) and AC_SUBST([bait]) both before I
AC_SUBST([foobar])).

For similar reasons and lack thereof, I escape the beginning of each
variable with |#@!_!@#|, which I later delete, so that if $shove='ob',
@fo@shove@ar@ doesn't become "whoops" either.  (This would be especially
dangerous if neither $fo nor $ar were output variables, in which case the
user might reasonably expect the @fo@ and @ar@ to be safe.)  Note that
this also allows "@fish@bait@" to be disambiguated into either
"@|#@!_!@#|fish@bait@" or "@fish@|#@!_!@#|bait@".  I suppose it would be
possible to detect and gripe to the user about "@fish@bait@", but this
would be a major pain, and I figure it's their own problem.

I believe that these two escapes cause substitutions to now be strictly
non-recursive, and fully deterministic with respect to number and order of
AC_SUBST.

I've changed AC_SUBST_FILE'd output variables to only work on lines by
themselves, and now delete the lines.  I allow spaces and tabs at either
end of the line to reduce confusion and consternation.  (By the way, I
wasn't sure whether "
/@foo@/{
r $foo
d
}
" is two, three, or four sed commands.  I counted it as four.)

I'd like somebody else to try stress-testing this code to see if it plays
well in all cases.  (E.g. did I get the logic right if there are exactly
99 output variables?)  Even just a second brain looking at it would be
nice.

-Dan
--- doc/autoconf.texi.old	2004-08-20 16:51:34.000000000 -0400
+++ doc/autoconf.texi	2005-01-25 19:08:11.162836800 -0500
@@ -7126,9 +7126,15 @@
 or more @file{Makefile}s).  This means that @code{AC_OUTPUT} will
 replace instances of @samp{@@@var{variable}@@} in input files with the
 value that the shell variable @var{variable} has when @code{AC_OUTPUT}
-is called.  This value of @var{variable} should not contain literal
-newlines.  (There is currently no portable way to escape literal
-newlines.)
+is called.  It is now permissible for the value to contain newlines.
+The substituted value is not rescanned for more output variables;
+@samp{@@@var{variable}@@} in the value is inserted literally into the
+output file.  (Actually, the surrounding @code{@@}s are replaced with
+@code{|#@@!@@#|}, and all occurances of @code{|#@@!@@#|} are ultimately
+replaced with @code{@@}.  If by some misfortune, you need a literal
+@code{|#@@!@@#|} in your output file, use @code{|#|#@@!@@#|!@@#|}.
+Similarly, all values are begun with with @code{|#@@!_!@@#|}, which is
+in turn deleted.)
 
 If @var{value} is given, in addition assign it to @var{variable}.
 @end defmac
@@ -7142,7 +7148,10 @@
 @samp{@@@var{variable}@@} in output files (such as @file{Makefile.in})
 with the contents of the file that the shell variable @var{variable}
 names when @code{AC_OUTPUT} is called.  Set the variable to
-@file{/dev/null} for cases that do not have a file to insert.
+@file{/dev/null} for cases that do not have a file to insert.  This
+substitution only occurs when the @samp{@@@var{variable}@@} is on a line
+by itself, optionally surrounded by spaces and tabs.  The whole line,
+including the spaces, tabs, and the terminating newline, is replaced.
 
 This macro is useful for inserting @file{Makefile} fragments containing
 special dependencies or other @code{make} directives for particular host
--- lib/autoconf/status.m4.old	2005-01-25 17:32:16.568136000 -0500
+++ lib/autoconf/status.m4	2005-01-25 19:08:41.746814400 -0500
@@ -850,7 +850,15 @@
 m4_define([AC_LIST_FILES])
 m4_define([AC_LIST_FILES_COMMANDS])
 
-
+# _AC_SED_CMD_LIMIT
+# -----------------
+# Evaluate to an m4 number equal to the maximum number of commands to put
+# in any single sed program.
+#
+# Some seds have small command number limits, like on Digital OSF/1 and HP-UX.
+m4_define([_AC_SED_CMD_LIMIT],
+dnl One cannot portably go further than 100 commands because of HP-UX.
+[100])
 
 # _AC_OUTPUT_FILES
 # ----------------
@@ -860,80 +868,173 @@
 # It has to send itself into $CONFIG_STATUS (eg, via here documents).
 # Upon exit, no here document shall be opened.
 m4_define([_AC_OUTPUT_FILES],
-[cat >>$CONFIG_STATUS <<_ACEOF
-
+[cat >>$CONFIG_STATUS <<\_ACEOF
 #
 # CONFIG_FILES section.
 #
 
 # No need to generate the scripts if there are no CONFIG_FILES.
 # This happens for instance when ./config.status config.h
-if test -n "\$CONFIG_FILES"; then
-  # Protect against being on the right side of a sed subst in config.status.
-dnl Please, pay attention that this sed code depends a lot on the shape
-dnl of the sed commands issued by AC_SUBST.  So if you change one, change
-dnl the other too.
-[  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
-   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF]
-dnl These here document variables are unquoted when configure runs
-dnl but quoted when config.status runs, so variables are expanded once.
-dnl Insert the sed substitutions of variables.
-m4_ifdef([_AC_SUBST_VARS],
-	 [AC_FOREACH([AC_Var], m4_defn([_AC_SUBST_VARS]),
-[s,@AC_Var@,$AC_Var,;t t
-])])dnl
+if test -n "$CONFIG_FILES"; then
+
+_ACEOF
+
+m4_pushdef([_AC_SED_CMDS], [])dnl Part of pipeline that does substitutions.
+dnl
+m4_pushdef([_AC_SED_FRAG_NUM], 0)dnl Fragment number.
+m4_pushdef([_AC_SED_SUBS], 0)dnl Num of substitutions in current frag so far.
+dnl Max substitutions to put in each fragment.
+m4_pushdef([_AC_SED_SUBS_LIMIT], m4_eval(_AC_SED_CMD_LIMIT-1))dnl
+dnl Do something about the last fragment.  Most of the time, this is just
+dnl a newline.  When generating a fragment which is partially substitutions for
+dnl file output variables, this should be a newline, then those substitutions,
+dnl and a redefinition of itself to a newline.  For the last fragment, this
+dnl may be agumented to first have part of a set command and last redefine
+dnl itself to another sed command.  See below.
+m4_pushdef([_AC_SED_LAST_FRAG], [
+])dnl
 m4_ifdef([_AC_SUBST_FILES],
-	 [AC_FOREACH([AC_Var], m4_defn([_AC_SUBST_FILES]),
-[/@AC_Var@/r $AC_Var
-s,@AC_Var@,,;t t
-])])dnl
+[# Create sed commands to substitute file output variables.
+
+m4_define([_AC_SED_SUBS_LIMIT], m4_eval((_AC_SED_CMD_LIMIT-1)/4))dnl
+AC_FOREACH([_AC_Var], m4_defn([_AC_SUBST_FILES]),
+[dnl End fragments at beginning of loop so that last fragment is not ended
+dnl   for file output variables.  This is because a few non-file output
+dnl   variables can fit in the last fragment.
+dnl End fragment if needed.
+m4_if(_AC_SED_SUBS, _AC_SED_SUBS_LIMIT,
+[dnl Fragment is full and not the last one, so no need for the final un-escape.
+dnl Increment fragment number.
+m4_define([_AC_SED_FRAG_NUM],m4_eval(_AC_SED_FRAG_NUM+1))dnl
+dnl Record that this frament will need to be used.
+m4_define([_AC_SED_CMDS],
+m4_defn([_AC_SED_CMDS])[| sed -f $tmp/subs-]_AC_SED_FRAG_NUM[.sed ])dnl
+m4_divert_pop[]dnl Actually write to ./configure again.
+cat >>$CONFIG_STATUS <<_ACEOF
+  cat >\$tmp/subs-_AC_SED_FRAG_NUM.sed <<\CEOF
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+m4_undivert(m4_eval(m4_divnum+1))dnl
 CEOF
 
 _ACEOF
 
-  cat >>$CONFIG_STATUS <<\_ACEOF
-  # Split the substitutions into bite-sized pieces for seds with
-  # small command number limits, like on Digital OSF/1 and HP-UX.
-dnl One cannot portably go further than 100 commands because of HP-UX.
-dnl Here, there are 2 cmd per line, and two cmd are added later.
-  ac_max_sed_lines=48
-  ac_sed_frag=1 # Number of current file.
-  ac_beg=1 # First line for current file.
-  ac_end=$ac_max_sed_lines # Line after last line for current file.
-  ac_more_lines=:
-  ac_sed_cmds=
-  while $ac_more_lines; do
-    if test $ac_beg -gt 1; then
-      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
-    else
-      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
-    fi
-    if test ! -s $tmp/subs.frag; then
-      ac_more_lines=false
-    else
-      # The purpose of the label and of the branching condition is to
-      # speed up the sed processing (if there are no `@' at all, there
-      # is no need to browse any of the substitutions).
-      # These are the two extra sed commands mentioned above.
-      (echo [':t
-  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b'] && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
-      if test -z "$ac_sed_cmds"; then
-	ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
-      else
-	ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
-      fi
-      ac_sed_frag=`expr $ac_sed_frag + 1`
-      ac_beg=$ac_end
-      ac_end=`expr $ac_end + $ac_max_sed_lines`
-    fi
-  done
-  if test -z "$ac_sed_cmds"; then
-    ac_sed_cmds=cat
-  fi
-fi # test -n "$CONFIG_FILES"
+m4_define([_AC_SED_LINES], 0)dnl
+])dnl Old fragment is ended.
+dnl Start new fragment if needed.
+m4_if(_AC_SED_SUBS, 0,
+[dnl Stash output until start of frag is known.
+m4_divert_push(m4_eval(m4_divnum+1))dnl
+])dnl New fragment is started.
+/^[[   ]]*@_AC_Var@[[   ]]*$/{
+r $_AC_Var
+d
+}
+m4_define([_AC_SED_SUBS], m4_incr(_AC_SED_SUBS))dnl Increment substitution.
+])dnl End of AC_FOREACH(..._AC_SUBST_FILES...)
+m4_divert_pop[]dnl
+dnl Diversion m4_divnum+1 now holds substitutions for pending file output vars.
+# Remaining file output variable substitutions leave some room left in their
+# fragment, so they are inserted in the first fragment created below.
 
+m4_define([_AC_SED_LAST_FRAG], [
+m4_undivert(m4_eval(m4_divnum+1))m4_define([_AC_SED_LAST_FRAG], [
+])])dnl
+m4_define([_AC_SED_SUBS_LIMIT], m4_eval(_AC_SED_CMD_LIMIT-1-4*_AC_SED_SUBS))dnl
+m4_define([_AC_SED_SUBS], 0)dnl
+])dnl end of ifdef([_AC_SUBST_FILES]...)
+dnl
+# Create sed commands to substitute non-file output variables.
+
+# Init the delimiter to something very unlikely.
+ac_delim='%^!_!#_'
+
+AC_FOREACH([_AC_Var],
+m4_ifdef([_AC_SUBST_VARS],[m4_defn([_AC_SUBST_VARS])])[ @END@],
+[m4_if(_AC_Var, @END@,
+[dnl Temporarily store the sed command for the final un-escape.
+m4_define([_AC_Var],[[s/|#@!@#|/@/g; s/|#@!_!@#|//g]])dnl
+m4_if( 1, m4_eval((_AC_SED_SUBS_LIMIT-_AC_SED_SUBS)<2),
+[dnl Last fragment cannot fit final un-escape.  Do it as non-file sed program.
+m4_define([_AC_SED_CMDS],m4_defn([_AC_SED_CMDS])[| sed ']_AC_Var[' ])],
+[dnl Final unescape can be done in last fragment.
+m4_define([_AC_SED_LAST_FRAG],
+dnl Cause no-remaining-output-variables short-circuit sed command branch
+dnl to final unescape and then define final un-escape command.
+[ e]m4_defn([_AC_SED_LAST_FRAG])[m4_define([_AC_SED_LAST_FRAG], [
+:e
+]]m4_dquote(_AC_Var)[[
+])])dnl
+m4_define([_AC_SED_SUBS_LIMIT], _AC_SED_SUBS)])],
+[dnl Start new fragment if needed.
+m4_if(_AC_SED_SUBS, 0,
+[dnl Increment fragment number.
+m4_define([_AC_SED_FRAG_NUM],m4_eval(_AC_SED_FRAG_NUM+1))dnl
+dnl Record that this frament will need to be used.
+m4_define([_AC_SED_CMDS],
+m4_defn([_AC_SED_CMDS])[| sed -f $tmp/subs-]_AC_SED_FRAG_NUM[.sed ])dnl
+[while :; do
+  # Create some of the output variable substitutions.
+  cat > conf$$subs.sed <<_ACEOF
+]])dnl New fragment is started.
+$ac_delim<_AC_Var>$ac_delim$_AC_Var$ac_delim
+m4_define([_AC_SED_SUBS], m4_incr(_AC_SED_SUBS))dnl Increment substitution.
+])dnl Frag started, end of frag faked, or final sed command added as needed.
+dnl End fragment if needed.
+m4_if(_AC_SED_SUBS, _AC_SED_SUBS_LIMIT,
+[_ACEOF
+  # Make certain that only the expected number of $ac_delim's have been output
+  # into the program.  If there is a different number, the delimiter has
+  # appeared in one of the output variables, and this is sure to confuse
+  # something, so change the delimiter and generate all the sed program
+  # fragments again.
+dnl Note that grep -c doesn't do the right thing because it counts lines
+dnl with matches, not total number of matches.
+  if test `sed -n '
+:d
+s/'"$ac_delim"'//; t i
+$!b
+dnl This can't be looking for more than (_AC_SED_CMD_LIMIT-2)/2*3, which is
+dnl plenty small enough to not trip any line length limits.
+x; s/x\{m4_eval(_AC_SED_SUBS*3)\}/yes/; s/yesx+/no/; /^yes$/!s/.*/no/; p; q
+:i
+x; s/$/x/; x; t d
+' < conf$$subs.sed
+` != yes; then
+    ac_delim=$ac_delim'_'
+  else break; fi
+done
+# Have config.status create the needed sed program.
+cat >>$CONFIG_STATUS <<_ACEOF
+  cat >\$tmp/subs-_AC_SED_FRAG_NUM.sed <<\CEOF
+[/@[a-zA-Z_][a-zA-Z_0-9]*@/!b]_AC_SED_LAST_FRAG[]dnl
+[_ACEOF
+# Output the sed program verbatim to config.status, properly escaping its
+# contents as needed.  Note that this escaping is now safe because $ac_delim
+# contains none of [[\\&,]] and occurs only where it was inserted above.
+sed '
+s/@/|#@!@#|/g
+s/[\\&,]/\\&/g
+s/$/\\/
+s/'"$ac_delim"'</s,@/
+s/>'"$ac_delim"'/@,|#@!_!@#|/
+s/'"$ac_delim"'$/,g/
+' <conf$$subs.sed >>$CONFIG_STATUS
+cat >>$CONFIG_STATUS <<\_ACEOF]_AC_SED_LAST_FRAG[]dnl
+CEOF
 _ACEOF
+
+m4_define([_AC_SED_SUBS_LIMIT], m4_eval(_AC_SED_CMD_LIMIT-1))dnl
+m4_define([_AC_SED_SUBS], 0)dnl
+])])dnl End of AC_FOREACH(..._AC_SUBST_VARS...)
+dnl
+m4_popdef([_AC_SED_FRAG_NUM])dnl
+m4_popdef([_AC_SED_SUBS])dnl
+m4_popdef([_AC_SED_SUBS_LIMIT])dnl
+m4_popdef([_AC_SED_LAST_FRAG])dnl
+dnl
 cat >>$CONFIG_STATUS <<\_ACEOF
+fi # test -n "$CONFIG_FILES"
+
 for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
   # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
   case $ac_file in
@@ -1018,8 +1119,8 @@
 s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
 AC_PROVIDE_IFELSE([AC_PROG_INSTALL], [s,@INSTALL@,$ac_INSTALL,;t t
 ])dnl
-dnl The parens around the eval prevent an "illegal io" in Ultrix sh.
-" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+" $ac_file_inputs m4_defn([_AC_SED_CMDS])>$tmp/out
+m4_popdef([_AC_SED_CMDS])dnl
   rm -f $tmp/stdin
 dnl This would break Makefile dependencies.
 dnl  if diff $ac_file $tmp/out >/dev/null 2>&1; then
_______________________________________________
Autoconf mailing list
Autoconf@xxxxxxx
http://lists.gnu.org/mailman/listinfo/autoconf

[Index of Archives]     [GCC Help]     [Kernel Discussion]     [RPM Discussion]     [Red Hat Development]     [Yosemite News]     [Linux USB]     [Samba]

  Powered by Linux