#!/usr/bin/perl # Configuration file and script preprocessor # (c) 2004--2007 Martin Mares use strict; use warnings; @ARGV == 3 or die "Usage: genconf "; open CF, $ARGV[2] or die "Unable to open $ARGV[2]"; my %options = (); my %vars = (); sub opt { my ($k,$v) = @_; $vars{$k} = $v; $options{$k} = 1 if ($k =~ /^CONFIG_/); } foreach my $k (keys %ENV) { opt($k, $ENV{$k}); } while () { chomp; if (my ($k,$v) = /^(\w+)=(.*)/) { $v =~ s/\s+$//; opt($k, $v); } } close CF; sub eval_expr { $_ = shift @_; s/\b(CONFIG_\w+)\b/defined($options{$1}) ? 1 : 0/ge; return eval $_; } open IN, $ARGV[0] or die "Unable to open $ARGV[0]"; open OUT, ">$ARGV[1]" or die "Unable to create $ARGV[1]"; my @ifs = (); # stack of conditions, 1=satisfied, -1=unsatisfied, 0=shadowed my $empty = 0; # last line was empty while () { if (/^#ifdef\s+(\w+)/) { push @ifs, (@ifs && $ifs[$#ifs] <= 0) ? 0 : (defined $options{$1}) ? 1 : -1; } elsif (/^#ifndef\s+(\w+)/) { push @ifs, (@ifs && $ifs[$#ifs] <= 0) ? 0 : (defined $options{$1}) ? -1 : 1; } elsif (/^#if\s(.*)$/) { push @ifs, (@ifs && $ifs[$#ifs] <= 0) ? 0 : (eval_expr $1) ? 1 : -1; } elsif (/^#endif/) { defined pop @ifs || die "Improper nesting of conditionals"; } elsif (/^#else/) { my $x = pop @ifs; defined $x || die "Improper nesting of conditionals"; push @ifs, $x >= 0 ? 0 : 1; } elsif (/^#elsif\s(.*)$/) { my $x = pop @ifs; defined $x || die "Improper nesting of conditionals"; push @ifs, $x >= 0 ? 0 : (eval_expr $1) ? 1 : -1; } else { @ifs && $ifs[$#ifs] <= 0 && next; if (/^$/) { $empty && next; $empty = 1; } else { $empty = 0; } if (/^#pipe\s+(.+)/) { my $cmd = $1; my $val = `$cmd`; die "Piped command '$cmd' failed" if $?; print OUT `$1`; } else { sub repl ($) { my $v = shift @_; exists $vars{$v} or die "Cannot substitute $v: variable not set"; return $vars{$v}; } s/@(\w+)@/repl($1)/ge; print OUT; } } } @ifs && die "Unterminated #ifdef"; close IN; close OUT;