Command File Parser

By: Wout Mertens - Revised: 2006-07-11 devin

Download Script – ZIP-File, 8.5 KB

Introduction

A shell & perl script that parses a command file and handles if/then/else type structures.

Script

create-defines.sh
#!/bin/bash
# Get the user requested defines
# UDEFS is a list of keywords, and is meant to be writeable by your users
# PRE and POSTCMD get added as command files if they exist, allowing
# system defines or system-local command files.
# (note that system-local transcripts won't be able to be downloaded from the
# server though)
DEFDIR=/var/radmind/state
UDEFS=/etc/TACSUNS/flags/user-radmind-defines
PRECMD=$DEFDIR/pre-command.K
POSTCMD=$DEFDIR/post-command.K
DEFAULTS=/var/radmind/client/defines.K
COMMAND=/var/radmind/client/command.K

if [ -n "$1" ]; then
    COMMAND=$1
fi

[ -r $DEFAULTS ] && echo k $DEFAULTS
# Let's assume defines only contain a-zA-Z0-9 and _ or -
[ -r $UDEFS ] && egrep '^#(define|undef) [a-zA-Z0-9_-][a-zA-Z0-9_-]*$' $UDEFS
[ -r $PRECMD ] && echo k $PRECMD
echo k command.K
[ -r $POSTCMD ] && echo k $POSTCMD
exit 0

parse-command.pl
#!/usr/bin/perl
# Parse a command file, creating a new command file
# Features:
# - Consolidates command files
# - Removes duplicate transcripts
# - Allows removing a transcript
# - ifdef statements
# - standard defines: ppc, intel, osx, panther, tiger, leopard, linux, solaris
#
# Syntax of a parseable command file:
#   [p|n|k] [file] : as normal
#   #- [file] : remove a transcript from the list
#   #define [foo] : define a condition named foo
#   #undef [foo]  : delete the condition named foo
#   #ifdef [foo]  : parse this block if foo is defined
#   #else         : otherwise, parse this block
#   #endif        : end of an if block
#   Anything else is ignored
#  !!! Important: If a command file is called defines.K, it is NOT included.
#  This allows you to send along standard defines that can be used by the
#  define stage described below.
#
# Recommended use:
# "standard" radmind sequence is ktcheck, fsdiff, lapply
# this should become ktcheck, define, parse, fsdiff, lapply
# - define stage: Business logic that creates a file that looks like:
#     k defines.K
#     #define foo
#     #undef bar
#     k command.K
#   and saves it as /var/radmind/client/defines.K
#   Note that you could use user preferences to generate this.
# - parse stage: Run this script on /var/radmind/client/defines.K
#   and send the output to /var/radmind/client/parsed.K
# - fsdiff stage should then use parsed.K instead of command.K

use strict;
my %transcripts;
my %type;
my %defines;
my $count=0;

# Standard defines. Only tested on OSX/ppc.
chomp(my $uname = `uname -sp`);
if ($uname) {
    $uname =~ /powerpc/i and $defines{ppc} = 1;
    $uname =~ /intel/i and $defines{intel} = 1;
    if ($uname =~ /darwin/i) {
        $defines{osx} = 1;
        chomp(my $version = `uname -r`);
        $version =~ /^7\./i and $defines{panther} = 1;
        $version =~ /^8\./i and $defines{tiger} = 1;
        $version =~ /^9\./i and $defines{leopard} = 1;
    }
    $uname =~ /linux/i and $defines{linux} = 1;
    $uname =~ /solaris/i and $defines{solaris} = 1;
}

sub test_def {
    my $name = lc(shift);
    warn "Warning: Illegal define: \"$name\". Defines should only contain alphanumerics."
        unless $name =~ /^[a-z0-9_-]+$/;
    return $name;
}

sub read_command {
    my $cmd = shift;
    local *CFILE;
    open(CFILE,"<",$cmd) or die "Can't open $cmd: $!";
    # My Fugly Parser
    my $iflvl=0;
    my $ifshow=0;
    while (my $line = <CFILE>) {
        chomp ($line);
        next unless $line =~ m/^\s*([npk]|#(-|ifdef|else|endif|define|undef))\s*(.*)$/;
        my $type = $1;
        my $name = $3;
        #print "$type $name $iflvl $ifshow\n";
        
        # First handle flow control
        if ($type eq "#endif") {
            $iflvl--;
            $ifshow = 0 if ($ifshow > $iflvl);
        } elsif ($type eq "#else") {
            if ($ifshow) {
                if ($iflvl == $ifshow) {
                    $ifshow = 0;
                }
            } else {
                $ifshow = $iflvl;
            }
        } elsif ($type eq "#ifdef") {
            $iflvl++;
            $name=test_def($name);
            if(!$ifshow && !defined($defines{$name})) {
                $ifshow=$iflvl;
            }
        }
        next if $ifshow && $iflvl >= $ifshow;

        # Now handle the rest
        if ($type eq "k") {
            next if $name eq "defines.K";
            read_command($name);
        } elsif ($type eq "n" or $type eq "p") {
            $transcripts{$name}=$count;
            $type{$name}=$type;
            $count++;
        } elsif ($type eq "#-") {
            delete $transcripts{$name};
        } elsif ($type eq "#define") {
            $name=test_def($name);
            $defines{$name}=1;
        } elsif ($type eq "#undef") {
            $name=test_def($name);
            delete $defines{$name};
        }
    }
    warn "Warning: Unmatched ifdef statement!\n" if ($iflvl);
    close CFILE;
}

my $commandfile = "/var/radmind/client/command.K";
if ($#ARGV > -1) {
    $commandfile = $ARGV[0];
}

if ($commandfile =~ /^(.*)\/([^\/]*)/) {
    my $dir = $1;
    $commandfile = $2;
    chdir($dir) || die "Could not chdir to $dir: $!";
}

# Parse command file
read_command $commandfile;

# Print transcripts that survived parsing, in order
foreach my $key (sort { $transcripts{$a} <=> $transcripts{$b} } keys %transcripts) {
    print "$type{$key} $key\n";
}

exit 0;