home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 189_01 / roff.c < prev    next >
Text File  |  1985-08-21  |  11KB  |  535 lines

  1. /* text formatter transcribed from the book Software Tools */
  2.  
  3. #include <ctype.h>
  4. #include <stdio.h>
  5.  
  6. #define PAGEWIDTH 60    /* default page width */
  7. #define PAGELEN    66        /* default page length */
  8. #define MAXLINE 255
  9. #define PAGECHAR '#'    /* page number escape char */
  10. #define HUGE 32700
  11. #define YES    1
  12. #define NO    0
  13. #define OK    1
  14.  
  15. #define max(a,b) (a >= b) ? a : b
  16. #define min(a,b) (a <= b) ? a : b
  17. #define abs(x)   (x >= 0) ? x : -x
  18.  
  19. int    fill = YES,                /* in fill mode if YES */
  20.        lsval = 1,                /* current line spacing */
  21.        inval = 0,                /* current indent; >= 0 */
  22.        rmval = PAGEWIDTH,            /* current right margin */
  23.        tival = 0,                /* current temporary indent */
  24.        ceval = 0,                /* number of lines to center */
  25.        ulval = 0,                /* number of lines to underline */
  26.        curpag = 0,                /* current output page number */
  27.        newpag = 1,                /* next output page number */
  28.        lineno = 0,                /* next line to be printed */
  29.        plval = PAGELEN,        /* page length in lines */
  30.        m1val = 2,                /* top margin, including header */
  31.        m2val = 3,                /* margin after header */
  32.        m3val = 2,                /* margin after last text line */
  33.        m4val = 3,                /* bottom margin, including footer */
  34.        bottom,                    /* last live line on page: */
  35.                             /*  = plval - m3val - m4val */
  36.        outp,                    /* index into outbuf */
  37.        outw,                    /* width of text in outbuf */
  38.        outwds,                    /* number of words in outbuf */
  39.      dir;                    /* directions flag */
  40.  
  41. char header[MAXLINE],          /* top of page title */
  42.      footer[MAXLINE],          /* bottom of page title */
  43.      outbuf[MAXLINE];        /* lines to be filled go here */
  44.  
  45. main(argc,argv)
  46. int argc;
  47. char *argv[];
  48. {
  49.     register i;
  50.  
  51.     init();
  52.  
  53.     for (i = 1; i < argc; ++i)
  54.         if (freopen(argv[i],"r",stdin) == NULL)
  55.             fprintf(stderr,"can't open %s\n",argv[i]);
  56.         else
  57.             roff();
  58. }
  59.  
  60.  
  61. /* initialize header, footer, and line-count invariant */
  62.  
  63. init()
  64. {
  65.     bottom = plval - m3val - m4val; /* invariant */
  66.     strcpy(header,"\n");
  67.     strcpy(footer,"\n");
  68. }
  69.  
  70.  
  71. /* format current file */
  72.  
  73. roff()
  74. {
  75.     char inbuf[MAXLINE];
  76.  
  77.     while (fgets(inbuf,MAXLINE-1,stdin) != NULL)
  78.         if (inbuf[0] == '.')
  79.             command(inbuf);
  80.         else
  81.             text(inbuf);
  82.  
  83.     if (lineno > 0)
  84.         space(HUGE);
  85. }
  86.  
  87.  
  88. /* perform formatting command */
  89.  
  90. command(buf)
  91. char *buf;
  92. {
  93.     int val, spval;
  94.     char *argtyp;
  95.  
  96.     val = getval(buf,argtyp);
  97.     if (lookup(buf,"bp"))
  98.     {
  99.         if (lineno > 0)
  100.             space(HUGE);
  101.         set(&curpag,val,argtyp,curpag+1,-HUGE,HUGE);
  102.         newpag = curpag;
  103.     }
  104.     else if (lookup(buf,"br"))
  105.         brk();
  106.     else if (lookup(buf,"ce"))
  107.     {
  108.         brk();
  109.         set(&ceval,val,argtyp,1,0,HUGE);
  110.     }
  111.     else if (lookup(buf,"fi"))
  112.     {
  113.         brk();
  114.         fill = YES;
  115.     }
  116.     else if (lookup(buf,"fo"))
  117.          strcpy(footer,buf+3);
  118.     else if (lookup(buf,"he"))
  119.         strcpy(header,buf+3);
  120.     else if (lookup(buf,"in"))
  121.     {
  122.         set(&inval,val,argtyp,0,0,rmval-1);
  123.         tival = inval;
  124.     }
  125.     else if (lookup(buf,"ls"))
  126.         set(&lsval,val,argtyp,1,1,HUGE);
  127.     else if (lookup(buf,"nf"))
  128.     {
  129.         brk();
  130.         fill = NO;
  131.     }
  132.     else if (lookup(buf,"pl"))
  133.     {
  134.         set(&plval,val,argtyp,PAGELEN,m1val+m2val+m3val+m4val+1,HUGE);
  135.         bottom=plval-m3val-m4val;
  136.     }
  137.     else if (lookup(buf,"rm"))
  138.         set(&rmval,val,argtyp,PAGEWIDTH,tival+1,HUGE);
  139.     else if (lookup(buf,"sp"))
  140.     {
  141.         set(&spval,val,argtyp,1,0,HUGE);
  142.         space(spval);
  143.     }
  144.     else if (lookup(buf,"ti"))
  145.     {
  146.         brk();
  147.         set(&tival,val,argtyp,0,0,rmval);
  148.     }
  149.     else if (lookup(buf,"ul"))
  150.         set(&ulval,val,argtyp,0,1,HUGE);
  151.     else
  152.         return;    /* ignore unknown commands */
  153. }
  154.  
  155.  
  156. /* lookup routine for commands */
  157.  
  158. lookup(buf,string)
  159. char *buf, *string;
  160. {
  161.     return (buf[1] == string[0]) && (buf[2] == string[1]);
  162. }
  163.  
  164.  
  165. /* evaluate optional numeric argument */
  166.  
  167. getval(buf,argtyp)
  168. char *buf, *argtyp;
  169. {
  170.     int i;
  171.  
  172.     i = 3;
  173.     /* ..find argument.. */
  174.     while (buf[i] == ' ' || buf[i] == '\t')
  175.         ++i;
  176.     *argtyp = buf[i];
  177.     if (*argtyp == '+' || *argtyp=='-')
  178.         i++;
  179.  
  180.     return(atoi(buf+i));
  181. }
  182.  
  183.  
  184. /* set parameter and check range */
  185.  
  186. set(param,val,argtyp,defval,minval,maxval)
  187. int *param,val,defval,minval,maxval;
  188. char *argtyp;
  189. {
  190.     if (*argtyp == '\n')
  191.         *param = defval;
  192.     else if (*argtyp == '+')
  193.         *param += val;
  194.     else if (*argtyp == '-')
  195.         *param -= val;
  196.     else
  197.         *param = val;
  198.  
  199.     *param = min(*param,maxval);
  200.     *param = max(*param,minval);
  201. }
  202.  
  203. /* process text lines */
  204.  
  205. text(inbuf)
  206. char *inbuf;
  207. {
  208.     char wrdbuf[MAXLINE];
  209.     int i;
  210.  
  211.     if (isspace(*inbuf))
  212.         leadbl(inbuf);    /* go left. set tival */
  213.  
  214.     if (ulval > 0)
  215.     {
  216.         underl(inbuf,wrdbuf,MAXLINE);
  217.         ulval--;
  218.     }
  219.     if (ceval > 0)
  220.     {
  221.         center(inbuf);
  222.         put(inbuf);
  223.         ceval--;
  224.     }
  225.     else if (*inbuf == '\n')
  226.         put(inbuf);
  227.     else if (fill == NO)
  228.         put(inbuf);
  229.     else
  230.     {
  231.         i = 0;
  232.         while (getwrd(inbuf,&i,wrdbuf) > 0)
  233.             putwrd(wrdbuf);
  234.     }
  235. }
  236.  
  237.  
  238. /* delete leading blanks.  Set tival */
  239.  
  240. leadbl(buf)
  241. char *buf;
  242. {
  243.     register i,j;
  244.  
  245.     brk();
  246.  
  247.     /* find first non blank */
  248.     i = 0;
  249.     while (buf[i] == ' ')
  250.         i++;
  251.  
  252.     if (buf[i] != '\n')
  253.         tival += i;
  254.  
  255.     /* move line to left */
  256.     j = 0;
  257.     while ((buf[j++] = buf[i++]) != '\0') ;
  258. }
  259.  
  260.  
  261. /* put out line with proper spacing and indenting */
  262.  
  263. put(buf)
  264. char *buf;
  265. {
  266.     register i;
  267.  
  268.     if ((lineno == 0) || (lineno > bottom))
  269.         phead();
  270.  
  271.     i = 1;
  272.     while (i++ <= tival)
  273.         putchar(' ');
  274.  
  275.     tival = inval;    /* tival good for one line only */
  276.     fputs(buf,stdout);
  277.     skip(min(lsval-1,bottom-lineno));
  278.     lineno += lsval;
  279.     if (lineno > bottom)
  280.         pfoot();
  281. }
  282.  
  283.  
  284. /* put out page header */
  285.  
  286. phead()
  287. {
  288.     curpag = newpag++;
  289.     if (m1val > 0)
  290.     {
  291.         skip(m1val-1);
  292.         puttl(header,curpag);
  293.     }
  294.     skip(m2val);
  295.     lineno = m1val+m2val+1;
  296. }
  297.  
  298.  
  299. /* put out page footer */
  300.  
  301. pfoot()
  302. {
  303.     skip(m3val);
  304.     if (m4val > 0)
  305.     {
  306.         puttl(footer,curpag);
  307.         skip(m4val-1);
  308.     }
  309. }
  310.  
  311.  
  312. /* put out title line with optional page number */
  313.  
  314. puttl(buf,pageno)
  315. char *buf;
  316. int pageno;
  317. {
  318.     register i;
  319.  
  320.     for (i = 0; buf[i] != '\n' && buf[i] != '\0'; i++)
  321.         if (buf[i] == PAGECHAR)
  322.             printf("%*d",1,pageno);
  323.         else
  324.             putchar(buf[i]);
  325.     putchar('\n');
  326. }
  327.  
  328.  
  329. /* space n lines or to bottom of page */
  330.  
  331. space(n)
  332. int n;
  333. {
  334.     brk();
  335.     if (lineno > bottom)
  336.         return;
  337.  
  338.     if (lineno == 0)
  339.         phead();
  340.  
  341.     skip(min(n,bottom+1-lineno));
  342.     lineno += n;
  343.     if (lineno > bottom)
  344.         pfoot();
  345. }
  346.  
  347.  
  348. /* output n blank lines */
  349.  
  350. skip(n)
  351. register n;
  352. {
  353.     while ((n--) > 0)
  354.         putchar('\n');
  355. }
  356.  
  357.  
  358. /* get non-blank word from in[i] into out.
  359.  * increment *i.
  360.  */
  361.  
  362. getwrd(in,ii,out)
  363. char *in, *out;
  364. int *ii;
  365. {
  366.     register i, j;
  367.  
  368.     i = *ii;
  369.     while ((in[i]  == ' ') || (in[i] == '\t'))
  370.         i++;
  371.  
  372.     j = 0;
  373.     while ( (in[i] != '\0') && !isspace(in[i]) )
  374.         out[j++] = in[i++];
  375.  
  376.     out[j] = '\0';
  377.     *ii = i;        /* return index in ii */
  378.     return(j);    /* return length of word */
  379. }
  380.  
  381.  
  382. /* put a word in outbuf; includes margin justification */
  383.  
  384. putwrd(wrdbuf)
  385. char *wrdbuf;
  386. {
  387.     int last, llval, w, nextra;
  388.  
  389.     w = width(wrdbuf);
  390.     /* new end of wrdbuf */
  391.     last = strlen(wrdbuf)+outp+1;
  392.     llval = rmval-tival;
  393.     if ( (outp > 0) && ((outw+w>llval) || (last>=MAXLINE)) )
  394.     {
  395.         /* too big */
  396.         last -= outp;    /* remember end of wrdbuf */
  397.         nextra = llval-outw+1;
  398.         spread(outbuf,outp-1,nextra,outwds);
  399.         if (nextra > 0 && outwds > 1)
  400.             outp += nextra;
  401.         brk();    /* flush previous line */
  402.     }
  403.     strcpy(outbuf+outp,wrdbuf);
  404.     outp = last;
  405.     outbuf[outp-1] = ' ';    /* blank between words */
  406.     outw += w+1;            /* 1 extra for blank */
  407.     outwds++;
  408. }
  409.  
  410.  
  411. /* spread words to justify right margin */
  412.  
  413. spread(buf,outp,ne,outwds)
  414. char *buf;
  415. int outp, ne, outwds;
  416. {
  417.     register nholes, i, j, nb;
  418.  
  419.     if (ne <= 0 || outwds <= 1)
  420.         return;
  421.  
  422.     dir = 1-dir;    /* reverse direction */
  423.     nholes = outwds-1;
  424.     i = outp-1;
  425.     /* leave room for NEWLINE, EOS */
  426.     j = min(MAXLINE-2,i+ne);
  427.     while (i < j)
  428.     {
  429.         buf[j] = buf[i];
  430.         if (buf[i] == ' ')
  431.         {
  432.             /* compute # of blanks */
  433.             if (dir == 0)
  434.                 nb = (ne-1)/nholes+1;
  435.             else
  436.                 nb = ne/nholes;
  437.             ne -= nb;
  438.             nholes--;
  439.             /* put blanks in buffer */
  440.             while (nb-- > 0)
  441.                 buf[--j] = ' ';
  442.         }
  443.         i--;
  444.         j--;
  445.     }
  446. }
  447.  
  448.  
  449. /* compute width of character string */
  450.  
  451. width(buf)
  452. char *buf;
  453. {
  454.     register i, val;
  455.  
  456.     for (val = i = 0; buf[i] != '\0'; i++)
  457.         if (buf[i] == '\b')
  458.             val--;
  459.         else if (buf[i] != '\n')
  460.             val++;
  461.     return(val);
  462. }
  463.  
  464.  
  465. /* end current filled line */
  466.  
  467. brk()
  468. {
  469.     if (outp > 0)
  470.     {
  471.         outbuf[outp-1] = '\n';
  472.         outbuf[outp] = '\0';
  473.         put(outbuf);
  474.     }
  475.     outp = outw = outwds = 0;
  476. }
  477.  
  478.  
  479. /* center a line by setting tival */
  480.  
  481. center(buf)
  482. char *buf;
  483. {
  484.     tival = max((rmval+tival-width(buf))/2,0);
  485. }
  486.  
  487.  
  488. /* underline a line */
  489.  
  490. underl(buf,tbuf,size)
  491. char *buf, *tbuf;
  492. int size;
  493. {
  494.     int i, j,
  495.         k, k1;    /* how many chars in last word */
  496.  
  497.     i = j = k = 0;
  498.     while (j < size-2)
  499.     {
  500.         if (isspace(buf[i]))
  501.         {
  502.             k1 = k;
  503.             while (k-- > 0)
  504.             {
  505.                 if (j >= size-2)
  506.                     break;
  507.                 tbuf[j++] = '\b';
  508.             }
  509.             k = k1;
  510.             while (k-- > 0) {
  511.                 if (j >= size-2)
  512.                     break;
  513.                 tbuf[j++] = '_';
  514.             }
  515.         }
  516.         if (buf[i] == '\n')
  517.             break;
  518.         else if (j >= size-2)
  519.             break;
  520.         else
  521.         {
  522.             if (buf[i] != ' ' && buf[i] != '\t')
  523.                 k++;
  524.             else
  525.                 k = 0;
  526.             tbuf[j++] = buf[i++];
  527.         }
  528.     }
  529.     tbuf[j++] = '\n';
  530.     tbuf[j] = '\0';
  531.     strcpy(buf,tbuf);
  532. }
  533.        while (k-- > 0) {
  534.                 if (j >= size-2)
  535.