autoconf-patches
[Top][All Lists]
Advanced

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

Re: [PATCH 1/10] Proper file name escaping in Autoconf programs and Perl


From: Ralf Wildenhues
Subject: Re: [PATCH 1/10] Proper file name escaping in Autoconf programs and Perl modules.
Date: Sat, 8 Dec 2007 10:16:30 +0100
User-agent: Mutt/1.5.13 (2006-08-11)

Hello Russ,

* Russ Allbery wrote on Thu, Dec 06, 2007 at 11:02:28PM CET:
> Ralf Wildenhues <address@hidden> writes:
> 
> > +sub open_quote($)
> > +{
> > +  my ($s) = @_;
> > +  if ($s =~ m!^\s!)
> > +    {
> > +      $s = "./$s";
> > +    }
> > +  return $s;
> > +}
> 
> If you're going to use the two-argument format, you should also add a nul
> byte to the end of the file name ($s .= "\0";):

Hmm, weird enough that my testing did not show this.  I needed to add
another special case (with m4_pattern_forbid).  Thanks for bringing this
to attention!

[...]
>             Use 3-argument form to open a file with arbitrary weird
>             characters in it,
> 
>                 open(FOO, '<', $file);
> 
>             otherwise it's necessary to protect any leading and trailing
>             whitespace:
> 
>                 $file =~ s#^(\s)#./$1#;
>                 open(FOO, "< $file\0");
> 
> If the string ends in a nul byte, trailing whitespace will be significant;
> otherwise, it isn't.

Thanks.  Since it also says in the following:

|                (this may not work on some bizarre filesystems).

I chose to only add \0 for file names where this is needed.

This is the patch that I will apply.

Cheers,
Ralf

   Proper file name escaping in Autoconf programs and Perl modules.
   
   This includes escaping of characters special to the shell
   as well as special to Perl, e.g., leading `<' or `>'.
   For example, when $file starts with `>', `open ">$file"'
   wrongly tries to append to a different file.
   
   * bin/autoconf.as: Fix quoting for autom4te options.
   * lib/Autom4te/General.pm (shell_quote): New function, taken
   from coreutils, written by Jim Meyering.
   (mktmpdir): Use it.
   * lib/Autom4te/FileUtils.pm (open_quote): New function.
   (update_file, contents): Use it.
   * bin/autom4te.in (files_to_options, handle_m4): Use shell_quote
   and open_quote
   * bin/autoreconf.in (parse_args): Likewise.
   * bin/autoscan.in (main): Likewise.
   * bin/autoupdate.in (main): Likewise.
   * bin/autoheader.in: Likewise, fixing old insufficient escaping.
   * bin/ifnames.in: Likewise, XFile usage fixes.
   * tests/tools.at (autom4te and whitespace in file names): Extend
   test.  Test twice, with special characters allowed on w32, and the
   rest.  Test leading and trailing whitespace, for `open_quote'.
   (autotools and whitespace in file names): New, analogous test.
   Reported by Paul Eggert and Benoit Sigoure, additional suggestions
   by Russ Allbery and Eric Blake.

diff --git a/bin/autoconf.as b/bin/autoconf.as
index b68d4ef..dbabc30 100644
--- a/bin/autoconf.as
+++ b/bin/autoconf.as
@@ -107,14 +107,21 @@ while test $# -gt 0 ; do
     --include=*  | -I?* | \
     --prepend-include=* | -B?* | \
     --warnings=* | -W?* )
-       autom4te_options="$autom4te_options '$1'"; shift ;;
-
+       case $1 in
+         *\'*) arg=`AS_ECHO(["$1"]) | sed "s/'/'\\\\\\\\''/g"` ;;
+        *) arg=$1 ;;
+       esac
+       autom4te_options="$autom4te_options '$arg'"; shift ;;
     # Options with separated arg passed as is to autom4te.
     --include  | -I | \
     --prepend-include  | -B | \
     --warnings | -W )
        test $# = 1 && eval "$exit_missing_arg"
-       autom4te_options="$autom4te_options $option '$2'"
+       case $2 in
+         *\'*) arg=`AS_ECHO(["$2"]) | sed "s/'/'\\\\\\\\''/g"` ;;
+        *) arg=$2 ;;
+       esac
+       autom4te_options="$autom4te_options $option '$arg'"
        shift; shift ;;
 
     --trace=* | -t?* )
@@ -178,8 +185,8 @@ esac
 test -z "$outfile" && outfile=-
 
 # Run autom4te with expansion.
-eval set x $autom4te_options \
-  --language=autoconf --output=\$outfile "$traces" \$infile
+eval set x "$autom4te_options" \
+  --language=autoconf --output=\"\$outfile\" "$traces" \"\$infile\"
 shift
 $verbose && AS_ECHO(["$as_me: running $AUTOM4TE $*"]) >&2
 exec "$AUTOM4TE" "$@"
diff --git a/bin/autoheader.in b/bin/autoheader.in
index 9c19ea3..2565db2 100644
--- a/bin/autoheader.in
+++ b/bin/autoheader.in
@@ -157,8 +157,8 @@ END
 
 # Set up autoconf.
 my $autoconf = "'$autom4te' --language=autoconf ";
-$autoconf .= join (' ', map { "--include='$_'" } @include);
-$autoconf .= join (' ', map { "--prepend-include='$_'" } @prepend_include);
+$autoconf .= join (' --include=', '', map { shell_quote ($_) } @include);
+$autoconf .= join (' --prepend-include=', '', map { shell_quote ($_) } 
@prepend_include);
 $autoconf .= ' --debug' if $debug;
 $autoconf .= ' --force' if $force;
 $autoconf .= ' --verbose' if $verbose;
@@ -169,16 +169,17 @@ $autoconf .= ' --verbose' if $verbose;
 
 # Source what the traces are trying to tell us.
 verb "$me: running $autoconf to trace from $ARGV[0]";
+my $quoted_tmp = shell_quote ($tmp);
 xsystem ("$autoconf"
         # If you change this list, update the
         # `Autoheader-preselections' section of autom4te.in.
         . ' --trace AC_CONFIG_HEADERS:\'$$config_h ||= \'"\'"\'$1\'"\'"\';\''
         . ' --trace AH_OUTPUT:\'$$verbatim{\'"\'"\'$1\'"\'"\'} = 
\'"\'"\'$2\'"\'"\';\''
         . ' --trace AC_DEFINE_TRACE_LITERAL:\'$$symbol{\'"\'"\'$1\'"\'"\'} = 
1;\''
-        . " $ARGV[0] >$tmp/traces.pl");
+        . " " . shell_quote ($ARGV[0]) . " >$quoted_tmp/traces.pl");
 
 local (%verbatim, %symbol);
-debug "$me: \`do'ing $tmp/traces.pl:\n" . `sed 's/^/| /' $tmp/traces.pl`;
+debug "$me: \`do'ing $tmp/traces.pl:\n" . `sed 's/^/| /' 
$quoted_tmp/traces.pl`;
 do "$tmp/traces.pl";
 warn "couldn't parse $tmp/traces.pl: $@" if $@;
 unless ($config_h)
@@ -197,7 +198,7 @@ $config_h_in ||= "$config_h.in";
 # only the name of the macro.
 %symbol = map { s/\(.*//; $_ => 1 } keys %symbol;
 
-my $out = new Autom4te::XFile (">$tmp/config.hin");
+my $out = new Autom4te::XFile ("> " . open_quote ("$tmp/config.hin"));
 
 # Don't write "do not edit" -- it will get copied into the
 # config.h, which it's ok to edit.
@@ -206,7 +207,7 @@ print $out "/* $config_h_in.  Generated from $ARGV[0] by 
autoheader.  */\n";
 # Dump the top.
 if ($config_h_top)
   {
-    my $in = new Autom4te::XFile ($config_h_top);
+    my $in = new Autom4te::XFile ("< " . open_quote ($config_h_top));
     while ($_ = $in->getline)
       {
        print $out $_;
@@ -216,7 +217,7 @@ if ($config_h_top)
 # Dump `acconfig.h', except for its bottom portion.
 if ($acconfig_h)
   {
-    my $in = new Autom4te::XFile ($acconfig_h);
+    my $in = new Autom4te::XFile ("< " . open_quote ($acconfig_h));
     while ($_ = $in->getline)
       {
        last if /address@hidden@/;
@@ -234,7 +235,7 @@ foreach (sort keys %verbatim)
 # Dump bottom portion of `acconfig.h'.
 if ($acconfig_h)
   {
-    my $in = new Autom4te::XFile ($acconfig_h);
+    my $in = new Autom4te::XFile ("< " . open_quote ($acconfig_h));
     my $dump = 0;
     while ($_ = $in->getline)
       {
@@ -246,7 +247,7 @@ if ($acconfig_h)
 # Dump the bottom.
 if ($config_h_bot)
   {
-    my $in = new Autom4te::XFile ($config_h_bot);
+    my $in = new Autom4te::XFile ("< " . open_quote ($config_h_bot));
     while ($_ = $in->getline)
       {
        print $out $_;
@@ -257,7 +258,7 @@ $out->close;
 
 # Check that all the symbols have a template.
 {
-  my $in = new Autom4te::XFile ("$tmp/config.hin");
+  my $in = new Autom4te::XFile ("< " . open_quote ("$tmp/config.hin"));
   my $suggest_ac_define = 1;
   while ($_ = $in->getline)
     {
diff --git a/bin/autom4te.in b/bin/autom4te.in
index b273b9a..8efe537 100644
--- a/bin/autom4te.in
+++ b/bin/autom4te.in
@@ -235,14 +235,10 @@ sub files_to_options (@)
   my @res;
   foreach my $file (@file)
     {
-      (my $arg = $file) =~ s/'/'\\''/g;
+      my $arg = shell_quote ($file);
       if ($file =~ /\.m4f$/)
        {
-         $arg = "--reload-state=$file";
-       }
-      if ($file =~ /[\t "'\\\$()]/)
-       {
-         $arg = "'$arg'";
+         $arg = "--reload-state=$arg";
        }
       push @res, $arg;
     }
@@ -258,7 +254,7 @@ sub load_configuration ($)
   my ($file) = @_;
   use Text::ParseWords;
 
-  my $cfg = new Autom4te::XFile ($file);
+  my $cfg = new Autom4te::XFile ("< " . open_quote ($file));
   my $lang;
   while ($_ = $cfg->getline)
     {
@@ -462,13 +458,13 @@ sub handle_m4 ($@)
   # We don't output directly to the cache files, to avoid problems
   # when we are interrupted (that leaves corrupted files).
   xsystem ("$m4"
-          . join (' --include=', '', @include)
+          . join (' --include=', '', map { shell_quote ($_) } @include)
           . ' --debug=aflq'
           . (!exists $ENV{'AUTOM4TE_NO_FATAL'} ? ' --fatal-warning' : '')
-          . " @address@hidden" . $req->id . "t"
+          . " @address@hidden" . shell_quote ("$tcache" . $req->id . "t")
           . join (' --trace=',   '', sort @macro)
           . " " . files_to_options (@ARGV)
-          . " >$ocache" . $req->id . "t");
+          . " > " . shell_quote ("$ocache" . $req->id . "t"));
 
   # Everything went ok: preserve the outputs.
   foreach my $file (map { $_ . $req->id } ($tcache, $ocache))
@@ -526,7 +522,7 @@ sub handle_output ($$)
   handle_traces ($req, "$tmp/patterns",
                 ('m4_pattern_forbid' => 'forbid:$1:$2',
                  'm4_pattern_allow'  => 'allow:$1'));
-  my @patterns = new Autom4te::XFile ("$tmp/patterns")->getlines;
+  my @patterns = new Autom4te::XFile ("< " . open_quote 
("$tmp/patterns"))->getlines;
   chomp @patterns;
   my %forbidden =
     map { /^forbid:([^:]+):.+$/ => /^forbid:[^:]+:(.+)$/ } @patterns;
@@ -553,7 +549,7 @@ sub handle_output ($$)
     }
   fatal "cannot create $output: $!"
     unless $out;
-  my $in = new Autom4te::XFile ($ocache . $req->id);
+  my $in = new Autom4te::XFile ("< " . open_quote ($ocache . $req->id));
 
   my %prohibited;
   my $res;
@@ -594,7 +590,7 @@ sub handle_output ($$)
   if ($ARGV[$#ARGV] ne '-')
     {
       my $prohibited = '\b(' . join ('|', keys %prohibited) . ')\b';
-      my $file = new Autom4te::XFile ($ARGV[$#ARGV]);
+      my $file = new Autom4te::XFile ("< " . open_quote ($ARGV[$#ARGV]));
 
       while ($_ = $file->getline)
        {
@@ -712,7 +708,7 @@ sub handle_traces ($$%)
   verb "formatting traces for `$output': " . join (', ', sort keys %trace);
 
   # Processing the traces.
-  my $trace_m4 = new Autom4te::XFile (">$tmp/traces.m4");
+  my $trace_m4 = new Autom4te::XFile ("> " . open_quote ("$tmp/traces.m4"));
 
   $_ = <<'EOF';
   divert(-1)
@@ -816,7 +812,7 @@ EOF
   #
   # Pay attention that the file name might include colons, if under DOS
   # for instance, so we don't use `[^:]+'.
-  my $traces = new Autom4te::XFile ($tcache . $req->id);
+  my $traces = new Autom4te::XFile ("< " . open_quote ($tcache . $req->id));
   while ($_ = $traces->getline)
     {
       # Trace with arguments, as the example above.  We don't try
@@ -831,8 +827,8 @@ EOF
     }
   $trace_m4->close;
 
-  my $in = new Autom4te::XFile ("$m4 $tmp/traces.m4 |");
-  my $out = new Autom4te::XFile (">$output");
+  my $in = new Autom4te::XFile ("$m4 " . shell_quote ("$tmp/traces.m4") . " 
|");
+  my $out = new Autom4te::XFile ("> " . open_quote ($output));
 
   # This is dubious: should we really transform the quadrigraphs in
   # traces?  It might break balanced [ ] etc. in the output.  The
@@ -892,7 +888,7 @@ sub up_to_date ($)
   handle_traces ($req, "$tmp/dependencies",
                 ('include'    => '$1',
                  'm4_include' => '$1'));
-  my $deps = new Autom4te::XFile ("$tmp/dependencies");
+  my $deps = new Autom4te::XFile ("< " . open_quote ("$tmp/dependencies"));
   while ($_ = $deps->getline)
     {
       chomp;
@@ -925,7 +921,7 @@ sub freeze ($)
   # output but comments and empty lines.
   my $result = xqx ("$m4"
                    . ' --fatal-warning'
-                   . join (' --include=', '', @include)
+                   . join (' --include=', '', map { shell_quote ($_) } 
@include)
                    . ' --define=divert'
                    . " " . files_to_options (@ARGV)
                    . ' </dev/null');
@@ -939,8 +935,8 @@ sub freeze ($)
   # or an improper paren etc.
   xsystem ("$m4"
           . ' --fatal-warning'
-          . join (' --include=', '', @include)
-          . " --freeze-state=$output"
+          . join (' --include=', '', map { shell_quote ($_) } @include)
+          . " --freeze-state=" . shell_quote ($output)
           . " " . files_to_options (@ARGV)
           . ' </dev/null');
 }
diff --git a/bin/autoreconf.in b/bin/autoreconf.in
index c964b03..e707446 100644
--- a/bin/autoreconf.in
+++ b/bin/autoreconf.in
@@ -182,10 +182,10 @@ sub parse_args ()
 
   # Dispatch autoreconf's option to the tools.
   # --include;
-  $autoconf   .= join (' --include=', '', @include);
-  $autoconf   .= join (' --prepend-include=', '', @prepend_include);
-  $autoheader .= join (' --include=', '', @include);
-  $autoheader .= join (' --prepend-include=', '', @prepend_include);
+  $autoconf   .= join (' --include=', '', map { shell_quote ($_) } @include);
+  $autoconf   .= join (' --prepend-include=', '', map { shell_quote ($_) } 
@prepend_include);
+  $autoheader .= join (' --include=', '', map { shell_quote ($_) } @include);
+  $autoheader .= join (' --prepend-include=', '', map { shell_quote ($_) } 
@prepend_include);
 
   # --install and --symlink;
   if ($install)
@@ -316,7 +316,7 @@ sub autoreconf_current_directory ()
   my $uses_gettext;
   if (-f $configure_ac)
     {
-      my $configure_ac_file = new Autom4te::XFile $configure_ac;
+      my $configure_ac_file = new Autom4te::XFile "< $configure_ac";
       while ($_ = $configure_ac_file->getline)
        {
          s/#.*//;
diff --git a/bin/autoscan.in b/bin/autoscan.in
index 372a878..59227e2 100644
--- a/bin/autoscan.in
+++ b/bin/autoscan.in
@@ -166,7 +166,7 @@ sub init_tables ()
   # instead of duplicating the code in lots of configure.ac files.
   my $file = find_file ("autoscan/autoscan.list",
                        reverse (@prepend_include), @include);
-  my $table = new Autom4te::XFile $file;
+  my $table = new Autom4te::XFile "< " . open_quote ($file);
   my $tables_are_consistent = 1;
 
   while ($_ = $table->getline)
@@ -241,7 +241,7 @@ sub scan_c_file ($)
   # Nonzero if in a multiline comment.
   my $in_comment = 0;
 
-  my $file = new Autom4te::XFile "<$file_name";
+  my $file = new Autom4te::XFile "< " . open_quote ($file_name);
 
   while ($_ = $file->getline)
     {
@@ -307,7 +307,7 @@ sub scan_makefile ($)
   my ($file_name) = @_;
   push @makefiles, $File::Find::name;
 
-  my $file = new Autom4te::XFile "<$file_name";
+  my $file = new Autom4te::XFile "< " . open_quote ($file_name);
 
   while ($_ = $file->getline)
     {
@@ -349,7 +349,7 @@ sub scan_sh_file ($)
   my ($file_name) = @_;
   push @shfiles, $File::Find::name;
 
-  my $file = new Autom4te::XFile "<$file_name";
+  my $file = new Autom4te::XFile "< " . open_quote ($file_name);
 
   while ($_ = $file->getline)
     {
@@ -518,7 +518,7 @@ sub output ($)
   my $configure_scan = shift;
   my %unique_makefiles;
 
-  my $file = new Autom4te::XFile ">$configure_scan";
+  my $file = new Autom4te::XFile "> " . open_quote ($configure_scan);
 
   print $file
     ("#                                               -*- Autoconf -*-\n" .
@@ -587,7 +587,7 @@ sub check_configure_ac ($)
 
   verb "running: $autoconf $trace_option $configure_ac";
   my $traces =
-    new Autom4te::XFile "$autoconf $trace_option $configure_ac|";
+    new Autom4te::XFile "$autoconf $trace_option $configure_ac |";
 
   while ($_ = $traces->getline)
     {
@@ -636,12 +636,12 @@ sub check_configure_ac ($)
 ## -------------- ##
 
 parse_args;
-$log = new Autom4te::XFile ">$me.log";
+$log = new Autom4te::XFile "> " . open_quote ("$me.log");
 
 $autoconf .= " --debug" if $debug;
 $autoconf .= " --verbose" if $verbose;
-$autoconf .= join (' --include=', '', @include);
-$autoconf .= join (' --prepend-include=', '', @prepend_include);
+$autoconf .= join (' --include=', '', map { shell_quote ($_) } @include);
+$autoconf .= join (' --prepend-include=', '', map { shell_quote ($_) } 
@prepend_include);
 
 my $configure_ac = find_configure_ac;
 init_tables;
diff --git a/bin/autoupdate.in b/bin/autoupdate.in
index d69211d..23060f2 100644
--- a/bin/autoupdate.in
+++ b/bin/autoupdate.in
@@ -127,8 +127,8 @@ my (%ac_macros, %au_macros, %m4_builtins);
 sub handle_autoconf_macros ()
 {
   # Get the builtins.
-  xsystem ("echo dumpdef | $m4 2>$tmp/m4.defs >/dev/null");
-  my $m4_defs = new Autom4te::XFile "$tmp/m4.defs";
+  xsystem ("echo dumpdef | $m4 2>" . shell_quote ("$tmp/m4.defs") . " 
>/dev/null");
+  my $m4_defs = new Autom4te::XFile "< " . open_quote ("$tmp/m4.defs");
   while ($_ = $m4_defs->getline)
     {
       $m4_builtins{$1} = 1
@@ -184,9 +184,9 @@ sub handle_autoconf_macros ()
 
   # ac.m4 -- autoquoting definitions of the AC macros (M4sugar excluded).
   # unac.m4 -- undefine the AC macros.
-  my $ac_m4 = new Autom4te::XFile ">$tmp/ac.m4";
+  my $ac_m4 = new Autom4te::XFile "> " . open_quote ("$tmp/ac.m4");
   print $ac_m4 "# ac.m4 -- autoquoting definitions of the AC macros.\n";
-  my $unac_m4 = new Autom4te::XFile ">$tmp/unac.m4";
+  my $unac_m4 = new Autom4te::XFile "> " . open_quote ("$tmp/unac.m4");
   print $unac_m4 "# unac.m4 -- undefine the AC macros.\n";
   foreach (sort keys %ac_macros)
     {
@@ -197,11 +197,11 @@ sub handle_autoconf_macros ()
   # m4save.m4 -- save the m4 builtins.
   # unm4.m4 -- disable the m4 builtins.
   # m4.m4 -- enable the m4 builtins.
-  my $m4save_m4 = new Autom4te::XFile ">$tmp/m4save.m4";
+  my $m4save_m4 = new Autom4te::XFile "> " . open_quote ("$tmp/m4save.m4");
   print $m4save_m4 "# m4save.m4 -- save the m4 builtins.\n";
-  my $unm4_m4 = new Autom4te::XFile ">$tmp/unm4.m4";
+  my $unm4_m4 = new Autom4te::XFile "> " . open_quote ("$tmp/unm4.m4");
   print $unm4_m4 "# unm4.m4 -- disable the m4 builtins.\n";
-  my $m4_m4 = new Autom4te::XFile ">$tmp/m4.m4";
+  my $m4_m4 = new Autom4te::XFile "> " . open_quote ("$tmp/m4.m4");
   print $m4_m4 "# m4.m4 -- enable the m4 builtins.\n";
   foreach (sort keys %m4_builtins)
     {
@@ -220,8 +220,8 @@ parse_args;
 $autoconf .= " --debug" if $debug;
 $autoconf .= " --force" if $force;
 $autoconf .= " --verbose" if $verbose;
-$autoconf .= join (' --include=', '', @include);
-$autoconf .= join (' --prepend-include=', '', @prepend_include);
+$autoconf .= join (' --include=', '', map { shell_quote ($_) } @include);
+$autoconf .= join (' --prepend-include=', '', map { shell_quote ($_) } 
@prepend_include);
 
 mktmpdir ('au');
 handle_autoconf_macros;
@@ -233,7 +233,7 @@ my $au_changequote =
 # au.m4 -- definitions the AU macros.
 xsystem ("$autoconf --trace AU_DEFINE:'_au_defun(\@<:address@hidden@:>\@,
 \@<:address@hidden@:>\@)' --melt /dev/null "
-       . ">$tmp/au.m4");
+       . ">" . shell_quote ("$tmp/au.m4"));
 
 
 
@@ -247,7 +247,7 @@ foreach my $file (@ARGV)
     if ($file eq '-')
       {
        $file = "$tmp/stdin";
-       system "cat >$file";
+       system "cat >" . shell_quote ($file);
       }
     elsif (! -r "$file")
       {
@@ -376,9 +376,9 @@ EOF
     $input_m4 =~ s/\$file/$file/g;
 
     # prepared input -- input, but reenables the quote before each AU macro.
-    open INPUT_M4, ">$tmp/input.m4"
+    open INPUT_M4, "> " . open_quote ("$tmp/input.m4")
        or error "cannot open: $!";
-    open FILE, "<$file"
+    open FILE, "< " . open_quote ($file)
        or error "cannot open: $!";
     print INPUT_M4 "$input_m4";
     while (<FILE>)
@@ -392,10 +392,10 @@ EOF
        or error "cannot close $tmp/input.m4: $!";
 
     # Now ask m4 to perform the update.
-    xsystem ("$m4 --include=$tmp"
-            . join (' --include=', '', reverse (@prepend_include))
-            . join (' --include=', '', @include)
-            . " $tmp/input.m4 >$tmp/updated");
+    xsystem ("$m4 --include=" . shell_quote ($tmp)
+            . join (' --include=', '', map { shell_quote ($_) } reverse 
(@prepend_include))
+            . join (' --include=', '', map { shell_quote ($_) } @include)
+            . " " . shell_quote ("$tmp/input.m4") . " > " . shell_quote 
("$tmp/updated"));
     update_file ("$tmp/updated",
                 "$file" eq "$tmp/stdin" ? '-' : "$file");
   }
diff --git a/bin/ifnames.in b/bin/ifnames.in
index d7c36ba..bd65bbf 100644
--- a/bin/ifnames.in
+++ b/bin/ifnames.in
@@ -43,6 +43,7 @@ BEGIN
 
 use Autom4te::General;
 use Autom4te::XFile;
+use Autom4te::FileUtils;
 
 # $HELP
 # -----
@@ -92,7 +93,7 @@ my %occurrence;
 sub scan_file ($)
 {
   my ($file_name) = @_;
-  my $file = new Autom4te::XFile ($file_name);
+  my $file = new Autom4te::XFile ("< " . open_quote ($file_name));
   while ($_ = $file->getline)
     {
       # Continuation lines.
diff --git a/lib/Autom4te/FileUtils.pm b/lib/Autom4te/FileUtils.pm
index 3c9c1ba..8363c71 100644
--- a/lib/Autom4te/FileUtils.pm
+++ b/lib/Autom4te/FileUtils.pm
@@ -1,4 +1,4 @@
-# Copyright (C) 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
+# Copyright (C) 2003, 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
 
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -44,12 +44,38 @@ use Autom4te::ChannelDefs;
 use vars qw (@ISA @EXPORT);
 
 @ISA = qw (Exporter);
address@hidden = qw (&contents
address@hidden = qw (&open_quote &contents
              &find_file &mtime
              &update_file &up_to_date_p
              &xsystem &xqx &dir_has_case_matching_file &reset_dir_cache);
 
 
+=item C<open_quote ($file_name)>
+
+Quote C<$file_name> for open.
+
+=cut
+
+# $FILE_NAME
+# open_quote ($FILE_NAME)
+# -----------------------
+# If the string $S is a well-behaved file name, simply return it.
+# If it starts with white space, prepend `./', if it ends with
+# white space, add `\0'.  Return the new string.
+sub open_quote($)
+{
+  my ($s) = @_;
+  if ($s =~ m/^\s/)
+    {
+      $s = "./$s";
+    }
+  if ($s =~ m/\s$/)
+    {
+      $s = "$s\0";
+    }
+  return $s;
+}
+
 =item C<find_file ($file_name, @include)>
 
 Return the first path for a C<$file_name> in the C<include>s.
@@ -139,7 +165,7 @@ sub update_file ($$;$)
 
   if ($to eq '-')
     {
-      my $in = new IO::File ("$from");
+      my $in = new IO::File ("< " . open_quote ($from));
       my $out = new IO::File (">-");
       while ($_ = $in->getline)
        {
@@ -303,7 +329,7 @@ sub contents ($)
   my ($file) = @_;
   verb "reading $file";
   local $/;                    # Turn on slurp-mode.
-  my $f = new Autom4te::XFile "< $file";
+  my $f = new Autom4te::XFile "< " . open_quote ($file);
   my $contents = $f->getline;
   $f->close;
   return $contents;
diff --git a/lib/Autom4te/General.pm b/lib/Autom4te/General.pm
index b79467f..5639e4c 100644
--- a/lib/Autom4te/General.pm
+++ b/lib/Autom4te/General.pm
@@ -1,5 +1,6 @@
 # autoconf -- create `configure' using m4 macros
-# Copyright (C) 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007 Free Software
+# Foundation, Inc.
 
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -53,7 +54,7 @@ my @export_vars =
 # Functions we define and export.
 my @export_subs =
   qw (&debug
-      &getopt &mktmpdir
+      &getopt &shell_quote &mktmpdir
       &uniq);
 
 # Functions we forward (coming from modules we use).
@@ -281,6 +282,31 @@ sub getopt (%)
 }
 
 
+=item C<shell_quote ($file_name)>
+
+Quote C<$file_name> for the shell.
+
+=cut
+
+# $FILE_NAME
+# shell_quote ($FILE_NAME)
+# ------------------------
+# If the string $S is a well-behaved file name, simply return it.
+# If it contains white space, quotes, etc., quote it, and return
+# the new string.
+sub shell_quote($)
+{
+  my ($s) = @_;
+  if ($s =~ m![^\w+/.,-]!)
+    {
+      # Convert each single quote to '\''
+      $s =~ s/\'/\'\\\'\'/g;
+      # Then single quote the string.
+      $s = "'$s'";
+    }
+  return $s;
+}
+
 =item C<mktmpdir ($signature)>
 
 Create a temporary directory which name is based on C<$signature>.
@@ -295,10 +321,11 @@ sub mktmpdir ($)
 {
   my ($signature) = @_;
   my $TMPDIR = $ENV{'TMPDIR'} || '/tmp';
+  my $quoted_tmpdir = shell_quote ($TMPDIR);
 
   # If mktemp supports dirs, use it.
   $tmp = `(umask 077 &&
-          mktemp -d "$TMPDIR/${signature}XXXXXX") 2>/dev/null`;
+          mktemp -d $quoted_tmpdir/"${signature}XXXXXX") 2>/dev/null`;
   chomp $tmp;
 
   if (!$tmp || ! -d $tmp)
diff --git a/tests/tools.at b/tests/tools.at
index 4096d9e..199c15e 100644
--- a/tests/tools.at
+++ b/tests/tools.at
@@ -138,22 +138,73 @@ AT_CHECK([cat file], 0,
 AT_CLEANUP
 
 
-# autom4te and file names containing white space
-# ----------------------------------------------
+# autom4te and file names containing whitespace
+# ---------------------------------------------
+
+AT_SETUP([autom4te and whitespace in file names])
+
+x=
+export x
+rm -f a b
+# the first one omits special characters that are not w32 safe.
+for funny in \
+  'with  funny '\'' $x & #! name ' \
+  'with  funny \ '\'' \'\'' " <a >b * ? name '
+do
+  file=" file $funny"
+  outfile="$file out "
+  dir=" dir $funny"
+  cachedir=" cache$dir"
+  TMPDIR=" tmp$dir"
+  export TMPDIR
+
+  cat >"$file" <<'END'
+[m4@&address@hidden(foo.m4)
+m4@&address@hidden(0)d@&address@hidden
+FOO]
+END
+  # skip if we cannot create such a file or directory
+  AT_CHECK([mkdir "$dir" "$cachedir" "$TMPDIR" && test -f "$file" || exit 77])
+  cat >"$dir"/foo.m4 <<'END'
+[m4@&address@hidden([FOO], [bar])]
+END
 
-AT_SETUP([autom4te and white space in file names])
+  AT_CHECK_AUTOM4TE([-C "$cachedir" -B "$dir" --language=m4sugar -o "$outfile" 
"$file"])
+  AT_CHECK([cat "$outfile"], [],
+  [[bar
+]])
+  rm -rf "$outfile" "$cachedir"
+  AT_CHECK_AUTOM4TE([-C "$cachedir" -I "$dir" --language=m4sugar -o "$outfile" 
"$file"])
+  AT_CHECK([cat "$outfile"], [],
+  [[bar
+]])
 
-file='file with  funny \ '\'' \'\'' $ & #!*? name'
-cat >"$file.m4" <<'END'
-right
+  # This exercises a slightly different code path and will catch an open with
+  # trailing whitespace:
+  cat >"$file" <<'END'
+[m4@&address@hidden(foo.m4)
+m4@&address@hidden([^bar$])
+m4@&address@hidden(0)d@&address@hidden
+FOO]
+END
+  rm -rf "$outfile" "$cachedir"
+  AT_CHECK_AUTOM4TE([-C "$cachedir" -I "$dir" --language=m4sugar -o "$outfile" 
"$file"],
+                   [1], [], [stderr])
+  AT_CHECK([grep 'possibly undefined macro' stderr], [], [ignore])
+
+  cat >"$file" <<'END'
+[m4@&address@hidden(foo.m4)
+m4@&address@hidden(0)d@&address@hidden
 END
-# skip if we cannot create such a file
-AT_CHECK([test -f "$file.m4" || exit 77])
-AT_CHECK_AUTOM4TE([-o "$file" "$file.m4"])
+  rm -rf "$file.m4f"
+  AT_CHECK_AUTOM4TE([-C "$cachedir" -I "$dir" --language=m4sugar --freeze -o 
"$file.m4f" "$file"])
+  AT_CHECK([test -s "$file.m4f"])
 
-AT_CHECK([cat "$file"], [],
-[[right
-]])
+  # Check --reload-state
+  AT_CHECK_AUTOM4TE([-C "$cachedir" --language=m4sugar -o "$outfile" 
"$file.m4f" /dev/null])
+
+  test ! -f b
+done
 
 AT_CLEANUP
 
@@ -925,3 +976,64 @@ AT_CHECK([test "`find configure -newer newer`" = "" ||
          { diff old-requests autom4te.cache/requests; exit 1; }])
 
 AT_CLEANUP
+
+
+# autotools and file names containing whitespace
+# ---------------------------------------------
+
+AT_SETUP([autotools and whitespace in file names])
+
+x=
+export x
+rm -f a b
+# the first one omits special characters that are not w32 safe.
+for funny in \
+  'with  funny '\'' $x & #! name ' \
+  'with  funny \ '\'' \'\'' " <a >b * ? name '
+do
+  file=" file $funny"
+  dir=" dir $funny"
+  TMPDIR=" tmp$dir"
+  export TMPDIR
+
+  cat >"$file.in" <<'END'
+[AC_INIT(x,0)
+m4@&address@hidden([foo.m4])
+AC_CONFIG_HEADERS([config.h:config.hin])
+AC_MACRO
+AC_OUTPUT]
+END
+  # skip if we cannot create such a file or directory
+  AT_CHECK([mkdir "$dir" "$TMPDIR" && test -f "$file.in" || exit 77])
+  cat >"$dir"/foo.m4 <<'END'
+[AC_DEFUN([AC_MACRO], [echo hi])]
+END
+
+  AT_CHECK_AUTOHEADER([-B "$dir" "$file.in"])
+  AT_CHECK_AUTOHEADER([-I "$dir" "$file.in"])
+  AT_CHECK_AUTOUPDATE([-B "$dir" "$file.in"])
+  AT_CHECK_AUTOUPDATE([-I "$dir" "$file.in"])
+  AT_CHECK_AUTOUPDATE([-B "$dir" - < "$file.in"], [], [ignore])
+  AT_CHECK_AUTOCONF([-B "$dir" -o "$file" "$file.in"])
+  AT_CHECK_AUTOCONF([-I "$dir" -o "$file" "$file.in"])
+  # In autoconf, these exercise a slightly different code path:
+  AT_CHECK_AUTOCONF([--prepend-include="$dir" -o "$file" "$file.in"])
+  AT_CHECK_AUTOCONF([--include="$dir" -o "$file" "$file.in"])
+  AT_CHECK([autoscan -B "$dir"], [], [], [ignore])
+  AT_CHECK([autoscan -I "$dir"], [], [], [ignore])
+  # autoreconf requires a sane input file name.  Also, disable aclocal.
+  mv -f "$file.in" configure.in
+  AT_DATA([aclocal.m4])
+  AT_CHECK([autoreconf -B "$dir"])
+  AT_CHECK([autoreconf -I "$dir"])
+
+  cat >"$file.c" <<'END'
+#if FOO
+#endif
+END
+  AT_CHECK([ifnames "$file.c"], [], [ignore])
+
+  test ! -f b
+done
+
+AT_CLEANUP
-- 
1.5.3.7.1112.g9758e





reply via email to

[Prev in Thread] Current Thread [Next in Thread]