ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-2.0/scripts/filepp.in
Revision: 2199
Committed: Thu Apr 14 21:41:56 2005 UTC (19 years, 2 months ago) by gezelter
File size: 91185 byte(s)
Log Message:
configure now searches for perl to use with filepp

File Contents

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

Properties

Name Value
svn:executable *