ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/scripts/filepp.in
Revision: 1465
Committed: Fri Jul 9 23:08:25 2010 UTC (14 years, 9 months ago) by chuckv
File size: 91091 byte(s)
Log Message:
Creating busticated version of OpenMD

File Contents

# Content
1 #!@PERLINTERP@ -w
2 ########################################################################
3 #
4 # filepp is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; see the file COPYING. If not, write to
16 # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17 #
18 ########################################################################
19 #
20 # Project : File Preprocessor
21 # Filename : $RCSfile: filepp.in,v $
22 # Author : $Author: gezelter $
23 # Maintainer : Darren Miller: darren@cabaret.demon.co.uk
24 # File version : $Revision: 1.2 $
25 # Last changed : $Date: 2009-11-25 20:01:56 $
26 # Description : Main program
27 # Licence : GNU copyleft
28 #
29 ########################################################################
30
31 package Filepp;
32
33 use strict "vars";
34 #use strict "subs";
35 # Used to all filepp to work with any char, not just ascii,
36 # feel free to remove this if it causes you problems
37 #use bytes;
38
39 # version number of program
40 my $VERSION = '1.7.1';
41
42 # list of paths to search for modules, normal Perl list + module dir
43
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 *