2007-12-08 Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+ 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.
+ * 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.
+
Sync from Automake.
* lib/Autom4te/Channels.pm, lib/Autom4te/Configure_ac.pm,
lib/Autom4te/Struct.pm, lib/Autom4te/XFile.pm: Likewise.
--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?* )
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" "$@"
# 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;
# 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)
# 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.
# 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 $_;
# 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 /\@BOTTOM\@/;
# 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)
{
# 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 $_;
# 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)
{
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;
}
my ($file) = @_;
use Text::ParseWords;
- my $cfg = new Autom4te::XFile ($file);
+ my $cfg = new Autom4te::XFile ("< " . open_quote ($file));
my $lang;
while ($_ = $cfg->getline)
{
# 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' : '')
- . " @M4_DEBUGFILE@=$tcache" . $req->id . "t"
+ . " @M4_DEBUGFILE@=" . 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))
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;
}
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;
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)
{
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)
#
# 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
}
$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
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;
# 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');
# 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');
}
# 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)
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/#.*//;
# 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)
# 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)
{
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)
{
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)
{
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" .
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)
{
## -------------- ##
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;
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
# 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)
{
# 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)
{
$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;
# au.m4 -- definitions the AU macros.
xsystem ("$autoconf --trace AU_DEFINE:'_au_defun(\@<:\@\$1\@:>\@,
\@<:\@\$2\@:>\@)' --melt /dev/null "
- . ">$tmp/au.m4");
+ . ">" . shell_quote ("$tmp/au.m4"));
if ($file eq '-')
{
$file = "$tmp/stdin";
- system "cat >$file";
+ system "cat >" . shell_quote ($file);
}
elsif (! -r "$file")
{
$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>)
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");
}
use Autom4te::General;
use Autom4te::XFile;
+use Autom4te::FileUtils;
# $HELP
# -----
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.
# 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
# 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).
}
+=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>.
{
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)
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@&t@_include(foo.m4)
+m4@&t@_divert(0)d@&t@nl
+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@&t@_define([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@&t@_include(foo.m4)
+m4@&t@_pattern_forbid([^bar$])
+m4@&t@_divert(0)d@&t@nl
+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@&t@_include(foo.m4)
+m4@&t@_divert(0)d@&t@nl]
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
{ 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@&t@_include([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