home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume25 / roff / roff.c < prev   
C/C++ Source or Header  |  1992-01-02  |  21KB  |  1,231 lines

  1. /*
  2.  *    roff - C version.
  3.  *    the Colonel.  19 May 1983.
  4.  *
  5.  *    Copyright 1983 by G. L. Sicherman.
  6.  *    You may use and alter this software freely for noncommercial ends
  7.  *    so long as you leave this message alone.
  8.  *
  9.  *    Fix by Tim Maroney, 31 Dec 1984.
  10.  *    .hc implemented, 8 Feb 1985.
  11.  *    Fix to hyphenating with underlining, 12 Feb 1985.
  12.  *    Fixes to long-line hang and .bp by Dave Tutelman, 30 Mar 1985.
  13.  *    Fix to centering valve with long input lines, 4 May 1987.
  14.  */
  15.  
  16. #include <sgtty.h>
  17. #include <signal.h>
  18. #include <stdio.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21.  
  22. /*    MAXMAC - maximum number of macros.    */
  23. #define MAXMAC    64
  24. /*    MAXDEPTH - maximum recursion of macros.    */
  25. #define MAXDEPTH 10
  26. #define MAXLENGTH 255
  27. #define UNDERL    '\200'
  28. #define SUFTAB    "/usr/lib/suftab"
  29. #define TXTLEN    (o_pl-o_m1-o_m2-o_m3-o_m4-2)
  30. #define IDTLEN    (o_ti>=0?o_ti:o_in)
  31.  
  32. char spacechars[] = " \t\n";
  33. int sflag, hflag, startpage, stoppage;
  34. char holdword[MAXLENGTH], *holdp;
  35. char assyline[MAXLENGTH];
  36. int assylen;
  37. char ehead[100], efoot[100], ohead[100], ofoot[100];
  38. struct macrotype {
  39.     char mname[3];
  40.     long int moff;
  41. } macro[MAXMAC];
  42. int n_macros;
  43. int depth;
  44. int adjtoggle;
  45. int isrequest = 0;
  46. char o_tr[40][2];    /* OUTPUT TRANSLATION TABLE */
  47. int o_cc = '.';    /* CONTROL CHARACTER */
  48. int o_hc = -1;    /* HYPHENATION CHARACTER */
  49. int o_tc = ' ';    /* TABULATION CHARACTER */
  50. int o_in = 0;    /* INDENT SIZE */
  51. int o_ix = -1;    /* NEXT INDENT SIZE */
  52. int o_ta[20] = {
  53.     9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 
  54.     113, 121, 129, 137, 145, 153, 161};
  55. int n_ta = 20;    /* #TAB STOPS */
  56. int o_ll = 65, o_ad = 1, o_po = 0, o_ls = 1, o_ig = 0, o_fi = 1;
  57. int o_pl = 66, o_ro = 0, o_hx = 0, o_bl = 0, o_sp = 0, o_sk = 0;
  58. int o_ce = 0, o_m1 = 2, o_m2 = 2, o_m3 = 1, o_m4 = 3, o_ul = 0;
  59. int o_li = 0, o_n1 = 0, o_n2 = 0, o_bp = -1, o_hy = 1;
  60. int o_ni = 1;    /* LINE-NUMBER INDENT */
  61. int o_nn = 0;    /* #LINES TO SUPPRESS NUMBERING */
  62. int o_ti = -1;    /* TEMPORARY INDENT */
  63. int page_no = -1;
  64. int line_no = 9999;
  65. int n_outwords;
  66. FILE *File, *Macread, *Macwrite;
  67. FILE *Save;
  68. long int teller[MAXDEPTH], ftell();
  69. char *strcat(), *strcpy(), *strend(), *strhas();
  70. char *sprintf();
  71. char *request[] = {
  72.     "ad","ar","bl","bp","br","cc","ce","de",
  73.     "ds","ef","eh","fi","fo","hc","he","hx","hy","ig",
  74.     "in","ix","li","ll","ls","m1","m2","m3","m4",
  75.     "n1","n2","na","ne","nf","ni","nn","nx","of","oh",
  76.     "pa","pl","po","ro","sk","sp","ss","ta","tc","ti",
  77.     "tr","ul",0};
  78. char *mktemp(), *mfilnam = "/tmp/rtmXXXXXX";
  79. int c;        /* LAST CHAR READ */
  80. struct sgttyb tty;
  81.  
  82. main(argc,argv)
  83. int argc;
  84. char **argv;
  85. {
  86.     while (--argc) switch (**++argv) {
  87.     case '+':
  88.         startpage=atoi(++*argv);
  89.         break;
  90.     case '-':
  91.         ++*argv;
  92.         if (isdigit(**argv)) stoppage=atoi(*argv);
  93.         else switch(**argv) {
  94.         case 's':
  95.             sflag++;
  96.             break;
  97.         case 'h':
  98.             hflag++;
  99.             break;
  100.         default:
  101.             bomb();
  102.         }
  103.         break;
  104.     default:
  105.         argc++;
  106.         goto endargs;
  107.     }
  108. endargs:
  109.     if (sflag) gtty(0,&tty);
  110.     mesg(0);    /* BLOCK OUT MESSAGES */
  111.     assylen=0;
  112.     assyline[0]='\0';
  113.     if (!argc) {
  114.         File=stdin;
  115.         readfile();
  116.     }
  117.     else while (--argc) {
  118.         File=fopen(*argv,"r");
  119.         if (NULL==File) {
  120.             fprintf(stderr,"roff: cannot read %s\n",*argv);
  121.             exit(1);
  122.         }
  123.         readfile();
  124.         fclose(File);
  125.         argv++;
  126.     }
  127.     writebreak();            /* TERMINATE PARTIAL OUTPUT LINE */
  128.     endpage();            /* FLUSH LAST PAGE */
  129.     for (; o_sk; o_sk--) blankpage();    /* FLUSH BLANK PAGES */
  130.     mesg(1);            /* ALLOW MESSAGES */
  131. }
  132.  
  133. mesg(f)
  134. int f;
  135. {
  136.     static int mode;
  137.     struct stat cbuf;
  138.     char *ttyname();
  139.  
  140.     if (!isatty(1)) return;
  141.     if (!f) {
  142.         fstat(1,&cbuf);
  143.         mode = cbuf.st_mode;
  144.         chmod(ttyname(1),mode & ~022);
  145.     }
  146.     else chmod(ttyname(1),mode);
  147. }
  148.  
  149. readfile()
  150. {
  151.     while (readline()) {
  152.         if (isrequest) continue;
  153.         if (o_ce || !o_fi) {
  154.             if (assylen) writeline(0,1);
  155.             else blankline();
  156.             if (o_ce) o_ce--;
  157.         }
  158.     }
  159. }
  160.  
  161. readline()
  162. {
  163.     int startline, doingword;
  164.     isrequest = 0;
  165.     startline = 1;
  166.     doingword = 0;
  167.     c=suck();
  168.     if (c == '\n') {
  169.         o_sp = 1;
  170.         writebreak();
  171.         goto out;
  172.     }
  173.     else if (isspace(c)) writebreak();
  174.     for (;;) {
  175.         if (c==EOF) {        /* EOF - FLUSH WORD, IF ANY */
  176.             if (doingword) bumpword();
  177.             break;
  178.         }
  179.         if (c!=o_cc && o_ig) {    /* IGNORE A TEXT LINE */
  180.             while (c!='\n' && c!=EOF) c=suck();
  181.             break;
  182.         }
  183.         if (isspace(c) && !doingword) {
  184.             startline=0;
  185.             switch (c) {
  186.             case ' ':
  187.                 assyline[assylen++]=' ';
  188.                 break;
  189.             case '\t':
  190.                 tabulate();
  191.                 break;
  192.             case '\n':
  193.                 goto out;
  194.             }
  195.             c = suck();
  196.             continue;
  197.         }
  198.         if (isspace(c) && doingword) {    /* WHITE SPACE - FLUSH WORD */
  199.             bumpword();
  200.             if (c=='\t') tabulate();
  201.             else if (assylen) assyline[assylen++]=' ';
  202.             doingword=0;
  203.             if (c=='\n') break;
  204.         }
  205.         if (!isspace(c)) {
  206. /*
  207.  *    It's not a space.  If we're doing a word, continue it.
  208.  *    If it's a request, process it.
  209.  */
  210.             if (doingword) *holdp++ = o_ul? c|UNDERL: c;
  211.             else if (startline && c==o_cc && !o_li) {
  212.                 isrequest=1;
  213.                 return readreq();
  214.             }
  215. /*
  216.  *    It's the start of a new word.  Store it.
  217.  */
  218.             else {
  219.                 doingword=1;
  220.                 holdp=holdword;
  221.                 *holdp++ = o_ul? c|UNDERL: c;
  222.             }
  223.         }
  224.         startline=0;
  225.         c = suck();
  226.     }
  227. out:
  228.     if (o_ul) o_ul--;
  229.     if (o_li) o_li--;
  230.     return c!=EOF;
  231. }
  232.  
  233. /*
  234.  *    bumpword - add word to current line.
  235.  */
  236.  
  237. bumpword()
  238. {
  239.     char *hc;
  240.     *holdp = '\0';
  241. /*
  242.  *    Tutelman's fix #1, modified by the Colonel.
  243.  */
  244.     if (!o_fi || o_ce) goto giveup;
  245. /*
  246.  *    We use a while-loop in case of ridiculously long words with
  247.  *    multiple hyphenation indicators.
  248.  */
  249.     if (assylen + reallen(holdword) > o_ll - IDTLEN) {
  250.         if (!o_hy) writeline(o_ad,0);
  251.         else while (assylen + reallen(holdword) > o_ll - IDTLEN) {
  252. /*
  253.  *    Try hyphenating it.
  254.  */
  255.             if (o_hc && strhas(holdword,o_hc)) {
  256. /*
  257.  *    There are hyphenation marks.  Use them!
  258.  */
  259.                 for (hc=strend(holdword); hc>=holdword; hc--) {
  260.                     if ((*hc&~UNDERL)!=o_hc) continue;
  261.                     *hc = '\0';
  262.                     if (assylen + reallen(holdword) + 1 >
  263.                     o_ll - IDTLEN) {
  264.                         *hc = o_hc;
  265.                         continue;
  266.                     }
  267. /*
  268.  *    Yay - it fits!
  269.  */
  270.                     dehyph(holdword);
  271.                     strcpy(&assyline[assylen],holdword);
  272.                     strcat(assyline,"-");
  273.                     assylen += strlen(holdword) + 1;
  274.                     strcpy(holdword,++hc);
  275.                     break;    /* STOP LOOKING */
  276.                 } /* for */
  277. /*
  278.  *    It won't fit, or we've succeeded in breaking the word.
  279.  */
  280.                 writeline(o_ad,0);
  281.                 if (hc<holdword) goto giveup;
  282.             }
  283.             else {
  284. /*
  285.  *    If no hyphenation marks, give up.
  286.  *    Let somebody else implement it.
  287.  */
  288.                 writeline(o_ad,0);
  289.                 goto giveup;
  290.             }
  291.         } /* while */
  292.     }
  293. giveup:
  294. /*
  295.  *    remove hyphenation marks, even if hyphenation is disabled.
  296.  */
  297.     if (o_hc) dehyph(holdword);
  298.     strcpy(&assyline[assylen],holdword);
  299.     assylen+=strlen(holdword);
  300.     holdp = holdword;
  301. }
  302.  
  303. /*
  304.  *    dehyph - remove hyphenation marks.
  305.  */
  306.  
  307. dehyph(s)
  308. char *s;
  309. {
  310.     char *t;
  311.     for (t=s; *s; s++) if ((*s&~UNDERL) != o_hc) *t++ = *s;
  312.     *t='\0';
  313. }
  314.  
  315. /*
  316.  *    reallen - length of a word, excluding hyphenation marks.
  317.  */
  318.  
  319. int
  320. reallen(s)
  321. char *s;
  322. {
  323.     register n;
  324.     n=0;
  325.     while (*s) n += (o_hc != (~UNDERL & *s++));
  326.     return n;
  327. }
  328.  
  329. tabulate()
  330. {
  331.     int j;
  332.     for (j=0; j<n_ta; j++) if (o_ta[j]-1>assylen+IDTLEN) {
  333.         for (; assylen+IDTLEN<o_ta[j]-1; assylen++)
  334.             assyline[assylen]=o_tc;
  335.         return;
  336.     }
  337.     /* NO TAB STOPS REMAIN */
  338.     assyline[assylen++]=o_tc;
  339. }
  340.  
  341. int
  342. readreq()
  343. {
  344.     char req[3];
  345.     int r, s;
  346.     if (skipsp()) return c!=EOF;
  347.     c=suck();
  348.     if (c==EOF || c=='\n') return c!=EOF;
  349.     if (c=='.') {
  350.         o_ig = 0;
  351.         do (c=suck());
  352.         while (c!=EOF && c!='\n');
  353.         if (depth) endmac();
  354.         return c!=EOF;
  355.     }
  356.     if (o_ig) {
  357.         while (c!=EOF && c!='\n') c=suck();
  358.         return c!=EOF;
  359.     }
  360.     req[0]=c;
  361.     c=suck();
  362.     if (c==EOF || c=='\n') req[1]='\0';
  363.     else req[1]=c;
  364.     req[2]='\0';
  365.     for (r=0; r<n_macros; r++) if (!strcmp(macro[r].mname,req)) {
  366. /*
  367.  *    It's a macro.  Invoke it.
  368.  */
  369.         submac(r);
  370.         goto reqflsh;
  371.     }
  372.     for (r=0; request[r]; r++) if (!strcmp(request[r],req)) break;
  373.     if (!request[r]) {
  374. /*
  375.  *    Invalid request.  Ignore it.
  376.  */
  377.         do (c=suck());
  378.         while (c!=EOF && c!='\n');
  379.         return c!=EOF;
  380.     }
  381.     switch (r) {
  382.     case 0: /* ad */
  383.         o_ad=1;
  384.         writebreak();
  385.         break;
  386.     case 1: /* ar */
  387.         o_ro=0;
  388.         break;
  389.     case 2: /* bl */
  390.         nread(&o_bl);
  391.         writebreak();
  392.         break;
  393.     case 3: /* bp */
  394.     case 37: /* pa */
  395.         c=snread(&r,&s,1);
  396. /*
  397.  *    Tutelman's fix #2 - the signs were reversed!
  398.  */
  399.         if (s>0) o_bp=page_no+r;
  400.         else if (s<0) o_bp=page_no-r;
  401.         else o_bp=r;
  402.         writebreak();
  403.         if (line_no) {
  404.             endpage();
  405.             beginpage();
  406.         }
  407.         break;
  408.     case 4: /* br */
  409.         writebreak();
  410.         break;
  411.     case 5: /* cc */
  412.         c=cread(&o_cc);
  413.         break;
  414.     case 6: /* ce */
  415. /*
  416.  *    Fix to centering.  Set counter _after_ breaking!  --G.L.S.
  417.  */
  418.         nread(&r);
  419.         writebreak();
  420.         o_ce = r;
  421.         break;
  422.     case 7: /* de */
  423.         defmac();
  424.         break;
  425.     case 8: /* ds */
  426.         o_ls=2;
  427.         writebreak();
  428.         break;
  429.     case 9: /* ef */
  430.         c=tread(efoot);
  431.         break;
  432.     case 10: /* eh */
  433.         c=tread(ehead);
  434.         break;
  435.     case 11: /* fi */
  436.         o_fi=1;
  437.         writebreak();
  438.         break;
  439.     case 12: /* fo */
  440.         c=tread(efoot);
  441.         strcpy(ofoot,efoot);
  442.         break;
  443.     case 13: /* hc */
  444.         c=cread(&o_hc);
  445.         break;
  446.     case 14: /* he */
  447.         c=tread(ehead);
  448.         strcpy(ohead,ehead);
  449.         break;
  450.     case 15: /* hx */
  451.         o_hx=1;
  452.         break;
  453.     case 16: /* hy */
  454.         nread(&o_hy);
  455.         break;
  456.     case 17: /* ig */
  457.         o_ig=1;
  458.         break;
  459.     case 18: /* in */
  460.         writebreak();
  461.         snset(&o_in);
  462.         o_ix = -1;
  463.         break;
  464.     case 19: /* ix */
  465.         snset(&o_ix);
  466.         if (!n_outwords) o_in=o_ix;
  467.         break;
  468.     case 20: /* li */
  469.         nread(&o_li);
  470.         break;
  471.     case 21: /* ll */
  472.         snset(&o_ll);
  473.         break;
  474.     case 22: /* ls */
  475.         snset(&o_ls);
  476.         break;
  477.     case 23: /* m1 */
  478.         nread(&o_m1);
  479.         break;
  480.     case 24: /* m2 */
  481.         nread(&o_m2);
  482.         break;
  483.     case 25: /* m3 */
  484.         nread(&o_m3);
  485.         break;
  486.     case 26: /* m4 */
  487.         nread(&o_m4);
  488.         break;
  489.     case 27: /* n1 */
  490.         o_n1=1;
  491.         break;
  492.     case 28: /* n2 */
  493.         nread(&o_n2);
  494.         break;
  495.     case 29: /* na */
  496.         o_ad=0;
  497.         writebreak();
  498.         break;
  499.     case 30: /* ne */
  500.         nread(&r);
  501.         if (line_no+(r-1)*o_ls+1 > TXTLEN) {
  502.             writebreak();
  503.             endpage();
  504.             beginpage();
  505.         }
  506.         break;
  507.     case 31: /* nf */
  508.         o_fi=0;
  509.         writebreak();
  510.         break;
  511.     case 32: /* ni */
  512.         snset(&o_ni);
  513.         break;
  514.     case 33: /* nn */
  515.         snset(&o_nn);
  516.         break;
  517.     case 34: /* nx */
  518.         do_nx();
  519.         c='\n';    /* SO WE DON'T FLUSH THE FIRST LINE */
  520.         break;
  521.     case 35: /* of */
  522.         c=tread(ofoot);
  523.         break;
  524.     case 36: /* oh */
  525.         c=tread(ohead);
  526.         break;
  527.     case 38: /* pl */
  528.         snset(&o_pl);
  529.         break;
  530.     case 39: /* po */
  531.         snset(&o_po);
  532.         break;
  533.     case 40: /* ro */
  534.         o_ro=1;
  535.         break;
  536.     case 41: /* sk */
  537.         nread(&o_sk);
  538.         break;
  539.     case 42: /* sp */
  540.         nread(&o_sp);
  541.         writebreak();
  542.         break;
  543.     case 43: /* ss */
  544.         writebreak();
  545.         o_ls=1;
  546.         break;
  547.     case 44: /* ta */
  548.         do_ta();
  549.         break;
  550.     case 45: /* tc */
  551.         c=cread(&o_tc);
  552.         break;
  553.     case 46: /* ti */
  554.         writebreak();
  555.         c=snread(&r,&s,0);
  556.         if (s>0) o_ti=o_in+r;
  557.         else if (s<0) o_ti=o_in-r;
  558.         else o_ti=r;
  559.         break;
  560.     case 47: /* tr */
  561.         do_tr();
  562.         break;
  563.     case 48: /* ul */
  564.         nread(&o_ul);
  565.         break;
  566.     }
  567. reqflsh:
  568.     while (c!=EOF && c!='\n') c=suck();
  569.     return c!=EOF;
  570. }
  571.  
  572. /*
  573.  *    snset - set a value upwards, downwards, or absolutely.
  574.  */
  575.  
  576. snset(par)
  577. int *par;
  578. {
  579.     int r, s;
  580.     c=snread(&r,&s,0);
  581.     if (s>0) *par+=r;
  582.     else if (s<0) *par-=r;
  583.     else *par=r;
  584. }
  585.  
  586. tread(s)
  587. char *s;
  588. {
  589.     int leadbl;
  590.     leadbl=0;
  591.     for (;;) {
  592.         c=suck();
  593.         if (c==' ' && !leadbl) continue;
  594.         if (c==EOF || c=='\n') {
  595.             *s = '\0';
  596.             return c;
  597.         }
  598.         *s++ = c;
  599.         leadbl++;
  600.     }
  601. }
  602.  
  603. nread(i)
  604. int *i;
  605. {
  606.     int f;
  607.     f=0;
  608.     *i=0;
  609.     if (!skipsp()) for (;;) {
  610.         c=suck();
  611.         if (c==EOF) break;
  612.         if (isspace(c)) break;
  613.         if (isdigit(c)) {
  614.             f++;
  615.             *i = *i*10 + c - '0';
  616.         }
  617.         else break;
  618.     }
  619.     if (!f) *i=1;
  620. }
  621.  
  622. int
  623. snread(i,s,sdef)
  624. int *i, *s, sdef;
  625. {
  626.     int f;
  627.     f = *i = *s = 0;
  628.     for (;;) {
  629.         c=suck();
  630.         if (c==EOF || c=='\n') break;
  631.         if (isspace(c)) {
  632.             if (f) break;
  633.             else continue;
  634.         }
  635.         if (isdigit(c)) {
  636.             f++;
  637.             *i = *i*10 + c - '0';
  638.         }
  639.         else if ((c=='+' || c=='-') && !f) {
  640.             f++;
  641.             *s = c=='+' ? 1 : -1;
  642.         }
  643.         else break;
  644.     }
  645.     while (c!=EOF && c!='\n') c=suck();
  646.     if (!f) {
  647.         *i=1;
  648.         *s=sdef;
  649.     }
  650.     return c;
  651. }
  652.  
  653. int
  654. cread(k)
  655. int *k;
  656. {
  657.     int u;
  658.     *k = -1;
  659.     for (;;) {
  660.         u=suck();
  661.         if (u==EOF || u=='\n') return u;
  662.         if (isspace(u)) continue;
  663.         if (*k < 0) *k=u;
  664.     }
  665. }
  666.  
  667. /*
  668.  *    defmac - define a macro.
  669.  */
  670.  
  671. defmac()
  672. {
  673.     int i;
  674.     char newmac[3], *nm;
  675.     if (skipsp()) return;
  676.     nm=newmac;
  677.     if (!Macwrite) openmac();
  678.     *nm++ = suck();
  679.     c=suck();
  680.     if (c!=EOF && c!='\n' && c!=' ' && c!='\t') *nm++ = c;
  681.     *nm = '\0';
  682.         /* KILL OLD DEFINITION IF ANY */
  683.     for (i=0; i<n_macros; i++) if (!strcmp(newmac,macro[i].mname)) {
  684.         macro[i].mname[0]='\0';
  685.         break;
  686.     }
  687.     macro[n_macros].moff=ftell(Macwrite);
  688.     strcpy(macro[n_macros++].mname,newmac);    /* OVERFLOW HAZARD HERE! */
  689.     while (c!=EOF && c!='\n') c=suck();    /* FLUSH HEADER LINE */
  690.     while (copyline());
  691.     fflush(Macwrite);
  692. }
  693.  
  694. openmac()
  695. {
  696.     if (NULL==(Macwrite=fopen(mktemp(mfilnam),"w"))) {
  697.         fprintf(stderr,"roff: cannot open temp file\n");
  698.         exit(1);
  699.     }
  700.     Macread=fopen(mfilnam,"r");
  701.     unlink(mfilnam);
  702. }
  703.  
  704. int
  705. copyline()
  706. {
  707.     int n, first, second;
  708.     if (c==EOF) {
  709.         fprintf(Macwrite,"..\n");
  710.         return 0;
  711.     }
  712.     n=0;
  713.     first=1;
  714.     second=0;
  715.     for (;;) {
  716.         c=suck();
  717.         if (c==EOF) {
  718.             if (!first) putc('\n',Macwrite);
  719.             return 0;
  720.         }
  721.         if (c=='\n') {
  722.             putc('\n',Macwrite);
  723.             return n!=2;
  724.         }
  725.         if (first && c=='.') n++;
  726.         else if (second && n==1 && c=='.') n++;
  727.         putc(c,Macwrite);
  728.         second=first;
  729.         first=0;
  730.     }
  731. }
  732.  
  733. submac(r)
  734. int r;
  735. {
  736.     while (c!=EOF && c!='\n') c=suck();
  737.     if (depth) teller[depth-1]=ftell(Macread);
  738.     else {
  739.         Save = File;
  740.         File = Macread;
  741.     }
  742.     depth++;
  743.     fseek(Macread,macro[r].moff,0);
  744. }
  745.  
  746. endmac()
  747. {
  748.     depth--;
  749.     if (depth) fseek(Macread,teller[depth-1],0);
  750.     else File = Save;
  751.     c='\n';
  752. }
  753.  
  754. do_ta()
  755. {
  756.     int v;
  757.     n_ta = 0;
  758.     for (;;) {
  759.         nread(&v);
  760.         if (v==1) return;
  761.         else o_ta[n_ta++]=v;
  762.         if (c=='\n' || c==EOF) break;
  763.     }
  764. }
  765.  
  766. do_tr()
  767. {
  768.     char *t;
  769.     t = &o_tr[0][0];
  770.     *t='\0';
  771.     if (skipsp()) return;
  772.     for (;;) {
  773.         c=suck();
  774.         if (c==EOF || c=='\n') break;
  775.         *t++ = c;
  776.     }
  777.     *t = '\0';
  778. }
  779.  
  780. do_nx()
  781. {
  782.     char fname[100], *f;
  783.     f=fname;
  784.     if (skipsp()) return;
  785.     for (;;) switch(c=suck()) {
  786.     case EOF:
  787.     case '\n':
  788.     case ' ':
  789.     case '\t':
  790.         if (f==fname) return;
  791.         goto got_nx;
  792.     default:
  793.         *f++ = c;
  794.     }
  795. got_nx:
  796.     fclose(File);
  797.     *f = '\0';
  798.     if (!(File=fopen(fname,"r"))) {
  799.         fprintf(stderr,"roff: cannot read %s\n",fname);
  800.         exit(1);
  801.     }
  802. }
  803.  
  804. int
  805. skipsp()
  806. {
  807.     for (;;) switch(c=suck()) {
  808.     case EOF:
  809.     case '\n':
  810.         return 1;
  811.     case ' ':
  812.     case '\t':
  813.         continue;
  814.     default:
  815.         ungetc(c,File);
  816.         return 0;
  817.     }
  818. }
  819.  
  820. writebreak()
  821. {
  822.     int q;
  823.     if (assylen) writeline(0,1);
  824.     q = TXTLEN;
  825.     if (o_bl) {
  826.         if (o_bl + line_no > q) {
  827.             endpage();
  828.             beginpage();
  829.         }
  830.         for (; o_bl; o_bl--) blankline();
  831.     }
  832.     else if (o_sp) {
  833.         if (o_sp + line_no > q) newpage();
  834.         else if (line_no) for (; o_sp; o_sp--) blankline();
  835.     }
  836. }
  837.  
  838. blankline()
  839. {
  840.     if (line_no >= TXTLEN) newpage();
  841.     if (o_n2) o_n2++;
  842.     spit('\n');
  843.     line_no++;
  844. }
  845.  
  846. writeline(adflag,flushflag)
  847. int adflag, flushflag;
  848. {
  849.     int j, q;
  850.     char lnstring[7];
  851.     for (j=assylen-1; j; j--) {
  852.         if (assyline[j]==' ') assylen--;
  853.         else break;
  854.     }
  855.     q = TXTLEN;
  856.     if (line_no >= q) newpage();
  857.     for (j=0; j<o_po; j++) spit(' ');
  858.     if (o_n1) {
  859.         if (o_nn) for (j=0; j<o_ni+4; j++) spit(' ');
  860.         else {
  861.             for (j=0; j<o_ni; j++) spit(' ');
  862.             sprintf(lnstring,"%3d ",line_no+1);
  863.             spits(lnstring);
  864.         }
  865.     }
  866.     if (o_n2) {
  867.         if (o_nn) for (j=0; j<o_ni+4; j++) spit(' ');
  868.         else {
  869.             for (j=0; j<o_ni; j++) spit(' ');
  870.             sprintf(lnstring,"%3d ",o_n2++);
  871.             spits(lnstring);
  872.         }
  873.     }
  874.     if (o_nn) o_nn--;
  875.     if (o_ce) for (j=0; j<(o_ll-assylen+1)/2; j++) spit(' ');
  876.     else for (j=0; j<IDTLEN; j++) spit(' ');
  877.     if (adflag && !flushflag) fillline();
  878.     for (j=0; j<assylen; j++) spit(assyline[j]);
  879.     spit('\n');
  880.     assylen=0;
  881.     assyline[0]='\0';
  882.     line_no++;
  883.     for (j=1; j<o_ls; j++) if (line_no <= q) blankline();
  884.     if (!flushflag)  {
  885.         if (o_hc) dehyph(holdword);
  886.         strcpy(assyline,holdword);
  887.         assylen=strlen(holdword);
  888.         *holdword='\0';
  889.         holdp=holdword;
  890.     }
  891.     if (o_ix>=0) o_in=o_ix;
  892.     o_ix = o_ti = -1;
  893. }
  894.  
  895. fillline()
  896. {
  897.     int excess, j, s, inc, spaces;
  898.     adjtoggle^=1;
  899.     if (!(excess = o_ll - IDTLEN - assylen)) return;
  900.     if (excess < 0) {
  901.         fprintf(stderr,"roff: internal error #2 [%d]\n",excess);
  902.         exit(1);
  903.     }
  904.     for (j=2;; j++) {
  905.         if (adjtoggle) {
  906.             s=0;
  907.             inc = 1;
  908.         }
  909.         else {
  910.             s=assylen-1;
  911.             inc = -1;
  912.         }
  913.         spaces=0;
  914.         while (s>=0 && s<assylen) {
  915.             if (assyline[s]==' ') spaces++;
  916.             else {
  917.                 if (0<spaces && spaces<j) {
  918.                     insrt(s-inc);
  919.                     if (inc>0) s++;
  920.                     if (!--excess) return;
  921.                 }
  922.                 spaces=0;
  923.             }
  924.             s+=inc;
  925.         }
  926.     }
  927. }
  928.  
  929. insrt(p)
  930. int p;
  931. {
  932.     int i;
  933.     for (i=assylen; i>p; i--) assyline[i]=assyline[i-1];
  934.     assylen++;
  935. }
  936.  
  937. newpage()
  938. {
  939.     if (page_no >= 0) endpage();
  940.     else page_no=1;
  941.     for (; o_sk; o_sk--) blankpage();
  942.     beginpage();
  943. }
  944.  
  945. beginpage()
  946. {
  947.     int i;
  948.     if (sflag) waitawhile();
  949.     for (i=0; i<o_m1; i++) spit('\n');
  950.     writetitle(page_no&1? ohead: ehead);
  951.     for (i=0; i<o_m2; i++) spit('\n');
  952.     line_no=0;
  953. }
  954.  
  955. endpage()
  956. {
  957.     int i;
  958.     for (i=line_no; i<TXTLEN; i++) blankline();
  959.     for (i=0; i<o_m3; i++) spit('\n');
  960.     writetitle(page_no&1? ofoot: efoot);
  961.     for (i=0; i<o_m4; i++) spit('\n');
  962.     if (o_bp < 0) page_no++;
  963.     else {
  964.         page_no = o_bp;
  965.         o_bp = -1;
  966.     }
  967. }
  968.  
  969. blankpage()
  970. {
  971.     int i;
  972.     if (sflag) waitawhile();
  973.     for (i=0; i<o_m1; i++) spit('\n');
  974.     writetitle(page_no&1? ohead: ehead);
  975.     for (i=0; i<o_m2; i++) spit('\n');
  976.     for (i=0; i<TXTLEN; i++) spit('\n');
  977.     for (i=0; i<o_m3; i++) spit('\n');
  978.     writetitle(page_no&1? ofoot: efoot);
  979.     page_no++;
  980.     for (i=0; i<o_m4; i++) spit('\n');
  981.     line_no=0;
  982. }
  983.  
  984. waitawhile()
  985. {
  986.     int nix(), oldflags;
  987.     if (isatty(0)) {
  988.         oldflags=tty.sg_flags;
  989.         tty.sg_flags &= ~ECHO;    /* DON'T ECHO THE RUBOUT */
  990.         stty(0,&tty);
  991.     }
  992.     signal(SIGINT,nix);
  993.     pause();
  994.     if (isatty(0)) {
  995.         tty.sg_flags = oldflags;
  996.         stty(0,&tty);
  997.     }
  998. }
  999.  
  1000. nix()
  1001. {}
  1002.  
  1003. writetitle(t)
  1004. char *t;
  1005. {
  1006.     char d, *pst, *pgform();
  1007.     int j, l, m, n;
  1008.     d = *t;
  1009.     if (o_hx || !d) {
  1010.         spit('\n');
  1011.         return;
  1012.     }
  1013.     pst=pgform();
  1014.     for (j=0; j<o_po; j++) spit(' ');
  1015.     l=titlen(++t,d,strlen(pst));
  1016.     while (*t && *t!=d) {
  1017.         if (*t=='%') spits(pst);
  1018.         else spit(*t);
  1019.         t++;
  1020.     }
  1021.     if (!*t) {
  1022.         spit('\n');
  1023.         return;
  1024.     }
  1025.     m=titlen(++t,d,strlen(pst));
  1026.     for (j=l; j<(o_ll-m)/2; j++) spit(' ');
  1027.     while (*t && *t!=d) {
  1028.         if (*t=='%') spits(pst);
  1029.         else spit(*t);
  1030.         t++;
  1031.     }
  1032.     if (!*t) {
  1033.         spit('\n');
  1034.         return;
  1035.     }
  1036.     if ((o_ll-m)/2 > l) m+=(o_ll-m)/2;
  1037.     else m+=l;
  1038.     n=titlen(++t,d,strlen(pst));
  1039.     for (j=m; j<o_ll-n; j++) spit(' ');
  1040.     while (*t && *t!=d) {
  1041.         if (*t=='%') spits(pst);
  1042.         else spit(*t);
  1043.         t++;
  1044.     }
  1045.     spit('\n');
  1046. }
  1047.  
  1048. char *
  1049. pgform()
  1050. {
  1051.     static char pst[11];
  1052.     int i;
  1053.     if (o_ro) {
  1054.         *pst='\0';
  1055.         i=page_no;
  1056.         if (i>=400) {
  1057.             strcat(pst,"cd");
  1058.             i-=400;
  1059.         }
  1060.         while (i>=100) {
  1061.             strcat(pst,"c");
  1062.             i-=100;
  1063.         }
  1064.         if (i>=90) {
  1065.             strcat(pst,"xc");
  1066.             i-=90;
  1067.         }
  1068.         if (i>=50) {
  1069.             strcat(pst,"l");
  1070.             i-=50;
  1071.         }
  1072.         if (i>=40) {
  1073.             strcat(pst,"xl");
  1074.             i-=40;
  1075.         }
  1076.         while (i>=10) {
  1077.             strcat(pst,"x");
  1078.             i-=10;
  1079.         }
  1080.         if (i>=9) {
  1081.             strcat(pst,"ix");
  1082.             i-=9;
  1083.         }
  1084.         if (i>=5) {
  1085.             strcat(pst,"v");
  1086.             i-=5;
  1087.         }
  1088.         if (i>=4) {
  1089.             strcat(pst,"iv");
  1090.             i-=4;
  1091.         }
  1092.         while (i--) strcat(pst,"i");
  1093.     }
  1094.     else sprintf(pst,"%d",page_no);
  1095.     return pst;
  1096. }
  1097.  
  1098. int
  1099. titlen(t,c,k)
  1100. char *t, c;
  1101. int k;
  1102. {
  1103.     int q;
  1104.     q=0;
  1105.     while (*t && *t!=c) q += *t++ == '%' ? k : 1;
  1106.     return q;
  1107. }
  1108.  
  1109. spits(s)
  1110. char *s;
  1111. {
  1112.     while (*s) spit(*s++);
  1113. }
  1114.  
  1115. spit(c)
  1116. char c;
  1117. {
  1118.     static int col_no, n_blanks;
  1119.     int ulflag;
  1120.     char *t;
  1121.     ulflag=c&UNDERL;
  1122.     c&=~UNDERL;
  1123.     for (t = (char *)o_tr; *t; t++) if (*t++==c) {
  1124. /*
  1125.  *    fix - last char translates to space.
  1126.  */
  1127.         c = *t? *t: ' ';
  1128.         break;
  1129.     }
  1130.     if (page_no < startpage || (stoppage && page_no > stoppage)) return;
  1131.     if (c != ' ' && c != '\n' && n_blanks) {
  1132.         if (hflag && n_blanks>1)
  1133.         while (col_no/8 < (col_no+n_blanks)/8) {
  1134.             putc('\t',stdout);
  1135.             n_blanks-= 8 - (col_no & 07);
  1136.             col_no = 8 + col_no & ~07;
  1137.         }
  1138.         for (; n_blanks; n_blanks--) {
  1139.             putc(' ',stdout);
  1140.             col_no++;
  1141.         }
  1142.     }
  1143.     if (ulflag && isalnum(c)) fputs("_\b",stdout);
  1144.     if (c == ' ') n_blanks++;
  1145.     else {
  1146.         putc(c,stdout);
  1147.         col_no++;
  1148.     }
  1149.     if (c == '\n') {
  1150.         col_no=0;
  1151.         n_blanks=0;
  1152.     }
  1153. }
  1154.  
  1155. int
  1156. suck()
  1157. {
  1158.     for (;;) {
  1159.         c=getc(File);
  1160.         if (islegal(c)) return c;
  1161.     }
  1162. }
  1163.  
  1164. /*
  1165.  *    strhas - does string have character?  Allow UNDERL flag.
  1166.  */
  1167.  
  1168. char *
  1169. strhas(p,c)
  1170. char *p, c;
  1171. {
  1172.     for (; *p; p++) if ((*p&~UNDERL)==c) return p;
  1173.     return NULL;
  1174. }
  1175.  
  1176. /*
  1177.  *    strend - find NULL at end of string.
  1178.  */
  1179.  
  1180. char *
  1181. strend(p)
  1182. char *p;
  1183. {
  1184.     while (*p++);
  1185.     return p;
  1186. }
  1187.  
  1188. /*
  1189.  *    isspace, isalnum, isdigit, islegal - classify a character.
  1190.  *    We could just as well use <ctype.h> if it didn't vary from
  1191.  *    one version of Unix to another.  As it is, these routines
  1192.  *    must be modified for weird character sets, like EBCDIC and
  1193.  *    CDC Scientific.
  1194.  */
  1195.  
  1196. int
  1197. isspace(c)
  1198. int c;
  1199. {
  1200.     char *s;
  1201.     for (s=spacechars; *s; s++) if (*s==c) return 1;
  1202.     return 0;
  1203. }
  1204.  
  1205. int
  1206. isalnum(c)
  1207. int c;
  1208. {
  1209.     return (c>='A'&&c<='Z') || (c>='a'&&c<='z') || (c>='0'&&c<='9');
  1210. }
  1211.  
  1212. int
  1213. isdigit(c)
  1214. int c;
  1215. {
  1216.     return c>='0' && c<='9';
  1217. }
  1218.  
  1219. int
  1220. islegal(c)
  1221. int c;
  1222. {
  1223.     return c>=' ' && c<='~' || isspace(c) || c=='\n' || c==EOF;
  1224. }
  1225.  
  1226. bomb()
  1227. {
  1228.     fprintf(stderr,"usage: roff [+00] [-00] [-s] [-h] file ...\n");
  1229.     exit(1);
  1230. }
  1231.