/* 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;
}