2 * Acme interface to nntpfs.
15 int lo; /* next message to look at in dir */
16 int hi; /* current hi message in dir */
17 char *dir = "/mnt/news";
21 typedef struct Article Article;
39 cistrncmp(char *a, char *b, int n)
42 if(tolower(*a++) != tolower(*b++))
49 cistrcmp(char *a, char *b)
52 if(tolower(*a) != tolower(*b++))
74 if((d = dirstat(dir)) == nil)
95 char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", };
97 "Jan", "Feb", "Mar", "Apr",
98 "May", "Jun", "Jul", "Aug",
99 "Sep", "Oct", "Nov", "Dec",
105 char *f[10], *m, *t, *wd, tmp[40];
106 int d, i, j, nf, hh, mm;
108 nf = tokenize(s, f, nelem(f));
116 if(cistrncmp(day[j], f[i], 3)==0)
119 if(cistrncmp(mon[j], f[i], 3)==0)
122 if(1 <= j && j <= 31 && d != 0)
124 if(strchr(f[i], ':'))
128 if(d==0 || wd==nil || m==nil || t==nil)
131 hh = strtol(t, 0, 10);
132 mm = strtol(strchr(t, ':')+1, 0, 10);
133 sprint(tmp, "%s %d %s %d:%.2d", wd, d, m, hh, mm);
138 msgheadline(Biobuf *bin, int n, Biobuf *bout)
148 while(p = Brdline(bin, '\n')){
149 p[Blinelen(bin)-1] = '\0';
150 if((q = strchr(p, ':')) == nil)
153 if(cistrcmp(p, "from")==0)
154 from = fixfrom(skipwhite(q));
155 else if(cistrcmp(p, "subject")==0)
156 subject = estrdup(skipwhite(q));
157 else if(cistrcmp(p, "date")==0)
158 date = fixdate(skipwhite(q));
161 Bprint(bout, "%d/\t%s", n, from ? from : "");
163 Bprint(bout, "\t%s", date);
165 Bprint(bout, "\n\t%s", subject);
174 * Write the headers for at most nshow messages to b,
175 * starting with hi and working down to lo.
176 * Return number of first message not scanned.
179 adddir(Biobuf *body, int hi, int lo, int nshow)
181 char *p, *q, tmp[40];
186 for(i=hi; i>=lo && n<nshow; i--){
187 sprint(tmp, "%d", i);
188 p = estrstrdup(dir, tmp);
189 if(access(p, OREAD) < 0){
193 q = estrstrdup(p, "/header");
199 msgheadline(b, i, body);
207 * Show the first nshow messages in the window.
208 * This depends on nntpfs presenting contiguously
209 * numbered directories, and on the qid version being
210 * the topmost numbered directory.
219 w->data = winopenfile(w, "data");
221 fprint(w->ctl, "dirty\n");
223 winopenbody(w, OWRITE);
224 lo = adddir(w->body, hi, 0, nshow);
228 /* return 1 if handled, 0 otherwise */
230 iscmd(char *s, char *cmd)
235 return strncmp(s, cmd, len)==0 && (s[len]=='\0' || s[len]==' ' || s[len]=='\t' || s[len]=='\n');
239 skip(char *s, char *cmd)
242 while(*s==' ' || *s=='\t' || *s=='\n')
254 m->next->prev = m->prev;
256 m->prev->next = m->next;
257 m->next = m->prev = nil;
261 int fillmesgwindow(int, Article*);
262 Article *newpost(void);
263 void replywindow(Article*);
264 void mesgpost(Article*);
267 * Depends on d.qid.vers being highest numbered message in dir.
270 acmetimer(Article *m, Window *w)
275 assert(m==nil && w==root);
277 if((d = dirstat(dir))==nil | hi==d->qid.vers){
283 w->data = winopenfile(w, "data");
284 if(winsetaddr(w, "0", 0))
285 write(w->data, "", 0);
287 b = emalloc(sizeof(*b));
288 Binit(b, w->data, OWRITE);
289 adddir(b, d->qid.vers, hi+1, d->qid.vers);
294 winselect(w, "0,.", 0);
298 acmeload(Article*, Window*, char *s)
302 //fprint(2, "load %s\n", s);
306 /* skip directory name */
307 if(strncmp(s, dir, strlen(dir))==0)
309 nopen += mesgopen(s);
310 if((s = strchr(s, '\n')) == nil)
318 acmecmd(Article *m, Window *w, char *s)
323 //fprint(2, "cmd %s\n", s);
328 if(m == nil){ /* don't close dir until messages close */
330 ctlprint(mlist->w->ctl, "show\n");
342 if(m==nil && iscmd(s, "More")){
348 w->data = winopenfile(w, "data");
349 winsetaddr(w, "$", 1);
351 fprint(w->ctl, "dirty\n");
353 b = emalloc(sizeof(*b));
354 Binit(b, w->data, OWRITE);
355 lo = adddir(b, lo, 0, nshow);
359 winsetaddr(w, ".,", 0);
361 if(m!=nil && !m->ispost && iscmd(s, "Headers")){
362 m->headers = !m->headers;
363 fillmesgwindow(-1, m);
366 if(iscmd(s, "Newpost")){
368 winopenbody(m->w, OWRITE);
369 Bprint(m->w->body, "%s\nsubject: \n\n", group);
371 winselect(m->w, "$", 0);
374 if(m!=nil && !m->ispost && iscmd(s, "Reply")){
378 // if(m!=nil && iscmd(s, "Replymail")){
379 // fprint(2, "no replymail yet\n");
382 if(iscmd(s, "Post")){
390 acmeevent(Article *m, Window *w, Event *e)
398 switch(e->c1){ /* origin of action */
401 fprint(2, "unknown message %c%c\n", e->c1, e->c2);
404 case 'T': /* bogus timer event! */
408 case 'F': /* generated by our actions; ignore */
411 case 'E': /* write to body or tag; can't affect us */
414 case 'K': /* type away; we don't care */
415 if(m && (e->c2 == 'I' || e->c2 == 'D')){
418 wintagwrite(w, "Post ", 5);
424 case 'M': /* mouse event */
425 switch(e->c2){ /* type of action */
426 case 'x': /* mouse: button 2 in tag */
427 case 'X': /* mouse: button 2 in body */
431 if(e->flag & 2){ /* null string with non-null expansion */
432 e2 = recvp(w->cevent);
436 if(e->flag & 8){ /* chorded argument */
437 ea = recvp(w->cevent); /* argument */
439 recvp(w->cevent); /* ignore origin */
443 /* append chorded arguments */
445 t = emalloc(strlen(s)+1+na+1);
446 sprint(t, "%s %s", s, ea->b);
449 /* if it's a known command, do it */
450 /* if it's a long message, it can't be for us anyway */
451 // DPRINT(2, "exec: %s\n", s);
452 if(!acmecmd(m, w, s)) /* send it back */
458 case 'l': /* mouse: button 3 in tag */
459 case 'L': /* mouse: button 3 in body */
463 e2 = recvp(w->cevent);
467 if(eq->q1>eq->q0 && eq->nb==0){
468 buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
469 winread(w, eq->q0, eq->q1, buf);
472 if(!acmeload(m, w, s))
476 case 'i': /* mouse: text inserted in tag */
477 case 'd': /* mouse: text deleted from tag */
480 case 'I': /* mouse: text inserted in body */
481 case 'D': /* mouse: text deleted from body */
487 wintagwrite(w, "Post ", 5);
505 while(e = recvp(w->cevent))
506 acmeevent(nil, w, e);
518 while(!m->dead && (e = recvp(m->w->cevent)))
519 acmeevent(m, m->w, e);
521 //fprint(2, "msg %p exits\n", m);
529 Xref: news.research.att.com comp.os.plan9:7360
530 Newsgroups: comp.os.plan9
531 Path: news.research.att.com!batch0!uunet!ffx.uu.net!finch!news.mindspring.net!newsfeed.mathworks.com!fu-berlin.de!server1.netnews.ja.net!hgmp.mrc.ac.uk!pegasus.csx.cam.ac.uk!bath.ac.uk!ccsdhd
532 From: Stephen Adam <saadam@bigpond.com>
533 Subject: Future of Plan9
534 Approved: plan9mod@bath.ac.uk
535 X-Newsreader: Microsoft Outlook Express 5.00.2014.211
536 X-Mimeole: Produced By Microsoft MimeOLE V5.00.2014.211
537 Sender: ccsdhd@bath.ac.uk (Dennis Davis)
538 Nntp-Posting-Date: Wed, 13 Dec 2000 21:28:45 EST
539 NNTP-Posting-Host: 203.54.121.233
540 Organization: Telstra BigPond Internet Services (http://www.bigpond.com)
541 X-Date: Wed, 13 Dec 2000 20:43:37 +1000
543 Message-ID: <xbIZ5.157945$e5.114349@newsfeeds.bigpond.com>
544 References: <95pghu$3lf$1@news.fas.harvard.edu> <95ph36$3m9$1@news.fas.harvard.edu> <slrn980iic.u5q.mperrin@hcs.harvard.edu> <95png6$4ln$1@news.fas.harvard.edu> <95poqg$4rq$1@news.fas.harvard.edu> <slrn980vh8.2gb.myLastName@is07.fas.harvard.edu> <95q40h$66c$2@news.fas.harvard.edu> <95qjhu$8ke$1@news.fas.harvard.edu> <95riue$bu2$1@news.fas.harvard.edu> <95rnar$cbu$1@news.fas.harvard.edu>
545 X-Msmail-Priority: Normal
546 X-Trace: newsfeeds.bigpond.com 976703325 203.54.121.233 (Wed, 13 Dec 2000 21:28:45 EST)
548 Date: Wed, 13 Dec 2000 10:49:50 GMT
569 fillmesgwindow(int fd, Article *m)
573 int i, inhdr, copy, xfd;
578 sprint(tmp, "%d/article", m->n);
579 p = estrstrdup(dir, tmp);
580 if((xfd = open(p, OREAD)) < 0){
590 w->data = winopenfile(w, "data");
591 if(winsetaddr(w, ",", 0))
592 write(w->data, "", 0);
594 winopenbody(m->w, OWRITE);
595 b = emalloc(sizeof(*b));
600 while(p = Brdline(b, '\n')){
603 if(inhdr && !isspace(p[0])){
606 if(cistrncmp(p, "from:", 5)==0){
607 p[Blinelen(b)-1] = '\0';
608 p = fixfrom(skip(p, "from:"));
609 Bprint(m->w->body, "From: %s\n", p);
614 for(i=0; i<nelem(skipheader); i++)
615 if(cistrncmp(p, skipheader[i], strlen(skipheader[i]))==0)
620 Bwrite(m->w->body, p, Blinelen(b));
637 m = emalloc(sizeof *m);
638 sprint(tmp, "Post%d", ++nnew);
639 p = estrstrdup(dir, tmp);
642 proccreate(wineventproc, m->w, STACK);
644 wintagwrite(m->w, "Post ", 5);
647 threadcreate(mesgthread, m, STACK);
658 replywindow(Article *m)
661 char *p, *ep, *q, tmp[40];
665 sprint(tmp, "%d/article", m->n);
666 p = estrstrdup(dir, tmp);
667 if((fd = open(p, OREAD)) < 0){
674 winopenbody(reply->w, OWRITE);
675 b = emalloc(sizeof(*b));
678 while(p = Brdline(b, '\n')){
684 if(cistrncmp(p, "newsgroups:", 11)==0){
685 for(q=p+11; *q!='\n'; q++)
689 }else if(cistrncmp(p, "subject:", 8)==0){
690 if(!strstr(p, " Re:") && !strstr(p, " RE:") && !strstr(p, " re:")){
691 p = skip(p, "subject:");
693 Bprint(reply->w->body, "Subject: Re: %s\n", p);
696 }else if(cistrncmp(p, "message-id:", 11)==0){
697 Bprint(reply->w->body, "References: ");
703 Bwrite(reply->w->body, p, ep-p);
708 Bprint(reply->w->body, "\n");
710 winselect(reply->w, "$", 0);
714 skipbl(char *s, char *e)
717 if(*s!=' ' && *s!='\t' && *s!=',')
725 findbl(char *s, char *e)
728 if(*s==' ' || *s=='\t' || *s==',')
736 * comma-separate possibly blank-separated strings in line; e points before newline
739 commas(char *s, char *e)
743 /* may have initial blanks */
750 if(t == e) /* no more words */
763 int isfirst, ishdr, havegroup, havefrom;
765 p = estrstrdup(dir, "post");
766 if((b = Bopen(p, OWRITE)) == nil){
767 fprint(2, "cannot open %s: %r\n", p);
773 winopenbody(m->w, OREAD);
776 havegroup = havefrom = 0;
777 while(p = Brdline(m->w->body, '\n')){
778 ep = p+Blinelen(m->w->body);
779 if(ishdr && p+1==ep){
781 Bprint(b, "Newsgroups: %s\n", group);
783 Bprint(b, "From: %s\n", from);
788 if(isfirst && strchr(p, ':')==0){ /* group list */
790 Bprint(b, "newsgroups: %s\n", p);
795 if(cistrncmp(p, "newsgroup:", 10)==0){
796 commas(skip(p, "newsgroup:"), ep);
797 Bprint(b, "newsgroups: %s\n", skip(p, "newsgroup:"));
801 if(cistrncmp(p, "newsgroups:", 11)==0){
802 commas(skip(p, "newsgroups:"), ep);
803 Bprint(b, "newsgroups: %s\n", skip(p, "newsgroups:"));
807 if(cistrncmp(p, "from:", 5)==0)
815 if(write(Bfildes(b), "", 0) == 0)
818 fprint(2, "post: %r\n");
833 for(m=mlist; m; m=m->next){
835 ctlprint(m->w->ctl, "show\n");
840 sprint(tmp, "%d/article", n);
841 p = estrstrdup(dir, tmp);
842 if((fd = open(p, OREAD)) < 0){
847 m = emalloc(sizeof(*m));
850 proccreate(wineventproc, m->w, STACK);
851 p[strlen(p)-strlen("article")] = '\0';
854 wintagwrite(m->w, "Reply ", 6);
855 wintagwrite(m->w, "Headers ", 8);
863 threadcreate(mesgthread, m, STACK);
865 fillmesgwindow(fd, m);
874 fprint(2, "usage: News [-d /mnt/news] comp.os.plan9\n");
884 memset(&e, 0, sizeof e);
890 sendp(w->cevent, &e);
904 p = estrstrstrdup("/usr/", u, "/lib/newsfrom");
908 p = Brdline(b, '\n');
910 p[Blinelen(b)-1] = '\0';
918 p = estrstrstrdup("/mail/box/", u, "/headers");
922 while(p = Brdline(b, '\n')){
923 p[Blinelen(b)-1] = '\0';
924 if(cistrncmp(p, "from:", 5)==0){
925 p = estrdup(skip(p, "from:"));
937 threadmain(int argc, char **argv)
948 dir = EARGF(usage());
960 group = estrdup(argv[0]); /* someone will be cute */
961 while(q=strchr(group, '/'))
964 p = estrdup(argv[0]);
965 while(q=strchr(p, '.'))
967 p = estrstrstrdup(dir, "/", p);
970 if((d = dirstat(p)) == nil){ /* maybe it is a new group */
971 if((d = dirstat(dir)) == nil){
972 fprint(2, "dirstat(%s) fails: %r\n", dir);
975 if((d->mode&DMDIR)==0){
976 fprint(2, "%s not a directory\n", dir);
980 if((d = dirstat(p)) == nil){
981 fprint(2, "stat %s: %r\n", p);
985 if((d->mode&DMDIR)==0){
986 fprint(2, "%s not a directory\n", dir);
990 dir = estrstrdup(p, "/");
992 q = estrstrdup(dir, "post");
993 canpost = access(q, AWRITE)==0;
997 proccreate(wineventproc, w, STACK);
998 proccreate(timerproc, w, STACK);
1002 wintagwrite(w, "Newpost ", 8);
1003 wintagwrite(w, "More ", 5);
1005 threadcreate(dirthread, w, STACK);