+
/* $Header$
*
- * This generates the /usr/lib/aliases file for the mailhub, and an additional
- * information file for helper programs that send back error messages.
+ * This generates the /usr/lib/aliases file for the mailhub.
*
* (c) Copyright 1988 by the Massachusetts Institute of Technology.
* For copying and distribution information, please see the file
#include <stdio.h>
#include <string.h>
#include <ctype.h>
-#include <sms.h>
-#include <sms_app.h>
+#include <moira.h>
+#include <moira_site.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
+
extern int errno;
-char *whoami = "aliases.gen";
+char *whoami = "mailhub.gen";
char *ingres_date_and_time();
-char *divide = "################################################################\n";
+char *perm_malloc();
+char *pstrsave();
+char *divide = "##############################################################";
+
+#define ML_WID 72
+#define AL_MAX_WID 896
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+FILE *out= stdout;
main(argc, argv)
char **argv;
{
long tm = time(NULL);
- FILE *out= stdout;
char filename[64], *targetfile;
struct stat sb;
## int flag;
if (flag < 0) {
fprintf(stderr, "File %s does not need to be rebuilt.\n",
argv[1]);
- exit(SMS_NO_CHANGE);
+ exit(MR_NO_CHANGE);
}
}
targetfile = argv[1];
sprintf(filename, "%s~", targetfile);
if ((out = fopen(filename, "w")) == NULL) {
fprintf(stderr, "unable to open %s for output\n", filename);
- exit(SMS_OCONFIG);
+ exit(MR_OCONFIG);
}
} else if (argc != 1) {
fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
- exit(SMS_ARGS);
+ exit(MR_ARGS);
}
fprintf(out, "%s\n# Aliases File Extract of %s", divide, ctime(&tm));
sort_info();
fprintf(stderr, "Dumping information\n");
- do_people(out);
+ do_people();
fprintf(out, "\n%s\n# End of aliases file\n", divide);
if (fclose(out)) {
perror("close failed");
- exit(SMS_CCONFIG);
+ exit(MR_CCONFIG);
}
if (argc == 2)
fix_file(targetfile);
- exit(SMS_SUCCESS);
+ exit(MR_SUCCESS);
}
switch (*num) {
case INGRES_DEADLOCK:
- ingres_errno = SMS_DEADLOCK;
+ ingres_errno = MR_DEADLOCK;
break;
default:
- ingres_errno = SMS_INGRES_ERR;
+ ingres_errno = MR_INGRES_ERR;
}
- com_err(whoami, SMS_INGRES_ERR, " code %d\n", *num);
+ com_err(whoami, MR_INGRES_ERR, " code %d\n", *num);
critical_alert("DCM", "Alias build encountered INGRES ERROR %d", *num);
exit(ingres_errno);
}
-struct hash *users, *machines, *strings, *names;
+struct hash *users, *machines, *strings, *lists, *names;
struct user {
char *login;
- unsigned short status;
char *first;
char *last;
char mi;
- char *fullname;
- char *addr;
char *pobox;
- int id;
};
-#define MAXIDS 10
+struct member {
+ struct member *next;
+ char *name;
+ int list_id;
+};
+struct list {
+ char *name;
+ char maillist;
+ char acl_t;
+ int acl_id;
+ struct member *m;
+};
struct names {
char *name;
struct names *next;
- int ids[MAXIDS];
- short count;
+ int keep:1;
+ int id:31;
};
get_info()
##{
-## int id, pid, bid, stat, cnt;
-## char name[129], type[9], fname[17], mname[17], lname[17], year[9];
-## char dept[13], oaddr[17], ophone[13], buf[257], haddr[17], hphone[13];
- char *s, *depttmp, *savedat(), *office_addr, *office_phone;
+## int id, pid, bid, stat, cnt, maillistp, acl, mid;
+## char name[129], type[9], fname[17], mname[17], lname[17], buf[257];
+ char *s;
register struct user *u;
+ struct list *l, *memberlist;
+ register struct member *m;
/* get locks */
## retrieve (buf = users.modtime) where users.users_id = 0
+## retrieve (buf = list.modtime) where list.list_id = 0
cnt = 0;
- machines = create_hash(1000);
-## retrieve (id = machine.mach_id, name = machine.#name) {
+ machines = create_hash(10);
+## retrieve (id = machine.mach_id, name = machine.#name)
+## where machine.mach_id = users.pop_id {
if (s = index(name, '.'))
*s = 0;
- sprintf(buf, "%s.LOCAL", name);
- hash_store(machines, id, strsave(buf));
+ else
+ strtrim(name);
+#ifdef ATHENA
+ strcat(name, ".LOCAL");
+#endif
+ if (hash_store(machines, id, pstrsave(name)) < 0) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(MR_NO_MEM);
+ }
cnt++;
## }
fprintf(stderr, "Loaded %d machines\n", cnt);
cnt = 0;
- strings = create_hash(2000);
+ strings = create_hash(4000);
## retrieve (id = strings.string_id, name = strings.string) {
- hash_store(strings, id, strsave(strtrim(name)));
+ if (hash_store(strings, id, pstrsave(strtrim(name))) < 0) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(MR_NO_MEM);
+ }
cnt++;
## }
fprintf(stderr, "Loaded %d strings\n", cnt);
cnt = 0;
- users = create_hash(15000);
+ users = create_hash(12001);
## range of u is users
-## retrieve (id = u.users_id, name = u.login, stat = u.status,
-## fname = u.first, mname = u.middle, lname = u.last,
-## year = u.mit_year, dept = u.mit_dept,
-## oaddr = u.office_addr, ophone = u.office_phone,
-## haddr = u.home_addr, hphone = u.home_phone,
-## type = u.potype, pid = u.pop_id, bid = u.box_id) {
- u = (struct user *) malloc(sizeof(struct user));
- u->status = stat;
- u->login = strsave(strtrim(name));
- u->first = strsave(strtrim(fname));
- u->last = strsave(strtrim(lname));
+## retrieve (id = u.users_id, name = u.login, fname = u.first,
+## mname = u.middle, lname = u.last,
+## type = u.potype, pid = u.pop_id, bid = u.box_id)
+## where u.status != 3 {
+ u = (struct user *) perm_malloc(sizeof(struct user));
+ u->login = pstrsave(strtrim(name));
+ u->first = pstrsave(strtrim(fname));
+ u->last = pstrsave(strtrim(lname));
if (mname[0] != ' ')
u->mi = mname[0];
else
u->mi = 0;
- if (atoi(year) > 1900)
- depttmp = "Undergraduate";
- else
- depttmp = strtrim(dept);
- sprintf(buf, "%s, %s %c; ID: %s; Dept: %s %s",
- u->last, u->first, u->mi ? u->mi : ' ',
- u->login, depttmp, strtrim(year));
- u->fullname = strsave(buf);
-
- office_addr = strtrim(oaddr);
- if (*office_addr == 0) {
- office_addr = strtrim(haddr);
- }
- office_phone = strtrim(ophone);
- if (*office_phone == 0) {
- office_phone = strtrim(hphone);
- }
- if (*office_addr) {
- sprintf(buf, "%s; %s",
- office_addr, office_phone);
- u->addr = strsave(buf);
+ if (type[0] == 'P' && (s = hash_lookup(machines, pid))) {
+ sprintf(buf, "%s@%s", u->login, s);
+ u->pobox = pstrsave(buf);
+ } else if (type[0] == 'S') {
+ u->pobox = hash_lookup(strings, bid);
} else
- u->addr = NULL;
-
- u->id = 0;
- u->pobox = (char *) NULL;
- if (stat != 3) {
- if (type[0] == 'P') {
- if (s = hash_lookup(machines, pid)) {
- sprintf(buf, "%s@%s", strtrim(name), s);
- u->pobox = strsave(buf);
- } else {
- fprintf(stderr,
- "User %s's pobox is on a missing machine!\n",
- u->login);
- }
- } else if (type[0] == 'S') {
- if ((u->pobox = hash_lookup(strings, bid)) == NULL)
- fprintf(stderr,
- "User %s's pobox string is missing!\n",
- u->login);
- }
+ u->pobox = (char *) NULL;
+ if (hash_store(users, id, u) < 0) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(MR_NO_MEM);
}
- hash_store(users, id, u);
cnt++;
## }
fprintf(stderr, "Loaded %d users\n", cnt);
+
+ cnt = 0;
+ lists = create_hash(15000);
+## range of l is list
+## retrieve (id = l.list_id, name = l.#name, maillistp = l.maillist,
+## type = l.acl_type, acl = l.acl_id)
+## where l.active != 0 {
+ l = (struct list *) perm_malloc(sizeof(struct list));
+ l->name = pstrsave(strtrim(name));
+ l->maillist = maillistp;
+ l->acl_t = type[0];
+ l->acl_id = acl;
+ l->m = (struct member *) NULL;
+ if (hash_store(lists, id, l) < 0) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(MR_NO_MEM);
+ }
+ cnt++;
+## }
+ fprintf(stderr, "Loaded %d lists\n", cnt);
+
+ cnt = 0;
+## range of m is imembers
+## retrieve (id = m.list_id, type = m.member_type, mid = m.member_id)
+## where m.direct = 1 {
+ cnt++;
+ if (l = (struct list *) hash_lookup(lists, id)) {
+ m = (struct member *) perm_malloc(sizeof(struct member));
+ if (type[0] == 'U' &&
+ (u = (struct user *) hash_lookup(users, mid))) {
+ m->list_id = 0;
+ m->name = u->login;
+ m->next = l->m;
+ l->m = m;
+ } else if (type[0] == 'L' &&
+ (memberlist = (struct list *) hash_lookup(lists, mid))) {
+ m->list_id = mid;
+ m->name = memberlist->name;
+ m->next = l->m;
+ l->m = m;
+ } else if (type[0] == 'S' &&
+ (s = hash_lookup(strings, mid))) {
+ m->list_id = 0;
+ m->next = l->m;
+ l->m = m;
+ m->name = s;
+ }
+ }
+## }
+ fprintf(stderr, "Loaded %d members\n", cnt);
##}
-char *savedat(s)
-register char *s;
+save_mlist(id, l, force)
+int id;
+struct list *l;
+int force;
{
- char buf[256];
- register char *ss = &buf[0];
- char *lastchar = NULL;
-
- while (isspace(*s))
- s++;
- if (*s == 0)
- return("");
-
- for (; *s; s++) {
- if (isspace(*s)) {
- *ss++ = '_';
- } else if (islower(*s)) {
- *ss++ = toupper(*s);
- lastchar = ss;
- } else {
- *ss++ = *s;
- lastchar = ss;
- }
+ register struct member *m;
+ register struct list *l1;
+
+ if (l->maillist > 1 ||
+ (l->maillist == 0 && !force))
+ return;
+
+ if (l->m && l->m->next == NULL &&
+ !strcasecmp(l->name, l->m->name)) {
+ l->maillist = 3;
+ return;
}
- if (lastchar)
- *lastchar = 0;
- *ss = 0;
- if ((ss = (char *) malloc(strlen(buf)+1)) == NULL) {
- fprintf(stderr, "ran out of memory saving data\n");
- exit(1);
+ l->maillist = 2;
+ insert_name(l->name, -1, TRUE, FALSE);
+ output_mlist(id, l);
+
+ if (l->acl_t == 'L' && (l1 = (struct list *)hash_lookup(lists, l->acl_id)))
+ save_mlist(0, l1, TRUE);
+
+ for (m = l->m; m; m = m->next) {
+ if (m->list_id && (l1 = (struct list *)hash_lookup(lists, m->list_id)))
+ save_mlist(0, l1, TRUE);
}
- strcpy(ss, buf);
- return(ss);
}
+insert_login(id, u, dummy)
+int id;
+struct user *u;
+int dummy;
+{
+ if (u->pobox && u->login[0] != '#')
+ insert_name(u->login, id, TRUE, FALSE);
+}
+
void insert_names(id, u, dummy)
int id;
struct user *u;
{
char buffer[256];
- if (u->status != 0 && u->status != 4)
- insert_name(u->login, id);
- insert_name(u->last, id);
+ insert_name(u->last, id, FALSE, FALSE);
sprintf(buffer, "%s_%s", u->first, u->last);
- insert_name(buffer, id);
+ insert_name(buffer, id, FALSE, TRUE);
+/* sprintf(buffer, "%c_%s", u->first[0], u->last);
+ insert_name(buffer, id, FALSE, TRUE); */
if (u->mi) {
sprintf(buffer, "%s_%c_%s", u->first, u->mi, u->last);
- insert_name(buffer, id);
+ insert_name(buffer, id, FALSE, TRUE);
}
}
int incount = 0;
-insert_name(s, id)
+insert_name(s, id, nodups, copy)
char *s;
int id;
+int nodups;
+int copy;
{
int code;
register struct names *ns;
code = hashstr(s);
ns = (struct names *) hash_lookup(names, code);
if (ns == NULL) {
- if ((ns = (struct names *) malloc(sizeof(struct names))) == NULL) {
+ if ((ns = (struct names *) perm_malloc(sizeof(struct names))) == NULL) {
fprintf(stderr, "ran out of memory inserting name (sorting)\n");
- exit(1);
+ exit(MR_NO_MEM);
+ }
+ if (copy)
+ ns->name = pstrsave(s);
+ else
+ ns->name = s;
+ ns->keep = nodups;
+ ns->id = id;
+ ns->next = NULL;
+ if (hash_store(names, code, ns) < 0) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(MR_NO_MEM);
}
- ns->name = strsave(s);
- ns->count = 1;
- ns->ids[0] = id;
- hash_store(names, code, ns);
return;
}
if (strcasecmp(ns->name, s)) {
if (!strcasecmp(ns->name, s))
goto foundns;
}
- if ((ns->next = (struct names *)malloc(sizeof(struct names))) == NULL) {
+ if ((ns->next = (struct names *) perm_malloc(sizeof(struct names)))
+ == NULL) {
fprintf(stderr, "ran out of memory insterting name (sorting)\n");
- exit(1);
+ exit(MR_NO_MEM);
}
ns = ns->next;
- ns->name = strsave(s);
- ns->count = 1;
- ns->ids[0] = id;
- hash_store(names, code, ns);
+ if (copy)
+ ns->name = pstrsave(s);
+ else
+ ns->name = s;
+ ns->keep = nodups;
+ ns->id = id;
+ ns->next = NULL;
return;
}
foundns:
- if (ns->count < MAXIDS - 1) {
- ns->ids[ns->count++] = id;
+ if (nodups || ns->keep) {
+ if (nodups && ns->keep)
+ fprintf(stderr, "duplicated named: %s\n", s);
return;
}
- ns->count++;
+ ns->id = 0;
}
+/* Illegal chars: ! " % ( ) , . / : ; < = > @ [ \ ] ^ { | } */
+
+static int illegalchars[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
+ 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, /* SPACE - / */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, /* 0 - ? */
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* P - _ */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+
+/* While hashing the string, punt any illegal characters */
+
int hashstr(s)
register char *s;
{
register int result;
register int c;
- for (result = 0; *s; s++) {
- if (isupper(*s))
- c = tolower(*s);
- else
- c = *s;
+ for (result = 0; c = *s; s++) {
+ if (illegalchars[c]) {
+ register char *p;
+ for (p = s; *p; p++)
+ *p = p[1];
+ continue;
+ }
+ if (isupper(c))
+ c = *s = tolower(c);
/* result = result * 31 + *s; */
- result = (result << 5) - result + c - 'a';
+ result = (result << 5) - result + c - '`';
}
return(result < 0 ? -result : result);
}
sort_info()
{
- names = create_hash(10000);
+ names = create_hash(20001);
+ hash_step(users, insert_login, NULL);
+ incount = 0;
+ fprintf(out, "\n%s\n# Mailing lists\n%s\n", divide, divide);
+ hash_step(lists, save_mlist, FALSE);
+ fprintf(stderr, "Output %d lists\n", incount);
hash_step(users, insert_names, NULL);
fprintf(stderr, "Inserted %d names\n", incount);
}
-static long offset;
-static FILE *msgs;
-
output_data(dummy, nms, out)
int dummy;
struct names *nms;
{
register struct names *ns;
register struct user *u;
- int i;
incount++;
for (ns = nms; ns; ns = ns->next) {
- if (strlen(ns->name) < 2)
- continue;
- if (ns->count == 1) {
- u = (struct user *) hash_lookup(users, ns->ids[0]);
+ if (ns->name[0] == 0 || ns->name[1] == 0) {
+ fprintf(stderr, "punting %s due to short name\n", ns->name);
+ continue;
+ }
+ if (ns->id > 0) {
+ u = (struct user *) hash_lookup(users, ns->id);
if (u->pobox) {
fprintf(out, "%s: %s\n", ns->name, u->pobox);
- continue;
+ } else {
+ fprintf(out, "%s: =%s=@nobox\n", ns->name, ns->name);
}
- if (u->id == 0) {
- u->id = offset;
- if (*u->addr) {
- fprintf(msgs, "%s\n %s\n\001", u->fullname, u->addr);
- offset += strlen(u->fullname) + strlen(u->addr) + 5;
- } else {
- fprintf(msgs, "%s\n\001", u->fullname);
- offset += strlen(u->fullname) + 2;
- }
- }
- fprintf(out, "%s: +%d+@no_po_box\n", ns->name, u->id);
- } else if (ns->count < 10) {
- fprintf(out, "%s: +%d+@ambiguous\n", ns->name, offset);
- for (i = 0; i < ns->count; i++) {
- u = (struct user *) hash_lookup(users, ns->ids[i]);
- fprintf(msgs, "%s\n", u->fullname);
- offset += strlen(u->fullname) + 1;
- }
- fputs("\001", msgs);
- offset++;
- } else {
- fprintf(out, "%s: +%d+@too_many\n", ns->name, ns->count);
+ } else if (ns->id == 0) {
+ fprintf(out, "%s: =%s=@ambig\n", ns->name, ns->name);
}
}
}
-do_people(out)
+int lwid, bol, awid;
+
+output_mlist(id, l)
+int id;
+register struct list *l;
+{
+ struct list *l1;
+ register struct member *m;
+ register struct user *u;
+
+ if (l->acl_t == 'L' &&
+ (l1 = (struct list *) hash_lookup(lists, l->acl_id)))
+ fprintf(out, "owner-%s: %s\n%s: ", l->name, l1->name, l->name);
+ else if (l->acl_t == 'U' &&
+ (u = (struct user *) hash_lookup(users, l->acl_id)))
+ fprintf(out, "owner-%s: %s\n%s: ", l->name, u->login, l->name);
+ else
+ fprintf(out, "%s: ", l->name);
+
+
+ lwid = strlen(l->name) + 2;
+ bol = 1;
+ for (m = l->m; m; m = m->next) {
+ do_member(out, m->name);
+ }
+ if (l->m == (struct member *)NULL)
+ fprintf(out, "/dev/null");
+ fprintf(out, "\n\n");
+ incount++;
+}
+
+
+/* print out strings separated by commas, doing line breaks as appropriate */
+
+do_member(out, s)
FILE *out;
+register char *s;
{
- incount = 0;
- msgs = fopen("aliases.strings", "w");
- fputs(msgs, "\001");
- offset = 1;
+ register wwid;
+ static int cont = 1;
+
+ wwid = strlen(s);
+
+ if (!bol && awid + wwid + 2 > AL_MAX_WID) {
+ fprintf(out, ",\n\tcontinuation-%d\ncontinuation-%d: ", cont, cont);
+ cont++;
+ awid = lwid = 17 + wwid;
+ fputs(s, out);
+ return;
+ }
+
+ if (bol) {
+ lwid += wwid;
+ awid = lwid;
+ fputs(s, out);
+ bol = 0;
+ return;
+ }
+ if (lwid + wwid + 2 > ML_WID) {
+ fprintf(out, ",\n\t%s", s);
+ awid += lwid + wwid + 2;
+ lwid = wwid + 8;
+ return;
+ }
+ lwid += wwid + 2;
+ fprintf(out, ", %s", s);
+}
+
+do_people()
+{
+ incount = 0;
+ fprintf(out, "\n%s\n# People\n%s\n", divide, divide);
hash_step(names, output_data, out);
- fputs("\n", msgs);
- fclose(msgs);
fprintf(stderr, "Output %d entries\n", incount);
}
+
+
+#define chunk_size 102400
+
+char *perm_malloc(size)
+unsigned size;
+{
+ static char *pool = NULL;
+ static unsigned pool_size = 0;
+ register char *ret;
+
+ if (size > pool_size) {
+ pool = (char *) malloc(chunk_size);
+ pool_size = chunk_size;
+ }
+ ret = pool;
+ pool += size;
+ pool = (char *)(((unsigned) (pool + 1)) & ~1);
+ pool_size -= (pool - ret);
+ return(ret);
+}
+
+
+/*
+ * Make a (permenant) copy of a string.
+ */
+char *
+pstrsave(s)
+ char *s;
+{
+ register int len;
+ register char *p;
+ /* Kludge for sloppy string semantics */
+ if (!s) {
+ printf("NULL != \"\" !!!!\r\n");
+ p = perm_malloc(1);
+ *p = '\0';
+ return p;
+ }
+ len = strlen(s) + 1;
+ p = perm_malloc((u_int)len);
+ if (p) bcopy(s, p, len);
+ return p;
+}
+
+#define hash_func(h, key) (key >= 0 ? (key % h->size) : (-key % h->size))
+
+/* Create a hash table. The size is just a hint, not a maximum. */
+
+struct hash *create_hash(size)
+int size;
+{
+ struct hash *h;
+
+ h = (struct hash *) perm_malloc(sizeof(struct hash));
+ if (h == (struct hash *) NULL)
+ return((struct hash *) NULL);
+ h->size = size;
+ h->data = (struct bucket **) perm_malloc(size * sizeof(char *));
+ if (h->data == (struct bucket **) NULL) {
+ free(h);
+ return((struct hash *) NULL);
+ }
+ bzero(h->data, size * sizeof(char *));
+ return(h);
+}
+
+/* Lookup an object in the hash table. Returns the value associated with
+ * the key, or NULL (thus NULL is not a very good value to store...)
+ */
+
+char *hash_lookup(h, key)
+struct hash *h;
+register int key;
+{
+ register struct bucket *b;
+
+ b = h->data[hash_func(h, key)];
+ while (b && b->key != key)
+ b = b->next;
+ if (b && b->key == key)
+ return(b->data);
+ else
+ return(NULL);
+}
+
+
+/* Update an existing object in the hash table. Returns 1 if the object
+ * existed, or 0 if not.
+ */
+
+int hash_update(h, key, value)
+struct hash *h;
+register int key;
+char *value;
+{
+ register struct bucket *b;
+
+ b = h->data[hash_func(h, key)];
+ while (b && b->key != key)
+ b = b->next;
+ if (b && b->key == key) {
+ b->data = value;
+ return(1);
+ } else
+ return(0);
+}
+
+
+/* Store an item in the hash table. Returns 0 if the key was not previously
+ * there, 1 if it was, or -1 if we ran out of memory.
+ */
+
+int hash_store(h, key, value)
+struct hash *h;
+register int key;
+char *value;
+{
+ register struct bucket *b, **p;
+
+ p = &(h->data[hash_func(h, key)]);
+ if (*p == NULL) {
+ b = *p = (struct bucket *) perm_malloc(sizeof(struct bucket));
+ if (b == (struct bucket *) NULL)
+ return(-1);
+ b->next = NULL;
+ b->key = key;
+ b->data = value;
+ return(0);
+ }
+
+ for (b = *p; b && b->key != key; b = *p)
+ p = (struct bucket **) *p;
+ if (b && b->key == key) {
+ b->data = value;
+ return(1);
+ }
+ b = *p = (struct bucket *) perm_malloc(sizeof(struct bucket));
+ if (b == (struct bucket *) NULL)
+ return(-1);
+ b->next = NULL;
+ b->key = key;
+ b->data = value;
+ return(0);
+}
+
+
+/* Search through the hash table for a given value. For each piece of
+ * data with that value, call the callback proc with the corresponding key.
+ */
+
+hash_search(h, value, callback)
+struct hash *h;
+register char *value;
+void (*callback)();
+{
+ register struct bucket *b, **p;
+
+ for (p = &(h->data[h->size - 1]); p >= h->data; p--) {
+ for (b = *p; b; b = b->next) {
+ if (b->data == value)
+ (*callback)(b->key);
+ }
+ }
+}
+
+
+/* Step through the hash table, calling the callback proc with each key.
+ */
+
+hash_step(h, callback, hint)
+struct hash *h;
+void (*callback)();
+char *hint;
+{
+ register struct bucket *b, **p;
+
+ for (p = &(h->data[h->size - 1]); p >= h->data; p--) {
+ for (b = *p; b; b = b->next) {
+ (*callback)(b->key, b->data, hint);
+ }
+ }
+}
+
+
+/* Deallocate all of the memory associated with a table */
+
+hash_destroy(h)
+struct hash *h;
+{
+ register struct bucket *b, **p, *b1;
+
+ for (p = &(h->data[h->size - 1]); p >= h->data; p--) {
+ for (b = *p; b; b = b1) {
+ b1 = b->next;
+ free(b);
+ }
+ }
+}