This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
infnan: move the mantbits definitions from perl.h to Configure
[metaconfig.git] / bin / metaxref
1 #!/usr/bin/perl
2         eval "exec perl -S $0 $*"
3                 if $running_under_some_shell;
4
5 #
6 # This perl program uses dynamic loading [generated by perload]
7 #
8
9 $ENV{LC_ALL} = 'C';
10
11 # $Id: mxref.SH 16 2006-11-04 12:11:51Z rmanfredi $
12 #
13 #  Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi
14 #  
15 #  You may redistribute only under the terms of the Artistic Licence,
16 #  as specified in the README file that comes with the distribution.
17 #  You may reuse parts of this distribution only within the terms of
18 #  that same Artistic Licence; a copy of which may be found at the root
19 #  of the source tree for dist 4.0.
20 #
21 # Original Author: Harlan Stenn <harlan@mumps.pfcs.com>
22 #
23 # $Log: mxref.SH,v $
24 # Revision 3.0.1.3  1997/02/28  16:30:49  ram
25 # patch61: new -L option to match metaconfig and metalint
26 #
27 # Revision 3.0.1.2  1994/01/24  14:21:04  ram
28 # patch16: added ~/.dist_profile awareness
29 #
30 # Revision 3.0.1.1  1993/08/19  06:42:27  ram
31 # patch1: leading config.sh searching was not aborting properly
32 #
33 # Revision 3.0  1993/08/18  12:10:18  ram
34 # Baseline for dist 3.0 netwide release.
35 #
36
37 # Perload ON
38
39 $MC = '/pro/3gl/CPAN/lib/dist';
40 $version = '3.5';
41 $patchlevel = '0';
42 $grep = '/usr/bin/grep';
43 &profile;                                               # Read ~/.dist_profile
44 require 'getopts.pl';
45 &usage unless &Getopts("df:hkmsVL:");
46
47 chop($date = `date`);
48 chop($WD = `pwd`);                              # Working directory
49 $MC = $opt_L if $opt_L;                 # May override  library path
50 $MC = &tilda_expand($MC);               # ~name expansion
51 chdir $MC || die "Can't chdir to $MC: $!\n";
52 chop($MC = `pwd`);                              # Real metaxref lib path (no symbolic links)
53 chdir $WD || die "Can't chdir back to $WD: $!\n";
54
55 if ($opt_V) {
56         print STDERR "metaxref $version PL$patchlevel\n";
57         exit 0;
58 } elsif ($opt_h) {
59         &usage;
60 }
61
62 $NEWMANI = $opt_f || (-f 'MANIFEST.new' ? 'MANIFEST.new' : 'MANIFEST');
63
64 &init;                                                                  # Various initializations
65 `mkdir .MT 2>&1` unless -d '.MT';               # For private temporary files
66 unlink 'Obsolete';                                              # Obsolete file rebuilt
67
68 &locate_units;                          # Fill in @ARGV with a unit list
69 &extract_dependencies;          # Extract dependencies from units
70 &extract_filenames;                     # Get source files from MANIFEST.new
71 &build_xref;                            # Parse files, build I.* output
72
73 if ($opt_k) {
74         print "Leaving subdirectory .MT unremoved so you can peruse it.\n"
75                 unless $opt_s;
76 } else {
77         `rm -rf .MT 2>&1`;
78 }
79 print "Done.\n" unless $opt_s;
80
81 sub main'init { &auto_main'init; }
82 sub auto_main'init { &main'dataload; }
83
84 sub main'init_except { &auto_main'init_except; }
85 sub auto_main'init_except { &main'dataload; }
86
87 sub main'usage { &auto_main'usage; }
88 sub auto_main'usage { &main'dataload; }
89
90 package locate;
91
92 sub main'locate_units { &auto_main'locate_units; }
93 sub auto_main'locate_units { &main'dataload; }
94
95 sub locate'dump_list { &auto_locate'dump_list; }
96 sub auto_locate'dump_list { &main'dataload; }
97
98 sub locate'private_units { &auto_locate'private_units; }
99 sub auto_locate'private_units { &main'dataload; }
100
101 sub locate'public_units { &auto_locate'public_units; }
102 sub auto_locate'public_units { &main'dataload; }
103
104 sub locate'units_path { &auto_locate'units_path; }
105 sub auto_locate'units_path { &main'dataload; }
106
107 package main;
108
109 sub main'init_extraction { &auto_main'init_extraction; }
110 sub auto_main'init_extraction { &main'dataload; }
111
112 sub main'end_extraction { &auto_main'end_extraction; }
113 sub auto_main'end_extraction { &main'dataload; }
114
115 sub main'p_make { &auto_main'p_make; }
116 sub auto_main'p_make { &main'dataload; }
117
118 sub main'p_obsolete { &auto_main'p_obsolete; }
119 sub auto_main'p_obsolete { &main'dataload; }
120
121 sub main'p_shell { &auto_main'p_shell; }
122 sub auto_main'p_shell { &main'dataload; }
123
124 sub main'p_c { &auto_main'p_c; }
125 sub auto_main'p_c { &main'dataload; }
126
127 sub main'p_config { &auto_main'p_config; }
128 sub auto_main'p_config { &main'dataload; }
129
130 sub main'p_magic { &auto_main'p_magic; }
131 sub auto_main'p_magic { &main'dataload; }
132
133 sub p_ignore {}         # Ignore comment line
134 sub p_lint {}           # Ignore lint directives
135 sub p_visible {}        # No visible checking in metaconfig
136 sub p_temp {}           # No temporary variable control
137 sub p_file {}           # Ignore produced file directives (for now)
138
139 sub main'p_wanted { &auto_main'p_wanted; }
140 sub auto_main'p_wanted { &main'dataload; }
141
142 # Ingnore the following:
143 sub p_init {}
144 sub p_default {}
145 sub p_library {}
146 sub p_include {}
147 sub p_public {}
148 sub p_layout {}
149
150 sub main'extract_filenames { &auto_main'extract_filenames; }
151 sub auto_main'extract_filenames { &main'dataload; }
152
153 sub main'build_filext { &auto_main'build_filext; }
154 sub auto_main'build_filext { &main'dataload; }
155
156 sub main'build_extfun { &auto_main'build_extfun; }
157 sub auto_main'build_extfun { &main'dataload; }
158
159 sub main'q { &auto_main'q; }
160 sub auto_main'q { &main'dataload; }
161
162 sub main'init_depend { &auto_main'init_depend; }
163 sub auto_main'init_depend { &main'dataload; }
164
165 sub main'extract_dependencies { &auto_main'extract_dependencies; }
166 sub auto_main'extract_dependencies { &main'dataload; }
167
168 sub main'complete_line { &auto_main'complete_line; }
169 sub auto_main'complete_line { &main'dataload; }
170
171 sub main'record_obsolete { &auto_main'record_obsolete; }
172 sub auto_main'record_obsolete { &main'dataload; }
173
174 sub main'dump_obsolete { &auto_main'dump_obsolete; }
175 sub auto_main'dump_obsolete { &main'dataload; }
176
177 sub main'build_xref { &auto_main'build_xref; }
178 sub auto_main'build_xref { &main'dataload; }
179
180 sub main'ofound { &auto_main'ofound; }
181 sub auto_main'ofound { &main'dataload; }
182
183 sub main'gensym { &auto_main'gensym; }
184 sub auto_main'gensym { &main'dataload; }
185
186 sub main'manifake { &auto_main'manifake; }
187 sub auto_main'manifake { &main'dataload; }
188
189 sub main'tilda_expand { &auto_main'tilda_expand; }
190 sub auto_main'tilda_expand { &main'dataload; }
191
192 sub main'profile { &auto_main'profile; }
193 sub auto_main'profile { &main'dataload; }
194
195 # Load the calling function from DATA segment and call it. This function is
196 # called only once per routine to be loaded.
197 sub main'dataload {
198         local($__packname__) = (caller(1))[3];
199         $__packname__ =~ s/::/'/;
200         local($__rpackname__) = $__packname__;
201         local($__at__) = $@;
202         $__rpackname__ =~ s/^auto_//;
203         &perload'load_from_data($__rpackname__);
204         local($__fun__) = "$__rpackname__";
205         $__fun__ =~ s/'/'load_/;
206         eval "*$__packname__ = *$__fun__;";     # Change symbol table entry
207         die $@ if $@;           # Should not happen
208         $@ = $__at__;           # Restore value $@ had on entrance
209         &$__fun__;                      # Call newly loaded function
210 }
211
212 # Load function name given as argument, fatal error if not existent
213 sub perload'load_from_data {
214         package perload;
215         local($pos) = $Datapos{$_[0]};                  # Offset within DATA
216         # Avoid side effects by protecting special variables which will be changed
217         # by the dataloading operation.
218         local($., $_, $@);
219         $pos = &fetch_function_code unless $pos;
220         die "Function $_[0] not found in data section.\n" unless $pos;
221         die "Cannot seek to $pos into data section.\n"
222                 unless seek(main'DATA, $pos, 0);
223         local($/) = "\n}";
224         local($body) = scalar(<main'DATA>);
225         die "End of file found while loading $_[0].\n" unless $body =~ /^\}$/m;
226         eval $body;             # Load function into perl space
227         chop($@) && die "$@, while parsing code of $_[0].\n";
228 }
229
230 # This function is called only once, and fills in the %Datapos array with
231 # the offset of each of the dataloaded routines held in the data section.
232 sub perload'fetch_function_code {
233         package perload;
234         local($start) = 0;
235         local($., $_);
236         while (<main'DATA>) {                   # First move to start of offset table
237                 next if /^#/;
238                 last if /^$/ && ++$start > 2;   # Skip two blank line after end token
239         }
240         $start = tell(main'DATA);               # Offsets in table are relative to here
241         local($key, $value);
242         while (<main'DATA>) {                   # Load the offset table
243                 last if /^$/;                           # Ends with a single blank line
244                 ($key, $value) = split(' ');
245                 $Datapos{$key} = $value + $start;
246         }
247         $Datapos{$_[0]};                # All that pain to get this offset...
248 }
249
250 #
251 # The perl compiler stops here.
252 #
253
254 __END__
255
256 #
257 # Beyond this point lie functions we may never compile.
258 #
259
260 #
261 # DO NOT CHANGE A IOTA BEYOND THIS COMMENT!
262 # The following table lists offsets of functions within the data section.
263 # Should modifications be needed, change original code and rerun perload
264 # with the -o option to regenerate a proper offset table.
265 #
266
267                  locate'dump_list       2938
268              locate'private_units       3071
269               locate'public_units       3858
270                 locate'units_path       5351
271                 main'build_extfun      16719
272                 main'build_filext      16368
273                   main'build_xref      25627
274                main'complete_line      22216
275                main'dump_obsolete      23859
276               main'end_extraction       7714
277         main'extract_dependencies      19358
278            main'extract_filenames      15511
279                       main'gensym      29705
280                         main'init       1308
281                  main'init_depend      18208
282                  main'init_except       1534
283              main'init_extraction       6862
284                 main'locate_units       2321
285                     main'manifake      29793
286                       main'ofound      29321
287                          main'p_c      10283
288                     main'p_config      11589
289                      main'p_magic      13345
290                       main'p_make       8066
291                   main'p_obsolete       9676
292                      main'p_shell       9830
293                     main'p_wanted      14245
294                      main'profile      30974
295                            main'q      17825
296              main'record_obsolete      22806
297                 main'tilda_expand      30619
298                        main'usage       1832
299
300 #
301 # End of offset table and beginning of dataloading section.
302 #
303
304 # General initializations
305 sub main'load_init {
306         package main;
307         &init_except;                   # Token which have upper-cased letters
308         &init_depend;                   # The %Depend array records control line handling
309 }
310
311 # Record the exceptions -- all symbols but these are lower case
312 sub main'load_init_except {
313         package main;
314         $Except{'Author'}++;
315         $Except{'Date'}++;
316         $Except{'Header'}++;
317         $Except{'Id'}++;
318         $Except{'Locker'}++;
319         $Except{'Log'}++;
320         $Except{'RCSfile'}++;
321         $Except{'Revision'}++;
322         $Except{'Source'}++;
323         $Except{'State'}++;
324 }
325
326 # Print out metaxref's usage and exits
327 sub main'load_usage {
328         package main;
329         print STDERR <<EOM;
330 Usage: metaxref [-dhkmsV] [-f manifest] [-L dir]
331   -d : debug mode.
332   -f : use that file as manifest instead of MANIFEST.new.
333   -h : print this help message and exits.
334   -k : keep temporary directory.
335   -m : assume lots of memory and swap space.
336   -s : silent mode.
337   -L : specify main units repository.
338   -V : print version number and exits.
339 EOM
340         exit 1;
341 }
342
343 # Locate the units and push their path in @ARGV (sorted alphabetically)
344 sub main'load_locate_units {
345         package locate;
346         print "Locating units...\n" unless $main'opt_s;
347         local(*WD) = *main'WD;                  # Current working directory
348         local(*MC) = *main'MC;                  # Public metaconfig library
349         undef %myUlist;                                 # Records private units paths
350         undef %myUseen;                                 # Records private/public conflicts
351         &private_units;                                 # Locate private units in @myUlist
352         &public_units;                                  # Locate public units in @ARGV
353         @ARGV = sort @ARGV;                             # Sort it alphabetically
354         push(@ARGV, sort @myUlist);             # Append user's units sorted
355         &dump_list if $main'opt_v;              # Dump the list of units
356 }
357
358 # Dump the list of units on stdout
359 sub locate'load_dump_list {
360         package locate;
361         print "\t";
362         $, = "\n\t";
363         print @ARGV;
364         $, = '';
365         print "\n";
366 }
367
368 # Scan private units
369 sub locate'load_private_units {
370         package locate;
371         return unless -d 'U';                   # Nothing to be done if no 'U' entry
372         local(*ARGV) = *myUlist;                # Really fill in @myUlist
373         local($MC) = $WD;                               # We are really in the working directory
374         &units_path("U");                               # Locate units in the U directory
375         local($unit_name);                              # Unit's name (without .U)
376         local(@kept);                                   # Array of kept units
377         # Loop over the units and remove duplicates (the first one seen is the one
378         # we keep). Also set the %myUseen H table to record private units seen.
379         foreach (@ARGV) {
380                 ($unit_name) = m|^.*/(.*)\.U$|; # Get unit's name from path
381                 next if $myUseen{$unit_name};   # Already recorded
382                 $myUseen{$unit_name} = 1;               # Record pirvate unit
383                 push(@kept, $_);                                # Keep this unit
384         }
385         @ARGV = @kept;
386 }
387
388 # Scan public units
389 sub locate'load_public_units {
390         package locate;
391         chdir($MC) || die "Can't find directory $MC.\n";
392         &units_path("U");                               # Locate units in public U directory
393         chdir($WD) || die "Can't go back to directory $WD.\n";
394         local($path);                                   # Relative path from $WD
395         local($unit_name);                              # Unit's name (without .U)
396         local(*Unit) = *main'Unit;              # Unit is a global from main package
397         local(@kept);                                   # Units kept
398         local(%warned);                                 # Units which have already issued a message
399         # Loop over all the units and keep only the ones that were not found in
400         # the user's U directory. As it is possible two or more units with the same
401         # name be found in
402         foreach (@ARGV) {
403                 ($unit_name) = m|^.*/(.*)\.U$|; # Get unit's name from path
404                 next if $warned{$unit_name};    # We have already seen this unit
405                 $warned{$unit_name} = 1;                # Remember we have warned the user
406                 if ($myUseen{$unit_name}) {             # User already has a private unit
407                         $path = $Unit{$unit_name};      # Extract user's unit path
408                         next if $path eq $_;            # Same path, we must be in mcon/
409                         $path =~ s|^$WD/||o;            # Weed out leading working dir path
410                         print "    Your private $path overrides the public one.\n"
411                                 unless $main'opt_s;
412                 } else {
413                         push(@kept, $_);                        # We may keep this one
414                 }
415         }
416         @ARGV = @kept;
417 }
418
419 # Recursively locate units in the directory. Each file ending with .U has to be
420 # a unit. Others are stat()'ed, and if they are a directory, they are also
421 # scanned through. The $MC and @ARGV variable are dynamically set by the caller.
422 sub locate'load_units_path {
423         package locate;
424         local($dir) = @_;                                       # Directory where units are to be found
425         local(@contents);                                       # Contents of the directory
426         local($unit_name);                                      # Unit's name, without final .U
427         local($path);                                           # Full path of a unit
428         local(*Unit) = *main'Unit;                      # Unit is a global from main package
429         unless (opendir(DIR, $dir)) {
430                 warn("Cannot open directory $dir.\n");
431                 return;
432         }
433         print "Locating in $MC/$dir...\n" if $main'opt_v;
434         @contents = readdir DIR;                        # Slurp the whole thing
435         closedir DIR;                                           # And close dir, ready for recursion
436         foreach (@contents) {
437                 next if $_ eq '.' || $_ eq '..';
438                 if (/\.U$/) {                                   # A unit, definitely
439                         ($unit_name) = /^(.*)\.U$/;
440                         $path = "$MC/$dir/$_";                          # Full path of unit
441                         push(@ARGV, $path);                                     # Record its path
442                         if (defined $Unit{$unit_name}) {        # Already seen this unit
443                                 if ($main'opt_v) {
444                                         ($path) = $Unit{$unit_name} =~ m|^(.*)/.*|;
445                                         print "    We've already seen $unit_name.U in $path.\n";
446                                 }
447                         } else {
448                                 $Unit{$unit_name} = $path;              # Map name to path
449                         }
450                         next;
451                 }
452                 # We have found a file which does not look like a unit. If it is a
453                 # directory, then scan it. Otherwise skip the file.
454                 unless (-d "$dir/$_") {
455                         print "    Skipping file $_ in $dir.\n" if $main'opt_v;
456                         next;
457                 }
458                 &units_path("$dir/$_");
459                 print "Back to $MC/$dir...\n" if $main'opt_v;
460         }
461 }
462
463 # Initialize the extraction process by setting some variables.
464 # We return a string to be eval to do more customized initializations.
465 sub main'load_init_extraction {
466         package main;
467         open(INIT, ">$WD/.MT/Init.U") ||
468                 die "Can't create .MT/Init.U\n";
469         open(CONF_H, ">$WD/.MT/Config_h.U") ||
470                 die "Can't create .MT/Config_h.U\n";
471         open(EXTERN, ">$WD/.MT/Extern.U") ||
472                 die "Can't create .MT/Extern.U\n";
473         open(MAGIC_H, ">$WD/.MT/Magic_h.U") ||
474                 die "Can't create .MT/Magic_h.U\n";
475
476         $c_symbol = '';                         # Current symbol seen in ?C: lines
477         $s_symbol = '';                         # Current symbol seen in ?S: lines
478         $m_symbol = '';                         # Current symbol seen in ?M: lines
479         $heredoc = '';                          # Last "here" document symbol seen
480         $heredoc_nosubst = 0;           # True for <<'EOM' here docs
481         $condlist = '';                         # List of conditional symbols
482         $defined = '';                          # List of defined symbols in the unit
483         $body = '';                                     # No procedure to handle body
484         $ending = '';                           # No procedure to clean-up
485 }
486
487 # End the extraction process
488 sub main'load_end_extraction {
489         package main;
490         close EXTERN;                   # External dependencies (libraries, includes...)
491         close CONF_H;                   # C symbol definition template
492         close INIT;                             # Required initializations
493         close MAGIC;                    # Magic C symbol redefinition templates
494
495         print $dependencies if $opt_v;  # Print extracted dependencies
496 }
497
498 # Process the ?MAKE: line
499 sub main'load_p_make {
500         package main;
501         local($_) = @_;
502         local(@ary);                                    # Locally defined symbols
503         local(@dep);                                    # Dependencies
504         if (/^[\w+ ]*:/) {                              # Main dependency rule
505                 s|^\s*||;                                       # Remove leading spaces
506                 chop;
507                 s/:(.*)//;
508                 @dep = split(' ', $1);                  # Dependencies
509                 @ary = split(' ');                              # Locally defined symbols
510                 foreach $sym (@ary) {
511                         # Symbols starting with a '+' are meant for internal use only.
512                         next if $sym =~ s/^\+//;
513                         # Only sumbols starting with a lowercase letter are to
514                         # appear in config.sh, excepted the ones listed in Except.
515                         if ($sym =~ /^[_a-z]/ || $Except{$sym}) {
516                                 $shmaster{"\$$sym"} = undef;
517                                 push(@Master,"?$unit:$sym=''\n");       # Initializations
518                         }
519                 }
520                 $condlist = '';                         # List of conditional symbols
521                 local($sym);                            # Symbol copy, avoid @dep alteration
522                 foreach $dep (@dep) {
523                         if ($dep =~ /^\+[A-Za-z]/) {
524                                 ($sym = $dep) =~ s|^\+||;
525                                 $condlist .= "$sym ";
526                                 push(@Cond, $sym) unless $condseen{$sym};
527                                 $condseen{$sym}++;              # Conditionally wanted
528                         }
529                 }
530                 # Append to already existing dependencies. The 'defined' variable
531                 # is set for &write_out, used to implement ?L: and ?I: canvas. It is
532                 # reset each time a new unit is parsed.
533                 # NB: leading '+' for defined symbols (internal use only) have been
534                 # removed at this point, but conditional dependencies still bear it.
535                 $defined = join(' ', @ary);             # Symbols defined by this unit
536                 $dependencies .= $defined . ':' . join(' ', @dep) . "\n";
537                 $dependencies .= "      -cond $condlist\n" if $condlist;
538         } else {
539                 $dependencies .= $_;            # Building rules
540         }
541 }
542
543 # Process the ?O: line
544 sub main'load_p_obsolete {
545         package main;
546         local($_) = @_;
547         $Obsolete{"$unit.U"} .= $_;             # Message(s) to print if unit is used
548 }
549
550 # Process the ?S: lines
551 sub main'load_p_shell {
552         package main;
553         local($_) = @_;
554         unless ($s_symbol) {
555                 if (/^(\w+).*:/) {
556                         $s_symbol = $1;
557                         print "  ?S: $s_symbol\n" if $opt_d;
558                 } else {
559                         warn "\"$file\", line $.: syntax error in ?S: construct.\n";
560                         $s_symbol = $unit;
561                         return;
562                 }
563                 # Deal with obsolete symbol list (enclosed between parenthesis)
564                 &record_obsolete("\$$_") if /\(/;
565         }
566         m|^\.\s*$| && ($s_symbol = '');         # End of comment
567 }
568
569 # Process the ?C: lines
570 sub main'load_p_c {
571         package main;
572         local($_) = @_;
573         unless ($c_symbol) {
574                 if (s/^(\w+)\s*~\s*(\S+)\s*(.*):/$1 $3:/) {
575                         # The ~ operator aliases the main C symbol to another symbol which
576                         # is to be used instead for definition in config.h. That is to say,
577                         # the line '?C:SYM ~ other:' would look for symbol 'other' instead,
578                         # and the documentation for symbol SYM would only be included in
579                         # config.h if 'other' were actually wanted.
580                         $c_symbol = $2;                 # Alias for definition in config.h
581                         print "  ?C: $1 ~ $c_symbol\n" if $opt_d;
582                 } elsif (/^(\w+).*:/) {
583                         # Default behaviour. Include in config.h if symbol is needed.
584                         $c_symbol = $1;
585                         print "  ?C: $c_symbol\n" if $opt_d;
586                 } else {
587                         warn "\"$file\", line $.: syntax error in ?C: construct.\n";
588                         $c_symbol = $unit;
589                         return;
590                 }
591                 # Deal with obsolete symbol list (enclosed between parenthesis) and
592                 # make sure that list do not appear in config.h.SH by removing it.
593                 &record_obsolete("$_") if /\(/;
594                 s/\s*\(.*\)//;                                  # Get rid of obsolete symbol list
595         }
596         s|^(\w+)\s*|?$c_symbol:/* $1| ||                                                # Start of comment
597         (s|^\.\s*$|?$c_symbol: */\n| && ($c_symbol = '', 1)) || # End of comment
598         s|^(.*)|?$c_symbol: *$1|;                                                               # Middle of comment
599         &p_config("$_");                                        # Add comments to config.h.SH
600 }
601
602 # Process the ?H: lines
603 sub main'load_p_config {
604         package main;
605         local($_) = @_;
606         local($constraint);                                     # Constraint to be used for inclusion
607         ++$old_version if s/^\?%1://;           # Old version
608         if (s/^\?(\w+)://) {                            # Remove leading '?var:'
609                 $constraint = $1;                               # Constraint is leading '?var'
610         } else {
611                 $constraint = '';                               # No constraint
612         }
613         if (/^#.*\$/) {                                         # Look only for cpp lines
614                 if (m|^#\$(\w+)\s+(\w+).*\$(\w+)|) {
615                         # Case: #$d_var VAR "$var"
616                         $constraint = $2 unless $constraint;
617                         print "  ?H: ($constraint) #\$$1 $2 \"\$$3\"\n" if $opt_d;
618                         $cmaster{$2} = undef;
619                         $cwanted{$2} = "$1\n$3";
620                 } elsif (m|^#define\s+(\w+)\((.*)\)\s+\$(\w+)|) {
621                         # Case: #define VAR(x) $var
622                         $constraint = $1 unless $constraint;
623                         print "  ?H: ($constraint) #define $1($2) \$$3\n" if $opt_d;
624                         $cmaster{$1} = undef;
625                         $cwanted{$1} = $3;
626                 } elsif (m|^#\$define\s+(\w+)|) {
627                         # Case: #$define VAR
628                         $constraint = $1 unless $constraint;
629                         print "  ?H: ($constraint) #define $1\n" if $opt_d;
630                         $cmaster{$1} = undef;
631                         $cwanted{$1} = "define\n$unit";
632                 } elsif (m|^#\$(\w+)\s+(\w+)|) {
633                         # Case: #$d_var VAR
634                         $constraint = $2 unless $constraint;
635                         print "  ?H: ($constraint) #\$$1 $2\n" if $opt_d;
636                         $cmaster{$2} = undef;
637                         $cwanted{$2} = $1;
638                 } elsif (m|^#define\s+(\w+).*\$(\w+)|) {
639                         # Case: #define VAR "$var"
640                         $constraint = $1 unless $constraint;
641                         print "  ?H: ($constraint) #define $1 \"\$$2\"\n" if $opt_d;
642                         $cmaster{$1} = undef;
643                         $cwanted{$1} = $2;
644                 } else {
645                         $constraint = $unit unless $constraint;
646                         print "  ?H: ($constraint) $_" if $opt_d;
647                 }
648         } else {
649                 print "  ?H: ($constraint) $_" if $opt_d;
650         }
651         # If not a single ?H:. line, add the leading constraint
652         s/^\.// || s/^/?$constraint:/;
653         print CONF_H;
654 }
655
656 # Process the ?M: lines
657 sub main'load_p_magic {
658         package main;
659         local($_) = @_;
660         unless ($m_symbol) {
661                 if (/^(\w+):\s*([\w\s]*)\n$/) {
662                         # A '?M:sym:' line implies a '?W:%<:sym' since we'll need to know
663                         # about the wantedness of sym later on when building confmagic.h.
664                         # Buf is sym is wanted, then the C symbol dependencies have to
665                         # be triggered. That is done by introducing sym in the mwanted
666                         # array, known by the Wanted file construction process...
667                         $m_symbol = $1;
668                         print "  ?M: $m_symbol\n" if $opt_d;
669                         $mwanted{$m_symbol} = $2;               # Record C dependencies
670                         &p_wanted("$unit:$m_symbol");   # Build fake ?W: line
671                 } else {
672                         warn "\"$file\", line $.: syntax error in ?M: construct.\n";
673                 }
674                 return;
675         }
676         (s/^\.\s*$/?$m_symbol:\n/ && ($m_symbol = '', 1)) ||    # End of block
677         s/^/?$m_symbol:/;
678         print MAGIC_H;                                  # Definition goes to confmagic.h
679         print "  ?M: $_" if $opt_d;
680 }
681
682 # Process the ?W: lines
683 sub main'load_p_wanted {
684         package main;
685         # Syntax is ?W:<shell symbols>:<C symbols>
686         local($active) = $_[0] =~ /^([^:]*):/;          # Symbols to activate
687         local($look_symbols) = $_[0] =~ /:(.*)/;        # When those are used
688         local(@syms) = split(/ /, $look_symbols);       # Keep original spacing info
689         $active =~ s/\s+/\n/g;                                          # One symbol per line
690
691         # Concatenate quoted strings, so saying something like 'two words' will
692         # be introduced as one single symbol "two words".
693         local(@symbols);                                # Concatenated symbols to look for
694         local($concat) = '';                    # Concatenation buffer
695         foreach (@syms) {
696                 if (s/^\'//) {
697                         $concat = $_;
698                 } elsif (s/\'$//) {
699                         push(@symbols, $concat . ' ' . $_);
700                         $concat = '';
701                 } else {
702                         push(@symbols, $_) unless $concat;
703                         $concat .= ' ' . $_ if $concat;
704                 }
705         }
706
707         local($fake);           # Fake unique shell symbol to reparent C symbol
708
709         # Now record symbols in master and wanted tables
710         foreach (@symbols) {
711                 $cmaster{$_} = undef;                                   # Asks for look-up in C files
712                 # Make a fake C symbol and associate that with the wanted symbol
713                 # so that later we know were it comes from
714                 $fake = &gensym;
715                 $cwanted{$_} = "$fake";                                 # Attached to this symbol
716                 push(@Master, "?$unit:$fake=''");               # Fake initialization
717         }
718 }
719
720 # Extract filenames from manifest
721 sub main'load_extract_filenames {
722         package main;
723         &build_filext;                  # Construct &is_cfile and &is_shfile
724         print "Extracting filenames (C and SH files) from $NEWMANI...\n"
725                 unless $opt_s;
726         open(NEWMANI,$NEWMANI) || die "Can't open $NEWMANI.\n";
727         local($file);
728         while (<NEWMANI>) {
729                 ($file) = split(' ');
730                 next if $file eq 'config_h.SH';                 # skip config_h.SH
731                 next if $file eq 'Configure';                   # also skip Configure
732                 next if $file eq 'confmagic.h' && $opt_M;
733                 push(@SHlist, $file) if &is_shfile($file);
734                 push(@clist, $file) if &is_cfile($file);
735         }
736 }
737
738 # Construct two file identifiers based on the file suffix: one for C files,
739 # and one for SH files (using the $cext and $shext variables) defined in
740 # the .package file.
741 # The &is_cfile and &is_shfile routine may then be called to known whether
742 # a given file is a candidate for holding C or SH symbols.
743 sub main'load_build_filext {
744         package main;
745         &build_extfun('is_cfile', $cext, '.c .h .y .l');
746         &build_extfun('is_shfile', $shext, '.SH');
747 }
748
749 # Build routine $name to identify extensions listed in $exts, ensuring
750 # that $minimum is at least matched (both to be backward compatible with
751 # older .package and because it is really the minimum requirred).
752 sub main'load_build_extfun {
753         package main;
754         local($name, $exts, $minimum) = @_;
755         local(@single);         # Single letter dot extensions (may be grouped)
756         local(@others);         # Other extensions
757         local(%seen);           # Avoid duplicate extensions
758         foreach $ext (split(' ', "$exts $minimum")) {
759                 next if $seen{$ext}++;
760                 if ($ext =~ s/^\.(\w)$/$1/) {
761                         push(@single, $ext);
762                 } else {
763                         # Convert into perl's regexp
764                         $ext =~ s/\./\\./g;             # Escape .
765                         $ext =~ s/\?/./g;               # ? turns into .
766                         $ext =~ s/\*/.*/g;              # * turns into .*
767                         push(@others, $ext);
768                 }
769         }
770         local($fn) = &q(<<EOF);         # Function being built
771 :sub $name {
772 :       local(\$_) = \@_;
773 EOF
774         local($single);         # Single regexp: .c .h grouped into .[ch]
775         $single = '\.[' . join('', @single) . ']' if @single;
776         $fn .= &q(<<EOL) if @single;
777 :       return 1 if /$single\$/;
778 EOL
779         foreach $ext (@others) {
780                 $fn .= &q(<<EOL);
781 :       return 1 if /$ext\$/;
782 EOL
783         }
784         $fn .= &q(<<EOF);
785 :       0;      # None of the extensions may be applied to file name
786 :}
787 EOF
788         print $fn if $opt_d;
789         eval $fn;
790         chop($@) && die "Can't compile '$name':\n$fn\n$@.\n";
791 }
792
793 # Remove ':' quotations in front of the lines
794 sub main'load_q {
795         package main;
796         local($_) = @_;
797         local($*) = 1;
798         s/^://g;
799         $_;
800 }
801
802 # The %Depend array records the functions we use to process the configuration
803 # lines in the unit, with a special meaning. It is important that all the
804 # known control symbols be listed below, so that metalint does not complain.
805 # The %Lcmp array contains valid layouts and their comparaison value.
806 sub main'load_init_depend {
807         package main;
808         %Depend = (
809                 'MAKE', 'p_make',                               # The ?MAKE: line records dependencies
810                 'INIT', 'p_init',                               # Initializations printed verbatim
811                 'LINT', 'p_lint',                               # Hints for metalint
812                 'RCS', 'p_ignore',                              # RCS comments are ignored
813                 'C', 'p_c',                                             # C symbols
814                 'D', 'p_default',                               # Default value for conditional symbols
815                 'E', 'p_example',                               # Example of usage
816                 'F', 'p_file',                                  # Produced files
817                 'H', 'p_config',                                # Process the config.h lines
818                 'I', 'p_include',                               # Added includes
819                 'L', 'p_library',                               # Added libraries
820                 'M', 'p_magic',                                 # Process the confmagic.h lines
821                 'O', 'p_obsolete',                              # Unit obsolescence
822                 'P', 'p_public',                                # Location of PD implementation file
823                 'S', 'p_shell',                                 # Shell variables
824                 'T', 'p_temp',                                  # Shell temporaries used
825                 'V', 'p_visible',                               # Visible symbols like 'rp', 'dflt'
826                 'W', 'p_wanted',                                # Wanted value for interpreter
827                 'X', 'p_ignore',                                # User comment is ignored
828                 'Y', 'p_layout',                                # User-defined layout preference
829         );
830         %Lcmp = (
831                 'top',          -1,
832                 'default',      0,
833                 'bottom',       1,
834         );
835 }
836
837 # Extract dependencies from units held in @ARGV
838 sub main'load_extract_dependencies {
839         package main;
840         local($proc);                                           # Procedure used to handle a ctrl line
841         local($file);                                           # Current file scanned
842         local($dir, $unit);                                     # Directory and unit's name
843         local($old_version) = 0;                        # True when old-version unit detected
844         local($mc) = "$MC/U";                           # Public metaconfig directory
845         local($line);                                           # Last processed line for metalint
846
847         printf "Extracting dependency lists from %d units...\n", $#ARGV+1
848                 unless $opt_s;
849
850         chdir $WD;                                                      # Back to working directory
851         &init_extraction;                                       # Initialize extraction files
852         $dependencies = ' ' x (50 * @ARGV);     # Pre-extend
853         $dependencies = '';
854
855         # We do not want to use the <> construct here, because we need the
856         # name of the opened files (to get the unit's name) and we want to
857         # reset the line number for each files, and do some pre-processing.
858
859         file: while ($file = shift(@ARGV)) {
860                 close FILE;                                             # Reset line number
861                 $old_version = 0;                               # True if unit is an old version
862                 if (open(FILE, $file)) {
863                         ($dir, $unit) = ('', $file)
864                                 unless ($dir, $unit) = ($file =~ m|(.*)/(.*)|);
865                         $unit =~ s|\.U$||;                      # Remove extension
866                 } else {
867                         warn("Can't open $file.\n");
868                 }
869                 # If unit is in the standard public directory, keep only the unit name
870                 $file = "$unit.U" if $dir eq $mc;
871                 print "$dir/$unit.U:\n" if $opt_d;
872                 line: while (<FILE>) {
873                         $line = $_;                                     # Save last processed unit line
874                         if (s/^\?([\w\-]+)://) {        # We may have found a control line
875                                 $proc = $Depend{$1};    # Look for a procedure to handle it
876                                 unless ($proc) {                # Unknown control line
877                                         $proc = $1;                     # p_unknown expects symbol in '$proc'
878                                         eval '&p_unknown';      # Signal error (metalint only)
879                                         next line;                      # And go on next line
880                                 }
881                                 # Long lines may be escaped with a final backslash
882                                 $_ .= &complete_line(FILE) if s/\\\s*$//;
883                                 # Run macros substitutions
884                                 s/%</$unit/g;                   # %< expands into the unit's name
885                                 if (s/%\*/$unit/) {
886                                         # %* expanded into the entire set of defined symbols
887                                         # in the old version. Now it is only the unit's name.
888                                         ++$old_version;
889                                 }
890                                 eval { &$proc($_) };            # Process the line
891                         } else {
892                                 next file unless $body;         # No procedure to handle body
893                                 do {
894                                         $line = $_;                             # Save last processed unit line
895                                         eval { &$body($_) } ;   # From now on, it's the unit body
896                                 } while (defined ($_ = <FILE>));
897                                 next file;
898                         }
899                 }
900         } continue {
901                 warn("    Warning: $file is a pre-3.0 version.\n") if $old_version;
902                 &$ending($line) if $ending;                     # Post-processing for metalint
903         }
904
905         &end_extraction;                # End the extraction process
906 }
907
908 # The first line was escaped with a final \ character. Every following line
909 # is to be appended to it (until we found a real \n not escaped). Note that
910 # the leading spaces of the continuation line are removed, so any space should
911 # be added before the former \ if needed.
912 sub main'load_complete_line {
913         package main;
914         local($file) = @_;              # File where lines come from
915         local($_);
916         local($read) = '';              # Concatenation of all the continuation lines found
917         while (<$file>) {
918                 s/^\s+//;                               # Remove leading spaces
919                 if (s/\\\s*$//) {               # Still followed by a continuation line
920                         $read .= $_;    
921                 } else {                                # We've reached the end of the continuation
922                         return $read . $_;
923                 }
924         }
925 }
926
927 # Record obsolete symbols association (new versus old), that is to say for a
928 # given old symbol, $Obsolete{'old'} = new symbol to be used. A '$' is prepended
929 # for all shell variables
930 sub main'load_record_obsolete {
931         package main;
932         local($_) = @_;
933         local(@obsoleted);                                      # List of obsolete symbols
934         local($symbol);                                         # New symbol which must be used
935         local($dollar) = s/^\$// ? '$':'';      # The '$' or a null string
936         # Syntax for obsolete symbols specification is
937         #    list of symbols (obsolete ones):
938         if (/^(\w+)\s*\((.*)\)\s*:$/) {
939                 $symbol = "$dollar$1";
940                 @obsoleted = split(' ', $2);            # List of obsolete symbols
941         } else {
942                 if (/^(\w+)\s*\((.*):$/) {
943                         warn "\"$file\", line $.: final ')' before ':' missing.\n";
944                         $symbol = "$dollar$1";
945                         @obsoleted = split(' ', $2);
946                 } else {
947                         warn "\"$file\", line $.: syntax error.\n";
948                         return;
949                 }
950         }
951         foreach $val (@obsoleted) {
952                 $_ = $dollar . $val;
953                 if (defined $Obsolete{$_}) {
954                 warn "\"$file\", line $.: '$_' already obsoleted by '$Obsolete{$_}'.\n";
955                 } else {
956                         $Obsolete{$_} = $symbol;        # Record (old, new) tuple
957                 }
958         }
959 }
960
961 # Dump obsolete symbols used in file 'Obsolete'. Also write Obsol_h.U and
962 # Obsol_sh.U to record old versus new mappings if the -o option was used.
963 sub main'load_dump_obsolete {
964         package main;
965         unless (-f 'Obsolete') {
966                 open(OBSOLETE, ">Obsolete") || die "Can't create Obsolete.\n";
967         }
968         open(OBSOL_H, ">.MT/Obsol_h.U") || die "Can't create .MT/Obsol_h.U.\n";
969         open(OBSOL_SH, ">.MT/Obsol_sh.U") || die "Can't create .MT/Obsol_sh.U.\n";
970         local($file);                                           # File where obsolete symbol was found
971         local($old);                                            # Name of this old symbol
972         local($new);                                            # Value of the new symbol to be used
973         # Leave a blank line at the top so that anny added ^L will stand on a line
974         # by itself (the formatting process adds a ^L when a new page is needed).
975         format OBSOLETE_TOP =
976
977               File                 |      Old symbol      |      New symbol
978 -----------------------------------+----------------------+---------------------
979 .
980         format OBSOLETE =
981 @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | @<<<<<<<<<<<<<<<<<<< | @<<<<<<<<<<<<<<<<<<<
982 $file,                               $old,                  $new
983 .
984         local(%seen);
985         foreach $key (sort keys %ofound) {
986                 ($file, $old, $new) = ($key =~ /^(\S+)\s+(\S+)\s+(\S+)/);
987                 write(OBSOLETE) unless $file eq 'XXX';
988                 next unless $opt_o;                             # Obsolete mapping done only with -o
989                 next if $seen{$old}++;                  # Already remapped, thank you
990                 if ($new =~ s/^\$//) {                  # We found an obsolete shell symbol
991                         $old =~ s/^\$//;
992                         print OBSOL_SH "$old=\"\$$new\"\n";
993                 } else {                                                # We found an obsolete C symbol
994                         print OBSOL_H "#ifdef $new\n";
995                         print OBSOL_H "#define $old $new\n";
996                         print OBSOL_H "#endif\n\n";
997                 }
998         }
999         close OBSOLETE;
1000         close OBSOL_H;
1001         close OBSOL_SH;
1002         if (-s 'Obsolete') {
1003                 print "*** Obsolete symbols found -- see file 'Obsolete' for a list.\n";
1004         } else {
1005                 unlink 'Obsolete';
1006         }
1007         undef %ofound;                          # Not needed any more
1008 }
1009
1010 # Parse files and build cross references
1011 sub main'load_build_xref {
1012         package main;
1013         print "Building cross-reference files...\n" unless $opt_s;
1014         unless (-f $NEWMANI) {
1015                 &manifake;
1016                 die "No $NEWMANI--don't know who to scan.\n" unless -f $NEWMANI;
1017         }
1018
1019         open(FUI, "|sort | uniq >I.fui") || die "Can't create I.fui.\n";
1020         open(UIF, "|sort | uniq >I.uif") || die "Can't create I.uif.\n";
1021
1022         local($search);                                                 # Where to-be-evaled script is held
1023         local($_) = ' ' x 50000 if $opt_m;              # Pre-extend pattern search space
1024         local(%visited);                                                # Records visited files
1025         local(%lastfound);                                              # Where last occurence of key was
1026
1027         # Map shell symbol names to units by reverse engineering the @Master array
1028         # which records all the known shell symbols and the units where they
1029         # are defined.
1030         foreach $init (@Master) {
1031                 $init =~ /^\?(.*):(.*)=''/ && ($shwanted{"\$$2"} = $1);
1032         }
1033
1034         # Now we are a little clever, and build a loop to eval so that we don't
1035         # have to recompile our patterns on every file.  We also use "study" since
1036         # we are searching the same string for many different things.  Hauls!
1037
1038         if (@clist) {
1039                 print "    Scanning .[chyl] files for symbols...\n" unless $opt_s;
1040                 $search = ' ' x (40 * (@cmaster + @ocmaster));  # Pre-extend
1041                 $search = "while (<>) {study;\n";                               # Init loop over ARGV
1042                 foreach $key (keys(cmaster)) {
1043                         $search .= "\$cmaster{'$key'} .= \"\$ARGV#\" if /\\b$key\\b/;\n";
1044                 }
1045                 foreach $key (grep(!/^\$/, keys %Obsolete)) {
1046                         $search .= "&ofound('$key') if /\\b$key\\b/;\n";
1047                 }
1048                 $search .= "}\n";                       # terminate loop
1049                 print $search if $opt_d;
1050                 @ARGV = @clist;
1051                 # Swallow each file as a whole, if memory is available
1052                 undef $/ if $opt_m;
1053                 eval $search;
1054                 eval '';
1055                 $/ = "\n";
1056                 while (($key,$value) = each(cmaster)) {
1057                         next if $value eq '';
1058                         foreach $file (sort(split(/#/, $value))) {
1059                                 next if $file eq '';
1060                                 # %cwanted may contain value separated by \n -- take last one
1061                                 @sym = split(/\n/, $cwanted{$key});
1062                                 $sym = pop(@sym);
1063                                 $shell = "\$$sym";
1064                                 print FUI
1065                                         pack("A35", $file),
1066                                         pack("A20", "$shwanted{$shell}.U"),
1067                                         $key, "\n";
1068                                 print UIF
1069                                         pack("A20", "$shwanted{$shell}.U"),
1070                                         pack("A25", $key),
1071                                         $file, "\n";
1072                         }
1073                 }
1074         }
1075
1076         undef @clist;
1077         undef %cwanted;
1078         undef %cmaster;         # We're not building Configure, we may delete this
1079         %visited = ();
1080         %lastfound = ();
1081
1082         if (@SHlist) {
1083                 print "    Scanning .SH files for symbols...\n" unless $opt_s;
1084                 $search = ' ' x (40 * (@shmaster + @oshmaster));        # Pre-extend
1085                 $search = "while (<>) {study;\n";
1086                 # All the keys already have a leading '$'
1087                 foreach $key (keys(shmaster)) {
1088                         $search .= "\$shmaster{'$key'} .= \"\$ARGV#\" if /\\$key\\b/;\n";
1089                 }
1090                 foreach $key (grep (/^\$/, keys %Obsolete)) {
1091                         $search .= "&ofound('$key') if /\\$key\\b/;\n";
1092                 }
1093                 $search .= "}\n";
1094                 print $search if $opt_d;
1095                 @ARGV = @SHlist;
1096                 # Swallow each file as a whole, if memory is available
1097                 undef $/ if $opt_m;
1098                 eval $search;
1099                 eval '';
1100                 $/ = "\n";
1101                 while (($key,$value) = each(shmaster)) {
1102                         next if $value eq '';
1103                         foreach $file (sort(split(/#/, $value))) {
1104                                 next if $file eq '';
1105                                 print FUI
1106                                         pack("A35", $file),
1107                                         pack("A20", "$shwanted{$key}.U"),
1108                                         $key, "\n";
1109                                 print UIF
1110                                         pack("A20", "$shwanted{$key}.U"),
1111                                         pack("A25", $key),
1112                                         $file, "\n";
1113                         }
1114                 }
1115         }
1116
1117         close FUI;
1118         close UIF;
1119
1120         # If obsolete symbols where found, write an Obsolete file which lists where
1121         # each of them appear and the new symbol to be used. Also write Obsol_h.U
1122         # and Obsol_sh.U in .MT for later perusal.
1123
1124         &dump_obsolete;                                         # Dump obsolete symbols if any
1125
1126         # Clean-up memory by freeing useless data structures
1127         undef @SHlist;
1128         undef %shmaster;
1129 }
1130
1131 # This routine records matches of obsolete keys (C or shell)
1132 sub main'load_ofound {
1133         package main;
1134         local($key) = @_;
1135         local($_) = $Obsolete{$key};            # Value of new symbol
1136         $ofound{"$ARGV $key $_"}++;                     # Record obsolete match
1137         $cmaster{$_} .= "$ARGV#" unless /^\$/;  # A C hit
1138         $shmaster{$_} .= "$ARGV#" if /^\$/;             # Or a shell one
1139 }
1140
1141 # Create a new symbol name each time it is invoked. That name is suitable for
1142 # usage as a perl variable name.
1143 sub main'load_gensym {
1144         package main;
1145         $Gensym = 'AAAAA' unless $Gensym;
1146         $Gensym++;
1147 }
1148
1149 sub main'load_manifake {
1150         package main;
1151     # make MANIFEST and MANIFEST.new say the same thing
1152     if (! -f $NEWMANI) {
1153         if (-f $MANI) {
1154             open(IN,$MANI) || die "Can't open $MANI";
1155             open(OUT,">$NEWMANI") || die "Can't create $NEWMANI";
1156             while (<IN>) {
1157                 if (/---/) {
1158                                         # Everything until now was a header...
1159                                         close OUT;
1160                                         open(OUT,">$NEWMANI") ||
1161                                                 die "Can't recreate $NEWMANI";
1162                                         next;
1163                                 }
1164                 s/^\s*(\S+\s+)[0-9]*\s*(.*)/$1$2/;
1165                                 print OUT;
1166                                 print OUT "\n" unless /\n$/;    # If no description
1167             }
1168             close IN;
1169                         close OUT;
1170         }
1171         else {
1172 die "You need to make a $NEWMANI file, with names and descriptions.\n";
1173         }
1174     }
1175 }
1176
1177 # Perform ~name expansion ala ksh...
1178 # (banish csh from your vocabulary ;-)
1179 sub main'load_tilda_expand {
1180         package main;
1181         local($path) = @_;
1182         return $path unless $path =~ /^~/;
1183         $path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e;                    # ~name
1184         $path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e;   # ~
1185         $path;
1186 }
1187
1188 # Set up profile components into %Profile, add any profile-supplied options
1189 # into @ARGV and return the command invocation name.
1190 sub main'load_profile {
1191         package main;
1192         local($profile) = &tilda_expand($ENV{'DIST'} || '~/.dist_profile');
1193         local($me) = $0;                # Command name
1194         $me =~ s|.*/(.*)|$1|;   # Keep only base name
1195         return $me unless -s $profile;
1196         local(*PROFILE);                # Local file descriptor
1197         local($options) = '';   # Options we get back from profile
1198         unless (open(PROFILE, $profile)) {
1199                 warn "$me: cannot open $profile: $!\n";
1200                 return;
1201         }
1202         local($_);
1203         local($component);
1204         while (<PROFILE>) {
1205                 next if /^\s*#/;        # Skip comments
1206                 next unless /^$me/o;
1207                 if (s/^$me://o) {       # progname: options
1208                         chop;
1209                         $options .= $_; # Merge options if more than one line
1210                 }
1211                 elsif (s/^$me-([^:]+)://o) {    # progname-component: value
1212                         $component = $1;
1213                         chop;
1214                         s/^\s+//;               # Trim leading and trailing spaces
1215                         s/\s+$//;
1216                         $Profile{$component} = $_;
1217                 }
1218         }
1219         close PROFILE;
1220         return unless $options;
1221         require 'shellwords.pl';
1222         local(@opts);
1223         eval '@opts = &shellwords($options)';   # Protect against mismatched quotes
1224         unshift(@ARGV, @opts);
1225         return $me;                             # Return our invocation name
1226 }
1227
1228 #
1229 # End of dataloading section.
1230 #
1231