diff --git a/src/Makefile b/src/Makefile index b37ae74..1e29d4a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -22,7 +22,7 @@ false: true install: all install -Dm0755 ${BIN} ${DESTDIR}/${PREFIX}/bin -clean: all +clean: rm ${BIN} *.o check: diff --git a/src/support.h b/src/support.h index c259a62..1b60775 100644 --- a/src/support.h +++ b/src/support.h @@ -88,3 +88,34 @@ char *mastrcat(char *str1, char *str2) return nbuf; } +/* open stdin if path is null */ +FILE *file_open(char *path, char *mode) + { + FILE *fd; + + fd = 0; + + + if(!mode) + { + throw(MISC_FAIL, "file_open: mode is null. Terminating."); + } + + if(path == NULL || !strcmp(path, "-")) + { + fd = fopen("/dev/stdin", mode); + } + else + { + fd = fopen(path, mode); + } + + if(!fd) + { + throw(FOPEN_FAIL, mastrcat("file_open: couldn't open ", mastrcat(path, mastrcat(" using mode ", mode)))); + } + + + return(fd); + } + diff --git a/src/wc.c b/src/wc.c index 267b5aa..fb2b343 100644 --- a/src/wc.c +++ b/src/wc.c @@ -9,117 +9,184 @@ #include "support.h" +/* Parameters */ enum { - C, - M, - L, W, + L, + CM + }; + +/* States */ +enum { OUTWORD, - INWORD, + INWORD + }; + +/* Category names for count[] and result[] */ +enum { /* character or byte */ - COB = 0, - WORDS = 1, - LINES = 2, + LINES, + WORDS, + COB }; -int main(int argc, char **argv) +int wc(FILE *fd, int *results) { char state; - int c, i; - - char par[4] = {1}; - /* M and C conflict; M counts actual characters (theoretically), so we should go with that as the default */ - par[C] = 0; - int count[3] = {0}; - int total[3] = {0}; - FILE *fd; + int c; state = OUTWORD; - c = i = 0; + c = 0; + + for(c = 0; (c = fgetc(fd)) != EOF;) + { + if(!(isblank(c) || c == '\n')) + { + if(state == OUTWORD) + { + results[WORDS]++; + } + + state = INWORD; + } + else + { + if(c == '\n') + { + results[LINES]++; + } + + state = OUTWORD; + } + + results[COB]++; + } + + + return(0); + } + +/* if a particular pars element is non-zero, print the corresponding total element. */ +/* if pars is NULL, print everything */ +/* if total is NULL, throw an error */ +int printres(int *total, char *filepath, char *pars) + { + int i; + + i = 0; + + + if(!total) + { + throw(MISC_FAIL, "printres: total is null"); + } + + + for(i = 0; i <= 2; i++) + { + if(pars == NULL || pars[i]) + { + printf("%d ", total[i]); + } + } + + if(filepath) + { + if(!strcmp("-", filepath)) + { + printf("stdin"); + } + else + { + printf("%s", filepath); + } + } + putchar('\n'); + + + return(0); + } + +int main(int argc, char **argv) + { + /* haspar == 0 means we pass NULL to printres for default POSIX behavior */ + char haspar; + int c, i; + int current[3] = {0}; + int results[3] = {0}; + + char pars[3] = {0}; + FILE *fd; + + haspar = 0; + /* c value is arbitrary but is non-NULL for safety reasons; i is used to ensure the program does not open itself */ + c = i = 1; + fd = 0; - + /* process our parameters */ for(; (c = getopt(argc, argv, "cmlw")) != -1; c = 0) { switch(c) { - case 'c': par[C] = 1; - par[M] = 0; + case 'c': pars[CM] = 1; + haspar = 1; break; - case 'm': par[M] = 1; - par[C] = 0; + case 'm': pars[CM] = -1; + haspar = 1; break; - case 'l': par[L] = 1; + case 'l': pars[L] = 1; + haspar = 1; break; - case 'w': par[W] = 1; + case 'w': pars[W] = 1; + haspar = 1; break; /* Fall-through to default from ':' */ case ':': case '?': - default: throw(NEEDARG_FAIL, mastrcat(argv[0], "[-c|-m] [-lw] [file ...]")); + default: throw(NEEDARG_FAIL, mastrcat(argv[0], " [-c|-m] [-lw] [file ...]")); break; } } + i = optind; - - - /* for every file we're given... */ - for(fd = 0, i = optind; i < argc; fclose(fd), i++) + /* do-while allows easy opening of stdin */ + do { - /* ...open our file... */ - if( !(fd = fopen(argv[i], "r"))) - { - printf("%d %d\n", i, optind); - exit(1); - } - - /* ...then read until EOF... */ - for(c = 0; (c = fgetc(fd)) != EOF;) - { - /* ...and count the goods as we go. */ - if(!(isblank(c) || c == '\n')) - { - if(state == OUTWORD) - { - count[WORDS]++; - } - - state = INWORD; - } - else - { - if(c == '\n') - { - count[LINES]++; - } - - state = OUTWORD; - } - - count[COB]++; - } - - /* ...then print our total and reset. */ - printf("%d %d %d %s\n", count[LINES], count[WORDS], count[COB], argv[i]); + /* open file */ + fd = file_open(argv[i], "r"); - total[COB] += count[COB]; - total[WORDS] += count[WORDS]; - total[LINES] += count[LINES]; + /* process file */ + wc(fd, current); - count[COB] = 0; - count[WORDS] = 0; - count[LINES] = 0; - } + /* print results */ + if(haspar) + { + printres(current, argv[i], pars); + } + else + { + printres(current, argv[i], NULL); + } + + /* add results to global count */ + results[WORDS] += current[WORDS]; + results[LINES] += current[LINES]; + results[COB] += current[COB]; + +/* reset count and file descriptor */ + current[WORDS] = 0; + current[LINES] = 0; + current[COB] = 0; - - if((argc-optind) > 1) - { - printf("%d %d %d total\n", total[LINES], total[WORDS], total[COB]); + fclose(fd); } + /* this would normally be a pre-increment, but that can be confusing, so we use a more explicit approach here */ + while((i+=1) < argc); exit(0);