diff options
author | zlg <zlg@zlg.space> | 2015-03-03 03:35:37 -0800 |
---|---|---|
committer | zlg <zlg@zlg.space> | 2015-03-03 03:35:37 -0800 |
commit | e458edffa1e1cb3a7aa2c695e833e717bbd4efe4 (patch) | |
tree | da21a832a84895d5ab4557bf1a9ea02da2a7e69f /ch6 | |
parent | Solve Exercise 5-20: Expanded `dcl` (diff) | |
download | knr-e458edffa1e1cb3a7aa2c695e833e717bbd4efe4.tar.gz knr-e458edffa1e1cb3a7aa2c695e833e717bbd4efe4.tar.bz2 knr-e458edffa1e1cb3a7aa2c695e833e717bbd4efe4.tar.xz knr-e458edffa1e1cb3a7aa2c695e833e717bbd4efe4.zip |
Solve Exercise 6-01: Enhanced `getword`
This exercise improved my understanding of identifying comments and
quotes by focusing on edge cases.
Diffstat (limited to 'ch6')
-rw-r--r-- | ch6/6-01_getword-enhanced.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/ch6/6-01_getword-enhanced.c b/ch6/6-01_getword-enhanced.c new file mode 100644 index 0000000..c040d1e --- /dev/null +++ b/ch6/6-01_getword-enhanced.c @@ -0,0 +1,170 @@ +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +/* The C Programming Language: 2nd Edition + * + * Exercise 6-1: Our version of `getword` does not properly handle underscores, + * string constants, comments, or preprocessor control lines. Write a better + * version. + * + * Notes: Preprocessor lines and one-line comments should be treated the same by + * ignoring everything after the beginning. Underscores need to be treated like + * part of the word, otherwise identifiers such as 'if_else' will get added to + * the list. Anything inside quotes and multi-line comments needs to be ignored, + * with a state variable to help us figure out what's going on. + */ + + +#define MAXWORD 100 +#define BUFSIZE 1000 + +struct key { + char *word; + int count; +} keytab[] = { + "auto", 0, + "break", 0, + "case", 0, + "char", 0, + "const", 0, + "continue", 0, + "default", 0, + "else", 0, + "enum", 0, + "for", 0, + "if", 0, + "int", 0, + "long", 0, + "return", 0, + "short", 0, + "signed", 0, + "static", 0, + "struct", 0, + "switch", 0, + "typedef", 0, + "unsigned", 0, + "void", 0, + "volatile", 0, + "while", 0 +}; + +#define NKEYS (sizeof keytab / sizeof(struct key)) + +char buf[BUFSIZE]; +int bufp = 0; +enum states { + NORMAL, + INQUOTE, + INCOMMENT +}; +int state = NORMAL; + +int getch(void); +void ungetch(int); +int getword(char *, int); +int binsearch(char *, struct key *, int); + +int main() { + int n; + char word[MAXWORD]; + while (getword(word, MAXWORD) != EOF) { + if (isalpha(word[0])) { + if ((n = binsearch(word, keytab, NKEYS)) >= 0) { + keytab[n].count++; + } + } + } + for (n = 0; n < NKEYS; n++) { + if (keytab[n].count > 0) { + printf("%4d %s\n", keytab[n].count, keytab[n].word); + } + } + return 0; +} + +int getch(void) { + return (bufp > 0) ? buf[--bufp] : getchar(); +} + +void ungetch(int c) { + if (bufp >= BUFSIZE) { + printf("ungetch: Too many characters.\n"); + } else { + buf[bufp++] = c; + } +} + +int binsearch(char *word, struct key tab[], int n) { + int cond; + int low, high, mid; + low = 0; + high = n - 1; + while (low <= high) { + mid = (low + high) / 2; + if ((cond = strcmp(word, tab[mid].word)) < 0) { + high = mid - 1; + } else if (cond > 0) { + low = mid + 1; + } else { + return mid; + } + } + return -1; +} + +int getword(char *word, int lim) { + int c; + char *w = word; + while (isspace(c = getch())) { + } + if (c != EOF && c != '#' && c != '"' && c != '/' && c != '*') { + *w++ = c; + } + if (c == '*' && state == INCOMMENT) { + if ((c = getch()) == '/') { + state == NORMAL; + return '/'; + } + } + /* Ignore comments */ + if (c == '/') { + c = getch(); + if (c == '/') { + while ((c = getch()) != EOF && c != '\n') { + } + return '*'; + } + if (c == '*') { + state = INCOMMENT; + return '*'; + } + } + /* Handle quotes */ + if (c == '"') { + state = INQUOTE; + } + if (state == INQUOTE) { + while ((c = getch()) != '"' && c != EOF) { + } + state = NORMAL; + } + /* Ignore preprocessor lines */ + if (c == '#') { + while ((c = getch()) != '\n' && c != EOF) { + } + } + /* Add exceptions for underscores */ + if (!isalpha(c) && c != '_') { + *w = '\0'; + return c; + } + for ( ; --lim > 0; w++) { + if (!isalnum(*w = getch()) && *w != '_') { + ungetch(*w); + break; + } + } + *w = '\0'; + return word[0]; +} |