#!/usr/bin/env perl # # Copyright(C) Simon Howard # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # # # simple cpp-style preprocessor # # Understands: # # #define NAME # # Set an option # You can use -D on the command line too # # #undef NAME # # Unset an option if it is set # # #if .. #endif / #ifdef .. #endif # # Specify a list of options set, eg #ifdef DOOM2 || ULTDOOM || SHAREWARE # The block is only displayed if one of the options is set # # #ifn .. #endif / #ifndef .. #endif # # Similarly specify a list of options # The block is displayed if none of the options are set # # #include "filename" # # include the contents of a file use strict; my @readstack; my %defines; sub parse_cmdline { foreach (@ARGV) { if (/^-D/) { my ($define) = /^-D(.*)$/; $defines{$define} = 1; } } } # add contents of stdin to read stack sub read_stdin { my @lines = ; chomp @lines; @readstack = (@readstack, reverse(@lines)); } # add contents of a file to stack sub read_file { my ($filename) = @_; open(FILE, $filename) or die "cant open $filename: $!"; my @lines = ; close(FILE); chomp @lines; @readstack = (@readstack, reverse(@lines)); } # recursive block reading function # if 'ignore' argument is 1, contents are ignored sub readblock { my ($ignore) = @_; while (scalar @readstack > 0) { $_ = pop @readstack; next if (/^\s*$/); if (/^\#include\s+\".*\"\s*$/ ) { if (!$ignore) { my ($filename) = /^\#include\s+\"(.*)\"\s*/; read_file $filename; } } elsif (/^\#define\s/ ) { if (!$ignore) { my ($name) = /^\#define\s*(\w+)/; $defines{$name} = 1; } } elsif (/^\#undef\s/ ) { if (!$ignore) { my ($name) = /^\#undef\s*(\w+)/; $defines{$name} = undef; } } elsif (/^\#(if|ifdef|ifn|ifndef)\s/) { my ($type, $defines) = /^\#(\w+)\s+(.*)/; $defines =~ s/\s*$//; my @definelist = split(/\s*\|\|\s*/, $defines); my $ev; if ($type =~ /^(if|ifdef)$/) { $ev = 0; foreach (@definelist) { $ev = 1 if $defines{$_}; } } elsif ($type =~ /^(ifn|ifndef)$/) { $ev = 1; foreach (@definelist) { $ev = 0 if $defines{$_}; } } else { die "what type?"; } my $result = readblock($ignore || !$ev); die if $result == -1; if ($result == 1) { # block ended on an else # call again for the second block my $result = readblock($ignore || $ev); die if $result != 0; } } elsif (/^\#endif/) { return 0; } elsif (/^\#else/) { return 1; } elsif (/^\#/) { die "invalid # command"; } else { print "$_\n" if !$ignore; } } return -1; } parse_cmdline; read_stdin; die if (readblock(0) != -1);