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

File Contents

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

Properties

Name Value
svn:executable *