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