chiark / gitweb /
Import ezmlm-idx 0.40
[ezmlm] / idxthread.c
1 /*$Id: idxthread.c,v 1.35 1999/11/22 01:47:45 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
3
4 /* idxthread.c contains routines to from the ezmlm-idx subject index build */
5 /* a structure of unique subjects as well as a table of messages with      */
6 /* pointers to the subject. This leads to information on message threads   */
7 /* arranged chronologically within the thread, and with the threads        */
8 /* arranged chronologically by the first message within the range.         */
9 /* idx_mkthreads() will arrange the author list in a similar manner. This  */
10 /* saves some space, and takes a little extra time. It's needed when       */
11 /* generating an author index. */
12
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include "error.h"
16 #include "alloc.h"
17 #include "str.h"
18 #include "stralloc.h"
19 #include "strerr.h"
20 #include "lock.h"
21 #include "idx.h"
22 #include "errtxt.h"
23 #include "substdio.h"
24 #include "fmt.h"
25 #include "readwrite.h"
26 #include "makehash.h"
27 #include "yyyymm.h"
28
29 #define DATENO 100
30 static stralloc line = {0};             /* primary input */
31 static stralloc authline = {0};         /* second line of primary input */
32 static stralloc dummyind = {0};
33
34 static substdio ssindex;
35 static char indexbuf[1024];
36
37 static char strnum[FMT_ULONG];
38
39 struct stat st;
40                         /* if no data, these may be the entire table, so */
41                         /* need to be static */
42 static subentry sdummy;
43 static authentry adummy;
44
45
46 static void die_nomem(fatal)
47 char *fatal;
48 {
49   strerr_die2x(111,fatal,ERR_NOMEM);
50 }
51
52 int fdlock;
53
54 /* NOTE: These do NOT prevent double locking */
55 static void lockup(fatal)
56 char *fatal;
57 {
58   fdlock = open_append("lock");
59   if (fdlock == -1)
60     strerr_die2sys(111,fatal,ERR_OPEN_LOCK);
61   if (lock_ex(fdlock) == -1) {
62     close(fdlock);
63     strerr_die2sys(111,fatal,ERR_OBTAIN_LOCK);
64   }
65 }
66
67 static void unlock()
68 {
69     close(fdlock);
70 }
71
72 static void newsub(psubt,subject,sublen,msg,fatal)
73 /* Initializes subentry pointed to by psubt, adds a '\0' to subject,    */
74 /* allocates space and copies in subject, and puts a pointer to it in   */
75 /* the entry. */
76 subentry *psubt;
77 char *subject;
78 unsigned int sublen;
79 unsigned long msg;
80 char *fatal;
81 {
82   register char *cpfrom, *cpto;
83   register unsigned int cpno;
84
85   psubt->higher = (subentry *) 0;
86   psubt->lower = (subentry *) 0;
87   psubt->firstmsg = msg;
88   psubt->lastmsg = msg;
89   psubt->msginthread = 1;
90   if (!(psubt->sub = alloc ((sublen) * sizeof(char))))
91     die_nomem(fatal);
92   cpto = psubt->sub;
93   cpno = sublen;
94   cpfrom = subject;
95   while (cpno--) *(cpto++) = *(cpfrom++);
96   psubt->sublen = sublen;
97 }
98
99 static void newauth(pautht,author,authlen,msg,fatal)
100 /* Allocates space for author of length authlen+1 adding a terminal '\0' */
101 /* and puts the pointer in pautht->auth. Analog to newsub().             */
102 authentry *pautht;      /* entry for current message */
103 char *author;           /* pointer to author string (not sz!) */
104 unsigned int authlen;   /* lenth of author */
105 unsigned long msg;
106 char *fatal;            /* sz */
107
108 {
109   register char *cpfrom, *cpto;
110   register unsigned int cpno;
111
112   pautht->higher = (subentry *) 0;
113   pautht->lower = (subentry *) 0;
114   pautht->firstmsg = msg;
115   if (!(pautht->auth = alloc ((authlen) * sizeof(char))))
116     die_nomem(fatal);
117   cpto = pautht->auth;
118   cpno = authlen;
119   cpfrom = author;
120   while (cpno--) *(cpto++) = *(cpfrom++);
121   pautht->authlen = authlen;
122 }
123
124 static void init_dummy(fatal)
125 char *fatal;
126 {
127   unsigned int i;
128
129   if (!stralloc_ready(&dummyind,HASHLEN + 1)) die_nomem(fatal);
130   for (i = 0; i< HASHLEN; i++)
131     dummyind.s[i] = 'a';
132   dummyind.len = HASHLEN;
133   if (!stralloc_append(&dummyind," ")) die_nomem(fatal);
134 }
135
136 void idx_mkthreads(pmsgtable,psubtable,pauthtable,pdatetable,
137         msg_from,msg_to,msg_latest,locked,fatal)
138 /* Threads messages msg_from -> msg_to into pmsgtable & psubtable. When  */
139 /* reading the latest index file (containing msg_latest) it locks the    */
140 /* directory, unless it is already locked (as in digest creation).       */
141 /* msgtable has the subject number 1.. (0 if there is no subject match,  */
142 /* which should happen only if the subject index is corrupt.)            */
143
144 /* 19971107 Changed to deal with index files that are missing, or have   */
145 /* missing entries, not necessarily reflecting missing archive files.    */
146 /* This all to make ezmlm-get more robust to get maximal info out of     */
147 /* corrupted archives.                                                   */
148
149   msgentry **pmsgtable;         /* table of message<->subject */
150   subentry **psubtable;         /* subject no, len, str char * */
151   authentry **pauthtable;       /* author no, len, str char * */
152   dateentry **pdatetable;       /* message per date */
153   unsigned long msg_from;       /* first message in range */
154   unsigned long msg_to;         /* last message in range */
155   unsigned long msg_latest;     /* latest message in archive (for locking) */
156   int locked;                   /* if already locked */
157   char *fatal;                  /* Program-specific */
158
159 {
160   unsigned long idxlatest;      /* need to lock for this (last) index file */
161   unsigned long msg;            /* current msg number */
162   unsigned long endmsg;         /* max msg in this idx file */
163   unsigned long tmpmsg;         /* index entry's msg number */
164   unsigned long idx;            /* current index file no */
165   unsigned long idxto;          /* index containing end of range */
166   unsigned long ulmrange;       /* total # of messages in range */
167   char *subject;                /* subject on line */
168   unsigned int sublen;          /* length of subject */
169   char *auth;
170   unsigned int authlen;
171   unsigned int pos,posa;
172   unsigned long submax;         /* max subject num in subtable */
173   subentry *psubnext;           /* points to next entry in subtable */
174   subentry *psubt;              /* points to entry in subtable */
175   authentry *pauthnext;         /* points to next entry in authtable */
176   authentry *pautht;            /* points to entry in authtable */
177   int fd;                       /* index file handle */
178   int flagmissingindex;         /* current index file is missing */
179   int flagauth;                 /* read index entry has author info */
180   int hasauth;                  /* current msg's entry has author info */
181   msgentry *pmsgt;
182   int res;
183   int match;
184   unsigned int datepos,datemax;
185   unsigned int datetablesize,datetableunit;
186   unsigned int lastdate = 0;
187   unsigned int thisdate;
188   register msgentry *x, *y;
189
190                                 /* a few unnecessary sanity checks */
191   if (msg_to > msg_latest)
192     msg_to = msg_latest;
193   if (msg_to < msg_from)
194     strerr_die2x(100,fatal,"Program error: bad range in idx_mkthreads");
195   ulmrange = msg_to - msg_from + 1;
196   if (!(*pmsgtable = (msgentry *) alloc(ulmrange * sizeof(msgentry))))
197         die_nomem(fatal);
198   y = *pmsgtable;
199   x = y + ulmrange;             /* clear */
200   while (--x >= y) {
201     x->subnum = 0;
202     x->authnum = 0;
203     x->date = 0;
204   }
205                                 /* max entries - acceptable waste for now */
206   if (!(*psubtable = (subentry *) alloc((ulmrange+1) * sizeof(subentry))))
207         die_nomem(fatal);
208
209   if (!(*pauthtable = (authentry *) alloc((ulmrange+1) * sizeof(authentry))))
210         die_nomem(fatal);
211   datetableunit = DATENO * sizeof(dateentry);
212   datetablesize = datetableunit;
213   if (!(*pdatetable = (dateentry *) alloc(datetablesize)))
214         die_nomem(fatal);
215   datepos = 0;
216   datemax = DATENO - 2;         /* entry 0 and end marker */
217   lastdate = 0;
218
219   idxlatest = msg_latest / 100;
220   idxto = msg_to / 100;
221   submax = 0;
222   psubnext = *psubtable;        /* dummy node to get tree going. Basically, */
223   psubt = &sdummy;              /* assure that subject > psubt-sub and that */
224   init_dummy(fatal);            /* below ok unless HASHLEN > 40 */
225   psubt->sub = "                                       ";
226   psubt->sublen = 40;           /* there is something to hold psubt->higher */
227   psubt->higher = (subentry *) 0;
228   psubt->lower = (subentry *) 0;
229   pauthnext = *pauthtable;
230   pautht = &adummy;
231   pautht->auth = psubt->sub;
232   pautht->authlen = psubt->sublen;
233   pautht->higher = (authentry *) 0;
234   pautht->lower = (authentry *) 0;
235   for (idx = msg_from / 100; idx <= idxto; idx++) {
236                                 /* make index file name */
237     if (!stralloc_copys(&line,"archive/")) die_nomem(fatal);
238     if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,idx))) die_nomem(fatal);
239     if (!stralloc_cats(&line,"/index")) die_nomem(fatal);
240     if (!stralloc_0(&line)) die_nomem(fatal);
241     if (!locked && idx == idxlatest)
242       lockup(fatal);
243     flagmissingindex = 0;
244     fd = open_read(line.s);
245     if (fd == -1) {
246       if (errno == error_noent) {       /* this means the index is not here */
247                                         /* but the lists is supposedly indexed*/
248         flagmissingindex = 1;
249       } else
250         strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
251     } else
252       substdio_fdbuf(&ssindex,read,fd,indexbuf,sizeof(indexbuf));
253
254     msg = 100L * idx;                   /* current msg# */
255     endmsg = msg + 99L;                 /* max msg in this index */
256     if (!msg) msg = 1L;                 /* for start to make msg > tmpmsg */
257     tmpmsg = 0L;                        /* msg number of read index line */
258     if (endmsg > msg_to)                /* skip non-asked for subjects */
259       endmsg = msg_to;
260     for (; msg <= endmsg; msg++) {
261       if (!flagmissingindex && (msg > tmpmsg)) {
262         flagauth = 0;
263         if (getln(&ssindex,&line,&match,'\n') == -1)
264           strerr_die3sys(111,fatal,ERR_READ,"index: ");
265         if (!match)
266           flagmissingindex = 1;
267         else {
268           pos = scan_ulong(line.s,&tmpmsg);
269           if (line.s[pos++] == ':') {
270             if (getln(&ssindex,&authline,&match,'\n') == -1)
271               strerr_die3sys(111,fatal,ERR_READ,"index: ");
272             if (!match)
273               flagmissingindex = 1;
274             else {
275               flagauth = 1;
276             }
277             pos++;
278           }
279         }
280       }
281       if (msg < msg_from)       /* Nothing before start of range */
282         continue;
283       if (msg == tmpmsg) {
284         subject = line.s + pos;
285         sublen = line.len - pos;
286         if (sublen <= HASHLEN)
287           strerr_die2x(100,fatal,ERR_BAD_INDEX);
288         hasauth = flagauth;
289       } else {
290         subject = dummyind.s;
291         sublen = dummyind.len;
292         hasauth = 0;
293       }
294       for(;;) {         /* search among already known subjects */
295         res = str_diffn(psubt->sub,subject,HASHLEN);
296         if (res < 0) {
297           if (psubt->higher)
298             psubt = psubt->higher;
299           else {
300             newsub(psubnext,subject,sublen,msg,fatal);
301             psubt->higher = psubnext;
302             psubt = psubnext;
303             psubnext++;
304             break;
305           }
306         } else if (res > 0) {
307           if (psubt->lower)
308             psubt = psubt->lower;
309           else {
310             newsub(psubnext,subject,sublen,msg,fatal);
311             psubt->lower = psubnext;
312             psubt = psubnext;
313             psubnext++;
314             break;
315           }
316         } else {
317           psubt->lastmsg = msg;
318           (psubt->msginthread)++;       /* one more message in thread */
319           break;
320         }
321       }
322                                 /* first subnum =1 (=0 is empty for thread) */
323       pmsgt = *pmsgtable + msg - msg_from;
324       pmsgt->subnum = (unsigned int) (psubt - *psubtable + 1);
325       pmsgt->date = lastdate;
326       if (hasauth) {
327         pos = 0;
328         while (authline.s[pos] && authline.s[pos] != ' ') pos++;
329         if (authline.s[++pos]) {
330           thisdate = date2yyyymm(authline.s + pos);
331           if (thisdate) pmsgt->date = thisdate;
332           if (pmsgt->date > lastdate) {
333             lastdate = pmsgt->date;
334             if (datepos >= datemax) {           /* more space */
335               datemax += DATENO;
336               if (!(*pdatetable = (dateentry *) alloc_re(*pdatetable,
337                         datetablesize,datetablesize+datetableunit)))
338                 die_nomem(fatal);
339             }
340             (*pdatetable)[datepos].msg = msg;   /* first msg this mo */
341             (*pdatetable)[datepos].date = lastdate;
342             datepos++;
343           }
344           posa = byte_chr(authline.s,authline.len,';');
345           if (authline.len > posa + HASHLEN + 1 && authline.s[pos+1] != ' ') {
346                                         /* old: "; auth", new: ";hash auth" */
347             auth = authline.s + posa + 1;
348             authlen = authline.len - posa - 1;
349           } else {
350             auth = dummyind.s;
351             authlen = dummyind.len;
352           }
353         }
354                         /* allright! Same procedure, but for author */
355         for (;;) {      /* search among already known authors */
356           res = str_diffn(pautht->auth,auth,HASHLEN);
357           if (res < 0) {
358             if (pautht->higher)
359               pautht = pautht->higher;
360             else {
361               newauth(pauthnext,auth,authlen,msg,fatal);
362               pautht->higher = pauthnext;
363               pautht = pauthnext;
364               pauthnext++;
365               break;
366             }
367           } else if (res > 0) {
368             if (pautht->lower)
369               pautht = pautht->lower;
370             else {
371               newauth(pauthnext,auth,authlen,msg,fatal);
372               pautht->lower = pauthnext;
373               pautht = pauthnext;
374               pauthnext++;
375               break;
376             }
377           } else {
378             break;
379           }
380         }                       /* link from message to this author */
381         pmsgt->authnum = (unsigned int) (pautht - *pauthtable + 1);
382         pautht = *pauthtable;
383       }
384
385       psubt = *psubtable;       /* setup psubt. Done here rather than before */
386                                 /* the for loop, so that we can start off    */
387                                 /* the dummy node. */
388     }
389     if (fd != -1)
390       close(fd);
391     if (!locked && idx == idxlatest)
392       unlock();                 /* 'locked' refers to locked before calling */
393   }
394   psubnext->sub = (char *) 0;           /* end of table marker */
395   pauthnext->auth = (char *) 0;         /* end of table marker */
396   (*pdatetable)[datepos].msg = msg_to + 1;
397   (*pdatetable)[datepos].date = lastdate + 1;
398 }
399
400
401 void idx_mkthread(pmsgtable,psubtable,pauthtable,msg_from,msg_to,msg_master,
402                 msg_latest,locked,fatal)
403 /* Works like idx_mkthreads, except that it finds the subject for message   */
404 /* msg_master, then identifies messages in the range that have the same     */
405 /* subject. msgtable entries with subject 0 do not match, with '1' do match.*/
406
407 msgentry **pmsgtable;           /* pointer to table of message<->subject */
408 subentry **psubtable;           /* ptr to tbl of subject no, len, str char * */
409 authentry **pauthtable;
410 unsigned long msg_from;         /* first message in range */
411 unsigned long msg_to;           /* last message in range */
412 unsigned long msg_latest;       /* latest message in archive (for locking) */
413 unsigned long msg_master;       /* master message for single thread, else 0*/
414 int locked;                     /* if already locked */
415 char *fatal;                    /* Program-specific */
416
417 {
418   unsigned long idxlatest;      /* need to lock for this (last) index file */
419   unsigned long idxto;          /* index for last msg in range */
420   unsigned long idx;            /* current index file no */
421   unsigned long msg;            /* index entry's msg number */
422   unsigned long ulmrange;       /* total # of messages in range */
423   subentry *psubt;              /* points to last entry in subtable */
424   int ffound;                   /* msg subject was found in subtable */
425   int flagauth;                 /* there is author info */
426   int firstfound = 1;           /* = 1 until first message in thread found */
427   int res;                      /* comparison result */
428   char *auth;
429   unsigned int authlen;
430   authentry *pauthnext;         /* points to next entry in authtable */
431   authentry *pautht;            /* points to entry in authtable */
432   unsigned int pos;
433   int fd;                       /* index file handle */
434   int match;
435   msgentry *pmsgt;
436   register msgentry *x,*y;
437
438   if ((ulmrange = msg_to - msg_from +1) <= 0)
439     strerr_die2x(100,fatal,"Program error: bad range in idx_mkthreads");
440   if (!(*pmsgtable = (msgentry *) alloc(ulmrange * sizeof(msgentry))))
441          die_nomem(fatal);
442   y = *pmsgtable;
443   x = y + ulmrange;
444   while (--x >= y) {
445     x->subnum = 0;
446     x->authnum = 0;
447     x->date = 0;
448   }
449
450   if (!(*psubtable = (subentry *) alloc(2 * sizeof(subentry))))
451           die_nomem(fatal);
452
453   if (!(*pauthtable = (authentry *) alloc((ulmrange + 1) * sizeof(authentry))))
454           die_nomem(fatal);
455
456   pauthnext = *pauthtable;
457   pautht = &adummy;
458   init_dummy();
459   pautht->auth = "                     ";
460   pautht->authlen = 21;
461   pautht->higher = (authentry *) 0;
462   pautht->lower = (authentry *) 0;
463   idxlatest = msg_latest / 100;
464   idxto = msg_to / 100;
465   idx = msg_master / 100;       /* index for master subject */
466
467                                 /* Get master subject */
468   if (!stralloc_copys(&line,"archive/")) die_nomem(fatal);
469   if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,idx))) die_nomem(fatal);
470   if (!stralloc_cats(&line,"/index")) die_nomem(fatal);
471   if (!stralloc_0(&line)) die_nomem(fatal);
472   ffound = 0;
473   if (!locked && idx == idxlatest)
474     lockup(fatal);
475   fd = open_read(line.s);
476   psubt = *psubtable;
477   if (fd == -1) {
478     if (errno != error_noent)
479       strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
480     else
481       strerr_die2x(111,fatal,ERR_NOINDEX);      /* temp - admin can fix! */
482   } else {
483     substdio_fdbuf(&ssindex,read,fd,indexbuf,sizeof(indexbuf));
484     for(;;) {
485       if (getln(&ssindex,&line,&match,'\n') == -1)
486           strerr_die3sys(111,fatal,ERR_OPEN,"index: ");
487       if (!match)
488         break;
489       pos=scan_ulong(line.s,&msg);
490       if (line.s[pos++] == ':') {        /* marker for author info */
491         pos++;
492         flagauth = 1;
493       } else
494         flagauth = 0;
495       if (msg == msg_master) {
496         newsub(psubt,line.s+pos,line.len-pos,msg,fatal);
497                                         /* need to update msg later! */
498         ffound = 1;
499         break;
500       }
501       if (flagauth) {                   /* skip author line */
502         if (getln(&ssindex,&line,&match,'\n') == -1)
503           strerr_die3sys(111,fatal,ERR_OPEN,"index: ");
504       if (!match)
505         break;
506       }
507     }
508     close(fd);
509   }
510   if (!locked && idx == idxlatest)
511     unlock();
512   if (!ffound)
513       strerr_die2x(100,fatal,ERR_NOINDEX);
514   for (idx = msg_from / 100; idx <= idxto; idx++) {
515                 /* make index file name */
516     if (!stralloc_copys(&line,"archive/")) die_nomem(fatal);
517     if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,idx))) die_nomem(fatal);
518     if (!stralloc_cats(&line,"/index")) die_nomem(fatal);
519     if (!stralloc_0(&line)) die_nomem(fatal);
520     if (!locked && idx == idxlatest)
521       lockup(fatal);
522     fd = open_read(line.s);
523     if (fd == -1) {
524       if (errno != error_noent)
525         strerr_die4sys(111,fatal,ERR_OPEN,line.s,": ");
526     } else {
527       substdio_fdbuf(&ssindex,read,fd,indexbuf,sizeof(indexbuf));
528       for(;;) {
529         if (getln(&ssindex,&line,&match,'\n') == -1)
530           strerr_die3sys(111,fatal,ERR_READ,"index: ");
531         if (!match)
532           break;
533         pos=scan_ulong(line.s,&msg);
534         if (line.s[pos++] == ':') {
535           pos++;
536           flagauth = 1;
537           if (getln(&ssindex,&authline,&match,'\n') == -1)
538             strerr_die3sys(111,fatal,ERR_READ,"index: ");
539           if (!match)
540             break;
541         } else
542           flagauth = 0;
543         if (msg < msg_from)     /* Nothing before start of range */
544           continue;
545         if (msg > msg_to)       /* Don't do anything after range */
546           break;
547         if (!str_diffn(psubt->sub,line.s+pos,HASHLEN)) {
548           pmsgt = *pmsgtable + msg - msg_from;
549           if (firstfound) {     /* update to first message with this subj */
550             psubt->firstmsg = msg;
551             firstfound = 0;
552           }
553           psubt->lastmsg = msg;
554           pmsgt->subnum = 1;
555           if (flagauth) {
556             if (*authline.s)
557               pmsgt->date = date2yyyymm(authline.s + 1);
558             pos = byte_chr(authline.s,authline.len,';');
559             if (authline.len > pos + HASHLEN + 1 && authline.s[pos+1] != ' ') {
560                                         /* old: "; auth", new: ";hash auth" */
561               auth = authline.s + pos + 1;
562               authlen = authline.len - pos - 1;
563             } else {
564               auth = dummyind.s;
565               authlen = dummyind.len;
566             }
567             for (;;) {          /* search among already known authors */
568               res = str_diffn(pautht->auth,auth,HASHLEN);
569               if (res < 0) {
570                 if (pautht->higher)
571                   pautht = pautht->higher;
572                 else {
573                   newauth(pauthnext,auth,authlen,msg,fatal);
574                   pautht->higher = pauthnext;
575                   pautht = pauthnext;
576                   pauthnext++;
577                   break;
578                 }
579               } else if (res > 0) {
580                 if (pautht->lower)
581                   pautht = pautht->lower;
582                 else {
583                   newauth(pauthnext,auth,authlen,msg,fatal);
584                   pautht->lower = pauthnext;
585                   pautht = pauthnext;
586                   pauthnext++;
587                   break;
588                 }
589               } else {
590                 break;
591               }
592             }                   /* link from message to this author */
593             pmsgt->authnum = (unsigned int) (pautht - *pauthtable + 1);
594             pautht = *pauthtable;
595           }
596
597         }
598       }
599       close(fd);
600     }
601     if (!locked && idx == idxlatest)
602       unlock();
603   }
604   ++psubt;
605   psubt->sub = (char *) 0;      /* end of table marker */
606   pauthnext->auth = (char *) 0; /* end of table marker */
607 }
608
609 void idx_mklist(pmsgtable,psubtable,pauthtable,msg_from,msg_to,fatal)
610 /* Like mkthreads, except that it works without a subject index. The result */
611 /* is just a dummy subject and a sequential list of messages. This to allow */
612 /* use of the same routines when creating digest from lists that have no    */
613 /* subject index (for whatever reason). */
614 msgentry **pmsgtable;           /* pointer to table of message<->subject */
615 subentry **psubtable;           /* ptr to tbl of subject no, len, str char * */
616 authentry **pauthtable;
617 unsigned long msg_from;         /* first message in range */
618 unsigned long msg_to;           /* last message in range */
619 char *fatal;                    /* Program-specific */
620 {
621   unsigned long ulmrange;
622   register msgentry *x,*y;
623   subentry *psubt;
624   authentry *pautht;
625
626   if ((ulmrange = msg_to - msg_from +1) <= 0)
627     strerr_die2x(111,fatal,"bad range in idx_mkthreads :");
628
629   if (!(*pmsgtable = (msgentry *) alloc(ulmrange * sizeof(msgentry))))
630          die_nomem(fatal);
631
632   y = *pmsgtable;
633   x = y + ulmrange;
634   while (--x >= y) {
635     x->subnum = 1;
636     x->authnum = 0;
637     x->date = 0;
638   }
639
640   if (!(*psubtable = (subentry *) alloc(2 * sizeof(subentry))))
641           die_nomem(fatal);
642   psubt = *psubtable;
643   newsub(psubt,dummyind.s,dummyind.len,msg_from,fatal);
644   psubt->lastmsg = msg_to;
645   ++psubt;
646   psubt->sub = (char *) 0;
647   if (!(*pauthtable = (authentry *) alloc(sizeof(authentry))))
648           die_nomem(fatal);     /* nodata. Avoid dangling ptr. */
649   pautht = *pauthtable;
650   pautht->auth = 0;             /* tells app that there are no author data */
651   pautht->higher = (authentry *) 0;
652   pautht->lower = (authentry *) 0;
653 }
654
655 void idx_destroythread(msgtable,subtable,authtable)
656 /* Frees space allocated by idxthread routines. This is needed only if */
657 /* one does several threadings in one program run. Otherwise, exit()   */
658 /* should free all allocated memory, which will be faster. */
659 msgentry *msgtable; subentry *subtable; authentry *authtable;
660 {
661   subentry *psubt;
662   authentry *pautht;
663
664   psubt = subtable;             /* free subjects */
665   while(psubt->sub) {
666     alloc_free(psubt->sub);
667     psubt++;
668   }
669
670   pautht = authtable;           /* free authors */
671   while(pautht->auth) {
672     alloc_free(pautht->auth);
673     pautht++;
674   }
675
676   alloc_free(subtable);         /* free subtable */
677   alloc_free(authtable);        /* free authtable */
678   alloc_free(msgtable);         /* free msgtable */
679   subtable = (subentry *) 0;    /* kill pointers */
680   authtable = (authentry *) 0;
681   msgtable = (msgentry *) 0;
682 }