home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
source
/
netnwscd.sit
/
newslists.c
< prev
next >
Wrap
Text File
|
1990-10-18
|
14KB
|
725 lines
/*
* newslists.c
* Copyright ⌐ Tom Bereiter, 1990
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "CGroupList.h"
#include "CArticleList.h"
#include "newslists.h"
#include "nntp.h"
#include "bits.h"
extern CursHandle gWatchCursor; /* Watch cursor for waiting */
#define dlogConfig 140
char newsrc[]="newsrc";
char newsrcnew[]="newsrc.new";
char newsrcold[]="newsrc.old";
char server[128];
int last_ngrps;
int last_txtsize;
char *Xrealloc(Ptr p, long n);
char *savestr();
long strtol();
char *txt; /* group name string space */
long txtsize;
#define ARTBUF_SZ 0x7F00L /* almost 32k */
#define ARTLINE_SZ 0x100L
char *artbuf; /* article buffer */
int artlen;
grp_t *grps; /* group list */
long ngrps; /* number of groups in group list */
grp_t *grp_head; /* head of subscribed list */
long nsgrps; /* number of subscribed groups */
act_compar(a,b)
grp_t *a,*b;
{
return strcmp(a->gname, b->gname);
}
init_news() {
CGroupList *lwin;
char imsg[256];
int response;
getconfig();
sprintf(imsg, "Connecting to %s...", server);
SetCursor(*gWatchCursor);
lwin = new(CGroupList);
lwin->IGroupList();
lwin->TmpMsg(imsg);
artbuf = NewPtr(ARTBUF_SZ + ARTLINE_SZ);
response = server_init(server, imsg, 256);
if (response < 0) {
errmsg(2,"Couldn't connect to %s news server, try again later.",server);
exit(1);
}
lwin->TmpMsg(imsg);
response = atoi(imsg);
if (response != OK_NOPOST & response != OK_CANPOST)
exit(1);
/* get and process active list */
getactive();
addnew();
qsort(grps, ngrps, sizeof(grp_t), act_compar);
/* get current list */
getcur();
/* initial group list */
lwin->ReDo();
}
getactive() {
char ser_line[256];
grp_t *gp, *gp1;
char *txtp;
char *s;
long n;
/*
* allocate area for active list. For convenience this list is forced to
* to be a contiguous array rather than a linked list.
*/
ngrps = last_ngrps;
txtsize = last_txtsize;
if ((grps = (grp_t *)NewPtr((long)ngrps * sizeof(grp_t))) == NULL) {
errmsg(3,"no mem");
exit(1);
}
if ((txt = NewPtr(txtsize)) == NULL) {
errmsg(3,"no mem");
exit(1);
}
/* open the active file */
put_server("LIST\r\n"); /* tell server we want the active file */
(void) get_server(ser_line, sizeof(ser_line));
if (*ser_line != CHAR_OK) { /* and then see if that's ok */
errmsg(3,"Can't get active file from server: %s", ser_line);
exit(1);
}
gp = grps;
txtp = txt;
while (get_server(ser_line, sizeof(ser_line)) >= 0) {
if (ser_line[0] == '.') /* no more */
break;
if (gp >= &grps[ngrps]) {
n = gp - grps;
ngrps += 20;
grps = (grp_t *)Xrealloc((Ptr)grps, ngrps * sizeof(grp_t));
gp = &grps[n];
}
s = strchr(ser_line, ' ');
*s++ = '\0';
/* save group name */
if (txtp + (s-ser_line) >= &txt[txtsize]) {
n = txtp - txt;
txtsize += 1024;
txtp = txt; /* remember old */
txt = Xrealloc(txt, txtsize);
/* update all text pointers */
for (gp1=grps; gp1 <= gp; gp1++)
gp1->gname = &txt[gp1->gname - txtp];
txtp = &txt[n];
}
gp->gname = txtp;
txtp += (s-ser_line);
strcpy(gp->gname, ser_line);
gp->last = strtol(s,0,10);
s = strchr(s, ' ');
gp->first = strtol(s+1,0,10);
gp->unread = (gp->last == gp->first) ? 0 : gp->last - gp->first + 1;
gp->bm = NULL;
gp->art_head = NULL;
gp->flags = 0;
gp->subindex = 0;
gp->next = NULL;
gp++;
}
/* new sizes */
txtsize = ((long)(txtp - txt) + (1024-1)) & ~(1024-1); /* round up to next K */
ngrps = gp - grps;
}
/*
* subscribe to new groups
*/
addnew() {
grp_t *gp;
int i, j;
/*
* don't ask if there are too many
*/
if (ngrps - last_ngrps > 10) {
errmsg(1,"%ld new groups. Add from main menu.",ngrps - last_ngrps);
return;
}
for (i=last_ngrps; i<ngrps; i++) {
gp = &grps[i];
if (askYesNo("Subscribe to new group '%s' ?", gp->gname)) {
gp->flags = G_SUB;
gp->subindex = 32000; /* a big number */
}
}
}
/*
* read first line.
* #config: host nnn mmm
*/
getconfig() {
char line[256];
FILE *fs;
long n,m;
if ((fs=fopen(newsrc,"r"))!=NULL) {
if (fgets(line,256,fs)!=NULL)
if (strncmp("#config: ", line, 9) == 0)
if (sscanf(&line[9], "%s %ld %ld", server, &n, &m) == 3) {
last_ngrps = n;
last_txtsize = m;
}
fclose(fs);
}
/* enforce some arbitrary limits */
if (last_ngrps < 100) last_ngrps = 100;
if (last_ngrps > 5000) last_ngrps = 100;
if (last_txtsize < 8192) last_txtsize = 8192;
if (last_txtsize > 64000) last_txtsize = 8192;
/* get server name */
if (server[0] == '\0') {
DialogPtr dptr;
int item, type;
Handle ih;
Rect r;
dptr = GetNewDialog(dlogConfig, 0L, -1L);
ModalDialog(0L, &item);
if (item == 2) /* cancel */
ExitToShell();
GetDItem(dptr, 3, &type, &ih, &r);
GetIText(ih, server);
PtoCstr(server);
DisposDialog(dptr);
}
}
/*
* read newsrc file -- currently subscribed groups
*/
getcur() {
FILE *fs;
char group[256];
char *p;
grp_t *gp, *pgp, *findgrp();
int i;
Boolean active;
/* get master group list */
if ((fs=fopen(newsrc,"r"))==NULL) {
errmsg(2,"No %s found",newsrc);
return;
}
top:
while (fgets(group,256,fs)!=NULL) {
p = group;
if (*p == '#')
continue;
while (*p!='!' && *p!=':')
if (*p++ == '\0') {
errmsg(1,"syntax error: %s",group);
goto top;
}
active = (*p == ':');
*p = '\0';
if ((gp = findgrp(group)) == NULL) {
errmsg(1,"bogus group: %s",group);
continue;
}
/* set bits for all previously read articles */
gp->bm = bmalloc(bmTOTAL(gp));
gp->unread = asciiToBits(gp, p+1);
if (active) {
gp->flags = G_SUB;
gp->subindex = nsgrps++;
}
else gp->flags = G_INACT;
}
fclose(fs);
/* make subscribed links */
nsgrps = 0;
for (i=0; i<ngrps; i++)
if (grps[i].flags & G_SUB)
add_sgrp(&grps[i]);
}
/*
* add a group to suscribed list
*/
add_sgrp(grp_t *addgp)
{
grp_t *gp, *pgp;
if (grp_head == NULL)
grp_head = addgp;
else { /* find ordered position in list */
for (gp=pgp=grp_head; ; pgp=gp,gp=gp->next)
if (gp==NULL || gp->subindex > addgp->subindex) {
if (gp && pgp == grp_head)
grp_head = addgp;
else
pgp->next = addgp;
addgp->next = gp;
break;
}
}
nsgrps++;
}
/*
* remove a group from suscribed list
*/
rm_sgrp(grp_t *rmgp)
{
grp_t *gp, *pgp;
for (gp=pgp=grp_head; gp; pgp=gp,gp=gp->next)
if (gp == rmgp) {
if (gp == grp_head)
grp_head = gp->next;
else
pgp->next = gp->next;
break;
}
nsgrps--;
}
/*
* convert strings of form: 1-45,47,49,52-120
* to a bitmap
*/
asciiToBits(grp_t *gp, char *numlist)
{
int cnt;
long n=0, m, i;
char *p = numlist, *p1;
cnt = bmTOTAL(gp);
while (*p) {
while (isspace(*p)) p++;
p1 = p;
while (isdigit(*p)) p++;
if (p == p1) {
bad: errmsg(2,"bad number list");
return 0;
}
switch(*p) {
case '-':
n = strtol(p1,0,10);
break;
case ',':
case '\r':
case '\n':
m = strtol(p1,0,10);
if (m < gp->first) { /* not in current article set */
n = 0;
break;
}
if (n == 0) /* single number */
n = m;
else if (n < gp->first)
n = gp->first;
i = m-n + 1;
if ((cnt -= i) < 0)
goto bad;
bfset(gp->bm, n - gp->first, i);
n = 0;
break;
default:
goto bad;
}
p++;
}
return cnt;
}
grp_t *
findgrp(gname)
char *gname;
{
grp_t *gp;
int c;
for (gp=grps; gp < &grps[ngrps]; gp++)
if (strcmp(gp->gname, gname) == 0)
return gp;
return NULL;
}
art_t *art_tail; /* temporary, for building article list */
/* add article */
addart(grp_t *gp, long n, char *txt)
{
char *s;
art_t *ap;
int re=0;
s = txt;
/* make subject search string */
s = strchr(s,' ');
while (isspace(*s)) s++;
if ((s[0]|040)=='r' && (s[1]|040)=='e' && s[2]==':') {
s += 3;
re++;
}
while (isspace(*s)) s++;
/*
* look for previous thread with same subject line. A 40 character match
* is considered close enough.
*/
for (ap=gp->art_head; ap; ap=ap->next)
if (strncmp(s, ap->title, 40) == 0) { /* found a thread */
if ((ap->nthread % THREADINC) == 0)
ap->thread = (unsigned int *)Xrealloc((Ptr)ap->thread,
(ap->nthread + THREADINC) * sizeof(unsigned int *));
ap->thread[ap->nthread++] = n;
return;
}
/* new one */
ap = (art_t *)NewPtr(sizeof(art_t));
ap->title = savestr(s);
ap->re = re;
ap->thread = (unsigned int *)NewPtr(THREADINC * sizeof(unsigned int *));
ap->nthread = 1;
ap->tindex = 0;
ap->thread[0] = n;
ap->gp = gp;
ap->refcnt = 0;
if (gp->art_head == NULL) { /* first one */
gp->art_head = ap;
ap->prev = NULL;
}
else {
ap->prev = art_tail;
art_tail->next = ap;
}
art_tail = ap;
ap->next = NULL;
}
/*
* return next article, remove if 'zap'. update parent display window if 'modline'.
*/
art_t *
next_article(CArticleList *alist, art_t *ap, Boolean zap, Boolean modline)
{
unsigned int i, cnt;
art_t *nap;
ap->refcnt--;
if (ap->thread == NULL) { /* previously unlinked */
if (ap->refcnt == 0)
DisposPtr(ap);
return (NULL);
}
/* mark article(s) as read */
cnt = zap ? ap->nthread - ap->tindex : 1;
for (i=0; i<cnt; i++, ap->tindex++)
Bset(ap->gp->bm, ap->thread[ap->tindex] - ap->gp->first);
ap->gp->unread -= cnt;
ap->re = 1; /* can nolonger be original thread */
if (!zap && ap->tindex < ap->nthread) { /* more threads this article */
if (modline)
alist->ModLine(ap, 1);
return (ap);
}
/* find next article with unread threads */
for (nap=ap->next; nap; nap=nap->next)
if (nap->tindex + nap->refcnt < nap->nthread)
break;
if (modline)
alist->ModLine(ap, 0);
del_article(ap);
return (nap);
}
/* delete single article */
del_article(art_t *ap)
{
DisposPtr(ap->title);
DisposPtr(ap->thread);
if (ap->gp->art_head == ap) { /* remove at head of list */
ap->gp->art_head = ap->next;
if (ap->next)
ap->next->prev = NULL;
}
else {
ap->prev->next = ap->next;
if (ap->next)
ap->next->prev = ap->prev;
}
if (ap->refcnt == 0)
DisposPtr(ap);
else {
ap->thread = NULL; /* mark it unlinked */
ap->next = ap->prev = NULL;
}
}
/* delete entire article list */
dispose_artlist(grp_t *gp)
{
art_t *ap, *nap;
/* remove previous */
if (gp->art_head) {
for (ap=gp->art_head; ap; ap=nap) {
DisposPtr(ap->title);
nap = ap->next;
DisposPtr(ap->thread);
if (ap->refcnt == 0)
DisposPtr(ap);
else {
ap->thread = NULL; /* mark it unlinked */
ap->next = ap->prev = NULL;
}
}
gp->art_head = NULL;
}
}
/*
* init a group's article list
*/
igroup(grp_t *gp)
{
char msg[256], *s;
art_t *ap, *nap;
long first, last, n, cnt;
if (gp->art_head) /* list already open */
return;
SetCursor(*gWatchCursor);
if (gp->bm == NULL) /* lazy allocation for new groups and master list */
gp->bm = bmalloc(bmTOTAL(gp));
first = gp->first + bfffc(gp->bm, 0, bmTOTAL(gp));
last = gp->last;
sprintf(msg, "GROUP %s\r\n", gp->gname);
put_server(msg);
if (get_server(msg, sizeof(msg)) < 0) {
errmsg(3,"rrn: Unexpected close of server socket.");
exit(1);
}
if (*msg != CHAR_OK) {
if (atoi(msg) != ERR_NOGROUP)
errmsg(2,"rrn: server response to GROUP %s:%s",
gp->gname, msg);
return (-1);
}
/* get subjects */
sprintf(msg,"XHDR subject %ld-%ld\r\n",first,last);
put_server(msg);
if (get_server(msg, sizeof(msg)) < 0) {
errmsg(3,"rrn: Unexpected close of server socket.");
exit(1);
}
if (*msg == CHAR_FATAL)
return -1;
cnt = 0;
while (get_server(msg, sizeof(msg)) >= 0) {
if (*msg == '.')
break;
/* parse article number */
s = msg;
while (isspace(*s)) s++;
n = strtol(s,0,10);
s = strchr(s,' ');
if (n < first || n > last) /* out of range */
continue;
if (Btst(gp->bm, n - gp->first)) /* already read */
continue;
/* missing articles */
if (n != first)
bfset(gp->bm, first - gp->first, n-first);
addart(gp, n, s);
first = n+1;
cnt++;
}
gp->unread = cnt;
}
readart(art_t *ap)
{
char msg[256], *p;
int n;
Boolean trunc = FALSE;
unsigned art = ap->thread[ap->tindex];
SetCursor(*gWatchCursor);
sprintf(msg, "ARTICLE %u\r\n", art);
put_server(msg); /* ask the server for the article */
if (get_server(msg, sizeof(msg)) < 0) {
errmsg(3,"rrn: Unexpected close of server socket.");
exit(1);
}
if (*msg != CHAR_OK) {
errmsg(1, "%s",msg);
return -1;
}
p = artbuf;
for (;;) {
if (get_server(p, (int)ARTLINE_SZ) < 0) {
errmsg(3,"rrn: Unexpected close of server socket.");
exit(1);
}
if (p[0] == '.' && p[1] == '\0')
break;
if (trunc)
continue;
n = strlen(p);
if (p+n >= &artbuf[ARTBUF_SZ]) {
errmsg(1,"article truncated");
trunc = TRUE;
continue;
}
p += n;
*p++ = '\r';
}
artlen = p - artbuf;
ap->refcnt++;
return 0;
}
save_newsrc() {
FILE *nf;
grp_t *gp;
/* create new newsrc */
if ((nf=fopen(newsrcnew,"w"))==NULL) {
errmsg(3,"Cannot create %s",newsrcnew); return; }
fprintf(nf,"#config: %s %ld %ld\n",server,ngrps,txtsize);
/* write subscribed groups */
for (gp=grp_head; gp; gp=gp->next) {
fprintf(nf, "%s: ",gp->gname);
putbitlist(nf, gp);
putc('\n', nf);
}
/* tack on any gone, but not forgotten groups */
for (gp=grps; gp < &grps[ngrps]; gp++) {
if (gp->flags & G_INACT) {
fprintf(nf, "%s! ",gp->gname);
putbitlist(nf, gp);
putc('\n', nf);
}
}
fclose(nf);
remove(newsrcold);
rename(newsrc, newsrcold);
rename(newsrcnew, newsrc);
}
putbitlist(FILE *nf, grp_t *gp)
{
long n, first, last;
if (gp->bm == NULL) { /* new group, never listed */
fprintf(nf, "1-%ld", Max(gp->first-1, 1));
return;
}
/* convert bitmap back to numeric form */
first = 1;
n = bfffc(gp->bm, 0, bmTOTAL(gp));
last = (n == -1) ? gp->last : gp->first + n - 1;
if (first == last)
fprintf(nf, "%ld", first);
else fprintf(nf, "%ld-%ld", first,last);
while (last != gp->last) {
if ((n = bfffs(gp->bm, n, bmTOTAL(gp))) == -1)
break;
first = gp->first + n;
n = bfffc(gp->bm, first-gp->first, bmTOTAL(gp));
last = (n == -1) ? gp->last : gp->first + n -1;
if (first == last)
fprintf(nf, ",%ld", first);
else fprintf(nf, ",%ld-%ld", first,last);
}
}
char *
Xrealloc(p, n)
char *p;
long n;
{
char *p1 = NewPtr(n);
BlockMove(p, p1, n);
DisposPtr(p);
return p1;
}
char *
savestr(s) char *s; {
char *p = NewPtr(strlen(s)+1);
strcpy(p, s);
return (p);
}