1 /* bits.c
2 */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4
5
6 #include "EXTERN.h"
7 #include "common.h"
8 #include "hash.h"
9 #include "cache.h"
10 #include "list.h"
11 #include "ngdata.h"
12 #include "nntpclient.h"
13 #include "datasrc.h"
14 #include "nntp.h"
15 #include "rcstuff.h"
16 #include "head.h"
17 #include "term.h"
18 #include "util.h"
19 #include "util2.h"
20 #include "final.h"
21 #include "trn.h"
22 #include "ng.h"
23 #include "artio.h"
24 #include "intrp.h"
25 #include "rcln.h"
26 #include "ndir.h"
27 #include "kfile.h"
28 #include "rthread.h"
29 #include "rt-process.h"
30 #include "rt-select.h"
31 #include "rt-util.h"
32 #include "INTERN.h"
33 #include "bits.h"
34 #include "bits.ih"
35
36 #ifdef DBM_XREFS
37 # ifdef NULL
38 # undef NULL
39 # endif
40 # include <dbm.h>
41 #endif
42
43 static long chase_count = 0;
44
45 void
bits_init()46 bits_init()
47 {
48 ;
49 }
50
51 void
rc_to_bits()52 rc_to_bits()
53 {
54 char* mybuf = buf; /* place to decode rc line */
55 register char* s;
56 register char* c;
57 register char* h;
58 register long i;
59 register ART_NUM unread;
60 ARTICLE* ap;
61
62 /* modify the article flags to reflect what has already been read */
63
64 for (s = ngptr->rcline + ngptr->numoffset; *s == ' '; s++) ;
65 /* find numbers in rc line */
66 i = strlen(s);
67 #ifndef lint
68 if (i >= LBUFLEN-2) /* bigger than buf? */
69 mybuf = safemalloc((MEM_SIZE)(i+2));
70 #endif
71 strcpy(mybuf,s); /* make scratch copy of line */
72 if (mybuf[0])
73 mybuf[i++] = ','; /* put extra comma on the end */
74 mybuf[i] = '\0';
75 s = mybuf; /* initialize the for loop below */
76 if (set_firstart(s)) {
77 s = index(s,',') + 1;
78 for (i = article_first(absfirst); i < firstart; i = article_next(i))
79 article_ptr(i)->flags &= ~AF_UNREAD;
80 firstart = i;
81 }
82 else
83 firstart = article_first(firstart);
84 unread = 0;
85 #ifdef DEBUG
86 if (debug & DEB_CTLAREA_BITMAP) {
87 printf("\n%s\n",mybuf) FLUSH;
88 termdown(2);
89 for (i = article_first(absfirst); i < firstart; i = article_next(i)) {
90 if (article_unread(i))
91 printf("%ld ",(long)i) FLUSH;
92 }
93 }
94 #endif
95 i = firstart;
96 for ( ; (c = index(s,',')) != NULL; s = ++c) { /* for each range */
97 ART_NUM min, max;
98 *c = '\0'; /* do not let index see past comma */
99 h = index(s,'-');
100 min = atol(s);
101 if (min < firstart) /* make sure range is in range */
102 min = firstart;
103 if (min > lastart)
104 min = lastart+1;
105 for (; i < min; i = article_next(i)) {
106 ap = article_ptr(i);
107 if (ap->flags & AF_EXISTS) {
108 if (ap->autofl & AUTO_KILLS)
109 ap->flags &= ~AF_UNREAD;
110 else {
111 ap->flags |= AF_UNREAD;
112 unread++;
113 if (ap->autofl & AUTO_SELS)
114 select_article(ap, ap->autofl);
115 }
116 }
117 }
118 if (!h)
119 max = min;
120 else if ((max = atol(h+1)) < min)
121 max = min-1;
122 if (max > lastart)
123 max = lastart;
124 /* mark all arts in range as read */
125 for ( ; i <= max; i = article_next(i))
126 article_ptr(i)->flags &= ~AF_UNREAD;
127 #ifdef DEBUG
128 if (debug & DEB_CTLAREA_BITMAP) {
129 printf("\n%s\n",s) FLUSH;
130 termdown(2);
131 for (i = absfirst; i <= lastart; i++) {
132 if (!was_read(i))
133 printf("%ld ",(long)i) FLUSH;
134 }
135 }
136 #endif
137 i = article_next(max);
138 }
139 for (; i <= lastart; i = article_next(i)) {
140 ap = article_ptr(i);
141 if (ap->flags & AF_EXISTS) {
142 if (ap->autofl & AUTO_KILLS)
143 ap->flags &= ~AF_UNREAD;
144 else {
145 ap->flags |= AF_UNREAD;
146 unread++;
147 if (ap->autofl & AUTO_SELS)
148 select_article(ap, ap->autofl);
149 }
150 }
151 }
152 #ifdef DEBUG
153 if (debug & DEB_CTLAREA_BITMAP) {
154 fputs("\n(hit CR)",stdout) FLUSH;
155 termdown(1);
156 fgets(cmd_buf, sizeof cmd_buf, stdin);
157 }
158 #endif
159 if (mybuf != buf)
160 free(mybuf);
161 ngptr->toread = unread;
162 }
163
164 bool
set_firstart(s)165 set_firstart(s)
166 char* s;
167 {
168 while (*s == ' ') s++;
169
170 if (strnEQ(s,"1-",2)) { /* can we save some time here? */
171 firstart = atol(s+2)+1; /* process first range thusly */
172 if (firstart < absfirst)
173 firstart = absfirst;
174 return TRUE;
175 }
176
177 firstart = absfirst;
178 return FALSE;
179 }
180
181 /* reconstruct the .newsrc line in a human readable form */
182
183 void
bits_to_rc()184 bits_to_rc()
185 {
186 register char* s;
187 register char* mybuf = buf;
188 register ART_NUM i;
189 ART_NUM count=0;
190 int safelen = LBUFLEN - 32;
191
192 strcpy(buf,ngptr->rcline); /* start with the newsgroup name */
193 s = buf + ngptr->numoffset - 1; /* use s for buffer pointer */
194 *s++ = ngptr->subscribechar; /* put the requisite : or !*/
195 for (i = article_first(absfirst); i <= lastart; i = article_next(i)) {
196 if (article_unread(i))
197 break;
198 }
199 sprintf(s," 1-%ld,",(long)i-1);
200 s += strlen(s);
201 for (; i<=lastart; i++) { /* for each article in newsgroup */
202 if (s-mybuf > safelen) { /* running out of room? */
203 safelen *= 2;
204 if (mybuf == buf) { /* currently static? */
205 *s = '\0';
206 mybuf = safemalloc((MEM_SIZE)safelen + 32);
207 strcpy(mybuf,buf); /* so we must copy it */
208 s = mybuf + (s-buf);
209 /* fix the pointer, too */
210 }
211 else { /* just grow in place, if possible */
212 int oldlen = s - mybuf;
213 mybuf = saferealloc(mybuf,(MEM_SIZE)safelen + 32);
214 s = mybuf + oldlen;
215 }
216 }
217 if (!was_read(i)) /* still unread? */
218 count++; /* then count it */
219 else { /* article was read */
220 ART_NUM oldi;
221
222 sprintf(s,"%ld",(long)i); /* put out the min of the range */
223 s += strlen(s); /* keeping house */
224 oldi = i; /* remember this spot */
225 do i++; while (i <= lastart && was_read(i));
226 /* find 1st unread article or end */
227 i--; /* backup to last read article */
228 if (i > oldi) { /* range of more than 1? */
229 sprintf(s,"-%ld,",(long)i);
230 /* then it out as a range */
231 s += strlen(s); /* and housekeep */
232 }
233 else
234 *s++ = ','; /* otherwise, just a comma will do */
235 }
236 }
237 if (*(s-1) == ',') /* is there a final ','? */
238 s--; /* take it back */
239 *s++ = '\0'; /* and terminate string */
240 #ifdef DEBUG
241 if ((debug & DEB_NEWSRC_LINE) && !panic) {
242 printf("%s: %s\n",ngptr->rcline,ngptr->rcline+ngptr->numoffset) FLUSH;
243 printf("%s\n",mybuf) FLUSH;
244 termdown(2);
245 }
246 #endif
247 free(ngptr->rcline); /* return old rc line */
248 if (mybuf == buf) {
249 ngptr->rcline = safemalloc((MEM_SIZE)(s-buf)+1);
250 /* grab a new rc line */
251 strcpy(ngptr->rcline, buf); /* and load it */
252 }
253 else {
254 mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1);
255 /* be nice to the heap */
256 ngptr->rcline = mybuf;
257 }
258 *(ngptr->rcline + ngptr->numoffset - 1) = '\0';
259 if (ngptr->subscribechar == NEGCHAR)/* did they unsubscribe? */
260 ngptr->toread = TR_UNSUB; /* make line invisible */
261 else
262 ngptr->toread = (ART_UNREAD)count; /* otherwise, remember the count */
263 ngptr->rc->flags |= RF_RCCHANGED;
264 }
265
266 void
find_existing_articles()267 find_existing_articles()
268 {
269 ART_NUM an;
270 ARTICLE* ap;
271
272 #ifdef SUPPORT_NNTP
273 if (datasrc->flags & DF_REMOTE) {
274 /* Parse the LISTGROUP output and remember everything we find */
275 if (/*nntp_rover() ||*/ nntp_artnums()) {
276 /*char* s;*/
277
278 for (ap = article_ptr(article_first(absfirst));
279 ap && article_num(ap) <= lastart;
280 ap = article_nextp(ap))
281 ap->flags &= ~AF_EXISTS;
282 for (;;) {
283 if (nntp_gets(ser_line, sizeof ser_line) < 0)
284 break; /*$$*/
285 if (nntp_at_list_end(ser_line))
286 break;
287 an = (ART_NUM)atol(ser_line);
288 if (an < absfirst)
289 continue; /* Ignore some wacked-out NNTP servers */
290 ap = article_ptr(an);
291 if (!(ap->flags2 & AF2_BOGUS))
292 ap->flags |= AF_EXISTS;
293 #if 0
294 s = index(ser_line, ' ');
295 if (s)
296 rover_thread(article_ptr(an), s);
297 #endif
298 }
299 }
300 else if (first_subject && cached_all_in_range) {
301 if (!datasrc->ov_opened || datasrc->over_dir != NULL) {
302 for (ap = article_ptr(article_first(first_cached));
303 ap && article_num(ap) <= last_cached;
304 ap = article_nextp(ap))
305 {
306 if (ap->flags & AF_CACHED)
307 ap->flags |= AF_EXISTS;
308 }
309 }
310 for (an = absfirst; an < first_cached; an++) {
311 ap = article_ptr(an);
312 if (!(ap->flags2 & AF2_BOGUS))
313 ap->flags |= AF_EXISTS;
314 }
315 for (an = last_cached+1; an <= lastart; an++) {
316 ap = article_ptr(an);
317 if (!(ap->flags2 & AF2_BOGUS))
318 ap->flags |= AF_EXISTS;
319 }
320 }
321 else {
322 for (an = absfirst; an <= lastart; an++) {
323 ap = article_ptr(an);
324 if (!(ap->flags2 & AF2_BOGUS))
325 ap->flags |= AF_EXISTS;
326 }
327 }
328 }
329 else
330 #endif
331 {
332 ART_NUM first = lastart+1;
333 ART_NUM last = 0;
334 DIR* dirp;
335 Direntry_t* dp;
336 char ch;
337 long lnum;
338
339 /* Scan the directory to find which articles are present. */
340
341 if (!(dirp = opendir(".")))
342 return;
343
344 for (ap = article_ptr(article_first(absfirst));
345 ap && article_num(ap) <= lastart;
346 ap = article_nextp(ap))
347 ap->flags &= ~AF_EXISTS;
348
349 while ((dp = readdir(dirp)) != NULL) {
350 if (sscanf(dp->d_name, "%ld%c", &lnum, &ch) == 1) {
351 an = (ART_NUM)lnum;
352 if (an <= lastart && an >= absfirst) {
353 if (an < first)
354 first = an;
355 if (an > last)
356 last = an;
357 ap = article_ptr(an);
358 if (!(ap->flags2 & AF2_BOGUS))
359 ap->flags |= AF_EXISTS;
360 }
361 }
362 }
363 closedir(dirp);
364
365 ngptr->abs1st = first;
366 ngptr->ngmax = last;
367
368 if (first > absfirst) {
369 checkexpired(ngptr,first);
370 for (absfirst = article_first(absfirst);
371 absfirst < first;
372 absfirst = article_next(absfirst))
373 {
374 onemissing(article_ptr(absfirst));
375 }
376 absfirst = first;
377 }
378 lastart = last;
379 }
380
381 if (firstart < absfirst)
382 firstart = absfirst;
383 if (firstart > lastart)
384 firstart = lastart + 1;
385 if (first_cached < absfirst)
386 first_cached = absfirst;
387 if (last_cached < absfirst)
388 last_cached = absfirst - 1;
389 }
390
391 /* mark an article unread, keeping track of toread[] */
392
393 void
onemore(ap)394 onemore(ap)
395 ARTICLE* ap;
396 {
397 if (!(ap->flags & AF_UNREAD)) {
398 register ART_NUM artnum = article_num(ap);
399 check_first(artnum);
400 ap->flags |= AF_UNREAD;
401 ap->flags &= ~AF_DEL;
402 ngptr->toread++;
403 if (ap->subj) {
404 if (selected_only) {
405 if (ap->subj->flags & sel_mask) {
406 ap->flags |= sel_mask;
407 selected_count++;
408 }
409 } else
410 ap->subj->flags |= SF_VISIT;
411 }
412 }
413 }
414
415 /* mark an article read, keeping track of toread[] */
416
417 void
oneless(ap)418 oneless(ap)
419 ARTICLE* ap;
420 {
421 if (ap->flags & AF_UNREAD) {
422 ap->flags &= ~AF_UNREAD;
423 /* Keep selected_count accurate */
424 if (ap->flags & sel_mask) {
425 selected_count--;
426 ap->flags &= ~sel_mask;
427 }
428 if (ngptr->toread > TR_NONE)
429 ngptr->toread--;
430 }
431 }
432
433 void
oneless_artnum(artnum)434 oneless_artnum(artnum)
435 ART_NUM artnum;
436 {
437 ARTICLE* ap = article_find(artnum);
438 if (ap)
439 oneless(ap);
440 }
441
442 void
onemissing(ap)443 onemissing(ap)
444 ARTICLE* ap;
445 {
446 missing_count += (ap->flags & AF_UNREAD) != 0;
447 oneless(ap);
448 ap->flags = (ap->flags & ~(AF_HAS_RE|AF_YANKBACK|AF_FROMTRUNCED|AF_EXISTS))
449 | AF_CACHED|AF_THREADED;
450 }
451
452 /* mark an article as unread, with possible xref chasing */
453
454 void
unmark_as_read(ap)455 unmark_as_read(ap)
456 register ARTICLE* ap;
457 {
458 onemore(ap);
459 #ifdef MCHASE
460 if (ap->xrefs != nullstr && !(ap->flags & AF_MCHASE)) {
461 ap->flags |= AF_MCHASE;
462 chase_count++;
463 }
464 #endif
465 }
466
467 /* Mark an article as read in this newsgroup and possibly chase xrefs.
468 ** Don't call this on missing articles.
469 */
470 void
set_read(ap)471 set_read(ap)
472 register ARTICLE* ap;
473 {
474 oneless(ap);
475 if (!olden_days && ap->xrefs != nullstr && !(ap->flags & AF_KCHASE)) {
476 ap->flags |= AF_KCHASE;
477 chase_count++;
478 }
479 }
480
481 /* temporarily mark article as read. When newsgroup is exited, articles */
482 /* will be marked as unread. Called via M command */
483
484 void
delay_unmark(ap)485 delay_unmark(ap)
486 ARTICLE* ap;
487 {
488 if (!(ap->flags & AF_YANKBACK)) {
489 ap->flags |= AF_YANKBACK;
490 dmcount++;
491 }
492 }
493
494 /* mark article as read. If article is cross referenced to other */
495 /* newsgroups, mark them read there also. */
496
497 void
mark_as_read(ap)498 mark_as_read(ap)
499 register ARTICLE* ap;
500 {
501 oneless(ap);
502 if (ap->xrefs != nullstr && !(ap->flags & AF_KCHASE)) {
503 ap->flags |= AF_KCHASE;
504 chase_count++;
505 }
506 checkcount++; /* get more worried about crashes */
507 }
508
509 void
mark_missing_articles()510 mark_missing_articles()
511 {
512 register ARTICLE* ap;
513 for (ap = article_ptr(article_first(absfirst));
514 ap && article_num(ap) <= lastart;
515 ap = article_nextp(ap))
516 {
517 if (!(ap->flags & AF_EXISTS))
518 onemissing(ap);
519 }
520 }
521
522 /* keep firstart pointing at the first unread article */
523
524 void
check_first(min)525 check_first(min)
526 ART_NUM min;
527 {
528 if (min < absfirst)
529 min = absfirst;
530 if (min < firstart)
531 firstart = min;
532 }
533
534 /* bring back articles marked with M */
535
536 void
yankback()537 yankback()
538 {
539 if (dmcount) { /* delayed unmarks pending? */
540 if (panic)
541 ;
542 else if (gmode == 's')
543 sprintf(msg, "Returned %ld Marked article%s.",(long)dmcount,
544 PLURAL(dmcount));
545 else {
546 #ifdef VERBOSE
547 printf("\nReturning %ld Marked article%s...\n",(long)dmcount,
548 PLURAL(dmcount)) FLUSH;
549 #endif
550 termdown(2);
551 }
552 article_walk(yank_article, 0);
553 dmcount = 0;
554 }
555 }
556
557 static bool
yank_article(ptr,arg)558 yank_article(ptr, arg)
559 char* ptr;
560 int arg;
561 {
562 register ARTICLE* ap = (ARTICLE*)ptr;
563 if (ap->flags & AF_YANKBACK) {
564 unmark_as_read(ap);
565 if (selected_only)
566 select_article(ap, 0);
567 ap->flags &= ~AF_YANKBACK;
568 }
569 return 0;
570 }
571
572 int
chase_xrefs(until_key)573 chase_xrefs(until_key)
574 bool_int until_key;
575 {
576 if (!chase_count)
577 return 1;
578 if (until_key)
579 setspin(SPIN_BACKGROUND);
580
581 article_walk(check_chase, until_key);
582 chase_count = 0;
583 return 1;
584 }
585
586 static bool
check_chase(ptr,until_key)587 check_chase(ptr, until_key)
588 char* ptr;
589 int until_key;
590 {
591 register ARTICLE* ap = (ARTICLE*)ptr;
592
593 if (ap->flags & AF_KCHASE) {
594 chase_xref(article_num(ap),TRUE);
595 ap->flags &= ~AF_KCHASE;
596 if (!--chase_count)
597 return 1;
598 }
599 #ifdef MCHASE
600 if (ap->flags & AF_MCHASE) {
601 chase_xref(article_num(ap),TRUE);
602 ap->flags &= ~AF_MCHASE;
603 if (!--chase_count)
604 return 1;
605 }
606 #endif
607 if (until_key && input_pending())
608 return 1;
609 return 0;
610 }
611
612 /* run down xref list and mark as read or unread */
613
614 #ifndef DBM_XREFS
615 /*=-=-=-=*/
616 static int
chase_xref(artnum,markread)617 chase_xref(artnum,markread) /* The Xref-line-using version */
618 ART_NUM artnum;
619 int markread;
620 {
621 register char* xartnum;
622 register ART_NUM x;
623 char* xref_buf, *curxref;
624 char tmpbuf[128];
625
626 #ifdef DF_NOXREFS
627 if (datasrc->flags & DF_NOXREFS)
628 return 0;
629 #endif
630
631 if (inbackground())
632 spin(10);
633 else {
634 if (output_chase_phrase) {
635 # ifdef VERBOSE
636 IF(verbose)
637 fputs("\nChasing xrefs", stdout);
638 ELSE
639 # endif
640 # ifdef TERSE
641 fputs("\nXrefs", stdout);
642 # endif
643 termdown(1);
644 output_chase_phrase = 0;
645 }
646 putchar('.');
647 fflush(stdout);
648 }
649
650 xref_buf = fetchcache(artnum, XREF_LINE, FILL_CACHE);
651 if (!xref_buf || !*xref_buf)
652 return 0;
653
654 xref_buf = savestr(xref_buf);
655 # ifdef DEBUG
656 if (debug & DEB_XREF_MARKER) {
657 printf("Xref: %s\n",xref_buf) FLUSH;
658 termdown(1);
659 }
660 # endif
661 curxref = cpytill(tmpbuf,xref_buf,' ') + 1;
662 # ifdef VALIDATE_XREF_SITE
663 if (valid_xref_site(artnum,tmpbuf))
664 # endif
665 {
666 while (*curxref) { /* for each newsgroup */
667 curxref = cpytill(tmpbuf,curxref,' ');
668 xartnum = index(tmpbuf,':');
669 if (!xartnum)
670 break;
671 *xartnum++ = '\0';
672 if (!(x = atol(xartnum)))
673 continue;
674 if (strEQ(tmpbuf,ngname)) {/* is this the current newsgroup? */
675 if (x < absfirst || x > lastart)
676 continue;
677 if (markread)
678 oneless(article_ptr(x)); /* take care of old C newses */
679 # ifdef MCHASE
680 else
681 onemore(article_ptr(x));
682 # endif
683 } else {
684 if (markread) {
685 if (addartnum(datasrc,x,tmpbuf))
686 break;
687 }
688 # ifdef MCHASE
689 else
690 subartnum(datasrc,x,tmpbuf);
691 # endif
692 }
693 while (*curxref && isspace(*curxref))
694 curxref++;
695 }
696 }
697 free(xref_buf);
698 return 0;
699 }
700
701 /* Make sure the site name on Xref matches what inews thinks the site
702 * is. Check first against last inews_site. If it matches, fine.
703 * If not, fetch inews_site from current Path or Relay-Version line and
704 * check again. This is so that if the new administrator decides
705 * to change the system name as known to inews, rn will still do
706 * Xrefs correctly--each article need only match itself to be valid.
707 */
708 # ifdef VALIDATE_XREF_SITE
709 static bool
valid_xref_site(artnum,site)710 valid_xref_site(artnum, site)
711 ART_NUM artnum;
712 char* site;
713 {
714 static char* inews_site = NULL;
715 char* sitebuf;
716 char* s;
717
718 if (inews_site && strEQ(site,inews_site))
719 return TRUE;
720
721 if (inews_site)
722 free(inews_site);
723 #ifndef ANCIENT_NEWS
724 /* Grab the site from the first component of the Path line */
725 sitebuf = fetchlines(artnum,PATH_LINE);
726 if ((s = index(sitebuf, '!')) != NULL) {
727 *s = '\0';
728 inews_site = savestr(sitebuf);
729 }
730 #else /* ANCIENT_NEWS */
731 /* Grab the site from the Posting-Version line */
732 sitebuf = fetchlines(artnum,RVER_LINE);
733 if ((s = instr(sitebuf,"; site ",TRUE)) != NULL) {
734 char* t = index(s+7, '.');
735 if (t)
736 *t = '\0';
737 inews_site = savestr(s+7);
738 }
739 #endif /* ANCIENT_NEWS */
740 else
741 inews_site = savestr(nullstr);
742 free(sitebuf);
743
744 if (strEQ(site,inews_site))
745 return TRUE;
746
747 #ifdef DEBUG
748 if (debug) {
749 printf("Xref not from %s -- ignoring\n",inews_site) FLUSH;
750 termdown(1);
751 }
752 #endif
753 return FALSE;
754 }
755 # endif /* VALIDATE_XREF_SITE */
756
757 #else /* DBM_XREFS */
758
759 static int
chase_xref(artnum,markread)760 chase_xref(artnum,markread) /* The DBM version */
761 ART_NUM artnum;
762 int markread;
763 {
764 datum lhs, rhs;
765 datum fetch();
766 register char* idp;
767 char* ident_buf;
768 static FILE* hist_file = NULL;
769 long pos;
770 register char* xartnum;
771 register ART_NUM x;
772 char* xref_buf;
773 char* curxref;
774 char tmpbuf[128];
775
776 #ifdef DF_NOXREFS
777 if (datasrc->flags & DF_NOXREFS)
778 return;
779 #endif
780
781 if (inbackground())
782 spin(10);
783 else {
784 if (output_chase_phrase) {
785 # ifdef VERBOSE
786 IF(verbose)
787 fputs("\nChasing xrefs", stdout);
788 ELSE
789 # endif
790 # ifdef TERSE
791 fputs("\nXrefs", stdout);
792 # endif
793 termdown(1);
794 output_chase_phrase = 0;
795 }
796 putchar('.');
797 fflush(stdout);
798 }
799
800 xref_buf = fetchcache(artnum, NGS_LINE, FILL_CACHE);
801 if (!xref_buf || !*xref_buf)
802 return 0;
803
804 xref_buf = safemalloc((MEM_SIZE)BUFSIZ);
805 if (hist_file == NULL) { /* Init. file accesses */
806 # ifdef DEBUG
807 if (debug) {
808 printf("chase_xref: opening files\n");
809 termdown(1);
810 }
811 # endif
812 dbminit(filexp(ARTFILE));
813 if ((hist_file = fopen(filexp(ARTFILE), "r")) == NULL)
814 return 0;
815 }
816 ident_buf = fetchlines(artnum,MSGID_LINE); /* get Message-ID */
817 # ifdef DEBUG
818 if (debug) {
819 printf ("chase_xref: Message-ID: %s\n", ident_buf);
820 termdown(1);
821 }
822 # endif
823
824 if ((idp = index(ident_buf, '@')) != NULL) {
825 while (*++idp) /* make message-id case insensitive */
826 if (isupper(*idp))
827 *idp = tolower(*idp);
828 }
829 lhs.dptr = ident_buf; /* look up article by id */
830 lhs.dsize = strlen(lhs.dptr) + 1;
831 rhs = fetch(lhs); /* fetch the record */
832 if (rhs.dptr == NULL) /* if NULL, nothing there */
833 goto wild_goose;
834 bcopy(rhs.dptr,(char*)&pos, 4);
835 fseek(hist_file, pos, 0); /* datum returned is position in hist file */
836 fgets(xref_buf, BUFSIZ, hist_file);
837 # ifdef DEBUG
838 if (debug) {
839 printf ("Xref from history: %s\n", xref_buf);
840 termdown(1);
841 }
842 # endif
843 curxref = cpytill(tmpbuf, xref_buf, '\t') + 1;
844 curxref = cpytill(tmpbuf, curxref, '\t') + 1;
845 # ifdef DEBUG
846 if (debug) {
847 printf ("chase_xref: curxref: %s\n", curxref);
848 termdown(1);
849 }
850 # endif
851 while (*curxref) { /* for each newsgroup */
852 curxref = cpytill(tmpbuf,curxref,' ');
853 xartnum = index(tmpbuf,'/');
854 if (!xartnum) /* probably an old-style Xref */
855 break;
856 *xartnum++ = '\0';
857 if (!(x = atol(xartnum)))
858 continue;
859 if (strNE(tmpbuf,ngname)) { /* not the current newsgroup? */
860 if (markread) {
861 if (addartnum(datasrc,x,tmpbuf))
862 goto wild_goose;
863 }
864 # ifdef MCHASE
865 else
866 subartnum(datasrc,x,tmpbuf);
867 # endif
868 }
869 while (*curxref && isspace(*curxref))
870 curxref++;
871 }
872 wild_goose:
873 free(xref_buf);
874 free(ident_buf);
875 return 0;
876 }
877 #endif /* DBM_XREFS */
878