Commit | Line | Data |
---|---|---|
f8beb284 MW |
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 | } |