Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_fixcpp/acme/news/src/news.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * Acme interface to nntpfs.
3
 */
4
#include <u.h>
5
#include <libc.h>
6
#include <bio.h>
7
#include <thread.h>
8
#include "win.h"
9
#include <ctype.h>
10
 
11
int canpost;
12
int debug;
13
int nshow = 20;
14
 
15
int lo;	/* next message to look at in dir */
16
int hi;	/* current hi message in dir */
17
char *dir = "/mnt/news";
18
char *group;
19
char *from;
20
 
21
typedef struct Article Article;
22
struct Article {
23
	Ref;
24
	Article *prev;
25
	Article *next;
26
	Window *w;
27
	int n;
28
	int dead;
29
	int dirtied;
30
	int sayspost;
31
	int headers;
32
	int ispost;
33
};
34
 
35
Article *mlist;
36
Window *root;
37
 
38
int
39
cistrncmp(char *a, char *b, int n)
40
{
41
	while(n-- > 0){
42
		if(tolower(*a++) != tolower(*b++))
43
			return -1;
44
	}
45
	return 0;
46
}
47
 
48
int
49
cistrcmp(char *a, char *b)
50
{
51
	for(;;){
52
		if(tolower(*a) != tolower(*b++))
53
			return -1;
54
		if(*a++ == 0)
55
			break;
56
	}
57
	return 0;
58
}
59
 
60
char*
61
skipwhite(char *p)
62
{
63
	while(isspace(*p))
64
		p++;
65
	return p;
66
}
67
 
68
int
69
gethi(void)
70
{
71
	Dir *d;
72
	int hi;
73
 
74
	if((d = dirstat(dir)) == nil)
75
		return -1;
76
	hi = d->qid.vers;
77
	free(d);
78
	return hi;
79
}
80
 
81
char*
82
fixfrom(char *s)
83
{
84
	char *r, *w;
85
 
86
	s = estrdup(s);
87
	/* remove quotes */
88
	for(r=w=s; *r; r++)
89
		if(*r != '"')
90
			*w++ = *r;
91
	*w = '\0';
92
	return s;
93
}
94
 
95
char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", };
96
char *mon[] = {
97
	"Jan", "Feb", "Mar", "Apr",
98
	"May", "Jun", "Jul", "Aug",
99
	"Sep", "Oct", "Nov", "Dec",
100
};
101
 
102
char*
103
fixdate(char *s)
104
{
105
	char *f[10], *m, *t, *wd, tmp[40];
106
	int d, i, j, nf, hh, mm;
107
 
108
	nf = tokenize(s, f, nelem(f));
109
 
110
	wd = nil;
111
	d = 0;
112
	m = nil;
113
	t = nil;
114
	for(i=0; i<nf; i++){
115
		for(j=0; j<7; j++)
116
			if(cistrncmp(day[j], f[i], 3)==0)
117
				wd = day[j];
118
		for(j=0; j<12; j++)
119
			if(cistrncmp(mon[j], f[i], 3)==0)
120
				m = mon[j];
121
		j = atoi(f[i]);
122
		if(1 <= j && j <= 31 && d != 0)
123
			d = j;
124
		if(strchr(f[i], ':'))
125
			t = f[i];
126
	}
127
 
128
	if(d==0 || wd==nil || m==nil || t==nil)
129
		return nil;
130
 
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);
134
	return estrdup(tmp);
135
}
136
 
137
void
138
msgheadline(Biobuf *bin, int n, Biobuf *bout)
139
{
140
	char *p, *q;
141
	char *date;
142
	char *from;
143
	char *subject;
144
 
145
	date = nil;
146
	from = nil;
147
	subject = nil;
148
	while(p = Brdline(bin, '\n')){
149
		p[Blinelen(bin)-1] = '\0';
150
		if((q = strchr(p, ':')) == nil)
151
			continue;
152
		*q++ = '\0';
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));
159
	}
160
 
161
	Bprint(bout, "%d/\t%s", n, from ? from : "");
162
	if(date)
163
		Bprint(bout, "\t%s", date);
164
	if(subject)
165
		Bprint(bout, "\n\t%s", subject);
166
	Bprint(bout, "\n");
167
 
168
	free(date);
169
	free(from);
170
	free(subject);
171
}
172
 
173
/*
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.
177
 */
178
int
179
adddir(Biobuf *body, int hi, int lo, int nshow)
180
{
181
	char *p, *q, tmp[40];
182
	int i, n;
183
	Biobuf *b;
184
 
185
	n = 0;
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){
190
			free(p);
191
			break;
192
		}
193
		q = estrstrdup(p, "/header");
194
		free(p);
195
		b = Bopen(q, OREAD);
196
		free(q);
197
		if(b == nil)
198
			continue;
199
		msgheadline(b, i, body);
200
		Bterm(b);
201
		n++;
202
	}
203
	return i;
204
}
205
 
206
/* 
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.
211
 */
212
void
213
dirwindow(Window *w)
214
{
215
	if((hi=gethi()) < 0)
216
		return;
217
 
218
	if(w->data < 0)
219
		w->data = winopenfile(w, "data");
220
 
221
	fprint(w->ctl, "dirty\n");
222
 
223
	winopenbody(w, OWRITE);
224
	lo = adddir(w->body, hi, 0, nshow);
225
	winclean(w);
226
}
227
 
228
/* return 1 if handled, 0 otherwise */
229
static int
230
iscmd(char *s, char *cmd)
231
{
232
	int len;
233
 
234
	len = strlen(cmd);
235
	return strncmp(s, cmd, len)==0 && (s[len]=='\0' || s[len]==' ' || s[len]=='\t' || s[len]=='\n');
236
}
237
 
238
static char*
239
skip(char *s, char *cmd)
240
{
241
	s += strlen(cmd);
242
	while(*s==' ' || *s=='\t' || *s=='\n')
243
		s++;
244
	return s;
245
}
246
 
247
void
248
unlink(Article *m)
249
{
250
	if(mlist == m)
251
		mlist = m->next;
252
 
253
	if(m->next)
254
		m->next->prev = m->prev;
255
	if(m->prev)
256
		m->prev->next = m->next;
257
	m->next = m->prev = nil;
258
}
259
 
260
int mesgopen(char*);
261
int fillmesgwindow(int, Article*);
262
Article *newpost(void);
263
void replywindow(Article*);
264
void mesgpost(Article*);
265
 
266
/*
267
 * Depends on d.qid.vers being highest numbered message in dir.
268
 */
269
void
270
acmetimer(Article *m, Window *w)
271
{
272
	Biobuf *b;
273
	Dir *d;
274
 
275
	assert(m==nil && w==root);
276
 
277
	if((d = dirstat(dir))==nil | hi==d->qid.vers){
278
		free(d);
279
		return;
280
	}
281
 
282
	if(w->data < 0)
283
		w->data = winopenfile(w, "data");
284
	if(winsetaddr(w, "0", 0))
285
		write(w->data, "", 0);
286
 
287
	b = emalloc(sizeof(*b));
288
	Binit(b, w->data, OWRITE);
289
	adddir(b, d->qid.vers, hi+1, d->qid.vers);
290
	hi = d->qid.vers;
291
	Bterm(b);
292
	free(b);
293
	free(d);
294
	winselect(w, "0,.", 0);
295
}
296
 
297
int
298
acmeload(Article*, Window*, char *s)
299
{
300
	int nopen;
301
 
302
//fprint(2, "load %s\n", s);
303
 
304
	nopen = 0;
305
	while(*s){
306
		/* skip directory name */
307
		if(strncmp(s, dir, strlen(dir))==0)
308
			s += strlen(dir);
309
		nopen += mesgopen(s);
310
		if((s = strchr(s, '\n')) == nil)
311
			break;
312
		s = skip(s, "");
313
	}
314
	return nopen;
315
}
316
 
317
int
318
acmecmd(Article *m, Window *w, char *s)
319
{
320
	int n;
321
	Biobuf *b;
322
 
323
//fprint(2, "cmd %s\n", s);
324
 
325
	s = skip(s, "");
326
 
327
	if(iscmd(s, "Del")){
328
		if(m == nil){	/* don't close dir until messages close */
329
			if(mlist != nil){
330
				ctlprint(mlist->w->ctl, "show\n");
331
				return 1;
332
			}
333
			if(windel(w, 0))
334
				threadexitsall(nil);
335
			return 1;
336
		}else{
337
			if(windel(w, 0))
338
				m->dead = 1;
339
			return 1;
340
		}
341
	}
342
	if(m==nil && iscmd(s, "More")){
343
		s = skip(s, "More");
344
		if(n = atoi(s))
345
			nshow = n;
346
 
347
		if(w->data < 0)
348
			w->data = winopenfile(w, "data");
349
		winsetaddr(w, "$", 1);
350
 
351
		fprint(w->ctl, "dirty\n");
352
 
353
		b = emalloc(sizeof(*b));
354
		Binit(b, w->data, OWRITE);
355
		lo = adddir(b, lo, 0, nshow);
356
		Bterm(b);
357
		free(b);		
358
		winclean(w);
359
		winsetaddr(w, ".,", 0);
360
	}
361
	if(m!=nil && !m->ispost && iscmd(s, "Headers")){
362
		m->headers = !m->headers;
363
		fillmesgwindow(-1, m);
364
		return 1;
365
	}
366
	if(iscmd(s, "Newpost")){
367
		m = newpost();
368
		winopenbody(m->w, OWRITE);
369
		Bprint(m->w->body, "%s\nsubject: \n\n", group);
370
		winclean(m->w);
371
		winselect(m->w, "$", 0);
372
		return 1;
373
	}
374
	if(m!=nil && !m->ispost && iscmd(s, "Reply")){
375
		replywindow(m);
376
		return 1;
377
	}
378
//	if(m!=nil && iscmd(s, "Replymail")){
379
//		fprint(2, "no replymail yet\n");
380
//		return 1;
381
//	}
382
	if(iscmd(s, "Post")){
383
		mesgpost(m);
384
		return 1;
385
	}
386
	return 0;
387
}
388
 
389
void
390
acmeevent(Article *m, Window *w, Event *e)
391
{
392
	Event *ea, *e2, *eq;
393
	char *s, *t, *buf;
394
	int na;
395
	//int n;
396
	//ulong q0, q1;
397
 
398
	switch(e->c1){	/* origin of action */
399
	default:
400
	Unknown:
401
		fprint(2, "unknown message %c%c\n", e->c1, e->c2);
402
		break;
403
 
404
	case 'T':	/* bogus timer event! */
405
		acmetimer(m, w);
406
		break;
407
 
408
	case 'F':	/* generated by our actions; ignore */
409
		break;
410
 
411
	case 'E':	/* write to body or tag; can't affect us */
412
		break;
413
 
414
	case 'K':	/* type away; we don't care */
415
		if(m && (e->c2 == 'I' || e->c2 == 'D')){
416
			m->dirtied = 1;
417
			if(!m->sayspost){
418
				wintagwrite(w, "Post ", 5);
419
				m->sayspost = 1;
420
			}
421
		}
422
		break;
423
 
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 */
428
			ea = nil;
429
			//e2 = nil;
430
			s = e->b;
431
			if(e->flag & 2){	/* null string with non-null expansion */
432
				e2 = recvp(w->cevent);
433
				if(e->nb==0)
434
					s = e2->b;
435
			}
436
			if(e->flag & 8){	/* chorded argument */
437
				ea = recvp(w->cevent);	/* argument */
438
				na = ea->nb;
439
				recvp(w->cevent);		/* ignore origin */
440
			}else
441
				na = 0;
442
 
443
			/* append chorded arguments */
444
			if(na){
445
				t = emalloc(strlen(s)+1+na+1);
446
				sprint(t, "%s %s", s, ea->b);
447
				s = t;
448
			}
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 */
453
				winwriteevent(w, e);
454
			if(na)
455
				free(s);
456
			break;
457
 
458
		case 'l':	/* mouse: button 3 in tag */
459
		case 'L':	/* mouse: button 3 in body */
460
			//buf = nil;
461
			eq = e;
462
			if(e->flag & 2){
463
				e2 = recvp(w->cevent);
464
				eq = e2;
465
			}
466
			s = eq->b;
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);
470
				s = buf;
471
			}
472
			if(!acmeload(m, w, s))
473
				winwriteevent(w, e);
474
			break;
475
 
476
		case 'i':	/* mouse: text inserted in tag */
477
		case 'd':	/* mouse: text deleted from tag */
478
			break;
479
 
480
		case 'I':	/* mouse: text inserted in body */
481
		case 'D':	/* mouse: text deleted from body */
482
			if(m == nil)
483
				break;
484
 
485
			m->dirtied = 1;
486
			if(!m->sayspost){
487
				wintagwrite(w, "Post ", 5);
488
				m->sayspost = 1;
489
			}
490
			break;
491
 
492
		default:
493
			goto Unknown;
494
		}
495
	}
496
}
497
 
498
void
499
dirthread(void *v)
500
{
501
	Event *e;
502
	Window *w;
503
 
504
	w = v;
505
	while(e = recvp(w->cevent))
506
		acmeevent(nil, w, e);
507
 
508
	threadexitsall(nil);
509
}
510
 
511
void
512
mesgthread(void *v)
513
{
514
	Event *e;
515
	Article *m;
516
 
517
	m = v;
518
	while(!m->dead && (e = recvp(m->w->cevent)))
519
		acmeevent(m, m->w, e);
520
 
521
//fprint(2, "msg %p exits\n", m);
522
	unlink(m);
523
	free(m->w);
524
	free(m);
525
	threadexits(nil);
526
}
527
 
528
/*
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
542
Lines: 12
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)
547
X-Priority: 3
548
Date: Wed, 13 Dec 2000 10:49:50 GMT
549
*/
550
 
551
char *skipheader[] = 
552
{
553
	"x-",
554
	"path:",
555
	"xref:",
556
	"approved:",
557
	"sender:",
558
	"nntp-",
559
	"organization:",
560
	"lines:",
561
	"message-id:",
562
	"references:",
563
	"reply-to:",
564
	"mime-",
565
	"content-",
566
};
567
 
568
int
569
fillmesgwindow(int fd, Article *m)
570
{
571
	Biobuf *b;
572
	char *p, tmp[40];
573
	int i, inhdr, copy, xfd;
574
	Window *w;
575
 
576
	xfd = -1;
577
	if(fd == -1){
578
		sprint(tmp, "%d/article", m->n);
579
		p = estrstrdup(dir, tmp);
580
		if((xfd = open(p, OREAD)) < 0){
581
			free(p);	
582
			return 0;
583
		}
584
		free(p);
585
		fd = xfd;
586
	}
587
 
588
	w = m->w;
589
	if(w->data < 0)
590
		w->data = winopenfile(w, "data");
591
	if(winsetaddr(w, ",", 0))
592
		write(w->data, "", 0);
593
 
594
	winopenbody(m->w, OWRITE);
595
	b = emalloc(sizeof(*b));
596
	Binit(b, fd, OREAD);
597
 
598
	inhdr = 1;
599
	copy = 1;
600
	while(p = Brdline(b, '\n')){
601
		if(Blinelen(b)==1)
602
			inhdr = 0, copy=1;
603
		if(inhdr && !isspace(p[0])){
604
			copy = 1;
605
			if(!m->headers){
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);
610
					free(p);
611
					copy = 0;
612
					continue;
613
				}
614
				for(i=0; i<nelem(skipheader); i++)
615
					if(cistrncmp(p, skipheader[i], strlen(skipheader[i]))==0)
616
						copy=0;
617
			}
618
		}
619
		if(copy)
620
			Bwrite(m->w->body, p, Blinelen(b));
621
	}
622
	Bterm(b);
623
	free(b);
624
	winclean(m->w);
625
	if(xfd != -1)
626
		close(xfd);
627
	return 1;
628
}
629
 
630
Article*
631
newpost(void)
632
{
633
	Article *m;
634
	char *p, tmp[40];
635
	static int nnew;
636
 
637
	m = emalloc(sizeof *m);
638
	sprint(tmp, "Post%d", ++nnew);
639
	p = estrstrdup(dir, tmp);
640
 
641
	m->w = newwindow();
642
	proccreate(wineventproc, m->w, STACK);
643
	winname(m->w, p);
644
	wintagwrite(m->w, "Post ", 5);
645
	m->sayspost = 1;
646
	m->ispost = 1;
647
	threadcreate(mesgthread, m, STACK);
648
 
649
	if(mlist){
650
		m->next = mlist;
651
		mlist->prev = m;
652
	}
653
	mlist = m;
654
	return m;
655
}
656
 
657
void
658
replywindow(Article *m)
659
{
660
	Biobuf *b;
661
	char *p, *ep, *q, tmp[40];
662
	int fd, copy;
663
	Article *reply;
664
 
665
	sprint(tmp, "%d/article", m->n);
666
	p = estrstrdup(dir, tmp);
667
	if((fd = open(p, OREAD)) < 0){
668
		free(p);	
669
		return;
670
	}
671
	free(p);
672
 
673
	reply = newpost();
674
	winopenbody(reply->w, OWRITE);
675
	b = emalloc(sizeof(*b));
676
	Binit(b, fd, OREAD);
677
	copy = 0;
678
	while(p = Brdline(b, '\n')){
679
		if(Blinelen(b)==1)
680
			break;
681
		ep = p+Blinelen(b);
682
		if(!isspace(*p)){
683
			copy = 0;
684
			if(cistrncmp(p, "newsgroups:", 11)==0){
685
				for(q=p+11; *q!='\n'; q++)
686
					if(*q==',')
687
						*q = ' ';
688
				copy = 1;
689
			}else if(cistrncmp(p, "subject:", 8)==0){
690
				if(!strstr(p, " Re:") && !strstr(p, " RE:") && !strstr(p, " re:")){
691
					p = skip(p, "subject:");
692
					ep[-1] = '\0';
693
					Bprint(reply->w->body, "Subject: Re: %s\n", p);
694
				}else
695
					copy = 1;
696
			}else if(cistrncmp(p, "message-id:", 11)==0){
697
				Bprint(reply->w->body, "References: ");
698
				p += 11;
699
				copy = 1;
700
			}
701
		}
702
		if(copy)
703
			Bwrite(reply->w->body, p, ep-p);
704
	}
705
	Bterm(b);
706
	close(fd);
707
	free(b);
708
	Bprint(reply->w->body, "\n");
709
	winclean(reply->w);
710
	winselect(reply->w, "$", 0);
711
}
712
 
713
char*
714
skipbl(char *s, char *e)
715
{
716
	while(s < e){
717
		if(*s!=' ' && *s!='\t' && *s!=',')
718
			break;
719
		s++;
720
	}
721
	return s;
722
}
723
 
724
char*
725
findbl(char *s, char *e)
726
{
727
	while(s < e){
728
		if(*s==' ' || *s=='\t' || *s==',')
729
			break;
730
		s++;
731
	}
732
	return s;
733
}
734
 
735
/*
736
 * comma-separate possibly blank-separated strings in line; e points before newline
737
 */
738
void
739
commas(char *s, char *e)
740
{
741
	char *t;
742
 
743
	/* may have initial blanks */
744
	s = skipbl(s, e);
745
	while(s < e){
746
		s = findbl(s, e);
747
		if(s == e)
748
			break;
749
		t = skipbl(s, e);
750
		if(t == e)	/* no more words */
751
			break;
752
		/* patch comma */
753
		*s++ = ',';
754
		while(s < t)
755
			*s++ = ' ';
756
	}
757
}
758
void
759
mesgpost(Article *m)
760
{
761
	Biobuf *b;
762
	char *p, *ep;
763
	int isfirst, ishdr, havegroup, havefrom;
764
 
765
	p = estrstrdup(dir, "post");
766
	if((b = Bopen(p, OWRITE)) == nil){
767
		fprint(2, "cannot open %s: %r\n", p);
768
		free(p);
769
		return;
770
	}
771
	free(p);
772
 
773
	winopenbody(m->w, OREAD);
774
	ishdr = 1;
775
	isfirst = 1;
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){
780
			if(!havegroup)
781
				Bprint(b, "Newsgroups: %s\n", group);
782
			if(!havefrom)
783
				Bprint(b, "From: %s\n", from);
784
			ishdr = 0;
785
		}
786
		if(ishdr){
787
			ep[-1] = '\0';
788
			if(isfirst && strchr(p, ':')==0){	/* group list */
789
				commas(p, ep);
790
				Bprint(b, "newsgroups: %s\n", p);
791
				havegroup = 1;
792
				isfirst = 0;
793
				continue;
794
			}
795
			if(cistrncmp(p, "newsgroup:", 10)==0){
796
				commas(skip(p, "newsgroup:"), ep);
797
				Bprint(b, "newsgroups: %s\n", skip(p, "newsgroup:"));
798
				havegroup = 1;
799
				continue;
800
			}
801
			if(cistrncmp(p, "newsgroups:", 11)==0){
802
				commas(skip(p, "newsgroups:"), ep);
803
				Bprint(b, "newsgroups: %s\n", skip(p, "newsgroups:"));
804
				havegroup = 1;
805
				continue;
806
			}
807
			if(cistrncmp(p, "from:", 5)==0)
808
				havefrom = 1;
809
			ep[-1] = '\n';
810
		}
811
		Bwrite(b, p, ep-p);
812
	}
813
	winclosebody(m->w);
814
	Bflush(b);
815
	if(write(Bfildes(b), "", 0) == 0)
816
		winclean(m->w);
817
	else
818
		fprint(2, "post: %r\n");
819
	Bterm(b);
820
}
821
 
822
int
823
mesgopen(char *s)
824
{
825
	char *p, tmp[40];
826
	int fd, n;
827
	Article *m;
828
 
829
	n = atoi(s);
830
	if(n==0)
831
		return 0;
832
 
833
	for(m=mlist; m; m=m->next){
834
		if(m->n == n){
835
			ctlprint(m->w->ctl, "show\n");
836
			return 1;
837
		}
838
	}
839
 
840
	sprint(tmp, "%d/article", n);
841
	p = estrstrdup(dir, tmp);
842
	if((fd = open(p, OREAD)) < 0){
843
		free(p);	
844
		return 0;
845
	}
846
 
847
	m = emalloc(sizeof(*m));
848
	m->w = newwindow();
849
	m->n = n;
850
	proccreate(wineventproc, m->w, STACK);
851
	p[strlen(p)-strlen("article")] = '\0';
852
	winname(m->w, p);
853
	if(canpost)
854
		wintagwrite(m->w, "Reply ", 6);
855
	wintagwrite(m->w, "Headers ", 8);
856
 
857
	free(p);
858
	if(mlist){
859
		m->next = mlist;
860
		mlist->prev = m;
861
	}
862
	mlist = m;
863
	threadcreate(mesgthread, m, STACK);
864
 
865
	fillmesgwindow(fd, m);
866
	close(fd);
867
	windormant(m->w);
868
	return 1;
869
}
870
 
871
void
872
usage(void)
873
{
874
	fprint(2, "usage: News [-d /mnt/news] comp.os.plan9\n");
875
	exits("usage");
876
}
877
 
878
void
879
timerproc(void *v)
880
{
881
	Event e;
882
	Window *w;
883
 
884
	memset(&e, 0, sizeof e);
885
	e.c1 = 'T';
886
	w = v;
887
 
888
	for(;;){
889
		sleep(60*1000);
890
		sendp(w->cevent, &e);
891
	}
892
}
893
 
894
char*
895
findfrom(void)
896
{
897
	char *p, *u;
898
	Biobuf *b;
899
 
900
	u = getuser();
901
	if(u==nil)
902
		return "glenda";
903
 
904
	p = estrstrstrdup("/usr/", u, "/lib/newsfrom");
905
	b = Bopen(p, OREAD);
906
	free(p);
907
	if(b){
908
		p = Brdline(b, '\n');
909
		if(p){
910
			p[Blinelen(b)-1] = '\0';
911
			p = estrdup(p);
912
			Bterm(b);
913
			return p;
914
		}
915
		Bterm(b);
916
	}
917
 
918
	p = estrstrstrdup("/mail/box/", u, "/headers");
919
	b = Bopen(p, OREAD);
920
	free(p);
921
	if(b){
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:"));
926
				Bterm(b);
927
				return p;
928
			}
929
		}
930
		Bterm(b);
931
	}
932
 
933
	return u;
934
}
935
 
936
void
937
threadmain(int argc, char **argv)
938
{
939
	char *p, *q;
940
	Dir *d;
941
	Window *w;
942
 
943
	ARGBEGIN{
944
	case 'D':
945
		debug++;
946
		break;
947
	case 'd':
948
		dir = EARGF(usage());
949
		break;
950
	default:
951
		usage();
952
		break;
953
	}ARGEND
954
 
955
	if(argc != 1)
956
		usage();
957
 
958
	from = findfrom();
959
 
960
	group = estrdup(argv[0]);	/* someone will be cute */
961
	while(q=strchr(group, '/'))
962
		*q = '.';
963
 
964
	p = estrdup(argv[0]);
965
	while(q=strchr(p, '.'))
966
		*q = '/';
967
	p = estrstrstrdup(dir, "/", p);
968
	cleanname(p);
969
 
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);
973
			threadexitsall(nil);
974
		}
975
		if((d->mode&DMDIR)==0){
976
			fprint(2, "%s not a directory\n", dir);
977
			threadexitsall(nil);
978
		}
979
		free(d);
980
		if((d = dirstat(p)) == nil){
981
			fprint(2, "stat %s: %r\n", p);
982
			threadexitsall(nil);
983
		}
984
	}
985
	if((d->mode&DMDIR)==0){
986
		fprint(2, "%s not a directory\n", dir);
987
		threadexitsall(nil);
988
	}
989
	free(d);
990
	dir = estrstrdup(p, "/");
991
 
992
	q = estrstrdup(dir, "post");
993
	canpost = access(q, AWRITE)==0;
994
 
995
	w = newwindow();
996
	root = w;
997
	proccreate(wineventproc, w, STACK);
998
	proccreate(timerproc, w, STACK);
999
 
1000
	winname(w, dir);
1001
	if(canpost)
1002
		wintagwrite(w, "Newpost ", 8);
1003
	wintagwrite(w, "More ", 5);
1004
	dirwindow(w);
1005
	threadcreate(dirthread, w, STACK);
1006
	threadexits(nil);
1007
}