home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / par150o2.zip / par.c < prev    next >
C/C++ Source or Header  |  1996-02-06  |  26KB  |  845 lines

  1. /*********************/
  2. /* par.c             */
  3. /* for Par 1.50      */
  4. /* Copyright 1996 by */
  5. /* Adam M. Costello  */
  6. /*********************/
  7.  
  8. /* This is ANSI C code. */
  9.  
  10.  
  11. #include "charset.h"   /* Also includes "errmsg.h". */
  12. #include "buffer.h"    /* Also includes <stddef.h>. */
  13. #include "reformat.h"
  14.  
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include <ctype.h>
  19.  
  20. #undef NULL
  21. #define NULL ((void *) 0)
  22.  
  23. #ifdef DONTFREE
  24. #define free(ptr)
  25. #endif
  26.  
  27.  
  28. static const char * const usagemsg =
  29. "\n"
  30. "Options for par:\n"
  31. "\n"
  32. "help       print option summary      "
  33.                                  "  ---------- Boolean parameters: ---------\n"
  34. "version    print version number      "
  35.                                  "  b<body>   let non-trailing body chars in\n"
  36. "B<op><set> as <op> is =/+/-,         "
  37.                                  "            prefix, non-leading in suffix\n"
  38. "           replace/augment/diminish  "
  39.                                  "  c<cap>    count all words as capitalized\n"
  40. "           body chars by <set>       "
  41.                                  "  d<div>    use indentation as a delimiter\n"
  42. "P<op><set> ditto for protective chars"
  43.                                  "  E<Err>    send messages to stderr\n"
  44. "Q<op><set> ditto for quote chars     "
  45.                                  "  e<expel>  discard superfluous lines\n"
  46. "-------- Integer parameters: --------"
  47.                                  "  f<fit>    narrow paragraph for best fit\n"
  48. "h<hang>    skip IP's 1st <hang> lines"
  49.                                  "  g<guess>  preserve wide sentence breaks\n"
  50. "           in scan for common affixes"
  51.                                  "  i<invis>  hide lines inserted by <quote>\n"
  52. "p<prefix>  prefix length             "
  53.                                  "  j<just>   justify paragraphs\n"
  54. "r<repeat>  if not 0, force bodiless  "
  55.                                  "  l<last>   treat last lines like others\n"
  56. "           lines to length <width>   "
  57.                                  "  q<quote>  supply vacant lines between\n"
  58. "s<suffix>  suffix length             "
  59.                                  "            different quote nesting levels\n"
  60. "T<Tab>     tab stops every <Tab> cols"
  61.                                  "  R<Report> print error for too-long words\n"
  62. "w<width>   max output line length    "
  63.                                  "  t<touch>  move suffixes left\n"
  64. "\n"
  65. "See par.doc or par.1 (the man page) for more information.\n"
  66. "\n"
  67. ;
  68.  
  69.  
  70. /* Structure for recording properties of lines within segments: */
  71.  
  72. typedef unsigned char lflag_t;
  73.  
  74. typedef struct lineprop {
  75.   short p, s;     /* Length of the prefix and suffix of a bodiless */
  76.                   /* line, or the fallback prelen and suflen       */
  77.                   /* of the IP containing a non-bodiless line.     */
  78.   lflag_t flags;  /* Boolean properties (see below).               */
  79.   char rc;        /* The repeated character of a bodiless line.    */
  80. } lineprop;
  81.  
  82. /* Flags for marking boolean properties: */
  83.  
  84. static const lflag_t L_BODILESS = 1,  /* Bodiless line.             */
  85.                      L_INVIS    = 2,  /* Invisible line.            */
  86.                      L_FIRST    = 4,  /* First line of a paragraph. */
  87.                      L_SUPERF   = 8;  /* Superfluous line.          */
  88.  
  89. #define isbodiless(prop) ( (prop)->flags & 1)
  90. #define    isinvis(prop) (((prop)->flags & 2) != 0)
  91. #define    isfirst(prop) (((prop)->flags & 4) != 0)
  92. #define   issuperf(prop) (((prop)->flags & 8) != 0)
  93. #define   isvacant(prop) (isbodiless(prop) && (prop)->rc == ' ')
  94.  
  95.  
  96. static int digtoint(char c)
  97.  
  98. /* Returns the value represented by the digit c, or -1 if c is not a digit. */
  99. {
  100.   const char *p, * const digits = "0123456789";
  101.  
  102.   if (!c) return -1;
  103.   p = strchr(digits,c);
  104.   return  p  ?  p - digits  :  -1;
  105.  
  106.   /* We can't simply return c - '0' because this is ANSI C code,  */
  107.   /* so it has to work for any character set, not just ones which */
  108.   /* put the digits together in order.  Also, an array that could */
  109.   /* be referenced as digtoint[c] might be bad because there's no */
  110.   /* upper limit on CHAR_MAX.                                     */
  111. }
  112.  
  113.  
  114. static int strtoudec(const char *s, int *pn)
  115.  
  116. /* Converts the longest prefix of string s consisting of decimal   */
  117. /* digits to an integer, which is stored in *pn.  Normally returns */
  118. /* 1.  If *s is not a digit, then *pn is not changed, but 1 is     */
  119. /* still returned.  If the integer represented is greater than     */
  120. /* 9999, then *pn is not changed and 0 is returned.                */
  121. {
  122.   int n = 0, d;
  123.  
  124.   d = digtoint(*s);
  125.   if (d < 0) return 1;
  126.  
  127.   do {
  128.     if (n >= 1000) return 0;
  129.     n = 10 * n + d;
  130.     d = digtoint(*++s);
  131.   } while (d >= 0);
  132.  
  133.   *pn = n;
  134.  
  135.   return 1;
  136. }
  137.  
  138.  
  139. static void parsearg(
  140.   const char *arg, int *phelp, int *pversion, charset *bodychars, charset
  141.   *protectchars, charset *quotechars, int *phang, int *pprefix, int *prepeat,
  142.   int *psuffix, int *pTab, int *pwidth, int *pbody, int *pcap, int *pdiv, int
  143.   *pErr, int *pexpel, int *pfit, int *pguess, int *pinvis, int *pjust, int
  144.   *plast, int *pquote, int *pReport, int *ptouch, errmsg_t errmsg
  145. )
  146. /* Parses the command line argument in *arg, setting the objects pointed to */
  147. /* by the other pointers as appropriate.  *phelp and *pversion are boolean  */
  148. /* flags indicating whether the help and version options were supplied.     */
  149. {
  150.   const char *savearg = arg;
  151.   charset *chars, *change;
  152.   char oc;
  153.   int n;
  154.  
  155.   *errmsg = '\0';
  156.  
  157.   if (*arg == '-') ++arg;
  158.  
  159.   if (!strcmp(arg, "help")) {
  160.     *phelp = 1;
  161.     return;
  162.   }
  163.  
  164.   if (!strcmp(arg, "version")) {
  165.     *pversion = 1;
  166.     return;
  167.   }
  168.  
  169.   if (*arg == 'B' || *arg == 'P' || *arg == 'Q' ) {
  170.     chars =  *arg == 'B'  ?  bodychars    :
  171.              *arg == 'P'  ?  protectchars :
  172.           /* *arg == 'Q' */  quotechars   ;
  173.     ++arg;
  174.     if (*arg != '='  &&  *arg != '+'  &&  *arg != '-') goto badarg;
  175.     change = parsecharset(arg + 1, errmsg);
  176.     if (change) {
  177.       if      (*arg == '=')   csswap(chars,change);
  178.       else if (*arg == '+')   csadd(chars,change,errmsg);
  179.       else  /* *arg == '-' */ csremove(chars,change,errmsg);
  180.       freecharset(change);
  181.     }
  182.     return;
  183.   }
  184.  
  185.   if (isdigit(*arg)) {
  186.     if (!strtoudec(arg, &n)) goto badarg;
  187.     if (n <= 8) *pprefix = n;
  188.     else *pwidth = n;
  189.   }
  190.  
  191.   for (;;) {
  192.     while (isdigit(*arg)) ++arg;
  193.     oc = *arg;
  194.     if (!oc) break;
  195.     n = -1;
  196.     if (!strtoudec(++arg, &n)) goto badarg;
  197.     if (   oc == 'h' || oc == 'p' || oc == 'r'
  198.         || oc == 's' || oc == 'T' || oc == 'w') {
  199.       if      (oc == 'h')   *phang   =  n >= 0 ? n :  1;
  200.       else if (oc == 'p')   *pprefix =  n;
  201.       else if (oc == 'r')   *prepeat =  n >= 0 ? n :  3;
  202.       else if (oc == 's')   *psuffix =  n;
  203.       else if (oc == 'T')   *pTab    =  n >= 0 ? n :  8;
  204.       else  /* oc == 'w' */ *pwidth  =  n >= 0 ? n : 79;
  205.     }
  206.     else {
  207.       if (n < 0) n = 1;
  208.       if (n > 1) goto badarg;
  209.       if      (oc == 'b') *pbody   = n;
  210.       else if (oc == 'c') *pcap    = n;
  211.       else if (oc == 'd') *pdiv    = n;
  212.       else if (oc == 'E') *pErr    = n;
  213.       else if (oc == 'e') *pexpel  = n;
  214.       else if (oc == 'f') *pfit    = n;
  215.       else if (oc == 'g') *pguess  = n;
  216.       else if (oc == 'i') *pinvis  = n;
  217.       else if (oc == 'j') *pjust   = n;
  218.       else if (oc == 'l') *plast   = n;
  219.       else if (oc == 'q') *pquote  = n;
  220.       else if (oc == 'R') *pReport = n;
  221.       else if (oc == 't') *ptouch  = n;
  222.       else goto badarg;
  223.     }
  224.   }
  225.  
  226.   return;
  227.  
  228. badarg:
  229.  
  230.   sprintf(errmsg, "Bad argument: %.*s\n", errmsg_size - 16, savearg);
  231.   *phelp = 1;
  232. }
  233.  
  234.  
  235. static char **readlines(
  236.   lineprop **pprops, const charset *protectchars,
  237.   const charset *quotechars, int Tab, int invis, int quote, errmsg_t errmsg
  238. )
  239. /* Reads lines from stdin until EOF, or until a line beginning with a   */
  240. /* protective character is encountered (in which case the protective    */
  241. /* character is pushed back onto the input stream), or until a blank    */
  242. /* line is encountered (in which case the newline is pushed back onto   */
  243. /* the input stream).  Returns a NULL-terminated array of pointers to   */
  244. /* individual lines, stripped of their newline characters.  Every NUL   */
  245. /* character is stripped, and every white character is changed to a     */
  246. /* space unless it is a newline.  If quote is 1, vacant lines will be   */
  247. /* supplied as described for the q option in par.doc.  *pprops is set   */
  248. /* to an array of lineprop structures, one for each line, each of whose */
  249. /* flags field is either 0 or L_INVIS (the other fields are 0).  If     */
  250. /* there are no lines, *pprops is set to NULL.  The returned array may  */
  251. /* be freed with freelines().  *pprops may be freed with free() if      */
  252. /* it's not NULL.  On failure, returns NULL and sets *pprops to NULL.   */
  253. {
  254.   buffer *cbuf = NULL, *lbuf = NULL, *lpbuf = NULL;
  255.   int c, empty, blank, firstline, qsonly, oldqsonly = 0, vlnlen, i;
  256.   char ch, *ln = NULL, nullchar = '\0', *nullline = NULL, *qpend,
  257.        *oldln = NULL, *oldqpend = NULL, *p, *op, *vln = NULL, **lines = NULL;
  258.   lineprop vprop = { 0, 0, 0, '\0' }, iprop = { 0, 0, 0, '\0' };
  259.  
  260.   /* oldqsonly, oldln, and oldquend don't really need to be initialized.   */
  261.   /* They are initialized only to appease compilers that try to be helpful */
  262.   /* by issuing warnings about unitialized automatic variables.            */
  263.  
  264.   iprop.flags = L_INVIS;
  265.   *errmsg = '\0';
  266.  
  267.   *pprops = NULL;
  268.  
  269.   cbuf = newbuffer(sizeof (char), errmsg);
  270.   if (*errmsg) goto rlcleanup;
  271.   lbuf = newbuffer(sizeof (char *), errmsg);
  272.   if (*errmsg) goto rlcleanup;
  273.   lpbuf = newbuffer(sizeof (lineprop), errmsg);
  274.   if (*errmsg) goto rlcleanup;
  275.  
  276.   for (empty = blank = firstline = 1;  ;  ) {
  277.     c = getchar();
  278.     if (c == EOF) break;
  279.     if (c == '\n') {
  280.       if (blank) {
  281.         ungetc(c,stdin);
  282.         break;
  283.       }
  284.       additem(cbuf, &nullchar, errmsg);
  285.       if (*errmsg) goto rlcleanup;
  286.       ln = copyitems(cbuf,errmsg);
  287.       if (*errmsg) goto rlcleanup;
  288.       if (quote) {
  289.         for (qpend = ln;
  290.              *qpend && csmember(*qpend, quotechars);
  291.              ++qpend);
  292.         for (p = qpend;  *p == ' ' || csmember(*p, quotechars);  ++p);
  293.         qsonly =  *p == '\0';
  294.         while (qpend > ln && qpend[-1] == ' ') --qpend;
  295.         if (!firstline) {
  296.           for (p = ln, op = oldln;
  297.                p < qpend && op < oldqpend && *p == *op;
  298.                ++p, ++op);
  299.           if (!(p == qpend && op == oldqpend))
  300.             if (!invis && (oldqsonly || qsonly)) {
  301.               if (oldqsonly) {
  302.                 *op = '\0';
  303.                 oldqpend = op;
  304.               }
  305.               if (qsonly) {
  306.                 *p = '\0';
  307.                 qpend = p;
  308.               }
  309.             }
  310.             else {
  311.               vlnlen = p - ln;
  312.               vln = malloc((vlnlen + 1) * sizeof (char));
  313.               if (!vln) {
  314.                 strcpy(errmsg,outofmem);
  315.                 goto rlcleanup;
  316.               }
  317.               strncpy(vln,ln,vlnlen);
  318.               vln[vlnlen] = '\0';
  319.               additem(lbuf, &vln, errmsg);
  320.               if (*errmsg) goto rlcleanup;
  321.               additem(lpbuf,  invis ? &iprop : &vprop,  errmsg);
  322.               if (*errmsg) goto rlcleanup;
  323.               vln = NULL;
  324.             }
  325.         }
  326.         oldln = ln;
  327.         oldqpend = qpend;
  328.         oldqsonly = qsonly;
  329.       }
  330.       additem(lbuf, &ln, errmsg);
  331.       if (*errmsg) goto rlcleanup;
  332.       ln = NULL;
  333.       additem(lpbuf, &vprop, errmsg);
  334.       if (*errmsg) goto rlcleanup;
  335.       clearbuffer(cbuf);
  336.       empty = blank = 1;
  337.       firstline = 0;
  338.     }
  339.     else {
  340.       if (empty) {
  341.         if (csmember((char) c, protectchars)) {
  342.           ungetc(c,stdin);
  343.           break;
  344.         }
  345.         empty = 0;
  346.       }
  347.       if (!c) continue;
  348.       if (c == '\t') {
  349.         ch = ' ';
  350.         for (i = Tab - numitems(cbuf) % Tab;  i > 0;  --i) {
  351.           additem(cbuf, &ch, errmsg);
  352.           if (*errmsg) goto rlcleanup;
  353.         }
  354.         continue;
  355.       }
  356.       if (isspace(c)) c = ' ';
  357.       else blank = 0;
  358.       ch = c;
  359.       additem(cbuf, &ch, errmsg);
  360.       if (*errmsg) goto rlcleanup;
  361.     }
  362.   }
  363.  
  364.   if (!blank) {
  365.     additem(cbuf, &nullchar, errmsg);
  366.     if (*errmsg) goto rlcleanup;
  367.     ln = copyitems(cbuf,errmsg);
  368.     if (*errmsg) goto rlcleanup;
  369.     additem(lbuf, &ln, errmsg);
  370.     if (*errmsg) goto rlcleanup;
  371.     ln = NULL;
  372.     additem(lpbuf, &vprop, errmsg);
  373.     if (*errmsg) goto rlcleanup;
  374.   }
  375.  
  376.   additem(lbuf, &nullline, errmsg);
  377.   if (*errmsg) goto rlcleanup;
  378.   *pprops = copyitems(lpbuf,errmsg);
  379.   if (*errmsg) goto rlcleanup;
  380.   lines = copyitems(lbuf,errmsg);
  381.  
  382. rlcleanup:
  383.  
  384.   if (cbuf) freebuffer(cbuf);
  385.   if (lpbuf) freebuffer(lpbuf);
  386.   if (lbuf) {
  387.     if (!lines)
  388.       for (;;) {
  389.         lines = nextitem(lbuf);
  390.         if (!lines) break;
  391.         free(*lines);
  392.       }
  393.     freebuffer(lbuf);
  394.   }
  395.   if (ln) free(ln);
  396.   if (vln) free(vln);
  397.  
  398.   return lines;
  399. }
  400.  
  401.  
  402. static void compresuflen(
  403.   const char * const *lines, const char * const *endline,
  404.   const charset *bodychars, int body, int pre, int suf, int *ppre, int *psuf
  405. )
  406. /* lines is an array of strings, up to but not including endline.  */
  407. /* Writes into *ppre and *psuf the comprelen and comsuflen of the  */
  408. /* lines in lines.  Assumes that they have already been determined */
  409. /* to be at least pre and suf.  endline must not equal lines.      */
  410. {
  411.   const char *start, *end, *knownstart, * const *line, *p1, *p2, *knownend,
  412.              *knownstart2;
  413.  
  414.   start = *lines;
  415.   end = knownstart = start + pre;
  416.   if (body)
  417.     while (*end) ++end;
  418.   else
  419.     while (*end && !csmember(*end, bodychars)) ++end;
  420.   for (line = lines + 1;  line < endline;  ++line) {
  421.     for (p1 = knownstart, p2 = *line + pre;
  422.          p1 < end && *p1 == *p2;
  423.          ++p1, ++p2);
  424.     end = p1;
  425.   }
  426.   if (body)
  427.     for (p1 = end;  p1 > knownstart;  )
  428.       if (*--p1 != ' ')
  429.         if (csmember(*p1, bodychars))
  430.           end = p1;
  431.         else
  432.           break;
  433.   *ppre = end - start;
  434.  
  435.   knownstart = *lines + *ppre;
  436.   for (end = knownstart;  *end;  ++end);
  437.   knownend = end - suf;
  438.   if (body)
  439.     start = knownstart;
  440.   else
  441.     for (start = knownend;
  442.          start > knownstart && !csmember(start[-1], bodychars);
  443.          --start);
  444.   for (line = lines + 1;  line < endline;  ++line) {
  445.     knownstart2 = *line + *ppre;
  446.     for (p2 = knownstart2;  *p2;  ++p2);
  447.     for (p1 = knownend, p2 -= suf;
  448.          p1 > start && p2 > knownstart2 && p1[-1] == p2[-1];
  449.          --p1, --p2);
  450.     start = p1;
  451.   }
  452.   if (body) {
  453.     for (p1 = start;
  454.          start < knownend && (*start == ' ' || csmember(*start, bodychars));
  455.          ++start);
  456.     if (start > p1 && start[-1] == ' ') --start;
  457.   }
  458.   else
  459.     while (end - start >= 2 && *start == ' ' && start[1] == ' ') ++start;
  460.   *psuf = end - start;
  461. }
  462.  
  463.  
  464. static void delimit(
  465.   const char * const *lines, const char * const *endline,
  466.   const charset *bodychars, int repeat, int body, int div,
  467.   int pre, int suf, lineprop *props
  468. )
  469. /* lines is an array of strings, up to but not including     */
  470. /* endline.  Sets fields in each lineprop in the parallel    */
  471. /* array props as appropriate, except for the L_SUPERF flag, */
  472. /* which is never set.  It is assumed that the comprelen     */
  473. /* and comsuflen of the lines in lines have already been     */
  474. /* determined to be at least pre and suf, respectively.      */
  475. {
  476.   const char * const *line, *end, *p, * const *nextline;
  477.   char rc;
  478.   lineprop *prop, *nextprop;
  479.   int anybodiless = 0, status;
  480.  
  481.   if (endline == lines) return;
  482.  
  483.   if (endline == lines + 1) {
  484.     props->flags |= L_FIRST;
  485.     props->p = pre, props->s = suf;
  486.     return;
  487.   }
  488.  
  489.   compresuflen(lines, endline, bodychars, body, pre, suf, &pre, &suf);
  490.  
  491.   line = lines, prop = props;
  492.   do {
  493.     prop->flags |= L_BODILESS;
  494.     prop->p = pre, prop->s = suf;
  495.     for (end = *line;  *end;  ++end);
  496.     end -= suf;
  497.     p = *line + pre;
  498.     rc =  p < end  ?  *p  :  ' ';
  499.     if (rc != ' ' && (!repeat || end - p < repeat))
  500.       prop->flags &= ~L_BODILESS;
  501.     else
  502.       while (p < end) {
  503.         if (*p != rc) {
  504.           prop->flags &= ~L_BODILESS;
  505.           break;
  506.         }
  507.         ++p;
  508.       }
  509.     if (isbodiless(prop)) {
  510.       anybodiless = 1;
  511.       prop->rc = rc;
  512.     }
  513.     ++line, ++prop;
  514.   } while (line < endline);
  515.  
  516.   if (anybodiless) {
  517.     line = lines, prop = props;
  518.     do {
  519.       if (isbodiless(prop)) {
  520.         ++line, ++prop;
  521.         continue;
  522.       }
  523.  
  524.       for (nextline = line + 1, nextprop = prop + 1;
  525.            nextline < endline && !isbodiless(nextprop);
  526.            ++nextline, ++nextprop);
  527.  
  528.       delimit(line,nextline,bodychars,repeat,body,div,pre,suf,prop);
  529.  
  530.       line = nextline, prop = nextprop;
  531.     } while (line < endline);
  532.  
  533.     return;
  534.   }
  535.  
  536.   if (!div) {
  537.     props->flags |= L_FIRST;
  538.     return;
  539.   }
  540.  
  541.   line = lines, prop = props;
  542.   status = ((*lines)[pre] == ' ');
  543.   do {
  544.     if (((*line)[pre] == ' ') == status)
  545.       prop->flags |= L_FIRST;
  546.     ++line, ++prop;
  547.   } while (line < endline);
  548. }
  549.  
  550.  
  551. static void marksuperf(
  552.   const char * const * lines, const char * const * endline, lineprop *props
  553. )
  554. /* lines points to the first line of a segment, and endline to one  */
  555. /* line beyond the last line in the segment.  Sets L_SUPERF bits in */
  556. /* the flags fields of the props array whenever the corresponding   */
  557. /* line is superfluous.  L_BODILESS bits must already be set.       */
  558. {
  559.   const char * const *line, *p;
  560.   lineprop *prop, *mprop, dummy;
  561.   int inbody, num, mnum;
  562.  
  563.   for (line = lines, prop = props;  line < endline;  ++line, ++prop)
  564.     if (isvacant(prop))
  565.       prop->flags |= L_SUPERF;
  566.  
  567.   inbody = mnum = 0;
  568.   mprop = &dummy;
  569.   for (line = lines, prop = props;  line < endline;  ++line, ++prop)
  570.     if (isvacant(prop)) {
  571.       for (num = 0, p = *line;  *p;  ++p)
  572.         if (*p != ' ') ++num;
  573.       if (inbody || num < mnum)
  574.         mnum = num, mprop = prop;
  575.       inbody = 0;
  576.     } else {
  577.       if (!inbody) mprop->flags &= ~L_SUPERF;
  578.       inbody = 1;
  579.     }
  580.  
  581.  
  582. static void setaffixes(
  583.   const char * const *inlines, const char * const *endline,
  584.   const lineprop *props, const charset *bodychars,
  585.   const charset *quotechars, int hang, int body, int quote,
  586.   int *pafp, int *pfs, int *pprefix, int *psuffix
  587. )
  588. /* inlines is an array of strings, up to but not including endline,    */
  589. /* representing an IP.  inlines and endline must not be equal.  props  */
  590. /* is the the parallel array of lineprop structures.  *pafp and *pfs   */
  591. /* are set to the augmented fallback prelen and fallback suflen of the */
  592. /* IP.  If either of *pprefix, *psuffix is less than 0, it is set to a */
  593. /* default value as specified in "par.doc".                            */
  594. {
  595.   int numin, pre, suf;
  596.   const char *p;
  597.  
  598.   numin = endline - inlines;
  599.  
  600.   if ((*pprefix < 0 || *psuffix < 0)  &&  numin > hang + 1)
  601.     compresuflen(inlines + hang, endline, bodychars, body, 0, 0, &pre, &suf);
  602.  
  603.   p = *inlines + props->p;
  604.   if (numin == 1 && quote)
  605.     while (*p && csmember (*p, quotechars))
  606.       ++p;
  607.   *pafp = p - *inlines;
  608.   *pfs = props->s;
  609.  
  610.   if (*pprefix < 0)
  611.     *pprefix  =  numin > hang + 1  ?  pre  :  *pafp;
  612.  
  613.   if (*psuffix < 0)
  614.     *psuffix  =  numin > hang + 1  ?  suf  :  *pfs;
  615. }
  616.  
  617.  
  618. static void freelines(char **lines)
  619. /* Frees the elements of lines, and lines itself. */
  620. /* lines is a NULL-terminated array of strings.   */
  621. {
  622.   char **line;
  623.  
  624.   for (line = lines;  *line;  ++line)
  625.     free(*line);
  626.  
  627.   free(lines);
  628. }
  629.  
  630.  
  631. int main(int argc, const char * const *argv)
  632. {
  633.   int help = 0, version = 0, hang = 0, prefix = -1, repeat = 0, suffix = -1,
  634.       Tab = 1, width = 72, body = 0, cap = 0, div = 0, Err = 0, expel = 0,
  635.       fit = 0, guess = 0, invis = 0, just = 0, last = 0, quote = 0, Report = 0,
  636.       touch = -1;
  637.   int prefixbak, suffixbak, c, sawnonblank, oweblank, n, i, afp, fs;
  638.   charset *bodychars = NULL, *protectchars = NULL, *quotechars = NULL;
  639.   char *parinit = NULL, *arg, **inlines = NULL, **endline, **firstline, *end,
  640.        **nextline, **outlines = NULL, **line;
  641.   const char *env, * const whitechars = " \f\n\r\t\v";
  642.   errmsg_t errmsg = { '\0' };
  643.   lineprop *props = NULL, *firstprop, *nextprop;
  644.   FILE *errout;
  645.  
  646. /* Process environment variables: */
  647.  
  648.   env = getenv("PARBODY");
  649.   if (!env) env = "";
  650.   bodychars = parsecharset(env,errmsg);
  651.   if (*errmsg) {
  652.     help = 1;
  653.     goto parcleanup;
  654.   }
  655.  
  656.   env = getenv("PARPROTECT");
  657.   if (!env) env = "";
  658.   protectchars = parsecharset(env,errmsg);
  659.   if (*errmsg) {
  660.     help = 1;
  661.     goto parcleanup;
  662.   }
  663.  
  664.   env = getenv("PARQUOTE");
  665.   if (!env) env = "> ";
  666.   quotechars = parsecharset(env,errmsg);
  667.   if (*errmsg) {
  668.     help = 1;
  669.     goto parcleanup;
  670.   }
  671.  
  672.   env = getenv("PARINIT");
  673.   if (env) {
  674.     parinit = malloc((strlen(env) + 1) * sizeof (char));
  675.     if (!parinit) {
  676.       strcpy(errmsg,outofmem);
  677.       goto parcleanup;
  678.     }
  679.     strcpy(parinit,env);
  680.     arg = strtok(parinit,whitechars);
  681.     while (arg) {
  682.       parsearg(arg, &help, &version, bodychars, protectchars,
  683.                quotechars, &hang, &prefix, &repeat, &suffix, &Tab,
  684.                &width, &body, &cap, &div, &Err, &expel, &fit, &guess,
  685.                &invis, &just, &last, "e, &Report, &touch, errmsg );
  686.       if (*errmsg || help || version) goto parcleanup;
  687.       arg = strtok(NULL,whitechars);
  688.     }
  689.     free(parinit);
  690.     parinit = NULL;
  691.   }
  692.  
  693. /* Process command line arguments: */
  694.  
  695.   while (*++argv) {
  696.     parsearg(*argv, &help, &version, bodychars, protectchars,
  697.              quotechars, &hang, &prefix, &repeat, &suffix, &Tab,
  698.              &width, &body, &cap, &div, &Err, &expel, &fit, &guess,
  699.              &invis, &just, &last, "e, &Report, &touch, errmsg );
  700.     if (*errmsg || help || version) goto parcleanup;
  701.   }
  702.  
  703.   if (Tab == 0) {
  704.     strcpy(errmsg, "<Tab> must not be 0.\n");
  705.     goto parcleanup;
  706.   }
  707.  
  708.   if (touch < 0) touch = fit || last;
  709.   prefixbak = prefix;
  710.   suffixbak = suffix;
  711.  
  712. /* Main loop: */
  713.  
  714.   for (sawnonblank = oweblank = 0;  ;  ) {
  715.     for (;;) {
  716.       c = getchar();
  717.       if (expel && c == '\n') {
  718.         oweblank = sawnonblank;
  719.         continue;
  720.       }
  721.       if (csmember((char) c, protectchars)) {
  722.         sawnonblank = 1;
  723.         if (oweblank) {
  724.           putchar('\n');
  725.           oweblank = 0;
  726.         }
  727.         while (c != '\n' && c != EOF) {
  728.           putchar(c);
  729.           c = getchar();
  730.         }
  731.       }
  732.       if (c != '\n') break;
  733.       putchar(c);
  734.     }
  735.     if (c == EOF) break;
  736.     ungetc(c,stdin);
  737.  
  738.     inlines =
  739.       readlines(&props, protectchars, quotechars, Tab, invis, quote, errmsg);
  740.     if (*errmsg) goto parcleanup;
  741.  
  742.     for (endline = inlines;  *endline;  ++endline);
  743.     if (endline == inlines) {
  744.       free(inlines);
  745.       inlines = NULL;
  746.       continue;
  747.     }
  748.  
  749.     sawnonblank = 1;
  750.     if (oweblank) {
  751.       putchar('\n');
  752.       oweblank = 0;
  753.     }
  754.  
  755.     delimit((const char * const *) inlines,
  756.             (const char * const *) endline,
  757.             bodychars, repeat, body, div, 0, 0, props);
  758.  
  759.     if (expel)
  760.       marksuperf((const char * const *) inlines,
  761.                  (const char * const *) endline, props);
  762.  
  763.     firstline = inlines, firstprop = props;
  764.     do {
  765.       if (isbodiless(firstprop)) {
  766.         if (!isinvis(firstprop) && !(expel && issuperf(firstprop))) {
  767.           for (end = *firstline;  *end;  ++end);
  768.           if (!repeat  ||  firstprop->rc == ' ' && !firstprop->s) {
  769.             while (end > *firstline && end[-1] == ' ') --end;
  770.             *end = '\0';
  771.             puts(*firstline);
  772.           }
  773.           else {
  774.             n = width - firstprop->p - firstprop->s;
  775.             if (n < 0) {
  776.               sprintf(errmsg,impossibility,5);
  777.               goto parcleanup;
  778.             }
  779.             printf("%.*s", firstprop->p, *firstline);
  780.             for (i = n;  i;  --i)
  781.               putchar(firstprop->rc);
  782.             puts(end - firstprop->s);
  783.           }
  784.         }
  785.         ++firstline, ++firstprop;
  786.         continue;
  787.       }
  788.  
  789.       for (nextline = firstline + 1, nextprop = firstprop + 1;
  790.            nextline < endline && !isbodiless(nextprop) && !isfirst(nextprop);
  791.            ++nextline, ++nextprop);
  792.  
  793.       prefix = prefixbak, suffix = suffixbak;
  794.       setaffixes((const char * const *) firstline,
  795.                  (const char * const *) nextline, firstprop, bodychars,
  796.                  quotechars, hang, body, quote, &afp, &fs, &prefix, &suffix);
  797.       if (width <= prefix + suffix) {
  798.         sprintf(errmsg,
  799.                 "<width> (%d) <= <prefix> (%d) + <suffix> (%d)\n",
  800.                 width, prefix, suffix);
  801.         goto parcleanup;
  802.       }
  803.  
  804.       outlines =
  805.         reformat((const char * const *) firstline,
  806.                  (const char * const *) nextline,
  807.                  afp, fs, hang, prefix, suffix, width, cap,
  808.                  fit, guess, just, last, Report, touch, errmsg);
  809.       if (*errmsg) goto parcleanup;
  810.  
  811.       for (line = outlines;  *line;  ++line)
  812.         puts(*line);
  813.  
  814.       freelines(outlines);
  815.       outlines = NULL;
  816.  
  817.       firstline = nextline, firstprop = nextprop;
  818.     } while (firstline < endline);
  819.  
  820.     freelines(inlines);
  821.     inlines = NULL;
  822.  
  823.     free(props);
  824.     props = NULL;
  825.   }
  826.  
  827. parcleanup:
  828.  
  829.   if (bodychars) freecharset(bodychars);
  830.   if (protectchars) freecharset(protectchars);
  831.   if (quotechars) freecharset(quotechars);
  832.   if (parinit) free(parinit);
  833.   if (inlines) freelines(inlines);
  834.   if (props) free(props);
  835.   if (outlines) freelines(outlines);
  836.  
  837.   errout = Err ? stderr : stdout;
  838.   if (*errmsg) fprintf(errout, "par error:\n%.*s", errmsg_size, errmsg);
  839.   if (version) fputs("par 1.50\n",errout);
  840.   if (help)    fputs(usagemsg,errout);
  841.  
  842.   return *errmsg ? EXIT_FAILURE : EXIT_SUCCESS;
  843. }
  844.