/* datasrc.c
*/
/* This software is copyrighted as detailed in the LICENSE file. */
#include "EXTERN.h"
#include "common.h"
#include "list.h"
#include "trn.h"
#include "hash.h"
#include "ngdata.h"
#include "nntpclient.h"
#include "term.h"
#include "env.h"
#include "opt.h"
#include "util.h"
#include "util2.h"
#include "intrp.h"
#include "init.h"
#include "rcstuff.h"
#include "edit_dist.h"
#include "cache.h"
#include "last.h"
#include "rt-mt.h"
#include "rt-ov.h"
#include "rt-util.h"
#include "INTERN.h"
#include "datasrc.h"
#include "datasrc.ih"
#include "EXTERN.h"
#include "nntp.h"
void
datasrc_init()
{
char** vals = prep_ini_words(datasrc_ini);
char* machine = NULL;
char* actname = NULL;
char* s;
datasrc_list = new_list(0,0,sizeof(DATASRC),20,LF_ZERO_MEM,NULL);
#ifdef SUPPORT_NNTP
nntp_auth_file = savestr(filexp(NNTP_AUTH_FILE));
machine = getenv("NNTPSERVER");
if (machine && strNE(machine,"local")) {
vals[DI_NNTP_SERVER] = machine;
vals[DI_AUTH_USER] = read_auth_file(nntp_auth_file,
&vals[DI_AUTH_PASS]);
vals[DI_FORCE_AUTH] = getenv("NNTP_FORCE_AUTH");
new_datasrc("default",vals);
}
#endif
trnaccess_mem = read_datasrcs(TRNACCESS);
s = read_datasrcs(DEFACCESS);
if (!trnaccess_mem)
trnaccess_mem = s;
else if (s)
free(s);
#ifdef SUPPORT_NNTP
if (!machine) {
machine = filexp(SERVER_NAME);
if (FILE_REF(machine))
machine = nntp_servername(machine);
if (strEQ(machine,"local")) {
machine = NULL;
actname = ACTIVE;
}
#else
actname = ACTIVE;
#endif
prep_ini_words(datasrc_ini); /* re-zero the values */
vals[DI_NNTP_SERVER] = machine;
vals[DI_ACTIVE_FILE] = actname;
vals[DI_SPOOL_DIR] = NEWSSPOOL;
vals[DI_THREAD_DIR] = THREAD_DIR;
vals[DI_OVERVIEW_DIR] = OVERVIEW_DIR;
vals[DI_OVERVIEW_FMT] = OVERVIEW_FMT;
vals[DI_ACTIVE_TIMES] = ACTIVE_TIMES;
vals[DI_GROUP_DESC] = GROUPDESC;
#ifdef SUPPORT_NNTP
if (machine) {
vals[DI_AUTH_USER] = read_auth_file(nntp_auth_file,
&vals[DI_AUTH_PASS]);
vals[DI_FORCE_AUTH] = getenv("NNTP_FORCE_AUTH");
}
#endif
new_datasrc("default",vals);
#ifdef SUPPORT_NNTP
}
#endif
unprep_ini_words(datasrc_ini);
}
char*
read_datasrcs(filename)
char* filename;
{
int fd;
char* s;
char* section;
char* cond;
char* filebuf = NULL;
char** vals = INI_VALUES(datasrc_ini);
if ((fd = open(filexp(filename),0)) >= 0) {
fstat(fd,&filestat);
if (filestat.st_size) {
int len;
filebuf = safemalloc((MEM_SIZE)filestat.st_size+2);
len = read(fd,filebuf,(int)filestat.st_size);
(filebuf)[len] = '\0';
prep_ini_data(filebuf,filename);
s = filebuf;
while ((s = next_ini_section(s,§ion,&cond)) != NULL) {
if (*cond && !check_ini_cond(cond))
continue;
if (strncaseEQ(section, "group ", 6))
continue;
s = parse_ini_section(s, datasrc_ini);
if (!s)
break;
new_datasrc(section,vals);
}
}
close(fd);
}
return filebuf;
}
DATASRC*
get_datasrc(name)
char* name;
{
DATASRC* dp;
for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp))
if (strEQ(dp->name,name))
return dp;
return NULL;
}
DATASRC*
new_datasrc(name,vals)
char* name;
char** vals;
{
DATASRC* dp = datasrc_ptr(datasrc_cnt++);
char* v;
if (vals[DI_NNTP_SERVER]) {
#ifdef SUPPORT_NNTP
dp->flags |= DF_REMOTE;
#else
datasrc_cnt--;
return NULL;
#endif
}
else if (!vals[DI_ACTIVE_FILE])
return NULL; /*$$*/
dp->name = savestr(name);
if (strEQ(name,"default"))
dp->flags |= DF_DEFAULT;
#ifdef SUPPORT_NNTP
if ((v = vals[DI_NNTP_SERVER]) != NULL) {
char* cp;
dp->newsid = savestr(v);
if ((cp = index(dp->newsid, ';')) != NULL) {
*cp = '\0';
dp->nntplink.port_number = atoi(cp+1);
}
if ((v = vals[DI_ACT_REFETCH]) != NULL && *v)
dp->act_sf.refetch_secs = text2secs(v,defRefetchSecs);
else if (!vals[DI_ACTIVE_FILE])
dp->act_sf.refetch_secs = defRefetchSecs;
}
else
#endif /* SUPPORT_NNTP */
dp->newsid = savestr(filexp(vals[DI_ACTIVE_FILE]));
if (!(dp->spool_dir = file_or_none(vals[DI_SPOOL_DIR])))
dp->spool_dir = savestr(tmpdir);
dp->over_dir = dir_or_none(dp,vals[DI_OVERVIEW_DIR],DF_TRY_OVERVIEW);
dp->over_fmt = file_or_none(vals[DI_OVERVIEW_FMT]);
dp->thread_dir = dir_or_none(dp,vals[DI_THREAD_DIR],DF_TRY_THREAD);
dp->grpdesc = dir_or_none(dp,vals[DI_GROUP_DESC],0);
dp->extra_name = dir_or_none(dp,vals[DI_ACTIVE_TIMES],DF_ADD_OK);
#ifdef SUPPORT_NNTP
if (dp->flags & DF_REMOTE) {
/* FYI, we know extra_name to be NULL in this case. */
if (vals[DI_ACTIVE_FILE]) {
dp->extra_name = savestr(filexp(vals[DI_ACTIVE_FILE]));
if (stat(dp->extra_name,&filestat) >= 0)
dp->act_sf.lastfetch = filestat.st_mtime;
}
else {
dp->extra_name = temp_filename();
dp->flags |= DF_TMPACTFILE;
if (!dp->act_sf.refetch_secs)
dp->act_sf.refetch_secs = 1;
}
if ((v = vals[DI_DESC_REFETCH]) != NULL && *v)
dp->desc_sf.refetch_secs = text2secs(v,defRefetchSecs);
else if (!dp->grpdesc)
dp->desc_sf.refetch_secs = defRefetchSecs;
if (dp->grpdesc) {
if (stat(dp->grpdesc,&filestat) >= 0)
dp->desc_sf.lastfetch = filestat.st_mtime;
}
else {
dp->grpdesc = temp_filename();
dp->flags |= DF_TMPGRPDESC;
if (!dp->desc_sf.refetch_secs)
dp->desc_sf.refetch_secs = 1;
}
}
if ((v = vals[DI_FORCE_AUTH]) != NULL && (*v == 'y' || *v == 'Y'))
dp->nntplink.flags |= NNTP_FORCE_AUTH_NEEDED;
if ((v = vals[DI_AUTH_USER]) != NULL)
dp->auth_user = savestr(v);
if ((v = vals[DI_AUTH_PASS]) != NULL)
dp->auth_pass = savestr(v);
#ifdef USE_GENAUTH
if ((v = vals[DI_AUTH_COMMAND]) != NULL)
dp->auth_command = savestr(v);
#endif
if ((v = vals[DI_XHDR_BROKEN]) != NULL && (*v == 'y' || *v == 'Y'))
dp->flags |= DF_XHDR_BROKEN;
if ((v = vals[DI_XREFS]) != NULL && (*v == 'n' || *v == 'N'))
dp->flags |= DF_NOXREFS;
#endif /* SUPPORT_NNTP */
return dp;
}
static char*
dir_or_none(dp,dir,flag)
DATASRC* dp;
char* dir;
int flag;
{
if (!dir || !*dir || strEQ(dir, "remote")) {
dp->flags |= flag;
#ifdef SUPPORT_NNTP
if (dp->flags & DF_REMOTE)
return NULL;
#endif
if (flag == DF_ADD_OK) {
char* cp = safemalloc(strlen(dp->newsid)+6+1);
sprintf(cp,"%s.times",dp->newsid);
return cp;
}
if (flag == 0) {
char* cp = rindex(dp->newsid,'/');
int len;
if (!cp)
return NULL;
len = cp - dp->newsid + 1;
cp = safemalloc(len+10+1);
strcpy(cp,dp->newsid);
strcpy(cp+len,"newsgroups");
return cp;
}
return dp->spool_dir;
}
if (strEQ(dir, "none"))
return NULL;
dp->flags |= flag;
dir = filexp(dir);
if (strEQ(dir,dp->spool_dir))
return dp->spool_dir;
return savestr(dir);
}
static char*
file_or_none(fn)
char* fn;
{
if (!fn || !*fn || strEQ(fn, "none") || strEQ(fn, "remote"))
return NULL;
return savestr(filexp(fn));
}
bool
open_datasrc(dp)
DATASRC* dp;
{
bool success;
if (dp->flags & DF_UNAVAILABLE)
return FALSE;
set_datasrc(dp);
if (dp->flags & DF_OPEN)
return TRUE;
#ifdef SUPPORT_NNTP
if (dp->flags & DF_REMOTE) {
if (nntp_connect(dp->newsid,1) <= 0) {
dp->flags |= DF_UNAVAILABLE;
return FALSE;
}
nntp_allow_timeout = FALSE;
dp->nntplink = nntplink;
if (dp->act_sf.refetch_secs) {
switch (nntp_list("active", "control", 7)) {
case 1:
if (strnNE(ser_line, "control ", 8)) {
strcpy(buf, ser_line);
dp->act_sf.lastfetch = 0;
success = actfile_hash(dp);
break;
}
if (nntp_gets(buf, sizeof buf - 1) > 0
&& !nntp_at_list_end(buf)) {
nntp_finish_list();
success = actfile_hash(dp);
break;
}
/* FALL THROUGH */
case 0:
dp->flags |= DF_USELISTACT;
if (dp->flags & DF_TMPACTFILE) {
dp->flags &= ~DF_TMPACTFILE;
free(dp->extra_name);
dp->extra_name = NULL;
dp->act_sf.refetch_secs = 0;
success = srcfile_open(&dp->act_sf,(char*)NULL,
(char*)NULL,(char*)NULL);
}
else
success = actfile_hash(dp);
break;
case -2:
printf("Failed to open news server %s:\n%s\n", dp->newsid, ser_line);
termdown(2);
success = FALSE;
break;
default:
success = actfile_hash(dp);
break;
}
} else
success = actfile_hash(dp);
}
else
#endif
success = actfile_hash(dp);
if (success) {
dp->flags |= DF_OPEN;
if (dp->flags & DF_TRY_OVERVIEW)
ov_init();
if (dp->flags & DF_TRY_THREAD)
mt_init();
}
else
dp->flags |= DF_UNAVAILABLE;
#ifdef SUPPORT_NNTP
if (dp->flags & DF_REMOTE)
nntp_allow_timeout = TRUE;
#endif
return success;
}
void
set_datasrc(dp)
DATASRC* dp;
{
#ifdef SUPPORT_NNTP
if (datasrc)
datasrc->nntplink = nntplink;
if (dp)
nntplink = dp->nntplink;
#endif
datasrc = dp;
}
void
check_datasrcs()
{
#ifdef SUPPORT_NNTP
DATASRC* dp;
time_t now = time((time_t*)NULL);
time_t limit;
if (datasrc_list) {
for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) {
if ((dp->flags & DF_OPEN) && dp->nntplink.rd_fp != NULL) {
limit = ((dp->flags & DF_ACTIVE)? 30*60 : 10*60);
if (now - dp->nntplink.last_command > limit) {
DATASRC* save_datasrc = datasrc;
/*printf("\n*** Closing %s ***\n", dp->name); $$*/
set_datasrc(dp);
nntp_close(TRUE);
dp->nntplink = nntplink;
set_datasrc(save_datasrc);
}
}
}
}
#endif
}
void
close_datasrc(dp)
DATASRC* dp;
{
#ifdef SUPPORT_NNTP
if (dp->flags & DF_REMOTE) {
if (dp->flags & DF_TMPACTFILE)
UNLINK(dp->extra_name);
else
srcfile_end_append(&dp->act_sf, dp->extra_name);
if (dp->grpdesc) {
if (dp->flags & DF_TMPGRPDESC)
UNLINK(dp->grpdesc);
else
srcfile_end_append(&dp->desc_sf, dp->grpdesc);
}
}
#endif
if (!(dp->flags & DF_OPEN))
return;
#ifdef SUPPORT_NNTP
if (dp->flags & DF_REMOTE) {
DATASRC* save_datasrc = datasrc;
set_datasrc(dp);
nntp_close(TRUE);
dp->nntplink = nntplink;
set_datasrc(save_datasrc);
}
#endif
srcfile_close(&dp->act_sf);
srcfile_close(&dp->desc_sf);
dp->flags &= ~DF_OPEN;
if (datasrc == dp)
datasrc = NULL;
}
bool
actfile_hash(dp)
DATASRC* dp;
{
int ret;
#ifdef SUPPORT_NNTP
if (dp->flags & DF_REMOTE) {
DATASRC* save_datasrc = datasrc;
set_datasrc(dp);
spin_todo = dp->act_sf.recent_cnt;
ret = srcfile_open(&dp->act_sf, dp->extra_name, "active", dp->newsid);
if (spin_count > 0)
dp->act_sf.recent_cnt = spin_count;
set_datasrc(save_datasrc);
}
else
#endif
ret = srcfile_open(&dp->act_sf, dp->newsid, (char*)NULL, (char*)NULL);
return ret;
}
bool
find_actgrp(dp, outbuf, nam, len, high)
DATASRC* dp;
register char* outbuf;
register char* nam;
register int len;
ART_NUM high;
{
HASHDATUM data;
ACT_POS act_pos;
FILE* fp = dp->act_sf.fp;
char* lbp;
int lbp_len;
/* Do a quick, hashed lookup */
outbuf[0] = '\0';
data = hashfetch(dp->act_sf.hp, nam, len);
if (data.dat_ptr) {
LISTNODE* node = (LISTNODE*)data.dat_ptr;
/*dp->act_sf.lp->recent = node;*/
act_pos = node->low + data.dat_len;
lbp = node->data + data.dat_len;
lbp_len = index(lbp, '\n') - lbp + 1;
}
else {
lbp = NULL;
lbp_len = 0;
}
#ifdef SUPPORT_NNTP
if ((dp->flags & DF_USELISTACT)
&& (DATASRC_NNTP_FLAGS(dp) & NNTP_NEW_CMD_OK)) {
DATASRC* save_datasrc = datasrc;
set_datasrc(dp);
switch (nntp_list("active", nam, len)) {
case 0:
set_datasrc(save_datasrc);
return 0;
case 1:
sprintf(outbuf, "%s\n", ser_line);
nntp_finish_list();
break;
case -2:
/*$$$$*/
break;
}
set_datasrc(save_datasrc);
if (!lbp_len) {
if (fp)
(void) srcfile_append(&dp->act_sf, outbuf, len);
return 1;
}
# ifndef ANCIENT_NEWS
/* Safely update the low-water mark */
{
char* f = rindex(outbuf, ' ');
char* t = lbp + lbp_len;
while (*--t != ' ') ;
while (t > lbp) {
if (*--t == ' ')
break;
if (f[-1] == ' ')
*t = '0';
else
*t = *--f;
}
}
# endif
high = (ART_NUM)atol(outbuf+len+1);
}
#endif
if (lbp_len) {
#ifdef SUPPORT_NNTP
if ((dp->flags & DF_REMOTE) && dp->act_sf.refetch_secs) {
int num;
char* cp;
if (high && high != (ART_NUM)atol(cp = lbp+len+1)) {
while (isdigit(*cp)) cp++;
while (*--cp != ' ') {
num = high % 10;
high = high / 10;
*cp = '0' + (char)num;
}
fseek(fp, act_pos, 0);
fwrite(lbp, 1, lbp_len, fp);
}
goto use_cache;
}
#endif
/* hopefully this forces a reread */
fseek(fp,2000000000L,1);
/*$$ if line has changed length or is not there, we should
* discard/close the active file, and re-open it. $$*/
if (fseek(fp, act_pos, 0) >= 0
&& fgets(outbuf, LBUFLEN, fp) != NULL
&& strnEQ(outbuf, nam, len) && outbuf[len] == ' ') {
/* Remember the latest info in our cache. */
(void) bcopy(outbuf, lbp, lbp_len);
return 1;
}
use_cache:
/* Return our cached version */
(void) bcopy(lbp, outbuf, lbp_len);
outbuf[lbp_len] = '\0';
return 1;
}
return 0; /* no such group */
}
char*
find_grpdesc(dp, groupname)
DATASRC* dp;
char* groupname;
{
HASHDATUM data;
int grouplen;
int ret;
if (!dp->grpdesc)
return nullstr;
if (!dp->desc_sf.hp) {
#ifdef SUPPORT_NNTP
if ((dp->flags & DF_REMOTE) && dp->desc_sf.refetch_secs) {
/*DATASRC* save_datasrc = datasrc;*/
set_datasrc(dp);
if ((dp->flags & (DF_TMPGRPDESC|DF_NOXGTITLE)) == DF_TMPGRPDESC
&& netspeed < 5) {
(void)srcfile_open(&dp->desc_sf,(char*)NULL,/*$$check return?*/
(char*)NULL,(char*)NULL);
grouplen = strlen(groupname);
goto try_xgtitle;
}
spin_todo = dp->desc_sf.recent_cnt;
ret = srcfile_open(&dp->desc_sf, dp->grpdesc,
"newsgroups", dp->newsid);
if (spin_count > 0)
dp->desc_sf.recent_cnt = spin_count;
/*set_datasrc(save_datasrc);*/
}
else
#endif
ret = srcfile_open(&dp->desc_sf, dp->grpdesc,
(char*)NULL, (char*)NULL);
if (!ret) {
#ifdef SUPPORT_NNTP
if (dp->flags & DF_TMPGRPDESC) {
dp->flags &= ~DF_TMPGRPDESC;
UNLINK(dp->grpdesc);
}
#endif
free(dp->grpdesc);
dp->grpdesc = NULL;
return nullstr;
}
#ifdef SUPPORT_NNTP
if (ret == 2 || !dp->desc_sf.refetch_secs)
dp->flags |= DF_NOXGTITLE;
#endif
}
grouplen = strlen(groupname);
data = hashfetch(dp->desc_sf.hp, groupname, grouplen);
if (data.dat_ptr) {
LISTNODE* node = (LISTNODE*)data.dat_ptr;
/*dp->act_sf.lp->recent = node;*/
return node->data + data.dat_len + grouplen + 1;
}
#ifdef SUPPORT_NNTP
try_xgtitle:
if ((dp->flags & (DF_REMOTE|DF_NOXGTITLE)) == DF_REMOTE) {
set_datasrc(dp);
sprintf(ser_line, "XGTITLE %s", groupname);
if (nntp_command(ser_line) > 0 && nntp_check() > 0) {
nntp_gets(buf, sizeof buf - 1);
if (nntp_at_list_end(buf))
sprintf(buf, "%s \n", groupname);
else {
nntp_finish_list();
strcat(buf, "\n");
}
groupname = srcfile_append(&dp->desc_sf, buf, grouplen);
return groupname+grouplen+1;
}
dp->flags |= DF_NOXGTITLE;
if (dp->desc_sf.lp->high == -1) {
srcfile_close(&dp->desc_sf);
if (dp->flags & DF_TMPGRPDESC)
return find_grpdesc(dp, groupname);
free(dp->grpdesc);
dp->grpdesc = NULL;
}
}
#endif
return nullstr;
}
int
srcfile_open(sfp, filename, fetchcmd, server)
SRCFILE* sfp;
char* filename;
char* fetchcmd;
char* server;
{
register unsigned offset;
register char* s;
HASHDATUM data;
long node_low;
int keylen, linelen;
FILE* fp;
char* lbp;
#ifdef SUPPORT_NNTP
time_t now = time((time_t*)NULL);
bool use_buffered_nntp_gets = 0;
if (!filename)
fp = NULL;
else if (server) {
if (!sfp->refetch_secs) {
server = NULL;
fp = fopen(filename, "r");
spin_todo = 0;
}
else if (now - sfp->lastfetch > sfp->refetch_secs
&& (sfp->refetch_secs != 2 || !sfp->lastfetch)) {
fp = fopen(filename, "w+");
if (fp) {
printf("Getting %s file from %s.", fetchcmd, server);
fflush(stdout);
/* tell server we want the file */
if (!(nntplink.flags & NNTP_NEW_CMD_OK))
use_buffered_nntp_gets = 1;
else if (nntp_list(fetchcmd, nullstr, 0) < 0) {
printf("\nCan't get %s file from server: \n%s\n",
fetchcmd, ser_line) FLUSH;
termdown(2);
fclose(fp);
return 0;
}
sfp->lastfetch = now;
if (netspeed > 8)
spin_todo = 0;
}
}
else {
server = NULL;
fp = fopen(filename, "r+");
if (!fp) {
sfp->refetch_secs = 0;
fp = fopen(filename, "r");
}
spin_todo = 0;
}
if (sfp->refetch_secs & 3)
sfp->refetch_secs += 365L*24*60*60;
}
else
#endif
{
fp = fopen(filename, "r");
spin_todo = 0;
}
if (filename && fp == NULL) {
printf(cantopen, filename) FLUSH;
termdown(1);
return 0;
}
setspin(spin_todo > 0? SPIN_BARGRAPH : SPIN_FOREGROUND);
srcfile_close(sfp);
/* Create a list with one character per item using a large chunk size. */
sfp->lp = new_list(0, 0, 1, SRCFILE_CHUNK_SIZE, 0, NULL);
sfp->hp = hashcreate(3001, srcfile_cmp);
sfp->fp = fp;
#ifdef SUPPORT_NNTP
if (!filename) {
(void) listnum2listitem(sfp->lp, 0);
sfp->lp->high = -1;
setspin(SPIN_OFF);
return 1;
}
#endif
lbp = listnum2listitem(sfp->lp, 0);
data.dat_ptr = (char*)sfp->lp->first;
for (offset = 0, node_low = 0; ; offset += linelen, lbp += linelen) {
#ifdef SUPPORT_NNTP
if (server) {
if (use_buffered_nntp_gets)
use_buffered_nntp_gets = 0;
else if (nntp_gets(buf, sizeof buf - 1) < 0) {
printf("\nError getting %s file.\n", fetchcmd) FLUSH;
termdown(2);
srcfile_close(sfp);
setspin(SPIN_OFF);
return 0;
}
if (nntp_at_list_end(buf))
break;
strcat(buf,"\n");
fputs(buf, fp);
spin(200 * netspeed);
}
#endif
ElseIf (!fgets(buf, sizeof buf, fp))
break;
for (s = buf; *s && !isspace(*s); s++) ;
if (!*s) {
linelen = 0;
continue;
}
keylen = s - buf;
if (*++s != '\n' && isspace(*s)) {
while (*++s != '\n' && isspace(*s)) ;
strcpy(buf+keylen+1, s);
s = buf+keylen+1;
}
for (s++; *s && *s != '\n'; s++) {
if (AT_GREY_SPACE(s))
*s = ' ';
}
linelen = s - buf + 1;
if (*s != '\n') {
if (linelen == sizeof buf) {
linelen = 0;
continue;
}
*s++ = '\n';
*s = '\0';
}
if (offset + linelen > SRCFILE_CHUNK_SIZE) {
LISTNODE* node = sfp->lp->recent;
node_low += offset;
node->high = node_low - 1;
node->data_high = node->data + offset - 1;
offset = 0;
lbp = listnum2listitem(sfp->lp, node_low);
data.dat_ptr = (char*)sfp->lp->recent;
}
data.dat_len = offset;
(void) bcopy(buf, lbp, linelen);
hashstore(sfp->hp, buf, keylen, data);
}
sfp->lp->high = node_low + offset - 1;
setspin(SPIN_OFF);
#ifdef SUPPORT_NNTP
if (server) {
fflush(fp);
if (ferror(fp)) {
printf("\nError writing the %s file %s.\n",fetchcmd,filename) FLUSH;
termdown(2);
srcfile_close(sfp);
return 0;
}
newline();
}
#endif
fseek(fp,0L,0);
return server? 2 : 1;
}
#ifdef SUPPORT_NNTP
char*
srcfile_append(sfp, bp, keylen)
SRCFILE* sfp;
char* bp;
int keylen;
{
LISTNODE* node;
long pos;
HASHDATUM data;
char* s;
char* lbp;
int linelen;
pos = sfp->lp->high + 1;
lbp = listnum2listitem(sfp->lp, pos);
node = sfp->lp->recent;
data.dat_len = pos - node->low;
s = bp + keylen + 1;
if (sfp->fp && sfp->refetch_secs && *s != '\n') {
fseek(sfp->fp, 0, 2);
fputs(bp, sfp->fp);
}
if (*s != '\n' && isspace(*s)) {
while (*++s != '\n' && isspace(*s)) ;
strcpy(bp+keylen+1, s);
s = bp+keylen+1;
}
for (s++; *s && *s != '\n'; s++) {
if (AT_GREY_SPACE(s))
*s = ' ';
}
linelen = s - bp + 1;
if (*s != '\n') {
*s++ = '\n';
*s = '\0';
}
if (data.dat_len + linelen > SRCFILE_CHUNK_SIZE) {
node->high = pos - 1;
node->data_high = node->data + data.dat_len - 1;
lbp = listnum2listitem(sfp->lp, pos);
node = sfp->lp->recent;
data.dat_len = 0;
}
data.dat_ptr = (char*)node;
(void) bcopy(bp, lbp, linelen);
hashstore(sfp->hp, bp, keylen, data);
sfp->lp->high = pos + linelen - 1;
return lbp;
}
#endif /* SUPPORT_NNTP */
#ifdef SUPPORT_NNTP
void
srcfile_end_append(sfp, filename)
SRCFILE* sfp;
char* filename;
{
if (sfp->fp && sfp->refetch_secs) {
fflush(sfp->fp);
if (sfp->lastfetch) {
struct utimbuf ut;
time(&ut.actime);
ut.modtime = sfp->lastfetch;
(void) utime(filename, &ut);
}
}
}
#endif /* SUPPORT_NNTP */
void
srcfile_close(sfp)
SRCFILE* sfp;
{
if (sfp->fp) {
fclose(sfp->fp);
sfp->fp = NULL;
}
if (sfp->lp) {
delete_list(sfp->lp);
sfp->lp = NULL;
}
if (sfp->hp) {
hashdestroy(sfp->hp);
sfp->hp = NULL;
}
}
static int
srcfile_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, ((LISTNODE*)data.dat_ptr)->data + data.dat_len, keylen);
}
#ifdef EDIT_DISTANCE
/* Edit Distance extension to trn
*
* Mark Maimone (mwm@cmu.edu)
* Carnegie Mellon Computer Science
* 9 May 1993
*
* This code helps trn handle typos in newsgroup names much more
* gracefully. Instead of "... does not exist!!", it will pick the
* nearest one, or offer you a choice if there are several options.
*/
/* find_close_match -- Finds the closest match for the string given in
* global ngname. If found, the result will be the corrected string
* returned in that global.
*
* We compare the (presumably misspelled) newsgroup name with all legal
* newsgroups, using the Edit Distance metric. The edit distance between
* two strings is the minimum number of simple operations required to
* convert one string to another (the implementation here supports INSERT,
* DELETE, CHANGE and SWAP). This gives every legal newsgroup an integer
* rank.
*
* You might want to present all of the closest matches, and let the user
* choose among them. But because I'm lazy I chose to only keep track of
* all with newsgroups with the *single* smallest error, in array ngptrs[].
* A more flexible approach would keep around the 10 best matches, whether
* or not they had precisely the same edit distance, but oh well.
*/
static char** ngptrs; /* List of potential matches */
static int ngn; /* Length of list in ngptrs[] */
static int best_match; /* Value of best match */
int
find_close_match()
{
DATASRC* dp;
int ret = 0;
best_match = -1;
ngptrs = (char**)safemalloc(MAX_NG * sizeof (char*));
ngn = 0;
/* Iterate over all legal newsgroups */
for (dp = datasrc_first(); dp && dp->name; dp = datasrc_next(dp)) {
if (dp->flags & DF_OPEN) {
if (dp->act_sf.hp)
hashwalk(dp->act_sf.hp, check_distance, 0);
else
ret = -1;
}
}
if (ret < 0) {
hashwalk(newsrc_hash, check_distance, 1);
ret = 0;
}
/* ngn is the number of possibilities. If there's just one, go with it. */
switch (ngn) {
case 0:
break;
case 1: {
char* cp = index(ngptrs[0], ' ');
if (cp)
*cp = '\0';
#ifdef VERBOSE
IF(verbose)
printf("(I assume you meant %s)\n", ngptrs[0]) FLUSH;
ELSE
#endif
#ifdef TERSE
printf("(Using %s)\n", ngptrs[0]) FLUSH;
#endif
set_ngname(ngptrs[0]);
if (cp)
*cp = ' ';
ret = 1;
break;
}
default:
ret = get_near_miss();
break;
}
free((char*)ngptrs);
return ret;
}
static int
check_distance(len, data, newsrc_ptr)
int len;
HASHDATUM* data;
int newsrc_ptr;
{
int value;
char* name;
if (newsrc_ptr)
name = ((NGDATA*)data->dat_ptr)->rcline;
else
name = ((LISTNODE*)data->dat_ptr)->data + data->dat_len;
/* Efficiency: don't call edit_dist when the lengths are too different. */
if (len < ngname_len) {
if (ngname_len - len > LENGTH_HACK)
return 0;
}
else {
if (len - ngname_len > LENGTH_HACK)
return 0;
}
value = edit_distn(ngname, ngname_len, name, len);
if (value > MIN_DIST)
return 0;
if (value < best_match)
ngn = 0;
if (best_match < 0 || value <= best_match) {
int i;
for (i = 0; i < ngn; i++) {
if (strEQ(name, ngptrs[i]))
return 0;
}
best_match = value;
if (ngn < MAX_NG)
ngptrs[ngn++] = name;
}
return 0;
}
/* Now we've got several potential matches, and have to choose between them
** somehow. Again, results will be returned in global ngname.
*/
static int
get_near_miss()
{
char promptbuf[256];
char options[MAX_NG+10];
char* op = options;
int i;
#ifdef VERBOSE
IF(verbose)
printf("However, here are some close matches:\n") FLUSH;
#endif
if (ngn > 9)
ngn = 9; /* Since we're using single digits.... */
for (i = 0; i < ngn; i++) {
char* cp = index(ngptrs[i], ' ');
if (cp)
*cp = '\0';
printf(" %d. %s\n", i+1, ngptrs[i]);
sprintf(op++, "%d", i+1); /* Expensive, but avoids ASCII deps */
if (cp)
*cp = ' ';
}
*op++ = 'n';
*op = '\0';
#ifdef VERBOSE
IF(verbose)
sprintf(promptbuf, "Which of these would you like?");
ELSE
#endif
#ifdef TERSE
sprintf(promptbuf, "Which?");
#endif
reask:
in_char(promptbuf, 'A', options);
#ifdef VERIFY
printcmd();
#endif
putchar('\n') FLUSH;
switch (*buf) {
case 'n':
case 'N':
case 'q':
case 'Q':
case 'x':
case 'X':
return 0;
case 'h':
case 'H':
#ifdef VERBOSE
IF(verbose)
fputs(" You entered an illegal newsgroup name, and these are the nearest possible\n matches. If you want one of these, then enter its number. Otherwise\n just say 'n'.\n", stdout) FLUSH;
ELSE
#endif
#ifdef TERSE
fputs("Illegal newsgroup, enter a number or 'n'.\n", stdout) FLUSH;
#endif
goto reask;
default:
if (isdigit(*buf)) {
char* s = index(options, *buf);
i = s ? (s - options) : ngn;
if (i >= 0 && i < ngn) {
char* cp = index(ngptrs[i], ' ');
if (cp)
*cp = '\0';
set_ngname(ngptrs[i]);
if (cp)
*cp = ' ';
return 1;
}
}
fputs(hforhelp, stdout) FLUSH;
break;
}
settle_down();
goto reask;
}
#endif /* EDIT_DISTANCE */
syntax highlighted by Code2HTML, v. 0.9.1