/* rcstuff.c
*/
/* This software is copyrighted as detailed in the LICENSE file. */
#include "EXTERN.h"
#include "common.h"
#include "list.h"
#include "util.h"
#include "util2.h"
#include "hash.h"
#include "cache.h"
#include "bits.h"
#include "ngdata.h"
#include "nntpclient.h"
#include "datasrc.h"
#include "nntp.h"
#include "term.h"
#include "final.h"
#include "trn.h"
#include "env.h"
#include "init.h"
#include "intrp.h"
#include "only.h"
#include "rcln.h"
#include "last.h"
#include "autosub.h"
#include "rt-select.h"
#include "rt-page.h"
#include "INTERN.h"
#include "rcstuff.h"
#include "rcstuff.ih"
static bool foundany;
bool
rcstuff_init()
{
MULTIRC* mptr = NULL;
int i;
multirc_list = new_list(0,0,sizeof(MULTIRC),20,LF_ZERO_MEM|LF_SPARSE,NULL);
if (trnaccess_mem) {
NEWSRC* rp;
char* s;
char* section;
char* cond;
char** vals = prep_ini_words(rcgroups_ini);
s = trnaccess_mem;
while ((s = next_ini_section(s,§ion,&cond)) != NULL) {
if (*cond && !check_ini_cond(cond))
continue;
if (strncaseNE(section, "group ", 6))
continue;
i = atoi(section+6);
if (i < 0)
i = 0;
s = parse_ini_section(s, rcgroups_ini);
if (!s)
break;
rp = new_newsrc(vals[RI_ID],vals[RI_NEWSRC],vals[RI_ADDGROUPS]);
if (rp) {
MULTIRC* mp;
NEWSRC* prev_rp;
mp = multirc_ptr(i);
prev_rp = mp->first;
if (!prev_rp)
mp->first = rp;
else {
while (prev_rp->next)
prev_rp = prev_rp->next;
prev_rp->next = rp;
}
mp->num = i;
if (!mptr)
mptr = mp;
/*rp->flags |= RF_USED;*/
}
/*else
;$$complain?*/
}
free((char*)vals);
free(trnaccess_mem);
}
if (UseNewsrcSelector && !checkflag)
return TRUE;
foundany = FALSE;
if (mptr && !use_multirc(mptr))
use_next_multirc(mptr);
if (!multirc) {
mptr = multirc_ptr(0);
mptr->first = new_newsrc("default",NULL,NULL);
if (!use_multirc(mptr)) {
printf("Couldn't open any newsrc groups. Is your access file ok?\n");
finalize(1);
}
}
if (checkflag) /* were we just checking? */
finalize(foundany); /* tell them what we found */
return foundany;
}
NEWSRC*
new_newsrc(name,newsrc,add_ok)
char* name;
char* newsrc;
char* add_ok;
{
char tmpbuf[CBUFLEN];
NEWSRC* rp;
DATASRC* dp;
if (!name || !*name)
return NULL;
if (!newsrc || !*newsrc) {
newsrc = getenv("NEWSRC");
if (!newsrc)
newsrc = RCNAME;
}
dp = get_datasrc(name);
if (!dp)
return NULL;
rp = (NEWSRC*)safemalloc(sizeof (NEWSRC));
bzero((char*)rp, sizeof (NEWSRC));
rp->datasrc = dp;
rp->name = savestr(filexp(newsrc));
sprintf(tmpbuf, RCNAME_OLD, rp->name);
rp->oldname = savestr(tmpbuf);
sprintf(tmpbuf, RCNAME_NEW, rp->name);
rp->newname = savestr(tmpbuf);
switch (add_ok? *add_ok : 'y') {
case 'n':
case 'N':
break;
default:
if (dp->flags & DF_ADD_OK)
rp->flags |= RF_ADD_NEWGROUPS;
/* FALL THROUGH */
case 'm':
case 'M':
rp->flags |= RF_ADD_GROUPS;
break;
}
return rp;
}
bool
use_multirc(mp)
MULTIRC* mp;
{
NEWSRC* rp;
bool had_trouble = FALSE;
bool had_success = FALSE;
for (rp = mp->first; rp; rp = rp->next) {
if ((rp->datasrc->flags & DF_UNAVAILABLE) || !lock_newsrc(rp)
|| !open_datasrc(rp->datasrc) || !open_newsrc(rp)) {
unlock_newsrc(rp);
had_trouble = TRUE;
}
else {
rp->datasrc->flags |= DF_ACTIVE;
rp->flags |= RF_ACTIVE;
had_success = TRUE;
}
}
if (had_trouble)
get_anything();
if (!had_success)
return FALSE;
multirc = mp;
#ifdef NO_FILELINKS
if (!write_newsrcs(multirc))
get_anything();
#endif
return TRUE;
}
void
unuse_multirc(mptr)
MULTIRC* mptr;
{
NEWSRC* rp;
if (!mptr)
return;
write_newsrcs(mptr);
for (rp = mptr->first; rp; rp = rp->next) {
unlock_newsrc(rp);
rp->flags &= ~RF_ACTIVE;
rp->datasrc->flags &= ~DF_ACTIVE;
}
if (ngdata_list) {
close_cache();
hashdestroy(newsrc_hash);
walk_list(ngdata_list, clear_ngitem, 0);
delete_list(ngdata_list);
ngdata_list = NULL;
first_ng = NULL;
last_ng = NULL;
ngptr = NULL;
current_ng = NULL;
recent_ng = NULL;
starthere = NULL;
sel_page_np = NULL;
}
ngdata_cnt = 0;
newsgroup_cnt = 0;
newsgroup_toread = 0;
multirc = NULL;
}
bool
use_next_multirc(mptr)
MULTIRC* mptr;
{
register MULTIRC* mp = multirc_ptr(mptr->num);
unuse_multirc(mptr);
for (;;) {
mp = multirc_next(mp);
if (!mp)
mp = multirc_low();
if (mp == mptr) {
use_multirc(mptr);
return FALSE;
}
if (use_multirc(mp))
break;
}
return TRUE;
}
bool
use_prev_multirc(mptr)
MULTIRC* mptr;
{
register MULTIRC* mp = multirc_ptr(mptr->num);
unuse_multirc(mptr);
for (;;) {
mp = multirc_prev(mp);
if (!mp)
mp = multirc_high();
if (mp == mptr) {
use_multirc(mptr);
return FALSE;
}
if (use_multirc(mp))
break;
}
return TRUE;
}
char*
multirc_name(mp)
register MULTIRC* mp;
{
char* cp;
if (mp->first->next)
return "<each-newsrc>";
if ((cp = rindex(mp->first->name, '/')) != NULL)
return cp+1;
return mp->first->name;
}
static bool
clear_ngitem(cp, arg)
char* cp;
int arg;
{
NGDATA* ncp = (NGDATA*)cp;
if (ncp->rcline != NULL) {
if (!checkflag)
free(ncp->rcline);
ncp->rcline = NULL;
}
return 0;
}
/* make sure there is no trn out there reading this newsrc */
static bool
lock_newsrc(rp)
NEWSRC* rp;
{
long processnum = 0;
char* runninghost = "(Unknown)";
char* s;
if (checkflag)
return TRUE;
s = filexp(RCNAME);
if (strEQ(rp->name, s))
rp->lockname = savestr(filexp(LOCKNAME));
else {
sprintf(buf, RCNAME_INFO, rp->name);
rp->infoname = savestr(buf);
sprintf(buf, RCNAME_LOCK, rp->name);
rp->lockname = savestr(buf);
}
tmpfp = fopen(rp->lockname,"r");
if (tmpfp != NULL) {
if (fgets(buf,LBUFLEN,tmpfp)) {
processnum = atol(buf);
if (fgets(buf,LBUFLEN,tmpfp) && *buf
&& *(s = buf + strlen(buf) - 1) == '\n') {
*s = '\0';
runninghost = buf;
}
}
fclose(tmpfp);
}
if (processnum) {
#ifndef MSDOS
#ifdef VERBOSE
IF(verbose)
printf("\nThe requested newsrc is locked by process %ld on host %s.\n",
processnum, runninghost) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\nNewsrc locked by %ld on host %s.\n",processnum,runninghost) FLUSH;
#endif
termdown(2);
if (strNE(runninghost,localhost)) {
#ifdef VERBOSE
IF(verbose)
printf("\n\
Since that's not the same host as this one (%s), we must\n\
assume that process still exists. To override this check, remove\n\
the lock file: %s\n", localhost, rp->lockname) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\nThis host (%s) doesn't match.\nCan't unlock %s.\n",
localhost, rp->lockname) FLUSH;
#endif
termdown(2);
if (bizarre)
resetty();
finalize(0);
}
if (processnum == our_pid) {
#ifdef VERBOSE
IF(verbose)
printf("\n\
Hey, that *my* pid! Your access file is trying to use the same newsrc\n\
more than once.\n") FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\nAccess file error (our pid detected).\n") FLUSH;
#endif
termdown(2);
return FALSE;
}
if (kill(processnum, 0) != 0) {
/* Process is apparently gone */
sleep(2);
#ifdef VERBOSE
IF(verbose)
fputs("\n\
That process does not seem to exist anymore. The count of read articles\n\
may be incorrect in the last newsgroup accessed by that other (defunct)\n\
process.\n\n",stdout) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("\nProcess crashed.\n",stdout) FLUSH;
#endif
if (lastngname) {
#ifdef VERBOSE
IF(verbose)
printf("(The last newsgroup accessed was %s.)\n\n",
lastngname) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("(In %s.)\n\n",lastngname) FLUSH;
#endif
}
termdown(2);
get_anything();
newline();
}
else {
#ifdef VERBOSE
IF(verbose)
printf("\n\
It looks like that process still exists. To override this, remove\n\
the lock file: %s\n", rp->lockname) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\nCan't unlock %s.\n", rp->lockname) FLUSH;
#endif
termdown(2);
if (bizarre)
resetty();
finalize(0);
}
#endif
}
tmpfp = fopen(rp->lockname,"w");
if (tmpfp == NULL) {
printf(cantcreate,rp->lockname) FLUSH;
sig_catcher(0);
}
fprintf(tmpfp,"%ld\n%s\n",our_pid,localhost);
fclose(tmpfp);
return TRUE;
}
static void
unlock_newsrc(rp)
NEWSRC* rp;
{
safefree0(rp->infoname);
if (rp->lockname) {
UNLINK(rp->lockname);
free(rp->lockname);
rp->lockname = NULL;
}
}
static bool
open_newsrc(rp)
NEWSRC* rp;
{
register NGDATA* np;
NGDATA* prev_np;
char* some_buf;
long length;
FILE* rcfp;
HASHDATUM data;
/* make sure the .newsrc file exists */
if ((rcfp = fopen(rp->name,"r")) == NULL) {
rcfp = fopen(rp->name,"w+");
if (rcfp == NULL) {
printf("\nCan't create %s.\n", rp->name) FLUSH;
termdown(2);
return FALSE;
}
some_buf = SUBSCRIPTIONS;
#ifdef SUPPORT_NNTP
if ((rp->datasrc->flags & DF_REMOTE)
&& nntp_list("SUBSCRIPTIONS",nullstr,0) == 1) {
do {
fputs(ser_line,rcfp);
fputc('\n',rcfp);
if (nntp_gets(ser_line, sizeof ser_line) < 0)
break;
} while (!nntp_at_list_end(ser_line));
}
#endif
ElseIf (*some_buf && (tmpfp = fopen(filexp(some_buf),"r")) != NULL) {
while (fgets(buf,sizeof buf,tmpfp))
fputs(buf,rcfp);
fclose(tmpfp);
}
fseek(rcfp, 0L, 0);
}
else {
/* File exists; if zero length and backup isn't, complain */
if (fstat(fileno(rcfp),&filestat) < 0) {
perror(rp->name);
return FALSE;
}
if (filestat.st_size == 0
&& stat(rp->oldname,&filestat) >= 0 && filestat.st_size > 0) {
printf("Warning: %s is zero length but %s is not.\n",
rp->name,rp->oldname);
printf("Either recover your newsrc or else remove the backup copy.\n");
termdown(2);
return FALSE;
}
/* unlink backup file name and backup current name */
UNLINK(rp->oldname);
#ifndef NO_FILELINKS
safelink(rp->name,rp->oldname);
#endif
}
if (!ngdata_list) {
/* allocate memory for rc file globals */
ngdata_list = new_list(0, 0, sizeof (NGDATA), 200, 0, init_ngnode);
newsrc_hash = hashcreate(3001, rcline_cmp);
}
if (ngdata_cnt)
prev_np = ngdata_ptr(ngdata_cnt-1);
else
prev_np = NULL;
/* read in the .newsrc file */
while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != NULL) {
length = len_last_line_got; /* side effect of get_a_line */
if (length <= 1) /* only a newline??? */
continue;
np = ngdata_ptr(ngdata_cnt++);
if (prev_np)
prev_np->next = np;
else
first_ng = np;
np->prev = prev_np;
prev_np = np;
np->rc = rp;
newsgroup_cnt++;
if (some_buf[length-1] == '\n')
some_buf[--length] = '\0'; /* wipe out newline */
if (checkflag) /* no extra mallocs for -c */
np->rcline = some_buf;
else if (some_buf == buf)
np->rcline = savestr(some_buf); /* make semipermanent copy */
else {
/*NOSTRICT*/
#ifndef lint
some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
#endif
np->rcline = some_buf;
}
if (*some_buf == ' ' || *some_buf == '\t'
|| strnEQ(some_buf,"options",7)) { /* non-useful line? */
np->toread = TR_JUNK;
np->subscribechar = ' ';
np->numoffset = 0;
continue;
}
parse_rcline(np);
data = hashfetch(newsrc_hash, np->rcline, np->numoffset - 1);
if (data.dat_ptr) {
np->toread = TR_IGNORE;
continue;
}
if (np->subscribechar == NEGCHAR) {
np->toread = TR_UNSUB;
sethash(np);
continue;
}
newsgroup_toread++;
/* now find out how much there is to read */
if (!inlist(buf) || (suppress_cn && foundany && !paranoid))
np->toread = TR_NONE; /* no need to calculate now */
else
set_toread(np, ST_LAX);
if (np->toread > TR_NONE) { /* anything unread? */
if (!foundany) {
starthere = np;
foundany = TRUE; /* remember that fact*/
}
if (suppress_cn) { /* if no listing desired */
if (checkflag) /* if that is all they wanted */
finalize(1); /* then bomb out */
}
else {
#ifdef VERBOSE
IF(verbose)
printf("Unread news in %-40s %5ld article%s\n",
np->rcline,(long)np->toread,PLURAL(np->toread)) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("%s: %ld article%s\n",
np->rcline,(long)np->toread,PLURAL(np->toread)) FLUSH;
#endif
termdown(1);
if (int_count) {
countdown = 1;
int_count = 0;
}
if (countdown) {
if (!--countdown) {
fputs("etc.\n",stdout) FLUSH;
if (checkflag)
finalize(1);
suppress_cn = TRUE;
}
}
}
}
sethash(np);
}
if (prev_np) {
prev_np->next = NULL;
last_ng = prev_np;
}
fclose(rcfp); /* close .newsrc */
#ifdef NO_FILELINKS
UNLINK(rp->oldname);
RENAME(rp->name,rp->oldname);
rp->flags |= RF_RCCHANGED;
#endif
if (rp->infoname) {
if ((tmpfp = fopen(rp->infoname,"r")) != NULL) {
if (fgets(buf,sizeof buf,tmpfp) != NULL) {
long actnum, descnum;
char* s;
buf[strlen(buf)-1] = '\0';
if ((s = index(buf, ':')) != NULL && s[1] == ' ' && s[2]) {
safefree0(lastngname);
lastngname = savestr(s+2);
}
if (fscanf(tmpfp,"New-Group-State: %ld,%ld,%ld",
&lastnewtime,&actnum,&descnum) == 3) {
rp->datasrc->act_sf.recent_cnt = actnum;
rp->datasrc->desc_sf.recent_cnt = descnum;
}
}
}
}
else {
readlast();
#ifdef SUPPORT_NNTP
if (rp->datasrc->flags & DF_REMOTE) {
rp->datasrc->act_sf.recent_cnt = lastactsiz;
rp->datasrc->desc_sf.recent_cnt = lastextranum;
}
else
#endif
{
rp->datasrc->act_sf.recent_cnt = lastextranum;
rp->datasrc->desc_sf.recent_cnt = 0;
}
}
rp->datasrc->lastnewgrp = lastnewtime;
if (paranoid)
cleanup_newsrc(rp);
return TRUE;
}
/* Initialize the memory for an entire node's worth of article's */
static void
init_ngnode(list, node)
LIST* list;
LISTNODE* node;
{
register ART_NUM i;
register NGDATA* np;
bzero(node->data, list->items_per_node * list->item_size);
for (i = node->low, np = (NGDATA*)node->data; i <= node->high; i++, np++)
np->num = i;
}
static void
parse_rcline(np)
register NGDATA* np;
{
char* s;
int len;
for (s=np->rcline; *s && *s!=':' && *s!=NEGCHAR && !isspace(*s); s++) ;
len = s - np->rcline;
if ((!*s || isspace(*s)) && !checkflag) {
#ifndef lint
np->rcline = saferealloc(np->rcline,(MEM_SIZE)len + 3);
#endif
s = np->rcline + len;
strcpy(s, ": ");
}
if (*s == ':' && s[1] && s[2] == '0') {
np->flags |= NF_UNTHREADED;
s[2] = '1';
}
np->subscribechar = *s; /* salt away the : or ! */
np->numoffset = len + 1; /* remember where the numbers are */
*s = '\0'; /* null terminate newsgroup name */
}
void
abandon_ng(np)
NGDATA* np;
{
char* some_buf = NULL;
FILE* rcfp;
/* open newsrc backup copy and try to find the prior value for the group. */
if ((rcfp = fopen(np->rc->oldname, "r")) != NULL) {
int length = np->numoffset - 1;
while ((some_buf = get_a_line(buf,LBUFLEN,FALSE,rcfp)) != NULL) {
if (len_last_line_got <= 0)
continue;
some_buf[len_last_line_got-1] = '\0'; /* wipe out newline */
if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR)
&& strnEQ(np->rcline, some_buf, length)) {
break;
}
if (some_buf != buf)
free(some_buf);
}
fclose(rcfp);
} else if (errno != ENOENT) {
printf("Unable to open %s.\n", np->rc->oldname) FLUSH;
termdown(1);
return;
}
if (some_buf == NULL) {
some_buf = np->rcline + np->numoffset;
if (*some_buf == ' ')
some_buf++;
*some_buf = '\0';
np->abs1st = 0; /* force group to be re-calculated */
}
else {
free(np->rcline);
if (some_buf == buf)
np->rcline = savestr(some_buf);
else {
/*NOSTRICT*/
#ifndef lint
some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got));
#endif /* lint */
np->rcline = some_buf;
}
}
parse_rcline(np);
if (np->subscribechar == NEGCHAR)
np->subscribechar = ':';
np->rc->flags |= RF_RCCHANGED;
set_toread(np, ST_LAX);
}
/* try to find or add an explicitly specified newsgroup */
/* returns TRUE if found or added, FALSE if not. */
/* assumes that we are chdir'ed to NEWSSPOOL */
bool
get_ng(what, flags)
char* what;
int flags;
{
char* ntoforget;
char promptbuf[128];
int autosub;
#ifdef VERBOSE
IF(verbose)
ntoforget = "Type n to forget about this newsgroup.\n";
ELSE
#endif
#ifdef TERSE
ntoforget = "n to forget it.\n";
#endif
if (index(what,'/')) {
dingaling();
printf("\nBad newsgroup name.\n") FLUSH;
termdown(2);
check_fuzzy_match:
#ifdef EDIT_DISTANCE
if (fuzzyGet && (flags & GNG_FUZZY)) {
flags &= ~GNG_FUZZY;
if (find_close_match())
what = ngname;
else
return FALSE;
} else
#endif
return FALSE;
}
set_ngname(what);
ngptr = find_ng(ngname);
if (ngptr == NULL) { /* not in .newsrc? */
NEWSRC* rp;
for (rp = multirc->first; rp; rp = rp->next) {
if (!ALLBITS(rp->flags, RF_ADD_GROUPS | RF_ACTIVE))
continue;
/*$$ this may scan a datasrc multiple times... */
if (find_actgrp(rp->datasrc,buf,ngname,ngname_len,(ART_NUM)0))
break; /*$$ let them choose which server */
}
if (!rp) {
dingaling();
#ifdef VERBOSE
IF(verbose)
printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("\nNo %s!\n",ngname) FLUSH;
#endif
termdown(2);
if (novice_delays)
sleep(2);
goto check_fuzzy_match;
}
if (mode != 'i' || !(autosub = auto_subscribe(ngname)))
autosub = addnewbydefault;
if (autosub) {
if (append_unsub) {
printf("(Adding %s to end of your .newsrc %ssubscribed)\n",
ngname, (autosub == ADDNEW_SUB)? nullstr : "un") FLUSH;
termdown(1);
ngptr = add_newsgroup(rp, ngname, autosub);
} else {
if (autosub == ADDNEW_SUB) {
printf("(Subscribing to %s)\n", ngname) FLUSH;
termdown(1);
ngptr = add_newsgroup(rp, ngname, autosub);
} else {
printf("(Ignoring %s)\n", ngname) FLUSH;
termdown(1);
return FALSE;
}
}
flags &= ~GNG_RELOC;
} else {
#ifdef VERBOSE
IF(verbose)
sprintf(promptbuf,"\nNewsgroup %s not in .newsrc -- subscribe?",ngname);
ELSE
#endif
#ifdef TERSE
sprintf(promptbuf,"\nSubscribe %s?",ngname);
#endif
reask_add:
in_char(promptbuf,'A',"ynYN");
#ifdef VERIFY
printcmd();
#endif
newline();
if (*buf == 'h') {
#ifdef VERBOSE
IF(verbose) {
printf("Type y or SP to subscribe to %s.\n\
Type Y to subscribe to this and all remaining new groups.\n\
Type N to leave all remaining new groups unsubscribed.\n", ngname) FLUSH;
termdown(3);
}
ELSE
#endif
#ifdef TERSE
{
fputs("\
y or SP to subscribe, Y to subscribe all new groups, N to unsubscribe all\n",
stdout) FLUSH;
termdown(1);
}
#endif
fputs(ntoforget,stdout) FLUSH;
termdown(1);
goto reask_add;
}
else if (*buf == 'n' || *buf == 'q') {
if (append_unsub)
ngptr = add_newsgroup(rp, ngname, NEGCHAR);
return FALSE;
}
else if (*buf == 'y') {
ngptr = add_newsgroup(rp, ngname, ':');
flags |= GNG_RELOC;
}
else if (*buf == 'Y') {
addnewbydefault = ADDNEW_SUB;
if (append_unsub)
printf("(Adding %s to end of your .newsrc subscribed)\n",
ngname) FLUSH;
else
printf("(Subscribing to %s)\n", ngname) FLUSH;
termdown(1);
ngptr = add_newsgroup(rp, ngname, ':');
flags &= ~GNG_RELOC;
}
else if (*buf == 'N') {
addnewbydefault = ADDNEW_UNSUB;
if (append_unsub) {
printf("(Adding %s to end of your .newsrc unsubscribed)\n",
ngname) FLUSH;
termdown(1);
ngptr = add_newsgroup(rp, ngname, NEGCHAR);
flags &= ~GNG_RELOC;
} else {
printf("(Ignoring %s)\n", ngname) FLUSH;
termdown(1);
return FALSE;
}
}
else {
fputs(hforhelp,stdout) FLUSH;
termdown(1);
settle_down();
goto reask_add;
}
}
}
else if (mode == 'i') /* adding new groups during init? */
return FALSE;
else if (ngptr->subscribechar == NEGCHAR) {/* unsubscribed? */
#ifdef VERBOSE
IF(verbose)
sprintf(promptbuf,
"\nNewsgroup %s is unsubscribed -- resubscribe?",ngname)
FLUSH;
ELSE
#endif
#ifdef TERSE
sprintf(promptbuf,"\nResubscribe %s?",ngname)
FLUSH;
#endif
reask_unsub:
in_char(promptbuf,'R',"yn");
#ifdef VERIFY
printcmd();
#endif
newline();
if (*buf == 'h') {
#ifdef VERBOSE
IF(verbose)
printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("y or SP to resubscribe.\n",stdout) FLUSH;
#endif
fputs(ntoforget,stdout) FLUSH;
termdown(2);
goto reask_unsub;
}
else if (*buf == 'n' || *buf == 'q') {
return FALSE;
}
else if (*buf == 'y') {
register char* cp;
cp = ngptr->rcline + ngptr->numoffset;
ngptr->flags = (*cp && cp[1] == '0' ? NF_UNTHREADED : 0);
ngptr->subscribechar = ':';
ngptr->rc->flags |= RF_RCCHANGED;
flags &= ~GNG_RELOC;
}
else {
fputs(hforhelp,stdout) FLUSH;
termdown(1);
settle_down();
goto reask_unsub;
}
}
/* now calculate how many unread articles in newsgroup */
set_toread(ngptr, ST_STRICT);
#ifdef RELOCATE
if (flags & GNG_RELOC) {
if (!relocate_newsgroup(ngptr,-1))
return FALSE;
}
#endif
return ngptr->toread >= TR_NONE;
}
/* add a newsgroup to the newsrc file (eventually) */
static NGDATA*
add_newsgroup(rp, ngn, c)
NEWSRC* rp;
char* ngn;
char_int c;
{
register NGDATA* np;
np = ngdata_ptr(ngdata_cnt++);
np->prev = last_ng;
if (last_ng)
last_ng->next = np;
else
first_ng = np;
np->next = NULL;
last_ng = np;
newsgroup_cnt++;
np->rc = rp;
np->numoffset = strlen(ngn) + 1;
np->rcline = safemalloc((MEM_SIZE)(np->numoffset + 2));
strcpy(np->rcline,ngn); /* and copy over the name */
strcpy(np->rcline + np->numoffset, " ");
np->subscribechar = c; /* subscribe or unsubscribe */
if (c != NEGCHAR)
newsgroup_toread++;
np->toread = TR_NONE; /* just for prettiness */
sethash(np); /* so we can find it again */
rp->flags |= RF_RCCHANGED;
return np;
}
#ifdef RELOCATE
bool
relocate_newsgroup(move_np,newnum)
NGDATA* move_np;
NG_NUM newnum;
{
NGDATA* np;
int i;
char* dflt = (move_np!=current_ng ? "$^.Lq" : "$^Lq");
int save_sort = sel_sort;
if (sel_newsgroupsort != SS_NATURAL) {
if (newnum < 0) {
/* ask if they want to keep the current order */
in_char("Sort newsrc(s) using current sort order?", 'D', "yn"); /*$$ !'D' */
#ifdef VERIFY
printcmd();
#endif
newline();
if (*buf == 'y')
set_selector(SM_NEWSGROUP, SS_NATURAL);
else {
sel_sort = SS_NATURAL;
sel_direction = 1;
sort_newsgroups();
}
}
else {
sel_sort = SS_NATURAL;
sel_direction = 1;
sort_newsgroups();
}
}
starthere = NULL; /* Disable this optimization */
if (move_np != last_ng) {
if (move_np->prev)
move_np->prev->next = move_np->next;
else
first_ng = move_np->next;
move_np->next->prev = move_np->prev;
move_np->prev = last_ng;
move_np->next = NULL;
last_ng->next = move_np;
last_ng = move_np;
}
/* Renumber the groups according to current order */
for (np = first_ng, i = 0; np; np = np->next, i++)
np->num = i;
move_np->rc->flags |= RF_RCCHANGED;
if (newnum < 0) {
reask_reloc:
unflush_output(); /* disable any ^O in effect */
#ifdef VERBOSE
IF(verbose)
printf("\nPut newsgroup where? [%s] ", dflt);
ELSE
#endif
#ifdef TERSE
printf("\nPut where? [%s] ", dflt);
#endif
fflush(stdout);
termdown(1);
reinp_reloc:
eat_typeahead();
getcmd(buf);
if (errno || *buf == '\f') /* if return from stop signal */
goto reask_reloc; /* give them a prompt again */
setdef(buf,dflt);
#ifdef VERIFY
printcmd();
#endif
if (*buf == 'h') {
#ifdef VERBOSE
IF(verbose) {
printf("\n\n\
Type ^ to put the newsgroup first (position 0).\n\
Type $ to put the newsgroup last (position %d).\n", newsgroup_cnt-1);
printf("\
Type . to put it before the current newsgroup.\n");
printf("\
Type -newsgroup name to put it before that newsgroup.\n\
Type +newsgroup name to put it after that newsgroup.\n\
Type a number between 0 and %d to put it at that position.\n", newsgroup_cnt-1);
printf("\
Type L for a listing of newsgroups and their positions.\n\
Type q to abort the current action.\n") FLUSH;
}
ELSE
#endif
#ifdef TERSE
{
printf("\n\n\
^ to put newsgroup first (pos 0).\n\
$ to put last (pos %d).\n", newsgroup_cnt-1);
printf("\
. to put before current newsgroup.\n");
printf("\
-newsgroup to put before newsgroup.\n\
+newsgroup to put after.\n\
number in 0-%d to put at that pos.\n", newsgroup_cnt-1);
printf("\
L for list of newsrc.\n\
q to abort\n") FLUSH;
}
#endif
termdown(10);
goto reask_reloc;
}
else if (*buf == 'q')
return FALSE;
else if (*buf == 'L') {
newline();
list_newsgroups();
goto reask_reloc;
}
else if (isdigit(*buf)) {
if (!finish_command(TRUE)) /* get rest of command */
goto reinp_reloc;
newnum = atol(buf);
if (newnum < 0)
newnum = 0;
if (newnum >= newsgroup_cnt)
newnum = newsgroup_cnt-1;
}
else if (*buf == '^') {
newline();
newnum = 0;
}
else if (*buf == '$') {
newnum = newsgroup_cnt-1;
}
else if (*buf == '.') {
newline();
newnum = current_ng->num;
}
else if (*buf == '-' || *buf == '+') {
if (!finish_command(TRUE)) /* get rest of command */
goto reinp_reloc;
np = find_ng(buf+1);
if (np == NULL) {
fputs("Not found.",stdout) FLUSH;
goto reask_reloc;
}
newnum = np->num;
if (*buf == '+')
newnum++;
}
else {
printf("\n%s",hforhelp) FLUSH;
termdown(2);
settle_down();
goto reask_reloc;
}
}
if (newnum < newsgroup_cnt-1) {
for (np = first_ng; np; np = np->next)
if (np->num >= newnum)
break;
if (!np || np == move_np)
return FALSE; /* This can't happen... */
last_ng = move_np->prev;
last_ng->next = NULL;
move_np->prev = np->prev;
move_np->next = np;
if (np->prev)
np->prev->next = move_np;
else
first_ng = move_np;
np->prev = move_np;
move_np->num = newnum++;
for (; np; np = np->next, newnum++)
np->num = newnum;
}
if (sel_newsgroupsort != SS_NATURAL) {
sel_sort = sel_newsgroupsort;
sort_newsgroups();
sel_sort = save_sort;
}
return TRUE;
}
#endif /* RELOCATE */
/* List out the newsrc with annotations */
void
list_newsgroups()
{
register NGDATA* np;
register NG_NUM i;
char tmpbuf[2048];
static char* status[] = {"(READ)","(UNSUB)","(DUP)","(BOGUS)","(JUNK)"};
page_start();
print_lines(" # Status Newsgroup\n",STANDOUT);
for (np = first_ng, i = 0; np && !int_count; np = np->next, i++) {
if (np->toread >= 0)
set_toread(np, ST_LAX);
*(np->rcline + np->numoffset - 1) = np->subscribechar;
if (np->toread > 0)
sprintf(tmpbuf,"%3d %6ld ",i,(long)np->toread);
else
sprintf(tmpbuf,"%3d %7s ",i,status[-np->toread]);
safecpy(tmpbuf+13, np->rcline, sizeof tmpbuf - 13);
*(np->rcline + np->numoffset - 1) = '\0';
if (print_lines(tmpbuf,NOMARKING) != 0)
break;
}
int_count = 0;
}
/* find a newsgroup in any newsrc */
NGDATA*
find_ng(ngnam)
char* ngnam;
{
HASHDATUM data;
data = hashfetch(newsrc_hash, ngnam, strlen(ngnam));
return (NGDATA*)data.dat_ptr;
}
void
cleanup_newsrc(rp)
NEWSRC* rp;
{
register NGDATA* np;
register NG_NUM bogosity = 0;
#ifdef VERBOSE
IF(verbose)
printf("Checking out '%s' -- hang on a second...\n",rp->name) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("Checking '%s' -- hang on...\n",rp->name) FLUSH;
#endif
termdown(1);
for (np = first_ng; np; np = np->next) {
/*#ifdef CHECK_ALL_BOGUS $$ what is this? */
if (np->toread >= TR_UNSUB)
set_toread(np, ST_LAX); /* this may reset the group or declare it bogus */
/*#endif*/
if (np->toread == TR_BOGUS)
bogosity++;
}
for (np = last_ng; np && np->toread == TR_BOGUS; np = np->prev)
bogosity--; /* discount already moved ones */
if (newsgroup_cnt > 5 && bogosity > newsgroup_cnt / 2) {
fputs(
"It looks like the active file is messed up. Contact your news administrator,\n\
",stdout);
fputs(
"leave the \"bogus\" groups alone, and they may come back to normal. Maybe.\n\
",stdout) FLUSH;
termdown(2);
}
#ifdef RELOCATE
else if (bogosity) {
NGDATA* prev_np;
#ifdef VERBOSE
IF(verbose)
printf("Moving bogus newsgroups to the end of '%s'.\n",rp->name) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("Moving boguses to the end.\n",stdout) FLUSH;
#endif
termdown(1);
while (np) {
prev_np = np->prev;
if (np->toread == TR_BOGUS)
relocate_newsgroup(np, newsgroup_cnt-1);
np = prev_np;
}
rp->flags |= RF_RCCHANGED;
#ifdef DELBOGUS
reask_bogus:
in_char("Delete bogus newsgroups?", 'D', "ny");
#ifdef VERIFY
printcmd();
#endif
newline();
if (*buf == 'h') {
#ifdef VERBOSE
IF(verbose) {
fputs("\
Type y to delete bogus newsgroups.\n\
Type n or SP to leave them at the end in case they return.\n\
",stdout) FLUSH;
termdown(2);
}
ELSE
#endif
#ifdef TERSE
{
fputs("y to delete, n to keep\n",stdout) FLUSH;
termdown(1);
}
#endif
goto reask_bogus;
}
else if (*buf == 'n' || *buf == 'q')
;
else if (*buf == 'y') {
for (np = last_ng; np && np->toread == TR_BOGUS; np = np->prev) {
hashdelete(newsrc_hash, np->rcline, np->numoffset - 1);
clear_ngitem((char*)np,0);
newsgroup_cnt--;
}
rp->flags |= RF_RCCHANGED; /*$$ needed? */
last_ng = np;
if (np)
np->next = NULL;
else
first_ng = NULL;
if (current_ng && !current_ng->rcline)
current_ng = first_ng;
if (recent_ng && !recent_ng->rcline)
recent_ng = first_ng;
if (ngptr && !ngptr->rcline)
ngptr = first_ng;
if (sel_page_np && !sel_page_np->rcline)
sel_page_np = NULL;
}
else {
fputs(hforhelp,stdout) FLUSH;
termdown(1);
settle_down();
goto reask_bogus;
}
#endif /* DELBOGUS */
}
#else /* !RELOCATE */
#ifdef VERBOSE
IF(verbose)
printf("You should edit bogus newsgroups out of '%s'.\n",rp->name) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("Edit boguses from '%s'.\n",rp->name) FLUSH;
#endif
termdown(1);
#endif /* !RELOCATE */
paranoid = FALSE;
}
/* make an entry in the hash table for the current newsgroup */
void
sethash(np)
NGDATA* np;
{
HASHDATUM data;
data.dat_ptr = (char*)np;
data.dat_len = np->numoffset - 1;
hashstore(newsrc_hash, np->rcline, data.dat_len, data);
}
static int
rcline_cmp(key, keylen, data)
char* key;
int keylen;
HASHDATUM data;
{
/* We already know that the lengths are equal, just compare the strings */
return bcmp(key, ((NGDATA*)data.dat_ptr)->rcline, keylen);
}
/* checkpoint the newsrc(s) */
void
checkpoint_newsrcs()
{
#ifdef DEBUG
if (debug & DEB_CHECKPOINTING) {
fputs("(ckpt)",stdout);
fflush(stdout);
}
#endif
if (doing_ng)
bits_to_rc(); /* do not restore M articles */
if (!write_newsrcs(multirc))
get_anything();
#ifdef DEBUG
if (debug & DEB_CHECKPOINTING) {
fputs("(done)",stdout);
fflush(stdout);
}
#endif
}
/* write out the (presumably) revised newsrc(s) */
bool
write_newsrcs(mptr)
MULTIRC* mptr;
{
NEWSRC* rp;
register NGDATA* np;
int save_sort = sel_sort;
FILE* rcfp;
bool total_success = TRUE;
if (!mptr)
return TRUE;
if (sel_newsgroupsort != SS_NATURAL) {
sel_sort = SS_NATURAL;
sel_direction = 1;
sort_newsgroups();
}
for (rp = mptr->first; rp; rp = rp->next) {
if (!(rp->flags & RF_ACTIVE))
continue;
if (rp->infoname) {
if ((tmpfp = fopen(rp->infoname, "w")) != NULL) {
fprintf(tmpfp,"Last-Group: %s\nNew-Group-State: %ld,%ld,%ld\n",
ngname? ngname : nullstr,rp->datasrc->lastnewgrp,
rp->datasrc->act_sf.recent_cnt,
rp->datasrc->desc_sf.recent_cnt);
fclose(tmpfp);
}
}
else {
readlast();
#ifdef SUPPORT_NNTP
if (rp->datasrc->flags & DF_REMOTE) {
lastactsiz = rp->datasrc->act_sf.recent_cnt;
lastextranum = rp->datasrc->desc_sf.recent_cnt;
}
else
#endif
lastextranum = rp->datasrc->act_sf.recent_cnt;
lastnewtime = rp->datasrc->lastnewgrp;
writelast();
}
if (!(rp->flags & RF_RCCHANGED))
continue;
rcfp = fopen(rp->newname, "w");
if (rcfp == NULL) {
printf(cantrecreate,rp->name) FLUSH;
total_success = FALSE;
continue;
}
#ifndef MSDOS
if (stat(rp->name,&filestat)>=0) { /* preserve permissions */
chmod(rp->newname,filestat.st_mode&0666);
chown(rp->newname,filestat.st_uid,filestat.st_gid);
}
#endif
/* write out each line*/
for (np = first_ng; np; np = np->next) {
register char* delim;
if (np->rc != rp)
continue;
if (np->numoffset) {
delim = np->rcline + np->numoffset - 1;
*delim = np->subscribechar;
if ((np->flags & NF_UNTHREADED) && delim[2] == '1')
delim[2] = '0';
}
else
delim = NULL;
#ifdef DEBUG
if (debug & DEB_NEWSRC_LINE) {
printf("%s\n",np->rcline) FLUSH;
termdown(1);
}
#endif
if (fprintf(rcfp,"%s\n",np->rcline) < 0) {
fclose(rcfp); /* close new newsrc */
goto write_error;
}
if (delim) {
*delim = '\0'; /* might still need this line */
if ((np->flags & NF_UNTHREADED) && delim[2] == '0')
delim[2] = '1';
}
}
fflush(rcfp);
/* fclose is the only sure test for full disks via NFS */
if (ferror(rcfp)) {
fclose(rcfp);
goto write_error;
}
if (fclose(rcfp) == EOF) {
write_error:
printf(cantrecreate,rp->name) FLUSH;
UNLINK(rp->newname);
total_success = FALSE;
continue;
}
rp->flags &= ~RF_RCCHANGED;
UNLINK(rp->name);
RENAME(rp->newname,rp->name);
}
if (sel_newsgroupsort != SS_NATURAL) {
sel_sort = sel_newsgroupsort;
sort_newsgroups();
sel_sort = save_sort;
}
return total_success;
}
void
get_old_newsrcs(mptr)
MULTIRC* mptr;
{
NEWSRC* rp;
if (mptr) {
for (rp = mptr->first; rp; rp = rp->next) {
if (rp->flags & RF_ACTIVE) {
UNLINK(rp->newname);
RENAME(rp->name,rp->newname);
RENAME(rp->oldname,rp->name);
}
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1