/* EXERCISE 6-6 */ /* NOTE: NO ALLOWANCE IS MADE FOR CONTINUED #define LINES. */ /* IF EOF IS ENCOUNTERED WHILE REPLACEMENT TEXT IS BEING */ /* BUILT, THERE IS NO NEED TO LOAD THE TEXT INTO THE TABLE. */ /* NO TEXT TO BE REPLACED WILL FOLLOW. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> enum blank {SPACE = ' ', FORMFEED = '\f', RETURN = '\r', TAB = '\t', VTAB = '\v'}; struct nlist { /* TABLE ENTRY */ struct nlist *next; /* NEXT ENTRY IN CHAIN */ char *name; /* DEFINED NAME */ char *defn; /* REPLACEMENT TEXT */ }; #define MAXWORD 100 #define HASHSIZE 101 int blkcnt; int bldtext = 0; static struct nlist *hashtab[HASHSIZE]; /* POINTER TABLE */ struct nlist *install(char *, char *); struct nlist *lookup(char *); unsigned hash(char *); char *strdup(const char *); int getword(char *, int); main() { int i, c, first; int procline = 0; int bldname = 0; char *cp; char word[MAXWORD]; char defname[MAXWORD]; char deftext[MAXWORD]; struct nlist *np; for (i = 0; i < HASHSIZE; i++) /* INIT hashtab */ hashtab[i] = NULL; while ((c = getword(word, MAXWORD)) != EOF) { if (c == '\n') { /* LOAD REPLACEMENT TEXT */ if (bldtext && deftext[0] != '\0') if (install(defname, deftext) == NULL) { printf("NOT ENOUGH SPACE AVAILABLE FOR LIST\n"); return 1; } procline = bldname = bldtext = 0; } else if (procline) { procline = 0; if (strcmp(word, "define") == 0) bldname = 1; else if ((np = lookup(word)) != NULL) /* FIND REPLACEMENT TEXT */ strcpy(word, np->defn); } else if (bldname) { bldname = 0; strcpy(defname, word); /* LOAD NAME */ deftext[0] = '\0'; /* INIT REPLACEMENT TEXT */ bldtext = first = 1; } else /* BUILD REPLACEMENT TEXT FROM */ if (bldtext) { /* CONCATENATED WORDS AND SPACES */ if (blkcnt > 0 && !first) { cp = deftext + strlen(deftext); for (i = 1; i <= blkcnt; i++) *cp++ = ' '; *cp = '\0'; } first = 0; strcat(deftext, word); } else if (c == '#') procline = 1; else if ((np = lookup(word)) != NULL) /* FIND REPLACEMENT TEXT */ strcpy(word, np->defn); printf("%s", word); /* SEND WORD TO OUTPUT */ } printf("\nEND OF PROGRAM\n"); return 0; } /* INSTALL: PUT (NAME, DATA) IN HASHTAB */ struct nlist *install(char *name, char *defn) { struct nlist *np; unsigned hashval; if ((np = lookup(name)) == NULL) { /* NOT FOUND */ np = (struct nlist *) malloc(sizeof(*np)); if (np == NULL || (np->name = strdup(name)) == NULL) return NULL; hashval = hash(name); np->next = hashtab[hashval]; /* INSERT NEW ENTRY AT */ hashtab[hashval] = np; /* BEGINNING OF LIST */ } else /* ALREADY THERE */ free((void *) np->defn); /* FREE PREVIOUS defn */ if ((np->defn = strdup(defn)) == NULL) return NULL; return np; } /* LOOKUP: LOOK FOR s IN HASHTAB */ struct nlist *lookup(char *s) { struct nlist *np; for (np = hashtab[hash(s)]; np != NULL; np = np->next) if (strcmp(s, np->name) == 0) return np; /* FOUND */ return NULL; /* NOT FOUND */ } /* HASH: FORM HASH VALUE FOR STRING s */ unsigned hash(char *s) { unsigned hashval; for (hashval = 0; *s != '\0'; s++) hashval = *s + 31 * hashval; return hashval % HASHSIZE; } /* STRDUP: MAKE A DUPLICATE OF s */ char *strdup(const char *s) { char *p; p = (char *) malloc(strlen(s)+1); /* +1 FOR '\0' */ if (p != NULL) strcpy(p, s); return p; } int getch(void); void ungetch(int); /* GETWORD: GET NEXT WORD OR CHARACTER FROM INPUT */ /* COUNT BLANKS SKIPPED, EXCEPT NEWLINE, RETURN IT TO CALLER */ int getword(char *word, int lim) { int c; char *w = word; int skiprtn(int); blkcnt = 0; while ((c = getch()) == SPACE || c == FORMFEED || c == RETURN || c == TAB || c == VTAB) { putchar(c); blkcnt++; } if (c == '/' || ((c == '\"' || c == '\'') && !bldtext)) c = skiprtn(c); if (c == EOF) { *w = '\0'; return c; } *w++ = c; if (isalpha(c) || c == '_') /* UNDERSCORE */ for ( ; --lim > 0; w++) if (!isalnum(*w = getch()) && *w != '_') { ungetch(*w); break; } *w = '\0'; return word[0]; } /* SKIPRTN: SKIP MULTIPLE CONSECUTIVE OCCURRENCES OF STRING CONSTS */ /* AND COMMENT STRINGS, BUT NOT PREPROCESSOR CONTROL LINES. */ /* RETURN THE NEXT CHARACTER ENCOUNTERED. */ int skiprtn(int c) { for (;;) { if (c == '/') { /* SKIP COMMENTS */ if ((c = getch()) != '*') { ungetch(c); return '/'; } putchar('/'); putchar(c); while ((c = getch()) != EOF) { putchar(c); if (c == '*') { if ((c = getch()) == EOF) break; else { putchar(c); if (c == '/') break; } } } } else if (c == '\"' && !bldtext) { /* SKIP STRING CONSTANTS */ putchar(c); while ((c = getch()) != EOF) { putchar(c); if (c == '\"') break; if (c == '\\') /* SKIP ESCAPE SEQUENCE \" */ if ((c = getch()) != EOF) putchar(c); else break; } } else if (c == '\'' && !bldtext) { /* SKIP CHARACTER CONSTANTS */ putchar(c); while ((c = getch()) != EOF) { putchar(c); if (c == '\'') break; if (c == '\\') /* SKIP ESCAPE SEQUENCE \' */ if ((c = getch()) != EOF) putchar(c); else break; } } else /* CAPTURE THE CHARACTER AFTER */ return c; /* THE LAST OCCURRENCE */ if (c == EOF) return c; while ((c = getch()) == SPACE || c == FORMFEED || c == RETURN || c == TAB || c == VTAB) { putchar(c); blkcnt++; } } } #define BUFSIZE 100 char buf[BUFSIZE]; /* BUFFER FOR ungetch */ int bufp = 0; /* NEXT FREE POSITION IN buf */ /* GETCH: GET A (POSSIBLY PUSHED BACK) CHARACTER */ int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } /* UNGETCH: PUSH CHARACTER BACK ON INPUT */ void ungetch(int c) { if (bufp >= BUFSIZE) printf("UNGETCH: TOO MANY CHARACTERS\n"); else buf[bufp++] = c; }