1#! @PERL@
2# autoscan - Create configure.scan (a preliminary configure.in) for a package.
3# Copyright (C) 1994 Free Software Foundation, Inc.
4
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2, or (at your option)
8# any later version.
9
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18# 02111-1307, USA.
19
20# Written by David MacKenzie <djm@gnu.ai.mit.edu>.
21
22require "find.pl";
23
24$datadir = $ENV{"AC_MACRODIR"} || "@datadir@";
25$verbose = 0;
26# Reference these variables to pacify perl -w.
27undef %identifiers_macros;
28undef %makevars_macros;
29undef %programs_macros;
30
31&parse_args;
32&init_tables;
33&find('.');
34&scan_files;
35&output;
36
37exit 0;
38
39# Process any command line arguments.
40sub parse_args
41{
42    local ($usage) =
43	"Usage: autoscan [--macrodir=dir] [--help] [--verbose] [--version] [srcdir]\n";
44
45    foreach $_ (@ARGV) {
46	if (/^--m[a-z]*=(.*)/) {
47	    $datadir = $1;
48	} elsif (/^--h/) {
49	    print "$usage";
50	    exit 0;
51	} elsif (/^--verb/) {
52	    $verbose = 1;
53	} elsif (/^--vers/) {
54	    &version;
55	} elsif (/^[^-]/) {
56	    die "$usage" if defined($srcdir);
57	    # Top level directory of the package being autoscanned.
58	    $srcdir = $_;
59	} else {
60	    die "$usage";
61	}
62    }
63
64    $srcdir="." if !defined($srcdir);
65
66    print "srcdir=$srcdir\n" if $verbose;
67    chdir $srcdir || die "$0: cannot cd to $srcdir: $!\n";
68
69    open(CONF, ">configure.scan") ||
70	die "$0: cannot create configure.scan: $!\n";
71}
72
73# Print the version number and exit.
74sub version
75{
76    open(ACG, "<$datadir/acgeneral.m4") ||
77	die "$0: cannot open $datadir/acgeneral.m4: $!\n";
78    while (<ACG>) {
79	if (/define.AC_ACVERSION.\s*([0-9.]+)/) {
80	    print "Autoconf version $1\n";
81	    exit 0;
82	}
83    }
84    die "Autoconf version unknown\n";
85}
86
87# Put values in the tables of what to do with each token.
88sub init_tables
89{
90    local($kind, $word, $macro);
91
92    # Initialize a table of C keywords (to ignore).
93    # Taken from K&R 1st edition p. 180.
94    # ANSI C, GNU C, and C++ keywords can introduce portability problems,
95    # so don't ignore them.
96    foreach $word ('int', 'char', 'float', 'double', 'struct', 'union',
97		   'long', 'short', 'unsigned', 'auto', 'extern', 'register',
98		   'typedef', 'static', 'goto', 'return', 'sizeof', 'break',
99		   'continue', 'if', 'else', 'for', 'do', 'while', 'switch',
100		   'case', 'default') {
101	$c_keywords{$word} = 0;
102    }
103
104    # The data file format supports only one line of macros per function.
105    # If more than that is required for a common portability problem,
106    # a new Autoconf macro should probably be written for that case,
107    # instead of duplicating the code in lots of configure.in files.
108
109    foreach $kind ('functions', 'headers', 'identifiers', 'programs',
110		   'makevars') {
111	open(TABLE, "<$datadir/ac$kind") ||
112	    die "$0: cannot open $datadir/ac$kind: $!\n";
113	while (<TABLE>) {
114	    next if /^\s*$/ || /^\s*#/; # Ignore blank lines and comments.
115	    ($word, $macro) = split;
116	    eval "\$$kind" . "_macros{\$word} = \$macro";
117	}
118	close(TABLE);
119    }
120}
121
122# Collect names of various kinds of files in the package.
123# Called by &find on each file.
124sub wanted
125{
126    if (/^.*\.[chlymC]$/ || /^.*\.cc$/) {
127	$name =~ s?^\./??; push(@cfiles, $name);
128    }
129    elsif (/^[Mm]akefile$/ || /^[Mm]akefile\.in$/ || /^GNUmakefile$/) {
130	$name =~ s?^\./??; push(@makefiles, $name);
131    }
132    elsif (/^.*\.sh$/) {
133	$name =~ s?^\./??; push(@shfiles, $name);
134    }
135}
136
137# Read through the files and collect lists of tokens in them
138# that might create nonportabilities.
139sub scan_files
140{
141    $initfile = $cfiles[0];		# Pick one at random.
142
143    if ($verbose) {
144	print "cfiles:", join(" ", @cfiles), "\n";
145	print "makefiles:", join(" ", @makefiles), "\n";
146	print "shfiles:", join(" ", @shfiles), "\n";
147    }
148
149    foreach $file (@cfiles) {
150	&scan_c_file($file);
151    }
152
153    foreach $file (@makefiles) {
154	&scan_makefile($file);
155    }
156
157    foreach $file (@shfiles) {
158	&scan_sh_file($file);
159    }
160}
161
162sub scan_c_file
163{
164    local($file) = @_;
165    local($in_comment) = 0;	# Nonzero if in a multiline comment.
166
167    open(CFILE, "<$file") || die "$0: cannot open $file: $!\n";
168    while (<CFILE>) {
169	# Strip out comments, approximately.
170	# Ending on this line.
171	if ($in_comment && m,\*/,) {
172	    s,.*\*/,,;
173	    $in_comment = 0;
174	}
175	# All on one line.
176	s,/\*.*\*/,,g;
177	# Starting on this line.
178	if (m,/\*,) {
179	    $in_comment = 1;
180	}
181	# Continuing on this line.
182	next if $in_comment;
183
184	# Preprocessor directives.
185	if (/^\s*#\s*include\s*<([^>]*)>/) {
186	    $headers{$1}++;
187	}
188	# Ignore other preprocessor directives.
189	next if /^\s*#/;
190
191	# Remove string and character constants.
192	s,\"[^\"]*\",,g;
193        s,\'[^\']*\',,g;
194
195	# Tokens in the code.
196	# Maybe we should ignore function definitions (in column 0)?
197	while (s/\W([a-zA-Z_]\w*)\s*\(/ /) {
198	    $functions{$1}++ if !defined($c_keywords{$1});
199	}
200	while (s/\W([a-zA-Z_]\w*)\W/ /) {
201	    $identifiers{$1}++ if !defined($c_keywords{$1});
202	}
203    }
204    close(CFILE);
205
206    if ($verbose) {
207	local($word);
208
209	print "\n$file functions:\n";
210	foreach $word (sort keys %functions) {
211	    print "$word $functions{$word}\n";
212	}
213
214	print "\n$file identifiers:\n";
215	foreach $word (sort keys %identifiers) {
216	    print "$word $identifiers{$word}\n";
217	}
218
219	print "\n$file headers:\n";
220	foreach $word (sort keys %headers) {
221	    print "$word $headers{$word}\n";
222	}
223    }
224}
225
226sub scan_makefile
227{
228    local($file) = @_;
229
230    open(MFILE, "<$file") || die "$0: cannot open $file: $!\n";
231    while (<MFILE>) {
232	# Strip out comments and variable references.
233	s/#.*//;
234	s/\$\([^\)]*\)//g;
235	s/\${[^\}]*}//g;
236	s/@[^@]*@//g;
237
238	# Variable assignments.
239	while (s/\W([a-zA-Z_]\w*)\s*=/ /) {
240	    $makevars{$1}++;
241	}
242	# Libraries.
243	while (s/\W-l([a-zA-Z_]\w*)\W/ /) {
244	    $libraries{$1}++;
245	}
246	# Tokens in the code.
247	while (s/\W([a-zA-Z_]\w*)\W/ /) {
248	    $programs{$1}++;
249	}
250    }
251    close(MFILE);
252
253    if ($verbose) {
254	local($word);
255
256	print "\n$file makevars:\n";
257	foreach $word (sort keys %makevars) {
258	    print "$word $makevars{$word}\n";
259	}
260
261	print "\n$file libraries:\n";
262	foreach $word (sort keys %libraries) {
263	    print "$word $libraries{$word}\n";
264	}
265
266	print "\n$file programs:\n";
267	foreach $word (sort keys %programs) {
268	    print "$word $programs{$word}\n";
269	}
270    }
271}
272
273sub scan_sh_file
274{
275    local($file) = @_;
276
277    open(MFILE, "<$file") || die "$0: cannot open $file: $!\n";
278    while (<MFILE>) {
279	# Strip out comments and variable references.
280	s/#.*//;
281	s/\${[^\}]*}//g;
282	s/@[^@]*@//g;
283
284	# Tokens in the code.
285	while (s/\W([a-zA-Z_]\w*)\W/ /) {
286	    $programs{$1}++;
287	}
288    }
289    close(MFILE);
290
291    if ($verbose) {
292	local($word);
293
294	print "\n$file programs:\n";
295	foreach $word (sort keys %programs) {
296	    print "$word $programs{$word}\n";
297	}
298    }
299}
300
301# Print a configure.in.
302sub output
303{
304    local (%unique_makefiles);
305
306    print CONF "dnl Process this file with autoconf to produce a configure script.\n";
307    print CONF "AC_INIT($initfile)\n";
308
309    &output_programs;
310    &output_headers;
311    &output_identifiers;
312    &output_functions;
313
314    # Change DIR/Makefile.in to DIR/Makefile.
315    foreach $_ (@makefiles) {
316	s/\.in$//;
317	$unique_makefiles{$_}++;
318    }
319    print CONF "\nAC_OUTPUT(", join(" ", keys(%unique_makefiles)), ")\n";
320
321    close CONF;
322}
323
324# Print Autoconf macro $1 if it's not undef and hasn't been printed already.
325sub print_unique
326{
327    local($macro) = @_;
328
329    if (defined($macro) && !defined($printed{$macro})) {
330	print CONF "$macro\n";
331	$printed{$macro} = 1;
332    }
333}
334
335sub output_programs
336{
337    local ($word);
338
339    print CONF "\ndnl Checks for programs.\n";
340    foreach $word (sort keys %programs) {
341	&print_unique($programs_macros{$word});
342    }
343    foreach $word (sort keys %makevars) {
344	&print_unique($makevars_macros{$word});
345    }
346    print CONF "\ndnl Checks for libraries.\n";
347    foreach $word (sort keys %libraries) {
348	print CONF "dnl Replace `\main\' with a function in -l$word:\n";
349	print CONF "AC_CHECK_LIB($word, main)\n";
350    }
351}
352
353sub output_headers
354{
355    local ($word);
356
357    print CONF "\ndnl Checks for header files.\n";
358    foreach $word (sort keys %headers) {
359	if (defined($headers_macros{$word}) &&
360	    $headers_macros{$word} eq 'AC_CHECK_HEADERS') {
361	    push(@have_headers, $word);
362	} else {
363	    &print_unique($headers_macros{$word});
364	}
365    }
366    print CONF "AC_CHECK_HEADERS(" . join(' ', sort(@have_headers)) . ")\n"
367	if defined(@have_headers);
368}
369
370sub output_identifiers
371{
372    local ($word);
373
374    print CONF "\ndnl Checks for typedefs, structures, and compiler characteristics.\n";
375    foreach $word (sort keys %identifiers) {
376	&print_unique($identifiers_macros{$word});
377    }
378}
379
380sub output_functions
381{
382    local ($word);
383
384    print CONF "\ndnl Checks for library functions.\n";
385    foreach $word (sort keys %functions) {
386	if (defined($functions_macros{$word}) &&
387	    $functions_macros{$word} eq 'AC_CHECK_FUNCS') {
388	    push(@have_funcs, $word);
389	} else {
390	    &print_unique($functions_macros{$word});
391	}
392    }
393    print CONF "AC_CHECK_FUNCS(" . join(' ', sort(@have_funcs)) . ")\n"
394	if defined(@have_funcs);
395}
396