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