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