ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/scripts/filepp.in
Revision: 1390
Committed: Wed Nov 25 20:02:06 2009 UTC (15 years, 5 months ago) by gezelter
Original Path: trunk/scripts/filepp.in
File size: 91091 byte(s)
Log Message:
Almost all of the changes necessary to create OpenMD out of our old
project (OOPSE-4)

File Contents

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

Properties

Name Value
svn:executable *