ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/scripts/filepp
Revision: 1515
Committed: Fri Oct 1 21:11:29 2004 UTC (19 years, 9 months ago) by tim
File size: 88664 byte(s)
Log Message:
adding fortran90 make dependency tool

File Contents

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

Properties

Name Value
svn:executable *