Exercise 6.5 - undef: remove name and definition from table

Question

Write a function undef that will remove a name and definition from the table maintained by lookup and install.

/* Write a function undef that will remove a name and definition
 * from the table maintained by lookup and install.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HASHSIZE 101

static struct nlist *hashtab[HASHSIZE]; /* pointer table */

/* linked List of words. nlist from K&R Page 144 */
struct nlist {          /* table entry: */
    struct nlist *next; /* next entry in chain */
    char *name;         /* defined name */
    char *defn;         /* replacement text */
};

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

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

/* install: put (name, defn) 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];
        hashtab[hashval] = np;
    } else                      /* already there */
        free((void *) np->defn); /* free the previous defn */

    if ((np->defn = strdup(defn)) == NULL)
        return NULL;

    return np;
}

/**
 * undef will be if it is just hashtable. Remove it.
 * If it is in linked list, delete from linked list.
 **/
struct nlist *undef(char *name) {
    struct nlist *found;

    found = lookup(name);

    if (found == NULL) /* not found and nothing to do */
        return NULL;
    else {
        if (found->next != NULL) {
            found->next = found->next->next;
            found = found->next;
        } else {
            hashtab[hash(name)] = NULL;
            free((void *) found);
        }
    }
    return found;
}

int main(int argc, char *argv[]) {
    struct nlist *table[4] = {
            (install("key", "value")),
            (install("key1", "value1")),
            (install("key2", "value2")),
            (install("key3", "value3"))};

    int i;

    for (i = 0; i < 4; i++) {
        printf("%s->%s\n", table[i]->name, table[i]->defn);
    }

    undef("key");
    undef("key3");

    struct nlist *result;

    char *keys[4] = {"key", "key1", "key2", "key3"};

    for (i = 0; i < 4; i++) {
        if ((result = lookup(keys[i])) == NULL) {
            printf("key not found\n");
        } else {
            printf("%s->%s\n", result->name, result->defn);
        }
    }

    return 0;
}

Explanation

This program demonstrates implementing a hash table for inserting key -> value. When there is a collision to inserting values against a key, it will create a linked list of values.

So, it implements

  • install - installs the word in the hash table. Creates a linked list for the new value.

  • lookup - looks up the word in the hash table.

  • undef - removes the word in the hash table.

For each of these operations, it takes word, calculates the hash value. If there is a collision, it will add the word to the linked list in the hashtable key.

Sample run of this program.

key1->value1
key2->value2
key3->value3
key not found
key1->value1
key2->value2
key not found