1########################################################################
2#
3# remover is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; see the file COPYING.  If not, write to
15# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16#
17########################################################################
18#
19#  Project      :  File Preprocessor - remover module
20#  Filename     :  $RCSfile: remover.pm,v $
21#  Author       :  $Author: darren $
22#  Maintainer   :  Darren Miller: darren@cabaret.demon.co.uk
23#  File version :  $Revision: 1.2 $
24#  Last changed :  $Date: 2005/09/01 17:37:20 $
25#  Description  :  This module removes all code between #ifdef and #if
26#                  statements for a predifined set of macros.  Useful
27#                  for permanently removing uneeded bits of code.
28#  Licence      :  GNU copyleft
29#
30########################################################################
31# THIS IS A FILEPP MODULE, YOU NEED FILEPP TO USE IT!!!
32# usage: edit the list of macros below then do:
33# filepp -m remover.pm <files>
34########################################################################
35
36package Remover;
37
38use strict;
39
40# version number of module
41my $VERSION = '0.1.0';
42
43# list of macros to be removed
44# enter macro as '1' if it should be initially defined
45# enter macro as '0' if it should be initially undefined
46my %Macros = ();
47
48# "if", "else", "endif" keywords - should match set you are using,
49# defaults to standard filepp/cpp list
50my %Ifwords = ('if',     '',
51	       'ifdef',  '',
52	       'ifndef', '');
53my %Elsewords = ('else', '',
54		 'elif', '');
55my %Endifwords = ('endif', '');
56
57# turn off keyword processing - will be enabled only when needed
58Filepp::RemoveProcessor("Filepp::ParseKeywords");
59# turn off all macro processing
60Filepp::RemoveProcessor("Filepp::ReplaceDefines");
61
62my $keywordchar = Filepp::GetKeywordchar();
63
64
65# keywords which may or may not be removed
66my %Keywords = (
67		'define'  => \&Define,
68		'elif'    => \&Elif,
69		'else'    => \&Else,
70		'endif'   => \&Endif,
71		'if'      => \&If,
72		'ifdef'   => \&Ifdef,
73		'ifndef'  => \&Ifndef,
74		'remove'  => \&Remove,
75		'undef'   => \&Undef,
76		);
77
78
79# counter for number of #if[n][def] loops currently in
80my $iflevel = 0;
81# flag to control when to write output
82my @Writing = (1); # initialise default to 'writing'
83# flag to show if current 'if' block has passed a 'true if'
84my @Ifdone = (0); # initialise first to 'not passed true if'
85
86# flag to say if keyword with removable macros found
87my @RemoveLine = (0);
88my $remove_line = 0;
89
90# function to set macros for removal
91sub Remove
92{
93    my $macrodefn = shift;
94    my $macro;
95    my $defn;
96    my $i;
97
98    # check there is an argument
99    if($macrodefn !~ /\S/o) {
100	Filepp::Error("remove keyword used without arguments");
101    }
102
103    # find end of macroword - assume separated by space or tab
104    $i = Filepp::GetNextWordEnd($macrodefn);
105
106    # separate macro and defn (can't use split, doesn't work with '0')
107    $macro = substr($macrodefn, 0, $i);
108    $defn  = substr($macrodefn, $i);
109
110    # strip leading whitespace from $defn
111    if($defn) {
112	$defn =~ s/^[ \t]*//;
113    }
114    else {
115	$defn = "";
116    }
117    $remove_line = 1;
118    $Macros{$macro} = $defn;
119    if($defn) {
120	Filepp::Debug("Remove: $macro added for removal defined=$defn");
121	Filepp::Define($macrodefn);
122    }
123    else {
124	Filepp::Debug("Remove: $macro added for removal undefined");
125	Filepp::Undef($macro);
126    }
127}
128
129
130# remove #define for all defines of unwanted macro
131sub Define
132{
133    my $macrodefn = shift;
134    $macrodefn =~ /^\s*\w+\b/;
135    my $macro = $&;
136    if(exists($Macros{$macro})) {
137        Filepp::Debug("Remove: removing define <$macro>");
138	$remove_line = 1;
139    }
140    else { Filepp::Define($macrodefn); }
141}
142
143# remove #undef for all undefs of unwanted macro
144sub Undef
145{
146    my $macro = shift;
147    if(exists($Macros{$macro})) {
148        Filepp::Debug("Remove: removing undef <$macro>");
149	$remove_line = 1;
150    }
151    else { Filepp::Undef($macro); }
152}
153
154# remove if statements with unwanted conditional in, ignore rest
155sub If
156{
157    my $expr = shift;
158    my $macro;
159    my $found = 0;
160    foreach $macro (keys(%Macros)) {
161	if($expr=~ /\b$macro\b/) { $found = 1; }
162    }
163    if($found) {
164	Filepp::Debug("Remove: removing if line <$expr>");
165	$RemoveLine[$iflevel] = 1;
166	$remove_line = 1;
167	return Filepp::If($expr);
168    }
169    else { $RemoveLine[$iflevel] = 0; }
170    return 1;
171}
172
173# elif is not fully supported, prints warning out to for by hand removal
174sub Elif
175{
176    my $expr = shift;
177    my $macro;
178    foreach $macro (keys(%Macros)) {
179	if($expr=~ /\b$macro\b/) {
180	    Filepp::Warning("Remove: #elif removal not fully supported, remove by hand");
181	}
182    }
183    if($RemoveLine[$iflevel]) {
184	$remove_line = 1;
185	return Filepp::Elif($expr);
186    }
187    return 1;
188}
189
190# remove ifdef statements with unwanted conditional in, ignore rest
191sub Ifdef
192{
193    my $macro = shift;
194    # separate macro from any trailing garbage
195    $macro = substr($macro, 0, Filepp::GetNextWordEnd($macro));
196    if(exists($Macros{$macro})) {
197	Filepp::Debug("Remove: removing Ifdef line <$macro>");
198	$RemoveLine[$iflevel] = 1;
199	$remove_line = 1;
200	return Filepp::Ifdef($macro);
201    }
202    else { $RemoveLine[$iflevel] = 0; }
203    return 1;
204}
205
206
207# remove ifndef statements with unwanted conditional in, ignore rest
208sub Ifndef
209{
210    my $macro = shift;
211    # separate macro from any trailing garbage
212    $macro = substr($macro, 0, Filepp::GetNextWordEnd($macro));
213    if(exists($Macros{$macro})) {
214	Filepp::Debug("Remove: removing Ifndef line <$macro>");
215	$RemoveLine[$iflevel] = 1;
216        $remove_line = 1;
217        return Filepp::Ifndef($macro);
218    }
219    else { $RemoveLine[$iflevel] = 0; }
220    return 1;
221}
222
223sub Else
224{
225    $remove_line = $RemoveLine[$iflevel];
226    return 1;
227}
228
229sub Endif
230{
231    $remove_line = $RemoveLine[$iflevel];
232    return 1;
233}
234
235##############################################################################
236# Keyword parsing routine
237##############################################################################
238sub ParseKeywords
239{
240    # input is next line in file
241    my $inline = shift;
242    my $outline = "";
243
244    my $thisline = $inline;
245    my $keyword;
246
247    # remove whitespace from start of line
248    $thisline = Filepp::CleanStart($thisline);
249    # check if first char on line is a #
250    if($thisline && $thisline =~ /^$keywordchar/) {
251	# remove "#" and any following whitespace
252	$thisline =~ s/^$keywordchar\s*//g;
253	# check for keyword
254	if($thisline && $thisline =~ /^\w+\b/ && exists($Keywords{$&})) {
255	    $keyword = $&;
256	    # remove newline from line
257	    chomp($thisline);
258	    # remove leading whitespace and keyword from line
259	    my $inline = Filepp::CleanStart(substr($thisline,
260						   length($keyword)));
261
262	    # check for 'if' style keyword
263	    if(exists($Ifwords{$keyword})) {
264		# increment ifblock level and set ifdone to same
265		# value as previous block
266		$iflevel++;
267		$Ifdone[$iflevel] = 0;
268		$Writing[$iflevel] = $Writing[$iflevel - 1];
269		$RemoveLine[$iflevel] = $RemoveLine[$iflevel - 1];
270		if(!$Writing[$iflevel]) { $Ifdone[$iflevel] = 1; }
271	    }
272	    # check for out of place 'else' or 'endif' style keyword
273	    elsif($iflevel <= 0 && (exists($Elsewords{$keyword}) ||
274				    exists($Endifwords{$keyword}) )) {
275		Filepp::Warning($keywordchar.$keyword.
276				" found without preceding ".$keywordchar.
277				"[else]ifword");
278	    }
279
280	    # decide if to run 'if' or 'else' keyword
281	    if(exists($Ifwords{$keyword}) || exists($Elsewords{$keyword})){
282		# run keyword to set RemoveLine
283		my $value = $Keywords{$keyword}->($inline);
284		if($RemoveLine[$iflevel] && !$Ifdone[$iflevel]) {
285		    # check return value of 'if'
286		    if($value) {
287			$Ifdone[$iflevel] = 1;
288			$Writing[$iflevel] = 1;
289		    }
290		    else { $Writing[$iflevel] = 0; }
291		}
292		elsif($RemoveLine[$iflevel] || $Ifdone[$iflevel]) {
293		    $Writing[$iflevel] = 0;
294		}
295		else { $Writing[$iflevel] = 1; }
296	    }
297	    # check for 'endif' style keyword
298	    elsif(exists($Endifwords{$keyword})) {
299		# run endif keyword and decrement iflevel if true
300		if($Keywords{$keyword}->($inline)) { $iflevel--; }
301	    }
302	    # run all other keywords
303	    elsif($Writing[$iflevel] || $RemoveLine[$iflevel] == 0) {
304		$Keywords{$keyword}->($inline);
305	    }
306
307	} # keyword if statement
308    }
309    # no keywords in line - write line to file if not #ifdef'ed out
310    if($remove_line || !$Writing[$iflevel]) {
311	# do nothing
312    }
313    else {
314	$outline = $outline.$inline;
315    }
316
317    $remove_line = 0;
318    return $outline;
319}
320Filepp::AddProcessor("Remover::ParseKeywords");
321
322
323return 1;
324
325########################################################################
326# End of file
327########################################################################
328