Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <bio.h>
4
 
5
/*
6
 * Deroff command -- strip troff, eqn, and tbl sequences from
7
 * a file.  Has three flags argument, -w, to cause output one word per line
8
 * rather than in the original format.
9
 * -mm (or -ms) causes the corresponding macro's to be interpreted
10
 * so that just sentences are output
11
 * -ml  also gets rid of lists.
12
 * -i causes deroff to ignore .so and .nx commands.
13
 * Deroff follows .so and .nx commands, removes contents of macro
14
 * definitions, equations (both .EQ ... .EN and $...$),
15
 * Tbl command sequences, and Troff backslash vconstructions.
16
 * 
17
 * All input is through the C macro; the most recently read character is in c.
18
 */
19
 
20
/*
21
#define	C	((c = Bgetrune(infile)) < 0?\
22
			eof():\
23
			((c == ldelim) && (filesp == files)?\
24
				skeqn():\
25
				(c == '\n'?\
26
					(linect++,c):\
27
						c)))
28
 
29
#define	C1	((c = Bgetrune(infile)) == Beof?\
30
			eof():\
31
			(c == '\n'?\
32
				(linect++,c):\
33
				c))
34
*/
35
 
36
/* lose those macros! */
37
#define	C	fC()
38
#define	C1	fC1()
39
 
40
#define	SKIP	while(C != '\n') 
41
#define SKIP1	while(C1 != '\n')
42
#define SKIP_TO_COM		SKIP;\
43
				SKIP;\
44
				pc=c;\
45
				while(C != '.' || pc != '\n' || C > 'Z')\
46
						pc=c
47
 
48
#define YES		1
49
#define NO		0
50
#define MS		0
51
#define MM		1
52
#define ONE		1
53
#define TWO		2
54
 
55
#define NOCHAR		-2
56
#define	EXTENDED	-1		/* All runes above 0x7F */
57
#define SPECIAL		0
58
#define APOS		1
59
#define PUNCT		2
60
#define DIGIT		3
61
#define LETTER		4
62
 
63
 
64
int	linect	= 0;
65
int	wordflag= NO;
66
int	underscoreflag = NO;
67
int	msflag	= NO;
68
int	iflag	= NO;
69
int	mac	= MM;
70
int	disp	= 0;
71
int	inmacro	= NO;
72
int	intable	= NO;
73
int	eqnflag	= 0;
74
 
75
#define	MAX_ASCII	0X80
76
 
77
char	chars[MAX_ASCII];	/* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */
78
 
79
Rune	line[30000];
80
Rune*	lp;
81
 
82
long	c;
83
long	pc;
84
int	ldelim	= NOCHAR;
85
int	rdelim	= NOCHAR;
86
 
87
 
88
char**	argv;
89
 
90
char	fname[50];
91
Biobuf*	files[15];
92
Biobuf**filesp;
93
Biobuf*	infile;
94
char*	devnull	= "/dev/null";
95
Biobuf	*infile;
96
Biobuf	bout;
97
 
98
long	skeqn(void);
99
Biobuf*	opn(char *p);
100
int	eof(void);
101
int	charclass(int);
102
void	getfname(void);
103
void	fatal(char *s, char *p);
104
void	usage(void);
105
void	work(void);
106
void	putmac(Rune *rp, int vconst);
107
void	regline(int macline, int vconst);
108
void	putwords(void);
109
void	comline(void);
110
void	macro(void);
111
void	eqn(void);
112
void	tbl(void);
113
void	stbl(void);
114
void	sdis(char a1, char a2);
115
void	sce(void);
116
void	backsl(void);
117
char*	copys(char *s);
118
void	refer(int c1);
119
void	inpic(void);
120
 
121
int
122
fC(void)
123
{
124
	c = Bgetrune(infile);
125
	if(c < 0)
126
		return eof();
127
	if(c == ldelim && filesp == files)
128
		return skeqn();
129
	if(c == '\n')
130
		linect++;
131
	return c;
132
}
133
 
134
int
135
fC1(void)
136
{
137
	c = Bgetrune(infile);
138
	if(c == Beof)
139
		return eof();
140
	if(c == '\n')
141
		linect++;
142
	return c;
143
}
144
 
145
void
146
main(int argc, char *av[])
147
{
148
	int i;
149
	char *f;
150
 
151
	argv = av;
152
	Binit(&bout, 1, OWRITE);
153
	ARGBEGIN{
154
	case 'w':
155
		wordflag = YES;
156
		break;
157
	case '_':
158
		wordflag = YES;
159
		underscoreflag = YES;
160
		break;
161
	case 'm':
162
		msflag = YES;
163
		if(f = ARGF())
164
			switch(*f)
165
			{
166
			case 'm':	mac = MM; break;
167
			case 's':	mac = MS; break;
168
			case 'l':	disp = 1; break;
169
			default:	usage();
170
			}
171
		else
172
			usage();
173
		break;
174
	case 'i':
175
		iflag = YES;
176
		break;
177
	default:
178
		usage();
179
	}ARGEND
180
	if(*argv)
181
		infile = opn(*argv++);
182
	else{
183
		infile = malloc(sizeof(Biobuf));
184
		Binit(infile, 0, OREAD);
185
	}
186
	files[0] = infile;
187
	filesp = &files[0];
188
 
189
	for(i='a'; i<='z' ; ++i)
190
		chars[i] = LETTER;
191
	for(i='A'; i<='Z'; ++i)
192
		chars[i] = LETTER;
193
	for(i='0'; i<='9'; ++i)
194
		chars[i] = DIGIT;
195
	chars['\''] = APOS;
196
	chars['&'] = APOS;
197
	chars['\b'] = APOS;
198
	chars['.'] = PUNCT;
199
	chars[','] = PUNCT;
200
	chars[';'] = PUNCT;
201
	chars['?'] = PUNCT;
202
	chars[':'] = PUNCT;
203
	work();
204
}
205
 
206
long
207
skeqn(void)
208
{
209
	while(C1 != rdelim)
210
		if(c == '\\')
211
			c = C1;
212
		else if(c == '"')
213
			while(C1 != '"')
214
				if(c == '\\') 
215
					C1;
216
	if (msflag)
217
		eqnflag = 1;
218
	return(c = ' ');
219
}
220
 
221
Biobuf*
222
opn(char *p)
223
{
224
	Biobuf *fd;
225
 
226
	while ((fd = Bopen(p, OREAD)) == 0) {
227
		if(msflag || p == devnull)
228
			fatal("Cannot open file %s - quitting\n", p);
229
		else {
230
			fprint(2, "Deroff: Cannot open file %s - continuing\n", p);
231
			p = devnull;
232
		}
233
	}
234
	linect = 0;
235
	return(fd);
236
}
237
 
238
int
239
eof(void)
240
{
241
	if(Bfildes(infile) != 0)
242
		Bterm(infile);
243
	if(filesp > files)
244
		infile = *--filesp;
245
	else
246
	if(*argv)
247
		infile = opn(*argv++);
248
	else
249
		exits(0);
250
	return(C);
251
}
252
 
253
void
254
getfname(void)
255
{
256
	char *p;
257
	Rune r;
258
	Dir *dir;
259
	struct chain
260
	{ 
261
		struct	chain*	nextp; 
262
		char*	datap; 
263
	} *q;
264
 
265
	static struct chain *namechain= 0;
266
 
267
	while(C == ' ')
268
		;
269
	for(p = fname; (r=c) != '\n' && r != ' ' && r != '\t' && r != '\\'; C)
270
		p += runetochar(p, &r);
271
	*p = '\0';
272
	while(c != '\n')
273
		C;
274
	if(!strcmp(fname, "/sys/lib/tmac/tmac.cs")
275
			|| !strcmp(fname, "/sys/lib/tmac/tmac.s")) {
276
		fname[0] = '\0';
277
		return;
278
	}
279
	dir = dirstat(fname);
280
	if(dir!=nil && ((dir->mode & DMDIR) || dir->type != 'M')) {
281
		free(dir);
282
		fname[0] = '\0';
283
		return;
284
	}
285
	free(dir);
286
	/*
287
	 * see if this name has already been used
288
	 */
289
 
290
	for(q = namechain; q; q = q->nextp)
291
		if( !strcmp(fname, q->datap)) {
292
			fname[0] = '\0';
293
			return;
294
		}
295
	q = (struct chain*)malloc(sizeof(struct chain));
296
	q->nextp = namechain;
297
	q->datap = copys(fname);
298
	namechain = q;
299
}
300
 
301
void
302
usage(void)
303
{
304
	fprint(2,"usage: deroff [-nw_pi] [-m (m s l)] [file ...] \n");
305
	exits("usage");
306
}
307
 
308
void
309
fatal(char *s, char *p)
310
{
311
	fprint(2, "deroff: ");
312
	fprint(2, s, p);
313
	exits(s);
314
}
315
 
316
void
317
work(void)
318
{
319
 
320
	for(;;) {
321
		eqnflag = 0;
322
		if(C == '.'  ||  c == '\'')
323
			comline();
324
		else
325
			regline(NO, TWO);
326
	}
327
}
328
 
329
void
330
regline(int macline, int vconst)
331
{
332
	line[0] = c;
333
	lp = line;
334
	for(;;) {
335
		if(c == '\\') {
336
			*lp = ' ';
337
			backsl();
338
			if(c == '%')	/* no blank for hyphenation char */
339
				lp--;
340
		}
341
		if(c == '\n')
342
			break;
343
		if(intable && c=='T') {
344
			*++lp = C;
345
			if(c=='{' || c=='}') {
346
				lp[-1] = ' ';
347
				*lp = C;
348
			}
349
		} else {
350
			if(msflag == 1 && eqnflag == 1) {
351
				eqnflag = 0;
352
				*++lp = 'x';
353
			}
354
			*++lp = C;
355
		}
356
	}
357
	*lp = '\0';
358
	if(lp != line) {
359
		if(wordflag)
360
			putwords();
361
		else
362
		if(macline)
363
			putmac(line,vconst);
364
		else
365
			Bprint(&bout, "%S\n", line);
366
	}
367
}
368
 
369
void
370
putmac(Rune *rp, int vconst)
371
{
372
	Rune *t;
373
	int found;
374
	Rune last;
375
 
376
	found = 0;
377
	last = 0;
378
	while(*rp) {
379
		while(*rp == ' ' || *rp == '\t')
380
			Bputrune(&bout, *rp++);
381
		for(t = rp; *t != ' ' && *t != '\t' && *t != '\0'; t++)
382
			;
383
		if(*rp == '\"')
384
			rp++;
385
		if(t > rp+vconst && charclass(*rp) == LETTER
386
				&& charclass(rp[1]) == LETTER) {
387
			while(rp < t)
388
				if(*rp == '\"')
389
					rp++;
390
				else
391
					Bputrune(&bout, *rp++);
392
			last = t[-1];
393
			found++;
394
		} else
395
		if(found && charclass(*rp) == PUNCT && rp[1] == '\0')
396
			Bputrune(&bout, *rp++);
397
		else {
398
			last = t[-1];
399
			rp = t;
400
		}
401
	}
402
	Bputc(&bout, '\n');
403
	if(msflag && charclass(last) == PUNCT)
404
		Bprint(&bout, " %C\n", last);
405
}
406
 
407
/*
408
 * break into words for -w option
409
 */
410
void
411
putwords(void)
412
{
413
	Rune *p, *p1;
414
	int i, nlet;
415
 
416
 
417
	for(p1 = line;;) {
418
		/*
419
		 * skip initial specials ampersands and apostrophes
420
		 */
421
		while((i = charclass(*p1)) != EXTENDED && i < DIGIT)
422
			if(*p1++ == '\0')
423
				return;
424
		nlet = 0;
425
		for(p = p1; (i = charclass(*p)) != SPECIAL || (underscoreflag && *p=='_'); p++)
426
			if(i == LETTER || (underscoreflag && *p == '_'))
427
				nlet++;
428
		/*
429
		 * MDM definition of word
430
		 */
431
		if(nlet > 1) {
432
			/*
433
			 * delete trailing ampersands and apostrophes
434
			 */
435
			while(*--p == '\'' || *p == '&'
436
					   || charclass(*p) == PUNCT)
437
				;
438
			while(p1 <= p)
439
				Bputrune(&bout, *p1++);
440
			Bputc(&bout, '\n');
441
		} else
442
			p1 = p;
443
	}
444
}
445
 
446
void
447
comline(void)
448
{
449
	long c1, c2;
450
 
451
	while(C==' ' || c=='\t')
452
		;
453
comx:
454
	if((c1=c) == '\n')
455
		return;
456
	c2 = C;
457
	if(c1=='.' && c2!='.')
458
		inmacro = NO;
459
	if(msflag && c1 == '['){
460
		refer(c2);
461
		return;
462
	}
463
	if(c2 == '\n')
464
		return;
465
	if(c1 == '\\' && c2 == '\"')
466
		SKIP;
467
	else
468
	if (filesp==files && c1=='E' && c2=='Q')
469
			eqn();
470
	else
471
	if(filesp==files && c1=='T' && (c2=='S' || c2=='C' || c2=='&')) {
472
		if(msflag)
473
			stbl(); 
474
		else
475
			tbl();
476
	}
477
	else
478
	if(c1=='T' && c2=='E')
479
		intable = NO;
480
	else if (!inmacro &&
481
			((c1 == 'd' && c2 == 'e') ||
482
		   	 (c1 == 'i' && c2 == 'g') ||
483
		   	 (c1 == 'a' && c2 == 'm')))
484
				macro();
485
	else
486
	if(c1=='s' && c2=='o') {
487
		if(iflag)
488
			SKIP;
489
		else {
490
			getfname();
491
			if(fname[0]) {
492
				if(infile = opn(fname))
493
					*++filesp = infile;
494
				else infile = *filesp;
495
			}
496
		}
497
	}
498
	else
499
	if(c1=='n' && c2=='x')
500
		if(iflag)
501
			SKIP;
502
		else {
503
			getfname();
504
			if(fname[0] == '\0')
505
				exits(0);
506
			if(Bfildes(infile) != 0)
507
				Bterm(infile);
508
			infile = *filesp = opn(fname);
509
		}
510
	else
511
	if(c1 == 't' && c2 == 'm')
512
		SKIP;
513
	else
514
	if(c1=='h' && c2=='w')
515
		SKIP; 
516
	else
517
	if(msflag && c1 == 'T' && c2 == 'L') {
518
		SKIP_TO_COM;
519
		goto comx; 
520
	}
521
	else
522
	if(msflag && c1=='N' && c2 == 'R')
523
		SKIP;
524
	else
525
	if(msflag && c1 == 'A' && (c2 == 'U' || c2 == 'I')){
526
		if(mac==MM)SKIP;
527
		else {
528
			SKIP_TO_COM;
529
			goto comx; 
530
		}
531
	} else
532
	if(msflag && c1=='F' && c2=='S') {
533
		SKIP_TO_COM;
534
		goto comx; 
535
	}
536
	else
537
	if(msflag && (c1=='S' || c1=='N') && c2=='H') {
538
		SKIP_TO_COM;
539
		goto comx; 
540
	} else
541
	if(c1 == 'U' && c2 == 'X') {
542
		if(wordflag)
543
			Bprint(&bout, "UNIX\n");
544
		else
545
			Bprint(&bout, "UNIX ");
546
	} else
547
	if(msflag && c1=='O' && c2=='K') {
548
		SKIP_TO_COM;
549
		goto comx; 
550
	} else
551
	if(msflag && c1=='N' && c2=='D')
552
		SKIP;
553
	else
554
	if(msflag && mac==MM && c1=='H' && (c2==' '||c2=='U'))
555
		SKIP;
556
	else
557
	if(msflag && mac==MM && c2=='L') {
558
		if(disp || c1=='R')
559
			sdis('L', 'E');
560
		else {
561
			SKIP;
562
			Bprint(&bout, " .");
563
		}
564
	} else
565
	if(!msflag && c1=='P' && c2=='S') {
566
		inpic();
567
	} else
568
	if(msflag && (c1=='D' || c1=='N' || c1=='K'|| c1=='P') && c2=='S') { 
569
		sdis(c1, 'E'); 
570
	} else
571
	if(msflag && (c1 == 'K' && c2 == 'F')) { 
572
		sdis(c1,'E'); 
573
	} else
574
	if(msflag && c1=='n' && c2=='f')
575
		sdis('f','i');
576
	else
577
	if(msflag && c1=='c' && c2=='e')
578
		sce();
579
	else {
580
		if(c1=='.' && c2=='.') {
581
			if(msflag) {
582
				SKIP;
583
				return;
584
			}
585
			while(C == '.')
586
				;
587
		}
588
		inmacro++;
589
		if(c1 <= 'Z' && msflag)
590
			regline(YES,ONE);
591
		else {
592
			if(wordflag)
593
				C;
594
			regline(YES,TWO);
595
		}
596
		inmacro--;
597
	}
598
}
599
 
600
void
601
macro(void)
602
{
603
	if(msflag) {
604
		do { 
605
			SKIP1; 
606
		} while(C1 != '.' || C1 != '.' || C1 == '.');
607
		if(c != '\n')
608
			SKIP;
609
		return;
610
	}
611
	SKIP;
612
	inmacro = YES;
613
}
614
 
615
void
616
sdis(char a1, char a2)
617
{
618
	int c1, c2;
619
	int eqnf;
620
	int lct;
621
 
622
	if(a1 == 'P'){
623
		while(C1 == ' ')
624
			;
625
		if(c == '<') {
626
			SKIP1;
627
			return;
628
		}
629
	}
630
	lct = 0;
631
	eqnf = 1;
632
	if(c != '\n')
633
		SKIP1;
634
	for(;;) {
635
		while(C1 != '.')
636
			if(c == '\n')
637
				continue;
638
			else
639
				SKIP1;
640
		if((c1=C1) == '\n')
641
			continue;
642
		if((c2=C1) == '\n') {
643
			if(a1 == 'f' && (c1 == 'P' || c1 == 'H'))
644
				return;
645
			continue;
646
		}
647
		if(c1==a1 && c2 == a2) {
648
			SKIP1;
649
			if(lct != 0){
650
				lct--;
651
				continue;
652
			}
653
			if(eqnf)
654
				Bprint(&bout, " .");
655
			Bputc(&bout, '\n');
656
			return;
657
		} else
658
		if(a1 == 'L' && c2 == 'L') {
659
			lct++;
660
			SKIP1;
661
		} else
662
		if(a1 == 'D' && c1 == 'E' && c2 == 'Q') {
663
			eqn(); 
664
			eqnf = 0;
665
		} else
666
		if(a1 == 'f') {
667
			if((mac == MS && c2 == 'P') ||
668
				(mac == MM && c1 == 'H' && c2 == 'U')){
669
				SKIP1;
670
				return;
671
			}
672
			SKIP1;
673
		}
674
		else
675
			SKIP1;
676
	}
677
}
678
 
679
void
680
tbl(void)
681
{
682
	while(C != '.')
683
		;
684
	SKIP;
685
	intable = YES;
686
}
687
 
688
void
689
stbl(void)
690
{
691
	while(C != '.')
692
		;
693
	SKIP_TO_COM;
694
	if(c != 'T' || C != 'E') {
695
		SKIP;
696
		pc = c;
697
		while(C != '.' || pc != '\n' || C != 'T' || C != 'E')
698
			pc = c;
699
	}
700
}
701
 
702
void
703
eqn(void)
704
{
705
	long c1, c2;
706
	int dflg;
707
	char last;
708
 
709
	last = 0;
710
	dflg = 1;
711
	SKIP;
712
 
713
	for(;;) {
714
		if(C1 == '.'  || c == '\'') {
715
			while(C1==' ' || c=='\t')
716
				;
717
			if(c=='E' && C1=='N') {
718
				SKIP;
719
				if(msflag && dflg) {
720
					Bputc(&bout, 'x');
721
					Bputc(&bout, ' ');
722
					if(last) {
723
						Bputc(&bout, last); 
724
						Bputc(&bout, '\n'); 
725
					}
726
				}
727
				return;
728
			}
729
		} else
730
		if(c == 'd') {
731
			if(C1=='e' && C1=='l')
732
				if(C1=='i' && C1=='m') {
733
					while(C1 == ' ')
734
						;
735
					if((c1=c)=='\n' || (c2=C1)=='\n' ||
736
					  (c1=='o' && c2=='f' && C1=='f')) {
737
						ldelim = NOCHAR;
738
						rdelim = NOCHAR;
739
					} else {
740
						ldelim = c1;
741
						rdelim = c2;
742
					}
743
				}
744
			dflg = 0;
745
		}
746
		if(c != '\n')
747
			while(C1 != '\n') { 
748
				if(chars[c] == PUNCT)
749
					last = c;
750
				else
751
				if(c != ' ')
752
					last = 0;
753
			}
754
	}
755
}
756
 
757
/*
758
 * skip over a complete backslash vconstruction
759
 */
760
void
761
backsl(void)
762
{
763
	int bdelim;
764
 
765
sw:  
766
	switch(C1)
767
	{
768
	case '"':
769
		SKIP1;
770
		return;
771
 
772
	case 's':
773
		if(C1 == '\\')
774
			backsl();
775
		else {
776
			while(C1>='0' && c<='9')
777
				;
778
			Bungetrune(infile);
779
			c = '0';
780
		}
781
		lp--;
782
		return;
783
 
784
	case 'f':
785
	case 'n':
786
	case '*':
787
		if(C1 != '(')
788
			return;
789
 
790
	case '(':
791
		if(msflag) {
792
			if(C == 'e') {
793
				if(C1 == 'm') {
794
					*lp = '-';
795
					return;
796
				}
797
			} else
798
			if(c != '\n')
799
				C1;
800
			return;
801
		}
802
		if(C1 != '\n')
803
			C1;
804
		return;
805
 
806
	case '$':
807
		C1;	/* discard argument number */
808
		return;
809
 
810
	case 'b':
811
	case 'x':
812
	case 'v':
813
	case 'h':
814
	case 'w':
815
	case 'o':
816
	case 'l':
817
	case 'L':
818
		if((bdelim=C1) == '\n')
819
			return;
820
		while(C1!='\n' && c!=bdelim)
821
			if(c == '\\')
822
				backsl();
823
		return;
824
 
825
	case '\\':
826
		if(inmacro)
827
			goto sw;
828
	default:
829
		return;
830
	}
831
}
832
 
833
char*
834
copys(char *s)
835
{
836
	char *t, *t0;
837
 
838
	if((t0 = t = malloc((strlen(s)+1))) == 0)
839
		fatal("Cannot allocate memory", (char*)0);
840
	while(*t++ = *s++)
841
		;
842
	return(t0);
843
}
844
 
845
void
846
sce(void)
847
{
848
	int n = 1;
849
 
850
	while (C != L'\n' && !(L'0' <= c && c <= L'9'))
851
		;
852
	if (c != L'\n') {
853
		for (n = c-L'0';'0' <= C && c <= L'9';)
854
			n = n*10 + c-L'0';
855
	}
856
	while(n) {
857
		if(C == '.') {
858
			if(C == 'c') {
859
				if(C == 'e') {
860
					while(C == ' ')
861
						;
862
					if(c == '0') {
863
						SKIP;
864
						break;
865
					} else
866
						SKIP;
867
				} else
868
					SKIP;
869
			} else
870
			if(c == 'P' || C == 'P') {
871
				if(c != '\n')
872
					SKIP;
873
				break;
874
			} else
875
				if(c != '\n')
876
					SKIP;
877
		} else {
878
			SKIP;
879
			n--;
880
		}
881
	}
882
}
883
 
884
void
885
refer(int c1)
886
{
887
	int c2;
888
 
889
	if(c1 != '\n')
890
		SKIP;
891
	c2 = 0;
892
	for(;;) {
893
		if(C != '.')
894
			SKIP;
895
		else {
896
			if(C != ']')
897
				SKIP;
898
			else {
899
				while(C != '\n')
900
					c2 = c;
901
				if(charclass(c2) == PUNCT)
902
					Bprint(&bout, " %C",c2);
903
				return;
904
			}
905
		}
906
	}
907
}
908
 
909
void
910
inpic(void)
911
{
912
	int c1;
913
	Rune *p1;
914
 
915
/*	SKIP1;*/
916
	while(C1 != '\n')
917
		if(c == '<'){
918
			SKIP1;
919
			return;
920
		}
921
	p1 = line;
922
	c = '\n';
923
	for(;;) {
924
		c1 = c;
925
		if(C1 == '.' && c1 == '\n') {
926
			if(C1 != 'P' || C1 != 'E') {
927
				if(c != '\n'){
928
					SKIP1;
929
					c = '\n';
930
				}
931
				continue;
932
			}
933
			SKIP1;
934
			return;
935
		} else
936
		if(c == '\"') {
937
			while(C1 != '\"') {
938
				if(c == '\\') {
939
					if(C1 == '\"')
940
						continue;
941
					Bungetrune(infile);
942
					backsl();
943
				} else
944
					*p1++ = c;
945
			}
946
			*p1++ = ' ';
947
		} else
948
		if(c == '\n' && p1 != line) {
949
			*p1 = '\0';
950
			if(wordflag)
951
				putwords();
952
			else
953
				Bprint(&bout, "%S\n\n", line);
954
			p1 = line;
955
		}
956
	}
957
}
958
 
959
int
960
charclass(int c)
961
{
962
	if(c < MAX_ASCII)
963
		return chars[c];
964
	switch(c){
965
	case 0x2013: case 0x2014:	/* en dash, em dash */
966
		return SPECIAL;
967
	}
968
	return EXTENDED;
969
}