2 * pass2.c - cawf(1) pass 2 function
6 * Copyright (c) 1991 Purdue University Research Foundation,
7 * West Lafayette, Indiana 47907. All rights reserved.
9 * Written by Victor A. Abell <abe@mace.cc.purdue.edu>, Purdue
10 * University Computing Center. Not derived from licensed software;
11 * derived from awf(1) by Henry Spencer of the University of Toronto.
13 * Permission is granted to anyone to use this software for any
14 * purpose on any computer system, and to alter it and redistribute
15 * it freely, subject to the following restrictions:
17 * 1. The author is not responsible for any consequences of use of
18 * this software, even if they arise from flaws in it.
20 * 2. The origin of this software must not be misrepresented, either
21 * by explicit claim or by omission. Credits must appear in the
24 * 3. Altered versions must be plainly marked as such, and must not
25 * be misrepresented as being the original software. Credits must
26 * appear in the documentation.
28 * 4. This notice may not be removed or altered.
35 * Pass2(line) - process the nroff requests in a line and break
36 * text into words for pass 3
43 int brk
; /* request break status */
44 unsigned char buf
[MAXLINE
]; /* working buffer */
45 unsigned char c
; /* character buffer */
46 double d
; /* temporary double */
47 double exscale
; /* expression scaling factor */
48 double expr
[MAXEXP
]; /* expressions */
49 unsigned char exsign
[MAXEXP
]; /* expression signs */
50 int i
, j
; /* temporary indexes */
51 int inword
; /* word processing status */
52 int nexpr
; /* number of expressions */
53 unsigned char nm
[4], nm1
[4]; /* names */
54 int nsp
; /* number of spaces */
55 unsigned char op
; /* expression term operator */
56 unsigned char opstack
[MAXSP
]; /* expression operation stack */
57 unsigned char period
; /* end of word status */
58 unsigned char *s1
, *s2
, *s3
; /* temporary string pointers */
59 double sexpr
[MAXEXP
]; /* signed expressions */
60 int sp
; /* expression stack pointer */
61 unsigned char ssign
; /* expression's starting sign */
62 int tabpos
; /* tab position */
63 double tscale
; /* term scaling factor */
64 double tval
; /* term value */
65 double val
; /* term value */
66 double valstack
[MAXSP
]; /* expression value stack */
67 unsigned char xbuf
[MAXLINE
]; /* expansion buffer */
71 * End of macro expansion.
73 Pass3(DOBREAK
, (unsigned char *)"need", NULL
, 999);
82 * Empty line - "^[ \t]*$" or "^\\\"".
84 if (regexec(Pat
[6].pat
, line
)
85 || strncmp((char *)line
, "\\\"", 2) == 0) {
86 Pass3(DOBREAK
, (unsigned char *)"space", NULL
, 0);
90 * Line begins with white space.
92 if (*line
== ' ' || *line
== '\t') {
93 Pass3(DOBREAK
, (unsigned char *)"flush", NULL
, 0);
94 Pass3(0, (unsigned char *)"", NULL
, 0);
96 if (*line
!= '.' && *line
!= '\'') {
98 * Line contains text (not an nroff request).
100 if (Font
[0] == 'R' && Backc
== 0 && Aftnxt
== NULL
101 && regexec(Pat
[7].pat
, line
) == 0) {
103 * The font is Roman, there is no "\\c" or "after next"
104 * trap pending and and the line has no '\\', '\t', '-',
105 * or " " (regular expression "\\|\t|-| ").
107 * Output each word of the line as "<length> <word>".
110 while (*s1
&& *s1
== ' ')
114 for (s2
= s1
, s3
= buf
; *s2
&& *s2
!= ' ';)
115 *s3
++ = Trtbl
[(int)*s2
++];
117 Pass3((s2
- s1
), buf
, NULL
, 0);
118 s1
= *s2
? ++s2
: s2
;
121 * Line terminates with punctuation and optional
122 * bracketing (regular expression "[.!?:][\])'\"*]*$").
124 if (regexec(Pat
[8].pat
, line
))
125 Pass3(NOBREAK
, (unsigned char *)"gap", NULL
, 2);
127 Pass3(DOBREAK
,(unsigned char *)"center", NULL
,
130 } else if (Fill
== 0)
131 Pass3(DOBREAK
, (unsigned char *)"flush", NULL
,
136 * Line must be scanned a character at a time.
138 inword
= nsp
= tabpos
= 0;
140 for (s1
= line
;; s1
++) {
142 * Space or TAB causes state transition.
144 if (*s1
== '\0' || *s1
== ' ' || *s1
== '\t') {
148 Pass3(Wordl
, Word
, NULL
, 0);
151 (unsigned char *)"nohyphen",
170 (unsigned char *)"gap",
176 * Process a character.
191 if (tabpos
<= Ntabs
) {
193 (unsigned char *)"tabto", NULL
,
200 * Hyphen if word is being assembled
205 if ((i
= Findhy(NULL
, 0, 0)) < 0) {
206 Error(WARN
, LINE
, " no hyphen for font ",
211 Pass3(Wordl
, Word
, NULL
, Hychar
[i
].len
);
212 Pass3(NOBREAK
, (unsigned char *)"userhyphen",
213 Hychar
[i
].str
, Hychar
[i
].len
);
232 * Change font - "\\fN"
235 s1
= Asmcode(&s1
, nm
);
240 for (i
= 0; Fcode
[i
].nm
; i
++) {
241 if (Fcode
[i
].nm
== nm
[0])
244 if (Fcode
[i
].nm
== '\0'
246 Error(WARN
, LINE
, " unknown font ",
250 if (Fcode
[i
].status
!= '1') {
252 " font undefined ", (char *)nm
);
260 * Positive horizontal motion - "\\h\\n(NN" or
264 if (s1
[1] != '\\' || s1
[2] != 'n') {
266 " no \\n after \\h", NULL
);
270 s1
= Asmcode(&s1
, nm
);
271 if ((i
= Findnum(nm
, 0, 0)) < 0)
273 if ((j
= Numb
[i
].val
) < 0) {
274 Error(WARN
, LINE
, " \\h < 0 ",
280 if ((strlen((char *)s1
+1) + j
+ 1)
283 for (s2
= &xbuf
[1]; j
; j
--)
285 (void) strcpy((char *)s2
, (char *)s1
+1);
289 * Save current position in register if "\\k<reg>"
292 s1
= Asmcode(&s1
, nm
);
293 if ((i
= Findnum(nm
, 0, 0)) < 0)
294 i
= Findnum(nm
, 0, 1);
296 (int)((double)Outll
* Scalen
);
299 * Interpolate number - "\\n(NN" or "\\nN"
302 s1
= Asmcode(&s1
, nm
);
303 if ((i
= Findnum(nm
, 0, 0)) < 0) {
306 " unknown number register ",
310 (void) sprintf((char *)buf
, "%d",
312 if ((strlen((char *)buf
)
313 + strlen((char *)s1
+1) + 1)
316 Error(WARN
, LINE
, " line too long",
320 (void) sprintf((char *)buf
, "%d%s",
321 Numb
[i
].val
, (char *)s1
+1);
322 (void) strcpy((char *)&xbuf
[1],
327 * Change size - "\\s[+-][0-9]" - NOP
331 if (*s1
== '+' || *s1
== '-')
333 while (*s1
&& isdigit(*s1
))
344 * Interpolate string - "\\*(NN" or "\\*N"
347 s1
= Asmcode(&s1
, nm
);
348 s2
= Findstr(nm
, NULL
, 0);
350 if ((strlen((char *)s2
)
351 + strlen((char *)s1
+1) + 1)
354 (void) sprintf((char *)buf
, "%s%s",
355 (char *)s2
, (char *)s1
+1);
356 (void) strcpy((char *)&xbuf
[1],
362 * Discretionary hyphen - "\\%"
367 if ((i
= Findhy(NULL
, 0, 0)) < 0) {
369 " no hyphen for font ",
374 Pass3(Wordl
, Word
, NULL
, Hychar
[i
].len
);
376 (unsigned char *) "hyphen",
377 Hychar
[i
].str
, Hychar
[i
].len
);
382 * None of the above - may be special character
387 s1
= Asmcode(&s1
, nm
);
388 if ((i
= Findchar(nm
, 0, NULL
, 0)) < 0){
392 if (strcmp((char *)nm
, "em") == 0
395 * "\\(em" is a special case when a word
396 * has been assembled, because of
400 Pass3(Wordl
, Word
, NULL
,
403 (unsigned char *)"userhyphen",
404 Schar
[i
].str
, Schar
[i
].len
);
410 * Interpolate a special character
412 if (Str2word(Schar
[i
].str
,
413 strlen((char *)Schar
[i
].str
)) != 0)
415 Wordl
+= Schar
[i
].len
;
424 if (Str2word(s1
, 1) != 0)
427 if (*s1
== '.' || *s1
== '!'
428 || *s1
== '?' || *s1
== ':')
430 else if (period
== '.') {
433 if (regexec(Pat
[13].pat
, nm
) == 0)
439 * End of line processing
443 Pass3(NOBREAK
, (unsigned char *)"gap", NULL
, 2);
445 Pass3(DOBREAK
, (unsigned char *)"center", NULL
,
449 Pass3(DOBREAK
, (unsigned char *)"flush", NULL
,
454 /* else fall through to process an "after next trap */
457 * Special -man macro handling.
459 if (Marg
== MANMACROS
) {
461 * A text line - "^[^.]" - is only processed when there is an
462 * "after next" directive.
464 if (*line
!= '.' && *line
!= '\'') {
465 if (Aftnxt
!= NULL
) {
466 if (regexec(Pat
[9].pat
, Aftnxt
)) /* ",fP" */
468 if (regexec(Pat
[16].pat
, Aftnxt
)) /* ",fR" */
470 if (regexec(Pat
[10].pat
, Aftnxt
)) /* ",tP" */
472 (unsigned char *)"toindent",
479 * Special footer handling - "^.lF"
481 if (line
[1] == 'l' && line
[2] == 'F') {
482 s1
= Findstr((unsigned char *)"by", NULL
, 0);
483 s2
= Findstr((unsigned char *)"nb", NULL
, 0);
484 if (*s1
== '\0' || *s2
== '\0')
485 (void) sprintf((char *)buf
, "%s%s",
486 (char *)s1
, (char *)s2
);
488 (void) sprintf((char *)buf
, "%s; %s",
489 (char *)s1
, (char *)s2
);
490 Pass3(NOBREAK
, (unsigned char *)"LF", buf
, 0);
495 * Special -ms macro handling.
497 if (Marg
== MSMACROS
) {
499 * A text line - "^[^.]" - is only processed when there is an
500 * "after next" directive.
502 if (*line
!= '.' && *line
!= '\'') {
503 if (Aftnxt
!= NULL
) {
504 if (regexec(Pat
[10].pat
, Aftnxt
)) /* ",tP" */
506 (unsigned char *)"toindent",
513 * Numbered headings - "^[.']nH"
515 if (line
[1] == 'n' && line
[2] == 'H') {
516 s1
= Field(2, line
, 0);
518 i
= atoi((char *)s1
) - 1;
520 for (j
= 0; j
< MAXNHNR
; j
++) {
524 } else if (i
>= MAXNHNR
) {
525 (void) sprintf((char *)buf
,
526 " over NH limit (%d)", MAXNHNR
);
527 Error(WARN
, LINE
, (char *)buf
, NULL
);
532 for (j
= i
+ 1; j
< MAXNHNR
; j
++) {
536 for (j
= 0; j
<= i
; j
++) {
537 (void) sprintf((char *)s1
, "%d.", Nhnr
[j
]);
538 s1
= buf
+ strlen((char *)buf
);
540 (void) Findstr((unsigned char *)"Nh", buf
, 1);
545 * Remaining lines should begin with a '.' or '\'' unless an "after next"
548 if (*line
!= '.' && *line
!= '\'') {
550 Error(WARN
, LINE
, " failed .it: ", (char *)Aftnxt
);
552 Error(WARN
, LINE
, " unrecognized line ", NULL
);
555 brk
= (*line
== '.') ? DOBREAK
: NOBREAK
;
557 * Evaluate expressions for "^[.'](ta|ll|ls|in|ti|po|ne|sp|pl|nr)"
558 * Then process the requests.
560 if (regexec(Pat
[11].pat
, &line
[1])) {
562 * Establish default scale factor.
564 if ((line
[1] == 'n' && line
[2] == 'e')
565 || (line
[1] == 's' && line
[2] == 'p')
566 || (line
[1] == 'p' && line
[2] == 'l'))
568 else if (line
[1] == 'n' && line
[2] == 'r')
573 * Determine starting argument.
575 if (line
[1] == 'n' && line
[2] == 'r')
576 s1
= Field(2, &line
[3], 0);
578 s1
= Field(1, &line
[3], 0);
580 * Evaluate expressions.
582 for (nexpr
= 0; s1
!= NULL
&&*s1
!= '\0'; ) {
583 while (*s1
== ' ' || *s1
== '\t')
585 if (*s1
== '+' || *s1
== '-')
596 while (c
== '+' || c
== '*' || c
== '%'
597 || c
== ')' || c
== '-' || c
== '/') {
603 * Pop stack on right parenthesis.
613 " expression stack underflow", NULL
);
618 * Push stack on left parenthesis.
620 } else if (*s1
== '(') {
624 " expression stack overflow", NULL
);
632 } else if (*s1
== '\\') {
636 * "\\"" begins a comment.
643 * Crude width calculation for "\\w"
649 while (*s1
&& *s1
!= *s2
)
651 tval
= (double) (s1
- s2
- 1) * Scalen
;
657 * Interpolate number register if "\\n".
660 s1
= Asmcode(&s1
, nm
);
661 if ((i
= Findnum(nm
, 0, 0)) >= 0)
666 * Assemble numeric value.
668 } else if (*s1
== '.' || isdigit(*s1
)) {
669 for (i
= 0; isdigit(*s1
) || *s1
== '.'; s1
++) {
674 d
= (double) (*s1
- '0');
676 tval
= tval
+ (d
/ (double) i
);
679 tval
= (tval
* 10.0) + d
;
683 * It's not an expression. Ignore extra scale.
685 if ((i
= Findscale((int)*s1
, 0.0, 0)) < 0) {
686 (void) sprintf((char *)buf
,
687 " \"%s\" isn't an expression",
689 Error(WARN
, LINE
, (char *)buf
, NULL
);
694 * Add term to expression value.
696 if ((i
= Findscale((int)*s1
, 0.0, 0)) >= 0) {
697 tval
*= Scale
[i
].val
;
717 (*s1
== '/') ? "div" : "mod",
722 val
= (double) (i
/ j
);
724 val
= (double) (i
% j
);
730 * Save expression value and sign.
732 if (nexpr
>= MAXEXP
) {
733 (void) sprintf((char *)buf
,
734 " at expression limit of %d", MAXEXP
);
735 Error(WARN
, LINE
, (char *)buf
, NULL
);
738 exsign
[nexpr
] = ssign
;
741 sexpr
[nexpr
] = -1.0 * val
;
745 while (*s1
== ' ' || *s1
== '\t')
749 * Set parameters "(ll|ls|in|ti|po|pl)"
751 if (regexec(Pat
[12].pat
, &line
[1])) {
754 if ((i
= Findparms(nm
)) < 0) {
756 " can't find parameter register ",
760 if (nexpr
== 0 || exscale
== 0.0)
762 else if (exsign
[0] == '\0'
763 || (nm
[0] == 't' && nm
[1] == 'i'))
764 j
= (int)(sexpr
[0] / exscale
);
766 j
= Parms
[i
].val
+ (int)(sexpr
[0] / exscale
);
767 Parms
[i
].prev
= Parms
[i
].val
;
769 nm
[0] = (nexpr
) ? exsign
[0] : '\0'; /* for .ti */
771 Pass3(brk
, (unsigned char *)Parms
[i
].cmd
, nm
, j
);
774 if (line
[1] == 'n') {
777 * Need - "^[.']ne <expression>"
780 if (nexpr
&& Scalev
> 0.0)
781 i
= (int) ((expr
[0]/Scalev
) + 0.99);
784 Pass3(DOBREAK
, (unsigned char *)"need", NULL
,
788 * Number - "^[.']nr <name> <expression>"
791 if ((s1
= Field(2, line
, 0)) == NULL
) {
792 Error(WARN
, LINE
, " bad number register",
796 if ((i
= Findnum(s1
, 0, 0)) < 0)
797 i
= Findnum(s1
, 0, 1);
802 if (exsign
[0] == '\0')
803 Numb
[i
].val
= (int) expr
[0];
805 Numb
[i
].val
+= (int) sexpr
[0];
810 * Space - "^[.']sp <expression>"
812 if (line
[1] == 's' && line
[2] == 'p') {
816 i
= (int)((expr
[0] / Scalev
) + 0.99);
818 Pass3(brk
, (unsigned char *)"space", NULL
, 0);
822 * Tab positions - "^[.']ta <pos1> <pos2> . . ."
824 if (line
[1] == 't' && line
[2] == 'a') {
826 for (j
= 0; j
< nexpr
; j
++) {
827 if (exsign
[j
] == '\0')
831 Tabs
[j
] = (int) (tval
/ Scalen
);
838 * Process all other nroff requests via Nreq().
840 (void) Nreq(line
, brk
);