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
39 void Pass2(unsigned char *line
) {
40 int brk
; /* request break status */
41 unsigned char buf
[MAXLINE
]; /* working buffer */
42 unsigned char c
; /* character buffer */
43 double d
; /* temporary double */
44 double exscale
; /* expression scaling factor */
45 double expr
[MAXEXP
]; /* expressions */
46 unsigned char exsign
[MAXEXP
]; /* expression signs */
47 int i
, j
; /* temporary indexes */
48 int inword
; /* word processing status */
49 int nexpr
; /* number of expressions */
50 unsigned char nm
[4], nm1
[4]; /* names */
51 int nsp
; /* number of spaces */
52 unsigned char op
; /* expression term operator */
53 unsigned char opstack
[MAXSP
]; /* expression operation stack */
54 unsigned char period
; /* end of word status */
55 unsigned char *s1
, *s2
, *s3
; /* temporary string pointers */
56 double sexpr
[MAXEXP
]; /* signed expressions */
57 int sp
; /* expression stack pointer */
58 unsigned char ssign
; /* expression's starting sign */
59 int tabpos
; /* tab position */
60 double tscale
; /* term scaling factor */
61 double tval
; /* term value */
62 double val
; /* term value */
63 double valstack
[MAXSP
]; /* expression value stack */
64 unsigned char xbuf
[MAXLINE
]; /* expansion buffer */
68 * End of macro expansion.
70 Pass3(DOBREAK
, (unsigned char *)"need", NULL
, 999);
79 * Empty line - "^[ \t]*$" or "^\\\"".
81 if (regexec(Pat
[6].pat
, line
)
82 || strncmp((char *)line
, "\\\"", 2) == 0) {
83 Pass3(DOBREAK
, (unsigned char *)"space", NULL
, 0);
87 * Line begins with white space.
89 if (*line
== ' ' || *line
== '\t') {
90 Pass3(DOBREAK
, (unsigned char *)"flush", NULL
, 0);
91 Pass3(0, (unsigned char *)"", NULL
, 0);
93 if (*line
!= '.' && *line
!= '\'') {
95 * Line contains text (not an nroff request).
97 if (Font
[0] == 'R' && Backc
== 0 && Aftnxt
== NULL
98 && regexec(Pat
[7].pat
, line
) == 0) {
100 * The font is Roman, there is no "\\c" or "after next"
101 * trap pending and and the line has no '\\', '\t', '-',
102 * or " " (regular expression "\\|\t|-| ").
104 * Output each word of the line as "<length> <word>".
111 for (s2
= s1
, s3
= buf
; *s2
&& *s2
!= ' ';)
112 *s3
++ = Trtbl
[(int)*s2
++];
114 Pass3((s2
- s1
), buf
, NULL
, 0);
115 s1
= *s2
? ++s2
: s2
;
118 * Line terminates with punctuation and optional
119 * bracketing (regular expression "[.!?:][\])'\"*]*$").
121 if (regexec(Pat
[8].pat
, line
))
122 Pass3(NOBREAK
, (unsigned char *)"gap", NULL
, 2);
124 Pass3(DOBREAK
,(unsigned char *)"center", NULL
,
127 } else if (Fill
== 0)
128 Pass3(DOBREAK
, (unsigned char *)"flush", NULL
,
133 * Line must be scanned a character at a time.
135 inword
= nsp
= tabpos
= 0;
137 for (s1
= line
;; s1
++) {
139 * Space or TAB causes state transition.
141 if (*s1
== '\0' || *s1
== ' ' || *s1
== '\t') {
145 Pass3(Wordl
, Word
, NULL
, 0);
148 (unsigned char *)"nohyphen",
167 (unsigned char *)"gap",
173 * Process a character.
188 if (tabpos
<= Ntabs
) {
190 (unsigned char *)"tabto", NULL
,
197 * Hyphen if word is being assembled
202 if ((i
= Findhy(NULL
, 0, 0)) < 0) {
203 Error(WARN
, LINE
, " no hyphen for font ",
208 Pass3(Wordl
, Word
, NULL
, Hychar
[i
].len
);
209 Pass3(NOBREAK
, (unsigned char *)"userhyphen",
210 Hychar
[i
].str
, Hychar
[i
].len
);
229 * Change font - "\\fN"
232 s1
= Asmcode(&s1
, nm
);
237 for (i
= 0; Fcode
[i
].nm
; i
++) {
238 if (Fcode
[i
].nm
== nm
[0])
241 if (Fcode
[i
].nm
== '\0'
243 Error(WARN
, LINE
, " unknown font ",
247 if (Fcode
[i
].status
!= '1') {
249 " font undefined ", (char *)nm
);
257 * Positive horizontal motion - "\\h\\n(NN" or
261 if (s1
[1] != '\\' || s1
[2] != 'n') {
263 " no \\n after \\h", NULL
);
267 s1
= Asmcode(&s1
, nm
);
268 if ((i
= Findnum(nm
, 0, 0)) < 0)
270 if ((j
= Numb
[i
].val
) < 0) {
271 Error(WARN
, LINE
, " \\h < 0 ",
277 if ((strlen((char *)s1
+1) + j
+ 1)
280 for (s2
= &xbuf
[1]; j
; j
--)
282 (void) strcpy((char *)s2
, (char *)s1
+1);
286 * Save current position in register if "\\k<reg>"
289 s1
= Asmcode(&s1
, nm
);
290 if ((i
= Findnum(nm
, 0, 0)) < 0)
291 i
= Findnum(nm
, 0, 1);
293 (int)((double)Outll
* Scalen
);
296 * Interpolate number - "\\n(NN" or "\\nN"
299 s1
= Asmcode(&s1
, nm
);
300 if ((i
= Findnum(nm
, 0, 0)) < 0) {
303 " unknown number register ",
307 (void) sprintf((char *)buf
, "%d",
309 if ((strlen((char *)buf
)
310 + strlen((char *)s1
+1) + 1)
313 Error(WARN
, LINE
, " line too long",
317 (void) sprintf((char *)buf
, "%d%s",
318 Numb
[i
].val
, (char *)s1
+1);
319 (void) strcpy((char *)&xbuf
[1],
324 * Change size - "\\s[+-][0-9]" - NOP
328 if (*s1
== '+' || *s1
== '-')
330 while (*s1
&& isdigit(*s1
))
341 * Interpolate string - "\\*(NN" or "\\*N"
344 s1
= Asmcode(&s1
, nm
);
345 s2
= Findstr(nm
, NULL
, 0);
347 if ((strlen((char *)s2
)
348 + strlen((char *)s1
+1) + 1)
351 (void) sprintf((char *)buf
, "%s%s",
352 (char *)s2
, (char *)s1
+1);
353 (void) strcpy((char *)&xbuf
[1],
359 * Discretionary hyphen - "\\%"
364 if ((i
= Findhy(NULL
, 0, 0)) < 0) {
366 " no hyphen for font ",
371 Pass3(Wordl
, Word
, NULL
, Hychar
[i
].len
);
373 (unsigned char *) "hyphen",
374 Hychar
[i
].str
, Hychar
[i
].len
);
379 * None of the above - may be special character
384 s1
= Asmcode(&s1
, nm
);
385 if ((i
= Findchar(nm
, 0, NULL
, 0)) < 0){
389 if (strcmp((char *)nm
, "em") == 0
392 * "\\(em" is a special case when a word
393 * has been assembled, because of
397 Pass3(Wordl
, Word
, NULL
,
400 (unsigned char *)"userhyphen",
401 Schar
[i
].str
, Schar
[i
].len
);
407 * Interpolate a special character
409 if (Str2word(Schar
[i
].str
,
410 strlen((char *)Schar
[i
].str
)) != 0)
412 Wordl
+= Schar
[i
].len
;
421 if (Str2word(s1
, 1) != 0)
424 if (*s1
== '.' || *s1
== '!'
425 || *s1
== '?' || *s1
== ':')
427 else if (period
== '.') {
430 if (regexec(Pat
[13].pat
, nm
) == 0)
436 * End of line processing
440 Pass3(NOBREAK
, (unsigned char *)"gap", NULL
, 2);
442 Pass3(DOBREAK
, (unsigned char *)"center", NULL
,
446 Pass3(DOBREAK
, (unsigned char *)"flush", NULL
,
451 /* else fall through to process an "after next trap */
454 * Special -man macro handling.
456 if (Marg
== MANMACROS
) {
458 * A text line - "^[^.]" - is only processed when there is an
459 * "after next" directive.
461 if (*line
!= '.' && *line
!= '\'') {
462 if (Aftnxt
!= NULL
) {
463 if (regexec(Pat
[9].pat
, Aftnxt
)) /* ",fP" */
465 if (regexec(Pat
[16].pat
, Aftnxt
)) /* ",fR" */
467 if (regexec(Pat
[10].pat
, Aftnxt
)) /* ",tP" */
469 (unsigned char *)"toindent",
476 * Special footer handling - "^.lF"
478 if (line
[1] == 'l' && line
[2] == 'F') {
479 s1
= Findstr((unsigned char *)"by", NULL
, 0);
480 s2
= Findstr((unsigned char *)"nb", NULL
, 0);
481 if (*s1
== '\0' || *s2
== '\0')
482 (void) sprintf((char *)buf
, "%s%s",
483 (char *)s1
, (char *)s2
);
485 (void) sprintf((char *)buf
, "%s; %s",
486 (char *)s1
, (char *)s2
);
487 Pass3(NOBREAK
, (unsigned char *)"LF", buf
, 0);
492 * Special -ms macro handling.
494 if (Marg
== MSMACROS
) {
496 * A text line - "^[^.]" - is only processed when there is an
497 * "after next" directive.
499 if (*line
!= '.' && *line
!= '\'') {
500 if (Aftnxt
!= NULL
) {
501 if (regexec(Pat
[10].pat
, Aftnxt
)) /* ",tP" */
503 (unsigned char *)"toindent",
510 * Numbered headings - "^[.']nH"
512 if (line
[1] == 'n' && line
[2] == 'H') {
513 s1
= Field(2, line
, 0);
515 i
= atoi((char *)s1
) - 1;
517 for (j
= 0; j
< MAXNHNR
; j
++) {
521 } else if (i
>= MAXNHNR
) {
522 (void) sprintf((char *)buf
,
523 " over NH limit (%d)", MAXNHNR
);
524 Error(WARN
, LINE
, (char *)buf
, NULL
);
529 for (j
= i
+ 1; j
< MAXNHNR
; j
++) {
533 for (j
= 0; j
<= i
; j
++) {
534 (void) sprintf((char *)s1
, "%d.", Nhnr
[j
]);
535 s1
= buf
+ strlen((char *)buf
);
537 (void) Findstr((unsigned char *)"Nh", buf
, 1);
542 * Remaining lines should begin with a '.' or '\'' unless an "after next"
545 if (*line
!= '.' && *line
!= '\'') {
547 Error(WARN
, LINE
, " failed .it: ", (char *)Aftnxt
);
549 Error(WARN
, LINE
, " unrecognized line ", NULL
);
552 brk
= (*line
== '.') ? DOBREAK
: NOBREAK
;
554 * Evaluate expressions for "^[.'](ta|ll|ls|in|ti|po|ne|sp|pl|nr)"
555 * Then process the requests.
557 if (regexec(Pat
[11].pat
, &line
[1])) {
559 * Establish default scale factor.
561 if ((line
[1] == 'n' && line
[2] == 'e')
562 || (line
[1] == 's' && line
[2] == 'p')
563 || (line
[1] == 'p' && line
[2] == 'l'))
565 else if (line
[1] == 'n' && line
[2] == 'r')
570 * Determine starting argument.
572 if (line
[1] == 'n' && line
[2] == 'r')
573 s1
= Field(2, &line
[3], 0);
575 s1
= Field(1, &line
[3], 0);
577 * Evaluate expressions.
579 for (nexpr
= 0; s1
!= NULL
&&*s1
!= '\0'; ) {
580 while (*s1
== ' ' || *s1
== '\t')
582 if (*s1
== '+' || *s1
== '-')
593 while (c
== '+' || c
== '*' || c
== '%'
594 || c
== ')' || c
== '-' || c
== '/') {
600 * Pop stack on right parenthesis.
610 " expression stack underflow", NULL
);
615 * Push stack on left parenthesis.
617 } else if (*s1
== '(') {
621 " expression stack overflow", NULL
);
629 } else if (*s1
== '\\') {
633 * "\\"" begins a comment.
640 * Crude width calculation for "\\w"
646 while (*s1
&& *s1
!= *s2
)
648 tval
= (double) (s1
- s2
- 1) * Scalen
;
654 * Interpolate number register if "\\n".
657 s1
= Asmcode(&s1
, nm
);
658 if ((i
= Findnum(nm
, 0, 0)) >= 0)
663 * Assemble numeric value.
665 } else if (*s1
== '.' || isdigit(*s1
)) {
666 for (i
= 0; isdigit(*s1
) || *s1
== '.'; s1
++) {
671 d
= (double) (*s1
- '0');
673 tval
= tval
+ (d
/ (double) i
);
676 tval
= (tval
* 10.0) + d
;
680 * It's not an expression. Ignore extra scale.
682 if ((i
= Findscale((int)*s1
, 0.0, 0)) < 0) {
683 (void) sprintf((char *)buf
,
684 " \"%s\" isn't an expression",
686 Error(WARN
, LINE
, (char *)buf
, NULL
);
691 * Add term to expression value.
693 if ((i
= Findscale((int)*s1
, 0.0, 0)) >= 0) {
694 tval
*= Scale
[i
].val
;
714 (*s1
== '/') ? "div" : "mod",
719 val
= (double) (i
/ j
);
721 val
= (double) (i
% j
);
727 * Save expression value and sign.
729 if (nexpr
>= MAXEXP
) {
730 (void) sprintf((char *)buf
,
731 " at expression limit of %d", MAXEXP
);
732 Error(WARN
, LINE
, (char *)buf
, NULL
);
735 exsign
[nexpr
] = ssign
;
738 sexpr
[nexpr
] = -1.0 * val
;
742 while (*s1
== ' ' || *s1
== '\t')
746 * Set parameters "(ll|ls|in|ti|po|pl)"
748 if (regexec(Pat
[12].pat
, &line
[1])) {
751 if ((i
= Findparms(nm
)) < 0) {
753 " can't find parameter register ",
757 if (nexpr
== 0 || exscale
== 0.0)
759 else if (exsign
[0] == '\0'
760 || (nm
[0] == 't' && nm
[1] == 'i'))
761 j
= (int)(sexpr
[0] / exscale
);
763 j
= Parms
[i
].val
+ (int)(sexpr
[0] / exscale
);
764 Parms
[i
].prev
= Parms
[i
].val
;
766 nm
[0] = (nexpr
) ? exsign
[0] : '\0'; /* for .ti */
768 Pass3(brk
, (unsigned char *)Parms
[i
].cmd
, nm
, j
);
771 if (line
[1] == 'n') {
774 * Need - "^[.']ne <expression>"
777 if (nexpr
&& Scalev
> 0.0)
778 i
= (int) ((expr
[0]/Scalev
) + 0.99);
781 Pass3(DOBREAK
, (unsigned char *)"need", NULL
,
785 * Number - "^[.']nr <name> <expression>"
788 if ((s1
= Field(2, line
, 0)) == NULL
) {
789 Error(WARN
, LINE
, " bad number register",
793 if ((i
= Findnum(s1
, 0, 0)) < 0)
794 i
= Findnum(s1
, 0, 1);
799 if (exsign
[0] == '\0')
800 Numb
[i
].val
= (int) expr
[0];
802 Numb
[i
].val
+= (int) sexpr
[0];
807 * Space - "^[.']sp <expression>"
809 if (line
[1] == 's' && line
[2] == 'p') {
813 i
= (int)((expr
[0] / Scalev
) + 0.99);
815 Pass3(brk
, (unsigned char *)"space", NULL
, 0);
819 * Tab positions - "^[.']ta <pos1> <pos2> . . ."
821 if (line
[1] == 't' && line
[2] == 'a') {
823 for (j
= 0; j
< nexpr
; j
++) {
824 if (exsign
[j
] == '\0')
828 Tabs
[j
] = (int) (tval
/ Scalen
);
835 * Process all other nroff requests via Nreq().
837 (void) Nreq(line
, brk
);