]> andersk Git - moira.git/blob - gen/mailhub.dc
9d3c97192448692df77dbee3fd728b6d9ff49f84
[moira.git] / gen / mailhub.dc
1
2 /* $Header$
3  *
4  * This generates the /usr/lib/aliases file for the mailhub.
5  *
6  *  (c) Copyright 1988, 1990 by the Massachusetts Institute of Technology.
7  *  For copying and distribution information, please see the file
8  *  <mit-copyright.h>.
9  */
10
11 #include <mit-copyright.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <moira.h>
16 #include <moira_site.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/time.h>
20 EXEC SQL INCLUDE sqlca;
21
22
23 extern int errno;
24 char *whoami = "mailhub.gen";
25 char *perm_malloc();
26 char *pstrsave();
27 char *divide = "##############################################################";
28
29 #define ML_WID  72
30 #define AL_MAX_WID 896
31
32 #define FALSE 0
33 #define TRUE (!FALSE)
34
35 FILE *out= stdout;
36
37
38 main(argc, argv)
39 int argc;
40 char **argv;
41 {
42     long tm = time(NULL);
43     char filename[64], *targetfile;
44     struct stat sb;
45     EXEC SQL BEGIN DECLARE SECTION;
46     int flag;
47     EXEC SQL END DECLARE SECTION;
48
49 #ifsql INGRES
50     EXEC SQL CONNECT sms;
51 #endsql
52 #ifsql INFORMIX
53     EXEC SQL DATABASE sms;
54 #endsql
55
56     if (argc == 2) {
57         if (stat(argv[1], &sb) == 0) {
58             if (ModDiff (&flag, "users", sb.st_mtime))
59               exit(MR_DATE);
60             if (flag < 0) {
61                 fprintf(stderr, "File %s does not need to be rebuilt.\n",
62                         argv[1]);
63                 exit(MR_NO_CHANGE);
64             }
65         }
66         targetfile = argv[1];
67         sprintf(filename, "%s~", targetfile);
68         if ((out = fopen(filename, "w")) == NULL) {
69             fprintf(stderr, "unable to open %s for output\n", filename);
70             exit(MR_OCONFIG);
71         }
72     } else if (argc != 1) {
73         fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
74         exit(MR_ARGS);
75     }
76
77     fprintf(out, "%s\n# Aliases File Extract of %s", divide, ctime(&tm));
78     fprintf(out, "# This file is automatically generated, do not edit it directly.\n%s\n\n", divide);
79
80     get_info();
81
82 #ifsql INGRES
83     EXEC SQL DISCONNECT;
84 #endsql
85 #ifsql INFORMIX
86     EXEC SQL CLOSE DATABASE;
87 #endsql
88
89     fprintf(stderr, "Sorting Info\n");
90     sort_info();
91
92     fprintf(stderr, "Dumping information\n");
93     do_people();
94
95     fprintf(out, "\n%s\n# End of aliases file\n", divide);
96
97     if (fclose(out)) {
98         perror("close failed");
99         exit(MR_CCONFIG);
100     }
101
102     if (argc == 2)
103       fix_file(targetfile);
104     exit(MR_SUCCESS);
105 }
106
107
108
109 struct hash *users, *machines, *strings, *lists, *names;
110 struct user {
111     char *login;
112     char *first;
113     char *last;
114     char mi;
115     char *pobox;
116 };
117 struct member {
118     struct member *next;
119     char *name;
120     int list_id;
121 };
122 struct list {
123     char *name;
124     char maillist;
125     char acl_t;
126     int acl_id;
127     struct member *m;
128 };
129 struct names {
130     char *name;
131     struct names *next;
132     int keep:1;
133     int id:31;
134 };
135
136
137 get_info()
138 {
139     EXEC SQL BEGIN DECLARE SECTION;
140     int id, pid, bid, cnt, maillistp, acl, mid;
141     char name[129], type[9], fname[17], mname[17], lname[17], buf[257];
142     EXEC SQL END DECLARE SECTION;
143     char *s;
144     register struct user *u;
145     struct list *l, *memberlist;
146     register struct member *m;
147
148     EXEC SQL WHENEVER SQLERROR GOTO sqlerr;
149
150     EXEC SQL SELECT modtime INTO :buf FROM users WHERE users_id = 0;
151     EXEC SQL SELECT modtime INTO :buf FROM list WHERE list_id = 0;
152
153     cnt = 0;
154     machines = create_hash(10);
155
156     EXEC SQL DECLARE m_cursor CURSOR FOR
157       SELECT m.mach_id, m.name
158       FROM machine m, users u
159       WHERE m.mach_id = u.pop_id;
160     EXEC SQL OPEN m_cursor;
161     while (1) {
162         EXEC SQL FETCH m_cursor INTO :id, :name;
163         if (sqlca.sqlcode != 0) break;
164         if (s = index(name, '.'))
165           *s = 0;
166         else
167           strtrim(name);
168 #ifdef ATHENA
169         strcat(name, ".LOCAL");
170 #endif
171         if (hash_store(machines, id, pstrsave(name)) < 0) {
172             fprintf(stderr, "Out of memory!\n");
173             exit(MR_NO_MEM);
174         }
175         cnt++;
176     }
177     EXEC SQL CLOSE m_cursor;
178
179     fprintf(stderr, "Loaded %d machines\n", cnt);
180
181     cnt = 0;
182     strings = create_hash(4000);
183
184     EXEC SQL DECLARE s_cursor CURSOR FOR
185       SELECT string_id, string
186       FROM strings;
187     EXEC SQL OPEN s_cursor;
188     while (1) {
189         EXEC SQL FETCH s_cursor INTO :id, :name;
190         if (sqlca.sqlcode != 0) break;
191         if (hash_store(strings, id, pstrsave(strtrim(name))) < 0) {
192             fprintf(stderr, "Out of memory!\n");
193             exit(MR_NO_MEM);
194         }
195         cnt++;
196     }
197     EXEC SQL CLOSE s_cursor;
198
199     fprintf(stderr, "Loaded %d strings\n", cnt);
200
201     cnt = 0;
202     users = create_hash(12001);
203
204     EXEC SQL DECLARE u_cursor CURSOR FOR
205       SELECT users_id, login, first, middle, last, potype, pop_id, box_id
206       FROM users
207       WHERE status != 3;
208     EXEC SQL OPEN u_cursor;
209     while (1) {
210         EXEC SQL FETCH u_cursor INTO :id, :name, :fname, :mname, :lname,
211                                    :type, :pid, :bid;
212         if (sqlca.sqlcode != 0) break;
213         u = (struct user *) perm_malloc(sizeof(struct user));
214         u->login = pstrsave(strtrim(name));
215         u->first = pstrsave(strtrim(fname));
216         u->last  = pstrsave(strtrim(lname));
217         if (mname[0] != ' ')
218           u->mi = mname[0];
219         else
220           u->mi = 0;
221
222         if (type[0] == 'P' && (s = hash_lookup(machines, pid))) {
223             sprintf(buf, "%s@%s", u->login, s);
224             u->pobox = pstrsave(buf);
225         } else if (type[0] ==  'S') {
226             u->pobox = hash_lookup(strings, bid);
227         } else
228           u->pobox = (char *) NULL;
229         if (hash_store(users, id, u) < 0) {
230             fprintf(stderr, "Out of memory!\n");
231             exit(MR_NO_MEM);
232         }
233         cnt++;
234     }
235     EXEC SQL CLOSE u_cursor;
236     fprintf(stderr, "Loaded %d users\n", cnt);
237
238     cnt = 0;
239     lists = create_hash(15000);
240
241     EXEC SQL DECLARE l_cursor CURSOR FOR
242       SELECT list_id, name, maillist, acl_type, acl_id
243       FROM list
244       WHERE active != 0;
245     EXEC SQL OPEN l_cursor;
246     while (1) {
247         EXEC SQL FETCH l_cursor INTO :id, :name, :maillistp, :type, :acl;
248         if (sqlca.sqlcode != 0) break;
249         l = (struct list *) perm_malloc(sizeof(struct list));
250         l->name = pstrsave(strtrim(name));
251         l->maillist = maillistp;
252         l->acl_t = type[0];
253         l->acl_id = acl;
254         l->m = (struct member *) NULL;
255         if (hash_store(lists, id, l) < 0) {
256             fprintf(stderr, "Out of memory!\n");
257             exit(MR_NO_MEM);
258         }
259         cnt++;
260     }
261     EXEC SQL CLOSE l_cursor;
262     fprintf(stderr, "Loaded %d lists\n", cnt);
263
264     cnt = 0;
265
266     EXEC SQL DECLARE m_cursor2 CURSOR FOR
267       SELECT list_id, member_type, member_id
268       FROM imembers
269       WHERE direct = 1;
270     EXEC SQL OPEN m_cursor2;
271     while (1) {
272         EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid;
273         if (sqlca.sqlcode != 0) break;
274         cnt++;
275         if (l = (struct list *) hash_lookup(lists, id)) {
276             m = (struct member *) perm_malloc(sizeof(struct member));
277             if (type[0] == 'U' &&
278                 (u = (struct user *) hash_lookup(users, mid))) {
279                 m->list_id = 0;
280                 m->name = u->login;
281                 m->next = l->m;
282                 l->m = m;
283             } else if (type[0] == 'L' && 
284                        (memberlist = (struct list *) hash_lookup(lists, mid))) {
285                 m->list_id = mid;
286                 m->name = memberlist->name;
287                 m->next = l->m;
288                 l->m = m;
289             } else if (type[0] == 'S' &&
290                        (s = hash_lookup(strings, mid))) {
291                 m->list_id = 0;
292                 m->next = l->m;
293                 l->m = m;
294                 m->name = s;
295             }
296         }
297     }
298     EXEC SQL CLOSE m_cursor2;
299     fprintf(stderr, "Loaded %d members\n", cnt);
300     return;
301  sqlerr:
302     com_err(whoami, MR_INGRES_ERR, " code %d\n", sqlca.sqlcode);
303     critical_alert("DCM", "Hesiod build encountered DATABASE ERROR %d",
304                    sqlca.sqlcode);
305     exit(MR_INGRES_ERR);
306 }
307
308
309 save_mlist(id, l, force)
310 int id;
311 struct list *l;
312 int force;
313 {
314     register struct member *m;
315     register struct list *l1;
316
317     if (l->maillist > 1 ||
318         (l->maillist == 0 && !force))
319       return;
320
321     if (l->m && l->m->next == NULL &&
322         !strcasecmp(l->name, l->m->name)) {
323         l->maillist = 3;
324         return;
325     }
326     l->maillist = 2;
327     insert_name(l->name, -1, TRUE, FALSE);
328     output_mlist(id, l);
329
330     if (l->acl_t == 'L' && (l1 = (struct list *)hash_lookup(lists, l->acl_id)))
331       save_mlist(0, l1, TRUE);
332     
333     for (m = l->m; m; m = m->next) {
334         if (m->list_id && (l1 = (struct list *)hash_lookup(lists, m->list_id)))
335           save_mlist(0, l1, TRUE);
336     }
337 }
338
339
340 insert_login(id, u, dummy)
341 int id;
342 struct user *u;
343 int dummy;
344 {
345     if (u->pobox && u->login[0] != '#')
346       insert_name(u->login, id, TRUE, FALSE);
347 }
348
349 void insert_names(id, u, dummy)
350 int id;
351 struct user *u;
352 int dummy;
353 {
354     char buffer[256];
355
356     insert_name(u->last, id, FALSE, FALSE);
357     sprintf(buffer, "%s_%s", u->first, u->last);
358     insert_name(buffer, id, FALSE, TRUE);
359 /*    sprintf(buffer, "%c_%s", u->first[0], u->last);
360     insert_name(buffer, id, FALSE, TRUE); */
361     if (u->mi) {
362         sprintf(buffer, "%s_%c_%s", u->first, u->mi, u->last);
363         insert_name(buffer, id, FALSE, TRUE);
364     }
365 }
366
367 int incount = 0;
368
369 insert_name(s, id, nodups, copy)
370 char *s;
371 int id;
372 int nodups;
373 int copy;
374 {
375     int code;
376     register struct names *ns;
377
378     incount++;
379     code = hashstr(s);
380     ns = (struct names *) hash_lookup(names, code);
381     if (ns == NULL) {
382         if ((ns = (struct names *) perm_malloc(sizeof(struct names))) == NULL) {
383             fprintf(stderr, "ran out of memory inserting name (sorting)\n");
384             exit(MR_NO_MEM);
385         }
386         if (copy)
387           ns->name = pstrsave(s);
388         else
389           ns->name = s;
390         ns->keep = nodups;
391         ns->id = id;
392         ns->next = NULL;
393         if (hash_store(names, code, ns) < 0) {
394             fprintf(stderr, "Out of memory!\n");
395             exit(MR_NO_MEM);
396         }
397         return;
398     }
399     if (strcasecmp(ns->name, s)) {
400         while (ns->next) {
401             ns = ns->next;
402             if (!strcasecmp(ns->name, s))
403               goto foundns;
404         }
405         if ((ns->next = (struct names *) perm_malloc(sizeof(struct names)))
406                 == NULL) {
407             fprintf(stderr, "ran out of memory insterting name (sorting)\n");
408             exit(MR_NO_MEM);
409         }
410         ns = ns->next;
411         if (copy)
412           ns->name = pstrsave(s);
413         else
414           ns->name = s;
415         ns->keep = nodups;
416         ns->id = id;
417         ns->next = NULL;
418         return;
419     }
420  foundns:
421     if (nodups || ns->keep) {
422         if (nodups && ns->keep)
423           fprintf(stderr, "duplicated name: %s\n", s);
424         return;
425     }
426     ns->id = 0;
427 }
428
429
430 /* Illegal chars: ! " % ( ) , . / : ; < = > @ [ \ ] ^ { | } */
431
432 static int illegalchars[] = {
433     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
434     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
435     0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, /* SPACE - / */
436     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, /* 0 - ? */
437     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */
438     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* P - _ */
439     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
440     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
441     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
442     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
443     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
444     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
445     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
446     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
447     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
448     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
449 };
450
451
452 /* While hashing the string, punt any illegal characters */
453
454 int hashstr(s)
455 register char *s;
456 {
457     register int result;
458     register int c;
459
460     for (result = 0; c = *s; s++) {
461         if (illegalchars[c]) {
462             register char *p;
463             for (p = s; *p; p++)
464               *p = p[1];
465             continue;
466         }
467         if (isupper(c))
468           c = *s = tolower(c);
469 /*      result = result * 31 + *s; */
470         result = (result << 5) - result + c - '`';
471     }
472     return(result < 0 ? -result : result);
473 }
474
475
476 sort_info()
477 {
478     names = create_hash(20001);
479     hash_step(users, insert_login, NULL);
480     incount = 0;
481     fprintf(out, "\n%s\n# Mailing lists\n%s\n", divide, divide);
482     hash_step(lists, save_mlist, FALSE);
483     fprintf(stderr, "Output %d lists\n", incount);
484     hash_step(users, insert_names, NULL);
485     fprintf(stderr, "Inserted %d names\n", incount);
486 }
487
488
489 output_data(dummy, nms, out)
490 int dummy;
491 struct names *nms;
492 FILE *out;
493 {
494     register struct names *ns;
495     register struct user *u;
496
497     incount++;
498     for (ns = nms; ns; ns = ns->next) {
499         if (ns->name[0] == 0 || ns->name[1] == 0) {
500             fprintf(stderr, "punting %s due to short name\n", ns->name);
501             continue;
502         }
503         if (ns->id > 0) {
504             u = (struct user *) hash_lookup(users, ns->id);
505             if (u->pobox) {
506                 fprintf(out, "%s: %s\n", ns->name, u->pobox);
507             } else {
508                 fprintf(out, "%s: =%s=@nobox\n", ns->name, ns->name);
509             }
510         } else if (ns->id == 0) {
511             fprintf(out, "%s: =%s=@ambig\n", ns->name, ns->name);
512         }
513     }
514 }
515
516
517 int lwid, bol, awid;
518
519 output_mlist(id, l)
520 int id;
521 register struct list *l;
522 {
523     struct list *l1;
524     register struct member *m;
525     register struct user *u;
526
527     if (l->acl_t ==  'L' &&
528         (l1 = (struct list *) hash_lookup(lists, l->acl_id)))
529       fprintf(out, "owner-%s: %s\n%s: ", l->name, l1->name, l->name);
530     else if (l->acl_t ==  'U' &&
531              (u = (struct user *) hash_lookup(users, l->acl_id)))
532       fprintf(out, "owner-%s: %s\n%s: ", l->name, u->login, l->name);
533     else
534       fprintf(out, "%s: ", l->name);
535       
536
537     lwid = strlen(l->name) + 2;
538     bol = 1;
539     for (m = l->m; m; m = m->next) {
540         do_member(out, m->name);
541     }
542     if (l->m == (struct member *)NULL)
543       fprintf(out, "/dev/null");
544     fprintf(out, "\n\n");
545     incount++;
546 }
547
548
549 /* print out strings separated by commas, doing line breaks as appropriate */
550
551 do_member(out, s)
552 FILE *out;
553 register char *s;
554 {
555     register wwid;
556     static int cont = 1;
557
558     wwid = strlen(s);
559
560     if (!bol && awid + wwid + 2 > AL_MAX_WID) {
561         fprintf(out, ",\n\tcontinuation-%d\ncontinuation-%d: ", cont, cont);
562         cont++;
563         awid = lwid = 17 + wwid;
564         fputs(s, out);
565         return;
566     }
567
568     if (bol) {
569         lwid += wwid;
570         awid = lwid;
571         fputs(s, out);
572         bol = 0;
573         return;
574     }
575     if (lwid + wwid + 2 > ML_WID) {
576         fprintf(out, ",\n\t%s", s);
577         awid += lwid + wwid + 2;
578         lwid = wwid + 8;
579         return;
580     }
581     lwid += wwid + 2;
582     fprintf(out, ", %s", s);
583 }
584
585
586 do_people()
587 {
588     incount = 0;
589     fprintf(out, "\n%s\n# People\n%s\n", divide, divide);
590     hash_step(names, output_data, out);
591     fprintf(stderr, "Output %d entries\n", incount);
592 }
593
594
595 #define chunk_size      102400
596
597 char *perm_malloc(size)
598 unsigned size;
599 {
600     static char *pool = NULL;
601     static unsigned pool_size = 0;
602     register char *ret;
603
604     if (size > pool_size) {
605         pool = (char *) malloc(chunk_size);
606         pool_size = chunk_size;
607     }
608     ret = pool;
609     pool += size;
610     pool = (char *)(((unsigned) (pool + 1)) & ~1);
611     pool_size -= (pool - ret);
612     return(ret);
613 }
614
615
616 /*
617  * Make a (permenant) copy of a string.
618  */
619 char *
620 pstrsave(s)
621     char *s;
622 {
623     register int len;
624     register char *p;
625     /* Kludge for sloppy string semantics */
626     if (!s) {
627             printf("NULL != \"\" !!!!\r\n");
628             p = perm_malloc(1);
629             *p = '\0';
630             return p;
631     }
632     len = strlen(s) + 1;
633     p = perm_malloc((u_int)len);
634     if (p) bcopy(s, p, len);
635     return p;
636 }
637
638 #define hash_func(h, key) (key >= 0 ? (key % h->size) : (-key % h->size))
639
640 /* Create a hash table.  The size is just a hint, not a maximum. */
641
642 struct hash *create_hash(size)
643 int size;
644 {
645     struct hash *h;
646
647     h = (struct hash *) perm_malloc(sizeof(struct hash));
648     if (h == (struct hash *) NULL)
649       return((struct hash *) NULL);
650     h->size = size;
651     h->data = (struct bucket **) perm_malloc(size * sizeof(char *));
652     if (h->data == (struct bucket **) NULL) {
653         free(h);
654         return((struct hash *) NULL);
655     }
656     bzero(h->data, size * sizeof(char *));
657     return(h);
658 }
659
660 /* Lookup an object in the hash table.  Returns the value associated with
661  * the key, or NULL (thus NULL is not a very good value to store...)
662  */
663
664 char *hash_lookup(h, key)
665 struct hash *h;
666 register int key;
667 {
668     register struct bucket *b;
669
670     b = h->data[hash_func(h, key)];
671     while (b && b->key != key)
672       b = b->next;
673     if (b && b->key == key)
674       return(b->data);
675     else
676       return(NULL);
677 }
678
679
680 /* Update an existing object in the hash table.  Returns 1 if the object
681  * existed, or 0 if not.
682  */
683
684 int hash_update(h, key, value)
685 struct hash *h;
686 register int key;
687 char *value;
688 {
689     register struct bucket *b;
690
691     b = h->data[hash_func(h, key)];
692     while (b && b->key != key)
693       b = b->next;
694     if (b && b->key == key) {
695         b->data = value;
696         return(1);
697     } else
698       return(0);
699 }
700
701
702 /* Store an item in the hash table.  Returns 0 if the key was not previously
703  * there, 1 if it was, or -1 if we ran out of memory.
704  */
705
706 int hash_store(h, key, value)
707 struct hash *h;
708 register int key;
709 char *value;
710 {
711     register struct bucket *b, **p;
712
713     p = &(h->data[hash_func(h, key)]);
714     if (*p == NULL) {
715         b = *p = (struct bucket *) perm_malloc(sizeof(struct bucket));
716         if (b == (struct bucket *) NULL)
717           return(-1);
718         b->next = NULL;
719         b->key = key;
720         b->data = value;
721         return(0);
722     }
723
724     for (b = *p; b && b->key != key; b = *p)
725       p = (struct bucket **) *p;
726     if (b && b->key == key) {
727         b->data = value;
728         return(1);
729     }
730     b = *p = (struct bucket *) perm_malloc(sizeof(struct bucket));
731     if (b == (struct bucket *) NULL)
732       return(-1);
733     b->next = NULL;
734     b->key = key;
735     b->data = value;
736     return(0);
737 }
738
739
740 /* Search through the hash table for a given value.  For each piece of
741  * data with that value, call the callback proc with the corresponding key.
742  */
743
744 hash_search(h, value, callback)
745 struct hash *h;
746 register char *value;
747 void (*callback)();
748 {
749     register struct bucket *b, **p;
750
751     for (p = &(h->data[h->size - 1]); p >= h->data; p--) {
752         for (b = *p; b; b = b->next) {
753             if (b->data == value)
754               (*callback)(b->key);
755         }
756     }
757 }
758
759
760 /* Step through the hash table, calling the callback proc with each key.
761  */
762
763 hash_step(h, callback, hint)
764 struct hash *h;
765 void (*callback)();
766 char *hint;
767 {
768     register struct bucket *b, **p;
769
770     for (p = &(h->data[h->size - 1]); p >= h->data; p--) {
771         for (b = *p; b; b = b->next) {
772             (*callback)(b->key, b->data, hint);
773         }
774     }
775 }
776
777
778 /* Deallocate all of the memory associated with a table */
779
780 hash_destroy(h)
781 struct hash *h;
782 {
783     register struct bucket *b, **p, *b1;
784
785     for (p = &(h->data[h->size - 1]); p >= h->data; p--) {
786         for (b = *p; b; b = b1) {
787             b1 = b->next;
788             free(b);
789         }
790     }
791 }
This page took 0.084955 seconds and 3 git commands to generate.