ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/scripts/filepp
Revision: 1515
Committed: Fri Oct 1 21:11:29 2004 UTC (19 years, 9 months ago) by tim
File size: 88664 byte(s)
Log Message:
adding fortran90 make dependency tool

File Contents

# User Rev Content
1 tim 1515 #!/usr/bin/perl -w
2    
3     package FileEntry;
4    
5     sub new {
6     my $type = shift;
7     my $filename = shift;
8     my $path = shift;
9     my $self = {};
10     $self->{'source_file'} = $filename;
11     $self->{'filepath'} = $path;
12     $self->{'includes'} = {};
13     $self->{'uses'} = {};
14     $self->{'modules'} = {};
15     bless $self;
16     }
17    
18     ########################################################################
19     #
20     # filepp is free software; you can redistribute it and/or modify
21     # it under the terms of the GNU General Public License as published by
22     # the Free Software Foundation; either version 2 of the License, or
23     # (at your option) any later version.
24     #
25     # This program is distributed in the hope that it will be useful,
26     # but WITHOUT ANY WARRANTY; without even the implied warranty of
27     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28     # GNU General Public License for more details.
29     #
30     # You should have received a copy of the GNU General Public License
31     # along with this program; see the file COPYING. If not, write to
32     # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
33     #
34     ########################################################################
35     #
36     # Project : File Preprocessor
37     # Filename : $RCSfile: filepp,v $
38     # Author : $Author: tim $
39     # Maintainer : Darren Miller: darren@cabaret.demon.co.uk
40     # File version : $Revision: 1.1 $
41     # Last changed : $Date: 2004-10-01 21:11:29 $
42     # Description : Main program
43     # Licence : GNU copyleft
44     #
45     ########################################################################
46    
47     package Filepp;
48    
49     use strict "vars";
50     use strict "subs";
51     #use Graph;
52     # Used to all filepp to work with any char, not just ascii,
53     # feel free to remove this if it causes you problems
54     use bytes;
55    
56     # version number of program
57     my $VERSION = '1.7.1';
58    
59     # list of paths to search for modules, normal Perl list + module dir
60     push(@INC, "/home/maul/gezelter/tim/code/OOPSE-2.0/scripts/filepp-1.7.1/modules/f90dpend/");
61    
62     # index of keywords supported and functions to deal with them
63     my %Keywords = (
64     '#comment' => \&Comment,
65     '#define' => \&Define,
66     '#elif' => \&Elif,
67     '#else' => \&Else,
68     '#endif' => \&Endif,
69     '#error' => \&Error,
70     '#if' => \&If,
71     '#ifdef' => \&Ifdef,
72     '#ifndef' => \&Ifndef,
73     '#include' => \&Include,
74     '#pragma' => \&Pragma,
75     '#undef' => \&Undef,
76     '#warning' => \&Warning
77     );
78    
79     # set of functions which process the file in the Parse routine.
80     # Processors are functions which take in a line and return the processed line.
81     # Note: this is done as a string rather than pointer to a function because
82     # it makes list easier to modify/remove from/print.
83     my @Processors = ( "Filepp::ParseKeywords", "Filepp::ReplaceDefines" );
84     # processor types say what the processor should be run on: choice is:
85     # 0: Everything (default)
86     # 1: Full lines only (lines originating from Parse function)
87     # 2: Part lines only (lines originating from within keywords, eg:
88     # #if "condition", "condition" is a part line)
89     my %ProcessorTypes = (
90     'Filepp::ParseKeywords' => 1,
91     'Filepp::ReplaceDefines' => 0
92     );
93    
94     # functions to run each time a new base input file is opened or closed
95     my @OpenInputFuncs = ();
96     my @CloseInputFuncs = ();
97    
98     # functions to run each time a new output file is opened or closed
99     my @OpenOutputFuncs = ();
100     my @CloseOutputFuncs = ();
101    
102     # safe mode is for the paranoid, when enabled turns off #pragma filepp,
103     # enabled by default
104     my $safe_mode = 0;
105    
106     # test for shebang mode, used for "filepp script", ie. executable file with
107     # "#!/usr/bin/perl /usr/local/bin/filepp" at the top
108     my $shebang = 1;
109    
110     # allow $keywordchar, $contchar, $optlineendchar and $macroprefix
111     # to be perl regexps
112     my $charperlre = 0;
113    
114     # character(s) which prefix environment variables - defaults to shell-style '$'
115     my $envchar = "\$";
116    
117     # boolean determining whether line continuation is implicit if there are more
118     # open brackets than close brackets on a line
119     # disabled by default
120     my $parselineend = \&Filepp::ParseLineEnd;
121    
122     # character(s) which replace continuation char(s) - defaults to C-style nothing
123     my $contrepchar = "";
124    
125     # character(s) which prefix keywords - defaults to C-style '#'
126     my $keywordchar;
127     if($charperlre) { $keywordchar = "\#"; }
128     else { $keywordchar = "\Q#\E"; }
129    
130     # character(s) which signifies continuation of a line - defaults to C-style '\'
131     my $contchar;
132     if($charperlre) { $contchar = "\\\\"; }
133     else { $contchar = "\Q\\\E"; }
134    
135     # character(s) which optionally signifies the end of a line -
136     # defaults to empty string ''
137     my $optlineendchar = "";
138    
139     # character(s) which prefix macros - defaults to nothing
140     my $macroprefix = "";
141    
142     # flag to use macro prefix in keywords (on by default)
143     my $macroprefixinkeywords = 1;
144    
145     # check if macros must occur as words when replacing, set this to '\b' if
146     # you prefer cpp style behaviour as default
147     my $bound = '';
148    
149     # number of line currently being parsed (int)
150     my $line = 0;
151    
152     # file currently being parsed
153     my $file = "";
154    
155     # list of input files
156     my @Inputfiles;
157    
158     # list of files to include macros from
159     my @Imacrofiles;
160    
161     # flag to control when output is written
162     my $output = 1;
163    
164     # name of outputfile - defaults to STDOUT
165     my $outputfile = "";
166    
167     # overwrite mode - automatically overwrites old file with new file
168     my $overwrite = 0;
169    
170     # overwrite conversion mode - conversion from input filename to output filename
171     my $overwriteconv = "";
172    
173     # list of keywords which have "if" functionality
174     my %Ifwords = ('#if', '',
175     '#ifdef', '',
176     '#ifndef', '');
177    
178     # list of keywords which have "else" functionality
179     my %Elsewords = ('#else', '',
180     '#elif', '');
181    
182     # list of keywords which have "endif" functionality
183     my %Endifwords = ('#endif', '');
184    
185     # current level of include files
186     my $include_level = -1;
187    
188     # suppress blank lines in header files (indexed by include level)
189     my $blanksuppopt = 0;
190     my @blanksupp;
191     # try to keep same number lines in output file as input file
192     my $preserveblank = 0;
193    
194     # counter of recursion level for detecting recursive macros
195     my $recurse_level = -1;
196    
197     # debugging info, 1=on, 0=off
198     my $debug = 0;
199     # send debugging info to stdout rather than stderr
200     my $debugstdout = 0;
201     # debug prefix character or string
202     my $debugprefix = "";
203     # debug postfix character or string
204     my $debugpostfix = "\n";
205    
206     # hash of macros defined - standard ones already included
207     my %Defines = (
208     '__BASE_FILE__' => "",
209     '__DATE__' => "",
210     '__FILEPP_INPUT__' => "Generated automatically from __BASE_FILE__ by filepp",
211     '__FILE__' => $file,
212     '__INCLUDE_LEVEL__' => $include_level,
213     '__ISO_DATE__' => "",
214     '__LINE__' => $line,
215     '__NEWLINE__' => "\n",
216     '__NULL__' => "",
217     '__TAB__' => "\t",
218     '__TIME__' => "",
219     '__VERSION__' => $VERSION
220     );
221     # hash of first chars in each macro
222     my %DefineLookup;
223     # length of longest and shortest define
224     my ($defmax, $defmin);
225     GenerateDefinesKeys();
226    
227     # set default values for date and time
228     {
229     # conversions of month number into letters (0-11)
230     my @MonthChars = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
231     'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
232     #prepare standard defines
233     my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isbst) =
234     localtime(time());
235     $year += 1900;
236     $sec = sprintf("%02d", $sec);
237     $min = sprintf("%02d", $min);
238     $hour = sprintf("%02d", $hour);
239     $mday = sprintf("%02d", $mday);
240     $mon = sprintf("%02d", $mon);
241     Redefine("__TIME__", $hour.":".$min.":".$sec);
242     Redefine("__DATE__", $MonthChars[$mon]." ".$mday." ".$year);
243     $mon = sprintf("%02d", ++$mon);
244     Redefine("__ISO_DATE__", $year."-".$mon."-".$mday);
245     }
246    
247     # hash table for arguments to macros which need them
248     my %DefinesArgs = ();
249    
250     # hash table for functions which macros should call (if any)
251     my %DefinesFuncs = ();
252    
253     # eat-trailing-whitespace flag for each macro
254     my %EatTrail = ();
255    
256     # list of include paths
257     my @IncludePaths;
258    
259     # help string
260     my $usage = "filepp: generic file preprocessor, version ".$VERSION."
261     usage: filepp [options] inputfile(s)
262     options:
263     -b\t\tsuppress blank lines from include files
264     -c\t\tread input from STDIN instead of file
265     -Dmacro[=defn]\tdefine macros (same as #define)
266     -d\t\tprint debugging information
267     -dd\t\tprint verbose debugging information
268     -dl\t\tprint some (light) debugging information
269     -dpre char\tprefix all debugging information with char
270     -dpost char\tpostfix all debugging information with char, defaults to newline
271     -ds\t\tsend debugging info to stdout rather than stderr
272     -e\t\tdefine all environment variables as macros
273     -ec char\tset environment variable prefix char to \"char\" (default \$)
274     -ecn\t\tset environment variable prefix char to nothing (default \$)
275     -h\t\tprint this help message
276     -Idir\t\tdirectory to search for include files
277     -imacros file\tread in macros from file, but discard rest of file
278     -k\t\tturn off parsing of all keywords, just macro expansion is done
279     -kc char\tset keyword prefix char to \"char\" (defaults to #)
280     -lc char\tset line continuation character to \"char\" (defaults to \\)
281     -lec char\tset optional keyword line end char to \"char\"
282     -lr char\tset line continuation replacement character to \"char\"
283     -lrn\t\tset line continuation replacement character to newline
284     -m module\tload module
285     -mp char\tprefix all macros with \"char\" (defaults to no prefix)
286     -mpnk\t\tdo not use macro prefix char in keywords
287     -Mdir\t\tdirectory to search for filepp modules
288     -o output\tname of output file (defaults to stdout)
289     -ov\t\toverwrite mode - output file will overwrite input file
290     -ovc IN=OUT\toutput file(s) will have be input file(s) with IN conveted to OUT
291     -pb\t\tpreseve blank lines in output that would normally be removed
292     -s\t\trun in safe mode (turns off pragma keyword)
293     -re\t\ttreat keyword and macro prefixes and line cont chars as reg exps
294     -u\t\tundefine all predefined macros
295     -v\t\tprint version and exit
296     -w\t\tturn on word boundaries when replacing macros
297     all other arguments are assumed to be input files
298     ";
299    
300     # graph for dependency files
301     my $dependencyGraph;
302    
303     # visited table
304     my %visitedTable = ();
305    
306     #object directory
307     my $objDir ="";
308    
309     # f90moduleList
310     my %parsedModList = ();
311    
312     #
313     my %f90ModList = ();
314    
315     #
316     my $objExt = '.o';
317     ##############################################################################
318     # SetDebug - controls debugging level
319     ##############################################################################
320     sub SetDebug
321     {
322     $debug = shift;
323     Debug("Debugging level set to $debug", 1);
324     }
325    
326    
327     ##############################################################################
328     # Debugging info
329     ##############################################################################
330     sub Debug
331     {
332     # print nothing if not debugging
333     if($debug == 0) { return; }
334     my $msg = shift;
335     my $level = 1;
336     # check if level has been provided
337     if($#_ > -1) { $level = shift; }
338     if($level <= $debug) {
339     # if currently parsing a file show filename and line number
340     if($file ne "" && $line > 0) {
341     $msg = $file.":".$line.": ".$msg;
342     }
343     # else show program name
344     else { $msg = "filepp: ".$msg; }
345     if($debugstdout) {
346     print(STDOUT $debugprefix.$msg.$debugpostfix);
347     }
348     else {
349     print(STDERR $debugprefix.$msg.$debugpostfix);
350     }
351     }
352     }
353    
354    
355     ##############################################################################
356     # Standard error handler.
357     # #error msg - print error message "msg" and exit
358     ##############################################################################
359     sub Error
360     {
361     my $msg = shift;
362     # close and delete output file if created
363     close(OUTPUT);
364     if($outputfile ne "-") { # output is not stdout
365     my $inputfile;
366     my $found = 0;
367     # do paranoid check to make sure we are not deleting an input file
368     foreach $inputfile (@Inputfiles) {
369     if($outputfile eq $inputfile) { $found = 1; last; }
370     }
371     # delete output file
372     if($found == 0) { unlink($outputfile); }
373     }
374     # print error message
375     $debug = 1;
376     Debug($msg, 0);
377     exit(1);
378     }
379    
380    
381     ##############################################################################
382     # SafeMode - turns safe mode on
383     ##############################################################################
384     sub SafeMode
385     {
386     $safe_mode = 1;
387     Debug("Filepp safe mode enabled", 2);
388     }
389    
390    
391     ##############################################################################
392     # CleanStart($sline) - strip leading whitespace from start of $sline.
393     ##############################################################################
394     sub CleanStart
395     {
396     my $sline = shift;
397     for($sline) {
398     # '^' = start of line, '\s+' means all whitespace, replace with nothing
399     s/^\s+//;
400     }
401     return $sline;
402     }
403    
404    
405     ##############################################################################
406     # Strip($sline, $char, $level) - strip $char's from start and end of $sline
407     # removes up to $level $char's from start and end of line, it is not an
408     # error if $level chars do not exist at the start or end of line
409     ##############################################################################
410     sub Strip
411     {
412     my $sline = shift;
413     my $char = shift;
414     my $level = shift;
415     # strip leading chars from line
416     $sline =~ s/\A([$char]{0,$level})//g;
417     # strip trailing chars from line
418     $sline =~ s/([$char]{0,$level})\Z//g;
419     return $sline;
420     }
421    
422    
423     ##############################################################################
424     # SetMacroPrefix $string - prefixs all macros with $string
425     ##############################################################################
426     sub SetMacroPrefix
427     {
428     $macroprefix = shift;
429     # make sure prefix will not be treated as a Perl regular expression
430     if(!$charperlre) { $macroprefix = "\Q$macroprefix\E"; }
431     Debug("Setting macro prefix to <".$macroprefix.">", 2);
432     }
433    
434    
435     ##############################################################################
436     # SetKeywordchar $string - sets the first char(s) of each keyword to
437     # something other than "#"
438     ##############################################################################
439     sub SetKeywordchar
440     {
441     $keywordchar = shift;
442     # make sure char will not be treated as a Perl regular expression
443     if(!$charperlre) { $keywordchar = "\Q$keywordchar\E"; }
444     Debug("Setting keyword prefix character to <".$keywordchar.">", 2);
445     }
446    
447     ##############################################################################
448     # GetKeywordchar - returns the current keywordchar
449     ##############################################################################
450     sub GetKeywordchar
451     {
452     return $keywordchar;
453     }
454    
455    
456     ##############################################################################
457     # SetContchar $string - sets the line continuation char to something other
458     # than "\"
459     ##############################################################################
460     sub SetContchar
461     {
462     $contchar = shift;
463     # make sure char will not be treated as a Perl regular expression
464     if(!$charperlre) { $contchar = "\Q$contchar\E"; }
465     Debug("Setting line continuation character to <".$contchar.">", 2);
466     }
467    
468    
469     ##############################################################################
470     # SetContrepchar $string - sets the replace of the line continuation char to
471     # something other than ""
472     ##############################################################################
473     sub SetContrepchar
474     {
475     $contrepchar = shift;
476     Debug("Setting line continuation replacement character to <".$contrepchar.">", 2);
477     }
478    
479    
480     ##############################################################################
481     # SetOptLineEndchar $string - sets the optional line end char to something
482     # other than ""
483     ##############################################################################
484     sub SetOptLineEndchar
485     {
486     $optlineendchar = shift;
487     # make sure char will not be treated as a Perl regular expression
488     if(!$charperlre) { $optlineendchar = "\Q$optlineendchar\E"; }
489     Debug("Setting optional line end character to <".$optlineendchar.">", 2);
490     }
491    
492    
493     ##############################################################################
494     # SetEnvchar $string - sets the first char(s) of each defined environment
495     # variable to $string - NOTE: change only takes effect when DefineEnv run
496     ##############################################################################
497     sub SetEnvchar
498     {
499     $envchar = shift;
500     Debug("Setting environment variable prefix character to <".$envchar.">",2);
501     }
502    
503     ##############################################################################
504     # RunProcessors $string, $calledfrom
505     # run the current processing chain on the string
506     # $string is the string to be processed and should be returned by the processor
507     # $calledfrom says where the processors are called from, the choice is:
508     #
509     # 0 or default: Part line (from within a keyword) - if called recursively
510     # runs all processors AFTER current processor, then continues with processing.
511     # This is used when a keyword want to run all remaining processors on a line
512     # before doing its keyword task.
513     #
514     # 1: Full line (from Parse function) - if called recursively runs all
515     # processors BEFORE current processor, then continues with processing
516     #
517     # 2: Part line (from within a keyword) - if called recursively runs all
518     # processors BEFORE current processor, then continues with processing.
519     # This is used when keywords are using text taken from somewhere other than
520     # the current line, this text needs to go through the same processors as
521     # the current line has been through so it can "catch up" (eg: regexp.pm).
522     #
523     ##############################################################################
524     my @Running;
525     my @Currentproc;
526     sub RunProcessors
527     {
528     my $string = shift;
529     my $calledfrom = 0;
530     if($#_ > -1) { $calledfrom = shift; }
531     my $i;
532    
533     # turn off macoprefix if in a keyword
534     my $tmpprefix = "";
535     if($calledfrom != 1 && $macroprefixinkeywords == 0) {
536     $tmpprefix = $macroprefix;
537     $macroprefix = "";
538     }
539    
540     # These tests are done to make RunProcessors recursion safe.
541     # If RunProcessors is called from with a function that was itself called
542     # by RunProcessors, then the second calling of RunProcessors will only
543     # execute the processors before the currently running processor in the
544     # chain.
545     my $recursing = 0;
546     my $firstproc = 0;
547     my $lastproc = $#Processors;
548     if($Running[$include_level]) {
549     if($calledfrom == 0) {
550     $firstproc = $Currentproc[$include_level] + 1;
551     }
552     else {
553     $lastproc = $Currentproc[$include_level] - 1;
554     }
555     $recursing = 1;
556     }
557     else { $Running[$include_level] = 1; }
558    
559     for($i = $firstproc; $i <= $lastproc; $i++) {
560     if(!$recursing) { $Currentproc[$include_level] = $i; }
561     # called from anywhere (default)
562     if($ProcessorTypes{$Processors[$i]} == 0 ||
563     # called from keyword (part lines only - within keywords)
564     (($calledfrom == 0 || $calledfrom == 2) &&
565     $ProcessorTypes{$Processors[$i]} == 2) ||
566     # called from Parse function (whole lines only)
567     ($calledfrom == 1 && $ProcessorTypes{$Processors[$i]} == 1)) {
568     # run processor
569     # Debug("Running processor $Processors[$i] on \"$string\"", 2);
570     $string = $Processors[$i]->($string);
571     }
572     # check that no processors have been deleted (bigdef.pm)
573     if($lastproc > $#Processors) { $lastproc = $#Processors; }
574     }
575    
576     if(!$recursing) { $Running[$include_level] = 0; }
577    
578     # return macro prefix to its former glory
579     if($calledfrom != 1 && $macroprefixinkeywords == 0) {
580     $macroprefix = $tmpprefix;
581     }
582    
583     return $string;
584     }
585    
586     ##############################################################################
587     # PrintProcessors
588     # print the current processing chain
589     ##############################################################################
590     sub PrintProcessors
591     {
592     my $processor;
593     Debug("Current processing chain:", 3);
594     my $i = 0;
595     foreach $processor (@Processors) {
596     Debug($processor." type ".$ProcessorTypes{$Processors[$i]}, 3);
597     $i++;
598     }
599     }
600    
601     ##############################################################################
602     # AddProcessor(function[, first[, type]])
603     # add a line processor to processing chain, defaults to end of chain
604     # if "first" is set to one adds processor to start of chain
605     ##############################################################################
606     sub AddProcessor
607     {
608     my $function = shift;
609     my $first = 0;
610     my $type = 0;
611     # check if flag to add processor to start of chain is set
612     if($#_ > -1) { $first = shift; }
613     # check if processor has a type
614     if($#_ > -1) { $type = shift; }
615     # adding processor to start of chasin
616     if($first) {
617     @Processors = reverse(@Processors);
618     }
619     push(@Processors, $function);
620     if($first) {
621     @Processors = reverse(@Processors);
622     }
623     $ProcessorTypes{$function} = $type;
624     Debug("Added processor ".$function." of type ".$type, 2);
625     if($debug > 1) { PrintProcessors(); }
626     }
627    
628     ##############################################################################
629     # AddProcessorAfter(function, processor[, type])
630     # add a line processor to processing chain immediately after an existing
631     # processor, if existing processor not found, new processor is added to
632     # end of chain
633     ##############################################################################
634     sub AddProcessorAfter
635     {
636     my $function = shift;
637     my $existing = shift;
638     my $type = 0;
639     # check if processor has a type
640     if($#_ > -1) { $type = shift; }
641     my $i = 0;
642     my $found = 0;
643     my @CurrentProcessors = @Processors;
644     my $processor;
645     # reset processing chain
646     @Processors = ();
647     foreach $processor (@CurrentProcessors) {
648     push(@Processors, $processor);
649     if(!$found) {
650     # check done as regular expression for greater flexibility
651     if($processor =~ /$existing/) {
652     push(@Processors, $function);
653     $found = 1;
654     }
655     }
656     }
657     if(!$found) {
658     Warning("Did not find processor $existing in chain, processor $processor added to end of list");
659     AddProcessor($function, 0, $type);
660     return;
661     }
662     $ProcessorTypes{$function} = $type;
663     Debug("Added processor ".$function." of type ".$type, 2);
664     if($debug > 1) { PrintProcessors(); }
665     }
666    
667     ##############################################################################
668     # AddProcessorBefore(function, processor[, type])
669     # add a line processor to processing chain immediately after an existing
670     # processor, if existing processor not found, new processor is added to
671     # end of chain
672     ##############################################################################
673     sub AddProcessorBefore
674     {
675     my $function = shift;
676     my $existing = shift;
677     my $type = 0;
678     # check if processor has a type
679     if($#_ > -1) { $type = shift; }
680     my $i = 0;
681     my $found = 0;
682     my @CurrentProcessors = @Processors;
683     my $processor;
684     # reset processing chain
685     @Processors = ();
686     foreach $processor (@CurrentProcessors) {
687     if(!$found) {
688     # check done as regular expression for greater flexibility
689     if($processor =~ /$existing/) {
690     push(@Processors,$function);
691     $found = 1;
692     }
693     }
694     push(@Processors, $processor);
695     }
696     if(!$found) {
697     Warning("Did not find processor $existing in chain, processor $processor added to start of list");
698     AddProcessor($function, 1, $type);
699     return;
700     }
701     $ProcessorTypes{$function} = $type;
702     Debug("Added processor ".$function." of type ".$type, 2);
703     if($debug > 1) { PrintProcessors(); }
704     }
705    
706     ##############################################################################
707     # RemoveProcessor(function)
708     # remove a processor name "function" from list
709     ##############################################################################
710     sub RemoveProcessor
711     {
712     my $function = shift;
713     my $i = 0;
714     # find function
715     while($i <= $#Processors && $Processors[$i] ne $function) { $i++; }
716     # check function found
717     if($i > $#Processors) {
718     Warning("Attempt to remove function ".$function.
719     " which does not exist");
720     return;
721     }
722     # remove function
723     for(; $i<$#Processors; $i++) {
724     $Processors[$i] = $Processors[$i+1];
725     }
726     pop(@Processors);
727     delete($ProcessorTypes{$function});
728     Debug("Removed processor ".$function, 2);
729     PrintProcessors();
730     }
731    
732    
733     ##############################################################################
734     # Add a function to run each time a base file is opened
735     ##############################################################################
736     sub AddOpenInputFunc
737     {
738     my $func = shift;
739     push(@OpenInputFuncs, $func);
740     }
741    
742     ##############################################################################
743     # Add a function to run each time a base file is closed
744     ##############################################################################
745     sub AddCloseInputFunc
746     {
747     my $func = shift;
748     push(@CloseInputFuncs, $func);
749     }
750    
751     ##############################################################################
752     # Add a function to run each time a base file is opened
753     ##############################################################################
754     sub AddOpenOutputFunc
755     {
756     my $func = shift;
757     push(@OpenOutputFuncs, $func);
758     }
759    
760     ##############################################################################
761     # Add a function to run each time a base file is closed
762     ##############################################################################
763     sub AddCloseOutputFunc
764     {
765     my $func = shift;
766     push(@CloseOutputFuncs, $func);
767     }
768    
769    
770     ##############################################################################
771     # AddKeyword(keyword, function)
772     # Define a new keyword, when keyword (preceded by keyword char) is found,
773     # function is run on the remainder of the line.
774     ##############################################################################
775     sub AddKeyword
776     {
777     my $keyword = shift;
778     my $function = shift;
779     $Keywords{$keyword} = $function;
780     Debug("Added keyword ".$keyword." which runs ".$function, 2);
781     }
782    
783    
784     ##############################################################################
785     # RemoveKeyword(keyword)
786     # Keyword is deleted from list, all occurrences of keyword found in
787     # document are ignored.
788     ##############################################################################
789     sub RemoveKeyword
790     {
791     my $keyword = shift;
792     delete $Keywords{$keyword};
793     # sort keywords index into reverse order, this ensures #if[n]def comes
794     # before #if when comparing input with keywords
795     Debug("Removed keyword ".$keyword, 2);
796     }
797    
798    
799     ##############################################################################
800     # RemoveAllKeywords - removes all current keywords.
801     ##############################################################################
802     sub RemoveAllKeywords
803     {
804     %Keywords = ();
805     Debug("Removed all current keywords", 2);
806     }
807    
808    
809     ##############################################################################
810     # AddIfword - adds a keyword to ifword hash
811     ##############################################################################
812     sub AddIfword
813     {
814     my $ifword = shift;
815     $Ifwords{$ifword} = '';
816     Debug("Added Ifword: ".$ifword, 2);
817     }
818    
819     ##############################################################################
820     # RemoveIfword - removes a keyword from ifword hash
821     ##############################################################################
822     sub RemoveIfword
823     {
824     my $ifword = shift;
825     delete $Ifwords{$ifword};
826     Debug("Removed Ifword: ".$ifword, 2);
827     }
828    
829     ##############################################################################
830     # AddElseword - adds a keyword to elseword hash
831     ##############################################################################
832     sub AddElseword
833     {
834     my $elseword = shift;
835     $Elsewords{$elseword} = '';
836     Debug("Added Elseword: ".$elseword, 2);
837     }
838    
839     ##############################################################################
840     # RemoveElseword - removes a keyword from elseword hash
841     ##############################################################################
842     sub RemoveElseword
843     {
844     my $elseword = shift;
845     delete $Elsewords{$elseword};
846     Debug("Removed Elseword: ".$elseword, 2);
847     }
848    
849     ##############################################################################
850     # AddEndifword - adds a keyword to endifword hash
851     ##############################################################################
852     sub AddEndifword
853     {
854     my $endifword = shift;
855     $Endifwords{$endifword} = '';
856     Debug("Added Endifword: ".$endifword, 2);
857     }
858    
859     ##############################################################################
860     # RemoveEndifword - removes a keyword from endifword hash
861     ##############################################################################
862     sub RemoveEndifword
863     {
864     my $endifword = shift;
865     delete $Endifwords{$endifword};
866     Debug("Removed Endifword: ".$endifword, 2);
867     }
868    
869    
870     ##############################################################################
871     # AddIncludePath - adds another include path to the list
872     ##############################################################################
873     sub AddIncludePath
874     {
875     my $path = shift;
876     push(@IncludePaths, $path);
877     Debug("Added include path: \"".$path."\"", 2);
878     }
879    
880    
881     ##############################################################################
882     # AddModulePath - adds another module search path to the list
883     ##############################################################################
884     sub AddModulePath
885     {
886     my $path = shift;
887     push(@INC, $path);
888     Debug("Added module path: \"".$path."\"", 2);
889     }
890    
891    
892     # set if file being written to has same name as input file
893     my $same_file = "";
894    
895     ##############################################################################
896     # OpenOutputFile - opens the output file
897     ##############################################################################
898     sub OpenOutputFile
899     {
900     $outputfile = shift;
901     Debug("Output file: ".$outputfile, 1);
902    
903     # check for outputfile name, if not specified use STDOUT
904     if($outputfile eq "") { $outputfile = "-"; }
905    
906     # output is not stdout and file with that name already exists
907     if($outputfile ne "-" && FileExists($outputfile) ) {
908     $same_file = $outputfile;
909     # paranoid: check file is writable and normal file
910     if(-w $outputfile && -f $outputfile) {
911     $outputfile = $outputfile.".fpp".$$;
912     my $i=0; # paranoid: check temp file does not exist
913     while(FileExists($outputfile)) {
914     $outputfile = $outputfile.$i;
915     $i++;
916     if($i >= 10) { Error("Cound not get temp filename"); }
917     }
918     }
919     else {
920     Error("Cannot read or write to ".$outputfile);
921     }
922     }
923     if(!open(OUTPUT, ">".$outputfile)) {
924     Error("Cannot open output file: ".$outputfile);
925     }
926     # run any open functions
927     my $func;
928     foreach $func (@OpenOutputFuncs) { $func->(); }
929     }
930    
931    
932     ##############################################################################
933     # CloseOutputFile - close the output file
934     ##############################################################################
935     sub CloseOutputFile
936     {
937     # run any close functions
938     my $func;
939     foreach $func (@CloseOutputFuncs) { $func->(); }
940     close(OUTPUT);
941    
942     # if input and output have same name, rename output to input now
943     if($same_file ne "") {
944     if(rename($same_file, $same_file."~") == -1) {
945     Error("Could not rename ".$same_file." ".$same_file."~");
946     }
947     if(rename($outputfile, $same_file) == -1) {
948     Error("Could not rename ".$outputfile." ".$same_file);
949     }
950     }
951     # reset same_file
952     $same_file = "";
953     }
954    
955    
956     ##############################################################################
957     # ChangeOutputFile - change the output file
958     ##############################################################################
959     sub ChangeOutputFile
960     {
961     CloseOutputFile();
962     $outputfile = shift;
963     OpenOutputFile($outputfile);
964     }
965    
966    
967     ##############################################################################
968     # AddInputFile - adds another input file to the list
969     ##############################################################################
970     sub AddInputFile
971     {
972     my $file = shift;
973     push(@Inputfiles, $file);
974     Debug("Added input file: \"".$file."\"", 2);
975     }
976    
977    
978     ##############################################################################
979     # UseModule(module)
980     # Module "module.pm" is used, "module.pm" can be any perl module and can use
981     # or replace any of the functions in this package
982     ##############################################################################
983     sub UseModule
984     {
985     my $module = shift;
986     Debug("Loading module ".$module, 1);
987     require $module;
988     if($@) { Error($@); }
989     }
990    
991    
992     ##############################################################################
993     # find end of next word in $sline, assumes leading whitespace removed
994     ##############################################################################
995     sub GetNextWordEnd
996     {
997     my $sline = shift;
998     # check for whitespace in this string
999     if($sline =~ /\s/) {
1000     # return length of everything up to first whitespace
1001     return length($`);
1002     }
1003     # whitespace not found, return length of the whole string
1004     return length($sline);
1005     }
1006    
1007    
1008     ##############################################################################
1009     # Print current table of defines - used for debugging
1010     ##############################################################################
1011     sub PrintDefines
1012     {
1013     my $define;
1014     Debug("Current ".$keywordchar."define's:", 3);
1015     foreach $define (keys(%Defines)) {
1016     Debug(" macro:\"".$define."\", definition:\"".$Defines{$define}."\"",3);
1017     }
1018     }
1019    
1020    
1021     ##############################################################################
1022     # DefineEnv - define's all environment variables to macros, each prefixed
1023     # by $envchar
1024     ##############################################################################
1025     sub DefineEnv
1026     {
1027     my $macro;
1028     Debug("Defining environment variables as macros", 2);
1029     foreach $macro (keys(%ENV)) {
1030     Define($envchar.$macro." ".$ENV{$macro});
1031     }
1032     }
1033    
1034    
1035     ##############################################################################
1036     # Find out if arguments have been used with macro
1037     ##############################################################################
1038     sub DefineArgsUsed
1039     {
1040     my $string = shift;
1041     # check '(' is first non-whitespace char after macro
1042     if($string =~ /^\s*\(/) {
1043     return 1;
1044     }
1045     return 0;
1046     }
1047    
1048    
1049     ##############################################################################
1050     # ParseArgs($string) - find the arguments in a string of form
1051     # (arg1, arg2, arg3...) trailing chars
1052     # or
1053     # arg1, arg2, arg3...
1054     ##############################################################################
1055     sub ParseArgs
1056     {
1057     my $string = shift;
1058     $string = CleanStart($string);
1059     my @Chars;
1060     my $char;
1061     # split string into chars (can't use split coz it deletes \n at end)
1062     for($char=0; $char<length($string); $char++) {
1063     push(@Chars, substr($string, $char, 1));
1064     }
1065     my @Args; # list of Args
1066     my $arg = "";
1067     my @Endchar;
1068     # special characters - no processing is done between character pairs
1069     my %SpecialChars = ('(' => ')', '"' => '"', '\'' => '\'');
1070     my $s = -1; # start of chars
1071     my $backslash = 0;
1072     # number of special char pairs to allow
1073     my $pairs = 1;
1074    
1075     # deal with first '(' if there (ie func(args) rather than func args)
1076     if($#Chars >= 0 && $Chars[0] eq '(') {
1077     push(@Endchar, ')');
1078     $Chars[0] = '';
1079     $s++;
1080     $pairs++; # ignore this pair of special char pairs
1081     }
1082    
1083     # replace args with their values
1084     foreach $char (@Chars) {
1085     # deal with end of special chars, ),",' etc.
1086     if($#Endchar > -1 && $char eq $Endchar[$#Endchar]) {
1087     # if char before this was a backslash, ignore this char
1088     if($backslash) {
1089     chop($arg); # delete backslash from string
1090     }
1091     else {
1092     # pop end char of list and reduce pairs if its a bracket
1093     if(pop(@Endchar) eq ')') { $pairs--; }
1094     }
1095     }
1096     # deal with start of special chars
1097     elsif(exists($SpecialChars{$char})) {
1098     # if char before this was a backslash, ignore this char
1099     if($backslash) {
1100     chop($arg); # delete backslash from string
1101     }
1102     # only start new pair if not already in special char pair
1103     # (not including main args brackets of course)
1104     elsif($#Endchar < $pairs-1) {
1105     push(@Endchar, $SpecialChars{$char});
1106     # need to treat brackets differently for macros within
1107     # macros "this(that(tother)))", otherwise lose track of ()'s
1108     if($char eq '(') { $pairs++; }
1109     }
1110     }
1111     # deal with ',', add arg to list and start search for next one
1112     elsif($#Endchar == $s && $char eq ',') {
1113     # if char before this was a backslash, ignore this char
1114     if($backslash) {
1115     chop($arg); # delete backslash from string
1116     }
1117     else {
1118     push(@Args, CleanStart($arg));
1119     $char = '';
1120     $arg = "";
1121     next;
1122     }
1123     }
1124     # deal \\ with an escaping \ ie. \" or \, or \\
1125     if($char eq '\\') {
1126     if($backslash) { # found \\
1127     $backslash = 0; # second backslash ignored
1128     chop($arg); # delete backslash from string
1129     }
1130     else{$backslash = 1;}
1131     }
1132     elsif($backslash) { $backslash = 0; }
1133     # check for end of args string
1134     if($#Endchar < $s) {
1135     push(@Args, CleanStart($arg));
1136     $char = '';
1137     # put remainder of string back together
1138     $arg = join('', @Chars);
1139     last;
1140     }
1141     $arg = $arg.$char; # add char to current arg
1142     $char = ''; # set char to null
1143     }
1144    
1145     # deal with last arg or string following args if it exists
1146     push(@Args, $arg);
1147    
1148     return @Args;
1149     }
1150    
1151    
1152     ##############################################################################
1153     # Find the arguments in a macro and replace them
1154     ##############################################################################
1155     sub FindDefineArgs
1156     {
1157     my $substring = shift;
1158     my $macro = shift;
1159    
1160     # get definition list for this macro
1161     my @Argnames = split(/\,/, $DefinesArgs{$macro});
1162    
1163     # check to see if macro can have any number of arguments (last arg ...)
1164     my $anyargs = ($#Argnames >= 0 && $Argnames[$#Argnames] =~ /\.\.\.\Z/o);
1165    
1166     # get arguments passed to this macro
1167     my @Argvals = ParseArgs($substring);
1168     # everything following macro args should be returned as tail
1169     my $tail = pop(@Argvals);
1170    
1171     # check the right number of args have been passed, should be all args
1172     # present plus string at end of args (assuming macro cannot have any number
1173     # of arguments)
1174     if(!$anyargs && $#Argvals != $#Argnames) {
1175     # show warning if wrong args (unless macro should have zero args and
1176     # 1 arg provided which is blank space
1177     if(!($#Argnames == -1 && $#Argvals == 0 && $Argvals[0] =~ /\A\s*\Z/)) {
1178     Warning("Macro \'".$macro."\' used with ".$#Argvals.
1179     " args, expected ".($#Argnames+1));
1180     }
1181     # delete all excess args
1182     while($#Argvals > $#Argnames) { pop(@Argvals); }
1183     }
1184     # make all missing args blanks
1185     while($#Argvals < $#Argnames) { push(@Argvals, ""); }
1186    
1187     return (@Argvals, $tail);
1188     }
1189    
1190    
1191     ##############################################################################
1192     # FunctionMacro: used with functions to inform a module which macro
1193     # was being replaced when the function was called - used in bigfunc.pm
1194     ##############################################################################
1195     my $functionmacro = "";
1196     sub FunctionMacro
1197     {
1198     return $functionmacro;
1199     }
1200    
1201    
1202     ##############################################################################
1203     # Replace all defined macro's arguments with their values
1204     # Inputs:
1205     # $macro = the macro to be replaces
1206     # $string = the string following the occurrence of macro
1207     ##############################################################################
1208     sub ReplaceDefineArgs
1209     {
1210     my ($string, $tail, %Used) = @_;
1211     # check if args used, if not do nothing
1212     if(DefineArgsUsed($tail)) {
1213     my $macro = $string;
1214     # get arguments following macro
1215     my @Argvals = FindDefineArgs($tail, $macro);
1216     $tail = pop(@Argvals); # tail returned as last element
1217    
1218     my @Argnames = split(/\,/, $DefinesArgs{$macro});
1219     my ($i, $j);
1220    
1221     # replace previous macro with defn + args
1222     $string = $Defines{$macro};
1223    
1224     # check if macro should call a function
1225     if(exists($DefinesFuncs{$macro})) {
1226     # replace all macros in argument list
1227     for($i=0; $i<=$#Argvals; $i++) {
1228     $Argvals[$i] = ReplaceDefines($Argvals[$i]);
1229     }
1230     if($debug > 1) {
1231     my $argstring = "";
1232     if($#Argvals >= 0) { $argstring = join(", ", @Argvals); }
1233     Debug("Running function $DefinesFuncs{$macro} with args (".
1234     $argstring.")", 2);
1235     }
1236     # set name of macro which is being parse (needed in bigfunc.pm)
1237     $functionmacro = $macro;
1238     $string = $DefinesFuncs{$macro}->(@Argvals);
1239     # don't need do anything else, return now
1240     return $string, $tail;
1241     }
1242    
1243     # check if last arg ends in ... (allows any number of args in macro)
1244     if($#Argnames >= 0 && $Argnames[$#Argnames] =~ s/\.\.\.\Z//o) {
1245     # concatanate all extra args into final arg
1246     while($#Argvals > $#Argnames) {
1247     my $arg1 = pop(@Argvals);
1248     my $arg2 = pop(@Argvals);
1249     push(@Argvals, $arg2.", ".$arg1);
1250     }
1251     # check for ## at start of macro name in args list
1252     if($string =~ /\#\#$Argnames[$#Argnames]/) {
1253     # if last argument is empty remove preciding ","
1254     if($#Argvals == $#Argnames && $Argvals[$#Argnames] eq "") {
1255     $string =~ s/\,\s*\#\#$Argnames[$#Argnames]//g;
1256     }
1257     else {
1258     $string =~
1259     s/\#\#$Argnames[$#Argnames]/$Argnames[$#Argnames]/g;
1260     }
1261     }
1262     }
1263    
1264     # to get args passed to macro to same processed level as rest of
1265     # macro, they need to be checked for occurrences of all used macros,
1266     # this is a nasty hack to temporarily change defines list to %Used
1267     {
1268     my %RealDefines = %Defines;
1269     my $realdefmin = $defmin;
1270     my $realdefmax = $defmax;
1271     my %RealDefineLookup = %DefineLookup;
1272     %Defines = %Used;
1273     GenerateDefinesKeys();
1274    
1275     for($i=0; $i<=$#Argvals; $i++) {
1276     $Argvals[$i] = ReplaceDefines($Argvals[$i]);
1277     }
1278    
1279     # return defines to normal
1280     %Defines = %RealDefines;
1281     $defmin = $realdefmin;
1282     $defmax = $realdefmax;
1283     %DefineLookup = %RealDefineLookup;
1284     }
1285    
1286     # The next step replaces argnames with argvals. Once a bit of string
1287     # has been replaced it is removed from further processing to avoid
1288     # unwanted recursive macro replacement.
1289     my @InString = ( $string ); # string to be replaced
1290     my @InDone = ( 0 ); # flag to say if string section replaced
1291     my @OutString; # output of string sections after each
1292     # macro has been replaced
1293     my @OutDone; # output flags
1294     my $k = 0;
1295     for($i=0; $i<=$#Argnames; $i++) {
1296     for($j=0; $j<=$#InString; $j++) {
1297     if($InDone[$j] == 0) {
1298     # replace macros and split up string so replaced part
1299     # is flagged as done and rest is left for further
1300     # processing
1301     while($InString[$j] =~ /$bound$Argnames[$i]$bound/) {
1302     $OutString[$k] = $`; $OutDone[$k] = 0;
1303     $k++;
1304     $OutString[$k] = $Argvals[$i]; $OutDone[$k] = 1;
1305     $k++;
1306     $InString[$j] = $'; # one more quote for emacs '
1307     }
1308     }
1309     $OutString[$k] = $InString[$j]; $OutDone[$k] = $InDone[$j];
1310     $k++;
1311     }
1312     @InString = @OutString; @InDone = @OutDone;
1313     $k = 0;
1314     }
1315     # rebuild string
1316     $string = join('', @InString);
1317    
1318     Debug("Replaced \"".$macro."\" for \"".$string."\" [".$recurse_level."]", 2);
1319     }
1320     else {
1321     Debug("Macro \"".$string."\" found without args, ignored", 2);
1322     }
1323     return ($string, $tail);
1324     }
1325    
1326    
1327     ##############################################################################
1328     # When replacing macros with args, the macro and everything following the
1329     # macro (the tail) are passed to ReplaceDefineArgs. The function extracts
1330     # the args from the tail and then returns the replaced macro and the new
1331     # tail. This function extracts the remaining part of the real tail from
1332     # the current input string.
1333     ##############################################################################
1334     sub ReclaimTail
1335     {
1336     my ($input, $tail) = @_;
1337     # split strings into chars and compare each one until difference found
1338     my @Input = split(//, $input);
1339     my @Tail = split(//, $tail);
1340     $tail = $input = "";
1341     while($#Input >= 0 && $#Tail >= 0 && $Input[$#Input] eq $Tail[$#Tail]) {
1342     $tail = pop(@Tail).$tail;
1343     pop(@Input);
1344     }
1345     while($#Input >=0) { $input = pop(@Input).$input; }
1346     return ($input, $tail);
1347     }
1348    
1349    
1350     ##############################################################################
1351     # Replace all defined macro's in a line with their value. Recursively run
1352     # through macros as many times as needed (to find macros within macros).
1353     # Inputs:
1354     # $input = string to process
1355     # $tail = rest of line following $string (if any), this will only be used
1356     # if string contains a macro with args, the args will probably be
1357     # at the start of the tail
1358     # %Used = all macros found in $string so far, these will not be checked
1359     # again to avoid possible recursion
1360     # Initially just $input is passed in, other args are added for recursive calls
1361     ##############################################################################
1362     sub ReplaceDefines
1363     {
1364     my ($input, $tail, %Used) = @_;
1365     # check for recursive macro madness (set to same level as Perl warning)
1366     if(++$recurse_level > 97) {
1367     $recurse_level--;
1368     Warning("Recursive macro detected in \"".$input."\"");
1369     if($tail) { return ($input, $tail); }
1370     return $input;
1371     }
1372    
1373     my $out = ""; # initialise output to empty string
1374     OUTER : while($input =~ /\S/o) {
1375     my ($macro, $string);
1376     my @Words;
1377    
1378    
1379     ######################################################################
1380     # if macros start with prefix, skip to next prefix
1381     ######################################################################
1382     if($macroprefix ne "") {
1383     my $found = 0;
1384     # find next potential macro in line if any
1385     while(!$found && $input =~ /$macroprefix\S/) {
1386     # everything before prefix
1387     $out = $out.$`;
1388     # reclaim first char in macro
1389     my $match = $&;
1390     # everything after prefix
1391     $input = chop($match).$'; # one more quote for emacs '
1392     # check if first chars are in macro
1393     if(exists($DefineLookup{substr($input, 0, $defmin)})) {
1394     $found = 1;
1395     }
1396     # put prefix back onto output and carry on searching
1397     else { $out = $out.$match; }
1398     }
1399     # no more macros
1400     if(!$found) { $out = $out.$input; $input = ""; last OUTER; }
1401     }
1402    
1403    
1404     ######################################################################
1405     # replacing macros which are "words" only - quick and easy
1406     ######################################################################
1407     if($bound eq '\b') {
1408     @Words = split(/(\w+)/, $input, 2);
1409     $out = $out.$Words[0];
1410     if($#Words == 2) { $macro = $Words[1]; $input = $Words[2]; }
1411     else { $input = ""; last OUTER; }
1412     }
1413    
1414     ######################################################################
1415     # replacing all types of macro - slow and horrid
1416     ######################################################################
1417     else {
1418     # forward string to next non-whitespace char that starts a macro
1419     while(!exists($DefineLookup{substr($input, 0, $defmin)})) {
1420     if($input =~ /^\s/ ) { # remove preceding whitespace
1421     @Words = split(/^(\s+)/, $input, 2);
1422     $out = $out.$Words[1];
1423     $input = $Words[2];
1424     }
1425     else { # skip to next char
1426     $out = $out.substr($input, 0, 1);
1427     $input = substr($input, 1);
1428     }
1429     if($input eq "") { last OUTER; }
1430     }
1431     # remove the longest possible potential macro (containing no
1432     # whitespace) from the start of input
1433     @Words = split(/(\s+)/, $input, 2);
1434     $macro = $Words[0];
1435     if($#Words == 2) {$input = $Words[1].$Words[2]; }
1436     else {$input = ""; }
1437     # shorten macro if too long
1438     if(length($macro) > $defmax) {
1439     $input = substr($macro, $defmax).$input;
1440     $macro = substr($macro, 0, $defmax);
1441     }
1442     # see if a macro exists in "macro"
1443     while(length($macro) > $defmin &&
1444     !(exists($Defines{$macro}) && !exists($Used{$macro}))) {
1445     # chop a char off macro and try again
1446     $input = chop($macro).$input;
1447     }
1448     }
1449    
1450     # check if macro is at start of string and has not been used yet
1451     if(exists($Defines{$macro}) && !exists($Used{$macro})) {
1452     # set macro as used
1453     $Used{$macro} = $Defines{$macro};
1454     # temporarily add tail to input
1455     if($tail) { $input = $input.$tail; }
1456     # replace macro with defn
1457     if(CheckDefineArgs($macro)) {
1458     ($string, $input) = ReplaceDefineArgs($macro, $input, %Used);
1459     }
1460     else {
1461     $string = $Defines{$macro};
1462     Debug("Replaced \"".$macro."\" for \"".$string."\" [".$recurse_level."]", 2);
1463     }
1464    
1465     ($string=~ m/\#\#/) and ($string=~ s/\s*\#\#\s*//gm);
1466    
1467     @Words = ReplaceDefines($string, $input, %Used);
1468     $out = $out.$Words[0];
1469     if($#Words == 0) { $input = ""; }
1470     else {
1471     # remove space up to start of next char
1472     if(CheckEatTrail($macro)) { $Words[1] =~ s/^[ \t]*//o; }
1473     $input = $Words[1];
1474     }
1475     delete($Used{$macro});
1476     # reclaim all unparsed tail
1477     if($tail && $tail ne "") {
1478     ($input, $tail) = ReclaimTail($input, $tail);
1479     }
1480     }
1481     # macro not matched, add to output and move swiftly on
1482     else {
1483     if($bound eq '\b') { $out = $out.$macro; }
1484     else {
1485     $out = $out.substr($macro, 0, 1);
1486     $input = substr($macro, 1).$input;
1487     }
1488     }
1489     }
1490     $recurse_level--;
1491     # append any whitespace left in string and return it
1492     if($tail) { return ($out.$input, $tail); }
1493     return $out.$input;
1494     }
1495    
1496    
1497     ##############################################################################
1498     # GenerateDefinesKey creates all keys and indices needed for %Defines
1499     ##############################################################################
1500     sub GenerateDefinesKeys
1501     {
1502     # find longest and shortest macro
1503     my ($define, $length) = each %Defines;
1504     $defmin = $defmax = length($define);
1505     %DefineLookup = ();
1506     foreach $define (keys(%Defines)) {
1507     $length = length($define);
1508     if($length > $defmax) { $defmax = $length; }
1509     if($length < $defmin) { $defmin = $length; }
1510     }
1511     # regenerate lookup table of first letters
1512     foreach $define (keys(%Defines)) {
1513     $DefineLookup{substr($define, 0, $defmin)} = 1;
1514     }
1515     }
1516    
1517    
1518     ##############################################################################
1519     # Set a define
1520     ##############################################################################
1521     sub SetDefine
1522     {
1523     my ($macro, $value) = @_;
1524     # add macro and value to hash table
1525     $Defines{$macro} = $value;
1526     # add define to keys
1527     my $length = length($macro);
1528     if($length < $defmin || $defmin == 0) { GenerateDefinesKeys(); }
1529     else {
1530     if($length > $defmax) { $defmax = $length; }
1531     $length = substr($macro, 0, $defmin);
1532     $DefineLookup{$length} = 1;
1533     }
1534     }
1535    
1536    
1537     ##############################################################################
1538     # Get a define without doing any macro replacement
1539     ##############################################################################
1540     sub GetDefine
1541     {
1542     my $macro = shift;
1543     return $Defines{$macro};
1544     }
1545    
1546    
1547     ##############################################################################
1548     # Replace a define, checks if macro defined and only redefine's if it is
1549     ##############################################################################
1550     sub Redefine
1551     {
1552     my $macro = shift;
1553     my $value = shift;
1554     # check if defined
1555     if(CheckDefine($macro)) { SetDefine($macro, $value); }
1556     }
1557    
1558    
1559     ##############################################################################
1560     # Set a define argument list
1561     ##############################################################################
1562     sub SetDefineArgs
1563     {
1564     my $macro = shift;
1565     my $args = shift;
1566     # add macro args to hash table
1567     $DefinesArgs{$macro} = $args;
1568     }
1569    
1570    
1571     ##############################################################################
1572     # Set a function which should be called when a macro is found
1573     ##############################################################################
1574     sub SetDefineFuncs
1575     {
1576     my $macro = shift;
1577     my $func = shift;
1578     # add macro function to hash table
1579     $DefinesFuncs{$macro} = $func;
1580     }
1581    
1582    
1583     ##############################################################################
1584     # Check if a macro is defined
1585     ##############################################################################
1586     sub CheckDefine
1587     {
1588     my $macro = shift;
1589     return exists($Defines{$macro});
1590     }
1591    
1592    
1593     ##############################################################################
1594     # Check if a macro is defined and has arguments
1595     ##############################################################################
1596     sub CheckDefineArgs
1597     {
1598     my $macro = shift;
1599     return exists($DefinesArgs{$macro});
1600     }
1601    
1602    
1603     ##############################################################################
1604     # Check if a macro is defined and calls a function
1605     ##############################################################################
1606     sub CheckDefineFuncs
1607     {
1608     my $macro = shift;
1609     return exists($DefinesFuncs{$macro});
1610     }
1611    
1612    
1613     ##############################################################################
1614     # Check if a macro is defined and eats trailing whitespace
1615     ##############################################################################
1616     sub CheckEatTrail
1617     {
1618     my $macro = shift;
1619     return exists($EatTrail{$macro});
1620     }
1621    
1622    
1623     ##############################################################################
1624     # Set eat-trailing-whitespace for a macro
1625     ##############################################################################
1626     sub SetEatTrail
1627     {
1628     my $macro = shift;
1629     $EatTrail{$macro} = 1;
1630     }
1631    
1632    
1633     ##############################################################################
1634     # Test if a file exists and is readable
1635     ##############################################################################
1636     sub FileExists
1637     {
1638     my $filename = shift;
1639     # test if file is readable and not a directory
1640     if( !(-r $filename) || -d $filename ) {
1641     Debug("Checking for file: ".$filename."...not found!", 2);
1642     return 0;
1643     }
1644     Debug("Checking for file: ".$filename."...found!", 2);
1645     return 1;
1646     }
1647    
1648    
1649     ##############################################################################
1650     # #comment - rest of line ignored as a comment
1651     ##############################################################################
1652     sub Comment
1653     {
1654     # nothing to be done here
1655     Debug("Commented line", 2);
1656     }
1657    
1658    
1659     ##############################################################################
1660     # Define a variable, accepted inputs:
1661     # $macrodefn = $macro $defn - $macro associated with $defn
1662     # ie: #define TEST test string
1663     # $macro = TEST, $defn = "test string"
1664     # Note: $defn = rest of line after $macro
1665     # $macrodefn = $macro - $macro defined without a defn, rest of line ignored
1666     # ie: #define TEST_DEFINE
1667     # $macro = TEST_DEFINE, $defn = "1"
1668     ##############################################################################
1669     sub Define
1670     {
1671     my $macrodefn = shift;
1672     my $macro;
1673     my $defn;
1674     my $i;
1675    
1676     # check there is an argument
1677     if($macrodefn !~ /\S/o) {
1678     Filepp::Error("define keyword used without arguments");
1679     }
1680    
1681     # find end of macroword - assume separated by space or tab
1682     $i = GetNextWordEnd($macrodefn);
1683    
1684     # separate macro and defn (can't use split, doesn't work with '0')
1685     $macro = substr($macrodefn, 0, $i);
1686     $defn = substr($macrodefn, $i);
1687    
1688     # strip leading whitespace from $defn
1689     if($defn) {
1690     $defn =~ s/^[ \t]*//;
1691     }
1692     else {
1693     $defn = "";
1694     }
1695    
1696     # check if macro has arguments (will be a '(' in macro)
1697     if($macro =~ /\(/) {
1698     # split up macro, args and defn - delimiters = space, (, ), ','
1699     my @arglist = split(/([\s,\(,\),\,])/, $macro." ".$defn);
1700     my $macroargs = "";
1701     my $arg;
1702    
1703     # macro is first element in list, remove it from list
1704     $macro = $arglist[0];
1705     $arglist[0] = "";
1706     # loop through list until ')' and find all args
1707     foreach $arg (@arglist) {
1708     if($arg) {
1709     # end of arg list, leave loop
1710     if($arg eq ")") {
1711     $arg = "";
1712     last;
1713     }
1714     # ignore space, ',' and '('
1715     elsif($arg =~ /([\s,\,,\(])/) {
1716     $arg = "";
1717     }
1718     # argument found, add to ',' separated list
1719     else {
1720     $macroargs = $macroargs.",".$arg;
1721     $arg = "";
1722     }
1723     }
1724     }
1725     $macroargs = Strip($macroargs, ",", 1);
1726     # store args
1727     SetDefineArgs($macro, $macroargs);
1728    
1729     Debug("Define: macro ".$macro." has args (".$macroargs.")", 2);
1730     # put rest of defn back together
1731     $defn = join('',@arglist);
1732     $defn = CleanStart($defn);
1733     }
1734     # make sure macro is not being redefined and used to have args
1735     else {
1736     delete($DefinesArgs{$macro});
1737     delete($DefinesFuncs{$macro});
1738     }
1739    
1740     # define the macro defn pair
1741     SetDefine($macro, $defn);
1742    
1743     Debug("Defined \"".$macro."\" to be \"".$defn."\"", 2);
1744     if($debug > 2) { PrintDefines(); }
1745     }
1746    
1747    
1748    
1749     ##############################################################################
1750     # Else, standard if[n][def]-else-endif
1751     # usage: #else somewhere between #if[n][def] key and #endif
1752     ##############################################################################
1753     sub Else
1754     {
1755     # else always true - only ran when all preceding 'if's have failed
1756     return 1;
1757     }
1758    
1759    
1760     ##############################################################################
1761     # Endif, standard ifdef-[else]-endif
1762     # usage: #endif somewhere after #ifdef key and optionally #else
1763     ##############################################################################
1764     sub Endif
1765     {
1766     # this always terminates an if block
1767     return 1;
1768     }
1769    
1770    
1771     ##############################################################################
1772     # If conditionally includes or ignores parts of a file based on expr
1773     # usage: #if expr
1774     # expr is evaluated to true(1) or false(0) and include usual ==, !=, > etc.
1775     # style comparisons. The "defined" keyword can also be used, ie:
1776     # #if defined MACRO || !defined(MACRO)
1777     ##############################################################################
1778     sub If
1779     {
1780     my $expr = shift;
1781     Debug("If: parsing: \"".$expr."\"", 2);
1782    
1783     # check for any "defined MACRO" tests and evaluate them
1784     if($expr =~ /defined/) {
1785     my $indefined = 0;
1786    
1787     # split expr up into its component parts, the split is done on the
1788     # following list of chars and strings: '!','(',')','&&','||', space
1789     my @Exprs = split(/([\s,\!,\(,\)]|\&\&|\|\|)/, $expr);
1790    
1791     # search through parts for "defined" keyword and check if macros
1792     # are defined
1793     foreach $expr (@Exprs) {
1794     if($indefined == 1) {
1795     # previously found a defined keyword, check if next word
1796     # could be the macro to test for (not any of the listed chars)
1797     if($expr && $expr !~ /([\s,\!,\(,\)]|\&\&|\|\|)/) {
1798     # replace macro with 0 or 1 depending if it is defined
1799     Debug("If: testing if \"".$expr."\" defined...", 2);
1800     if(CheckDefine($expr)) {
1801     $expr = 1;
1802     Debug("If: defined", 2);
1803     }
1804     else {
1805     $expr = 0;
1806     Debug("If: NOT defined", 2);
1807     }
1808     $indefined = 0;
1809     }
1810     }
1811     elsif($expr eq "defined") {
1812     # get rid of defined keyword
1813     $expr = "";
1814     # search for next macro following "defined"
1815     $indefined = 1;
1816     }
1817     }
1818    
1819     # put full expr string back together
1820     my $newexpr = join('',@Exprs);
1821     $expr = $newexpr;
1822     }
1823    
1824     # pass parsed line though processors
1825     $expr = RunProcessors($expr);
1826    
1827     # evaluate line and return result (1 = true)
1828     Debug("If: evaluating \"".$expr."\"", 2);
1829     my $result = eval($expr);
1830     # check if statement is valid
1831     if(!defined($result)) { Warning($@); }
1832     elsif($result) {
1833     Debug("If: \"".$expr."\" true", 1);
1834     return 1;
1835     }
1836     Debug("If: \"".$expr."\" false", 1);
1837     return 0;
1838     }
1839    
1840    
1841     ##############################################################################
1842     # Elif equivalent to "else if". Placed between #if[n][def] and #endif,
1843     # equivalent to nesting #if's
1844     ##############################################################################
1845     sub Elif
1846     {
1847     my $input = shift;
1848     return If($input);
1849     }
1850    
1851    
1852     ##############################################################################
1853     # Ifdef conditionally includes or ignores parts of a file based on macro,
1854     # usage: #ifdef MACRO
1855     # if macro has been previously #define'd everything following the
1856     # #ifdef will be included, else it will be ignored until #else or #endif
1857     ##############################################################################
1858     sub Ifdef
1859     {
1860     my $macro = shift;
1861    
1862     # separate macro from any trailing garbage
1863     $macro = substr($macro, 0, GetNextWordEnd($macro));
1864    
1865     # check if macro defined - if not set to be #ifdef'ed out
1866     if(CheckDefine($macro)) {
1867     Debug("Ifdef: ".$macro." defined", 1);
1868     return 1;
1869     }
1870     Debug("Ifdef: ".$macro." not defined", 1);
1871     return 0;
1872     }
1873    
1874    
1875     ##############################################################################
1876     # Ifndef conditionally includes or ignores parts of a file based on macro,
1877     # usage: #ifndef MACRO
1878     # if macro has been previously #define'd everything following the
1879     # #ifndef will be ignored, else it will be included until #else or #endif
1880     ##############################################################################
1881     sub Ifndef
1882     {
1883     my $macro = shift;
1884    
1885     # separate macro from any trailing garbage
1886     $macro = substr($macro, 0, GetNextWordEnd($macro));
1887    
1888     # check if macro defined - if not set to be #ifdef'ed out
1889     if(CheckDefine($macro)) {
1890     Debug("Ifndef: ".$macro." defined", 1);
1891     return 0;
1892     }
1893     Debug("Ifndef: ".$macro." not defined", 1);
1894     return 1;
1895     }
1896    
1897    
1898     ##############################################################################
1899     # Parses all macros from file, but discards all other output
1900     ##############################################################################
1901     sub IncludeMacros
1902     {
1903     my $file = shift;
1904     my $currentoutput = $output;
1905     SetOutput(0);
1906     Parse($file);
1907     SetOutput($currentoutput);
1908     }
1909    
1910    
1911     ##############################################################################
1912     # Include $filename in output file, format:
1913     # #include "filename" - local include file, ie. in same directory, try -Ipath
1914     # also if not not found in current directory
1915     # #include <filename> - system include file, use -Ipath
1916     ##############################################################################
1917     sub Include
1918     {
1919     my $input = shift;
1920     my $filename = $input;
1921     my $fullname;
1922     my $sysinclude = 0;
1923     my $found = 0;
1924     my $i;
1925    
1926    
1927     # check for recursive includes (level set to same as Perl recurse warn)
1928     if($include_level >= 98) {
1929     Warning("Include recursion too deep - skipping \"".$filename."\"\n");
1930     return;
1931     }
1932    
1933     # replace any defined values in the include line
1934     $filename = RunProcessors($filename);
1935    
1936     # check if it is a system include file (#include <filename>) or a local
1937     # include file (#include "filename")
1938     if(substr($filename, 0, 1) eq "<") {
1939     $sysinclude = 1;
1940     # remove <> from filename
1941     $filename = substr($filename, 1);
1942     ($filename) = split(/\>/, $filename, 2);
1943     }
1944     elsif(substr($filename, 0, 1) eq "\"") {
1945     # remove double quotes from filename
1946     $filename = substr($filename, 1);
1947     ($filename) = split(/\"/, $filename, 2);
1948     }
1949     # else assume filename given without "" or <>, naughty but allowed
1950    
1951     # check for file in current directory
1952     if($sysinclude == 0) {
1953     # get name of directory base file is in
1954     my $dir = "";
1955     if($file =~ /\//) {
1956     my @Dirs = split(/(\/)/, $file);
1957     for($i=0; $i<$#Dirs; $i++) {
1958     $dir = $dir.$Dirs[$i];
1959     }
1960     }
1961     if(FileExists($dir.$filename)) {
1962     $fullname = $dir.$filename;
1963     $found = 1;
1964     }
1965     }
1966    
1967     # search for file in include paths, first path on command line first
1968     $i = 0;
1969     while($found == 0 && $i <= $#IncludePaths) {
1970     $fullname = $IncludePaths[$i]."/".$filename;
1971     if(FileExists($fullname)) { $found = 1; }
1972     $i++;
1973     }
1974    
1975     # include file if found, error if not
1976     if($found == 1) {
1977     Debug("Including file: \"".$fullname."\"", 1);
1978    
1979     #if $filename is already visited, just return
1980     if (IsVisited($fullname)){
1981     return;
1982     } else {
1983     # recursively call Parse
1984     print " " . $fullname . " \\\n";
1985     Parse($fullname);
1986     }
1987     }
1988     else {
1989     Warning("Include file \"".$filename."\" not found", 1);
1990     }
1991     }
1992    
1993    
1994    
1995     ##############################################################################
1996     # Pragma filepp Function Args
1997     # Pragma executes a filepp function, everything following the function name
1998     # is passed as arguments to the function.
1999     # The format is:
2000     # #pragma filepp function args...
2001     # If pragma is not followed by "filepp", it is ignored.
2002     ##############################################################################
2003     sub Pragma
2004     {
2005     my $input = shift;
2006    
2007     # check for "filepp" in string
2008     if($input =~ /^filepp\b/) {
2009     my ($function, $args);
2010     ($input, $function, $args) = split(/\s/, $input, 3);
2011     if($function) {
2012     if(!$args) { $args = ""; }
2013     if($safe_mode) {
2014     Debug("Safe mode enabled, NOT running: ".$function."(".$args.")", 1);
2015     }
2016     else {
2017     my @Args = ParseArgs($args);
2018     Debug("Running function: ".$function."(".$args.")", 1);
2019     $function->(@Args);
2020     }
2021     }
2022     }
2023     }
2024    
2025    
2026     ##############################################################################
2027     # Turn normal output on/off (does not affect any output produced by keywords)
2028     # 1 = on, 0 = off
2029     ##############################################################################
2030     sub SetOutput
2031     {
2032     $output = shift;
2033     Debug("Output set to ".$output, 2);
2034     }
2035    
2036    
2037     ##############################################################################
2038     # Turn blank suppression on and off at this include level
2039     # 1 = on, 0 = off
2040     ##############################################################################
2041     sub SetBlankSupp
2042     {
2043     $blanksupp[$include_level] = shift;
2044     Debug("Blank suppression set to ".$blanksupp[$include_level], 2);
2045     }
2046    
2047    
2048     ##############################################################################
2049     # Reset blank suppression to command-line value (except at level 0)
2050     ##############################################################################
2051     sub ResetBlankSupp
2052     {
2053     if($include_level == 0) {
2054     $blanksupp[$include_level] = 0;
2055     } else {
2056     $blanksupp[$include_level] = $blanksuppopt;
2057     }
2058     Debug("Blank suppression reset to ".$blanksupp[$include_level], 2);
2059     }
2060    
2061    
2062     ##############################################################################
2063     # Set if macros are only replaced if the macro is a 'word'
2064     ##############################################################################
2065     sub SetWordBoundaries
2066     {
2067     my $on = shift;
2068     if($on) {
2069     $bound = '\b';
2070     Debug("Word Boundaries turned on", 2);
2071     }
2072     else {
2073     $bound = '';
2074     Debug("Word Boundaries turned off", 2);
2075     }
2076     }
2077    
2078     ##############################################################################
2079     # DEPRECATED - this function will be removed in later versions, use Set
2080     # Toggle if macros are only replaced if the macro is a 'word'
2081     ##############################################################################
2082     sub ToggleWordBoundaries
2083     {
2084     if($bound eq '\b') { SetWordBoundaries(1); }
2085     else { SetWordBoundaries(0); }
2086     }
2087    
2088    
2089     ##############################################################################
2090     # Set treating keywordchar, contchar, macroprefix and optlineendchar as
2091     # Perl regexps
2092     ##############################################################################
2093     sub SetCharPerlre
2094     {
2095     $charperlre = shift;
2096     Debug("Characters treated as Perl regexp's : ".$charperlre, 2);
2097     }
2098    
2099    
2100     ##############################################################################
2101     # Undef a previously defined variable, usage:
2102     # #undef $macro
2103     ##############################################################################
2104     sub Undef
2105     {
2106     my $macro = shift;
2107     my $i;
2108    
2109     # separate macro from any trailing garbage
2110     $macro = substr($macro, 0, GetNextWordEnd($macro));
2111    
2112     # delete macro from table
2113     delete $Defines{$macro};
2114     delete $DefinesArgs{$macro};
2115     delete $DefinesFuncs{$macro};
2116    
2117     # and remove its eat-trailing-whitespace flag
2118     if(CheckEatTrail($macro)) { delete $EatTrail{$macro}; }
2119    
2120     # regenerate keys
2121     GenerateDefinesKeys();
2122    
2123     Debug("Undefined macro \"".$macro."\"", 2);
2124     if($debug > 1) { PrintDefines(); }
2125     }
2126    
2127    
2128     ##############################################################################
2129     # UndefAll - undefines ALL macros
2130     ##############################################################################
2131     sub UndefAll
2132     {
2133     %Defines = ();
2134     %DefineLookup = ();
2135     %EatTrail = ();
2136     $defmin = $defmax = 0;
2137     Debug("Undefined ALL macros", 2);
2138     if($debug > 1) { PrintDefines(); }
2139     }
2140    
2141    
2142     ##############################################################################
2143     # #warning msg - print warning message "msg"
2144     ##############################################################################
2145     sub Warning
2146     {
2147     my $msg = shift;
2148     my $lastdebug = $debug;
2149     $debug = 1;
2150     Debug($msg, 1);
2151     $debug = $lastdebug;
2152     }
2153    
2154    
2155     ##############################################################################
2156     # ParseLineEnd - takes in line from input most recently read and checks
2157     # if line should be continued (ie. next line in input read and appended
2158     # to current line).
2159     # Returns two values:
2160     # $more - boolean, 1 = read another line from input to append to this one
2161     # 0 = no line continuation
2162     # $line - the line to be read. If any modification needs to be done to the
2163     # line for line contination, it is done here.
2164     # Example: if line is to be continued: set $more = 1, then
2165     # remove line continuation character and newline from end of
2166     # $line and replace with line continuation character.
2167     ##############################################################################
2168     sub ParseLineEnd
2169     {
2170     my $thisline = shift;
2171     my $more = 0;
2172     # check if end of line has a continuation char, if it has get next line
2173     if($thisline =~ /$contchar$/) {
2174     $more = 1;
2175     # remove backslash and newline
2176     $thisline =~ s/$contchar\n\Z//;
2177     # append line continuation character
2178     $thisline = $thisline.$contrepchar;
2179     }
2180     return ($more, $thisline);
2181     }
2182    
2183    
2184     ##############################################################################
2185     # Set name of function to take check if line shoule be continued
2186     ##############################################################################
2187     sub SetParseLineEnd
2188     {
2189     my $func = shift;
2190     $parselineend = $func;
2191     }
2192    
2193     ##############################################################################
2194     # Get name of function to take check if line shoule be continued
2195     ##############################################################################
2196     sub GetParseLineEnd
2197     {
2198     return $parselineend;
2199     }
2200    
2201    
2202     ##############################################################################
2203     # GetNextLine - returns the next line of the current INPUT line,
2204     # line continuation is taken care of here.
2205     ##############################################################################
2206     sub GetNextLine
2207     {
2208     my $thisline = <INPUT>;
2209     if($thisline) {
2210     Redefine("__LINE__", ++$line);
2211     my $more = 0;
2212     ($more, $thisline) = $parselineend->($thisline);
2213     while($more) {
2214     Debug("Line continuation", 2);
2215     my $nextline = <INPUT>;
2216     if(!$nextline) { return $thisline; }
2217     # increment line count
2218     Redefine("__LINE__", ++$line);
2219     ($more, $thisline) = $parselineend->($thisline.$nextline);
2220     # maintain same number of lines in input as output
2221     if($preserveblank) { Filepp::Output("\n"); }
2222     }
2223     }
2224     return $thisline;
2225     }
2226    
2227    
2228     ##############################################################################
2229     # Write($string) - writes $string to OUTPUT file
2230     ##############################################################################
2231     sub Write
2232     {
2233     my $string = shift;
2234     #print(OUTPUT $string);
2235     }
2236    
2237    
2238     ##############################################################################
2239     # Output($string) - conditionally writes $string to OUTPUT file
2240     ##############################################################################
2241     sub Output
2242     {
2243     my $string = shift;
2244     if($output) { Write($string); }
2245     }
2246    
2247     # counter for number of #if[n][def] loops currently in
2248     my $iflevel = 0;
2249     # flag to control when to write output
2250     my @Writing = (1); # initialise default to 'writing'
2251     # flag to show if current 'if' block has passed a 'true if'
2252     my @Ifdone = (0); # initialise first to 'not passed true if'
2253    
2254     ##############################################################################
2255     # Keyword parsing routine
2256     ##############################################################################
2257     sub ParseKeywords
2258     {
2259     # input is next line in file
2260     my $inline = shift;
2261     my $outline = "";
2262    
2263     my $thisline = $inline;
2264     my $keyword;
2265     my $found = 0;
2266     # remove whitespace from start of line
2267     $thisline = CleanStart($thisline);
2268     # check if first char on line is a #
2269    
2270     #if($thisline && $thisline =~ /^$keywordchar/) {
2271     if($thisline) {
2272     # remove "#" and any following whitespace
2273     #$thisline =~ s/^$keywordchar\s*//g;
2274     # remove the optional end line char
2275     if($optlineendchar ne "") {
2276     $thisline =~ s/$optlineendchar\Z//g;
2277     }
2278     # check for keyword
2279     #if($thisline && $thisline =~ /^\w+\b/ && exists($Keywords{$&})) {
2280     if($thisline && $thisline =~ /^#*\w+\b/ && exists($Keywords{$&})) {
2281     $keyword = $&;
2282     $found = 1;
2283     # remove newline from line
2284     chomp($thisline);
2285     # remove leading whitespace and keyword from line
2286     my $inline = CleanStart(substr($thisline, length($keyword)));
2287    
2288     # check for 'if' style keyword
2289     if(exists($Ifwords{$keyword})) {
2290     # increment ifblock level and set ifdone to same
2291     # value as previous block
2292     $iflevel++;
2293     $Ifdone[$iflevel] = 0;
2294     $Writing[$iflevel] = $Writing[$iflevel - 1];
2295     if(!$Writing[$iflevel]) { $Ifdone[$iflevel] = 1; }
2296     }
2297     # check for out of place 'else' or 'endif' style keyword
2298     elsif($iflevel <= 0 && (exists($Elsewords{$keyword}) ||
2299     exists($Endifwords{$keyword}) )) {
2300     Warning($keywordchar.$keyword." found without preceding ".
2301     $keywordchar."[else]ifword");
2302     }
2303    
2304     # decide if to run 'if' or 'else' keyword
2305     if(exists($Ifwords{$keyword}) || exists($Elsewords{$keyword})){
2306     if(!($Ifdone[$iflevel])) {
2307     # check return value of 'if'
2308     if($Keywords{$keyword}->($inline)) {
2309     $Ifdone[$iflevel] = 1;
2310     $Writing[$iflevel] = 1;
2311     }
2312     else { $Writing[$iflevel] = 0; }
2313     }
2314     else { $Writing[$iflevel] = 0; }
2315     }
2316     # check for 'endif' style keyword
2317     elsif(exists($Endifwords{$keyword})) {
2318     # run endif keyword and decrement iflevel if true
2319     if($Keywords{$keyword}->($inline)) { $iflevel--; }
2320     }
2321     # run all other keywords
2322     elsif($Writing[$iflevel]) { $Keywords{$keyword}->($inline); }
2323    
2324     # write a blank line if preserving blank lines
2325     # (assumes keywords have no output)
2326     if($preserveblank) { $outline = $outline."\n"; }
2327    
2328     } # keyword if statement
2329     }
2330     # no keywords in line - write line to file if not #ifdef'ed out
2331     if(!$found && $Writing[$iflevel]) {
2332     $outline = $outline.$inline;
2333     }
2334     # keep same number of files in output and input
2335     elsif(!$found && $preserveblank) { $outline = $outline."\n"; }
2336    
2337     return $outline;
2338     }
2339    
2340     ##############################################################################
2341     # Main parsing routine
2342     ##############################################################################
2343     sub Parse
2344     {
2345     # change file being parsed to this file, remember last filename so
2346     # it can be returned at the end
2347     my $lastparse = $file;
2348     $file = shift;
2349    
2350     Debug("Parsing ".$file."...", 1);
2351     Redefine("__FILE__", $file);
2352    
2353     # reset line count, remembering previous count for future reference
2354     my $lastcount = $line;
2355     $line = 0;
2356     Redefine("__LINE__", $line);
2357    
2358     # increment include level
2359     Redefine("__INCLUDE_LEVEL__", ++$include_level);
2360    
2361     # set blank line suppression:
2362     # no suppression for top level files
2363     if($include_level == 0) {
2364     $blanksupp[$include_level] = 0;
2365     }
2366     # include level 1 - set suppression to command line given value
2367     elsif($include_level == 1) {
2368     # inherit root value if set
2369     if($blanksupp[0]) { $blanksupp[$include_level] = 1; }
2370     else {$blanksupp[$include_level] = $blanksuppopt; }
2371     }
2372     # all other include levels - keep suppression at existing value
2373     else {
2374     $blanksupp[$include_level] = $blanksupp[$include_level - 1];
2375     }
2376    
2377     # reset RunProcessors function for this file
2378     $Running[$include_level] = 0;
2379     $Currentproc[$include_level] = 0;
2380    
2381     # open file and set its handle to INPUT
2382     local *INPUT;
2383     if(!open(INPUT, $file)) {
2384     Error("Could not open file ".$file);
2385     }
2386    
2387     # change the behavior of OpenInputFuncs
2388     # every time a file is open, the functions in @OpenInputFuncs
2389     # are called.
2390     # if a base file, run any initialisation functions
2391     # if($include_level == 0) {
2392     my $func;
2393     foreach $func (@OpenInputFuncs) { $func->(); }
2394     #}
2395    
2396     # parse each line of file
2397     $_ = GetNextLine();
2398     # if in "shebang" mode, throw away first line (the #!/blah bit)
2399     if($shebang) {
2400     # check for "#!...perl ...filepp..."
2401     if($_ && $_ =~ /^\#\!.*perl.+filepp/) {
2402     Debug("Skipping first line (shebang): ".$_, 1);
2403     $_ = GetNextLine();
2404     }
2405     }
2406    
2407     while($_) {
2408     # unless blank lines are suppressed at this include level
2409     unless($blanksupp[$include_level] && /^\s*$/) {
2410     # run processing chain (defaults to ReplaceDefines)
2411     $_ = RunProcessors($_, 1);
2412     # write output to file or STDOUT
2413     if($output) { Write($_); }
2414     }
2415     $_ = GetNextLine();
2416     }
2417    
2418     # run any close functions
2419     #if($include_level == 0) {
2420     #my $func;
2421     foreach $func (@CloseInputFuncs) { $func->(); }
2422     #}
2423    
2424     # check all #if blocks have been closed at end of parsing
2425     if($lastparse eq "" && $iflevel > 0) { Warning("Unterminated if block"); }
2426    
2427     # close file
2428     close(INPUT);
2429     Debug("Parsing ".$file." done. (".$line." lines processed)", 1);
2430    
2431     # reset $line
2432     $line = $lastcount;
2433     Redefine("__LINE__", $line);
2434    
2435     # reset $file
2436     $file = $lastparse;
2437     Redefine("__FILE__", $file);
2438     if($file ne "") {
2439     Debug("Parsing returned to ".$file." at line ".$line, 1);
2440     }
2441    
2442     # decrement include level
2443     Redefine("__INCLUDE_LEVEL__", --$include_level);
2444    
2445     }
2446    
2447     ##############################################################################
2448     # module keyword - declare a fortran90 module
2449     ##############################################################################
2450     sub Module{
2451     my $modulename = shift;
2452     my $modulefile;
2453     my $file;
2454     if ($modulename !~ /^procedure/){
2455    
2456     $parsedModList{uc($modulename) . ".mod"} = Filepp::GetDefine('__FILE__');
2457    
2458     #$modulefile = Filepp::GetDefine('__BASE_FILE__');
2459     #print $modulefile;
2460     #$file = Filepp::GetDefine('__FILE__');
2461     #print $modulefile;
2462     }
2463     }
2464    
2465     ##############################################################################
2466     # add use keyword
2467     ##############################################################################
2468     Filepp::AddKeyword("module", "Filepp::Module");
2469    
2470     ##############################################################################
2471     # use keyword - use other fortran90 module
2472     ##############################################################################
2473     sub Use{
2474     my $line = shift;
2475     $line =~ /^(\w+).*/;
2476     my $f90module = $1;
2477     $f90module = uc($f90module);
2478    
2479     print " " . $objDir . $f90module . '.mod \\', "\n";
2480     #addModule($f90module);
2481     }
2482    
2483     ##############################################################################
2484     # add use keyword
2485     ##############################################################################
2486     Filepp::AddKeyword("use", "Filepp::Use");
2487    
2488     ##############################################################################
2489     # add include keyword which is the same as c's #include
2490     ##############################################################################
2491     Filepp::AddKeyword("include", "Filepp::Include");
2492    
2493     ##############################################################################
2494     # add RecordFileInfo info Filepp. Every time a file is opened, an entry
2495     # of this file is created
2496     ##############################################################################
2497    
2498     sub RecordFileInfo{
2499     my $file = Filepp::GetDefine('__FILE__');
2500     # dependenyGraph->add_vertex(new );
2501    
2502     #if it is not base file, we need to add an edge
2503     if ($include_level > 0) {
2504    
2505     }
2506    
2507     }
2508    
2509     Filepp::AddOpenInputFunc("Filepp::RecordFileInfo");
2510    
2511     sub IsVisited {
2512     my $fullfile = shift;
2513    
2514     if (exists($visitedTable{$fullfile})){
2515     return 1;
2516     } else {
2517     $visitedTable{$fullfile} = 1;
2518     return 0;
2519     }
2520     }
2521    
2522     sub AddModule {
2523     my $modulename = shift;
2524    
2525     if (!exists($f90ModList{$modulename})){
2526     $f90ModList{$modulename} = 1;
2527     } else {
2528     $f90ModList{$modulename}++;
2529    
2530     }
2531    
2532     }
2533    
2534    
2535     sub printModule {
2536     my $modname;
2537     my $objname;
2538     print "\n";
2539     foreach $modname (keys %parsedModList) {
2540     $objname = GetObjFile($parsedModList{$modname});
2541     print $objDir . $modname . " : " . $objDir . $objname . "\n";
2542     }
2543     }
2544    
2545     sub GetObjFile {
2546     use File::Basename;
2547     my $fullname = shift;
2548     my $filename;
2549     my $dir;
2550     my $suffix;
2551     ($filename, $dir, $suffix) = fileparse($fullname, '\.[^.]*');
2552     return $filename . $objExt;
2553     }
2554     ##############################################################################
2555     # Main routine
2556     ##############################################################################
2557    
2558     # parse command line
2559     my $i=0;
2560     my $argc=0;
2561     while($ARGV[$argc]) { $argc++; }
2562    
2563     while($ARGV[$i]) {
2564    
2565     # suppress blank lines in header files
2566     if($ARGV[$i] eq "-b") {
2567     $blanksuppopt = 1;
2568     }
2569    
2570     # read from stdin instead of file
2571     elsif($ARGV[$i] eq "-c") {
2572     AddInputFile("-");
2573     }
2574    
2575     # Defines: -Dmacro[=defn] or -D macro[=defn]
2576     elsif(substr($ARGV[$i], 0, 2) eq "-D") {
2577     my $macrodefn;
2578     # -D macro[=defn] format
2579     if(length($ARGV[$i]) == 2) {
2580     if($i+1 >= $argc) {
2581     Error("Argument to `-D' is missing");
2582     }
2583     $macrodefn = $ARGV[++$i];
2584     }
2585     # -Dmacro[=defn] format
2586     else {
2587     $macrodefn = substr($ARGV[$i], 2);
2588     }
2589     my $macro = $macrodefn;
2590     my $defn = "";
2591     my $j = index($macrodefn, "=");
2592     if($j > -1) {
2593     $defn = substr($macrodefn, $j+1);
2594     $macro = substr($macrodefn, 0, $j);
2595     }
2596     # add macro and defn to hash table
2597     Define($macro." ".$defn);
2598     }
2599    
2600     # Debugging turned on: -d
2601     elsif($ARGV[$i] eq "-d") {
2602     SetDebug(2);
2603     }
2604    
2605     # Full debugging turned on: -dd
2606     elsif($ARGV[$i] eq "-dd") {
2607     SetDebug(3);
2608     }
2609    
2610     # Light debugging turned on: -dl
2611     elsif($ARGV[$i] eq "-dl") {
2612     SetDebug(1);
2613     }
2614    
2615     # Send debugging info to stdout rather than stderr
2616     elsif($ARGV[$i] eq "-ds") {
2617     $debugstdout = 1;
2618     }
2619    
2620     # prefix all debugging info with string
2621     elsif($ARGV[$i] eq "-dpre") {
2622     if($i+1 >= $argc) {
2623     Error("Argument to `-dpre' is missing");
2624     }
2625     $debugprefix = ReplaceDefines($ARGV[++$i]);
2626     }
2627    
2628     # prefix all debugging info with string
2629     elsif($ARGV[$i] eq "-dpost") {
2630     if($i+1 >= $argc) {
2631     Error("Argument to `-dpost' is missing");
2632     }
2633     # replace defines is called here in case a newline is required,
2634     # this allows it to be added as __NEWLINE__
2635     $debugpostfix = ReplaceDefines($ARGV[++$i]);
2636     }
2637    
2638     # define environment variables as macros: -e
2639     elsif($ARGV[$i] eq "-e") {
2640     DefineEnv();
2641     }
2642    
2643     # set environment variable prefix char
2644     elsif($ARGV[$i] eq "-ec") {
2645     if($i+1 >= $argc) {
2646     Error("Argument to `-ec' is missing");
2647     }
2648     SetEnvchar($ARGV[++$i]);
2649     }
2650    
2651     # set environment variable prefix char to nothing
2652     elsif($ARGV[$i] eq "-ecn") {
2653     SetEnvchar("");
2654     }
2655    
2656     # show help
2657     elsif($ARGV[$i] eq "-h") {
2658     print(STDERR $usage);
2659     exit(0);
2660     }
2661    
2662     # Include paths: -Iinclude or -I include
2663     elsif(substr($ARGV[$i], 0, 2) eq "-I") {
2664     # -I include format
2665     if(length($ARGV[$i]) == 2) {
2666     if($i+1 >= $argc) {
2667     Error("Argument to `-I' is missing");
2668     }
2669     AddIncludePath($ARGV[++$i]);
2670     }
2671     # -Iinclude format
2672     else {
2673     AddIncludePath(substr($ARGV[$i], 2));
2674     }
2675     }
2676    
2677     # Include macros from file: -imacros file
2678     elsif($ARGV[$i] eq "-imacros") {
2679     if($i+1 >= $argc) {
2680     Error("Argument to `-imacros' is missing");
2681     }
2682     push(@Imacrofiles, $ARGV[++$i]);
2683     }
2684    
2685     # turn off keywords
2686     elsif($ARGV[$i] eq "-k") {
2687     RemoveAllKeywords();
2688     }
2689    
2690     # set keyword prefix char
2691     elsif($ARGV[$i] eq "-kc") {
2692     if($i+1 >= $argc) {
2693     Error("Argument to `-kc' is missing");
2694     }
2695     SetKeywordchar($ARGV[++$i]);
2696     }
2697    
2698     # set line continuation character
2699     elsif($ARGV[$i] eq "-lc") {
2700     if($i+1 >= $argc) {
2701     Error("Argument to `-lc' is missing");
2702     }
2703     SetContchar($ARGV[++$i]);
2704     }
2705    
2706     # set optional line end character
2707     elsif($ARGV[$i] eq "-lec") {
2708     if($i+1 >= $argc) {
2709     Error("Argument to `-lec' is missing");
2710     }
2711     SetOptLineEndchar($ARGV[++$i]);
2712     }
2713    
2714     # set line continuation replacement char to newline
2715     elsif($ARGV[$i] eq "-lrn") {
2716     SetContrepchar("\n");
2717     }
2718    
2719     # set line continuation replacement character
2720     elsif($ARGV[$i] eq "-lr") {
2721     if($i+1 >= $argc) {
2722     Error("Argument to `-lr' is missing");
2723     }
2724     SetContrepchar($ARGV[++$i]);
2725     }
2726    
2727     # Module paths: -Minclude or -M include
2728     elsif(substr($ARGV[$i], 0, 2) eq "-M") {
2729     # -M include format
2730     if(length($ARGV[$i]) == 2) {
2731     if($i+1 >= $argc) {
2732     Error("Argument to `-M' is missing");
2733     }
2734     AddModulePath($ARGV[++$i]);
2735     }
2736     # -Minclude format
2737     else {
2738     AddModulePath(substr($ARGV[$i], 2));
2739     }
2740     }
2741    
2742     # use module
2743     elsif($ARGV[$i] eq "-m") {
2744     if($i+1 >= $argc) {
2745     Error("Argument to `-m' is missing");
2746     }
2747     UseModule($ARGV[++$i]);
2748     }
2749    
2750     # set macro prefix
2751     elsif($ARGV[$i] eq "-mp") {
2752     if($i+1 >= $argc) {
2753     Error("Argument to `-mp' is missing");
2754     }
2755     SetMacroPrefix($ARGV[++$i]);
2756     }
2757    
2758     # turn off macro prefix within keywords
2759     elsif($ARGV[$i] eq "-mpnk") {
2760     $macroprefixinkeywords = 0;
2761     }
2762    
2763     # tells filepp that the object and
2764     # module files will be built in a separate directory from the sources.
2765     elsif($ARGV[$i] eq "-od") {
2766     $objDir = $ARGV[++$i];
2767     }
2768     # turn on overwrite mode
2769     elsif($ARGV[$i] eq "-ov") {
2770     $overwrite = 1;
2771     }
2772    
2773     # turn on overwrite conversion mode
2774     elsif($ARGV[$i] eq "-ovc") {
2775     if($i+1 >= $argc) {
2776     Error("Argument to `-ovc' is missing");
2777     }
2778     $overwriteconv = $ARGV[++$i];
2779     if($overwriteconv !~ /=/) {
2780     Error("-ovc argument is of form IN=OUT");
2781     }
2782     $overwrite = 1;
2783     }
2784    
2785     # Output filename: -o filename or -ofilename
2786     elsif(substr($ARGV[$i], 0, 2) eq "-o") {
2787     # -o filename
2788     if(length($ARGV[$i]) == 2) {
2789     if($i+1 >= $argc) {
2790     Error("Argument to `-o' is missing");
2791     }
2792     $outputfile = $ARGV[++$i];
2793     }
2794     # -ofilename
2795     else {
2796     $outputfile = substr($ARGV[$i], 2);
2797     }
2798     }
2799    
2800     # preserve blank lines in output file
2801     elsif($ARGV[$i] eq "-pb") {
2802     $preserveblank = 1;
2803     }
2804    
2805     # treat $keywordchar, $contchar and $optlineendchar as regular expressions
2806     elsif($ARGV[$i] eq "-re") {
2807     if($charperlre) { SetCharPerlre(0); }
2808     else { SetCharPerlre(1); }
2809     }
2810    
2811     # Safe mode - turns off #pragma
2812     elsif($ARGV[$i] eq "-s") {
2813     SafeMode();
2814     }
2815    
2816     # Undefine all macros
2817     elsif($ARGV[$i] eq "-u") {
2818     UndefAll();
2819     }
2820    
2821     # print version number and exit
2822     elsif($ARGV[$i] eq "-v") {
2823     print(STDERR "filepp version ".$VERSION."\n");
2824     exit(0);
2825     }
2826    
2827     # only replace macros if they appear as 'words'
2828     elsif($ARGV[$i] eq "-w") {
2829     if($bound eq '') { SetWordBoundaries(1); }
2830     else { SetWordBoundaries(0); }
2831     }
2832    
2833     # default - an input file name
2834     else {
2835     if(!FileExists($ARGV[$i])) {
2836     Error("Input file \"".$ARGV[$i]."\" not readable");
2837     }
2838     AddInputFile($ARGV[$i]);
2839     }
2840    
2841     $i++;
2842     }
2843    
2844     # check input files have been specified
2845     if($#Inputfiles == -1) {
2846     Error("No input files given");
2847     }
2848    
2849     # import macros from file if any
2850     if($#Imacrofiles >= 0) {
2851     my $file;
2852     foreach $file (@Imacrofiles) { IncludeMacros($file); }
2853     }
2854    
2855     # print initial defines if debugging
2856     if($debug > 1) { PrintDefines(); }
2857    
2858     # open the output file
2859     if(!$overwrite) { OpenOutputFile($outputfile); }
2860    
2861     # parse all input files in order given on command line
2862     my $base_file = "";
2863     foreach $base_file (@Inputfiles) {
2864     Redefine("__BASE_FILE__", $base_file);
2865     # set open output file if in overwrite mode
2866     if($overwrite) {
2867     if($overwriteconv ne "") { # convert output filename if needed
2868     my ($in,$out) = split(/=/, $overwriteconv, 2);
2869     my $outfile = $base_file;
2870     $outfile =~ s/\Q$in\E/$out/;
2871     OpenOutputFile($outfile);
2872     }
2873     else { OpenOutputFile($base_file); }
2874     }
2875    
2876     #clean visitedTable
2877     %visitedTable = ();
2878     print "\n";
2879     print $objDir . GetObjFile($base_file) . " : ";
2880     Parse($base_file);
2881     # close output file if in overwrite mode
2882     if($overwrite) { CloseOutputFile(); }
2883     }
2884    
2885     printModule();
2886    
2887     # close output file
2888     if(!$overwrite) { CloseOutputFile(); }
2889    
2890     exit(0);
2891    
2892     # Hey emacs !!
2893     # Local Variables:
2894     # mode: perl
2895     # End:
2896    
2897     ########################################################################
2898     # End of file
2899     ########################################################################

Properties

Name Value
svn:executable *