This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Prevent errors when run outside metaconfig folder
[metaconfig.git] / bin / mconfig
CommitLineData
6092c506 1#!/usr/bin/perl
459d3fb5
MBT
2
3use sort "stable";
4BEGIN { $ENV{LC_ALL} = "C"; }
a8ae8817
DH
5
6use FindBin;
5bc5d425 7use Getopt::Std;
a8ae8817
DH
8
9$p5_metaconfig_base = "$FindBin::Bin/../";
2edea9d0 10chdir "$p5_metaconfig_base/perl" or
a8ae8817 11 die "perl/ directory missing in $p5_metaconfig_base\n";
459d3fb5 12
2edea9d0 13-w 'Configure' && -w 'config_h.SH' or
e9be536b
DH
14 die "both Configure and config_h.SH must be writable\n";
15
ba12031e
DH
16-l '.package' && -l 'U' or
17 die ".package and U should be symlinks as per README\n";
18
459d3fb5
MBT
19# $Id: mconfig.SH 22 2008-05-28 08:01:59Z rmanfredi $
20#
21# Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi
22#
23# You may redistribute only under the terms of the Artistic Licence,
24# as specified in the README file that comes with the distribution.
25# You may reuse parts of this distribution only within the terms of
26# that same Artistic Licence; a copy of which may be found at the root
27# of the source tree for dist 4.0.
28#
29# Original Author: Larry Wall <lwall@netlabs.com>
30# Key Contributor: Harlan Stenn <harlan@mumps.pfcs.com>
31#
32# $Log: mconfig.SH,v $
33# Revision 3.0.1.5 1995/07/25 14:19:05 ram
34# patch56: new -G option
35#
36# Revision 3.0.1.4 1994/06/20 07:11:04 ram
37# patch30: new -L option to override public library path for testing
38#
39# Revision 3.0.1.3 1994/01/24 14:20:53 ram
40# patch16: added ~/.dist_profile awareness
41#
42# Revision 3.0.1.2 1993/10/16 13:53:10 ram
43# patch12: new -M option for magic symbols and confmagic.h production
44#
45# Revision 3.0.1.1 1993/08/19 06:42:26 ram
46# patch1: leading config.sh searching was not aborting properly
47#
48# Revision 3.0 1993/08/18 12:10:17 ram
49# Baseline for dist 3.0 netwide release.
50#
51
52# Perload ON
53
a8ae8817 54$MC = "$p5_metaconfig_base/dist";
459d3fb5
MBT
55$version = '3.5';
56$patchlevel = '0';
57$grep = '/usr/bin/grep';
58chop($date = `date`);
59&profile; # Read ~/.dist_profile
c3a1ef25 60&usage unless getopts("dhkmoOstvwGMVL:X:");
459d3fb5
MBT
61
62$MC = $opt_L if $opt_L; # May override public library path
63$MC = &tilda_expand($MC); # ~name expansion
64chop($WD = `pwd`); # Working directory
65chdir $MC || die "Can't chdir to $MC: $!\n";
66chop($MC = `pwd`); # Real metaconfig lib path (no symbolic links)
67chdir $WD || die "Can't chdir back to $WD: $!\n";
68
69++$opt_k if $opt_d;
70++$opt_M if -f 'confmagic.h'; # Force -M if confmagic.h already there
71
72if ($opt_V) {
73 print STDERR "metaconfig $version PL$patchlevel\n";
74 exit 0;
75} elsif ($opt_h) {
76 &usage;
77}
78
79unlink 'Wanted' unless $opt_w; # Wanted rebuilt if no -w
80unlink 'Obsolete' unless $opt_w; # Obsolete file rebuilt if no -w
81&readpackage; # Merely get the package's name
82&init; # Various initializations
720e47b7
AC
83my %excluded_symbol;
84read_exclusions($opt_X); # Load -X or .package equivalent
459d3fb5
MBT
85`mkdir .MT 2>&1` unless -d '.MT'; # For private temporary files
86
87&locate_units; # Fill in @ARGV with a unit list
88&extract_dependencies; # Extract dependencies from units
89&extract_filenames; # Extract files to be scanned for
90&build_wanted; # Build a list of wanted symbols in file Wanted
91&build_makefile; # To do the transitive closure of dependencies
92&solve_dependencies; # Now run the makefile to close dependency graph
93&create_configure; # Create the Configure script and related files
94&cosmetic_update; # Update the manifests
95
96if ($opt_k) {
97 print "Leaving subdirectory .MT unremoved so you can peruse it.\n"
98 unless $opt_s;
99} else {
100 `rm -rf .MT 2>&1`;
101}
e9be536b 102system $^X, "Porting/config_h.pl";
459d3fb5
MBT
103print "Done.\n" unless $opt_s;
104
105# General initializations
106sub init {
107 &init_except; # Token which have upper-cased letters
108 &init_keep; # The keep status for built-in interpreter
109 &init_priority; # Priorities for diadic operators
110 &init_constants; # Define global constants
111 &init_depend; # The %Depend array records control line handling
112}
113
114sub init_constants {
115 $NEWMANI = 'MANIFEST.new'; # List of files to be scanned
116 $MANI = 'MANIFEST'; # For manifake
117
118 # The distinction between MANIFEST.new and MANIFEST can make sense
119 # when the "pat" tools are used, but if only metaconfig is used, then
120 # we can very well leave without a MANIFEST.new. --RAM, 2006-08-25
121 $NEWMANI = $MANI if -f $MANI && ! -f $NEWMANI;
122}
123
124# Record the exceptions -- almost all symbols but these are lower case
125# We also use three symbols from Unix.U for default file suffixes.
126sub init_except {
127 $Except{'Author'}++;
128 $Except{'Date'}++;
129 $Except{'Header'}++;
130 $Except{'Id'}++;
131 $Except{'Locker'}++;
132 $Except{'Log'}++;
133 $Except{'RCSfile'}++;
134 $Except{'Revision'}++;
135 $Except{'Source'}++;
136 $Except{'State'}++;
137 $Except{'_a'}++;
138 $Except{'_o'}++;
139 $Except{'_exe'}++;
140}
141
142# Print out metaconfig's usage and exits
143sub usage {
144 print STDERR <<'EOH';
2a192f88 145Usage: metaconfig [-dhkmostvwGMV] [-L dir] [-X file]
459d3fb5
MBT
146 -d : debug mode.
147 -h : print this help message and exits.
148 -k : keep temporary directory.
149 -m : assume lots of memory and swap space.
150 -o : maps obsolete symbols on new ones.
151 -s : silent mode.
152 -t : trace symbols as they are found.
153 -v : verbose mode.
154 -w : trust Wanted file as being up-to-date.
155 -G : also provide a GNU configure-like front end.
156 -L : specify main units repository.
157 -M : activate production of confmagic.h.
158 -V : print version number and exits.
2a192f88 159 -X : read symbol exclusions from file (overriding .package)
459d3fb5
MBT
160EOH
161 exit 1;
162}
163
164package locate;
165
166# Locate the units and push their path in @ARGV (sorted alphabetically)
167sub main'locate_units {
168 print "Locating units...\n" unless $main'opt_s;
169 local(*WD) = *main'WD; # Current working directory
170 local(*MC) = *main'MC; # Public metaconfig library
171 undef %myUlist; # Records private units paths
172 undef %myUseen; # Records private/public conflicts
173 &private_units; # Locate private units in @myUlist
174 &public_units; # Locate public units in @ARGV
175 @ARGV = sort @ARGV; # Sort it alphabetically
176 push(@ARGV, sort @myUlist); # Append user's units sorted
177 &dump_list if $main'opt_v; # Dump the list of units
178}
179
180# Dump the list of units on stdout
181sub dump_list {
182 print "\t";
183 $, = "\n\t";
184 print @ARGV;
185 $, = '';
186 print "\n";
187}
188
189# Scan private units
190sub private_units {
191 return unless -d 'U'; # Nothing to be done if no 'U' entry
192 local(*ARGV) = *myUlist; # Really fill in @myUlist
193 local($MC) = $WD; # We are really in the working directory
194 &units_path("U"); # Locate units in the U directory
195 local($unit_name); # Unit's name (without .U)
196 local(@kept); # Array of kept units
197 # Loop over the units and remove duplicates (the first one seen is the one
198 # we keep). Also set the %myUseen H table to record private units seen.
199 foreach (@ARGV) {
200 ($unit_name) = m|^.*/(.*)\.U$|; # Get unit's name from path
201 next if $myUseen{$unit_name}; # Already recorded
202 $myUseen{$unit_name} = 1; # Record pirvate unit
203 push(@kept, $_); # Keep this unit
204 }
205 @ARGV = @kept;
206}
207
208# Scan public units
209sub public_units {
210 chdir($MC) || die "Can't find directory $MC.\n";
211 &units_path("U"); # Locate units in public U directory
212 chdir($WD) || die "Can't go back to directory $WD.\n";
213 local($path); # Relative path from $WD
214 local($unit_name); # Unit's name (without .U)
215 local(*Unit) = *main'Unit; # Unit is a global from main package
216 local(@kept); # Units kept
217 local(%warned); # Units which have already issued a message
218 # Loop over all the units and keep only the ones that were not found in
219 # the user's U directory. As it is possible two or more units with the same
220 # name be found in
221 foreach (@ARGV) {
222 ($unit_name) = m|^.*/(.*)\.U$|; # Get unit's name from path
223 next if $warned{$unit_name}; # We have already seen this unit
224 $warned{$unit_name} = 1; # Remember we have warned the user
225 if ($myUseen{$unit_name}) { # User already has a private unit
226 $path = $Unit{$unit_name}; # Extract user's unit path
227 next if $path eq $_; # Same path, we must be in mcon/
228 $path =~ s|^$WD/||o; # Weed out leading working dir path
229 $::opt_O and next;
230 print " Your private $path overrides the public one.\n"
231 unless $main'opt_s;
232 } else {
233 push(@kept, $_); # We may keep this one
234 }
235 }
236 @ARGV = @kept;
237}
238
239# Recursively locate units in the directory. Each file ending with .U has to be
240# a unit. Others are stat()'ed, and if they are a directory, they are also
241# scanned through. The $MC and @ARGV variable are dynamically set by the caller.
242sub units_path {
243 local($dir) = @_; # Directory where units are to be found
244 local(@contents); # Contents of the directory
245 local($unit_name); # Unit's name, without final .U
246 local($path); # Full path of a unit
247 local(*Unit) = *main'Unit; # Unit is a global from main package
248 unless (opendir(DIR, $dir)) {
249 warn("Cannot open directory $dir.\n");
250 return;
251 }
252 print "Locating in $MC/$dir...\n" if $main'opt_v;
253 @contents = readdir DIR; # Slurp the whole thing
254 closedir DIR; # And close dir, ready for recursion
90dcc1e7 255 foreach (sort @contents) {
459d3fb5
MBT
256 next if $_ eq '.' || $_ eq '..';
257 if (/\.U$/) { # A unit, definitely
258 ($unit_name) = /^(.*)\.U$/;
259 $path = "$MC/$dir/$_"; # Full path of unit
260 push(@ARGV, $path); # Record its path
261 if (defined $Unit{$unit_name}) { # Already seen this unit
262 if ($main'opt_v) {
263 ($path) = $Unit{$unit_name} =~ m|^(.*)/.*|;
264 print " We've already seen $unit_name.U in $path.\n";
265 }
266 } else {
267 $Unit{$unit_name} = $path; # Map name to path
268 }
269 next;
270 }
271 # We have found a file which does not look like a unit. If it is a
272 # directory, then scan it. Otherwise skip the file.
273 unless (-d "$dir/$_") {
274 print " Skipping file $_ in $dir.\n" if $main'opt_v;
275 next;
276 }
277 &units_path("$dir/$_");
278 print "Back to $MC/$dir...\n" if $main'opt_v;
279 }
280}
281
282package main;
283
284# Initialize the extraction process by setting some variables.
285# We return a string to be eval to do more customized initializations.
286sub init_extraction {
287 open(INIT, ">$WD/.MT/Init.U") ||
288 die "Can't create .MT/Init.U\n";
289 open(CONF_H, ">$WD/.MT/Config_h.U") ||
290 die "Can't create .MT/Config_h.U\n";
291 open(EXTERN, ">$WD/.MT/Extern.U") ||
292 die "Can't create .MT/Extern.U\n";
293 open(MAGIC_H, ">$WD/.MT/Magic_h.U") ||
294 die "Can't create .MT/Magic_h.U\n";
295
296 $c_symbol = ''; # Current symbol seen in ?C: lines
297 $s_symbol = ''; # Current symbol seen in ?S: lines
298 $m_symbol = ''; # Current symbol seen in ?M: lines
299 $heredoc = ''; # Last "here" document symbol seen
300 $heredoc_nosubst = 0; # True for <<'EOM' here docs
301 $condlist = ''; # List of conditional symbols
302 $defined = ''; # List of defined symbols in the unit
303 $body = ''; # No procedure to handle body
304 $ending = ''; # No procedure to clean-up
305}
306
307# End the extraction process
308sub end_extraction {
309 close EXTERN; # External dependencies (libraries, includes...)
310 close CONF_H; # C symbol definition template
311 close INIT; # Required initializations
312 close MAGIC; # Magic C symbol redefinition templates
313
314 print $dependencies if $opt_v; # Print extracted dependencies
315}
316
317# Process the ?MAKE: line
318sub p_make {
319 local($_) = @_;
320 local(@ary); # Locally defined symbols
321 local(@dep); # Dependencies
322 if (/^[\w+ ]*:/) { # Main dependency rule
323 s|^\s*||; # Remove leading spaces
324 chop;
325 s/:(.*)//;
326 @dep = split(' ', $1); # Dependencies
327 @ary = split(' '); # Locally defined symbols
328 foreach $sym (@ary) {
329 # Symbols starting with a '+' are meant for internal use only.
330 next if $sym =~ s/^\+//;
331 # Only sumbols starting with a lowercase letter are to
332 # appear in config.sh, excepted the ones listed in Except.
333 if ($sym =~ /^[_a-z]/ || $Except{$sym}) {
334 $shmaster{"\$$sym"} = undef;
335 push(@Master,"?$unit:$sym=''\n"); # Initializations
336 }
337 }
338 $condlist = ''; # List of conditional symbols
339 local($sym); # Symbol copy, avoid @dep alteration
340 foreach $dep (@dep) {
341 if ($dep =~ /^\+[A-Za-z]/) {
342 ($sym = $dep) =~ s|^\+||;
343 $condlist .= "$sym ";
344 push(@Cond, $sym) unless $condseen{$sym};
345 $condseen{$sym}++; # Conditionally wanted
346 }
347 }
348 # Append to already existing dependencies. The 'defined' variable
349 # is set for &write_out, used to implement ?L: and ?I: canvas. It is
350 # reset each time a new unit is parsed.
351 # NB: leading '+' for defined symbols (internal use only) have been
352 # removed at this point, but conditional dependencies still bear it.
353 $defined = join(' ', @ary); # Symbols defined by this unit
354 $dependencies .= $defined . ':' . join(' ', @dep) . "\n";
355 $dependencies .= " -cond $condlist\n" if $condlist;
356 } else {
357 $dependencies .= $_; # Building rules
358 }
359}
360
361# Process the ?O: line
362sub p_obsolete {
363 local($_) = @_;
364 $Obsolete{"$unit.U"} .= $_; # Message(s) to print if unit is used
365}
366
367# Process the ?S: lines
368sub p_shell {
369 local($_) = @_;
370 unless ($s_symbol) {
371 if (/^(\w+).*:/) {
372 $s_symbol = $1;
373 print " ?S: $s_symbol\n" if $opt_d;
374 } else {
375 warn "\"$file\", line $.: syntax error in ?S: construct.\n";
376 $s_symbol = $unit;
377 return;
378 }
379 # Deal with obsolete symbol list (enclosed between parenthesis)
380 &record_obsolete("\$$_") if /\(/;
381 }
382 m|^\.\s*$| && ($s_symbol = ''); # End of comment
383}
384
385# Process the ?C: lines
386sub p_c {
387 local($_) = @_;
388 unless ($c_symbol) {
389 if (s/^(\w+)\s*~\s*(\S+)\s*(.*):/$1 $3:/) {
390 # The ~ operator aliases the main C symbol to another symbol which
391 # is to be used instead for definition in config.h. That is to say,
392 # the line '?C:SYM ~ other:' would look for symbol 'other' instead,
393 # and the documentation for symbol SYM would only be included in
394 # config.h if 'other' were actually wanted.
395 $c_symbol = $2; # Alias for definition in config.h
396 print " ?C: $1 ~ $c_symbol\n" if $opt_d;
397 } elsif (/^(\w+).*:/) {
398 # Default behaviour. Include in config.h if symbol is needed.
399 $c_symbol = $1;
400 print " ?C: $c_symbol\n" if $opt_d;
401 } else {
402 warn "\"$file\", line $.: syntax error in ?C: construct.\n";
403 $c_symbol = $unit;
404 return;
405 }
406 # Deal with obsolete symbol list (enclosed between parenthesis) and
407 # make sure that list do not appear in config.h.SH by removing it.
408 &record_obsolete("$_") if /\(/;
409 s/\s*\(.*\)//; # Get rid of obsolete symbol list
410 }
411 s|^(\w+)\s*|?$c_symbol:/* $1| || # Start of comment
412 (s|^\.\s*$|?$c_symbol: */\n| && ($c_symbol = '', 1)) || # End of comment
413 s|^(.*)|?$c_symbol: *$1|; # Middle of comment
414 &p_config("$_"); # Add comments to config.h.SH
415}
416
417# Process the ?H: lines
418sub p_config {
419 local($_) = @_;
420 local($constraint); # Constraint to be used for inclusion
421 ++$old_version if s/^\?%1://; # Old version
422 if (s/^\?(\w+)://) { # Remove leading '?var:'
423 $constraint = $1; # Constraint is leading '?var'
424 } else {
425 $constraint = ''; # No constraint
426 }
427 if (/^#.*\$/) { # Look only for cpp lines
428 if (m|^#\$(\w+)\s+(\w+).*\$(\w+)|) {
429 # Case: #$d_var VAR "$var"
430 $constraint = $2 unless $constraint;
431 print " ?H: ($constraint) #\$$1 $2 \"\$$3\"\n" if $opt_d;
432 $cmaster{$2} = undef;
433 $cwanted{$2} = "$1\n$3";
434 } elsif (m|^#define\s+(\w+)\((.*)\)\s+\$(\w+)|) {
435 # Case: #define VAR(x) $var
436 $constraint = $1 unless $constraint;
437 print " ?H: ($constraint) #define $1($2) \$$3\n" if $opt_d;
438 $cmaster{$1} = undef;
439 $cwanted{$1} = $3;
440 } elsif (m|^#\$define\s+(\w+)|) {
441 # Case: #$define VAR
442 $constraint = $1 unless $constraint;
443 print " ?H: ($constraint) #define $1\n" if $opt_d;
444 $cmaster{$1} = undef;
445 $cwanted{$1} = "define\n$unit";
446 } elsif (m|^#\$(\w+)\s+(\w+)|) {
447 # Case: #$d_var VAR
448 $constraint = $2 unless $constraint;
449 print " ?H: ($constraint) #\$$1 $2\n" if $opt_d;
450 $cmaster{$2} = undef;
451 $cwanted{$2} = $1;
452 } elsif (m|^#define\s+(\w+).*\$(\w+)|) {
453 # Case: #define VAR "$var"
454 $constraint = $1 unless $constraint;
455 print " ?H: ($constraint) #define $1 \"\$$2\"\n" if $opt_d;
456 $cmaster{$1} = undef;
457 $cwanted{$1} = $2;
458 } else {
459 $constraint = $unit unless $constraint;
460 print " ?H: ($constraint) $_" if $opt_d;
461 }
462 } else {
463 print " ?H: ($constraint) $_" if $opt_d;
464 }
465 # If not a single ?H:. line, add the leading constraint
466 s/^\.// || s/^/?$constraint:/;
467 print CONF_H;
468}
469
470# Process the ?M: lines
471sub p_magic {
472 local($_) = @_;
473 unless ($m_symbol) {
474 if (/^(\w+):\s*([\w\s]*)\n$/) {
475 # A '?M:sym:' line implies a '?W:%<:sym' since we'll need to know
476 # about the wantedness of sym later on when building confmagic.h.
477 # Buf is sym is wanted, then the C symbol dependencies have to
478 # be triggered. That is done by introducing sym in the mwanted
479 # array, known by the Wanted file construction process...
480 $m_symbol = $1;
481 print " ?M: $m_symbol\n" if $opt_d;
482 $mwanted{$m_symbol} = $2; # Record C dependencies
483 &p_wanted("$unit:$m_symbol"); # Build fake ?W: line
484 } else {
485 warn "\"$file\", line $.: syntax error in ?M: construct.\n";
486 }
487 return;
488 }
489 (s/^\.\s*$/?$m_symbol:\n/ && ($m_symbol = '', 1)) || # End of block
490 s/^/?$m_symbol:/;
491 print MAGIC_H; # Definition goes to confmagic.h
492 print " ?M: $_" if $opt_d;
493}
494
495sub p_ignore {} # Ignore comment line
496sub p_lint {} # Ignore lint directives
497sub p_visible {} # No visible checking in metaconfig
498sub p_temp {} # No temporary variable control
499sub p_file {} # Ignore produced file directives (for now)
500
501# Process the ?W: lines
502sub p_wanted {
503 # Syntax is ?W:<shell symbols>:<C symbols>
504 local($active) = $_[0] =~ /^([^:]*):/; # Symbols to activate
505 local($look_symbols) = $_[0] =~ /:(.*)/; # When those are used
506 local(@syms) = split(/ /, $look_symbols); # Keep original spacing info
507 $active =~ s/\s+/\n/g; # One symbol per line
508
509 # Concatenate quoted strings, so saying something like 'two words' will
510 # be introduced as one single symbol "two words".
511 local(@symbols); # Concatenated symbols to look for
512 local($concat) = ''; # Concatenation buffer
513 foreach (@syms) {
514 if (s/^\'//) {
515 $concat = $_;
516 } elsif (s/\'$//) {
517 push(@symbols, $concat . ' ' . $_);
518 $concat = '';
519 } else {
520 push(@symbols, $_) unless $concat;
521 $concat .= ' ' . $_ if $concat;
522 }
523 }
524
525 # Now record symbols in master and wanted tables
526 foreach (@symbols) {
527 $cmaster{$_} = undef; # Asks for look-up in C files
528 $cwanted{$_} = "$active" if $active; # Shell symbols to activate
529 }
c3a1ef25
AC
530
531 delete @cmaster{keys %excluded_symbol};
532 delete @cwanted{keys %excluded_symbol};
459d3fb5
MBT
533}
534
535# Process the ?INIT: lines
536sub p_init {
537 local($_) = @_;
538 print INIT "?$unit:", $_; # Wanted only if unit is loaded
539}
540
541# Process the ?D: lines
542sub p_default {
543 local($_) = @_;
544 s/^([A-Za-z_]+)=(.*)/\@if !$1\n%$1:$1=$2\n\@define $1\n\@end/
545 && ($hasdefault{$1}++, print INIT $_);
546}
547
548# Process the ?P: lines
549sub p_public {
550 local($_) = @_;
551 local($csym); # C symbol(s) we're trying to look at
552 local($nosym); # List of symbol(s) which mustn't be wanted
553 local($cfile); # Name of file implementing csym (no .ext)
554 ($csym, $nosym, $cfile) = /([^()]+)\s*(\(.*\))\s*:\s*(\S+)/;
555 unless ($csym eq '' || $cfile eq '') {
556 # Add dependencies for each C symbol, of the form:
557 # -pick public <sym> <file> <notdef symbols list>
558 # and the file will be added to config.c whenever sym is wanted and
559 # none of the notdef symbols is wanted.
560 foreach $sym (split(' ', $csym)) {
561 $dependencies .= "\t-pick public $sym $cfile $nosym\n";
562 }
563 }
564}
565
566# Process the ?Y: lines
567# Valid layouts are for now are: top, bottom, default.
568#
569# NOTA BENE:
570# This routine relies on the $defined variable, a global variable set
571# during the ?MAKE: processing, which lists all the defined symbols in
572# the unit (the optional leading '+' for internal symbols has been removed
573# if present).
574#
575# The routine fills up a %Layout table, indexed by symbol, yielding the
576# layout imposed to this unit. That table will then be used later on when
577# we sort wanted symbols for the Makefile.
578sub p_layout {
579 local($_) = @_;
580 local($layout) = /^\s*(\w+)/;
581 $layout =~ tr/A-Z/a-z/; # Case is not significant for layouts
582 unless (defined $Lcmp{$layout}) {
583 warn "\"$file\", line $.: unknown layout directive '$layout'.\n";
584 return;
585 }
586 foreach $sym (split(' ', $defined)) {
587 $Layout{$sym} = $Lcmp{$layout};
588 }
589}
590
591# Process the ?L: lines
592# There should not be any '-l' in front of the library name
593sub p_library {
594 &write_out("L:$_");
595}
596
597# Process the ?I: lines
598sub p_include {
599 &write_out("I:$_");
600}
601
602# Write out line in file Extern.U. The information recorded there has the
603# following prototypical format:
604# ?symbol:L:inet bsd
605# If 'symbol' is wanted, then 'inet bsd' will be added to $libswanted.
606sub write_out {
607 local($_) = @_;
608 local($target) = $defined; # By default, applies to defined symbols
609 $target = $1 if s/^(.*)://; # List is qualified "?L:target:symbols"
610 local(@target) = split(' ', $target);
611 chop;
612 foreach $key (@target) {
613 print EXTERN "?$key:$_\n"; # EXTERN file defined in xref.pl
614 }
615}
616
617# The %Depend array records the functions we use to process the configuration
618# lines in the unit, with a special meaning. It is important that all the
619# known control symbols be listed below, so that metalint does not complain.
620# The %Lcmp array contains valid layouts and their comparaison value.
621sub init_depend {
622 %Depend = (
623 'MAKE', 'p_make', # The ?MAKE: line records dependencies
624 'INIT', 'p_init', # Initializations printed verbatim
625 'LINT', 'p_lint', # Hints for metalint
626 'RCS', 'p_ignore', # RCS comments are ignored
627 'C', 'p_c', # C symbols
628 'D', 'p_default', # Default value for conditional symbols
629 'E', 'p_example', # Example of usage
630 'F', 'p_file', # Produced files
631 'H', 'p_config', # Process the config.h lines
632 'I', 'p_include', # Added includes
633 'L', 'p_library', # Added libraries
634 'M', 'p_magic', # Process the confmagic.h lines
635 'O', 'p_obsolete', # Unit obsolescence
636 'P', 'p_public', # Location of PD implementation file
637 'S', 'p_shell', # Shell variables
638 'T', 'p_temp', # Shell temporaries used
639 'V', 'p_visible', # Visible symbols like 'rp', 'dflt'
640 'W', 'p_wanted', # Wanted value for interpreter
641 'X', 'p_ignore', # User comment is ignored
642 'Y', 'p_layout', # User-defined layout preference
643 );
644 %Lcmp = (
645 'top', -1,
646 'default', 0,
647 'bottom', 1,
648 );
649}
650
651# Extract dependencies from units held in @ARGV
652sub extract_dependencies {
653 local($proc); # Procedure used to handle a ctrl line
654 local($file); # Current file scanned
655 local($dir, $unit); # Directory and unit's name
656 local($old_version) = 0; # True when old-version unit detected
657 local($mc) = "$MC/U"; # Public metaconfig directory
658 local($line); # Last processed line for metalint
659
660 printf "Extracting dependency lists from %d units...\n", $#ARGV+1
661 unless $opt_s;
662
663 chdir $WD; # Back to working directory
664 &init_extraction; # Initialize extraction files
665 $dependencies = ' ' x (50 * @ARGV); # Pre-extend
666 $dependencies = '';
667
668 # We do not want to use the <> construct here, because we need the
669 # name of the opened files (to get the unit's name) and we want to
670 # reset the line number for each files, and do some pre-processing.
671
672 file: while ($file = shift(@ARGV)) {
673 close FILE; # Reset line number
674 $old_version = 0; # True if unit is an old version
675 if (open(FILE, $file)) {
676 ($dir, $unit) = ('', $file)
677 unless ($dir, $unit) = ($file =~ m|(.*)/(.*)|);
678 $unit =~ s|\.U$||; # Remove extension
679 } else {
680 warn("Can't open $file.\n");
681 }
682 # If unit is in the standard public directory, keep only the unit name
683 $file = "$unit.U" if $dir eq $mc;
684 print "$dir/$unit.U:\n" if $opt_d;
685 line: while (<FILE>) {
686 $line = $_; # Save last processed unit line
687 if (s/^\?([\w\-]+)://) { # We may have found a control line
688 $proc = $Depend{$1}; # Look for a procedure to handle it
689 unless ($proc) { # Unknown control line
690 $proc = $1; # p_unknown expects symbol in '$proc'
691 eval '&p_unknown'; # Signal error (metalint only)
692 next line; # And go on next line
693 }
694 # Long lines may be escaped with a final backslash
695 $_ .= &complete_line(FILE) if s/\\\s*$//;
696 # Run macros substitutions
697 s/%</$unit/g; # %< expands into the unit's name
698 if (s/%\*/$unit/) {
699 # %* expanded into the entire set of defined symbols
700 # in the old version. Now it is only the unit's name.
701 ++$old_version;
702 }
703 eval { &$proc($_) }; # Process the line
704 } else {
705 next file unless $body; # No procedure to handle body
706 do {
707 $line = $_; # Save last processed unit line
708 eval { &$body($_) } ; # From now on, it's the unit body
709 } while (defined ($_ = <FILE>));
710 next file;
711 }
712 }
713 } continue {
714 warn(" Warning: $file is a pre-3.0 version.\n") if $old_version;
715 &$ending($line) if $ending; # Post-processing for metalint
716 }
717
718 &end_extraction; # End the extraction process
719}
720
721# The first line was escaped with a final \ character. Every following line
722# is to be appended to it (until we found a real \n not escaped). Note that
723# the leading spaces of the continuation line are removed, so any space should
724# be added before the former \ if needed.
725sub complete_line {
726 local($file) = @_; # File where lines come from
727 local($_);
728 local($read) = ''; # Concatenation of all the continuation lines found
729 while (<$file>) {
730 s/^\s+//; # Remove leading spaces
731 if (s/\\\s*$//) { # Still followed by a continuation line
732 $read .= $_;
733 } else { # We've reached the end of the continuation
734 return $read . $_;
735 }
736 }
737}
738
739# Extract filenames from manifest
740sub extract_filenames {
741 &build_filext; # Construct &is_cfile and &is_shfile
742 print "Extracting filenames (C and SH files) from $NEWMANI...\n"
743 unless $opt_s;
744 open(NEWMANI,$NEWMANI) || die "Can't open $NEWMANI.\n";
745 local($file);
746 while (<NEWMANI>) {
747 ($file) = split(' ');
748 next if $file eq 'config_h.SH'; # skip config_h.SH
749 next if $file eq 'Configure'; # also skip Configure
750 next if $file eq 'confmagic.h' && $opt_M;
751 push(@SHlist, $file) if &is_shfile($file);
752 push(@clist, $file) if &is_cfile($file);
753 }
754}
755
756# Construct two file identifiers based on the file suffix: one for C files,
757# and one for SH files (using the $cext and $shext variables) defined in
758# the .package file.
759# The &is_cfile and &is_shfile routine may then be called to known whether
760# a given file is a candidate for holding C or SH symbols.
761sub build_filext {
762 &build_extfun('is_cfile', $cext, '.c .h .y .l');
763 &build_extfun('is_shfile', $shext, '.SH');
764}
765
766# Build routine $name to identify extensions listed in $exts, ensuring
767# that $minimum is at least matched (both to be backward compatible with
768# older .package and because it is really the minimum requirred).
769sub build_extfun {
770 local($name, $exts, $minimum) = @_;
771 local(@single); # Single letter dot extensions (may be grouped)
772 local(@others); # Other extensions
773 local(%seen); # Avoid duplicate extensions
774 foreach $ext (split(' ', "$exts $minimum")) {
775 next if $seen{$ext}++;
776 if ($ext =~ s/^\.(\w)$/$1/) {
777 push(@single, $ext);
778 } else {
779 # Convert into perl's regexp
780 $ext =~ s/\./\\./g; # Escape .
781 $ext =~ s/\?/./g; # ? turns into .
782 $ext =~ s/\*/.*/g; # * turns into .*
783 push(@others, $ext);
784 }
785 }
786 local($fn) = &q(<<EOF); # Function being built
787:sub $name {
788: local(\$_) = \@_;
789EOF
790 local($single); # Single regexp: .c .h grouped into .[ch]
791 $single = '\.[' . join('', @single) . ']' if @single;
792 $fn .= &q(<<EOL) if @single;
793: return 1 if /$single\$/;
794EOL
795 foreach $ext (@others) {
796 $fn .= &q(<<EOL);
797: return 1 if /$ext\$/;
798EOL
799 }
800 $fn .= &q(<<EOF);
801: 0; # None of the extensions may be applied to file name
802:}
803EOF
804 print $fn if $opt_d;
805 eval $fn;
806 chop($@) && die "Can't compile '$name':\n$fn\n$@.\n";
807}
808
809# Remove ':' quotations in front of the lines
810sub q {
811 local($_) = @_;
812 s/^://gm;
813 $_;
814}
815
c3a1ef25
AC
816sub read_exclusions {
817 my ($filename) = @_;
2a192f88
AC
818 if (!defined $filename) {
819 $filename = $exclusions_file; # default to name from .package
820 return if !defined $filename || $filename eq '';
821 }
c3a1ef25
AC
822 print "Reading exclusions from $filename...\n" unless $opt_s;
823 open(EXCLUSIONS, "< $filename\0") || die "Can't read $filename: $!\n";
824 local $_;
825 while (<EXCLUSIONS>) {
826 if (/^\s*#|^\s*$/) {
827 # comment or blank line, ignore
828 }
829 elsif (/^\s*(\w+)\s*$/) {
830 $excluded_symbol{$1} = 1;
831 }
832 else {
833 die "$filename:$.: unrecognised line\n";
834 }
835 }
836 close(EXCLUSIONS) || die "Can't close $filename: $!\n";
837}
838
459d3fb5
MBT
839# Build a wanted file from the files held in @SHlist and @clist arrays
840sub build_wanted {
841 # If wanted file is already there, parse it to map obsolete if -o option
842 # was used. Otherwise, build a new one.
843 if (-f 'Wanted') {
844 &map_obsolete if $opt_o; # Build Obsol*.U files
845 &dump_obsolete; # Dump obsolete symbols if any
846 return;
847 }
848 &parse_files;
849}
850
851sub parse_files {
852 print "Building a Wanted file...\n" unless $opt_s;
853 open(WANTED,"| sort | uniq >Wanted") || die "Can't create Wanted.\n";
854 unless (-f $NEWMANI) {
855 &manifake;
856 die "No $NEWMANI--can't build a Wanted file.\n" unless -f $NEWMANI;
857 }
858
859 local($search); # Where to-be-evaled script is held
860 local($_) = ' ' x 50000 if $opt_m; # Pre-extend pattern search space
861 local(%visited); # Records visited files
862 local(%lastfound); # Where last occurence of key was
863
864 # Now we are a little clever, and build a loop to eval so that we don't
865 # have to recompile our patterns on every file. We also use "study" since
866 # we are searching the same string for many different things. Hauls!
867
868 if (@clist) {
869 local($others) = $cext ? " $cext" : '';
870 print " Scanning .[chyl]$others files for symbols...\n"
871 unless $opt_s;
872 $search = ' ' x (40 * (@cmaster + @ocmaster)); # Pre-extend
873 $search = "while (<>) {study;\n"; # Init loop over ARGV
874 foreach $key (keys(%cmaster)) {
875 $search .= "&cmaster('$key') if /\\b$key\\b/;\n";
876 }
877 foreach $key (grep(!/^\$/, keys %Obsolete)) {
878 $search .= "&ofound('$key') if /\\b$key\\b/;\n";
879 }
880 $search .= "}\n"; # terminate loop
881 print $search if $opt_d;
882 @ARGV = @clist;
883 # Swallow each file as a whole, if memory is available
884 undef $/ if $opt_m;
885 eval $search;
886 eval '';
887 $/ = "\n";
888 while (($key,$value) = each(%cmaster)) {
889 print WANTED $cwanted{$key}, "\n", ">$key", "\n" if $value;
890 }
891 }
892
893 # If they don't use magic but use magically guarded symbols without
894 # their corresponding C symbol dependency, warn them, since they might
895 # not know about that portability issue.
896
897 if (@clist && !$opt_M) {
898 local($nused); # list of non-used symbols
899 local($warning) = 0; # true when one warning issued
900 foreach $cmag (keys %mwanted) { # loop over all used magic symbols
901 next unless $cmaster{$cmag};
902 $nused = '';
903 foreach $cdep (split(' ', $mwanted{$cmag})) {
904 $nused .= " $cdep" unless $cmaster{$cdep};
905 }
906 $nused =~ s/^ //;
907 $nused = "one of " . $nused if $nused =~ s/ /, /g;
908 if ($nused ne '') {
909 print " Warning: $cmag is used without $nused.\n";
910 $warning++;
911 }
912 }
913 if ($warning) {
914 local($those) = $warning == 1 ? 'that' : 'those';
915 local($s) = $warning == 1 ? '' : 's';
916 print "Note: $those previous warning$s may be suppressed by -M.\n";
917 }
918 }
919
920 # Cannot remove $cmaster as it is used later on when building Configure
921 undef @clist;
922 undef %cwanted;
923 undef %mwanted;
924 %visited = ();
925 %lastfound = ();
926
927 if (@SHlist) {
928 local($others) = $shext ? " $shext" : '';
929 print " Scanning .SH$others files for symbols...\n" unless $opt_s;
930 $search = ' ' x (40 * (@shmaster + @oshmaster)); # Pre-extend
931 $search = "while (<>) {study;\n";
932 # All the keys already have a leading '$'
933 foreach $key (keys(%shmaster)) {
934 $search .= "&shmaster('$key') if /\\$key\\b/;\n";
935 }
936 foreach $key (grep (/^\$/, keys %Obsolete)) {
937 $search .= "&ofound('$key') if /\\$key\\b/;\n";
938 }
939 $search .= "}\n";
940 print $search if $opt_d;
941 @ARGV = @SHlist;
942 # Swallow each file as a whole, if memory is available
943 undef $/ if $opt_m;
944 eval $search;
945 eval '';
946 $/ = "\n";
947 while (($key,$value) = each(%shmaster)) {
948 if ($value) {
949 $key =~ s/^\$//;
950 print WANTED $key, "\n";
951 }
952 }
953 }
954
955 # Obsolete symbols, if any, are written in the Wanted file preceded by a
956 # '!' character. In case -w is used, we'll thus be able to correctly build
957 # the Obsol_h.U and Obsol_sh.U files.
958
959 &add_obsolete; # Add obsolete symbols in Wanted file
960
961 close WANTED;
962
963 # If obsolete symbols where found, write an Obsolete file which lists where
964 # each of them appear and the new symbol to be used. Also write Obsol_h.U
965 # and Obsol_sh.U in .MT for later perusal.
966
967 &dump_obsolete; # Dump obsolete symbols if any
968
969 die "No desirable symbols found--aborting.\n" unless -s 'Wanted';
970
971 # Clean-up memory by freeing useless data structures
972 undef @SHlist;
973 undef %shmaster;
974}
975
976# This routine records matches of C master keys
977sub cmaster {
978 local($key) = @_;
979 $cmaster{$key}++; # This symbol is wanted
980 return unless $opt_t || $opt_M; # Return if neither -t nor -M
981 if ($opt_t &&
982 $lastfound{$key} ne $ARGV # Never mentionned for this file ?
983 ) {
984 $visited{$ARGV}++ || print $ARGV,":\n";
985 print "\t$key\n";
986 $lastfound{$key} = $ARGV;
987 }
988 if ($opt_M &&
989 defined($mwanted{$key}) # Found a ?M: symbol
990 ) {
991 foreach $csym (split(' ', $mwanted{$key})) {
992 $cmaster{$csym}++; # Activate C symbol dependencies
993 }
994 }
995}
996
997# This routine records matches of obsolete keys (C or shell)
998sub ofound {
999 local($key) = @_;
1000 local($_) = $Obsolete{$key}; # Value of new symbol
1001 $ofound{"$ARGV $key $_"}++; # Record obsolete match
1002 $cmaster{$_}++ unless /^\$/; # A C hit
1003 $shmaster{$_}++ if /^\$/; # Or a shell one
1004 return unless $opt_t; # Continue if trace option on
1005 if ($lastfound{$key} ne $ARGV) { # Never mentionned for this file ?
1006 $visited{$ARGV}++ || print $ARGV,":\n";
1007 print "\t$key (obsolete, use $_)\n";
1008 $lastfound{$key} = $ARGV;
1009 }
1010}
1011
1012# This routine records matches of shell master keys
1013sub shmaster {
1014 local($key) = @_;
1015 $shmaster{$key}++; # This symbol is wanted
1016 return unless $opt_t; # Continue if trace option on
1017 if ($lastfound{$key} ne $ARGV) { # Never mentionned for this file ?
1018 $visited{$ARGV}++ || print $ARGV,":\n";
1019 print "\t$key\n";
1020 $lastfound{$key} = $ARGV;
1021 }
1022}
1023
1024# Write obsolete symbols into the Wanted file for later perusal by -w.
1025sub add_obsolete {
1026 local($file); # File where obsolete symbol was found
1027 local($old); # Name of this old symbol
1028 local($new); # Value of the new symbol to be used
1029 foreach $key (sort keys %ofound) {
1030 ($file, $old, $new) = ($key =~ /^(\S+)\s+(\S+)\s+(\S+)/);
1031 if ($new =~ s/^\$//) { # We found an obsolete shell symbol
1032 print WANTED "!$old\n";
1033 } else { # We found an obsolete C symbol
1034 print WANTED "!>$old\n";
1035 }
1036 }
1037}
1038
1039# Map obsolete symbols from Wanted file into %Obsolete and call dump_obsolete
1040# to actually build the Obsol_sh.U and Obsol_h.U files. Those will be needed
1041# during the Configure building phase to actually do the remaping.
1042# The obsolete symbols found are entered in the %ofound array, tagged as from
1043# file 'XXX', which is specially recognized by dump_obsolete.
1044sub map_obsolete {
1045 open(WANTED, 'Wanted') || die "Can't open Wanted file.\n";
1046 local($new); # New symbol to be used instead of obsolete one
1047 while (<WANTED>) {
1048 chop;
1049 next unless s/^!//; # Skip non-obsolete symbols
1050 if (s/^>//) { # C symbol
1051 $new = $Obsolete{$_}; # Fetch new symbol
1052 $ofound{"XXX $_ $new"}++; # Record obsolete match (XXX = no file)
1053 } else { # Shell symbol
1054 $new = $Obsolete{"\$$_"}; # Fetch new symbol
1055 $ofound{"XXX \$$_ $new"}++; # Record obsolete match (XXX = no file)
1056 }
1057 }
1058 close WANTED;
1059}
1060
1061# Record obsolete symbols association (new versus old), that is to say for a
1062# given old symbol, $Obsolete{'old'} = new symbol to be used. A '$' is prepended
1063# for all shell variables
1064sub record_obsolete {
1065 local($_) = @_;
1066 local(@obsoleted); # List of obsolete symbols
1067 local($symbol); # New symbol which must be used
1068 local($dollar) = s/^\$// ? '$':''; # The '$' or a null string
1069 # Syntax for obsolete symbols specification is
1070 # list of symbols (obsolete ones):
1071 if (/^(\w+)\s*\((.*)\)\s*:$/) {
1072 $symbol = "$dollar$1";
1073 @obsoleted = split(' ', $2); # List of obsolete symbols
1074 } else {
1075 if (/^(\w+)\s*\((.*):$/) {
1076 warn "\"$file\", line $.: final ')' before ':' missing.\n";
1077 $symbol = "$dollar$1";
1078 @obsoleted = split(' ', $2);
1079 } else {
1080 warn "\"$file\", line $.: syntax error.\n";
1081 return;
1082 }
1083 }
1084 foreach $val (@obsoleted) {
1085 $_ = $dollar . $val;
1086 if (defined $Obsolete{$_}) {
1087 warn "\"$file\", line $.: '$_' already obsoleted by '$Obsolete{$_}'.\n";
1088 } else {
1089 $Obsolete{$_} = $symbol; # Record (old, new) tuple
1090 }
1091 }
1092}
1093
1094# Dump obsolete symbols used in file 'Obsolete'. Also write Obsol_h.U and
1095# Obsol_sh.U to record old versus new mappings if the -o option was used.
1096sub dump_obsolete {
1097 unless (-f 'Obsolete') {
1098 open(OBSOLETE, ">Obsolete") || die "Can't create Obsolete.\n";
1099 }
1100 open(OBSOL_H, ">.MT/Obsol_h.U") || die "Can't create .MT/Obsol_h.U.\n";
1101 open(OBSOL_SH, ">.MT/Obsol_sh.U") || die "Can't create .MT/Obsol_sh.U.\n";
1102 local($file); # File where obsolete symbol was found
1103 local($old); # Name of this old symbol
1104 local($new); # Value of the new symbol to be used
1105 # Leave a blank line at the top so that anny added ^L will stand on a line
1106 # by itself (the formatting process adds a ^L when a new page is needed).
1107 format OBSOLETE_TOP =
1108
1109 File | Old symbol | New symbol
1110-----------------------------------+----------------------+---------------------
1111.
1112 format OBSOLETE =
1113@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | @<<<<<<<<<<<<<<<<<<< | @<<<<<<<<<<<<<<<<<<<
1114$file, $old, $new
1115.
1116 local(%seen);
1117 foreach $key (sort keys %ofound) {
1118 ($file, $old, $new) = ($key =~ /^(\S+)\s+(\S+)\s+(\S+)/);
1119 write(OBSOLETE) unless $file eq 'XXX';
1120 next unless $opt_o; # Obsolete mapping done only with -o
1121 next if $seen{$old}++; # Already remapped, thank you
1122 if ($new =~ s/^\$//) { # We found an obsolete shell symbol
1123 $old =~ s/^\$//;
1124 print OBSOL_SH "$old=\"\$$new\"\n";
1125 } else { # We found an obsolete C symbol
1126 print OBSOL_H "#ifdef $new\n";
1127 print OBSOL_H "#define $old $new\n";
1128 print OBSOL_H "#endif\n\n";
1129 }
1130 }
1131 close OBSOLETE;
1132 close OBSOL_H;
1133 close OBSOL_SH;
1134 if (-s 'Obsolete') {
1135 print "*** Obsolete symbols found -- see file 'Obsolete' for a list.\n";
1136 } else {
1137 unlink 'Obsolete';
1138 }
1139 undef %ofound; # Not needed any more
1140}
1141
1142# Build the private makefile we use to compute the transitive closure of the
1143# previously determined dependencies.
1144sub build_makefile {
1145 print "Computing optimal dependency graph...\n" unless $opt_s;
1146 chdir('.MT') || die "Can't chdir to .MT\n";
1147 local($wanted); # Wanted shell symbols
1148 &build_private; # Build a first makefile from dependencies
1149 &compute_loadable; # Compute loadable units
1150 &update_makefile; # Update makefile using feedback from first pass
1151 chdir($WD) || die "Can't chdir back to $WD\n";
1152 # Free memory by removing useless data structures
1153 undef $dependencies;
1154 undef $saved_dependencies;
1155}
1156
1157# First pass: build a private makefile from the extracted dependency, changing
1158# conditional units to truly wanted ones if the symbol is used, removing the
1159# dependency otherwise. The original dependencies are saved.
1160sub build_private {
1161 print " Building private make file...\n" unless $opt_s;
1162 open(WANTED,"../Wanted") || die "Can't reopen Wanted.\n";
1163 $wanted = ' ' x 2000; # Pre-extend string
1164 $wanted = '';
1165 while (<WANTED>) {
1166 chop;
1167 next if /^!/; # Skip obsolete symbols
1168 if (s/^>//) {
1169 $cmaster{$_}++;
1170 } else {
1171 $wanted .= "$_ ";
1172 }
1173 }
1174 close WANTED;
1175
1176 # The wanted symbols are sorted so that d_* (checking for C library symbol)
1177 # come first and i_* (checking for includes) comes at the end. Grouping the
1178 # d_* symbols together has good chances of improving the locality of the
1179 # other questions and i_* symbols must come last since some depend on h_*
1180 # values which prevent incompatible headers inclusions.
1181 $wanted = join(' ', sort symbols split(' ', $wanted));
1182
1183 # Now generate the first makefile, which will be used to determine which
1184 # symbols we really need, so that conditional dependencies may be solved.
1185 open(MAKEFILE,">Makefile") || die "Can't create .MT/Makefile.\n";
1186 print MAKEFILE "SHELL = /bin/sh\n";
1187 print MAKEFILE "W = $wanted\n";
1188 $saved_dependencies = $dependencies;
1189 foreach $sym (@Cond) {
1190 if ($symwanted{$sym}) {
1191 $dependencies =~ s/\+($sym\s)/$1/gm;
1192 } else {
1193 $dependencies =~ s/\+$sym(\s)/$1/gm;
1194 }
1195 }
1196 print MAKEFILE $dependencies;
1197 close MAKEFILE;
1198}
1199
1200# Ordering for symbols. Give higher priority to d_* ones and lower to i_* ones.
1201# If any layout priority is defined in %Layout, it is used to order the
1202# symbols.
1203sub symbols {
1204 local($r) = $Layout{$a} <=> $Layout{$b};
1205 return $r if $r;
1206 # If we come here, both symbols have the same layout priority.
1207 if ($a =~ /^d_/) {
1208 return -1 unless $b =~ /^d_/;
1209 } elsif ($b =~ /^d_/) {
1210 return 1;
1211 } elsif ($a =~ /^i_/) {
1212 return 1 unless $b =~ /^i_/;
1213 } elsif ($b =~ /^i_/) {
1214 return -1;
1215 }
1216 $a cmp $b;
1217}
1218
1219# Run the makefile produced in the first pass to find the whole set of units we
1220# have to load, filling in the %symwanted and %condwanted structures.
1221sub compute_loadable {
1222 print " Determining loadable units...\n" unless $opt_s;
1223 open(MAKE, "make -n |") || die "Can't run make";
1224 while (<MAKE>) {
1225 s|^\s+||; # Some make print tabs before command
1226 if (/^pick/) {
1227 print "\t$_" if $opt_v;
1228 ($pick,$cmd,$symbol,$unit) = split(' ');
1229 $symwanted{$symbol}++;
1230 $symwanted{$unit}++;
1231 } elsif (/^cond/) {
1232 print "\t$_" if $opt_v;
1233 ($pick,@symbol) = split(' ');
1234 for (@symbol) {
1235 $condwanted{$_}++; # Default value is requested
1236 }
1237 }
1238 }
1239 close MAKE;
1240}
1241
1242# Now that we know all the desirable symbols, we have to rebuild
1243# another makefile, in order to have the units in a more optimal
1244# way.
1245# Actually, if we have both ?MAKE:a:+b and ?MAKE:d:b and 'd' is
1246# wanted; then 'b' will be loaded. However, 'b' is a conditional
1247# dependency for 'a', and it would be better if 'b' were loaded
1248# before 'a' is, though this is not necessary.
1249# It is hard to know that 'b' will be loaded *before* the first make.
1250
1251# Back to the original dependencies, make loadable units truly wanted ones and
1252# remove optional ones.
1253sub update_makefile {
1254 print " Updating make file...\n" unless $opt_s;
1255 open(MAKEFILE,">Makefile") || die "Can't create .MT/Makefile.\n";
1256 print MAKEFILE "SHELL = /bin/sh\n";
1257 print MAKEFILE "W = $wanted\n";
1258 foreach $sym (@Cond) {
1259 if ($symwanted{$sym}) {
1260 $saved_dependencies =~ s/\+($sym\s)/$1/gm;
1261 } else {
1262 $saved_dependencies =~ s/\+$sym(\s)/$1/gm;
1263 }
1264 }
1265 print MAKEFILE $saved_dependencies;
1266 close MAKEFILE;
1267}
1268
1269# Solve dependencies by saving the 'pick' command in @cmdwanted
1270sub solve_dependencies {
1271 local(%unitseen); # Record already picked units (avoid duplicates)
1272 print "Determining the correct order for the units...\n" unless $opt_s;
1273 chdir('.MT') || die "Can't chdir to .MT: $!.\n";
1274 open(MAKE, "make -n |") || die "Can't run make";
1275 while (<MAKE>) {
1276 s|^\s+||; # Some make print tabs before command
1277 print "\t$_" if $opt_v;
1278 if (/^pick/) {
1279 ($pick,$cmd,$symbol,$unit) = split(' ');
1280 push(@cmdwanted,"$cmd $symbol $unit")
1281 unless $unitseen{"$cmd:$unit"}++;
1282 } elsif (/^cond/) {
1283 # Ignore conditional symbol request
1284 } else {
1285 chop;
035bc654 1286 system $_;
459d3fb5
MBT
1287 }
1288 }
1289 chdir($WD) || die "Can't chdir to $WD: $!.\n";
1290 close MAKE;
1291}
1292
1293# Create the Configure script
1294sub create_configure {
1295 print "Creating Configure...\n" unless $opt_s;
1296 open(CONFIGURE,">Configure") || die "Can't create Configure: $!\n";
1297 open(CONF_H,">config_h.SH") || die "Can't create config_h.SH: $!\n";
1298 if ($opt_M) {
1299 open(MAGIC_H,">confmagic.h") || die "Can't create confmagic.h: $!\n";
1300 }
1301
1302 chdir('.MT') || die "Can't cd to .MT: $!\n";
1303 for (@cmdwanted) {
1304 &process_command($_); # Run the makefile command
1305 }
1306 chdir($WD) || die "Can't cd back to $WD\n";
1307 close CONFIGURE;
1308 print CONF_H "#endif\n"; # Close the opened #ifdef (see Config_h.U)
1309 print CONF_H "!GROK!THIS!\n";
1310 close CONF_H;
1311 if ($opt_M) {
1312 print MAGIC_H "#endif\n"; # Close the opened #ifdef (see Magic_h.U)
1313 close MAGIC_H;
1314 }
1315 `chmod +x Configure`;
1316}
1317
1318# Process a Makefile 'pick' command
1319sub process_command {
1320 local($cmd, $target, $unit_name) = split(' ', $_[0]);
1321 local($name) = $unit_name . '.U'; # Restore missing .U
1322 local($file) = $name; # Where unit is located
1323 unless ($file =~ m|^\./|) { # Unit produced earlier by metaconfig
1324 $file = $Unit{$unit_name}; # Fetch unit from U directory
1325 }
1326 if (defined $Obsolete{$name}) { # Signal use of an obsolete unit
1327 warn "\tObsolete unit $name is used:\n";
1328 local(@msg) = split(/\n/, $Obsolete{$name});
1329 foreach $msg (@msg) {
1330 warn "\t $msg\n";
1331 }
1332 }
d6208abd 1333 die "Can't open UNIT (name: $name, target: $target) $file.\n" unless open(UNIT, $file);
459d3fb5
MBT
1334 print "\t$cmd $file\n" if $opt_v;
1335 &init_interp; # Initializes the interpreter
1336
1337 # The 'add' command adds the unit to Configure.
1338 if ($cmd eq 'add') {
1339 while (<UNIT>) {
1340 print CONFIGURE unless &skipped || !&interpret($_);
1341 }
1342 }
1343
1344 # The 'weed' command adds the unit to Configure, but
1345 # makes some tests for the lines starting with '?' or '%'.
1346 # These lines are kept only if the symbol is wanted.
1347 elsif ($cmd eq 'weed') {
1348 while (<UNIT>) {
1349 if (/^\?(\w+):/) {
1350 s/^\?\w+:// if $symwanted{$1};
1351 }
1352 if (/^%(\w+):/) {
1353 s/^%\w+:// if $condwanted{$1};
1354 }
1355 print CONFIGURE unless &skipped || !&interpret($_);
1356 }
1357 }
1358
1359 # The 'wipe' command adds the unit to Configure, but
1360 # also substitues some hardwired macros.
1361 elsif ($cmd eq 'wipe') {
1362 while (<UNIT>) {
1363 s/<PACKAGENAME>/$package/g;
1364 s/<MAINTLOC>/$maintloc/g;
1365 s/<VERSION>/$version/g; # This is metaconfig's version
1366 s/<PATCHLEVEL>/$patchlevel/g; # And patchlevel information
1367 s/<DATE>/$date/g;
1368 s/<BASEREV>/$baserev/g;
1369 s/<\$(\w+)>/eval("\$$1")/ge; # <$var> -> $var substitution
1370 print CONFIGURE unless &skipped || !&interpret($_);
1371 }
1372 }
1373
1374 # The 'add.Null' command adds empty initializations
1375 # to Configure for all the shell variable used.
1376 elsif ($cmd eq 'add.Null') {
1377 for (sort @Master) {
1378 if (/^\?(\w+):/) {
1379 s/^\?\w+:// if $symwanted{$1};
1380 }
1381 print CONFIGURE unless &skipped;
1382 }
1383 for (sort @Cond) {
1384 print CONFIGURE "$_=''\n"
1385 unless $symwanted{$_} || $hasdefault{$_};
1386 }
1387 while (<UNIT>) {
1388 print CONFIGURE unless &skipped || !&interpret($_);
1389 }
1390 print CONFIGURE "CONFIG=''\n\n";
1391 }
1392
1393 # The 'add.Config_sh' command fills in the production of
1394 # the config.sh script within Configure. Only the used
1395 # variable are added, the conditional ones are skipped.
1396 elsif ($cmd eq 'add.Config_sh') {
1397 while (<UNIT>) {
1398 print CONFIGURE unless &skipped || !&interpret($_);
1399 }
1400 for (sort @Master) {
1401 if (/^\?(\w+):/) {
1402 # Can't use $shmaster, because config.sh must
1403 # also contain some internal defaults used by
1404 # Configure (e.g. nm_opt, libc, etc...).
1405 s/^\?\w+:// if $symwanted{$1};
1406 }
1407 s/^(\w+)=''/$1='\$$1'/;
1408 print CONFIGURE unless &skipped;
1409 }
1410 }
1411
1412 # The 'close.Config_sh' command adds the final EOT line at
1413 # the end of the here-document construct which produces the
1414 # config.sh file within Configure.
1415 elsif ($cmd eq 'close.Config_sh') {
1416 print CONFIGURE "EOT\n\n"; # Ends up file
1417 }
1418
1419 # The 'c_h_weed' command produces the config_h.SH file.
1420 # Only the necessary lines are kept. If no conditional line is
1421 # ever printed, then the file is useless and will be removed.
1422 elsif ($cmd eq 'c_h_weed') {
1423 $printed = 0;
1424 while (<UNIT>) {
1425 if (/^\?(\w+):/) {
1426 s/^\?\w+:// if $cmaster{$1} || $symwanted{$1};
1427 }
1428 unless (&skipped || !&interpret($_)) {
1429 if (/^$/) {
1430 print CONF_H "\n" if $printed;
1431 $printed = 0;
1432 } else {
1433 print CONF_H;
1434 ++$printed;
1435 }
1436 }
1437 }
1438 }
1439
1440 # The 'cm_h_weed' command produces the confmagic.h file.
1441 # Only the necessary lines are kept. If no conditional line is
1442 # ever printed, then the file is useless and will be removed.
1443 elsif ($cmd eq 'cm_h_weed') {
1444 if ($opt_M) {
1445 $printed = 0;
1446 while (<UNIT>) {
1447 if (/^\?(\w+):/) {
1448 s/^\?\w+:// if $cmaster{$1} || $symwanted{$1};
1449 }
1450 unless (&skipped || !&interpret($_)) {
1451 if (/^$/) {
1452 print MAGIC_H "\n" if $printed;
1453 $printed = 0;
1454 } else {
1455 print MAGIC_H;
1456 ++$printed;
1457 }
1458 }
1459 }
1460 }
1461 }
1462
1463 # The 'prepend' command will add the content of the target to
1464 # the current file (held in $file, the one which UNIT refers to),
1465 # if the file is not empty.
1466 elsif ($cmd eq 'prepend') {
1467 if (-s $file) {
1468 open(PREPEND, ">.prepend") ||
1469 die "Can't create .MT/.prepend.\n";
1470 open(TARGET, $Unit{$target}) ||
d6208abd 1471 die "Can't open Unit $Unit{$target}.\n";
459d3fb5
MBT
1472 while (<TARGET>) {
1473 print PREPEND unless &skipped;
1474 }
1475 print PREPEND <UNIT>; # Now add original file contents
1476 close PREPEND;
1477 close TARGET;
1478 rename('.prepend', $file) ||
1479 die "Can't rename .prepend into $file.\n";
1480 }
1481 }
1482
1483 # Command not found
1484 else {
1485 die "Unrecognized command from Makefile: $cmd\n";
1486 }
1487 &check_state; # Make sure there are no pending statements
1488 close UNIT;
1489}
1490
1491# Skip lines starting with ? or %, including all the following continuation
1492# lines, if any. Return 0 if the line was not to be skipped, 1 otherwise.
1493sub skipped {
1494 return 0 unless /^\?|^%/;
1495 &complete_line(UNIT) if /\\\s*$/; # Swallow continuation lines
1496 1;
1497}
1498
1499# Update the MANIFEST.new file if necessary
1500sub cosmetic_update {
1501 # Check for an "empty" config_h.SH (2 blank lines only). This test relies
1502 # on the actual text held in Config_h.U. If the unit is modified, then the
1503 # following might need adjustments.
1504 local($blank_lines) = 0;
1505 local($spaces) = 0;
1506 open(CONF_H, 'config_h.SH') || die "Can't open config_h.SH\n";
1507 while(<CONF_H>) {
1508 ++$blank_lines if /^$/;
1509 }
1510 unlink 'config_h.SH' unless $blank_lines > 3;
1511
1512 open(NEWMANI,$NEWMANI);
1513 $_ = <NEWMANI>;
1514 /(\S+\s+)\S+/ && ($spaces = length($1)); # Spaces wanted
1515 close NEWMANI;
1516 $spaces = 29 if ($spaces < 12); # Default value
1517 open(NEWMANI,$NEWMANI);
1518 $/ = "\001"; # Swallow the whole file
1519 $_ = <NEWMANI>;
1520 $/ = "\n";
1521 close NEWMANI;
1522
1523 &mani_add('Configure', 'Portability tool', $spaces) unless /^Configure\b/m;
1524 &mani_add('config_h.SH', 'Produces config.h', $spaces)
1525 unless /^config_h\.SH\b/m || !-f 'config_h.SH';
1526 &mani_add('confmagic.h', 'Magic symbol remapping', $spaces)
1527 if $opt_M && !/^confmagic\.h\b/m;
1528
1529 &mani_remove('config_h.SH') if /^config_h\.SH\b/m && !-f 'config_h.SH';
1530 &mani_remove('confmagic.h') if /^confmagic.h\b/m && !$opt_M;
1531
1532 if ($opt_G) { # Want a GNU-like configure wrapper
1533 &add_configure;
1534 &mani_add('configure', 'GNU configure-like wrapper', $spaces)
1535 if !/^configure\s/m && -f 'configure';
1536 } else {
1537 &mani_remove('configure') if /^configure\s/m && !-f 'configure';
1538 }
1539}
1540
1541# Add file to MANIFEST.new, with properly indented comment
1542sub mani_add {
1543 local($file, $comment, $spaces) = @_;
1544 print "Adding $file to your $NEWMANI file...\n" unless $opt_s;
1545 open(NEWMANI, ">>$NEWMANI") || warn "Can't add $file to $NEWMANI: $!\n";
1546 local($blank) = ' ' x ($spaces - length($file));
1547 print NEWMANI "${file}${blank}${comment}\n";
1548 close NEWMANI;
1549}
1550
1551# Remove file from MANIFEST.new
1552sub mani_remove {
1553 local($file) = @_;
1554 print "Removing $file from $NEWMANI...\n" unless $opt_s;
1555 unless (open(NEWMANI, ">$NEWMANI.x")) {
1556 warn "Can't create backup $NEWMANI copy: $!\n";
1557 return;
1558 }
1559 unless (open(OLDMANI, $NEWMANI)) {
1560 warn "Can't open $NEWMANI: $!\n";
1561 return;
1562 }
1563 local($_);
1564 while (<OLDMANI>) {
1565 print NEWMANI unless /^$file\b/
1566 }
1567 close OLDMANI;
1568 close NEWMANI;
1569 rename("$NEWMANI.x", $NEWMANI) ||
1570 warn "Couldn't restore $NEWMANI from $NEWMANI.x\n";
1571}
1572
1573# Copy GNU-like configure wrapper to the package root directory
1574sub add_configure {
1575 if (-f "$MC/configure") {
1576 print "Copying GNU configure-like front end...\n" unless $opt_s;
1577 system "cp $MC/configure ./configure";
1578 `chmod +x configure`;
1579 } else {
1580 warn "Can't locate $MC/configure: $!\n";
1581 }
1582}
1583
1584package interpreter;
1585
1586# States used by our interpeter -- in sync with @Keep
1587sub main'init_keep {
1588 # Status in which we keep lines -- $Keep[$status]
1589 @Keep = (0, 1, 1, 0, 1);
1590
1591 # Available status ($status)
1592 $SKIP = 0;
1593 $IF = 1;
1594 $ELSE = 2;
1595 $NOT = 3;
1596 $OUT = 4;
1597}
1598
1599# Priorities for operators -- magic numbers :-)
1600sub main'init_priority {
1601 $Priority{'&&'} = 4;
1602 $Priority{'||'} = 3;
1603}
1604
1605# Initializes the state stack of the interpreter
1606sub main'init_interp {
1607 @state = ();
1608 push(@state, $OUT);
1609}
1610
1611# Print error messages -- asssumes $unit and $. correctly set.
1612sub error {
1613 warn "\"$main'file\", line $.: @_.\n";
1614}
1615
1616# If some states are still in the stack, warn the user
1617sub main'check_state {
1618 &error("one statement pending") if $#state == 1;
1619 &error("$#state statements pending") if $#state > 1;
1620}
1621
1622# Add a value on the stack, modified by all the monadic operators.
1623# We use the locals @val and @mono from eval_expr.
1624sub push_val {
1625 local($val) = shift(@_);
1626 while ($#mono >= 0) {
1627 # Cheat... the only monadic operator is '!'.
1628 pop(@mono);
1629 $val = !$val;
1630 }
1631 push(@val, $val);
1632}
1633
1634# Execute a stacked operation, leave result in stack.
1635# We use the locals @val and @op from eval_expr.
1636# If the value stack holds only one operand, do nothing.
1637sub execute {
1638 return unless $#val > 0;
1639 local($op) = pop(@op);
1640 local($val1) = pop(@val);
1641 local($val2) = pop(@val);
1642 push(@val, eval("$val1 $op $val2") ? 1: 0);
1643}
1644
1645# Given an operator, either we add it in the stack @op, because its
1646# priority is lower than the one on top of the stack, or we first execute
1647# the stacked operations until we reach the end of stack or an operand
1648# whose priority is lower than ours.
1649# We use the locals @val and @op from eval_expr.
1650sub update_stack {
1651 local($op) = shift(@_); # Operator
1652 if (!$Priority{$op}) {
1653 &error("illegal operator $op");
1654 return;
1655 } else {
1656 if ($#val < 0) {
1657 &error("missing first operand for '$op' (diadic operator)");
1658 return;
1659 }
1660 # Because of the special behaviour of do-SUBR with the while modifier,
1661 # I'm using a while-BLOCK construct. I consider this to be a bug of perl
1662 # 4.0 PL19, although it is clearly documented in the man page.
1663 while (
1664 $Priority{$op[$#op]} > $Priority{$op} # Higher priority op
1665 && $#val > 0 # At least 2 values
1666 ) {
1667 &execute; # Execute an higher priority stacked operation
1668 }
1669 push(@op, $op); # Everything at higher priority has been executed
1670 }
1671}
1672
1673# This is the heart of our little interpreter. Here, we evaluate
1674# a logical expression and return its value.
1675sub eval_expr {
1676 local(*expr) = shift(@_); # Expression to parse
1677 local(@val) = (); # Stack of values
1678 local(@op) = (); # Stack of diadic operators
1679 local(@mono) =(); # Stack of monadic operators
1680 local($tmp);
1681 $_ = $expr;
1682 while (1) {
1683 s/^\s+//; # Remove spaces between words
1684 # The '(' construct
1685 if (s/^\(//) {
1686 &push_val(&eval_expr(*_));
1687 # A final '\' indicates an end of line
1688 &error("missing final parenthesis") if !s/^\\//;
1689 }
1690 # Found a ')' or end of line
1691 elsif (/^\)/ || /^$/) {
1692 s/^\)/\\/; # Signals: left parenthesis found
1693 $expr = $_; # Remove interpreted stuff
1694 &execute() while $#val > 0; # Executed stacked operations
1695 while ($#op >= 0) {
1696 $_ = pop(@op);
1697 &error("missing second operand for '$_' (diadic operator)");
1698 }
1699 return $val[0];
1700 }
1701 # A perl statement '{{'
1702 elsif (s/^\{\{//) {
1703 if (s/^(.*)\}\}//) {
1704 &push_val((system
1705 ('perl','-e', "if ($1) {exit 0;} else {exit 1;}"
1706 ))? 0 : 1);
1707 } else {
1708 &error("incomplete perl statement");
1709 }
1710 }
1711 # A shell statement '{'
1712 elsif (s/^\{//) {
1713 if (s/^(.*)\}//) {
1714 &push_val((system
1715 ("if $1 >/dev/null 2>&1; then exit 0; else exit 1; fi"
1716 ))? 0 : 1);
1717 } else {
1718 &error("incomplete shell statement");
1719 }
1720 }
1721 # Operator '||' and '&&'
1722 elsif (s/^(\|\||&&)//) {
1723 $tmp = $1; # Save for perl5 (Dataloaded update_stack)
1724 &update_stack($tmp);
1725 }
1726 # Unary operator '!'
1727 elsif (s/^!//) {
1728 push(@mono,'!');
1729 }
1730 # Everything else is a test for a defined value
1731 elsif (s/^([\?%]?\w+)//) {
1732 $tmp = $1;
1733 # Test for wanted
1734 if ($tmp =~ s/^\?//) {
1735 &push_val(($main'symwanted{$tmp})? 1 : 0);
1736 }
1737 # Test for conditionally wanted
1738 elsif ($tmp =~ s/^%//) {
1739 &push_val(($main'condwanted{$tmp})? 1 : 0);
1740 }
1741 # Default: test for definition (see op @define)
1742 else {
1743 &push_val((
1744 $main'symwanted{$tmp} ||
1745 $main'cmaster{$tmp} ||
1746 $main'userdef{$tmp}) ? 1 : 0);
1747 }
1748 }
1749 # An error occured -- we did not recognize the expression
1750 else {
1751 s/^([^\s\(\)\{\|&!]+)//; # Skip until next meaningful char
1752 }
1753 }
1754}
1755
1756# Given an expression in a '@' command, returns a boolean which is
1757# the result of the evaluation. Evaluate is collecting all the lines
1758# in the expression into a single string, and then calls eval_expr to
1759# really evaluate it.
1760sub evaluate {
1761 local($val); # Value returned
1762 local($expr) = ""; # Expression to be parsed
1763 chop;
1764 while (s/\\$//) { # While end of line escaped
1765 $expr .= $_;
1766 $_ = <UNIT>; # Fetch next line
1767 unless ($_) {
1768 &error("EOF in expression");
1769 last;
1770 }
1771 chop;
1772 }
1773 $expr .= $_;
1774 while ($expr ne '') {
1775 $val = &eval_expr(*expr); # Expression will be modified
1776 # We return from eval_expr either when a closing parenthisis
1777 # is found, or when the expression has been fully analysed.
1778 &error("extra closing parenthesis ignored") if $expr ne '';
1779 }
1780 $val;
1781}
1782
1783# Given a line, we search for commands (lines starting with '@').
1784# If there is no command in the line, then we return the boolean state.
1785# Otherwise, the command is analysed and a new state is computed.
1786# The returned value of interpret is 1 if the line is to be printed.
1787sub main'interpret {
1788 local($value);
1789 local($status) = $state[$#state]; # Current status
1790 if (s|^\s*@\s*(\w+)\s*(.*)|$2|) {
1791 local($cmd) = $1;
1792 $cmd =~ y/A-Z/a-z/; # Canonicalize to lower case
1793 # The 'define' command
1794 if ($cmd eq 'define') {
1795 chop;
1796 $userdef{$_}++ if $Keep[$status];
1797 return 0;
1798 }
1799 # The 'if' command
1800 elsif ($cmd eq 'if') {
1801 # We always evaluate, in order to find possible errors
1802 $value = &evaluate($_);
1803 if (!$Keep[$status]) {
1804 # We have to skip until next 'end'
1805 push(@state, $SKIP); # Record structure
1806 return 0;
1807 }
1808 if ($value) { # True
1809 push(@state, $IF);
1810 return 0;
1811 } else { # False
1812 push(@state, $NOT);
1813 return 0;
1814 }
1815 }
1816 # The 'else' command
1817 elsif ($cmd eq 'else') {
1818 &error("expression after 'else' ignored") if /\S/;
1819 $state[$#state] = $SKIP if $state[$#state] == $IF;
1820 return 0 if $state[$#state] == $SKIP;
1821 if ($state[$#state] == $OUT) {
1822 &error("unexpected 'else'");
1823 return 0;
1824 }
1825 $state[$#state] = $ELSE;
1826 return 0;
1827 }
1828 # The 'elsif' command
1829 elsif ($cmd eq 'elsif') {
1830 # We always evaluate, in order to find possible errors
1831 $value = &evaluate($_);
1832 $state[$#state] = $SKIP if $state[$#state] == $IF;
1833 return 0 if $state[$#state] == $SKIP;
1834 if ($state[$#state] == $OUT) {
1835 &error("unexpected 'elsif'");
1836 return 0;
1837 }
1838 if ($value) { # True
1839 $state[$#state] = $IF;
1840 return 0;
1841 } else { # False
1842 $state[$#state] = $NOT;
1843 return 0;
1844 }
1845 }
1846 # The 'end' command
1847 elsif ($cmd eq 'end') {
1848 &error("expression after 'end' ignored") if /\S/;
1849 pop(@state);
1850 &error("unexpected 'end'") if $#state < 0;
1851 return 0;
1852 }
1853 # Unknown command
1854 else {
1855 &error("unknown command '$cmd'");
1856 return 0;
1857 }
1858 }
1859 $Keep[$status];
1860}
1861
1862package main;
1863
1864sub readpackage {
1865 if (! -f '.package') {
1866 if (
1867 -f '../.package' ||
1868 -f '../../.package' ||
1869 -f '../../../.package' ||
1870 -f '../../../../.package'
1871 ) {
1872 die "Run in top level directory only.\n";
1873 } else {
1874 die "No .package file! Run packinit.\n";
1875 }
1876 }
1877 open(PACKAGE,'.package');
1878 while (<PACKAGE>) {
1879 next if /^:/;
1880 next if /^#/;
1881 if (($var,$val) = /^\s*(\w+)=(.*)/) {
1882 $val = "\"$val\"" unless $val =~ /^['"]/;
1883 eval "\$$var = $val;";
1884 }
1885 }
1886 close PACKAGE;
1887}
1888
1889sub manifake {
1890 # make MANIFEST and MANIFEST.new say the same thing
1891 if (! -f $NEWMANI) {
1892 if (-f $MANI) {
1893 open(IN,$MANI) || die "Can't open $MANI";
1894 open(OUT,">$NEWMANI") || die "Can't create $NEWMANI";
1895 while (<IN>) {
1896 if (/---/) {
1897 # Everything until now was a header...
1898 close OUT;
1899 open(OUT,">$NEWMANI") ||
1900 die "Can't recreate $NEWMANI";
1901 next;
1902 }
1903 s/^\s*(\S+\s+)[0-9]*\s*(.*)/$1$2/;
1904 print OUT;
1905 print OUT "\n" unless /\n$/; # If no description
1906 }
1907 close IN;
1908 close OUT;
1909 }
1910 else {
1911die "You need to make a $NEWMANI file, with names and descriptions.\n";
1912 }
1913 }
1914}
1915
1916# Perform ~name expansion ala ksh...
1917# (banish csh from your vocabulary ;-)
1918sub tilda_expand {
1919 local($path) = @_;
1920 return $path unless $path =~ /^~/;
1921 $path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e; # ~name
1922 $path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e; # ~
1923 $path;
1924}
1925
1926# Set up profile components into %Profile, add any profile-supplied options
1927# into @ARGV and return the command invocation name.
1928sub profile {
1929 local($profile) = &tilda_expand($ENV{'DIST'} || '~/.dist_profile');
1930 local($me) = $0; # Command name
1931 $me =~ s|.*/(.*)|$1|; # Keep only base name
1932 return $me unless -s $profile;
1933 local(*PROFILE); # Local file descriptor
1934 local($options) = ''; # Options we get back from profile
1935 unless (open(PROFILE, $profile)) {
1936 warn "$me: cannot open $profile: $!\n";
1937 return;
1938 }
1939 local($_);
1940 local($component);
1941 while (<PROFILE>) {
1942 next if /^\s*#/; # Skip comments
1943 next unless /^$me/o;
1944 if (s/^$me://o) { # progname: options
1945 chop;
1946 $options .= $_; # Merge options if more than one line
1947 }
1948 elsif (s/^$me-([^:]+)://o) { # progname-component: value
1949 $component = $1;
1950 chop;
1951 s/^\s+//; # Trim leading and trailing spaces
1952 s/\s+$//;
1953 $Profile{$component} = $_;
1954 }
1955 }
1956 close PROFILE;
1957 return unless $options;
1958 require 'shellwords.pl';
1959 local(@opts);
1960 eval '@opts = &shellwords($options)'; # Protect against mismatched quotes
1961 unshift(@ARGV, @opts);
1962 return $me; # Return our invocation name
1963}
1964