4 * consume options, initialization, main loop,
5 * input routines, escape function calling
17 char *Version
= "March 11, 1994";
19 int TROFF
= 1; /* assume we started in troff... */
25 static FILE *ifl
[NSO
]; /* open input file pointers */
26 char cfname
[NSO
+1][NS
] = { "stdin" }; /* file name stack */
27 int cfline
[NSO
]; /* input line count stack */
28 char *progname
; /* program name (troff or nroff) */
30 int trace
= 0; /* tracing mode: default off */
33 int main(int argc
, char *argv
[])
40 buf
[0] = '\0'; /* make sure it's empty (silly 3b2) */
44 if ((p
= strrchr(progname
, '/')) == NULL
)
48 if (strcmp(p
, "nroff") == 0)
51 alphabet
= 128; /* unicode for plan 9 */
59 while (--argc
> 0 && (++argv
)[0][0] == '-')
62 case 'N': /* ought to be used first... */
66 fprintf(stderr
, "troff/nroff version %s\n", Version
);
68 case 'F': /* switch font tables from default */
69 if (argv
[0][2] != '\0') {
70 strcpy(termtab
, &argv
[0][2]);
71 strcpy(fontdir
, &argv
[0][2]);
74 strcpy(termtab
, argv
[0]);
75 strcpy(fontdir
, argv
[0]);
84 npn
= atoi(&argv
[0][2]);
86 case 'u': /* set emboldening amount */
87 bdtab
[3] = atoi(&argv
[0][2]);
88 if (bdtab
[3] < 0 || bdtab
[3] > 50)
92 if (!(stop
= atoi(&argv
[0][2])))
96 sprintf(buf
+ strlen(buf
), ".nr %c %s\n",
97 argv
[0][2], &argv
[0][3]);
98 /* not yet cpushback(buf);*/
99 /* dotnr(&argv[0][2], &argv[0][3]); */
103 ERROR
"Too many macro packages: %s", argv
[0] WARN
;
106 strcpy(mfiles
[nmfi
], nextf
);
107 strcat(mfiles
[nmfi
++], &argv
[0][2]);
113 strcpy(devname
, &argv
[0][2]);
130 fprintf(stdout
, "%croff\n", TROFF
? 't' : 'n');
133 if (argv
[0][2] != '\0')
134 trace
= trace1
= argv
[0][2];
135 break; /* for the sake of compatibility */
137 ERROR
"unknown option %s", argv
[0] WARN
;
143 * cpushback maintains a LIFO, so push pack the -r arguments
144 * in reverse order to maintain a FIFO in case someone did -rC1 -rC3
151 while(strncmp(p
, ".nr", 3) != 0)
163 copyf
= lgf
= nb
= nflush
= nlflg
= 0;
164 if (ip
&& rbf0(ip
) == 0 && ejf
&& frame
->pframe
<= ejl
&& dip
== d
) {
173 if ((j
= cbits(i
)) == XPAR
) {
176 while (cbits(i
) != '\n')
182 if (j
== cc
|| j
== c2
) {
186 while ((j
= cbits(i
= getch())) == ' ' || j
== '\t')
210 for (i
= NTRTAB
; --i
; )
221 numtabp
[PID
].val
= getpid();
222 numtabp
[HP
].val
= init
= 0;
223 numtabp
[NL
].val
= -1;
226 sprintf(buf
, ".ds .T %s\n", devname
);
228 sprintf(buf
, ".ds .P %s\n", TBASE
);
230 numtabp
[CD
].val
= -1; /* compensation */
232 frame
= stk
= (Stack
*)setbrk(STACKSIZE
);
235 for (i
= 1; i
< NEV
; i
++) /* propagate the environment */
236 envcopy(&env
[i
], &env
[0]);
237 for (i
= 0; i
< NEV
; i
++) {
238 if ((env
[i
]._word
._bufp
= (Tchar
*)calloc(WDSIZE
, sizeof(Tchar
))) == NULL
) {
239 ERROR
"not enough room for word buffers" WARN
;
242 env
[i
]._word
._size
= WDSIZE
;
243 if ((env
[i
]._line
._bufp
= (Tchar
*)calloc(LNSIZE
, sizeof(Tchar
))) == NULL
) {
244 ERROR
"not enough room for line buffers" WARN
;
247 env
[i
]._line
._size
= LNSIZE
;
249 if ((oline
= (Tchar
*)calloc(OLNSIZE
, sizeof(Tchar
))) == NULL
) {
250 ERROR
"not enough room for line buffers" WARN
;
264 ltime
= localtime(&tt
);
265 numtabp
[YR
].val
= ltime
->tm_year
% 100;
267 numtabp
[MO
].val
= ltime
->tm_mon
+ 1; /* troff uses 1..12 */
268 numtabp
[DY
].val
= ltime
->tm_mday
;
269 numtabp
[DW
].val
= ltime
->tm_wday
+ 1; /* troff uses 1..7 */
276 void errprint(void) /* error message printer */
278 int savecd
= numtabp
[CD
].val
;
283 fprintf(stderr
, "%s: ", progname
);
284 fputs(errbuf
, stderr
);
286 fprintf(stderr
, "; %s:%d", cfname
[ifi
], numtabp
[CD
].val
);
290 numtabp
[CD
].val
= savecd
;
294 int control(int a
, int b
)
297 extern Contab
*contabp
;
301 if (a
== 0 || (j
= findmn(a
)) == -1)
303 if (contabp
[j
].f
== 0) {
305 fprintf(stderr
, "invoke macro %s\n", unpair(a
));
307 for (k
= dilev
; k
; k
--)
308 if (d
[k
].curd
== a
) {
309 ERROR
"diversion %s invokes itself during diversion",
317 return pushi(contabp
[j
].mx
, a
); /* BUG??? all that matters is 0/!0 */
321 fprintf(stderr
, "invoke request %s\n", unpair(a
));
335 i
= max(inumb(&trace
), 0);
349 if ((i
= getach()) == 0 || (j
= getach()) == 0)
357 * table encodes some special characters, to speed up tests
358 * in getch, viz FLSS, RPT, f, \b, \n, fc, tabch, ldrch
361 char gchtab
[NCHARS
] = {
362 000,004,000,000,010,000,000,000, /* fc, ldr */
363 001,002,001,000,001,000,000,000, /* \b, tab, nl, RPT */
364 000,000,000,000,000,000,000,000,
365 000,001,000,001,000,000,000,000, /* FLSS, ESC */
366 000,000,000,000,000,000,000,000,
367 000,000,000,000,000,000,000,000,
368 000,000,000,000,000,000,000,000,
369 000,000,000,000,000,000,000,000,
370 000,000,000,000,000,000,000,000,
371 000,000,000,000,000,000,000,000,
372 000,000,000,000,000,000,000,000,
373 000,000,000,000,000,000,000,000,
374 000,000,000,000,000,000,001,000, /* f */
375 000,000,000,000,000,000,000,000,
376 000,000,000,000,000,000,000,000,
377 000,000,000,000,000,000,000,000,
380 int realcbits(Tchar c
) /* return character bits, or MOTCH if motion */
396 if (cbits(i
) == '\n')
408 if (k
>= sizeof(gchtab
)/sizeof(gchtab
[0]) || gchtab
[k
] == 0) /* nothing special */
414 numtabp
[CD
].val
++; /* line number */
432 if (k
== 'f' && lg
&& !lgf
) {
436 if (k
== fc
|| k
== tabch
|| k
== ldrch
) {
437 if ((i
= setfield(k
)) == 0)
443 i
= makem(-width(' ' | chbits
));
450 k
= cbits(j
= getch0());
455 case 'n': /* number register */
458 case '$': /* argument indicator */
461 case '*': /* string indicator */
467 case '}': /* RIGHT */
470 case '"': /* comment */
471 while (cbits(i
= getch0()) != '\n')
474 numtabp
[CD
].val
++; /* line number */
478 /* experiment: put it here instead of copy mode */
479 case '(': /* special char name \(xx */
480 case 'C': /* \C'...' */
481 if ((i
= setch(k
)) == 0)
485 case ESC
: /* double backslash */
488 case 'e': /* printable version of current eschar */
491 case '\n': /* concealed newline */
494 case ' ': /* unpaddable space */
497 case '\'': /* \(aa */
506 case '-': /* current font minus */
509 case '&': /* filler */
512 case 'c': /* to be continued */
515 case '!': /* transparent indicator */
521 case 'a': /* leader (SOH) */
522 /* old: *pbp++ = LEADER; goto g0; */
528 case 'g': /* return format of a number register */
529 setaf(); /* should this really be in copy mode??? */
534 setsfbits(i
, sfbits(j
));
543 case 'f': /* font indicator */
546 case 's': /* size indicator */
549 case 'v': /* vert mot */
550 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
555 case 'h': /* horiz mot */
556 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
560 case '|': /* narrow space */
563 return(makem((int)(EM
)/6));
564 case '^': /* half narrow space */
567 return(makem((int)(EM
)/12));
568 case 'w': /* width function */
571 case 'p': /* spread */
574 case 'N': /* absolute character number */
575 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
576 if ((i
= setabs()) == 0)
579 case 'H': /* character height */
580 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
582 case 'S': /* slant */
583 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
585 case 'z': /* zero with char */
587 case 'l': /* hor line */
588 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
591 case 'L': /* vert line */
592 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
595 case 'D': /* drawing function */
596 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
599 case 'X': /* \X'...' for copy through */
602 case 'b': /* bracket */
605 case 'o': /* overstrike */
608 case 'k': /* mark hor place */
609 if ((k
= findr(getsn())) != -1) {
610 numtabp
[k
].val
= numtabp
[HP
].val
;
613 case '0': /* number space */
614 return(makem(width('0' | chbits
)));
615 case 'x': /* extra line space */
616 numerr
.type
= numerr
.escarg
= 0; numerr
.esc
= k
;
620 case 'u': /* half em up */
621 case 'r': /* full em up */
622 case 'd': /* half em down */
630 void setxon(void) /* \X'...' for copy through */
637 if (ismot(c
= getch()))
642 while ((k
= cbits(c
= getch())) != delim
&& k
!= '\n' && i
< xbuf
+NC
-1) {
647 *i
++ = XOFF
| chbits
;
653 char ifilt
[32] = { 0, 001, 002, 003, 0, 005, 006, 007, 010, 011, 012 };
676 if (nx
|| 1) { /* BUG: was ibufp >= eibuf, so EOF test is wrong */
678 ERROR
"in getch0, nfo = %d", nfo WARN
;
698 if (i
>= 040) /* zapped: && i < 0177 */
702 if (cbits(i
) == IMP
&& !raw
)
704 if (i
== 0 && !init
&& !raw
) { /* zapped: || i == 0177 */
710 if (copyf
== 0 && sfbits(i
) == 0)
712 if (cbits(i
) == eschar
&& !raw
)
718 int readutf8(int *dst
, char *src
)
726 while (l
< 6 && *s
& (0x40 >> l
))
728 *dst
= (0x3f >> l
) & *s
++;
730 *dst
= (*dst
<< 6) | (*s
++ & 0x3f);
734 static int utf8len(int c
)
739 while (l
< 6 && c
& (0x40 >> l
))
744 /* get one "character" from input, figure out what alphabet */
745 Tchar
get1ch(FILE *fp
)
753 if (c
== EOF
|| n
== 1)
756 for (i
= 1; i
< n
; i
++)
760 /* add name even if haven't seen it */
761 return chadd(buf
, MBchar
, Install
);
765 void pushback(Tchar
*b
)
772 while (b
> ob
&& pbp
< &pbbuf
[NC
-3])
774 if (pbp
>= &pbbuf
[NC
-3]) {
775 ERROR
"pushback overflow" WARN
;
780 void cpushback(char *b
)
787 while (b
> ob
&& pbp
< &pbbuf
[NC
-3])
789 if (pbp
>= &pbbuf
[NC
-3]) {
790 ERROR
"cpushback overflow" WARN
;
802 if (ifi
> 0 && !nx
) {
804 goto n0
; /* popf error */
805 return(1); /* popf ok */
807 if (nx
|| nmfi
< mflg
) {
813 if ((nfo
-= mflg
) && !stdi
) {
817 numtabp
[CD
].val
= stdi
= mflg
= 0;
819 strcpy(cfname
[ifi
], "stdin");
827 if (p
[0] == '-' && p
[1] == 0) {
829 strcpy(cfname
[ifi
], "stdin");
830 } else if ((ifile
= fopen(p
, "r")) == NULL
) {
831 ERROR
"cannot open file %s", p WARN
;
835 strcpy(cfname
[ifi
],p
);
845 ERROR
"popf went negative" WARN
;
848 numtabp
[CD
].val
= cfline
[ifi
]; /* restore line counter */
849 ip
= ipl
[ifi
]; /* input pointer */
850 ifile
= ifl
[ifi
]; /* input FILE * */
862 if (donef
&& frame
== stk
)
870 * return 16-bit, ascii/alphabetic character, ignore chars with more bits,
871 * (internal names), spaces and special cookies (below 040).
872 * Leave STX ETX ENQ ACK and BELL in to maintain compatibility with v7 troff.
880 j
= cbits(i
= getch());
881 if (ismot(i
) || j
> SHORTMASK
882 || (j
<= 040 && j
!= 002 /*STX*/
886 && j
!= 007)) { /*BELL*/
903 strcpy(mfiles
[nmfi
], nextf
);
918 for (k
= 0; k
< NS
- 1; k
++) {
936 if (skip() || !getname() || (fp
= fopen(nextf
, "r")) == NULL
|| ifi
>= NSO
) {
937 ERROR
"can't open file %s", nextf WARN
;
940 strcpy(cfname
[ifi
+1], nextf
);
941 cfline
[ifi
] = numtabp
[CD
].val
; /*hold line counter*/
953 void caself(void) /* set line number and file */
961 cfline
[ifi
] = numtabp
[CD
].val
= n
- 1;
963 if (getname()) { /* eats '\n' ? */
964 strcpy(cfname
[ifi
], nextf
);
970 void cpout(FILE *fin
, char *token
)
975 if (token
) { /* BUG: There should be no NULL bytes in input */
977 while ((fgets(buf
, sizeof buf
, fin
)) != NULL
) {
979 numtabp
[CD
].val
++; /* line number */
980 if (strcmp(token
, buf
) == 0)
983 newl
= strchr(buf
, '\n');
987 while ((n
= fread(buf
, sizeof *buf
, sizeof buf
, fin
)) > 0)
988 fwrite(buf
, n
, 1, ptid
);
994 { /* copy file without change */
997 extern int hpos
, esc
, po
;
999 /* this may not make much sense in nroff... */
1003 if (!skip() && getname()) {
1004 if (strncmp("<<", nextf
, 2) != 0) {
1005 if ((fd
= fopen(nextf
, "r")) == NULL
) {
1006 ERROR
"can't open file %s", nextf WARN
;
1009 eof
= (char *) NULL
;
1010 } else { /* current file */
1011 if (pbp
> lastpbp
|| ip
) {
1012 ERROR
"casecf: not reading from file" WARN
;
1017 ERROR
"casecf: missing end of input token" WARN
;
1028 ERROR
"casecf: no argument" WARN
;
1034 /* make it into a clean state, be sure that everything is out */
1038 ptesc(); /* to left margin */
1050 void getline(char *s
, int n
) /* get rest of input line into s */
1057 for (i
= 0; i
< n
-1; i
++)
1058 if ((s
[i
] = cbits(getch())) == '\n' || s
[i
] == RIGHT
)
1065 void casesy(void) /* call system */
1069 getline(sybuf
, NTM
);
1093 n
= 10 * n
+ *a
++ - '0';
1094 while (isdigit(*a
));
1098 *pnp
++ = neg
? -n
: n
;
1100 if (pnp
>= &pnlist
[NPN
-2]) {
1101 ERROR
"too many page numbers" WARN
;
1110 if (*pnp
!= -INT_MAX
)
1124 if ((long) i
< 0 || cbits(j
= getch0()) == RPT
)
1126 while (i
> 0 && pbp
< &pbbuf
[NC
-3]) {