xref: /minix/minix/commands/cawf/pass2.c (revision fb9c64b2)
1 /*
2  *	pass2.c - cawf(1) pass 2 function
3  */
4 
5 /*
6  *	Copyright (c) 1991 Purdue University Research Foundation,
7  *	West Lafayette, Indiana 47907.  All rights reserved.
8  *
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.
12  *
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:
16  *
17  *	1. The author is not responsible for any consequences of use of
18  *	   this software, even if they arise from flaws in it.
19  *
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.
23  *
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.
27  *
28  *	4. This notice may not be removed or altered.
29  */
30 
31 #include "cawf.h"
32 #include <ctype.h>
33 
34 /*
35  * Pass2(line) - process the nroff requests in a line and break
36  *		 text into words for pass 3
37  */
38 
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 */
65 
66 	if (line == NULL) {
67     /*
68      * End of macro expansion.
69      */
70 		Pass3(DOBREAK, (unsigned char *)"need", NULL, 999);
71 		return;
72 	}
73     /*
74      * Adjust line number.
75      */
76 	if (Lockil == 0)
77 		P2il++;
78     /*
79      * Empty line - "^[ \t]*$" or "^\\\"".
80      */
81 	if (regexec(Pat[6].pat, line)
82 	||  strncmp((char *)line, "\\\"", 2) == 0) {
83 		Pass3(DOBREAK, (unsigned char *)"space", NULL, 0);
84 		return;
85 	}
86     /*
87      * Line begins with white space.
88      */
89 	if (*line == ' ' || *line == '\t') {
90 		Pass3(DOBREAK, (unsigned char *)"flush", NULL, 0);
91 		Pass3(0, (unsigned char *)"", NULL, 0);
92 	}
93 	if (*line != '.' && *line != '\'') {
94     /*
95      * Line contains text (not an nroff request).
96      */
97 		if (Font[0] == 'R' && Backc == 0 && Aftnxt == NULL
98 		&&  regexec(Pat[7].pat, line) == 0) {
99 		    /*
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|-|  ").
103 		     *
104 		     * Output each word of the line as "<length> <word>".
105 		     */
106 			for (s1 = line;;) {
107 				while (*s1 == ' ')
108 					s1++;
109 				if (*s1 == '\0')
110 					break;
111 				for (s2 = s1, s3 = buf; *s2 && *s2 != ' ';)
112 				    *s3++ = Trtbl[(int)*s2++];
113 				*s3 = '\0';
114 				Pass3((s2 - s1), buf, NULL, 0);
115 				s1 = *s2 ? ++s2 : s2;
116 			}
117 		    /*
118 		     * Line terminates with punctuation and optional
119 		     * bracketing (regular expression "[.!?:][\])'\"*]*$").
120 		     */
121 			if (regexec(Pat[8].pat, line))
122 				Pass3(NOBREAK, (unsigned char *)"gap", NULL, 2);
123 			if (Centering > 0) {
124 				Pass3(DOBREAK,(unsigned char *)"center", NULL,
125 					0);
126 				Centering--;
127 			} else if (Fill == 0)
128 				Pass3(DOBREAK, (unsigned char *)"flush", NULL,
129 					0);
130 			return;
131 		}
132 	    /*
133 	     * Line must be scanned a character at a time.
134 	     */
135 		inword = nsp = tabpos = 0;
136 		period = '\0';
137 		for (s1 = line;; s1++) {
138 		    /*
139 		     * Space or TAB causes state transition.
140 		     */
141 			if (*s1 == '\0' || *s1 == ' ' || *s1 == '\t') {
142 				if (inword) {
143 					if (!Backc) {
144 						Endword();
145 						Pass3(Wordl, Word, NULL, 0);
146 						if (Uhyph) {
147 						  Pass3(NOBREAK,
148 						    (unsigned char *)"nohyphen",
149 						    NULL, 0);
150 						}
151 					}
152 					inword = 0;
153 					nsp = 0;
154 				}
155 				if (*s1 == '\0')
156 					break;
157 			} else {
158 				if (inword == 0) {
159 					if (Backc == 0) {
160 						Wordl = Wordx = 0;
161 						Uhyph = 0;
162 					}
163 					Backc = 0;
164 					inword = 1;
165 					if (nsp > 1) {
166 						Pass3(NOBREAK,
167 						    (unsigned char *)"gap",
168 						    NULL, nsp);
169 					}
170 				}
171 			}
172 		    /*
173 		     * Process a character.
174 		     */
175 			switch (*s1) {
176 		    /*
177 		     * Space
178 		     */
179 	     		case ' ':
180 				nsp++;
181 				period = '\0';
182 				break;
183 		    /*
184 		     * TAB
185 		     */
186 	     		case '\t':
187 				tabpos++;
188 				if (tabpos <= Ntabs) {
189 					Pass3(NOBREAK,
190 					    (unsigned char *)"tabto", NULL,
191 					    Tabs[tabpos-1]);
192 				}
193 				nsp = 0;
194 				period = '\0';
195 				break;
196 		    /*
197 		     * Hyphen if word is being assembled
198 		     */
199 			case '-':
200 				if (Wordl <= 0)
201 				    goto ordinary_char;
202 				if ((i = Findhy(NULL, 0, 0)) < 0) {
203 				    Error(WARN, LINE, " no hyphen for font ",
204 					(char *)Font);
205 				    return;
206 				}
207 				Endword();
208 				Pass3(Wordl, Word, NULL, Hychar[i].len);
209 				Pass3(NOBREAK, (unsigned char *)"userhyphen",
210 				    Hychar[i].str, Hychar[i].len);
211 				Wordl = Wordx = 0;
212 				period = '\0';
213 				Uhyph = 1;
214 				break;
215 		    /*
216 		     * Backslash
217 		     */
218 			case '\\':
219 				s1++;
220 				switch(*s1) {
221 			    /*
222 			     * Comment - "\\\""
223 			     */
224 				case '"':
225 					while (*(s1+1))
226 						s1++;
227 					break;
228 			    /*
229 			     * Change font - "\\fN"
230 			     */
231 				case 'f':
232 					s1 = Asmcode(&s1, nm);
233 					if (nm[0] == 'P') {
234 					    Font[0] = Prevfont;
235 					    break;
236 					}
237 					for (i = 0; Fcode[i].nm; i++) {
238 					    if (Fcode[i].nm == nm[0])
239 						break;
240 					}
241 					if (Fcode[i].nm == '\0'
242 					||  nm[1] != '\0') {
243 					    Error(WARN, LINE, " unknown font ",
244 					    	(char *)nm);
245 					    break;
246 					}
247 					if (Fcode[i].status != '1') {
248 					    Error(WARN, LINE,
249 						" font undefined ", (char *)nm);
250 					    break;
251 					} else {
252 					    Prevfont = Font[0];
253 					    Font[0] = nm[0];
254 					}
255 					break;
256 			    /*
257 			     * Positive horizontal motion - "\\h\\n(NN" or
258 			     * "\\h\\nN"
259 			     */
260 				case 'h':
261 					if (s1[1] != '\\' || s1[2] != 'n') {
262 					    Error(WARN, LINE,
263 						" no \\n after \\h", NULL);
264 					    break;
265 					}
266 					s1 +=2;
267 					s1 = Asmcode(&s1, nm);
268 					if ((i = Findnum(nm, 0, 0)) < 0)
269 						goto unknown_num;
270 					if ((j = Numb[i].val) < 0) {
271 					    Error(WARN, LINE, " \\h < 0 ",
272 					    NULL);
273 					    break;
274 					}
275 					if (j == 0)
276 						break;
277 					if ((strlen((char *)s1+1) + j + 1)
278 					>=  MAXLINE)
279 						goto line_too_long;
280 					for (s2 = &xbuf[1]; j; j--)
281 						*s2++ = ' ';
282 					(void) strcpy((char *)s2, (char *)s1+1);
283 					s1 = xbuf;
284 					break;
285 			    /*
286 			     * Save current position in register if "\\k<reg>"
287 			     */
288 			        case 'k':
289 					s1 = Asmcode(&s1, nm);
290 					if ((i = Findnum(nm, 0, 0)) < 0)
291 					    i = Findnum(nm, 0, 1);
292 					Numb[i].val =
293 						(int)((double)Outll * Scalen);
294 					break;
295 			    /*
296 			     * Interpolate number - "\\n(NN" or "\\nN"
297 			     */
298 				case 'n':
299 					s1 = Asmcode(&s1, nm);
300 					if ((i = Findnum(nm, 0, 0)) < 0) {
301 unknown_num:
302 					    Error(WARN, LINE,
303 					        " unknown number register ",
304 						(char *)nm);
305 					    break;
306 					}
307 					(void) sprintf((char *)buf, "%d",
308 					    Numb[i].val);
309 					if ((strlen((char *)buf)
310 					   + strlen((char *)s1+1) + 1)
311 					>=  MAXLINE) {
312 line_too_long:
313 					    Error(WARN, LINE, " line too long",
314 					        NULL);
315 					    break;
316 					}
317 					(void) sprintf((char *)buf, "%d%s",
318 					    Numb[i].val, (char *)s1+1);
319 					(void) strcpy((char *)&xbuf[1],
320 						(char *)buf);
321 				        s1 = xbuf;
322 					break;
323 			    /*
324 			     * Change size - "\\s[+-][0-9]" - NOP
325 			     */
326 				case 's':
327 					s1++;
328 					if (*s1 == '+' || *s1 == '-')
329 						s1++;
330 					while (*s1 && isdigit(*s1))
331 						s1++;
332 					s1--;
333 					break;
334 			    /*
335 			     * Continue - "\\c"
336 			     */
337 				case 'c':
338 					Backc = 1;
339 					break;
340 			    /*
341 			     * Interpolate string - "\\*(NN" or "\\*N"
342 			     */
343 				case '*':
344 					s1 = Asmcode(&s1, nm);
345 					s2 = Findstr(nm, NULL, 0);
346 					if (*s2 != '\0') {
347 					    if ((strlen((char *)s2)
348 					       + strlen((char *)s1+1) + 1)
349 					    >=  MAXLINE)
350 						goto line_too_long;
351 					    (void) sprintf((char *)buf, "%s%s",
352 						(char *)s2, (char *)s1+1);
353 					    (void) strcpy((char *)&xbuf[1],
354 						(char *)buf);
355 					    s1 = xbuf;
356 					}
357 					break;
358 			    /*
359 			     * Discretionary hyphen - "\\%"
360 			     */
361 				case '%':
362 					if (Wordl <= 0)
363 					    break;
364 					if ((i = Findhy(NULL, 0, 0)) < 0) {
365 					    Error(WARN, LINE,
366 					        " no hyphen for font ",
367 						(char *)Font);
368 					    break;
369 					}
370 					Endword();
371 					Pass3(Wordl, Word, NULL, Hychar[i].len);
372 					Pass3(NOBREAK,
373 					    (unsigned char *) "hyphen",
374 					    Hychar[i].str, Hychar[i].len);
375 					Wordl = Wordx = 0;
376 					Uhyph = 1;
377 					break;
378 			    /*
379 			     * None of the above - may be special character
380 			     * name.
381 			     */
382 				default:
383 					s2 = s1--;
384 					s1 = Asmcode(&s1, nm);
385 					if ((i = Findchar(nm, 0, NULL, 0)) < 0){
386 					    s1 = s2;
387 					    goto ordinary_char;
388 					}
389 					if (strcmp((char *)nm, "em") == 0
390 					&& Wordx > 0) {
391 				    /*
392 				     * "\\(em" is a special case when a word
393 				     * has been assembled, because of
394 				     * hyphenation.
395 				     */
396 					    Endword();
397 					    Pass3(Wordl, Word, NULL,
398 					        Schar[i].len);
399 					    Pass3(NOBREAK,
400 						(unsigned char *)"userhyphen",
401 					        Schar[i].str, Schar[i].len);
402 				            Wordl = Wordx = 0;
403 					    period = '\0';
404 					    Uhyph = 1;
405 			 		}
406 				    /*
407 				     * Interpolate a special character
408 				     */
409 					if (Str2word(Schar[i].str,
410 					    strlen((char *)Schar[i].str)) != 0)
411 						return;
412 				        Wordl += Schar[i].len;
413 					period = '\0';
414 				}
415 				break;
416 		    /*
417 		     * Ordinary character
418 		     */
419 			default:
420 ordinary_char:
421 				if (Str2word(s1, 1) != 0)
422 					return;
423 				Wordl++;
424 				if (*s1 == '.' || *s1 == '!'
425 				||  *s1 == '?' || *s1 == ':')
426 				    period = '.';
427 				else if (period == '.') {
428 				    nm[0] = *s1;
429 				    nm[1] = '\0';
430 				    if (regexec(Pat[13].pat, nm) == 0)
431 					 period = '\0';
432 				}
433 			}
434 		}
435 	    /*
436 	     * End of line processing
437 	     */
438      		if (!Backc) {
439 			if (period == '.')
440 				Pass3(NOBREAK, (unsigned char *)"gap", NULL, 2);
441 			if (Centering > 0) {
442 				Pass3(DOBREAK, (unsigned char *)"center", NULL,
443 				0);
444 				Centering--;
445 			} else if (!Fill)
446 				Pass3(DOBREAK, (unsigned char *)"flush", NULL,
447 				0);
448 		}
449 		if (Aftnxt == NULL)
450 			return;
451 		/* else fall through to process an "after next trap */
452 	}
453     /*
454      * Special -man macro handling.
455      */
456 	if (Marg == MANMACROS) {
457 	    /*
458 	     * A text line - "^[^.]" - is only processed when there is an
459 	     * "after next" directive.
460 	     */
461 		if (*line != '.' && *line != '\'') {
462 			if (Aftnxt != NULL) {
463 				if (regexec(Pat[9].pat, Aftnxt))  /* ",fP" */
464 					Font[0] = Prevfont;
465 				if (regexec(Pat[16].pat, Aftnxt))  /* ",fR" */
466 					Font[0] = 'R';
467 				if (regexec(Pat[10].pat, Aftnxt))  /* ",tP" */
468 					Pass3(DOBREAK,
469 						(unsigned char *)"toindent",
470 						NULL, 0);
471 				Free(&Aftnxt);
472 			}
473 			return;
474 		}
475 	    /*
476 	     * Special footer handling - "^.lF"
477 	     */
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);
484 			else
485 				(void) sprintf((char *)buf, "%s; %s",
486 					(char *)s1, (char *)s2);
487 			Pass3(NOBREAK, (unsigned char *)"LF", buf, 0);
488 			return;
489 		}
490 	}
491     /*
492      * Special -ms macro handling.
493      */
494 	if (Marg == MSMACROS) {
495 	    /*
496 	     * A text line - "^[^.]" - is only processed when there is an
497 	     * "after next" directive.
498 	     */
499 		if (*line != '.' && *line != '\'') {
500 			if (Aftnxt != NULL) {
501 				if (regexec(Pat[10].pat, Aftnxt))  /* ",tP" */
502 					Pass3(DOBREAK,
503 						(unsigned char *)"toindent",
504 						NULL, 0);
505 				Free(&Aftnxt);
506 			}
507 			return;
508 		}
509 	    /*
510 	     * Numbered headings - "^[.']nH"
511 	     */
512 		if (line[1] == 'n' && line[2] == 'H') {
513 			s1 = Field(2, line, 0);
514 			if (s1 != NULL) {
515 				i = atoi((char *)s1) - 1;
516 				if (i < 0) {
517 					for (j = 0; j < MAXNHNR; j++) {
518 						Nhnr[j] = 0;
519 					}
520 					i = 0;
521 				} else if (i >= MAXNHNR) {
522 				    (void) sprintf((char *)buf,
523 					" over NH limit (%d)", MAXNHNR);
524 				    Error(WARN, LINE, (char *)buf, NULL);
525 				}
526 			} else
527 				i = 0;
528 			Nhnr[i]++;
529 			for (j = i + 1; j < MAXNHNR; j++) {
530 				Nhnr[j] = 0;
531 			}
532 			s1 = buf;
533 			for (j = 0; j <= i; j++) {
534 				(void) sprintf((char *)s1, "%d.", Nhnr[j]);
535 				s1 = buf + strlen((char *)buf);
536 			}
537 			(void) Findstr((unsigned char *)"Nh", buf, 1);
538 			return;
539 		}
540 	}
541     /*
542      * Remaining lines should begin with a '.' or '\'' unless an "after next"
543      * trap has failed.
544      */
545 	if (*line != '.' && *line != '\'') {
546 		if (Aftnxt != NULL)
547 			Error(WARN, LINE, " failed .it: ", (char *)Aftnxt);
548 		else
549 			Error(WARN, LINE, " unrecognized line ", NULL);
550 		return;
551 	}
552 	brk = (*line == '.') ? DOBREAK : NOBREAK;
553     /*
554      * Evaluate expressions for "^[.'](ta|ll|ls|in|ti|po|ne|sp|pl|nr)"
555      * Then process the requests.
556      */
557 	if (regexec(Pat[11].pat, &line[1])) {
558 	    /*
559 	     * Establish default scale factor.
560 	     */
561 		if ((line[1] == 'n' && line[2] == 'e')
562 		||  (line[1] == 's' && line[2] == 'p')
563 		||  (line[1] == 'p' && line[2] == 'l'))
564 			exscale = Scalev;
565 		else if (line[1] == 'n' && line[2] == 'r')
566 			exscale = Scaleu;
567 		else
568 			exscale = Scalen;
569 	    /*
570 	     * Determine starting argument.
571 	     */
572 		if (line[1] == 'n' && line[2] == 'r')
573 			s1 = Field(2, &line[3], 0);
574 		else
575 			s1 = Field(1, &line[3], 0);
576 	    /*
577 	     * Evaluate expressions.
578 	     */
579 		for (nexpr = 0; s1 != NULL &&*s1 != '\0'; ) {
580 			while (*s1 == ' ' || *s1 == '\t')
581 				s1++;
582 			if (*s1 == '+' || *s1 == '-')
583 				ssign = *s1++;
584 			else
585 				ssign = '\0';
586 		    /*
587 		     * Process terms.
588 		     */
589 			val = 0.0;
590 			sp = -1;
591 			c = '+';
592 			s1--;
593 			while (c == '+' || c == '*' || c == '%'
594 			||  c == ')' || c == '-' || c == '/') {
595 			    op = c;
596 			    s1++;
597 			    tscale = exscale;
598 			    tval = 0.0;
599 			/*
600 			 * Pop stack on right parenthesis.
601 			 */
602 			    if (op == ')') {
603 				tval = val;
604 				if (sp >= 0) {
605 				    val = valstack[sp];
606 				    op = opstack[sp];
607 				    sp--;
608 				} else {
609 				    Error(WARN, LINE,
610 					" expression stack underflow", NULL);
611 				    return;
612 				}
613 				tscale = Scaleu;
614 			/*
615 			 * Push stack on left parenthesis.
616 			 */
617 			    } else if (*s1 == '(') {
618 				sp++;
619 				if (sp >= MAXSP) {
620 				    Error(WARN, LINE,
621 				       " expression stack overflow", NULL);
622 				    return;
623 				}
624 				valstack[sp] = val;
625 				opstack[sp] = op;
626 				val = 0.0;
627 				c = '+';
628 				continue;
629 			    } else if (*s1 == '\\') {
630 			      s1++;
631 			      switch(*s1) {
632 			/*
633 			 * "\\"" begins a comment.
634 			 */
635 			      case '"':
636 				while (*s1)
637 					s1++;
638 				break;
639 			/*
640 			 * Crude width calculation for "\\w"
641 			 */
642 			      case 'w':
643 				s2 = ++s1;
644 				if (*s1) {
645 				    s1++;
646 				    while (*s1 && *s1 != *s2)
647 					s1++;
648 				    tval = (double) (s1 - s2 - 1) * Scalen;
649 				    if (*s1)
650 					s1++;
651 				}
652 				break;
653 			/*
654 			 * Interpolate number register if "\\n".
655 			 */
656 			      case 'n':
657 				s1 = Asmcode(&s1, nm);
658 				if ((i = Findnum(nm, 0, 0)) >= 0)
659 				    tval = Numb[i].val;
660 			        s1++;
661 			     }
662 			/*
663 			 * Assemble numeric value.
664 			 */
665 			    } else if (*s1 == '.' || isdigit(*s1)) {
666 				for (i = 0; isdigit(*s1) || *s1 == '.'; s1++) {
667 				    if (*s1 == '.') {
668 					i = 10;
669 					continue;
670 				    }
671 				    d = (double) (*s1 - '0');
672 				    if (i) {
673 					tval = tval + (d / (double) i);
674 					i = i * 10;
675 				    } else
676 					tval = (tval * 10.0) + d;
677 				}
678 			    } else {
679 			/*
680 			 * It's not an expression.  Ignore extra scale.
681 			 */
682 				if ((i = Findscale((int)*s1, 0.0, 0)) < 0) {
683 				    (void) sprintf((char *)buf,
684 					" \"%s\" isn't an expression",
685 					(char *)s1);
686 				    Error(WARN, LINE, (char *)buf, NULL);
687 				}
688 				s1++;
689 			    }
690 			/*
691 			 * Add term to expression value.
692 			 */
693 			    if ((i = Findscale((int)*s1, 0.0, 0)) >= 0) {
694 				tval *= Scale[i].val;
695 				s1++;
696 			    } else
697 				tval *= tscale;
698 			    switch (op) {
699 			    case '+':
700 				val += tval;
701 				break;
702 			    case '-':
703 				val -= tval;
704 				break;
705 			    case '*':
706 				val *= tval;
707 				break;
708 			    case '/':
709 			    case '%':
710 				i = (int) val;
711 				j = (int) tval;
712 				if (j == 0) {
713 				    Error(WARN, LINE,
714 					(*s1 == '/') ? "div" : "mod",
715 				        " by 0");
716 				    return;
717 				}
718 				if (op == '/')
719 					val = (double) (i / j);
720 				else
721 					val = (double) (i % j);
722 				break;
723 			    }
724 			    c = *s1;
725 			}
726 		    /*
727 		     * Save expression value and sign.
728 		     */
729 			if (nexpr >= MAXEXP) {
730 				(void) sprintf((char *)buf,
731 				    " at expression limit of %d", MAXEXP);
732 				Error(WARN, LINE, (char *)buf, NULL);
733 				return;
734 			}
735 			exsign[nexpr] = ssign;
736 			expr[nexpr] = val;
737 			if (ssign == '-')
738 				sexpr[nexpr] = -1.0 * val;
739 			else
740 				sexpr[nexpr] = val;
741 			nexpr++;
742 			while (*s1 == ' ' || *s1 == '\t')
743 				s1++;
744 		}
745 	    /*
746 	     * Set parameters "(ll|ls|in|ti|po|pl)"
747 	     */
748 		if (regexec(Pat[12].pat, &line[1])) {
749 			nm[0] = line[1];
750 			nm[1] = line[2];
751 			if ((i = Findparms(nm)) < 0) {
752 				Error(WARN, LINE,
753 				    " can't find parameter register ",
754 				    (char *)nm);
755 				return;
756 			}
757 			if (nexpr == 0 || exscale == 0.0)
758 				j = Parms[i].prev;
759 			else if (exsign[0] == '\0'
760 			     ||  (nm[0] == 't' && nm[1] == 'i'))
761 				 j = (int)(sexpr[0] / exscale);
762 			else
763 				j = Parms[i].val + (int)(sexpr[0] / exscale);
764 			Parms[i].prev = Parms[i].val;
765 			Parms[i].val = j;
766 			nm[0] = (nexpr) ? exsign[0] : '\0';     /* for .ti */
767 			nm[1] = '\0';
768 			Pass3(brk, (unsigned char *)Parms[i].cmd, nm, j);
769 			return;
770 		}
771 		if (line[1] == 'n') {
772 			switch(line[2]) {
773 	    /*
774 	     * Need - "^[.']ne <expression>"
775 	     */
776 			case 'e':
777 				if (nexpr && Scalev > 0.0)
778 					i = (int) ((expr[0]/Scalev) + 0.99);
779 				else
780 					i = 0;
781 				Pass3(DOBREAK, (unsigned char *)"need", NULL,
782 					i);
783 				return;
784 	    /*
785 	     * Number - "^[.']nr <name> <expression>"
786 	     */
787 			case 'r':
788 				if ((s1 = Field(2, line, 0)) == NULL) {
789 				    Error(WARN, LINE, " bad number register",
790 				        NULL);
791 				    return;
792 				}
793 				if ((i = Findnum(s1, 0, 0)) < 0)
794 				    i = Findnum(s1, 0, 1);
795 				if (nexpr < 1) {
796 				    Numb[i].val = 0;
797 				    return;
798 				}
799 				if (exsign[0] == '\0')
800 				    Numb[i].val = (int) expr[0];
801 				else
802 				    Numb[i].val += (int) sexpr[0];
803 				return;
804 			}
805 		}
806 	    /*
807 	     * Space - "^[.']sp <expression>"
808 	     */
809 		if (line[1] == 's' && line[2] == 'p') {
810 			if (nexpr == 0)
811 				i = 1;
812 			else
813 				i = (int)((expr[0] / Scalev) + 0.99);
814 			while (i--)
815 				Pass3(brk, (unsigned char *)"space", NULL, 0);
816 			return;
817 		}
818 	    /*
819 	     * Tab positions - "^[.']ta <pos1> <pos2> . . ."
820 	     */
821      		if (line[1] == 't' && line[2] == 'a') {
822 			tval = 0.0;
823 			for (j = 0; j < nexpr; j++) {
824 				if (exsign[j] == '\0')
825 					tval = expr[j];
826 				else
827 					tval += sexpr[j];
828 				Tabs[j] = (int) (tval / Scalen);
829 			}
830 			Ntabs = nexpr;
831 			return;
832 		}
833 	}
834     /*
835      * Process all other nroff requests via Nreq().
836      */
837 	(void) Nreq(line, brk);
838 	return;
839 }
840