diff --git a/pscat/Makefile b/pscat/Makefile index 0436451..7ed88ab 100644 --- a/pscat/Makefile +++ b/pscat/Makefile @@ -1,28 +1,22 @@ -all: pscat +CFLAGS = -g +RM = rm -f +TARGETS = pscat + +all: $(TARGETS) clean: - rm -rf ../dist/pscat ../dist/pscat.tar ../dist/pscat.tar.gz pscat + $(RM) $(TARGETS) -dist: ../dist/pscat.tar.gz +debug: + $(CC) $(DEBUG) -o pscat pscat.c -sane: pscat.c ../include/sysexits.h - $(CC) -DUSE_ASCII_H -DDONT_USE_SYSTEM_SYSEXITS -o pscat pscat.c +%.o: %.c %.h + $(CC) $(CFLAGS) -c -o %.o %.c -pscat: pscat.c - $(CC) -o pscat pscat.c +pscat.o: psargs.h pscat.c + $(CC) $(CFLAGS) -DUSE_ASCII_H -DDONT_USE_SYSTEM_SYSEXITS -c -o pscat.o pscat.c -../dist/pscat: pscat - mkdir -p ../dist/pscat.tmp/bin/ ../dist/pscat.tmp/share/man/man1/ - cp pscat ../dist/pscat.tmp/bin/pscat - cp pscat.1 ../dist/pscat.tmp/share/man/man1/pscat.1 - mv ../dist/pscat.tmp ../dist/pscat +pscat: pscat.o psargs.o + $(CC) $(CFLAGS) -o pscat psargs.o pscat.o -../dist/pscat.tar: ../dist/pscat - cd ../dist/pscat && pax -w -x ustar . >../pscat.tar.tmp - mv ../dist/pscat.tar.tmp ../dist/pscat.tar - -../dist/pscat.tar.gz: ../dist/pscat.tar - gzip -c <../dist/pscat.tar >../dist/pscat.tar.gz.tmp - mv ../dist/pscat.tar.gz.tmp ../dist/pscat.tar.gz - -.PHONY: all clean sane +.PHONY: all clean diff --git a/pscat/psargs.c b/pscat/psargs.c new file mode 100644 index 0000000..06d3974 --- /dev/null +++ b/pscat/psargs.c @@ -0,0 +1,56 @@ +#include + +#include "psargs.h" + +/* Test string containing { c, '\0' } without iteration. + * Theoretically saves a little bit of time compared to strcmp(3). */ +#define SCMPFLAT(a, b) (*(a) != '\0' && *((a)+1) == '\0' && *(a) == (b)) + +int +check_arg(char **args, char l, char r){ + enum { + UNINITIALIZED = 0, + NORMAL = 1, + INLPAREN = 2 + } s; + int terms; + + for(s = UNINITIALIZED, terms = 0; *args != NULL; ++args) + switch(s){ + case UNINITIALIZED: case NORMAL: + if(SCMPFLAT(*args, l)) + s = INLPAREN; + else + return 0; /* syntax error */ + break; + default: /* >= INLPAREN */ + /* decrement s if on an rparen, increment if on lparen + * increment terms if on an rparen */ + if(args[0][1] == '\0'){ + s += (args[0][0] == l) - (args[0][0] == r); + terms += (args[0][0] == r && s == NORMAL); + } + break; + } + + /* make sure nothing's open */ + return terms * (s == NORMAL); +} + +char ** +corresponding_arg(char **arg, char l, char r){ + size_t p; + + for(p = 1; p > 0;){ + ++arg; + /* branching here potentially saves a comparison. + * this seems like the most optimal way to do this, + * maybe it isn't, i don't care too much */ + if(SCMPFLAT(*arg, l)) + ++p; + else if(SCMPFLAT(*arg, r)) + --p; + } + + return arg; +} diff --git a/pscat/psargs.h b/pscat/psargs.h new file mode 100644 index 0000000..2ca4600 --- /dev/null +++ b/pscat/psargs.h @@ -0,0 +1,22 @@ +#if !defined _PSARGS_H +# define _PSARGS_H + +/* pscat(1) and pspipe(1) arguments are flanked by { '[', '\0' } and + * { ']', '\0' }. Nesting is allowed to facilitate nesting of pscat(1) and + * pspipe(1). This checks to make sure grouping symbols in args are balanced. + * args is the argv that was passed to main, incremented. l is the left + * grouping character (usually '[') and r is the right grouping character + * (usually ']'). + * Returns 0 in the events of a syntax error or there being no arguments. + * Otherwise, returns the number of top-level right grouping arguments + * (i.e. the number of terms). */ +int check_arg(char **args, char l, char r); + +/* arg is the location of the argument with the left grouping symbol within + * argv, l is the left grouping character itself, r is the right grouping + * character itself. + * Returns the location of the argument with the corresponding left grouping + * symbol within argv. */ +char **corresponding_arg(char **arg, char l, char r); + +#endif diff --git a/pscat/pscat.c b/pscat/pscat.c index c8be6a0..2ab41bb 100644 --- a/pscat/pscat.c +++ b/pscat/pscat.c @@ -1,12 +1,14 @@ +#include +#include +#include + #ifndef DONT_USE_SYSTEM_SYSEXITS # include #else -# include "../include/sysexits.h" +# include "../sysexits/sysexits.h" #endif /* ifndef DONT_USE_SYSTEM_SYSEXITS */ -#include -#include -#include +#include "psargs.h" static char *program_name = "pscat"; @@ -16,56 +18,21 @@ static char *program_name = "pscat"; # define L_PAREN '[' # define R_PAREN ']' #else -# include "../include/ascii.h" +# include "../ascii/ascii.h" # define L_PAREN ASCII_LEFT_SQUARE_BRACKET # define R_PAREN ASCII_RIGHT_SQUARE_BRACKET #endif /* ifndef USE_ASCII_H */ -/* Test string containing { c, '\0' } without iteration. - * Theoretically saves a little bit of time compared to strcmp(3). */ -#define SCMPFLAT(a, b) (*(a) != '\0' && *((a)+1) == '\0' && *(a) == (b)) - -/* Verifies arguments to pscat are sensible. */ -static int -check_arg(char **argv){ - enum { - UNINITIALIZED = 0, - NORMAL = 1, - INLPAREN = 2 - } s; - int terms; - - for(s = UNINITIALIZED, terms = 0; *argv != NULL; ++argv) - switch(s){ - case UNINITIALIZED: case NORMAL: - if(SCMPFLAT(*argv, L_PAREN)) - s = INLPAREN; - else - return 0; /* syntax error */ - break; - default: /* >= INLPAREN */ - if(argv[0][1] == '\0'){ - s -= (argv[0][0] == R_PAREN); - terms += (argv[0][0] == R_PAREN && s == NORMAL); - s += (argv[0][0] == L_PAREN); - } - break; - } - - return terms; -} - int main(int argc, char *argv[]){ char **psstart; int child; int i; - int p; int retval; int terms; retval = 0; - if((terms = check_arg(++argv)) == 0){ + if((terms = check_arg(++argv, L_PAREN, R_PAREN)) == 0){ fprintf(stderr, "Usage: %s \"[\" [utility [argument...]] \"]\" ...\n", argv[0] == NULL ? program_name : argv[0] @@ -75,18 +42,8 @@ int main(int argc, char *argv[]){ /* loop starts with *argv -> the next L_PAREN */ for(i = 0; i < terms; ++i){ - psstart = ++argv; - p = 1; - while(p > 0){ - ++argv; - /* branching here potentially saves a comparison. - * this seems like the most optimal way to do this, - * maybe it isn't, i don't care too much */ - if(SCMPFLAT(*argv, L_PAREN)) - ++p; - else if(SCMPFLAT(*argv, R_PAREN)) - --p; - } + psstart = argv + 1; + argv = corresponding_arg(argv, L_PAREN, R_PAREN); /* *argv -> the corresponding R_PAREN. turn it into NULL to * terminate the argument list to send to execvp(3) */ *argv = NULL;