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