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
fC(void)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
fC1(void)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
main(int argc,char * av[])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
skeqn(void)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*
opn(char * p)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
eof(void)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
getfname(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
usage(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
fatal(char * s,char * p)309 fatal(char *s, char *p)
310 {
311 	fprint(2, "deroff: ");
312 	fprint(2, s, p);
313 	exits(s);
314 }
315 
316 void
work(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
regline(int macline,int vconst)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
putmac(Rune * rp,int vconst)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
putwords(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
comline(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
macro(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
sdis(char a1,char a2)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
tbl(void)680 tbl(void)
681 {
682 	while(C != '.')
683 		;
684 	SKIP;
685 	intable = YES;
686 }
687 
688 void
stbl(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
eqn(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
backsl(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*
copys(char * s)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
sce(void)846 sce(void)
847 {
848 	int n = 1;
849 
850 	while (C != '\n' && !('0' <= c && c <= '9'))
851 		;
852 	if (c != '\n') {
853 		for (n = c-'0';'0' <= C && c <= '9';)
854 			n = n*10 + c-'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
refer(int c1)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
inpic(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
charclass(int c)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 }
970