<sys/ioccom.h>, <sys/ioctl.h>
[minix3.git] / commands / cawf / pass2.c
blob83e665fc202acee126fe1bd829553870ccc6ffa9
1 /*
2 * pass2.c - cawf(1) pass 2 function
3 */
5 /*
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
22 * documentation.
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.
31 #include "cawf.h"
32 #include <ctype.h>
35 * Pass2(line) - process the nroff requests in a line and break
36 * text into words for pass 3
39 void
40 Pass2(line)
41 unsigned char *line;
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 */
69 if (line == NULL) {
71 * End of macro expansion.
73 Pass3(DOBREAK, (unsigned char *)"need", NULL, 999);
74 return;
77 * Adjust line number.
79 if (Lockil == 0)
80 P2il++;
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);
87 return;
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>".
109 for (s1 = line;;) {
110 while (*s1 && *s1 == ' ')
111 s1++;
112 if (*s1 == '\0')
113 break;
114 for (s2 = s1, s3 = buf; *s2 && *s2 != ' ';)
115 *s3++ = Trtbl[(int)*s2++];
116 *s3 = '\0';
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);
126 if (Centering > 0) {
127 Pass3(DOBREAK,(unsigned char *)"center", NULL,
129 Centering--;
130 } else if (Fill == 0)
131 Pass3(DOBREAK, (unsigned char *)"flush", NULL,
133 return;
136 * Line must be scanned a character at a time.
138 inword = nsp = tabpos = 0;
139 period = '\0';
140 for (s1 = line;; s1++) {
142 * Space or TAB causes state transition.
144 if (*s1 == '\0' || *s1 == ' ' || *s1 == '\t') {
145 if (inword) {
146 if (!Backc) {
147 Endword();
148 Pass3(Wordl, Word, NULL, 0);
149 if (Uhyph) {
150 Pass3(NOBREAK,
151 (unsigned char *)"nohyphen",
152 NULL, 0);
155 inword = 0;
156 nsp = 0;
158 if (*s1 == '\0')
159 break;
160 } else {
161 if (inword == 0) {
162 if (Backc == 0) {
163 Wordl = Wordx = 0;
164 Uhyph = 0;
166 Backc = 0;
167 inword = 1;
168 if (nsp > 1) {
169 Pass3(NOBREAK,
170 (unsigned char *)"gap",
171 NULL, nsp);
176 * Process a character.
178 switch (*s1) {
180 * Space
182 case ' ':
183 nsp++;
184 period = '\0';
185 break;
187 * TAB
189 case '\t':
190 tabpos++;
191 if (tabpos <= Ntabs) {
192 Pass3(NOBREAK,
193 (unsigned char *)"tabto", NULL,
194 Tabs[tabpos-1]);
196 nsp = 0;
197 period = '\0';
198 break;
200 * Hyphen if word is being assembled
202 case '-':
203 if (Wordl <= 0)
204 goto ordinary_char;
205 if ((i = Findhy(NULL, 0, 0)) < 0) {
206 Error(WARN, LINE, " no hyphen for font ",
207 (char *)Font);
208 return;
210 Endword();
211 Pass3(Wordl, Word, NULL, Hychar[i].len);
212 Pass3(NOBREAK, (unsigned char *)"userhyphen",
213 Hychar[i].str, Hychar[i].len);
214 Wordl = Wordx = 0;
215 period = '\0';
216 Uhyph = 1;
217 break;
219 * Backslash
221 case '\\':
222 s1++;
223 switch(*s1) {
225 * Comment - "\\\""
227 case '"':
228 while (*(s1+1))
229 s1++;
230 break;
232 * Change font - "\\fN"
234 case 'f':
235 s1 = Asmcode(&s1, nm);
236 if (nm[0] == 'P') {
237 Font[0] = Prevfont;
238 break;
240 for (i = 0; Fcode[i].nm; i++) {
241 if (Fcode[i].nm == nm[0])
242 break;
244 if (Fcode[i].nm == '\0'
245 || nm[1] != '\0') {
246 Error(WARN, LINE, " unknown font ",
247 (char *)nm);
248 break;
250 if (Fcode[i].status != '1') {
251 Error(WARN, LINE,
252 " font undefined ", (char *)nm);
253 break;
254 } else {
255 Prevfont = Font[0];
256 Font[0] = nm[0];
258 break;
260 * Positive horizontal motion - "\\h\\n(NN" or
261 * "\\h\\nN"
263 case 'h':
264 if (s1[1] != '\\' || s1[2] != 'n') {
265 Error(WARN, LINE,
266 " no \\n after \\h", NULL);
267 break;
269 s1 +=2;
270 s1 = Asmcode(&s1, nm);
271 if ((i = Findnum(nm, 0, 0)) < 0)
272 goto unknown_num;
273 if ((j = Numb[i].val) < 0) {
274 Error(WARN, LINE, " \\h < 0 ",
275 NULL);
276 break;
278 if (j == 0)
279 break;
280 if ((strlen((char *)s1+1) + j + 1)
281 >= MAXLINE)
282 goto line_too_long;
283 for (s2 = &xbuf[1]; j; j--)
284 *s2++ = ' ';
285 (void) strcpy((char *)s2, (char *)s1+1);
286 s1 = xbuf;
287 break;
289 * Save current position in register if "\\k<reg>"
291 case 'k':
292 s1 = Asmcode(&s1, nm);
293 if ((i = Findnum(nm, 0, 0)) < 0)
294 i = Findnum(nm, 0, 1);
295 Numb[i].val =
296 (int)((double)Outll * Scalen);
297 break;
299 * Interpolate number - "\\n(NN" or "\\nN"
301 case 'n':
302 s1 = Asmcode(&s1, nm);
303 if ((i = Findnum(nm, 0, 0)) < 0) {
304 unknown_num:
305 Error(WARN, LINE,
306 " unknown number register ",
307 (char *)nm);
308 break;
310 (void) sprintf((char *)buf, "%d",
311 Numb[i].val);
312 if ((strlen((char *)buf)
313 + strlen((char *)s1+1) + 1)
314 >= MAXLINE) {
315 line_too_long:
316 Error(WARN, LINE, " line too long",
317 NULL);
318 break;
320 (void) sprintf((char *)buf, "%d%s",
321 Numb[i].val, (char *)s1+1);
322 (void) strcpy((char *)&xbuf[1],
323 (char *)buf);
324 s1 = xbuf;
325 break;
327 * Change size - "\\s[+-][0-9]" - NOP
329 case 's':
330 s1++;
331 if (*s1 == '+' || *s1 == '-')
332 s1++;
333 while (*s1 && isdigit(*s1))
334 s1++;
335 s1--;
336 break;
338 * Continue - "\\c"
340 case 'c':
341 Backc = 1;
342 break;
344 * Interpolate string - "\\*(NN" or "\\*N"
346 case '*':
347 s1 = Asmcode(&s1, nm);
348 s2 = Findstr(nm, NULL, 0);
349 if (*s2 != '\0') {
350 if ((strlen((char *)s2)
351 + strlen((char *)s1+1) + 1)
352 >= MAXLINE)
353 goto line_too_long;
354 (void) sprintf((char *)buf, "%s%s",
355 (char *)s2, (char *)s1+1);
356 (void) strcpy((char *)&xbuf[1],
357 (char *)buf);
358 s1 = xbuf;
360 break;
362 * Discretionary hyphen - "\\%"
364 case '%':
365 if (Wordl <= 0)
366 break;
367 if ((i = Findhy(NULL, 0, 0)) < 0) {
368 Error(WARN, LINE,
369 " no hyphen for font ",
370 (char *)Font);
371 break;
373 Endword();
374 Pass3(Wordl, Word, NULL, Hychar[i].len);
375 Pass3(NOBREAK,
376 (unsigned char *) "hyphen",
377 Hychar[i].str, Hychar[i].len);
378 Wordl = Wordx = 0;
379 Uhyph = 1;
380 break;
382 * None of the above - may be special character
383 * name.
385 default:
386 s2 = s1--;
387 s1 = Asmcode(&s1, nm);
388 if ((i = Findchar(nm, 0, NULL, 0)) < 0){
389 s1 = s2;
390 goto ordinary_char;
392 if (strcmp((char *)nm, "em") == 0
393 && Wordx > 0) {
395 * "\\(em" is a special case when a word
396 * has been assembled, because of
397 * hyphenation.
399 Endword();
400 Pass3(Wordl, Word, NULL,
401 Schar[i].len);
402 Pass3(NOBREAK,
403 (unsigned char *)"userhyphen",
404 Schar[i].str, Schar[i].len);
405 Wordl = Wordx = 0;
406 period = '\0';
407 Uhyph = 1;
410 * Interpolate a special character
412 if (Str2word(Schar[i].str,
413 strlen((char *)Schar[i].str)) != 0)
414 return;
415 Wordl += Schar[i].len;
416 period = '\0';
418 break;
420 * Ordinary character
422 default:
423 ordinary_char:
424 if (Str2word(s1, 1) != 0)
425 return;
426 Wordl++;
427 if (*s1 == '.' || *s1 == '!'
428 || *s1 == '?' || *s1 == ':')
429 period = '.';
430 else if (period == '.') {
431 nm[0] = *s1;
432 nm[1] = '\0';
433 if (regexec(Pat[13].pat, nm) == 0)
434 period = '\0';
439 * End of line processing
441 if (!Backc) {
442 if (period == '.')
443 Pass3(NOBREAK, (unsigned char *)"gap", NULL, 2);
444 if (Centering > 0) {
445 Pass3(DOBREAK, (unsigned char *)"center", NULL,
447 Centering--;
448 } else if (!Fill)
449 Pass3(DOBREAK, (unsigned char *)"flush", NULL,
452 if (Aftnxt == NULL)
453 return;
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" */
467 Font[0] = Prevfont;
468 if (regexec(Pat[16].pat, Aftnxt)) /* ",fR" */
469 Font[0] = 'R';
470 if (regexec(Pat[10].pat, Aftnxt)) /* ",tP" */
471 Pass3(DOBREAK,
472 (unsigned char *)"toindent",
473 NULL, 0);
474 Free(&Aftnxt);
476 return;
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);
487 else
488 (void) sprintf((char *)buf, "%s; %s",
489 (char *)s1, (char *)s2);
490 Pass3(NOBREAK, (unsigned char *)"LF", buf, 0);
491 return;
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" */
505 Pass3(DOBREAK,
506 (unsigned char *)"toindent",
507 NULL, 0);
508 Free(&Aftnxt);
510 return;
513 * Numbered headings - "^[.']nH"
515 if (line[1] == 'n' && line[2] == 'H') {
516 s1 = Field(2, line, 0);
517 if (s1 != NULL) {
518 i = atoi((char *)s1) - 1;
519 if (i < 0) {
520 for (j = 0; j < MAXNHNR; j++) {
521 Nhnr[j] = 0;
523 i = 0;
524 } else if (i >= MAXNHNR) {
525 (void) sprintf((char *)buf,
526 " over NH limit (%d)", MAXNHNR);
527 Error(WARN, LINE, (char *)buf, NULL);
529 } else
530 i = 0;
531 Nhnr[i]++;
532 for (j = i + 1; j < MAXNHNR; j++) {
533 Nhnr[j] = 0;
535 s1 = buf;
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);
541 return;
545 * Remaining lines should begin with a '.' or '\'' unless an "after next"
546 * trap has failed.
548 if (*line != '.' && *line != '\'') {
549 if (Aftnxt != NULL)
550 Error(WARN, LINE, " failed .it: ", (char *)Aftnxt);
551 else
552 Error(WARN, LINE, " unrecognized line ", NULL);
553 return;
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'))
567 exscale = Scalev;
568 else if (line[1] == 'n' && line[2] == 'r')
569 exscale = Scaleu;
570 else
571 exscale = Scalen;
573 * Determine starting argument.
575 if (line[1] == 'n' && line[2] == 'r')
576 s1 = Field(2, &line[3], 0);
577 else
578 s1 = Field(1, &line[3], 0);
580 * Evaluate expressions.
582 for (nexpr = 0; s1 != NULL &&*s1 != '\0'; ) {
583 while (*s1 == ' ' || *s1 == '\t')
584 s1++;
585 if (*s1 == '+' || *s1 == '-')
586 ssign = *s1++;
587 else
588 ssign = '\0';
590 * Process terms.
592 val = 0.0;
593 sp = -1;
594 c = '+';
595 s1--;
596 while (c == '+' || c == '*' || c == '%'
597 || c == ')' || c == '-' || c == '/') {
598 op = c;
599 s1++;
600 tscale = exscale;
601 tval = 0.0;
603 * Pop stack on right parenthesis.
605 if (op == ')') {
606 tval = val;
607 if (sp >= 0) {
608 val = valstack[sp];
609 op = opstack[sp];
610 sp--;
611 } else {
612 Error(WARN, LINE,
613 " expression stack underflow", NULL);
614 return;
616 tscale = Scaleu;
618 * Push stack on left parenthesis.
620 } else if (*s1 == '(') {
621 sp++;
622 if (sp >= MAXSP) {
623 Error(WARN, LINE,
624 " expression stack overflow", NULL);
625 return;
627 valstack[sp] = val;
628 opstack[sp] = op;
629 val = 0.0;
630 c = '+';
631 continue;
632 } else if (*s1 == '\\') {
633 s1++;
634 switch(*s1) {
636 * "\\"" begins a comment.
638 case '"':
639 while (*s1)
640 s1++;
641 break;
643 * Crude width calculation for "\\w"
645 case 'w':
646 s2 = ++s1;
647 if (*s1) {
648 s1++;
649 while (*s1 && *s1 != *s2)
650 s1++;
651 tval = (double) (s1 - s2 - 1) * Scalen;
652 if (*s1)
653 s1++;
655 break;
657 * Interpolate number register if "\\n".
659 case 'n':
660 s1 = Asmcode(&s1, nm);
661 if ((i = Findnum(nm, 0, 0)) >= 0)
662 tval = Numb[i].val;
663 s1++;
666 * Assemble numeric value.
668 } else if (*s1 == '.' || isdigit(*s1)) {
669 for (i = 0; isdigit(*s1) || *s1 == '.'; s1++) {
670 if (*s1 == '.') {
671 i = 10;
672 continue;
674 d = (double) (*s1 - '0');
675 if (i) {
676 tval = tval + (d / (double) i);
677 i = i * 10;
678 } else
679 tval = (tval * 10.0) + d;
681 } else {
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",
688 (char *)s1);
689 Error(WARN, LINE, (char *)buf, NULL);
691 s1++;
694 * Add term to expression value.
696 if ((i = Findscale((int)*s1, 0.0, 0)) >= 0) {
697 tval *= Scale[i].val;
698 s1++;
699 } else
700 tval *= tscale;
701 switch (op) {
702 case '+':
703 val += tval;
704 break;
705 case '-':
706 val -= tval;
707 break;
708 case '*':
709 val *= tval;
710 break;
711 case '/':
712 case '%':
713 i = (int) val;
714 j = (int) tval;
715 if (j == 0) {
716 Error(WARN, LINE,
717 (*s1 == '/') ? "div" : "mod",
718 " by 0");
719 return;
721 if (op == '/')
722 val = (double) (i / j);
723 else
724 val = (double) (i % j);
725 break;
727 c = *s1;
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);
736 return;
738 exsign[nexpr] = ssign;
739 expr[nexpr] = val;
740 if (ssign == '-')
741 sexpr[nexpr] = -1.0 * val;
742 else
743 sexpr[nexpr] = val;
744 nexpr++;
745 while (*s1 == ' ' || *s1 == '\t')
746 s1++;
749 * Set parameters "(ll|ls|in|ti|po|pl)"
751 if (regexec(Pat[12].pat, &line[1])) {
752 nm[0] = line[1];
753 nm[1] = line[2];
754 if ((i = Findparms(nm)) < 0) {
755 Error(WARN, LINE,
756 " can't find parameter register ",
757 (char *)nm);
758 return;
760 if (nexpr == 0 || exscale == 0.0)
761 j = Parms[i].prev;
762 else if (exsign[0] == '\0'
763 || (nm[0] == 't' && nm[1] == 'i'))
764 j = (int)(sexpr[0] / exscale);
765 else
766 j = Parms[i].val + (int)(sexpr[0] / exscale);
767 Parms[i].prev = Parms[i].val;
768 Parms[i].val = j;
769 nm[0] = (nexpr) ? exsign[0] : '\0'; /* for .ti */
770 nm[1] = '\0';
771 Pass3(brk, (unsigned char *)Parms[i].cmd, nm, j);
772 return;
774 if (line[1] == 'n') {
775 switch(line[2]) {
777 * Need - "^[.']ne <expression>"
779 case 'e':
780 if (nexpr && Scalev > 0.0)
781 i = (int) ((expr[0]/Scalev) + 0.99);
782 else
783 i = 0;
784 Pass3(DOBREAK, (unsigned char *)"need", NULL,
786 return;
788 * Number - "^[.']nr <name> <expression>"
790 case 'r':
791 if ((s1 = Field(2, line, 0)) == NULL) {
792 Error(WARN, LINE, " bad number register",
793 NULL);
794 return;
796 if ((i = Findnum(s1, 0, 0)) < 0)
797 i = Findnum(s1, 0, 1);
798 if (nexpr < 1) {
799 Numb[i].val = 0;
800 return;
802 if (exsign[0] == '\0')
803 Numb[i].val = (int) expr[0];
804 else
805 Numb[i].val += (int) sexpr[0];
806 return;
810 * Space - "^[.']sp <expression>"
812 if (line[1] == 's' && line[2] == 'p') {
813 if (nexpr == 0)
814 i = 1;
815 else
816 i = (int)((expr[0] / Scalev) + 0.99);
817 while (i--)
818 Pass3(brk, (unsigned char *)"space", NULL, 0);
819 return;
822 * Tab positions - "^[.']ta <pos1> <pos2> . . ."
824 if (line[1] == 't' && line[2] == 'a') {
825 tval = 0.0;
826 for (j = 0; j < nexpr; j++) {
827 if (exsign[j] == '\0')
828 tval = expr[j];
829 else
830 tval += sexpr[j];
831 Tabs[j] = (int) (tval / Scalen);
833 Ntabs = nexpr;
834 return;
838 * Process all other nroff requests via Nreq().
840 (void) Nreq(line, brk);
841 return;