home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume39 / par / part03 < prev    next >
Encoding:
Text File  |  1993-08-21  |  39.6 KB  |  1,390 lines

  1. Newsgroups: comp.sources.misc
  2. From: amc@wuecl.wustl.edu (Adam Costello)
  3. Subject: v39i042:  par - paragraph reformatter, v1.30, Part03/03
  4. Message-ID: <1993Aug22.014919.29656@sparky.sterling.com>
  5. X-Md4-Signature: 2d477e5f1f1394407717c1b020e583b0
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Sterling Software
  8. Date: Sun, 22 Aug 1993 01:49:19 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: amc@wuecl.wustl.edu (Adam Costello)
  12. Posting-number: Volume 39, Issue 42
  13. Archive-name: par/part03
  14. Environment: ANSI-C
  15. Supersedes: par: Volume 38, Issue 114-116
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then unpack
  19. # it by saving it into a file and typing "sh file".  To overwrite existing
  20. # files, type "sh file -c".  You can also feed this as standard input via
  21. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  22. # will see the following message at the end:
  23. #        "End of shell archive."
  24. # Contents:  Par130 Par130/errmsg.h Par130/errmsg.c Par130/reformat.h
  25. #   Par130/reformat.c Par130/par.c
  26. # Wrapped by amc@wuecl on Fri Aug 20 19:16:48 1993
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test ! -d 'Par130' ; then
  29.     echo shar: Creating directory \"'Par130'\"
  30.     mkdir 'Par130'
  31. fi
  32. if test -f 'Par130/errmsg.h' -a "${1}" != "-c" ; then 
  33.   echo shar: Will not clobber existing file \"'Par130/errmsg.h'\"
  34. else
  35. echo shar: Extracting \"'Par130/errmsg.h'\" \(887 characters\)
  36. sed "s/^X//" >'Par130/errmsg.h' <<'END_OF_FILE'
  37. X/*********************/
  38. X/* errmsg.h          */
  39. X/* for Par 1.30      */
  40. X/* Copyright 1993 by */
  41. X/* Adam M. Costello  */
  42. X/*********************/
  43. X
  44. X/* This is ANSI C code. */
  45. X
  46. X
  47. X#ifndef ERRMSG_H
  48. X#define ERRMSG_H
  49. X
  50. X
  51. X#define errmsg_size 163
  52. X
  53. X/* This is the maximum number of characters that will fit  */
  54. X/* in an errmsg_t, including the terminating '\0'. It will */
  55. X/* never decrease, but may increase in future versions of  */
  56. X/* this header file.                                       */
  57. X
  58. X
  59. Xtypedef char errmsg_t[errmsg_size];
  60. X
  61. X/* Any function which takes the argument errmsg_t errmsg must, before */
  62. X/* returning, either set errmsg[0] to '\0' (indicating success), or   */
  63. X/* write an error message string into errmsg, (indicating failure),   */
  64. X/* being careful not to overrun the space.                            */
  65. X
  66. X
  67. Xextern const char * const outofmem;  /* "Out of memory.\n" */
  68. X
  69. X
  70. X#endif
  71. END_OF_FILE
  72. if test 887 -ne `wc -c <'Par130/errmsg.h'`; then
  73.     echo shar: \"'Par130/errmsg.h'\" unpacked with wrong size!
  74. fi
  75. # end of 'Par130/errmsg.h'
  76. fi
  77. if test -f 'Par130/errmsg.c' -a "${1}" != "-c" ; then 
  78.   echo shar: Will not clobber existing file \"'Par130/errmsg.c'\"
  79. else
  80. echo shar: Extracting \"'Par130/errmsg.c'\" \(303 characters\)
  81. sed "s/^X//" >'Par130/errmsg.c' <<'END_OF_FILE'
  82. X/*********************/
  83. X/* errmsg.c          */
  84. X/* for Par 1.30      */
  85. X/* Copyright 1993 by */
  86. X/* Adam M. Costello  */
  87. X/*********************/
  88. X
  89. X/* This is ANSI C code. */
  90. X
  91. X
  92. X#include "errmsg.h"  /* Makes sure we're consistent with the declaration. */
  93. X
  94. X
  95. Xconst char * const outofmem = "Out of memory.\n";
  96. END_OF_FILE
  97. if test 303 -ne `wc -c <'Par130/errmsg.c'`; then
  98.     echo shar: \"'Par130/errmsg.c'\" unpacked with wrong size!
  99. fi
  100. # end of 'Par130/errmsg.c'
  101. fi
  102. if test -f 'Par130/reformat.h' -a "${1}" != "-c" ; then 
  103.   echo shar: Will not clobber existing file \"'Par130/reformat.h'\"
  104. else
  105. echo shar: Extracting \"'Par130/reformat.h'\" \(972 characters\)
  106. sed "s/^X//" >'Par130/reformat.h' <<'END_OF_FILE'
  107. X/*********************/
  108. X/* reformat.h        */
  109. X/* for Par 1.30      */
  110. X/* Copyright 1993 by */
  111. X/* Adam M. Costello  */
  112. X/*********************/
  113. X
  114. X/* This is ANSI C code. */
  115. X
  116. X
  117. X#include "errmsg.h"
  118. X
  119. X
  120. Xchar **reformat(
  121. X  const char * const *inlines, const char * const *endline,
  122. X  int hang, int prefix, int suffix, int width, int fit, int guess,
  123. X  int just, int last, int Report, int touch, errmsg_t errmsg
  124. X);
  125. X  /* inlines is an array of pointers to input lines, up to but not  */
  126. X  /* including endline.  The other parameters are the variables of  */
  127. X  /* the same name as described in "par.doc".  reformat(inlines,    */
  128. X  /* endline, hang, prefix, suffix, width, fit, guess, just, last,  */
  129. X  /* Report, touch, errmsg) returns a NULL-terminated array of      */
  130. X  /* pointers to output lines containing the reformatted paragraph, */
  131. X  /* according to the specification in "par.doc".  None of the      */
  132. X  /* integer parameters may be negative. Returns NULL on failure.   */
  133. END_OF_FILE
  134. if test 972 -ne `wc -c <'Par130/reformat.h'`; then
  135.     echo shar: \"'Par130/reformat.h'\" unpacked with wrong size!
  136. fi
  137. # end of 'Par130/reformat.h'
  138. fi
  139. if test -f 'Par130/reformat.c' -a "${1}" != "-c" ; then 
  140.   echo shar: Will not clobber existing file \"'Par130/reformat.c'\"
  141. else
  142. echo shar: Extracting \"'Par130/reformat.c'\" \(14623 characters\)
  143. sed "s/^X//" >'Par130/reformat.c' <<'END_OF_FILE'
  144. X/*********************/
  145. X/* reformat.c        */
  146. X/* for Par 1.30      */
  147. X/* Copyright 1993 by */
  148. X/* Adam M. Costello  */
  149. X/*********************/
  150. X
  151. X/* This is ANSI C code. */
  152. X
  153. X
  154. X#include "reformat.h"  /* Makes sure we're consistent with the */
  155. X                       /* prototype. Also includes "errmsg.h". */
  156. X#include "buffer.h"    /* Also includes <stddef.h>.            */
  157. X
  158. X#include <stdio.h>
  159. X#include <stdlib.h>
  160. X#include <ctype.h>
  161. X#include <string.h>
  162. X
  163. X#undef NULL
  164. X#define NULL ((void *) 0)
  165. X
  166. X#ifdef DONTFREE
  167. X#define free(ptr)
  168. X#endif
  169. X
  170. X
  171. Xstruct word {
  172. X  const char *chrs;       /* Pointer to the characters in the word */
  173. X                          /* (NOT terminated by '\0').             */
  174. X  struct word *prev,      /* Pointer to previous word.             */
  175. X              *next,      /* Pointer to next word.                 */
  176. X                          /* Supposing this word were the first... */
  177. X              *nextline;  /*   Pointer to first word in next line. */
  178. X  int score,              /*   Value of the objective function.    */
  179. X      length;             /* Length of this word.                  */
  180. X  short flags;            /* Notable properties of this word.      */
  181. X};
  182. X
  183. X/* The following may be bitwise-OR'd together */
  184. X/* to set the flags field of a struct word:   */
  185. X
  186. Xconst short W_SHIFTED = 1,  /* This word should have an extra space before */
  187. X                            /* it unless it's the first word in the line.  */
  188. X            W_CURIOUS = 2,  /* This is a curious word (see par.doc).       */
  189. X            W_CAPITAL = 4;  /* This is a capitalized word (see par.doc).   */
  190. X
  191. X#define isshifted(w) ((w)->flags & 1)
  192. X#define iscurious(w) (((w)->flags & 2) >> 1)
  193. X#define iscapital(w) (((w)->flags & 4) >> 2)
  194. X
  195. Xconst char * const impossibility =
  196. X  "Impossibility #%d has occurred. Please report it.\n";
  197. X
  198. X
  199. Xstatic int checkcapital(struct word *w)
  200. X/* Returns 1 if *w is capitalized according   */
  201. X/* to the definition in par.doc, or 0 if not. */
  202. X{
  203. X  const char *p, *end;
  204. X
  205. X  for (p = w->chrs, end = p + w->length;  p < end && !isalnum(*p);  ++p);
  206. X  return p < end && !islower(*p);
  207. X}
  208. X
  209. X
  210. Xstatic int checkcurious(struct word *w)
  211. X/* Returns 1 if *w is curious according to */
  212. X/* the definition in par.doc, or 0 if not. */
  213. X{
  214. X  const char *start, *p;
  215. X  char ch;
  216. X
  217. X  for (start = w->chrs, p = start + w->length;  p > start;  --p) {
  218. X    ch = p[-1];
  219. X    if (isalnum(ch)) return 0;
  220. X    if (ch == '.' || ch == '?' || ch == '!' || ch == ':') break;
  221. X  }
  222. X
  223. X  if (p <= start + 1) return 0;
  224. X
  225. X  --p;
  226. X  do if (isalnum(*--p)) return 1;
  227. X  while (p > start);
  228. X
  229. X  return 0;
  230. X}
  231. X
  232. X
  233. Xstatic int simplebreaks(struct word *head, struct word *tail, int L, int last)
  234. X
  235. X/* Chooses line breaks in a list of struct words which maximize the   */
  236. X/* length of the shortest line.  L is the maximum line length.  The   */
  237. X/* last line counts as a line only if last is non-zero. _head must    */
  238. X/* point to a dummy word, and tail must point to the last word, whose */
  239. X/* next field must be NULL.  Returns the length of the shortest line  */
  240. X/* on success, -1 if there is a word of length greater than L, or L   */
  241. X/* if there are no lines.                                             */
  242. X{
  243. X  struct word *w1, *w2;
  244. X  int linelen, score;
  245. X
  246. X  if (!head->next) return L;
  247. X
  248. X  for (w1 = tail, linelen = w1->length;
  249. X       w1 != head && linelen <= L;
  250. X       linelen += isshifted(w1), w1 = w1->prev, linelen += 1 + w1->length) {
  251. X    w1->score = last ? linelen : L;
  252. X    w1->nextline = NULL;
  253. X  }
  254. X
  255. X  for ( ;  w1 != head;  w1 = w1->prev) {
  256. X    w1->score = -1;
  257. X    for (linelen = w1->length,  w2 = w1->next;
  258. X         linelen <= L;
  259. X         linelen += 1 + isshifted(w2) + w2->length,  w2 = w2->next) {
  260. X      score = w2->score;
  261. X      if (linelen < score) score = linelen;
  262. X      if (score >= w1->score) {
  263. X        w1->nextline = w2;
  264. X        w1->score = score;
  265. X      }
  266. X    }
  267. X  }
  268. X
  269. X  return head->next->score;
  270. X}
  271. X
  272. X
  273. Xstatic void normalbreaks(struct word *head, struct word *tail,
  274. X                         int L, int fit, int last, errmsg_t errmsg)
  275. X
  276. X/* Chooses line breaks in a list of struct    */
  277. X/* words according to the policy in "par.doc" */
  278. X/* for <just> = 0 (L is <L>, fit is <fit>,    */
  279. X/* and last is <last>).  head must point to   */
  280. X/* a dummy word, and tail must point to the   */
  281. X/* last word, whose next field must be NULL.  */
  282. X{
  283. X  struct word *w1, *w2;
  284. X  int tryL, shortest, score, target, linelen, extra, minlen;
  285. X
  286. X  *errmsg = '\0';
  287. X  if (!head->next) return;
  288. X
  289. X  target = L;
  290. X
  291. X/* Determine minimum possible difference between  */
  292. X/* the lengths of the shortest and longest lines: */
  293. X
  294. X  if (fit) {
  295. X    score = L + 1;
  296. X    for (tryL = L;  ;  --tryL) {
  297. X      shortest = simplebreaks(head,tail,tryL,last);
  298. X      if (shortest < 0) break;
  299. X      if (tryL - shortest < score) {
  300. X        target = tryL;
  301. X        score = target - shortest;
  302. X      }
  303. X    }
  304. X  }
  305. X
  306. X/* Determine maximum possible length of the shortest line: */
  307. X
  308. X  shortest = simplebreaks(head,tail,target,last);
  309. X  if (shortest < 0) {
  310. X    sprintf(errmsg,impossibility,1);
  311. X    return;
  312. X  }
  313. X
  314. X/* Minimize the sum of the squares of the differences */
  315. X/* between target and the lengths of the lines:       */
  316. X
  317. X  w1 = tail;
  318. X  do {
  319. X    w1->score = -1;
  320. X    for (linelen = w1->length,  w2 = w1->next;
  321. X         linelen <= target;
  322. X         linelen += 1 + isshifted(w2) + w2->length,  w2 = w2->next) {
  323. X      extra = target - linelen;
  324. X      minlen = shortest;
  325. X      if (w2)
  326. X        score = w2->score;
  327. X      else {
  328. X        score = 0;
  329. X        if (!last) extra = minlen = 0;
  330. X      }
  331. X      if (linelen >= minlen  &&  score >= 0) {
  332. X        score += extra * extra;
  333. X        if (w1->score < 0  ||  score <= w1->score) {
  334. X          w1->nextline = w2;
  335. X          w1->score = score;
  336. X        }
  337. X      }
  338. X      if (!w2) break;
  339. X    }
  340. X    w1 = w1->prev;
  341. X  } while (w1 != head);
  342. X
  343. X  if (head->next->score < 0)
  344. X    sprintf(errmsg,impossibility,2);
  345. X}
  346. X
  347. X
  348. Xstatic void justbreaks(
  349. X  struct word *head, struct word *tail, int L, int last, errmsg_t errmsg
  350. X)
  351. X/* Chooses line breaks in a list of struct words according     */
  352. X/* to the policy in "par.doc" for <just> = 1 (L is <L> and     */
  353. X/* last is <last>).  head must point to a dummy word, and tail */
  354. X/* must point to the last word, whose next field must be NULL. */
  355. X{
  356. X  struct word *w1, *w2;
  357. X  int numgaps, extra, score, gap, maxgap, numbiggaps;
  358. X
  359. X  *errmsg = '\0';
  360. X  if (!head->next) return;
  361. X
  362. X/* Determine the minimum possible largest inter-word gap: */
  363. X
  364. X  w1 = tail;
  365. X  do {
  366. X    w1->score = L;
  367. X    for (numgaps = 0, extra = L - w1->length, w2 = w1->next;
  368. X         extra >= 0;
  369. X         ++numgaps, extra -= 1 + isshifted(w2) + w2->length, w2 = w2->next) {
  370. X      gap = numgaps ? (extra + numgaps - 1) / numgaps : L;
  371. X      if (w2)
  372. X        score = w2->score;
  373. X      else {
  374. X        score = 0;
  375. X        if (!last) gap = 0;
  376. X      }
  377. X      if (gap > score) score = gap;
  378. X      if (score < w1->score) {
  379. X        w1->nextline = w2;
  380. X        w1->score = score;
  381. X      }
  382. X      if (!w2) break;
  383. X    }
  384. X    w1 = w1->prev;
  385. X  } while (w1 != head);
  386. X
  387. X  maxgap = head->next->score;
  388. X  if (maxgap >= L) {
  389. X    strcpy(errmsg, "Cannot justify.\n");
  390. X    return;
  391. X  }
  392. X
  393. X/* Minimize the sum of the squares of the numbers   */
  394. X/* of extra spaces required in each inter-word gap: */
  395. X
  396. X  w1 = tail;
  397. X  do {
  398. X    w1->score = -1;
  399. X    for (numgaps = 0, extra = L - w1->length, w2 = w1->next;
  400. X         extra >= 0;
  401. X         ++numgaps, extra -= 1 + isshifted(w2) + w2->length, w2 = w2->next) {
  402. X      gap = numgaps ? (extra + numgaps - 1) / numgaps : L;
  403. X      if (w2)
  404. X        score = w2->score;
  405. X      else {
  406. X        if (!last) {
  407. X          w1->nextline = NULL;
  408. X          w1->score = 0;
  409. X          break;
  410. X        }
  411. X        score = 0;
  412. X      }
  413. X      if (gap <= maxgap && score >= 0) {
  414. X        numbiggaps = extra % numgaps;
  415. X        score += (extra / numgaps) * (extra + numbiggaps) + numbiggaps;
  416. X        /* The above may not look like the sum of the squares of the numbers */
  417. X        /* of extra spaces required in each inter-word gap, but trust me, it */
  418. X        /* is.  It's easier to prove graphically than algebraicly.           */
  419. X        if (w1->score < 0  ||  score <= w1->score) {
  420. X          w1->nextline = w2;
  421. X          w1->score = score;
  422. X        }
  423. X      }
  424. X      if (!w2) break;
  425. X    }
  426. X    w1 = w1->prev;
  427. X  } while (w1 != head);
  428. X
  429. X  if (head->next->score < 0)
  430. X    sprintf(errmsg,impossibility,3);
  431. X}
  432. X
  433. X
  434. Xchar **reformat(
  435. X  const char * const *inlines, const char * const *endline,
  436. X  int hang, int prefix, int suffix, int width, int fit, int guess,
  437. X  int just, int last, int Report, int touch, errmsg_t errmsg
  438. X)
  439. X{
  440. X  int numin, affix, L, onfirstword = 1, linelen, numout, numgaps, extra, phase;
  441. X  const char * const *line, **suffixes = NULL, **suf, *end, *p1, *p2;
  442. X  char *q1, *q2, **outlines = NULL;
  443. X  struct word dummy, *head, *tail, *w1, *w2;
  444. X  struct buffer *pbuf = NULL;
  445. X
  446. X/* Initialization: */
  447. X
  448. X  *errmsg = '\0';
  449. X  dummy.next = dummy.prev = NULL;
  450. X  dummy.flags = 0;
  451. X  head = tail = &dummy;
  452. X  numin = endline - inlines;
  453. X
  454. X/* Allocate space for pointers to the suffixes: */
  455. X
  456. X  if (numin) {
  457. X    suffixes = malloc(numin * sizeof (const char *));
  458. X    if (!suffixes) {
  459. X      strcpy(errmsg,outofmem);
  460. X      goto rfcleanup;
  461. X    }
  462. X  }
  463. X
  464. X/* Set the pointers to the suffixes, and create the words: */
  465. X
  466. X  affix = prefix + suffix;
  467. X  L = width - prefix - suffix;
  468. X
  469. X  for (line = inlines, suf = suffixes;  line < endline;  ++line, ++suf) {
  470. X    for (end = *line;  *end;  ++end);
  471. X    if (end - *line < affix) {
  472. X      sprintf(errmsg,
  473. X              "Line %d shorter than <prefix> + <suffix> = %d + %d = %d\n",
  474. X              line - inlines + 1, prefix, suffix, affix);
  475. X      goto rfcleanup;
  476. X    }
  477. X    end -= suffix;
  478. X    *suf = end;
  479. X    p1 = *line + prefix;
  480. X    for (;;) {
  481. X      while (p1 < end && *p1 == ' ') ++p1;
  482. X      if (p1 == end) break;
  483. X      p2 = p1;
  484. X      if (onfirstword) {
  485. X        p1 = *line + prefix;
  486. X        onfirstword = 0;
  487. X      }
  488. X      while (p2 < end && *p2 != ' ') ++p2;
  489. X      w1 = malloc(sizeof (struct word));
  490. X      if (!w1) {
  491. X        strcpy(errmsg,outofmem);
  492. X        goto rfcleanup;
  493. X      }
  494. X      w1->next = NULL;
  495. X      w1->prev = tail;
  496. X      tail = tail->next = w1;
  497. X      w1->chrs = p1;
  498. X      w1->length = p2 - p1;
  499. X      w1->flags = 0;
  500. X      p1 = p2;
  501. X    }
  502. X  }
  503. X
  504. X/* If guess is 1, set flag values and merge words: */
  505. X
  506. X  if (guess) {
  507. X    for (w1 = head, w2 = head->next;  w2;  w1 = w2, w2 = w2->next) {
  508. X      if (checkcurious(w2)) w2->flags |= W_CURIOUS;
  509. X      if (checkcapital(w2)) {
  510. X        w2->flags |= W_CAPITAL;
  511. X        if (iscurious(w1))
  512. X          if (w1->chrs[w1->length] && w1->chrs + w1->length + 1 == w2->chrs) {
  513. X            w2->length += w1->length + 1;
  514. X            w2->chrs = w1->chrs;
  515. X            w2->prev = w1->prev;
  516. X            w2->prev->next = w2;
  517. X            if (iscapital(w1)) w2->flags |= W_CAPITAL;
  518. X            else w2->flags &= ~W_CAPITAL;
  519. X            if (isshifted(w1)) w2->flags |= W_SHIFTED;
  520. X            else w2->flags &= ~W_SHIFTED;
  521. X            free(w1);
  522. X          }
  523. X          else
  524. X            w2->flags |= W_SHIFTED;
  525. X      }
  526. X    }
  527. X    tail = w1;
  528. X  }
  529. X
  530. X/* Check for too-long words: */
  531. X
  532. X  if (Report)
  533. X    for (w2 = head->next;  w2;  w2 = w2->next) {
  534. X      if (w2->length > L) {
  535. X        linelen = w2->length;
  536. X        if (linelen > errmsg_size - 17)
  537. X          linelen = errmsg_size - 17;
  538. X        sprintf(errmsg, "Word too long: %.*s\n", linelen, w2->chrs);
  539. X        goto rfcleanup;
  540. X      }
  541. X    }
  542. X  else
  543. X    for (w2 = head->next;  w2;  w2 = w2->next)
  544. X      while (w2->length > L) {
  545. X        w1 = malloc(sizeof (struct word));
  546. X        if (!w1) {
  547. X          strcpy(errmsg,outofmem);
  548. X          goto rfcleanup;
  549. X        }
  550. X        w1->next = w2;
  551. X        w1->prev = w2->prev;
  552. X        w1->prev->next = w1;
  553. X        w2->prev = w1;
  554. X        w1->chrs = w2->chrs;
  555. X        w2->chrs += L;
  556. X        w1->length = L;
  557. X        w2->length -= L;
  558. X        w1->flags = 0;
  559. X        if (iscapital(w2)) {
  560. X          w1->flags |= W_CAPITAL;
  561. X          w2->flags &= ~W_CAPITAL;
  562. X        }
  563. X        if (isshifted(w2)) {
  564. X          w1->flags |= W_SHIFTED;
  565. X          w2->flags &= ~W_SHIFTED;
  566. X        }
  567. X      }
  568. X
  569. X/* Choose line breaks according to policy in "par.doc": */
  570. X
  571. X  if (just) justbreaks(head,tail,L,last,errmsg);
  572. X  else normalbreaks(head,tail,L,fit,last,errmsg);
  573. X  if (*errmsg) goto rfcleanup;
  574. X
  575. X/* Change L to the length of the longest line if required: */
  576. X
  577. X  if (!just && touch) {
  578. X    L = 0;
  579. X    w1 = head->next;
  580. X    while (w1) {
  581. X      for (linelen = w1->length, w2 = w1->next;
  582. X           w2 != w1->nextline;
  583. X           linelen += 1 + isshifted(w2) + w2->length, w2 = w2->next);
  584. X      if (linelen > L) L = linelen;
  585. X      w1 = w2;
  586. X    }
  587. X  }
  588. X
  589. X/* Construct the lines: */
  590. X
  591. X  pbuf = newbuffer(sizeof (char *), errmsg);
  592. X  if (*errmsg) goto rfcleanup;
  593. X
  594. X  numout = 0;
  595. X  w1 = head->next;
  596. X  while (numout < hang || w1) {
  597. X    if (w1)
  598. X      for (w2 = w1->next, numgaps = 0, extra = L - w1->length;
  599. X           w2 != w1->nextline;
  600. X           ++numgaps, extra -= 1 + isshifted(w2) + w2->length, w2 = w2->next);
  601. X    linelen = suffix  ||  just && (w2 || last) ?
  602. X                L + affix :
  603. X                w1 ? prefix + L - extra : prefix;
  604. X    q1 = malloc((linelen + 1) * sizeof (char));
  605. X    if (!q1) {
  606. X      strcpy(errmsg,outofmem);
  607. X      goto rfcleanup;
  608. X    }
  609. X    additem(pbuf, &q1, errmsg);
  610. X    if (*errmsg) goto rfcleanup;
  611. X    ++numout;
  612. X    q2 = q1 + prefix;
  613. X    if      (numout <= numin) memcpy(q1, inlines[numout - 1], prefix);
  614. X    else if (numin > hang)    memcpy(q1, inlines[numin - 1], prefix);
  615. X    else                      while (q1 < q2) *q1++ = ' ';
  616. X    q1 = q2;
  617. X    if (w1) {
  618. X      phase = numgaps / 2;
  619. X      for (w2 = w1;  ;  ) {
  620. X        memcpy(q1, w2->chrs, w2->length);
  621. X        q1 += w2->length;
  622. X        w2 = w2->next;
  623. X        if (w2 == w1->nextline) break;
  624. X        *q1++ = ' ';
  625. X        if (just && (w1->nextline || last)) {
  626. X          phase += extra;
  627. X          while (phase >= numgaps) {
  628. X            *q1++ = ' ';
  629. X            phase -= numgaps;
  630. X          }
  631. X        }
  632. X        if (isshifted(w2)) *q1++ = ' ';
  633. X      }
  634. X    }
  635. X    q2 += linelen - affix;
  636. X    while (q1 < q2) *q1++ = ' ';
  637. X    q2 = q1 + suffix;
  638. X    if      (numout <= numin) memcpy(q1, suffixes[numout - 1], suffix);
  639. X    else if (numin)           memcpy(q1, suffixes[numin - 1], suffix);
  640. X    else                      while(q1 < q2) *q1++ = ' ';
  641. X    *q2 = '\0';
  642. X    if (w1) w1 = w1->nextline;
  643. X  }
  644. X
  645. X  q1 = NULL;
  646. X  additem(pbuf, &q1, errmsg);
  647. X  if (*errmsg) goto rfcleanup;
  648. X
  649. X  outlines = copyitems(pbuf,errmsg);
  650. X
  651. Xrfcleanup:
  652. X
  653. X  if (suffixes) free(suffixes);
  654. X
  655. X  while (tail != head) {
  656. X    tail = tail->prev;
  657. X    free(tail->next);
  658. X  }
  659. X
  660. X  if (pbuf) {
  661. X    if (!outlines)
  662. X      for (;;) {
  663. X        outlines = nextitem(pbuf);
  664. X        if (!outlines) break;
  665. X        free(*outlines);
  666. X      }
  667. X    freebuffer(pbuf);
  668. X  }
  669. X
  670. X  return outlines;
  671. X}
  672. END_OF_FILE
  673. if test 14623 -ne `wc -c <'Par130/reformat.c'`; then
  674.     echo shar: \"'Par130/reformat.c'\" unpacked with wrong size!
  675. fi
  676. # end of 'Par130/reformat.c'
  677. fi
  678. if test -f 'Par130/par.c' -a "${1}" != "-c" ; then 
  679.   echo shar: Will not clobber existing file \"'Par130/par.c'\"
  680. else
  681. echo shar: Extracting \"'Par130/par.c'\" \(19122 characters\)
  682. sed "s/^X//" >'Par130/par.c' <<'END_OF_FILE'
  683. X/*********************/
  684. X/* par.c             */
  685. X/* for Par 1.30      */
  686. X/* Copyright 1993 by */
  687. X/* Adam M. Costello  */
  688. X/*********************/
  689. X
  690. X/* This is ANSI C code. */
  691. X
  692. X
  693. X#include "buffer.h"    /* Also includes <stddef.h> and "errmsg.h". */
  694. X#include "reformat.h"
  695. X
  696. X#include <stdio.h>
  697. X#include <string.h>
  698. X#include <stdlib.h>
  699. X#include <ctype.h>
  700. X
  701. X#undef NULL
  702. X#define NULL ((void *) 0)
  703. X
  704. X#ifdef DONTFREE
  705. X#define free(ptr)
  706. X#endif
  707. X
  708. X
  709. Xconst char * const progname = "par";
  710. Xconst char * const versionnum = "1.30";
  711. X
  712. Xstruct charset {
  713. X  char *individuals;  /* Characters in this string are in the set.        */
  714. X  short flags;        /* Groups of characters can be included with flags. */
  715. X};
  716. X
  717. X/* The following may be bitwise-OR'd together  */
  718. X/* to set the flags field of a struct charset: */
  719. X
  720. Xconst short CS_UCASE = 1,  /* Includes all upper case letters. */
  721. X            CS_LCASE = 2,  /* Includes all lower case letters. */
  722. X            CS_DIGIT = 4,  /* Includes all decimal digits.     */
  723. X            CS_NUL   = 8;  /* Includes the NUL character.      */
  724. X
  725. X
  726. Xstatic int incharset(char c, struct charset cset)
  727. X
  728. X/* Returns 1 if c is in cset, 0 otherwise. */
  729. X{
  730. X  return     c && cset.individuals && strchr(cset.individuals, c)
  731. X         ||  cset.flags & CS_UCASE && isupper(c)
  732. X         ||  cset.flags & CS_LCASE && islower(c)
  733. X         ||  cset.flags & CS_DIGIT && isdigit(c)
  734. X         ||  cset.flags & CS_NUL   && !c
  735. X        ;
  736. X}
  737. X
  738. X
  739. Xstatic int digtoint(char c)
  740. X
  741. X/* Returns the value represented by the digit c, or -1 if c is not a digit. */
  742. X{
  743. X  return c == '0' ? 0 :
  744. X         c == '1' ? 1 :
  745. X         c == '2' ? 2 :
  746. X         c == '3' ? 3 :
  747. X         c == '4' ? 4 :
  748. X         c == '5' ? 5 :
  749. X         c == '6' ? 6 :
  750. X         c == '7' ? 7 :
  751. X         c == '8' ? 8 :
  752. X         c == '9' ? 9 :
  753. X         -1;
  754. X
  755. X  /* We can't simply return c - '0' because this is ANSI C code, so */
  756. X  /* it has to work for any character set, not just ones which put  */
  757. X  /* the digits together in order.  Also, a lookup-table would be   */
  758. X  /* bad because there's no upper limit on CHAR_MAX.                */
  759. X}
  760. X
  761. X
  762. Xstatic int hexdigtoint(char c)
  763. X
  764. X/* Returns the value represented by the hexadecimal */
  765. X/* digit c, or -1 if c is not a hexadecimal digit.  */
  766. X{
  767. X  return c == 'A' || c == 'a' ? 10 :
  768. X         c == 'B' || c == 'b' ? 11 :
  769. X         c == 'C' || c == 'c' ? 12 :
  770. X         c == 'D' || c == 'd' ? 13 :
  771. X         c == 'E' || c == 'e' ? 14 :
  772. X         c == 'F' || c == 'f' ? 15 :
  773. X         digtoint(c);
  774. X}
  775. X
  776. X
  777. Xstatic int strtoudec(const char *s, int *pn)
  778. X
  779. X/* Converts the longest prefix of string s consisting of decimal   */
  780. X/* digits to an integer, which is stored in *pn.  Normally returns */
  781. X/* 1.  If *s is not a digit, then *pn is not changed, but 1 is     */
  782. X/* still returned.  If the integer represented is greater than     */
  783. X/* 9999, then *pn is not changed and 0 is returned.                */
  784. X{
  785. X  int n = 0;
  786. X
  787. X  if (!isdigit(*s)) return 1;
  788. X
  789. X  do {
  790. X    if (n >= 1000) return 0;
  791. X    n = 10 * n + digtoint(*s);
  792. X  } while (isdigit(*++s));
  793. X
  794. X  *pn = n;
  795. X
  796. X  return 1;
  797. X}
  798. X
  799. X
  800. Xstatic void parsearg(
  801. X  const char *arg, int *phang, int *pprefix, int *psuffix, int *pwidth,
  802. X  int *pdiv, int *pfit, int *pguess, int *pjust, int *plast, int *pquote,
  803. X  int *pReport, int *ptouch, int *pversion, errmsg_t errmsg
  804. X)
  805. X/* Parses the command line argument in arg, setting *phang, *pprefix, */
  806. X/* *psuffix, *pwidth, *pdiv, *pfit, *pguess, *pjust, *plast, *pquote, */
  807. X/* *pReport, *ptouch, and/or *pversion as appropriate.                */
  808. X{
  809. X  const char *savearg = arg;
  810. X  char oc;
  811. X  int n;
  812. X
  813. X  *errmsg = '\0';
  814. X
  815. X  if (*arg == '-') ++arg;
  816. X
  817. X  if (!strcmp(arg, "version")) {
  818. X    *pversion = 1;
  819. X    return;
  820. X  }
  821. X
  822. X  if (isdigit(*arg)) {
  823. X    if (!strtoudec(arg, &n)) goto badarg;
  824. X    if (n <= 8) *pprefix = n;
  825. X    else *pwidth = n;
  826. X  }
  827. X
  828. X  for (;;) {
  829. X    while (isdigit(*arg)) ++arg;
  830. X    oc = *arg;
  831. X    if (!oc) break;
  832. X    n = 1;
  833. X    if (!strtoudec(++arg, &n)) goto badarg;
  834. X    if (oc == 'h' || oc == 'p' || oc == 's' || oc == 'w') {
  835. X      if (oc == 'h') *phang = n;
  836. X      else {
  837. X        if (!isdigit(*arg)) goto badarg;
  838. X        if      (oc == 'w') *pwidth  = n;
  839. X        else if (oc == 'p') *pprefix = n;
  840. X        else  /*oc == 's'*/ *psuffix = n;
  841. X      }
  842. X    }
  843. X    else {
  844. X      if (n > 1) goto badarg;
  845. X      if      (oc == 'd') *pdiv    = n;
  846. X      else if (oc == 'f') *pfit    = n;
  847. X      else if (oc == 'g') *pguess  = n;
  848. X      else if (oc == 'j') *pjust   = n;
  849. X      else if (oc == 'l') *plast   = n;
  850. X      else if (oc == 'q') *pquote  = n;
  851. X      else if (oc == 'R') *pReport = n;
  852. X      else if (oc == 't') *ptouch  = n;
  853. X      else goto badarg;
  854. X    }
  855. X  }
  856. X
  857. X  return;
  858. X
  859. Xbadarg:
  860. X  sprintf(errmsg, "Bad argument: %.*s\n", errmsg_size - 16, savearg);
  861. X}
  862. X
  863. X
  864. Xstatic struct charset getcharset(
  865. X  const char *varname, const char *defaultval, errmsg_t errmsg
  866. X)
  867. X/* Returns the set of characters defined by the environment variable named */
  868. X/* *varname, according to the syntax for PARBODY described in par.doc.     */
  869. X/* If *varname is not defined, the value *defaultval is used instead.      */
  870. X/* On failure, the individuals field of the return value will be NULL.     */
  871. X{
  872. X  const char *val, *p;
  873. X  struct buffer *cbuf = NULL;
  874. X  char ch;
  875. X  struct charset cs = { NULL, 0 };
  876. X
  877. X  val = getenv(varname);
  878. X  if (!val) val = defaultval;
  879. X
  880. X  cbuf = newbuffer(sizeof (char), errmsg);
  881. X  if (*errmsg) goto gcscleanup;
  882. X  for (p = val;  *p;  ++p)
  883. X    if (*p == '_') {
  884. X      ++p;
  885. X      if (*p == '_' || *p == 's' || *p == 'x') {
  886. X        if      (*p == '_') ch = '_';
  887. X        else if (*p == 's') ch = ' ';
  888. X        else /* *p == 'x' */ {
  889. X          if (!isxdigit(p[1]) || !isxdigit(p[2])) goto gcsbadval;
  890. X          ch = 16 * hexdigtoint(p[1]) + hexdigtoint(p[2]);
  891. X          p += 2;
  892. X        }
  893. X        if (ch) {
  894. X          additem(cbuf, &ch, errmsg);
  895. X          if (*errmsg) goto gcscleanup;
  896. X        }
  897. X        else
  898. X          cs.flags |= CS_NUL;
  899. X      }
  900. X      else {
  901. X        if      (*p == 'A') cs.flags |= CS_UCASE;
  902. X        else if (*p == 'a') cs.flags |= CS_LCASE;
  903. X        else if (*p == '0') cs.flags |= CS_DIGIT;
  904. X        else goto gcsbadval;
  905. X      }
  906. X    }
  907. X    else {
  908. X      additem(cbuf,p,errmsg);
  909. X      if (*errmsg) goto gcscleanup;
  910. X    }
  911. X  ch = '\0';
  912. X  additem(cbuf, &ch, errmsg);
  913. X  if (*errmsg) goto gcscleanup;
  914. X  cs.individuals = copyitems(cbuf,errmsg);
  915. X
  916. Xgcscleanup:
  917. X
  918. X  if (cbuf) freebuffer(cbuf);
  919. X  return cs;
  920. X
  921. Xgcsbadval:
  922. X
  923. X  sprintf(errmsg, "Bad %s: %.*s\n", varname,
  924. X          errmsg_size - 8 - strlen(varname), val);
  925. X  goto gcscleanup;
  926. X}
  927. X
  928. X
  929. Xstatic char **readlines(struct charset protectchars,
  930. X                        struct charset quotechars, int quote, errmsg_t errmsg)
  931. X
  932. X/* Reads lines from stdin until EOF, or until a line beginning with a */
  933. X/* protective character is encountered (in which case the protective  */
  934. X/* character is pushed back onto the input stream), or until a blank  */
  935. X/* line is encountered (in which case the newline is pushed back onto */
  936. X/* the input stream).  Returns a NULL-terminated array of pointers to */
  937. X/* individual lines, stripped of their newline characters.  Every NUL */
  938. X/* character is stripped, and every white character is changed to a   */
  939. X/* space unless it is a newline.  If quote is 1, vacant lines will be */
  940. X/* inserted as described for the q option in par.doc.  Returns NULL   */
  941. X/* on failure.                                                        */
  942. X{
  943. X  struct buffer *cbuf = NULL, *lbuf = NULL;
  944. X  int c, empty, blank, nonquote, oldnonquote = 0, qplen;
  945. X  char ch, *ln = NULL, nullchar = '\0', *nullline = NULL, *qpstart, *qpend,
  946. X       *oldqpstart = &nullchar, *oldqpend = &nullchar, *p, *op, *vln = NULL,
  947. X       **lines = NULL;
  948. X
  949. X  *errmsg = '\0';
  950. X
  951. X  cbuf = newbuffer(sizeof (char), errmsg);
  952. X  if (*errmsg) goto rlcleanup;
  953. X  lbuf = newbuffer(sizeof (char *), errmsg);
  954. X  if (*errmsg) goto rlcleanup;
  955. X
  956. X  for (empty = blank = 1;  ; ) {
  957. X    c = getchar();
  958. X    if (c == EOF) break;
  959. X    if (c == '\n') {
  960. X      if (blank) {
  961. X        ungetc(c,stdin);
  962. X        break;
  963. X      }
  964. X      additem(cbuf, &nullchar, errmsg);
  965. X      if (*errmsg) goto rlcleanup;
  966. X      ln = copyitems(cbuf,errmsg);
  967. X      if (*errmsg) goto rlcleanup;
  968. X      if (quote) {
  969. X        qpstart = ln;
  970. X        for (qpend = qpstart;
  971. X             *qpend && incharset(*qpend, quotechars);
  972. X             ++qpend);
  973. X        nonquote = *qpend != '\0';
  974. X        while (qpend > qpstart && qpend[-1] == ' ') --qpend;
  975. X        for (p = qpstart, op = oldqpstart;
  976. X             p < qpend && op < oldqpend && *p == *op;
  977. X             ++p, ++op);
  978. X        if (   (p < qpend && op == oldqpend  ||  p == qpend && op < oldqpend)
  979. X            && nonquote && oldnonquote) {
  980. X          qplen = p - qpstart;
  981. X          vln = malloc((qplen + 1) * sizeof (char));
  982. X          if (!vln) {
  983. X            strcpy(errmsg,outofmem);
  984. X            goto rlcleanup;
  985. X          }
  986. X          strncpy(vln,qpstart,qplen);
  987. X          vln[qplen] = '\0';
  988. X          additem(lbuf, &vln, errmsg);
  989. X          if (*errmsg) goto rlcleanup;
  990. X          vln = NULL;
  991. X        }
  992. X        oldqpstart = qpstart;
  993. X        oldqpend = qpend;
  994. X        oldnonquote = nonquote;
  995. X      }
  996. X      additem(lbuf, &ln, errmsg);
  997. X      if (*errmsg) goto rlcleanup;
  998. X      ln = NULL;
  999. X      clearbuffer(cbuf);
  1000. X      empty = blank = 1;
  1001. X    }
  1002. X    else {
  1003. X      if (empty) {
  1004. X        if (incharset((char) c,protectchars)) {
  1005. X          ungetc(c,stdin);
  1006. X          break;
  1007. X        }
  1008. X        empty = 0;
  1009. X      }
  1010. X      if (!c) continue;
  1011. X      if (isspace(c)) c = ' ';
  1012. X      else blank = 0;
  1013. X      ch = c;
  1014. X      additem(cbuf, &ch, errmsg);
  1015. X      if (*errmsg) goto rlcleanup;
  1016. X    }
  1017. X  }
  1018. X
  1019. X  if (!blank) {
  1020. X    additem(cbuf, &nullchar, errmsg);
  1021. X    if (*errmsg) goto rlcleanup;
  1022. X    ln = copyitems(cbuf,errmsg);
  1023. X    if (*errmsg) goto rlcleanup;
  1024. X    additem(lbuf, &ln, errmsg);
  1025. X    if (*errmsg) goto rlcleanup;
  1026. X    ln = NULL;
  1027. X  }
  1028. X
  1029. X  additem(lbuf, &nullline, errmsg);
  1030. X  if (*errmsg) goto rlcleanup;
  1031. X  lines = copyitems(lbuf,errmsg);
  1032. X
  1033. Xrlcleanup:
  1034. X
  1035. X  if (cbuf) freebuffer(cbuf);
  1036. X  if (lbuf) {
  1037. X    if (!lines)
  1038. X      for (;;) {
  1039. X        lines = nextitem(lbuf);
  1040. X        if (!lines) break;
  1041. X        free(*lines);
  1042. X      }
  1043. X    freebuffer(lbuf);
  1044. X  }
  1045. X  if (ln) free(ln);
  1046. X  if (vln) free(vln);
  1047. X
  1048. X  return lines;
  1049. X}
  1050. X
  1051. X
  1052. Xstatic void compresuflen(
  1053. X  const char * const *lines, const char * const *endline,
  1054. X  struct charset bodychars, int pre, int suf, int *ppre, int *psuf
  1055. X)
  1056. X/* lines is an array of strings, up to but not including endline.  */
  1057. X/* Writes into *ppre and *psuf the comprelen and comsuflen of the  */
  1058. X/* lines in lines.  Assumes that they have already been determined */
  1059. X/* to be at least pre and suf. endline must not equal lines.       */
  1060. X{
  1061. X  const char *start, *end, * const *line, *p1, *p2, *start2;
  1062. X
  1063. X  start = *lines;
  1064. X  for (end = start + pre;  *end && !incharset(*end, bodychars);  ++end);
  1065. X  for (line = lines + 1;  line < endline;  ++line) {
  1066. X    for (p1 = start + pre, p2 = *line + pre;
  1067. X         p1 < end && *p1 == *p2;
  1068. X         ++p1, ++p2);
  1069. X    end = p1;
  1070. X  }
  1071. X  *ppre = end - start;
  1072. X
  1073. X  start2 = *lines + *ppre;
  1074. X  for (end = start2;  *end;  ++end);
  1075. X  for (start = end - suf;
  1076. X       start > start2 && !incharset(start[-1], bodychars);
  1077. X       --start);
  1078. X  for (line = lines + 1;  line < endline;  ++line) {
  1079. X    start2 = *line + *ppre;
  1080. X    for (p2 = start2;  *p2;  ++p2);
  1081. X    for (p1 = end - suf, p2 -= suf;
  1082. X         p1 > start && p2 > start2 && p1[-1] == p2[-1];
  1083. X         --p1, --p2);
  1084. X    start = p1;
  1085. X  }
  1086. X  while (end - start >= 2 && *start == ' ' && start[1] == ' ') ++start;
  1087. X  *psuf = end - start;
  1088. X}
  1089. X
  1090. X
  1091. Xstatic void delimit(
  1092. X  const char * const *lines, const char * const *endline,
  1093. X  struct charset bodychars, int div, int pre, int suf, char *tags
  1094. X)
  1095. X/* lines is an array of strings, up to but not including endline.     */
  1096. X/* Sets each character in the parallel array tags to 'f', 'p', or     */
  1097. X/* 'v' according to whether the corresponding line in lines is the    */
  1098. X/* first line of a paragraph, some other line in a paragraph, or a    */
  1099. X/* vacant line, respectively, depending on the values of bodychars    */
  1100. X/* and div, according to "par.doc".  It is assumed that the comprelen */
  1101. X/* and comsuflen of the lines in lines have already been determined   */
  1102. X/* to be at least pre and suf, respectively.                          */
  1103. X{
  1104. X  const char * const *line, *end, *p, * const *nextline;
  1105. X  char *tag, *nexttag;
  1106. X  int anyvacant = 0, status;
  1107. X
  1108. X  if (endline == lines) return;
  1109. X
  1110. X  if (endline == lines + 1) {
  1111. X    *tags = 'f';
  1112. X    return;
  1113. X  }
  1114. X
  1115. X  compresuflen(lines, endline, bodychars, pre, suf, &pre, &suf);
  1116. X
  1117. X  line = lines;
  1118. X  tag = tags;
  1119. X  do {
  1120. X    *tag = 'v';
  1121. X    for (end = *line;  *end;  ++end);
  1122. X    end -= suf;
  1123. X    for (p = *line + pre;  p < end;  ++p)
  1124. X      if (*p != ' ') {
  1125. X        *tag = 'p';
  1126. X        break;
  1127. X      }
  1128. X    if (*tag == 'v') anyvacant = 1;
  1129. X    ++line;
  1130. X    ++tag;
  1131. X  } while (line < endline);
  1132. X
  1133. X  if (anyvacant) {
  1134. X    line = lines;
  1135. X    tag = tags;
  1136. X    do {
  1137. X      if (*tag == 'v') {
  1138. X        ++line;
  1139. X        ++tag;
  1140. X        continue;
  1141. X      }
  1142. X
  1143. X      for (nextline = line + 1, nexttag = tag + 1;
  1144. X           nextline < endline && *nexttag == 'p';
  1145. X           ++nextline, ++nexttag);
  1146. X
  1147. X      delimit(line,nextline,bodychars,div,pre,suf,tag);
  1148. X
  1149. X      line = nextline;
  1150. X      tag = nexttag;
  1151. X    } while (line < endline);
  1152. X
  1153. X    return;
  1154. X  }
  1155. X
  1156. X  if (!div) {
  1157. X    *tags = 'f';
  1158. X    return;
  1159. X  }
  1160. X
  1161. X  line = lines;
  1162. X  tag = tags;
  1163. X  status = ((*lines)[pre] == ' ');
  1164. X  do {
  1165. X    if (((*line)[pre] == ' ') == status)
  1166. X      *tag = 'f';
  1167. X    ++line;
  1168. X    ++tag;
  1169. X  } while (line < endline);
  1170. X}
  1171. X
  1172. X
  1173. Xstatic void setaffixes(
  1174. X  const char * const *inlines, const char * const *endline,
  1175. X  struct charset bodychars, struct charset quotechars,
  1176. X  int hang, int quote, int *pprefix, int *psuffix
  1177. X)
  1178. X/* inlines is an array of strings, up to but not including   */
  1179. X/* endline.  If either of *pprefix, *psuffix is less than 0, */
  1180. X/* sets it to a default value based on inlines, bodychars,   */
  1181. X/* quotechars, hang, and quote, according to "par.doc".      */
  1182. X{
  1183. X  int numin, pre, suf;
  1184. X  const char *start, *p;
  1185. X
  1186. X  numin = endline - inlines;
  1187. X
  1188. X  if ((*pprefix < 0 || *psuffix < 0) && numin > hang + 1)
  1189. X    compresuflen(inlines + hang, endline, bodychars, 0, 0, &pre, &suf);
  1190. X
  1191. X  if (*pprefix < 0)
  1192. X    if (quote && numin == hang + 1) {
  1193. X      start = inlines[hang];
  1194. X      for (p = start;  *p && incharset(*p, quotechars);  ++p);
  1195. X      *pprefix = p - start;
  1196. X    }
  1197. X    else *pprefix = numin > hang + 1  ?  pre  :  0;
  1198. X
  1199. X  if (*psuffix < 0)
  1200. X    *psuffix = numin > hang + 1  ?  suf  :  0;
  1201. X}
  1202. X
  1203. X
  1204. Xstatic void freelines(char **lines)
  1205. X/* Frees the elements of lines, and lines itself. */
  1206. X/* lines is a NULL-terminated array of strings.   */
  1207. X{
  1208. X  char **line;
  1209. X
  1210. X  for (line = lines;  *line;  ++line)
  1211. X    free(*line);
  1212. X
  1213. X  free(lines);
  1214. X}
  1215. X
  1216. X
  1217. Xmain(int argc, const char * const *argv)
  1218. X{
  1219. X  int hang = 0, prefix = -1, suffix = -1, width = 72, div = 0, fit = 0,
  1220. X      guess = 0, just = 0, last = 0, quote = 0, Report = 0, touch = -1,
  1221. X      version = 0, prefixbak, suffixbak, c;
  1222. X  char *parinit, *picopy = NULL, *arg, **inlines = NULL, **endline,
  1223. X       *tags = NULL, **firstline, *firsttag, *end, **nextline, *nexttag,
  1224. X       **outlines = NULL, **line;
  1225. X  const char * const whitechars = " \f\n\r\t\v";
  1226. X  struct charset bodychars = { NULL, 0 }, protectchars = { NULL, 0 },
  1227. X                 quotechars = { NULL, 0 };
  1228. X  errmsg_t errmsg = { '\0' };
  1229. X
  1230. X/* Process PARINIT environment variable: */
  1231. X
  1232. X  parinit = getenv("PARINIT");
  1233. X  if (parinit) {
  1234. X    picopy = malloc((strlen(parinit) + 1) * sizeof (char));
  1235. X    if (!picopy) {
  1236. X      strcpy(errmsg,outofmem);
  1237. X      goto parcleanup;
  1238. X    }
  1239. X    strcpy(picopy,parinit);
  1240. X    arg = strtok(picopy,whitechars);
  1241. X    while (arg) {
  1242. X      parsearg(arg, &hang, &prefix, &suffix, &width, &div, &fit, &guess,
  1243. X               &just, &last, "e, &Report, &touch, &version, errmsg);
  1244. X      if (*errmsg || version) goto parcleanup;
  1245. X      arg = strtok(NULL,whitechars);
  1246. X    }
  1247. X    free(picopy);
  1248. X    picopy = NULL;
  1249. X  }
  1250. X
  1251. X/* Process command line arguments: */
  1252. X
  1253. X  while (*++argv) {
  1254. X    parsearg(*argv, &hang, &prefix, &suffix, &width, &div, &fit,
  1255. X             &guess, &just, &last, "e, &Report, &touch, &version, errmsg);
  1256. X    if (*errmsg || version) goto parcleanup;
  1257. X  }
  1258. X
  1259. X  if (touch < 0) touch = fit || last;
  1260. X  prefixbak = prefix;
  1261. X  suffixbak = suffix;
  1262. X
  1263. X/* Process other environment variables: */
  1264. X
  1265. X  bodychars = getcharset("PARBODY", "", errmsg);
  1266. X  if (*errmsg) goto parcleanup;
  1267. X
  1268. X  protectchars = getcharset("PARPROTECT", "", errmsg);
  1269. X  if (*errmsg) goto parcleanup;
  1270. X
  1271. X  quotechars = getcharset("PARQUOTE", "> ", errmsg);
  1272. X  if (*errmsg) goto parcleanup;
  1273. X
  1274. X/* Main loop: */
  1275. X
  1276. X  for (;;) {
  1277. X    for (;;) {
  1278. X      c = getchar();
  1279. X      if (incharset((char) c, protectchars))
  1280. X        while (c != '\n' && c != EOF) {
  1281. X          putchar(c);
  1282. X          c = getchar();
  1283. X        }
  1284. X      if (c != '\n') break;
  1285. X      putchar(c);
  1286. X    }
  1287. X    if (c == EOF) break;
  1288. X    ungetc(c,stdin);
  1289. X
  1290. X    inlines = readlines(protectchars,quotechars,quote,errmsg);
  1291. X    if (*errmsg) goto parcleanup;
  1292. X
  1293. X    for (endline = inlines;  *endline;  ++endline);
  1294. X    if (endline == inlines) {
  1295. X      free(inlines);
  1296. X      inlines = NULL;
  1297. X      continue;
  1298. X    }
  1299. X
  1300. X    tags = malloc((endline - inlines) * sizeof(char));
  1301. X    if (!tags) {
  1302. X      strcpy(errmsg,outofmem);
  1303. X      goto parcleanup;
  1304. X    }
  1305. X
  1306. X    delimit((const char * const *) inlines,
  1307. X            (const char * const *) endline, bodychars, div, 0, 0, tags);
  1308. X
  1309. X    firstline = inlines;
  1310. X    firsttag = tags;
  1311. X    do {
  1312. X      if (*firsttag == 'v') {
  1313. X        for (end = *firstline;  *end;  ++end);
  1314. X        while (end > *firstline && end[-1] == ' ') --end;
  1315. X        *end = '\0';
  1316. X        puts(*firstline);
  1317. X        ++firsttag;
  1318. X        ++firstline;
  1319. X        continue;
  1320. X      }
  1321. X
  1322. X      for (nexttag = firsttag + 1, nextline = firstline + 1;
  1323. X           nextline < endline && *nexttag == 'p';
  1324. X           ++nexttag, ++nextline);
  1325. X
  1326. X      prefix = prefixbak;
  1327. X      suffix = suffixbak;
  1328. X      setaffixes((const char * const *) firstline,
  1329. X                 (const char * const *) nextline,
  1330. X                 bodychars, quotechars, hang, quote, &prefix, &suffix);
  1331. X      if (width <= prefix + suffix) {
  1332. X        sprintf(errmsg,
  1333. X                "<width> (%d) <= <prefix> (%d) + <suffix> (%d)\n",
  1334. X                width, prefix, suffix);
  1335. X        goto parcleanup;
  1336. X      }
  1337. X
  1338. X      outlines =
  1339. X        reformat((const char * const *) firstline,
  1340. X                 (const char * const *) nextline, hang, prefix, suffix,
  1341. X                  width, fit, guess, just, last, Report, touch, errmsg);
  1342. X      if (*errmsg) goto parcleanup;
  1343. X
  1344. X      for (line = outlines;  *line;  ++line)
  1345. X        puts(*line);
  1346. X
  1347. X      freelines(outlines);
  1348. X      outlines = NULL;
  1349. X
  1350. X      firsttag = nexttag;
  1351. X      firstline = nextline;
  1352. X    } while (firstline < endline);
  1353. X
  1354. X    free(tags);
  1355. X    tags = NULL;
  1356. X
  1357. X    freelines(inlines);
  1358. X    inlines = NULL;
  1359. X  }
  1360. X
  1361. Xparcleanup:
  1362. X
  1363. X  if (picopy) free(picopy);
  1364. X  if (bodychars.individuals) free(bodychars.individuals);
  1365. X  if (protectchars.individuals) free(protectchars.individuals);
  1366. X  if (quotechars.individuals) free(quotechars.individuals);
  1367. X  if (inlines) freelines(inlines);
  1368. X  if (tags) free(tags);
  1369. X  if (outlines) freelines(outlines);
  1370. X
  1371. X  if (*errmsg) {
  1372. X    printf("%s error:\n%.*s", progname, errmsg_size, errmsg);
  1373. X    exit(EXIT_FAILURE);
  1374. X  }
  1375. X
  1376. X  if (version) printf("%s %s\n", progname, versionnum);
  1377. X
  1378. X  exit(EXIT_SUCCESS);
  1379. X}
  1380. END_OF_FILE
  1381. if test 19122 -ne `wc -c <'Par130/par.c'`; then
  1382.     echo shar: \"'Par130/par.c'\" unpacked with wrong size!
  1383. fi
  1384. # end of 'Par130/par.c'
  1385. fi
  1386. echo shar: End of shell archive.
  1387. exit 0
  1388.  
  1389. exit 0 # Just in case...
  1390.