1 /* rcstuff.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 "list.h"
9 #include "util.h"
10 #include "util2.h"
11 #include "hash.h"
12 #include "cache.h"
13 #include "bits.h"
14 #include "ngdata.h"
15 #include "nntpclient.h"
16 #include "datasrc.h"
17 #include "nntp.h"
18 #include "term.h"
19 #include "final.h"
20 #include "trn.h"
21 #include "env.h"
22 #include "init.h"
23 #include "intrp.h"
24 #include "only.h"
25 #include "rcln.h"
26 #include "last.h"
27 #include "autosub.h"
28 #include "rt-select.h"
29 #include "rt-page.h"
30 #include "INTERN.h"
31 #include "rcstuff.h"
32 #include "rcstuff.ih"
33 
34 static bool foundany;
35 
36 bool
rcstuff_init()37 rcstuff_init()
38 {
39     MULTIRC* mptr = NULL;
40     int i;
41 
42     multirc_list = new_list(0,0,sizeof(MULTIRC),20,LF_ZERO_MEM|LF_SPARSE,NULL);
43 
44     if (trnaccess_mem) {
45 	NEWSRC* rp;
46 	char* s;
47 	char* section;
48 	char* cond;
49 	char** vals = prep_ini_words(rcgroups_ini);
50 	s = trnaccess_mem;
51 	while ((s = next_ini_section(s,&section,&cond)) != NULL) {
52 	    if (*cond && !check_ini_cond(cond))
53 		continue;
54 	    if (strncaseNE(section, "group ", 6))
55 		continue;
56 	    i = atoi(section+6);
57 	    if (i < 0)
58 		i = 0;
59 	    s = parse_ini_section(s, rcgroups_ini);
60 	    if (!s)
61 		break;
62 	    rp = new_newsrc(vals[RI_ID],vals[RI_NEWSRC],vals[RI_ADDGROUPS]);
63 	    if (rp) {
64 		MULTIRC* mp;
65 		NEWSRC* prev_rp;
66 
67 		mp = multirc_ptr(i);
68 		prev_rp = mp->first;
69 		if (!prev_rp)
70 		    mp->first = rp;
71 		else {
72 		    while (prev_rp->next)
73 			prev_rp = prev_rp->next;
74 		    prev_rp->next = rp;
75 		}
76 		mp->num = i;
77 		if (!mptr)
78 		    mptr = mp;
79 		/*rp->flags |= RF_USED;*/
80 	    }
81 	    /*else
82 		;$$complain?*/
83 	}
84 	free((char*)vals);
85 	free(trnaccess_mem);
86     }
87 
88     if (UseNewsrcSelector && !checkflag)
89 	return TRUE;
90 
91     foundany = FALSE;
92     if (mptr && !use_multirc(mptr))
93 	use_next_multirc(mptr);
94     if (!multirc) {
95 	mptr = multirc_ptr(0);
96 	mptr->first = new_newsrc("default",NULL,NULL);
97 	if (!use_multirc(mptr)) {
98 	    printf("Couldn't open any newsrc groups.  Is your access file ok?\n");
99 	    finalize(1);
100 	}
101     }
102     if (checkflag)			/* were we just checking? */
103 	finalize(foundany);		/* tell them what we found */
104     return foundany;
105 }
106 
107 NEWSRC*
new_newsrc(name,newsrc,add_ok)108 new_newsrc(name,newsrc,add_ok)
109 char* name;
110 char* newsrc;
111 char* add_ok;
112 {
113     char tmpbuf[CBUFLEN];
114     NEWSRC* rp;
115     DATASRC* dp;
116 
117     if (!name || !*name)
118 	return NULL;
119 
120     if (!newsrc || !*newsrc) {
121 	newsrc = getenv("NEWSRC");
122 	if (!newsrc)
123 	    newsrc = RCNAME;
124     }
125 
126     dp = get_datasrc(name);
127     if (!dp)
128 	return NULL;
129 
130     rp = (NEWSRC*)safemalloc(sizeof (NEWSRC));
131     bzero((char*)rp, sizeof (NEWSRC));
132     rp->datasrc = dp;
133     rp->name = savestr(filexp(newsrc));
134     sprintf(tmpbuf, RCNAME_OLD, rp->name);
135     rp->oldname = savestr(tmpbuf);
136     sprintf(tmpbuf, RCNAME_NEW, rp->name);
137     rp->newname = savestr(tmpbuf);
138 
139     switch (add_ok? *add_ok : 'y') {
140     case 'n':
141     case 'N':
142 	break;
143     default:
144 	if (dp->flags & DF_ADD_OK)
145 	    rp->flags |= RF_ADD_NEWGROUPS;
146 	/* FALL THROUGH */
147     case 'm':
148     case 'M':
149 	rp->flags |= RF_ADD_GROUPS;
150 	break;
151     }
152     return rp;
153 }
154 
155 bool
use_multirc(mp)156 use_multirc(mp)
157 MULTIRC* mp;
158 {
159     NEWSRC* rp;
160     bool had_trouble = FALSE;
161     bool had_success = FALSE;
162 
163     for (rp = mp->first; rp; rp = rp->next) {
164 	if ((rp->datasrc->flags & DF_UNAVAILABLE) || !lock_newsrc(rp)
165 	 || !open_datasrc(rp->datasrc) || !open_newsrc(rp)) {
166 	    unlock_newsrc(rp);
167 	    had_trouble = TRUE;
168 	}
169 	else {
170 	    rp->datasrc->flags |= DF_ACTIVE;
171 	    rp->flags |= RF_ACTIVE;
172 	    had_success = TRUE;
173 	}
174     }
175     if (had_trouble)
176 	get_anything();
177     if (!had_success)
178 	return FALSE;
179     multirc = mp;
180 #ifdef NO_FILELINKS
181     if (!write_newsrcs(multirc))
182 	get_anything();
183 #endif
184     return TRUE;
185 }
186 
187 void
unuse_multirc(mptr)188 unuse_multirc(mptr)
189 MULTIRC* mptr;
190 {
191     NEWSRC* rp;
192 
193     if (!mptr)
194 	return;
195 
196     write_newsrcs(mptr);
197 
198     for (rp = mptr->first; rp; rp = rp->next) {
199 	unlock_newsrc(rp);
200 	rp->flags &= ~RF_ACTIVE;
201 	rp->datasrc->flags &= ~DF_ACTIVE;
202     }
203     if (ngdata_list) {
204 	close_cache();
205 	hashdestroy(newsrc_hash);
206 	walk_list(ngdata_list, clear_ngitem, 0);
207 	delete_list(ngdata_list);
208 	ngdata_list = NULL;
209 	first_ng = NULL;
210 	last_ng = NULL;
211 	ngptr = NULL;
212 	current_ng = NULL;
213 	recent_ng = NULL;
214 	starthere = NULL;
215 	sel_page_np = NULL;
216     }
217     ngdata_cnt = 0;
218     newsgroup_cnt = 0;
219     newsgroup_toread = 0;
220     multirc = NULL;
221 }
222 
223 bool
use_next_multirc(mptr)224 use_next_multirc(mptr)
225 MULTIRC* mptr;
226 {
227     register MULTIRC* mp = multirc_ptr(mptr->num);
228 
229     unuse_multirc(mptr);
230 
231     for (;;) {
232 	mp = multirc_next(mp);
233 	if (!mp)
234 	    mp = multirc_low();
235 	if (mp == mptr) {
236 	    use_multirc(mptr);
237 	    return FALSE;
238 	}
239 	if (use_multirc(mp))
240 	    break;
241     }
242     return TRUE;
243 }
244 
245 bool
use_prev_multirc(mptr)246 use_prev_multirc(mptr)
247 MULTIRC* mptr;
248 {
249     register MULTIRC* mp = multirc_ptr(mptr->num);
250 
251     unuse_multirc(mptr);
252 
253     for (;;) {
254 	mp = multirc_prev(mp);
255 	if (!mp)
256 	    mp = multirc_high();
257 	if (mp == mptr) {
258 	    use_multirc(mptr);
259 	    return FALSE;
260 	}
261 	if (use_multirc(mp))
262 	    break;
263     }
264     return TRUE;
265 }
266 
267 char*
multirc_name(mp)268 multirc_name(mp)
269 register MULTIRC* mp;
270 {
271     char* cp;
272     if (mp->first->next)
273 	return "<each-newsrc>";
274     if ((cp = rindex(mp->first->name, '/')) != NULL)
275 	return cp+1;
276     return mp->first->name;
277 }
278 
279 static bool
clear_ngitem(cp,arg)280 clear_ngitem(cp, arg)
281 char* cp;
282 int arg;
283 {
284     NGDATA* ncp = (NGDATA*)cp;
285 
286     if (ncp->rcline != NULL) {
287 	if (!checkflag)
288 	    free(ncp->rcline);
289 	ncp->rcline = NULL;
290     }
291     return 0;
292 }
293 
294 /* make sure there is no trn out there reading this newsrc */
295 
296 static bool
lock_newsrc(rp)297 lock_newsrc(rp)
298 NEWSRC* rp;
299 {
300     long processnum = 0;
301     char* runninghost = "(Unknown)";
302     char* s;
303 
304     if (checkflag)
305 	return TRUE;
306 
307     s = filexp(RCNAME);
308     if (strEQ(rp->name, s))
309 	rp->lockname = savestr(filexp(LOCKNAME));
310     else {
311 	sprintf(buf, RCNAME_INFO, rp->name);
312 	rp->infoname = savestr(buf);
313 	sprintf(buf, RCNAME_LOCK, rp->name);
314 	rp->lockname = savestr(buf);
315     }
316 
317     tmpfp = fopen(rp->lockname,"r");
318     if (tmpfp != NULL) {
319 	if (fgets(buf,LBUFLEN,tmpfp)) {
320 	    processnum = atol(buf);
321 	    if (fgets(buf,LBUFLEN,tmpfp) && *buf
322 	     && *(s = buf + strlen(buf) - 1) == '\n') {
323 		*s = '\0';
324 		runninghost = buf;
325 	    }
326 	}
327 	fclose(tmpfp);
328     }
329     if (processnum) {
330 #ifndef MSDOS
331 #ifdef VERBOSE
332 	IF(verbose)
333 	    printf("\nThe requested newsrc is locked by process %ld on host %s.\n",
334 		   processnum, runninghost) FLUSH;
335 	ELSE
336 #endif
337 #ifdef TERSE
338 	    printf("\nNewsrc locked by %ld on host %s.\n",processnum,runninghost) FLUSH;
339 #endif
340 	termdown(2);
341 	if (strNE(runninghost,localhost)) {
342 #ifdef VERBOSE
343 	    IF(verbose)
344 		printf("\n\
345 Since that's not the same host as this one (%s), we must\n\
346 assume that process still exists.  To override this check, remove\n\
347 the lock file: %s\n", localhost, rp->lockname) FLUSH;
348 	    ELSE
349 #endif
350 #ifdef TERSE
351 		printf("\nThis host (%s) doesn't match.\nCan't unlock %s.\n",
352 		       localhost, rp->lockname) FLUSH;
353 #endif
354 	    termdown(2);
355 	    if (bizarre)
356 		resetty();
357 	    finalize(0);
358 	}
359 	if (processnum == our_pid) {
360 #ifdef VERBOSE
361 	    IF(verbose)
362 		printf("\n\
363 Hey, that *my* pid!  Your access file is trying to use the same newsrc\n\
364 more than once.\n") FLUSH;
365 	    ELSE
366 #endif
367 #ifdef TERSE
368 		printf("\nAccess file error (our pid detected).\n") FLUSH;
369 #endif
370 	    termdown(2);
371 	    return FALSE;
372 	}
373 	if (kill(processnum, 0) != 0) {
374 	    /* Process is apparently gone */
375 	    sleep(2);
376 #ifdef VERBOSE
377 	    IF(verbose)
378 		fputs("\n\
379 That process does not seem to exist anymore.  The count of read articles\n\
380 may be incorrect in the last newsgroup accessed by that other (defunct)\n\
381 process.\n\n",stdout) FLUSH;
382 	    ELSE
383 #endif
384 #ifdef TERSE
385 		fputs("\nProcess crashed.\n",stdout) FLUSH;
386 #endif
387 	    if (lastngname) {
388 #ifdef VERBOSE
389 		IF(verbose)
390 		    printf("(The last newsgroup accessed was %s.)\n\n",
391 			   lastngname) FLUSH;
392 		ELSE
393 #endif
394 #ifdef TERSE
395 		    printf("(In %s.)\n\n",lastngname) FLUSH;
396 #endif
397 	    }
398 	    termdown(2);
399 	    get_anything();
400 	    newline();
401 	}
402 	else {
403 #ifdef VERBOSE
404 	    IF(verbose)
405 		printf("\n\
406 It looks like that process still exists.  To override this, remove\n\
407 the lock file: %s\n", rp->lockname) FLUSH;
408 	    ELSE
409 #endif
410 #ifdef TERSE
411 		printf("\nCan't unlock %s.\n", rp->lockname) FLUSH;
412 #endif
413 	    termdown(2);
414 	    if (bizarre)
415 		resetty();
416 	    finalize(0);
417 	}
418 #endif
419     }
420     tmpfp = fopen(rp->lockname,"w");
421     if (tmpfp == NULL) {
422 	printf(cantcreate,rp->lockname) FLUSH;
423 	sig_catcher(0);
424     }
425     fprintf(tmpfp,"%ld\n%s\n",our_pid,localhost);
426     fclose(tmpfp);
427     return TRUE;
428 }
429 
430 static void
unlock_newsrc(rp)431 unlock_newsrc(rp)
432 NEWSRC* rp;
433 {
434     safefree0(rp->infoname);
435     if (rp->lockname) {
436  	UNLINK(rp->lockname);
437 	free(rp->lockname);
438 	rp->lockname = NULL;
439     }
440 }
441 
442 static bool
open_newsrc(rp)443 open_newsrc(rp)
444 NEWSRC* rp;
445 {
446     register NGDATA* np;
447     NGDATA* prev_np;
448     char* some_buf;
449     long length;
450     FILE* rcfp;
451     HASHDATUM data;
452 
453     /* make sure the .newsrc file exists */
454 
455     if ((rcfp = fopen(rp->name,"r")) == NULL) {
456 	rcfp = fopen(rp->name,"w+");
457 	if (rcfp == NULL) {
458 	    printf("\nCan't create %s.\n", rp->name) FLUSH;
459 	    termdown(2);
460 	    return FALSE;
461 	}
462 	some_buf = SUBSCRIPTIONS;
463 #ifdef SUPPORT_NNTP
464 	if ((rp->datasrc->flags & DF_REMOTE)
465 	 && nntp_list("SUBSCRIPTIONS",nullstr,0) == 1) {
466 	    do {
467 		fputs(ser_line,rcfp);
468 		fputc('\n',rcfp);
469 		if (nntp_gets(ser_line, sizeof ser_line) < 0)
470 		    break;
471 	    } while (!nntp_at_list_end(ser_line));
472 	}
473 #endif
474 	ElseIf (*some_buf && (tmpfp = fopen(filexp(some_buf),"r")) != NULL) {
475 	    while (fgets(buf,sizeof buf,tmpfp))
476 		fputs(buf,rcfp);
477 	    fclose(tmpfp);
478 	}
479 	fseek(rcfp, 0L, 0);
480     }
481     else {
482 	/* File exists; if zero length and backup isn't, complain */
483 	if (fstat(fileno(rcfp),&filestat) < 0) {
484 	    perror(rp->name);
485 	    return FALSE;
486 	}
487 	if (filestat.st_size == 0
488 	 && stat(rp->oldname,&filestat) >= 0 && filestat.st_size > 0) {
489 	    printf("Warning: %s is zero length but %s is not.\n",
490 		   rp->name,rp->oldname);
491 	    printf("Either recover your newsrc or else remove the backup copy.\n");
492 	    termdown(2);
493 	    return FALSE;
494 	}
495 	/* unlink backup file name and backup current name */
496 	UNLINK(rp->oldname);
497 #ifndef NO_FILELINKS
498 	safelink(rp->name,rp->oldname);
499 #endif
500     }
501 
502     if (!ngdata_list) {
503 	/* allocate memory for rc file globals */
504 	ngdata_list = new_list(0, 0, sizeof (NGDATA), 200, 0, init_ngnode);
505 	newsrc_hash = hashcreate(3001, rcline_cmp);
506     }
507 
508     if (ngdata_cnt)
509 	prev_np = ngdata_ptr(ngdata_cnt-1);
510     else
511 	prev_np = NULL;
512 
513     /* read in the .newsrc file */
514 
515     while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != NULL) {
516 	length = len_last_line_got;	/* side effect of get_a_line */
517 	if (length <= 1)		/* only a newline??? */
518 	    continue;
519 	np = ngdata_ptr(ngdata_cnt++);
520 	if (prev_np)
521 	    prev_np->next = np;
522 	else
523 	    first_ng = np;
524 	np->prev = prev_np;
525 	prev_np = np;
526 	np->rc = rp;
527 	newsgroup_cnt++;
528 	if (some_buf[length-1] == '\n')
529 	    some_buf[--length] = '\0';	/* wipe out newline */
530 	if (some_buf == buf)
531 	    np->rcline = savestr(some_buf);  /* make semipermanent copy */
532 	else {
533 	    /*NOSTRICT*/
534 #ifndef lint
535 	    some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
536 #endif
537 	    np->rcline = some_buf;
538 	}
539 	if (*some_buf == ' ' || *some_buf == '\t'
540 	 || strnEQ(some_buf,"options",7)) {	/* non-useful line? */
541 	    np->toread = TR_JUNK;
542 	    np->subscribechar = ' ';
543 	    np->numoffset = 0;
544 	    continue;
545 	}
546 	parse_rcline(np);
547 	data = hashfetch(newsrc_hash, np->rcline, np->numoffset - 1);
548 	if (data.dat_ptr) {
549 	    np->toread = TR_IGNORE;
550 	    continue;
551 	}
552 	if (np->subscribechar == NEGCHAR) {
553 	    np->toread = TR_UNSUB;
554 	    sethash(np);
555 	    continue;
556 	}
557 	newsgroup_toread++;
558 
559 	/* now find out how much there is to read */
560 
561 	if (!inlist(buf) || (suppress_cn && foundany && !paranoid))
562 	    np->toread = TR_NONE;	/* no need to calculate now */
563 	else
564 	    set_toread(np, ST_LAX);
565 	if (np->toread > TR_NONE) {	/* anything unread? */
566 	    if (!foundany) {
567 		starthere = np;
568 		foundany = TRUE;	/* remember that fact*/
569 	    }
570 	    if (suppress_cn) {		/* if no listing desired */
571 		if (checkflag)		/* if that is all they wanted */
572 		    finalize(1);	/* then bomb out */
573 	    }
574 	    else {
575 #ifdef VERBOSE
576 		IF(verbose)
577 		    printf("Unread news in %-40s %5ld article%s\n",
578 			np->rcline,(long)np->toread,PLURAL(np->toread)) FLUSH;
579 		ELSE
580 #endif
581 #ifdef TERSE
582 		    printf("%s: %ld article%s\n",
583 			np->rcline,(long)np->toread,PLURAL(np->toread)) FLUSH;
584 #endif
585 		termdown(1);
586 		if (int_count) {
587 		    countdown = 1;
588 		    int_count = 0;
589 		}
590 		if (countdown) {
591 		    if (!--countdown) {
592 			fputs("etc.\n",stdout) FLUSH;
593 			if (checkflag)
594 			    finalize(1);
595 			suppress_cn = TRUE;
596 		    }
597 		}
598 	    }
599 	}
600 	sethash(np);
601     }
602     if (prev_np) {
603 	prev_np->next = NULL;
604 	last_ng = prev_np;
605     }
606     fclose(rcfp);			/* close .newsrc */
607 #ifdef NO_FILELINKS
608     UNLINK(rp->oldname);
609     RENAME(rp->name,rp->oldname);
610     rp->flags |= RF_RCCHANGED;
611 #endif
612     if (rp->infoname) {
613 	if ((tmpfp = fopen(rp->infoname,"r")) != NULL) {
614 	    if (fgets(buf,sizeof buf,tmpfp) != NULL) {
615 		long actnum, descnum;
616 		char* s;
617 		buf[strlen(buf)-1] = '\0';
618 		if ((s = index(buf, ':')) != NULL && s[1] == ' ' && s[2]) {
619 		    safefree0(lastngname);
620 		    lastngname = savestr(s+2);
621 		}
622 		if (fscanf(tmpfp,"New-Group-State: %ld,%ld,%ld",
623 			   &lastnewtime,&actnum,&descnum) == 3) {
624 		    rp->datasrc->act_sf.recent_cnt = actnum;
625 		    rp->datasrc->desc_sf.recent_cnt = descnum;
626 		}
627 	    }
628 	}
629     }
630     else {
631 	readlast();
632 #ifdef SUPPORT_NNTP
633 	if (rp->datasrc->flags & DF_REMOTE) {
634 	    rp->datasrc->act_sf.recent_cnt = lastactsiz;
635 	    rp->datasrc->desc_sf.recent_cnt = lastextranum;
636 	}
637 	else
638 #endif
639 	{
640 	    rp->datasrc->act_sf.recent_cnt = lastextranum;
641 	    rp->datasrc->desc_sf.recent_cnt = 0;
642 	}
643     }
644     rp->datasrc->lastnewgrp = lastnewtime;
645 
646     if (paranoid && !checkflag)
647 	cleanup_newsrc(rp);
648     return TRUE;
649 }
650 
651 /* Initialize the memory for an entire node's worth of article's */
652 static void
init_ngnode(list,node)653 init_ngnode(list, node)
654 LIST* list;
655 LISTNODE* node;
656 {
657     register ART_NUM i;
658     register NGDATA* np;
659     bzero(node->data, list->items_per_node * list->item_size);
660     for (i = node->low, np = (NGDATA*)node->data; i <= node->high; i++, np++)
661 	np->num = i;
662 }
663 
664 static void
parse_rcline(np)665 parse_rcline(np)
666 register NGDATA* np;
667 {
668     char* s;
669     int len;
670 
671     for (s=np->rcline; *s && *s!=':' && *s!=NEGCHAR && !isspace(*s); s++) ;
672     len = s - np->rcline;
673     if ((!*s || isspace(*s)) && !checkflag) {
674 #ifndef lint
675 	np->rcline = saferealloc(np->rcline,(MEM_SIZE)len + 3);
676 #endif
677 	s = np->rcline + len;
678 	strcpy(s, ": ");
679     }
680     if (*s == ':' && s[1] && s[2] == '0') {
681 	np->flags |= NF_UNTHREADED;
682 	s[2] = '1';
683     }
684     np->subscribechar = *s;		/* salt away the : or ! */
685     np->numoffset = len + 1;		/* remember where the numbers are */
686     *s = '\0';			/* null terminate newsgroup name */
687 }
688 
689 void
abandon_ng(np)690 abandon_ng(np)
691 NGDATA* np;
692 {
693     char* some_buf = NULL;
694     FILE* rcfp;
695 
696     /* open newsrc backup copy and try to find the prior value for the group. */
697     if ((rcfp = fopen(np->rc->oldname, "r")) != NULL) {
698 	int length = np->numoffset - 1;
699 
700 	while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != NULL) {
701 	    if (len_last_line_got <= 0)
702 		continue;
703 	    some_buf[len_last_line_got-1] = '\0';	/* wipe out newline */
704 	    if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR)
705 	     && strnEQ(np->rcline, some_buf, length)) {
706 		break;
707 	    }
708 	    if (some_buf != buf)
709 		free(some_buf);
710 	}
711 	fclose(rcfp);
712     } else if (errno != ENOENT) {
713 	printf("Unable to open %s.\n", np->rc->oldname) FLUSH;
714 	termdown(1);
715 	return;
716     }
717     if (some_buf == NULL) {
718 	some_buf = np->rcline + np->numoffset;
719 	if (*some_buf == ' ')
720 	    some_buf++;
721 	*some_buf = '\0';
722 	np->abs1st = 0;		/* force group to be re-calculated */
723     }
724     else {
725 	free(np->rcline);
726 	if (some_buf == buf)
727 	    np->rcline = savestr(some_buf);
728 	else {
729 	    /*NOSTRICT*/
730 #ifndef lint
731 	    some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got));
732 #endif /* lint */
733 	    np->rcline = some_buf;
734 	}
735     }
736     parse_rcline(np);
737     if (np->subscribechar == NEGCHAR)
738 	np->subscribechar = ':';
739     np->rc->flags |= RF_RCCHANGED;
740     set_toread(np, ST_LAX);
741 }
742 
743 /* try to find or add an explicitly specified newsgroup */
744 /* returns TRUE if found or added, FALSE if not. */
745 /* assumes that we are chdir'ed to NEWSSPOOL */
746 
747 bool
get_ng(what,flags)748 get_ng(what, flags)
749 char* what;
750 int flags;
751 {
752     char* ntoforget;
753     char promptbuf[128];
754     int autosub;
755 
756 #ifdef VERBOSE
757     IF(verbose)
758 	ntoforget = "Type n to forget about this newsgroup.\n";
759     ELSE
760 #endif
761 #ifdef TERSE
762 	ntoforget = "n to forget it.\n";
763 #endif
764     if (index(what,'/')) {
765 	dingaling();
766 	printf("\nBad newsgroup name.\n") FLUSH;
767 	termdown(2);
768       check_fuzzy_match:
769 #ifdef EDIT_DISTANCE
770 	if (fuzzyGet && (flags & GNG_FUZZY)) {
771 	    flags &= ~GNG_FUZZY;
772 	    if (find_close_match())
773 		what = ngname;
774 	    else
775 		return FALSE;
776 	} else
777 #endif
778 	    return FALSE;
779     }
780     set_ngname(what);
781     ngptr = find_ng(ngname);
782     if (ngptr == NULL) {		/* not in .newsrc? */
783 	NEWSRC* rp;
784 	for (rp = multirc->first; rp; rp = rp->next) {
785 	    if (!ALLBITS(rp->flags, RF_ADD_GROUPS | RF_ACTIVE))
786 		continue;
787 	    /*$$ this may scan a datasrc multiple times... */
788 	    if (find_actgrp(rp->datasrc,buf,ngname,ngname_len,(ART_NUM)0))
789 		break;  /*$$ let them choose which server */
790 	}
791 	if (!rp) {
792 	    dingaling();
793 #ifdef VERBOSE
794 	    IF(verbose)
795 		printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH;
796 	    ELSE
797 #endif
798 #ifdef TERSE
799 		printf("\nNo %s!\n",ngname) FLUSH;
800 #endif
801 	    termdown(2);
802 	    if (novice_delays)
803 		sleep(2);
804 	    goto check_fuzzy_match;
805 	}
806 	if (mode != 'i' || !(autosub = auto_subscribe(ngname)))
807 	    autosub = addnewbydefault;
808 	if (autosub) {
809 	    if (append_unsub) {
810 		printf("(Adding %s to end of your .newsrc %ssubscribed)\n",
811 		       ngname, (autosub == ADDNEW_SUB)? nullstr : "un") FLUSH;
812 		termdown(1);
813 		ngptr = add_newsgroup(rp, ngname, autosub);
814 	    } else {
815 		if (autosub == ADDNEW_SUB) {
816 		    printf("(Subscribing to %s)\n", ngname) FLUSH;
817 		    termdown(1);
818 		    ngptr = add_newsgroup(rp, ngname, autosub);
819 		} else {
820 		    printf("(Ignoring %s)\n", ngname) FLUSH;
821 		    termdown(1);
822 		    return FALSE;
823 		}
824 	    }
825 	    flags &= ~GNG_RELOC;
826 	} else {
827 #ifdef VERBOSE
828 	    IF(verbose)
829 		sprintf(promptbuf,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname);
830 	    ELSE
831 #endif
832 #ifdef TERSE
833 		sprintf(promptbuf,"\nSubscribe %s?",ngname);
834 #endif
835 reask_add:
836 	    in_char(promptbuf,'A',"ynYN");
837 #ifdef VERIFY
838 	    printcmd();
839 #endif
840 	    newline();
841 	    if (*buf == 'h') {
842 #ifdef VERBOSE
843 		IF(verbose) {
844 		    printf("Type y or SP to subscribe to %s.\n\
845 Type Y to subscribe to this and all remaining new groups.\n\
846 Type N to leave all remaining new groups unsubscribed.\n", ngname) FLUSH;
847 		    termdown(3);
848 		}
849 		ELSE
850 #endif
851 #ifdef TERSE
852 		{
853 		    fputs("\
854 y or SP to subscribe, Y to subscribe all new groups, N to unsubscribe all\n",
855 			  stdout) FLUSH;
856 		    termdown(1);
857 		}
858 #endif
859 		fputs(ntoforget,stdout) FLUSH;
860 		termdown(1);
861 		goto reask_add;
862 	    }
863 	    else if (*buf == 'n' || *buf == 'q') {
864 		if (append_unsub)
865 		    ngptr = add_newsgroup(rp, ngname, NEGCHAR);
866 		return FALSE;
867 	    }
868 	    else if (*buf == 'y') {
869 		ngptr = add_newsgroup(rp, ngname, ':');
870 		flags |= GNG_RELOC;
871 	    }
872 	    else if (*buf == 'Y') {
873 		addnewbydefault = ADDNEW_SUB;
874 		if (append_unsub)
875 		    printf("(Adding %s to end of your .newsrc subscribed)\n",
876 			   ngname) FLUSH;
877 		else
878 		    printf("(Subscribing to %s)\n", ngname) FLUSH;
879 		termdown(1);
880 		ngptr = add_newsgroup(rp, ngname, ':');
881 		flags &= ~GNG_RELOC;
882 	    }
883 	    else if (*buf == 'N') {
884 		addnewbydefault = ADDNEW_UNSUB;
885 		if (append_unsub) {
886 		    printf("(Adding %s to end of your .newsrc unsubscribed)\n",
887 			   ngname) FLUSH;
888 		    termdown(1);
889 		    ngptr = add_newsgroup(rp, ngname, NEGCHAR);
890 		    flags &= ~GNG_RELOC;
891 		} else {
892 		    printf("(Ignoring %s)\n", ngname) FLUSH;
893 		    termdown(1);
894 		    return FALSE;
895 		}
896 	    }
897 	    else {
898 		fputs(hforhelp,stdout) FLUSH;
899 		termdown(1);
900 		settle_down();
901 		goto reask_add;
902 	    }
903 	}
904     }
905     else if (mode == 'i')		/* adding new groups during init? */
906 	return FALSE;
907     else if (ngptr->subscribechar == NEGCHAR) {/* unsubscribed? */
908 #ifdef VERBOSE
909 	IF(verbose)
910 	    sprintf(promptbuf,
911 "\nNewsgroup %s is unsubscribed -- resubscribe?",ngname)
912   FLUSH;
913 	ELSE
914 #endif
915 #ifdef TERSE
916 	    sprintf(promptbuf,"\nResubscribe %s?",ngname)
917 	      FLUSH;
918 #endif
919 reask_unsub:
920 	in_char(promptbuf,'R',"yn");
921 #ifdef VERIFY
922 	printcmd();
923 #endif
924 	newline();
925 	if (*buf == 'h') {
926 #ifdef VERBOSE
927 	    IF(verbose)
928 		printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH;
929 	    ELSE
930 #endif
931 #ifdef TERSE
932 		fputs("y or SP to resubscribe.\n",stdout) FLUSH;
933 #endif
934 	    fputs(ntoforget,stdout) FLUSH;
935 	    termdown(2);
936 	    goto reask_unsub;
937 	}
938 	else if (*buf == 'n' || *buf == 'q') {
939 	    return FALSE;
940 	}
941 	else if (*buf == 'y') {
942 	    register char* cp;
943 	    cp = ngptr->rcline + ngptr->numoffset;
944 	    ngptr->flags = (*cp && cp[1] == '0' ? NF_UNTHREADED : 0);
945 	    ngptr->subscribechar = ':';
946 	    ngptr->rc->flags |= RF_RCCHANGED;
947 	    flags &= ~GNG_RELOC;
948 	}
949 	else {
950 	    fputs(hforhelp,stdout) FLUSH;
951 	    termdown(1);
952 	    settle_down();
953 	    goto reask_unsub;
954 	}
955     }
956 
957     /* now calculate how many unread articles in newsgroup */
958 
959     set_toread(ngptr, ST_STRICT);
960 #ifdef RELOCATE
961     if (flags & GNG_RELOC) {
962 	if (!relocate_newsgroup(ngptr,-1))
963 	    return FALSE;
964     }
965 #endif
966     return ngptr->toread >= TR_NONE;
967 }
968 
969 /* add a newsgroup to the newsrc file (eventually) */
970 
971 static NGDATA*
add_newsgroup(rp,ngn,c)972 add_newsgroup(rp, ngn, c)
973 NEWSRC* rp;
974 char* ngn;
975 char_int c;
976 {
977     register NGDATA* np;
978 
979     np = ngdata_ptr(ngdata_cnt++);
980     np->prev = last_ng;
981     if (last_ng)
982 	last_ng->next = np;
983     else
984 	first_ng = np;
985     np->next = NULL;
986     last_ng = np;
987     newsgroup_cnt++;
988 
989     np->rc = rp;
990     np->numoffset = strlen(ngn) + 1;
991     np->rcline = safemalloc((MEM_SIZE)(np->numoffset + 2));
992     strcpy(np->rcline,ngn);		/* and copy over the name */
993     strcpy(np->rcline + np->numoffset, " ");
994     np->subscribechar = c;		/* subscribe or unsubscribe */
995     if (c != NEGCHAR)
996 	newsgroup_toread++;
997     np->toread = TR_NONE;		/* just for prettiness */
998     sethash(np);			/* so we can find it again */
999     rp->flags |= RF_RCCHANGED;
1000     return np;
1001 }
1002 
1003 #ifdef RELOCATE
1004 bool
relocate_newsgroup(move_np,newnum)1005 relocate_newsgroup(move_np,newnum)
1006 NGDATA* move_np;
1007 NG_NUM newnum;
1008 {
1009     NGDATA* np;
1010     int i;
1011     char* dflt = (move_np!=current_ng ? "$^.Lq" : "$^Lq");
1012     int save_sort = sel_sort;
1013 
1014     if (sel_newsgroupsort != SS_NATURAL) {
1015 	if (newnum < 0) {
1016 	    /* ask if they want to keep the current order */
1017 	    in_char("Sort newsrc(s) using current sort order?", 'D', "yn"); /*$$ !'D' */
1018 #ifdef VERIFY
1019 	    printcmd();
1020 #endif
1021 	    newline();
1022 	    if (*buf == 'y')
1023 		set_selector(SM_NEWSGROUP, SS_NATURAL);
1024 	    else {
1025 		sel_sort = SS_NATURAL;
1026 		sel_direction = 1;
1027 		sort_newsgroups();
1028 	    }
1029 	}
1030 	else {
1031 	    sel_sort = SS_NATURAL;
1032 	    sel_direction = 1;
1033 	    sort_newsgroups();
1034 	}
1035     }
1036 
1037     starthere = NULL;			/* Disable this optimization */
1038     if (move_np != last_ng) {
1039 	if (move_np->prev)
1040 	    move_np->prev->next = move_np->next;
1041 	else
1042 	    first_ng = move_np->next;
1043 	move_np->next->prev = move_np->prev;
1044 
1045 	move_np->prev = last_ng;
1046 	move_np->next = NULL;
1047 	last_ng->next = move_np;
1048 	last_ng = move_np;
1049     }
1050 
1051     /* Renumber the groups according to current order */
1052     for (np = first_ng, i = 0; np; np = np->next, i++)
1053 	np->num = i;
1054     move_np->rc->flags |= RF_RCCHANGED;
1055 
1056     if (newnum < 0) {
1057       reask_reloc:
1058 	unflush_output();		/* disable any ^O in effect */
1059 #ifdef VERBOSE
1060 	IF(verbose)
1061 	    printf("\nPut newsgroup where? [%s] ", dflt);
1062 	ELSE
1063 #endif
1064 #ifdef TERSE
1065 	    printf("\nPut where? [%s] ", dflt);
1066 #endif
1067 	fflush(stdout);
1068 	termdown(1);
1069       reinp_reloc:
1070 	eat_typeahead();
1071 	getcmd(buf);
1072 	if (errno || *buf == '\f')	/* if return from stop signal */
1073 	    goto reask_reloc;		/* give them a prompt again */
1074 	setdef(buf,dflt);
1075 #ifdef VERIFY
1076 	printcmd();
1077 #endif
1078 	if (*buf == 'h') {
1079 #ifdef VERBOSE
1080 	    IF(verbose) {
1081 		printf("\n\n\
1082 Type ^ to put the newsgroup first (position 0).\n\
1083 Type $ to put the newsgroup last (position %d).\n", newsgroup_cnt-1);
1084 		printf("\
1085 Type . to put it before the current newsgroup.\n");
1086 		printf("\
1087 Type -newsgroup name to put it before that newsgroup.\n\
1088 Type +newsgroup name to put it after that newsgroup.\n\
1089 Type a number between 0 and %d to put it at that position.\n", newsgroup_cnt-1);
1090 		printf("\
1091 Type L for a listing of newsgroups and their positions.\n\
1092 Type q to abort the current action.\n") FLUSH;
1093 	    }
1094 	    ELSE
1095 #endif
1096 #ifdef TERSE
1097 	    {
1098 		printf("\n\n\
1099 ^ to put newsgroup first (pos 0).\n\
1100 $ to put last (pos %d).\n", newsgroup_cnt-1);
1101 		printf("\
1102 . to put before current newsgroup.\n");
1103 		printf("\
1104 -newsgroup to put before newsgroup.\n\
1105 +newsgroup to put after.\n\
1106 number in 0-%d to put at that pos.\n", newsgroup_cnt-1);
1107 		printf("\
1108 L for list of newsrc.\n\
1109 q to abort\n") FLUSH;
1110 	    }
1111 #endif
1112 	    termdown(10);
1113 	    goto reask_reloc;
1114 	}
1115 	else if (*buf == 'q')
1116 	    return FALSE;
1117 	else if (*buf == 'L') {
1118 	    newline();
1119 	    list_newsgroups();
1120 	    goto reask_reloc;
1121 	}
1122 	else if (isdigit(*buf)) {
1123 	    if (!finish_command(TRUE))	/* get rest of command */
1124 		goto reinp_reloc;
1125 	    newnum = atol(buf);
1126 	    if (newnum < 0)
1127 		newnum = 0;
1128 	    if (newnum >= newsgroup_cnt)
1129 		newnum = newsgroup_cnt-1;
1130 	}
1131 	else if (*buf == '^') {
1132 	    newline();
1133 	    newnum = 0;
1134 	}
1135 	else if (*buf == '$') {
1136 	    newnum = newsgroup_cnt-1;
1137 	}
1138 	else if (*buf == '.') {
1139 	    newline();
1140 	    newnum = current_ng->num;
1141 	}
1142 	else if (*buf == '-' || *buf == '+') {
1143 	    if (!finish_command(TRUE))	/* get rest of command */
1144 		goto reinp_reloc;
1145 	    np = find_ng(buf+1);
1146 	    if (np == NULL) {
1147 		fputs("Not found.",stdout) FLUSH;
1148 		goto reask_reloc;
1149 	    }
1150 	    newnum = np->num;
1151 	    if (*buf == '+')
1152 		newnum++;
1153 	}
1154 	else {
1155 	    printf("\n%s",hforhelp) FLUSH;
1156 	    termdown(2);
1157 	    settle_down();
1158 	    goto reask_reloc;
1159 	}
1160     }
1161     if (newnum < newsgroup_cnt-1) {
1162 	for (np = first_ng; np; np = np->next)
1163 	    if (np->num >= newnum)
1164 		break;
1165 	if (!np || np == move_np)
1166 	    return FALSE;		/* This can't happen... */
1167 
1168 	last_ng = move_np->prev;
1169 	last_ng->next = NULL;
1170 
1171 	move_np->prev = np->prev;
1172 	move_np->next = np;
1173 
1174 	if (np->prev)
1175 	    np->prev->next = move_np;
1176 	else
1177 	    first_ng = move_np;
1178 	np->prev = move_np;
1179 
1180 	move_np->num = newnum++;
1181 	for (; np; np = np->next, newnum++)
1182 	    np->num = newnum;
1183     }
1184     if (sel_newsgroupsort != SS_NATURAL) {
1185 	sel_sort = sel_newsgroupsort;
1186 	sort_newsgroups();
1187 	sel_sort = save_sort;
1188     }
1189     return TRUE;
1190 }
1191 #endif /* RELOCATE */
1192 
1193 /* List out the newsrc with annotations */
1194 
1195 void
list_newsgroups()1196 list_newsgroups()
1197 {
1198     register NGDATA* np;
1199     register NG_NUM i;
1200     char tmpbuf[2048];
1201     static char* status[] = {"(READ)","(UNSUB)","(DUP)","(BOGUS)","(JUNK)"};
1202 
1203     page_start();
1204     print_lines("  #  Status  Newsgroup\n",STANDOUT);
1205     for (np = first_ng, i = 0; np && !int_count; np = np->next, i++) {
1206 	if (np->toread >= 0)
1207 	    set_toread(np, ST_LAX);
1208 	*(np->rcline + np->numoffset - 1) = np->subscribechar;
1209 	if (np->toread > 0)
1210 	    sprintf(tmpbuf,"%3d %6ld   ",i,(long)np->toread);
1211 	else
1212 	    sprintf(tmpbuf,"%3d %7s  ",i,status[-np->toread]);
1213 	safecpy(tmpbuf+13, np->rcline, sizeof tmpbuf - 13);
1214 	*(np->rcline + np->numoffset - 1) = '\0';
1215 	if (print_lines(tmpbuf,NOMARKING) != 0)
1216 	    break;
1217     }
1218     int_count = 0;
1219 }
1220 
1221 /* find a newsgroup in any newsrc */
1222 
1223 NGDATA*
find_ng(ngnam)1224 find_ng(ngnam)
1225 char* ngnam;
1226 {
1227     HASHDATUM data;
1228 
1229     data = hashfetch(newsrc_hash, ngnam, strlen(ngnam));
1230     return (NGDATA*)data.dat_ptr;
1231 }
1232 
1233 void
cleanup_newsrc(rp)1234 cleanup_newsrc(rp)
1235 NEWSRC* rp;
1236 {
1237     register NGDATA* np;
1238     register NG_NUM bogosity = 0;
1239 
1240 #ifdef VERBOSE
1241     IF(verbose)
1242 	printf("Checking out '%s' -- hang on a second...\n",rp->name) FLUSH;
1243     ELSE
1244 #endif
1245 #ifdef TERSE
1246 	printf("Checking '%s' -- hang on...\n",rp->name) FLUSH;
1247 #endif
1248     termdown(1);
1249     for (np = first_ng; np; np = np->next) {
1250 /*#ifdef CHECK_ALL_BOGUS $$ what is this? */
1251 	if (np->toread >= TR_UNSUB)
1252 	    set_toread(np, ST_LAX); /* this may reset the group or declare it bogus */
1253 /*#endif*/
1254 	if (np->toread == TR_BOGUS)
1255 	    bogosity++;
1256     }
1257     for (np = last_ng; np && np->toread == TR_BOGUS; np = np->prev)
1258 	bogosity--;			/* discount already moved ones */
1259     if (newsgroup_cnt > 5 && bogosity > newsgroup_cnt / 2) {
1260 	fputs(
1261 "It looks like the active file is messed up.  Contact your news administrator,\n\
1262 ",stdout);
1263 	fputs(
1264 "leave the \"bogus\" groups alone, and they may come back to normal.  Maybe.\n\
1265 ",stdout) FLUSH;
1266 	termdown(2);
1267     }
1268 #ifdef RELOCATE
1269     else if (bogosity) {
1270 	NGDATA* prev_np;
1271 #ifdef VERBOSE
1272 	IF(verbose)
1273 	    printf("Moving bogus newsgroups to the end of '%s'.\n",rp->name) FLUSH;
1274 	ELSE
1275 #endif
1276 #ifdef TERSE
1277 	    fputs("Moving boguses to the end.\n",stdout) FLUSH;
1278 #endif
1279 	termdown(1);
1280 	while (np) {
1281 	    prev_np = np->prev;
1282 	    if (np->toread == TR_BOGUS)
1283 		relocate_newsgroup(np, newsgroup_cnt-1);
1284 	    np = prev_np;
1285 	}
1286 	rp->flags |= RF_RCCHANGED;
1287 #ifdef DELBOGUS
1288 reask_bogus:
1289 	in_char("Delete bogus newsgroups?", 'D', "ny");
1290 #ifdef VERIFY
1291 	printcmd();
1292 #endif
1293 	newline();
1294 	if (*buf == 'h') {
1295 #ifdef VERBOSE
1296 	    IF(verbose) {
1297 		fputs("\
1298 Type y to delete bogus newsgroups.\n\
1299 Type n or SP to leave them at the end in case they return.\n\
1300 ",stdout) FLUSH;
1301 		termdown(2);
1302 	    }
1303 	    ELSE
1304 #endif
1305 #ifdef TERSE
1306 	    {
1307 		fputs("y to delete, n to keep\n",stdout) FLUSH;
1308 		termdown(1);
1309 	    }
1310 #endif
1311 	    goto reask_bogus;
1312 	}
1313 	else if (*buf == 'n' || *buf == 'q')
1314 	    ;
1315 	else if (*buf == 'y') {
1316 	    for (np = last_ng; np && np->toread == TR_BOGUS; np = np->prev) {
1317 		hashdelete(newsrc_hash, np->rcline, np->numoffset - 1);
1318 		clear_ngitem((char*)np,0);
1319 		newsgroup_cnt--;
1320 	    }
1321 	    rp->flags |= RF_RCCHANGED; /*$$ needed? */
1322 	    last_ng = np;
1323 	    if (np)
1324 		np->next = NULL;
1325 	    else
1326 		first_ng = NULL;
1327 	    if (current_ng && !current_ng->rcline)
1328 		current_ng = first_ng;
1329 	    if (recent_ng && !recent_ng->rcline)
1330 		recent_ng = first_ng;
1331 	    if (ngptr && !ngptr->rcline)
1332 		ngptr = first_ng;
1333 	    if (sel_page_np && !sel_page_np->rcline)
1334 		sel_page_np = NULL;
1335 	}
1336 	else {
1337 	    fputs(hforhelp,stdout) FLUSH;
1338 	    termdown(1);
1339 	    settle_down();
1340 	    goto reask_bogus;
1341 	}
1342 #endif /* DELBOGUS */
1343     }
1344 #else /* !RELOCATE */
1345 #ifdef VERBOSE
1346     IF(verbose)
1347 	printf("You should edit bogus newsgroups out of '%s'.\n",rp->name) FLUSH;
1348     ELSE
1349 #endif
1350 #ifdef TERSE
1351 	printf("Edit boguses from '%s'.\n",rp->name) FLUSH;
1352 #endif
1353     termdown(1);
1354 #endif /* !RELOCATE */
1355     paranoid = FALSE;
1356 }
1357 
1358 /* make an entry in the hash table for the current newsgroup */
1359 
1360 void
sethash(np)1361 sethash(np)
1362 NGDATA* np;
1363 {
1364     HASHDATUM data;
1365     data.dat_ptr = (char*)np;
1366     data.dat_len = np->numoffset - 1;
1367     hashstore(newsrc_hash, np->rcline, data.dat_len, data);
1368 }
1369 
1370 static int
rcline_cmp(key,keylen,data)1371 rcline_cmp(key, keylen, data)
1372 char* key;
1373 int keylen;
1374 HASHDATUM data;
1375 {
1376     /* We already know that the lengths are equal, just compare the strings */
1377     return bcmp(key, ((NGDATA*)data.dat_ptr)->rcline, keylen);
1378 }
1379 
1380 /* checkpoint the newsrc(s) */
1381 
1382 void
checkpoint_newsrcs()1383 checkpoint_newsrcs()
1384 {
1385 #ifdef DEBUG
1386     if (debug & DEB_CHECKPOINTING) {
1387 	fputs("(ckpt)",stdout);
1388 	fflush(stdout);
1389     }
1390 #endif
1391     if (doing_ng)
1392 	bits_to_rc();			/* do not restore M articles */
1393     if (!write_newsrcs(multirc))
1394 	get_anything();
1395 #ifdef DEBUG
1396     if (debug & DEB_CHECKPOINTING) {
1397 	fputs("(done)",stdout);
1398 	fflush(stdout);
1399     }
1400 #endif
1401 }
1402 
1403 /* write out the (presumably) revised newsrc(s) */
1404 
1405 bool
write_newsrcs(mptr)1406 write_newsrcs(mptr)
1407 MULTIRC* mptr;
1408 {
1409     NEWSRC* rp;
1410     register NGDATA* np;
1411     int save_sort = sel_sort;
1412     FILE* rcfp;
1413     bool total_success = TRUE;
1414 
1415     if (!mptr)
1416 	return TRUE;
1417 
1418     if (sel_newsgroupsort != SS_NATURAL) {
1419 	sel_sort = SS_NATURAL;
1420 	sel_direction = 1;
1421 	sort_newsgroups();
1422     }
1423 
1424     for (rp = mptr->first; rp; rp = rp->next) {
1425 	if (!(rp->flags & RF_ACTIVE))
1426 	    continue;
1427 
1428 	if (rp->infoname) {
1429 	    if ((tmpfp = fopen(rp->infoname, "w")) != NULL) {
1430 		fprintf(tmpfp,"Last-Group: %s\nNew-Group-State: %ld,%ld,%ld\n",
1431 			ngname? ngname : nullstr,rp->datasrc->lastnewgrp,
1432 			rp->datasrc->act_sf.recent_cnt,
1433 			rp->datasrc->desc_sf.recent_cnt);
1434 		fclose(tmpfp);
1435 	    }
1436 	}
1437 	else {
1438 	    readlast();
1439 #ifdef SUPPORT_NNTP
1440 	    if (rp->datasrc->flags & DF_REMOTE) {
1441 		lastactsiz = rp->datasrc->act_sf.recent_cnt;
1442 		lastextranum = rp->datasrc->desc_sf.recent_cnt;
1443 	    }
1444 	    else
1445 #endif
1446 		lastextranum = rp->datasrc->act_sf.recent_cnt;
1447 	    lastnewtime = rp->datasrc->lastnewgrp;
1448 	    writelast();
1449 	}
1450 
1451 	if (!(rp->flags & RF_RCCHANGED))
1452 	    continue;
1453 
1454 	rcfp = fopen(rp->newname, "w");
1455 	if (rcfp == NULL) {
1456 	    printf(cantrecreate,rp->name) FLUSH;
1457 	    total_success = FALSE;
1458 	    continue;
1459 	}
1460 #ifndef MSDOS
1461 	if (stat(rp->name,&filestat)>=0) { /* preserve permissions */
1462 	    chmod(rp->newname,filestat.st_mode&0666);
1463 	    chown(rp->newname,filestat.st_uid,filestat.st_gid);
1464 	}
1465 #endif
1466 	/* write out each line*/
1467 
1468 	for (np = first_ng; np; np = np->next) {
1469 	    register char* delim;
1470 	    if (np->rc != rp)
1471 		continue;
1472 	    if (np->numoffset) {
1473 		delim = np->rcline + np->numoffset - 1;
1474 		*delim = np->subscribechar;
1475 		if ((np->flags & NF_UNTHREADED) && delim[2] == '1')
1476 		    delim[2] = '0';
1477 	    }
1478 	    else
1479 		delim = NULL;
1480 #ifdef DEBUG
1481 	    if (debug & DEB_NEWSRC_LINE) {
1482 		printf("%s\n",np->rcline) FLUSH;
1483 		termdown(1);
1484 	    }
1485 #endif
1486 	    if (fprintf(rcfp,"%s\n",np->rcline) < 0) {
1487 		fclose(rcfp);		/* close new newsrc */
1488 		goto write_error;
1489 	    }
1490 	    if (delim) {
1491 		*delim = '\0';		/* might still need this line */
1492 		if ((np->flags & NF_UNTHREADED) && delim[2] == '0')
1493 		    delim[2] = '1';
1494 	    }
1495 	}
1496 	fflush(rcfp);
1497 	/* fclose is the only sure test for full disks via NFS */
1498 	if (ferror(rcfp)) {
1499 	    fclose(rcfp);
1500 	    goto write_error;
1501 	}
1502 	if (fclose(rcfp) == EOF) {
1503 	  write_error:
1504 	    printf(cantrecreate,rp->name) FLUSH;
1505 	    UNLINK(rp->newname);
1506 	    total_success = FALSE;
1507 	    continue;
1508 	}
1509 	rp->flags &= ~RF_RCCHANGED;
1510 
1511 	UNLINK(rp->name);
1512 	RENAME(rp->newname,rp->name);
1513     }
1514 
1515     if (sel_newsgroupsort != SS_NATURAL) {
1516 	sel_sort = sel_newsgroupsort;
1517 	sort_newsgroups();
1518 	sel_sort = save_sort;
1519     }
1520     return total_success;
1521 }
1522 
1523 void
get_old_newsrcs(mptr)1524 get_old_newsrcs(mptr)
1525 MULTIRC* mptr;
1526 {
1527     NEWSRC* rp;
1528 
1529     if (mptr) {
1530 	for (rp = mptr->first; rp; rp = rp->next) {
1531 	    if (rp->flags & RF_ACTIVE) {
1532 		UNLINK(rp->newname);
1533 		RENAME(rp->name,rp->newname);
1534 		RENAME(rp->oldname,rp->name);
1535 	    }
1536 	}
1537     }
1538 }
1539