ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/scripts/filepp
Revision: 1556
Committed: Mon Oct 11 21:10:28 2004 UTC (19 years, 8 months ago) by tim
File size: 91180 byte(s)
Log Message:
filepp can be used to generate dependencies for c/c++ now

File Contents

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

Properties

Name Value
svn:executable *