home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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);
- }
-