/* * roff - C version. * the Colonel. 19 May 1983. * * Copyright 1983 by G. L. Sicherman. * You may use and alter this software freely for noncommercial ends * so long as you leave this message alone. * * Fix by Tim Maroney, 31 Dec 1984. * .hc implemented, 8 Feb 1985. * Fix to hyphenating with underlining, 12 Feb 1985. * Fixes to long-line hang and .bp by Dave Tutelman, 30 Mar 1985. * Fix to centering valve with long input lines, 4 May 1987. */ /* * This version from ftp://minnie.tuhs.org/minix/1.5/Source/commands/roff.c * and then modified to be closer to the functionality of the B language * so that it can be translated into B for the PDP-7 version of Unix. */ #include #include #define TXTLEN (o_pl-2) #define IDTLEN (o_ti>=0?o_ti:o_in) #define MAXLENGTH 255 #define UNDERL '\200' int spacechars[] = { ' ', '\t', '\n', 0 }; int holdword[MAXLENGTH], *holdp; int assyline[MAXLENGTH]; int assylen; int ehead[100], efoot[100], ohead[100], ofoot[100]; int adjtoggle; int isrequest = 0; int o_tr[40][2]; /* OUTPUT TRANSLATION TABLE */ int o_cc = '.'; /* CONTROL CHARACTER */ int o_hc = -1; /* HYPHENATION CHARACTER */ int o_tc = ' '; /* TABULATION CHARACTER */ int o_in = 0; /* INDENT SIZE */ int o_ix = -1; /* NEXT INDENT SIZE */ int o_ta[20] = { 9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 113, 121, 129, 137, 145, 153, 161 }; int n_ta = 20; /* #TAB STOPS */ int o_ll = 65, o_ad = 1, o_po = 0, o_ls = 1, o_ig = 0, o_fi = 1; int o_pl = 66, o_ro = 0, o_hx = 0, o_sp = 0, o_sk = 0; int o_ce = 0, o_ul = 0; int o_li = 0, o_bp = -1, o_hy = 1; int o_m2 = 1; int o_nn = 0; /* #LINES TO SUPPRESS NUMBERING */ int o_ti = -1; /* TEMPORARY INDENT */ int page_no = -1; int line_no = 9999; FILE *File; int *request[] = { (int[]){'a', 'd', 0}, (int[]){'a', 'r', 0}, (int[]){'b', 'p', 0}, (int[]){'b', 'r', 0}, (int[]){'c', 'c', 0}, (int[]){'c', 'e', 0}, (int[]){'d', 's', 0}, (int[]){'e', 'f', 0}, (int[]){'e', 'h', 0}, (int[]){'f', 'i', 0}, (int[]){'f', 'o', 0}, (int[]){'h', 'c', 0}, (int[]){'h', 'e', 0}, (int[]){'h', 'x', 0}, (int[]){'h', 'y', 0}, (int[]){'i', 'g', 0}, (int[]){'i', 'n', 0}, (int[]){'i', 'x', 0}, (int[]){'l', 'i', 0}, (int[]){'l', 'l', 0}, (int[]){'l', 's', 0}, (int[]){'n', 'a', 0}, (int[]){'n', 'e', 0}, (int[]){'n', 'f', 0}, (int[]){'n', 'n', 0}, (int[]){'o', 'f', 0}, (int[]){'o', 'h', 0}, (int[]){'p', 'a', 0}, (int[]){'p', 'l', 0}, (int[]){'p', 'o', 0}, (int[]){'r', 'o', 0}, (int[]){'s', 'k', 0}, (int[]){'s', 'p', 0}, (int[]){'s', 's', 0}, (int[]){'t', 'a', 0}, (int[]){'t', 'c', 0}, (int[]){'t', 'i', 0}, (int[]){'t', 'r', 0}, (int[]){'u', 'l', 0}, NULL }; int c; /* LAST CHAR READ */ int main(int argc, char **argv); void mesg(int f); void readfile(void); int readline(void); void bumpword(void); void dehyph(int *s); int reallen(int *s); void tabulate(void); int readreq(void); void snset(int *par); int tread(int *s); void nread(int *i); int snread(int *i, int *s, int sdef); int cread(int *k); void do_ta(void); void do_tr(void); int skipsp(void); void writebreak(void); void blankline(void); void writeline(int adflag, int flushflag); void fillline(void); void insrt(int p); void newpage(void); void beginpage(void); void endpage(void); void blankpage(void); void writetitle(int *t); int *pgform(void); int titlen(int *t, int c, int k); void spits(int *s); void spit(int c); int suck(void); int *strhas(int *p, int c); int *strend(int *p); int isspace(int c); int isalnum(int c); int isdigit(int c); int islegal(int c); void bomb(void); int *Strcpy(int *s1, int *s2); int *Strcat(int *s1, int *s2); int Strlen(int *s); int Strcmp(int *s1, int *s2); void printn(int *buf, int n, int len); int main(argc, argv) int argc; char **argv; { while (--argc) switch (**++argv) { default: argc++; goto endargs; } endargs: assylen = 0; assyline[0] = '\0'; if (!argc) { File = stdin; readfile(); } else while (--argc) { File = fopen(*argv, "r"); if (NULL == File) { fprintf(stderr, "roff: cannot read %s\n", *argv); exit(1); } readfile(); fclose(File); argv++; } writebreak(); endpage(); while (o_sk) { blankpage(); o_sk--; } return (0); } void readfile() { startwhile1: while (readline()) { if (isrequest) goto startwhile1; if (o_ce || !o_fi) { if (assylen) writeline(0, 1); else blankline(); if (o_ce) o_ce--; } } } int readline() { int startline, doingword; isrequest = 0; startline = 1; doingword = 0; c = suck(); if (c == '\n') { o_sp = 1; writebreak(); goto out; } else if (isspace(c)) writebreak(); startfor1: while (1) { if (c == EOF) { if (doingword) bumpword(); goto out; } if (c != o_cc && o_ig) { while (c != '\n' && c != EOF) c = suck(); goto out; } if (isspace(c) && !doingword) { startline = 0; switch (c) { case ' ': assyline[assylen++] = ' '; break; case '\t': tabulate(); break; case '\n': goto out; } c = suck(); goto startfor1; } if (isspace(c) && doingword) { bumpword(); if (c == '\t') tabulate(); else if (assylen) assyline[assylen++] = ' '; doingword = 0; if (c == '\n') goto out; } if (!isspace(c)) { if (doingword) *holdp++ = o_ul ? c | UNDERL : c; else if (startline && c == o_cc && !o_li) { isrequest = 1; return readreq(); } else { doingword = 1; holdp = holdword; *holdp++ = o_ul ? c | UNDERL : c; } } startline = 0; c = suck(); } out: if (o_ul) o_ul--; if (o_li) o_li--; return c != EOF; } /* * bumpword - add word to current line. */ int minus[2] = { '-', 0 }; void bumpword() { int *hc; *holdp = '\0'; /* * Tutelman's fix #1, modified by the Colonel. */ if (!o_fi || o_ce) goto giveup; /* * We use a while-loop in case of ridiculously long words with * multiple hyphenation indicators. */ if (assylen + reallen(holdword) > o_ll - IDTLEN) { if (!o_hy) writeline(o_ad, 0); else while (assylen + reallen(holdword) > o_ll - IDTLEN) { /* * Try hyphenating it. */ if (o_hc && strhas(holdword, o_hc)) { /* * There are hyphenation marks. Use them! */ hc = strend(holdword); while (hc >= holdword) { if ((*hc & ~UNDERL) != o_hc) continue; *hc = '\0'; if (assylen + reallen(holdword) + 1 > o_ll - IDTLEN) { *hc = o_hc; continue; } /* * Yay - it fits! */ dehyph(holdword); Strcpy(&assyline[assylen], holdword); Strcat(assyline, minus); assylen += Strlen(holdword) + 1; Strcpy(holdword, ++hc); goto out2; /* STOP LOOKING */ hc--; /* How do we get here? */ } /* for */ out2: /* * It won't fit, or we've succeeded in breaking the word. */ writeline(o_ad, 0); if (hc < holdword) goto giveup; } else { /* * If no hyphenation marks, give up. * Let somebody else implement it. */ writeline(o_ad, 0); goto giveup; } } /* while */ } giveup: /* * remove hyphenation marks, even if hyphenation is disabled. */ if (o_hc) dehyph(holdword); Strcpy(&assyline[assylen], holdword); assylen += Strlen(holdword); holdp = holdword; } /* * dehyph - remove hyphenation marks. */ void dehyph(s) int *s; { int *t; t = s; while (*s) { if ((*s & ~UNDERL) != o_hc) *t++ = *s; s++; } *t = '\0'; } /* * reallen - length of a word, excluding hyphenation marks. */ int reallen(s) int *s; { int n; n = 0; while (*s) n += (o_hc != (~UNDERL & *s++)); return n; } void tabulate() { int j; j = 0; while (j < n_ta) { if (o_ta[j] - 1 > assylen + IDTLEN) { while (assylen + IDTLEN < o_ta[j] - 1) { assyline[assylen] = o_tc; assylen++; } return; } j++; } /* NO TAB STOPS REMAIN */ assyline[assylen++] = o_tc; } int readreq() { int req[3]; int r, s; if (skipsp()) return c != EOF; c = suck(); if (c == EOF || c == '\n') return c != EOF; if (c == '.') { o_ig = 0; do (c = suck()); while (c != EOF && c != '\n'); return c != EOF; } if (o_ig) { while (c != EOF && c != '\n') c = suck(); return c != EOF; } req[0] = c; c = suck(); if (c == EOF || c == '\n') req[1] = '\0'; else req[1] = c; req[2] = '\0'; r = 0; while (request[r]) { if (!Strcmp(request[r], req)) goto out3; r++; } out3: if (!request[r]) { do (c = suck()); while (c != EOF && c != '\n'); return c != EOF; } switch (r) { case 0: /* ad */ o_ad = 1; writebreak(); break; case 1: /* ar */ o_ro = 0; break; case 2: /* bp */ case 27: /* pa */ c = snread(&r, &s, 1); /* * Tutelman's fix #2 - the signs were reversed! */ if (s > 0) o_bp = page_no + r; else if (s < 0) o_bp = page_no - r; else o_bp = r; writebreak(); if (line_no) { endpage(); beginpage(); } break; case 3: /* br */ writebreak(); break; case 4: /* cc */ c = cread(&o_cc); break; case 5: /* ce */ /* * Fix to centering. Set counter _after_ breaking! --G.L.S. */ nread(&r); writebreak(); o_ce = r; break; case 6: /* ds */ o_ls = 2; writebreak(); break; case 7: /* ef */ c = tread(efoot); break; case 8: /* eh */ c = tread(ehead); break; case 9: /* fi */ o_fi = 1; writebreak(); break; case 10: /* fo */ c = tread(efoot); Strcpy(ofoot, efoot); break; case 11: /* hc */ c = cread(&o_hc); break; case 12: /* he */ c = tread(ehead); Strcpy(ohead, ehead); break; case 13: /* hx */ o_hx = 1; break; case 14: /* hy */ nread(&o_hy); break; case 15: /* ig */ o_ig = 1; break; case 16: /* in */ writebreak(); snset(&o_in); o_ix = -1; break; case 17: /* ix */ snset(&o_ix); o_in = o_ix; break; case 18: /* li */ nread(&o_li); break; case 19: /* ll */ snset(&o_ll); break; case 20: /* ls */ snset(&o_ls); break; case 21: /* na */ o_ad = 0; writebreak(); break; case 22: /* ne */ nread(&r); if (line_no + (r - 1) * o_ls + 1 > TXTLEN) { writebreak(); endpage(); beginpage(); } break; case 23: /* nf */ o_fi = 0; writebreak(); break; case 24: /* nn */ snset(&o_nn); break; case 25: /* of */ c = tread(ofoot); break; case 26: /* oh */ c = tread(ohead); break; case 28: /* pl */ snset(&o_pl); break; case 29: /* po */ snset(&o_po); break; case 30: /* ro */ o_ro = 1; break; case 31: /* sk */ nread(&o_sk); break; case 32: /* sp */ nread(&o_sp); writebreak(); break; case 33: /* ss */ writebreak(); o_ls = 1; break; case 34: /* ta */ do_ta(); break; case 35: /* tc */ c = cread(&o_tc); break; case 36: /* ti */ writebreak(); c = snread(&r, &s, 0); if (s > 0) o_ti = o_in + r; else if (s < 0) o_ti = o_in - r; else o_ti = r; break; case 37: /* tr */ do_tr(); break; case 38: /* ul */ nread(&o_ul); break; } while (c != EOF && c != '\n') c = suck(); return c != EOF; } void snset(par) int *par; { int r, s; c = snread(&r, &s, 0); if (s > 0) *par += r; else if (s < 0) *par -= r; else *par = r; } int tread(s) int *s; { int leadbl; leadbl = 0; startfor2: while (1) { c = suck(); if (c == ' ' && !leadbl) goto startfor2; if (c == EOF || c == '\n') { *s = '\0'; return c; } *s++ = c; leadbl++; } } void nread(i) int *i; { int f; f = 0; *i = 0; if (!skipsp()) while (1) { c = suck(); if (c == EOF) goto out4; if (isspace(c)) goto out4; if (isdigit(c)) { f++; *i = *i * 10 + c - '0'; } else goto out4; } out4: if (!f) *i = 1; } int snread(i, s, sdef) int *i, *s, sdef; { int f; f = *i = *s = 0; startfor3: while (1) { c = suck(); if (c == EOF || c == '\n') goto out5; if (isspace(c)) { if (f) goto out5; else goto startfor3; } if (isdigit(c)) { f++; *i = *i * 10 + c - '0'; } else if ((c == '+' || c == '-') && !f) { f++; *s = c == '+' ? 1 : -1; } else goto out5; } out5: while (c != EOF && c != '\n') c = suck(); if (!f) { *i = 1; *s = sdef; } return c; } int cread(k) int *k; { int u; *k = -1; startfor4: while (1) { u = suck(); if (u == EOF || u == '\n') return u; if (isspace(u)) goto startfor4; if (*k < 0) *k = u; } } void do_ta() { int v; n_ta = 0; while (1) { nread(&v); if (v == 1) return; else o_ta[n_ta++] = v; if (c == '\n' || c == EOF) return; } } void do_tr() { int *t; t = &o_tr[0][0]; *t = '\0'; if (skipsp()) return; while (1) { c = suck(); if (c == EOF || c == '\n') goto out6; *t++ = c; } out6: *t = '\0'; } int skipsp() { startfor5: while (1) switch (c = suck()) { case EOF: case '\n': return 1; case ' ': case '\t': goto startfor5; default: ungetc(c, File); return 0; } } void writebreak() { int q; if (assylen) writeline(0, 1); q = TXTLEN; if (o_sp) { if (o_sp + line_no > q) newpage(); else if (line_no) { while (o_sp) { blankline(); o_sp--; } } } } void blankline() { if (line_no >= TXTLEN) newpage(); spit('\n'); line_no++; } void writeline(adflag, flushflag) int adflag, flushflag; { int j, q; int lnstring[7]; j = assylen - 1; while (j) { if (assyline[j] == ' ') assylen--; else goto out7; j--; } out7: q = TXTLEN; if (line_no >= q) newpage(); j = 0; while (j < o_po) { spit(' '); j++; } if (o_nn) o_nn--; if (o_ce) { j = 0; while (j < (o_ll - assylen + 1) / 2) { spit(' '); j++; } } else { j = 0; while (j < IDTLEN) { spit(' '); j++; } } if (adflag && !flushflag) fillline(); j = 0; while (j < assylen) { spit(assyline[j]); j++; } spit('\n'); assylen = 0; assyline[0] = '\0'; line_no++; j = 1; while (j < o_ls) { if (line_no <= q) blankline(); j++; } if (!flushflag) { if (o_hc) dehyph(holdword); Strcpy(assyline, holdword); assylen = Strlen(holdword); *holdword = '\0'; holdp = holdword; } if (o_ix >= 0) o_in = o_ix; o_ix = o_ti = -1; } void fillline() { int excess, j, s, inc, spaces; adjtoggle ^= 1; if (!(excess = o_ll - IDTLEN - assylen)) return; if (excess < 0) { fprintf(stderr, "roff: internal error #2 [%d]\n", excess); exit(1); } j = 2; while (1) { if (adjtoggle) { s = 0; inc = 1; } else { s = assylen - 1; inc = -1; } spaces = 0; while (s >= 0 && s < assylen) { if (assyline[s] == ' ') spaces++; else { if (0 < spaces && spaces < j) { insrt(s - inc); if (inc > 0) s++; if (!--excess) return; } spaces = 0; } s += inc; } j++; } } void insrt(p) int p; { int i; i = assylen; while (i > p) { assyline[i] = assyline[i - 1]; i--; } assylen++; } void newpage() { if (page_no >= 0) endpage(); else page_no = 1; while (o_sk) { blankpage(); o_sk--; } beginpage(); } void beginpage() { int i; writetitle(page_no & 1 ? ohead : ehead); i = 0; while (i < o_m2) { spit('\n'); i++; } line_no = 0; } void endpage() { int i; i = line_no; while (i < TXTLEN) { blankline(); i++; } writetitle(page_no & 1 ? ofoot : efoot); if (o_bp < 0) page_no++; else { page_no = o_bp; o_bp = -1; } } void blankpage() { int i; writetitle(page_no & 1 ? ohead : ehead); i = 0; while (i < TXTLEN) { spit('\n'); i++; } writetitle(page_no & 1 ? ofoot : efoot); page_no++; line_no = 0; } void writetitle(t) int *t; { int d, *pst; int j, l, m, n; d = *t; if (o_hx || !d) { spit('\n'); return; } pst = pgform(); j = 0; while (j < o_po) { spit(' '); j++; } l = titlen(++t, d, Strlen(pst)); while (*t && *t != d) { if (*t == '%') spits(pst); else spit(*t); t++; } if (!*t) { spit('\n'); return; } m = titlen(++t, d, Strlen(pst)); j = l; while (j < (o_ll - m) / 2) { spit(' '); j++; } while (*t && *t != d) { if (*t == '%') spits(pst); else spit(*t); t++; } if (!*t) { spit('\n'); return; } if ((o_ll - m) / 2 > l) m += (o_ll - m) / 2; else m += l; n = titlen(++t, d, Strlen(pst)); j = m; while (j < o_ll - n) { spit(' '); j++; } while (*t && *t != d) { if (*t == '%') spits(pst); else spit(*t); t++; } spit('\n'); } int s_cd[] = { 'c', 'd', 0 }; int s_c[] = { 'c', 0 }; int s_xc[] = { 'x', 'c', 0 }; int s_l[] = { 'l', 0 }; int s_xl[] = { 'x', 'l', 0 }; int s_x[] = { 'x', 0 }; int s_ix[] = { 'i', 'x', 0 }; int s_v[] = { 'v', 0 }; int s_iv[] = { 'i', 'v', 0 }; int s_i[] = { 'i', 0 }; int *pgform() { static int pst[11]; int i; if (o_ro) { *pst = '\0'; i = page_no; if (i >= 400) { Strcat(pst, s_cd); i -= 400; } while (i >= 100) { Strcat(pst, s_c); i -= 100; } if (i >= 90) { Strcat(pst, s_xc); i -= 90; } if (i >= 50) { Strcat(pst, s_l); i -= 50; } if (i >= 40) { Strcat(pst, s_xl); i -= 40; } while (i >= 10) { Strcat(pst, s_x); i -= 10; } if (i >= 9) { Strcat(pst, s_ix); i -= 9; } if (i >= 5) { Strcat(pst, s_v); i -= 5; } if (i >= 4) { Strcat(pst, s_iv); i -= 4; } while (i--) Strcat(pst, s_i); } else printn(pst, page_no, 11); return pst; } int titlen(t, c, k) int *t, c; int k; { int q; q = 0; while (*t && *t != c) q += *t++ == '%' ? k : 1; return q; } void spits(s) int *s; { while (*s) spit(*s++); } void spit(c) int c; { static int col_no, n_blanks; int ulflag; int *t; ulflag = c & UNDERL; c &= ~UNDERL; t = (int *)o_tr; while (*t) { if (*t++ == c) { /* * fix - last int translates to space. */ c = *t ? *t : ' '; goto out8; } t++; } out8: if (c != ' ' && c != '\n' && n_blanks) { while (n_blanks) { putc(' ', stdout); col_no++; n_blanks--; } } if (ulflag && isalnum(c)) fputs("_\b", stdout); if (c == ' ') n_blanks++; else { putc(c, stdout); col_no++; } if (c == '\n') { col_no = 0; n_blanks = 0; } } int suck() { while (1) { c = getc(File); if (islegal(c)) { return c; } } } /* * strhas - does string have character? Allow UNDERL flag. */ int *strhas(p, c) int *p, c; { while (*p) { if ((*p & ~UNDERL) == c) return p; p++; } return NULL; } /* * strend - find NULL at end of string. */ int *strend(p) int *p; { while (*p++) ; return p; } /* * isspace, isalnum, isdigit, islegal - classify a character. * We could just as well use if it didn't vary from * one version of Unix to another. As it is, these routines * must be modified for weird character sets, like EBCDIC and * CDC Scientific. */ int isspace(c) int c; { int *s; s = spacechars; while (*s) { if (*s == c) return 1; s++; } return 0; } int isalnum(c) int c; { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); } int isdigit(c) int c; { return c >= '0' && c <= '9'; } int islegal(c) int c; { return (c >= ' ' && c <= '~') || isspace(c) || c == '\n' || c == EOF; } void bomb() { fprintf(stderr, "Usage: roff [+00] [-00] [-s] [-h] file ...\n"); exit(1); } /* These str functions come from BSD code */ int *Strcpy(s1, s2) int *s1, *s2; { int *os1; os1 = s1; while ((*s1++ = *s2++)) ; return (os1); } int *Strcat(s1, s2) int *s1, *s2; { int *os1; os1 = s1; while (*s1++) ; --s1; while ((*s1++ = *s2++)) ; return (os1); } int Strlen(s) int *s; { int n; n = 0; while (*s++) n++; return (n); } int Strcmp(s1, s2) int *s1, *s2; { while (*s1 == *s2++) if (*s1++ == '\0') return (0); return (*s1 - *--s2); } /* Given an int pointer to an array, * a number, and the array length, * convert the number into ASCII * digits and store in the array. * Null terminate the list. */ void printn(int *buf, int n, int len) { /* We build the string in a temp buffer * and copy it into the real one. */ int tempbuf[len]; int *digitptr = &tempbuf[len - 1]; *digitptr-- = 0; /* Null terminate the string */ while (n > 0) { *digitptr = (n % 10) + '0'; /* Store a digit */ digitptr--; n = n / 10; } Strcpy(buf, digitptr + 1); /* Copy the tempbuf */ }