ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/scripts/filepp
Revision: 1519
Committed: Sat Oct 2 04:40:48 2004 UTC (19 years, 9 months ago) by tim
File size: 88098 byte(s)
Log Message:
remove sfmakedepend from cvs tree

File Contents

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

Properties

Name Value
svn:executable *