home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume40 / par / part03 < prev    next >
Encoding:
Text File  |  1993-11-09  |  57.4 KB  |  1,787 lines

  1. Newsgroups: comp.sources.misc
  2. From: amc@wuecl.wustl.edu (Adam Costello)
  3. Subject: v40i126:  par - paragraph reformatter, v1.41, Part03/03
  4. Message-ID: <1993Nov9.184204.1166@sparky.sterling.com>
  5. X-Md4-Signature: 2337b3125fef3f0c9747a44f7a978c0c
  6. Sender: kent@sparky.sterling.com (Kent Landfield)
  7. Organization: Sterling Software
  8. Date: Tue, 9 Nov 1993 18:42:04 GMT
  9. Approved: kent@sparky.sterling.com
  10.  
  11. Submitted-by: amc@wuecl.wustl.edu (Adam Costello)
  12. Posting-number: Volume 40, Issue 126
  13. Archive-name: par/part03
  14. Environment: ANSI-C
  15. Supersedes: par131: Volume 39, Issue 83-85
  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:  Par141 Par141/reformat.h Par141/charset.h Par141/buffer.h
  25. #   Par141/releasenotes Par141/reformat.c Par141/par.c
  26. # Wrapped by amc@siesta on Sun Oct 31 01:26:08 1993
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test ! -d 'Par141' ; then
  29.     echo shar: Creating directory \"'Par141'\"
  30.     mkdir 'Par141'
  31. fi
  32. if test -f 'Par141/reformat.h' -a "${1}" != "-c" ; then 
  33.   echo shar: Will not clobber existing file \"'Par141/reformat.h'\"
  34. else
  35. echo shar: Extracting \"'Par141/reformat.h'\" \(1069 characters\)
  36. sed "s/^X//" >'Par141/reformat.h' <<'END_OF_FILE'
  37. X/*********************/
  38. X/* reformat.h        */
  39. X/* for Par 1.41      */
  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#include "errmsg.h"
  48. X
  49. X
  50. Xchar **reformat(
  51. X  const char * const *inlines, const char * const *endline, int afp, int fs,
  52. X  int hang, int prefix, int suffix, int width, int cap, int fit, int guess,
  53. X  int just, int last, int Report, int touch, errmsg_t errmsg
  54. X);
  55. X  /* inlines is an array of pointers to input lines, up to but not  */
  56. X  /* including endline.  inlines and endline must not be equal.     */
  57. X  /* The other parameters are the variables of the same name as     */
  58. X  /* described in "par.doc".  reformat(inlines, endline, afp, fs,   */
  59. X  /* hang, prefix, suffix, width, cap, fit, guess, just, last,      */
  60. X  /* Report, touch, errmsg) returns a NULL-terminated array of      */
  61. X  /* pointers to output lines containing the reformatted paragraph, */
  62. X  /* according to the specification in "par.doc".  None of the      */
  63. X  /* integer parameters may be negative.  Returns NULL on failure.  */
  64. END_OF_FILE
  65. if test 1069 -ne `wc -c <'Par141/reformat.h'`; then
  66.     echo shar: \"'Par141/reformat.h'\" unpacked with wrong size!
  67. fi
  68. # end of 'Par141/reformat.h'
  69. fi
  70. if test -f 'Par141/charset.h' -a "${1}" != "-c" ; then 
  71.   echo shar: Will not clobber existing file \"'Par141/charset.h'\"
  72. else
  73. echo shar: Extracting \"'Par141/charset.h'\" \(1853 characters\)
  74. sed "s/^X//" >'Par141/charset.h' <<'END_OF_FILE'
  75. X/*********************/
  76. X/* charset.h         */
  77. X/* for Par 1.41      */
  78. X/* Copyright 1993 by */
  79. X/* Adam M. Costello  */
  80. X/*********************/
  81. X
  82. X/* This is ANSI C code. */
  83. X
  84. X
  85. X/* Note: Those functions declared here which do not use errmsg    */
  86. X/* always succeed, provided that they are passed valid arguments. */
  87. X
  88. X
  89. X#include "errmsg.h"
  90. X
  91. X
  92. Xtypedef struct charset charset;
  93. X
  94. X
  95. Xcharset *parsecharset(const char *str, errmsg_t errmsg);
  96. X
  97. X  /* parsecharset(str,errmsg) returns the set of characters defined by */
  98. X  /* str using charset syntax (see par.doc).  Returns NULL on failure. */
  99. X
  100. X
  101. Xvoid freecharset(charset *cset);
  102. X
  103. X  /* freecharset(cset) frees any memory associated with */
  104. X  /* *cset.  cset may not be used after this call.      */
  105. X
  106. X
  107. Xint csmember(char c, const charset *cset);
  108. X
  109. X  /* csmember(c,cset) returns 1 if c is a member of *cset, 0 otherwise. */
  110. X
  111. X
  112. Xcharset *csunion(const charset *cset1, const charset *cset2, errmsg_t errmsg);
  113. X
  114. X  /* csunion(cset1,cset2) returns a pointer to the   */
  115. X  /* union of *cset1 and *cset2, or NULL on failure. */
  116. X
  117. X
  118. Xcharset *csdiff(const charset *cset1, const charset *cset2, errmsg_t errmsg);
  119. X
  120. X  /* csdiff(cset1,cset2) returns a pointer to the set */
  121. X  /* difference *cset1 - *cset2 , or NULL on failure. */
  122. X
  123. X
  124. Xvoid csadd(charset *cset1, const charset *cset2, errmsg_t errmsg);
  125. X
  126. X  /* csadd(cset1,cset2) adds the members of *cset2  */
  127. X  /* to *cset1.  On failure, *cset1 is not changed. */
  128. X
  129. X
  130. Xvoid csremove(charset *cset1, const charset *cset2, errmsg_t errmsg);
  131. X
  132. X  /* csremove(cset1,cset2) removes the members of *cset2 */
  133. X  /* from *cset1.  On failure, *cset1 is not changed.    */
  134. X
  135. X
  136. Xcharset *cscopy(const charset *cset, errmsg_t errmsg);
  137. X
  138. X  /* cscopy(cset) returns a copy of cset, or NULL on failure. */
  139. X
  140. X
  141. Xvoid csswap(charset *cset1, charset *cset2);
  142. X
  143. X  /* csswap(cset1,cset2) swaps the contents of *cset1 and *cset2. */
  144. END_OF_FILE
  145. if test 1853 -ne `wc -c <'Par141/charset.h'`; then
  146.     echo shar: \"'Par141/charset.h'\" unpacked with wrong size!
  147. fi
  148. # end of 'Par141/charset.h'
  149. fi
  150. if test -f 'Par141/buffer.h' -a "${1}" != "-c" ; then 
  151.   echo shar: Will not clobber existing file \"'Par141/buffer.h'\"
  152. else
  153. echo shar: Extracting \"'Par141/buffer.h'\" \(2292 characters\)
  154. sed "s/^X//" >'Par141/buffer.h' <<'END_OF_FILE'
  155. X/*********************/
  156. X/* buffer.h          */
  157. X/* for Par 1.41      */
  158. X/* Copyright 1993 by */
  159. X/* Adam M. Costello  */
  160. X/*********************/
  161. X
  162. X/* This is ANSI C code. */
  163. X
  164. X
  165. X/* Note: Those functions declared here which do not use errmsg    */
  166. X/* always succeed, provided that they are passed valid arguments. */
  167. X
  168. X
  169. X#include "errmsg.h"
  170. X
  171. X#include <stddef.h>
  172. X
  173. X
  174. Xtypedef struct buffer buffer;
  175. X
  176. X
  177. Xbuffer *newbuffer(size_t itemsize, errmsg_t errmsg);
  178. X
  179. X  /* newbuffer(itemsize,errmsg) returns a pointer to a    */
  180. X  /* new empty buffer which holds items of size itemsize. */
  181. X  /* itemsize must not be 0.  Returns NULL on failure.    */
  182. X
  183. X
  184. Xvoid freebuffer(buffer *buf);
  185. X
  186. X  /* freebuffer(buf) frees the memory associated with */
  187. X  /* *buf.  buf may not be used after this call.      */
  188. X
  189. X
  190. Xvoid clearbuffer(buffer *buf);
  191. X
  192. X  /* clearbuffer(buf) removes  */
  193. X  /* all items from *buf, but  */
  194. X  /* does not free any memory. */
  195. X
  196. X
  197. Xvoid additem(buffer *buf, const void *item, errmsg_t errmsg);
  198. X
  199. X  /* additem(buf,item,errmsg) copies *item to the end of     */
  200. X  /* *buf.  item must point to an object of the proper size  */
  201. X  /* for *buf.  If additem() fails, *buf will be unaffected. */
  202. X
  203. X
  204. Xint numitems(buffer *buf);
  205. X
  206. X  /* numitems(buf) returns the number of items in *buf. */
  207. X
  208. X
  209. Xvoid *copyitems(buffer *buf, errmsg_t errmsg);
  210. X
  211. X  /* copyitems(buf,errmsg) returns an array of objects of */
  212. X  /* the proper size for *buf, one for each item in *buf, */
  213. X  /* or NULL if there are no items in buf.  The elements  */
  214. X  /* of the array are copied from the items in *buf, in   */
  215. X  /* order.  The array is allocated with malloc(), so it  */
  216. X  /* may be freed with free().  Returns NULL on failure.  */
  217. X
  218. X
  219. Xvoid *nextitem(buffer *buf);
  220. X
  221. X  /* When buf was created by newbuffer, a pointer associated with buf  */
  222. X  /* was initialized to point at the first slot in *buf.  If there is  */
  223. X  /* an item in the slot currently pointed at, nextitem(buf) advances  */
  224. X  /* the pointer to the next slot and returns the old value.  If there */
  225. X  /* is no item in the slot, nextitem(buf) leaves the pointer where it */
  226. X  /* is and returns NULL.                                              */
  227. X
  228. X
  229. Xvoid rewindbuffer(buffer *buf);
  230. X
  231. X  /* rewindbuffer(buf) resets the pointer used by   */
  232. X  /* nextitem() to point at the first slot in *buf. */
  233. END_OF_FILE
  234. if test 2292 -ne `wc -c <'Par141/buffer.h'`; then
  235.     echo shar: \"'Par141/buffer.h'\" unpacked with wrong size!
  236. fi
  237. # end of 'Par141/buffer.h'
  238. fi
  239. if test -f 'Par141/releasenotes' -a "${1}" != "-c" ; then 
  240.   echo shar: Will not clobber existing file \"'Par141/releasenotes'\"
  241. else
  242. echo shar: Extracting \"'Par141/releasenotes'\" \(8910 characters\)
  243. sed "s/^X//" >'Par141/releasenotes' <<'END_OF_FILE'
  244. X  *********************
  245. X  * releasenotes      *
  246. X  * for Par 1.41      *
  247. X  * Copyright 1993 by *
  248. X  * Adam M. Costello  *
  249. X  *********************
  250. X
  251. X
  252. XEach entry below describes changes since the previous version.
  253. X
  254. XPar 1.00 released 25 July 1993
  255. X    The first release.
  256. X
  257. XPar 1.10 released 2 August 1993
  258. X    Fixed the following bugs:
  259. X        In reformat.c I used sprintf() but forgot to #include <stdio.h>.
  260. X        I forgot to verify that <width> > <prefix> + <suffix>.
  261. X        The first word of a paragraph was expanded to include initial white
  262. X        characters, not just spaces, contrary to par.doc.
  263. X        Some invalid options were not complained about.
  264. X        NUL characters in the input were not handled.
  265. X        A pointer foul-up in freelines() in par.c could cause a crash.
  266. X    Added the following features:
  267. X        The f, j, and t options.
  268. X        The PARBODY environment variable.
  269. X        Multiple options may be concatenated into a single argument.
  270. X    Removed the m option:
  271. X        Its function is better performed by the f and t options.  Normally
  272. X        I would avoid making incompatible changes, unless I were doing a
  273. X        complete overhaul of the whole program, in which case I'd make the
  274. X        version number 2.00 to alert users to possible incompatibilities.
  275. X        However, in this particular instance I allowed an incompatibility in
  276. X        a minor upgrade because version 1.00 was distributed to only four
  277. X        people.
  278. X    Changed the handling of white characters:
  279. X        par now changes all of them (except newlines) to spaces as they are
  280. X        read.  This is another incompatible change, excused for the same
  281. X        reason.
  282. X    Made all error messages begin with "par error:".
  283. X
  284. XPar 1.20 released 10 August 1993
  285. X    Since Par 1.10 was distributed to no one, I've made some more
  286. X        incompatible changes in Par 1.20.
  287. X    Added the following features:
  288. X        The d option.
  289. X        Paragraphs are now separated by vacant lines, not just blank lines.
  290. X        <hang> now affects not only <prefix> but also <suffix>.
  291. X
  292. XPar 1.30 released 18 August 1993
  293. X    Since Par 1.20 was posted to comp.sources.misc, I have made only
  294. X        backward-compatible changes in Par 1.30.
  295. X    Fixed the following bugs:
  296. X        One wrong word in par.c sometimes caused par to crash.  Thanks go to
  297. X            vogelke@c-17igp.wpafb.af.mil (Contr Karl Vogel) for sending me
  298. X            an input file that caused a crash.
  299. X        Too-long words were chopped up before the first word in a paragraph
  300. X            was expanded to include initial spaces, allowing impossibility
  301. X            #1 to occur.  The order of the two operations has been reversed.
  302. X            Thanks go to splat@deakin.oz.au (Andrew Cashin) for reporting
  303. X            the error message.
  304. X    Added the following features:
  305. X        The g option (motivated by suggestions from several people).
  306. X        The q option (inspired by a suggestion from splat@deakin.oz.au
  307. X            (Andrew Cashin)).
  308. X        The R option (my attempt to squash a bad idea from Par 1.00).
  309. X        The PARQUOTE environment variable (comes with the q option).
  310. X        The PARPROTECT environment variable (inspired by a suggestion from
  311. X            dennisf@se01.elk.miles.com (Dennis Flaherty)).
  312. X    Altered the terminology:
  313. X        Several terms have been added, and the meaning of some terms has
  314. X        been slightly modified.  This is a change in the language used to
  315. X        describe par's behavior, not a change in par's actual behavior.
  316. X    Added a clean target to protoMakefile (suggested by hlj@posix.com (Hal
  317. X        Jespersen)).
  318. X
  319. XPar 1.31 released 7 September 1993
  320. X    The version number is 1.31 rather than 1.40 because all added features
  321. X        are really just enhancements of existing features.
  322. X    Fixed the following bug:
  323. X        In par.doc, in the example of a paragraph produced by a greedy
  324. X            algorithm, the word "establish" appeared twice in a row.  Thanks
  325. X            go to daniel@astro.rug.nl (Daniel Kussendrager) for first
  326. X            pointing this out.  (The example is now even better because the
  327. X            paragraph looks even worse than before.)
  328. X    Added the following features:
  329. X        A usage message to accompany command line or environment variable
  330. X            syntax errors (first suggested by qarl@ecl.wustl.edu (Karl
  331. X            Stiefvater)).
  332. X        The help and c options.
  333. X        The B, P, and Q options, which render PARBODY, PARPROTECT, and
  334. X            PARQUOTE no longer necessary.  They are retained, though, for
  335. X            compatibility and convenience.
  336. X        The _b, _q, and _Q escape sequences for charset syntax.
  337. X    Added the term "charset syntax".
  338. X    Isolated the character set code in charset.c and charset.h.
  339. X
  340. XPar 1.32 released 13 September 1993
  341. X    Fixed the following bugs:
  342. X        par could crash when the + or - operator was used with the B, P, and
  343. X            Q options.  Thanks go to splat@deakin.oz.au (Andrew Cashin) for
  344. X            reporting this.
  345. X        If <quote> were 1 and two adjacent lines had different
  346. X            quoteprefixes, one of which was a prefix of the other, and only
  347. X            the line with the shorter quoteprefix contained a non-quote
  348. X            character, then nothing would be altered.  According to
  349. X            par.doc 1.31, this was correct, but since neither line is
  350. X            vacant, I consider this a bug in the design of the <quote>
  351. X            feature.  Now the longer quoteprefix will be truncated to match
  352. X            the shorter one, and will therefore be vacant.  Thanks go to
  353. X            splat@deakin.oz.au (Andrew Cashin) for asking about this.
  354. X    Made slight changes to the documentation.
  355. X
  356. XPar 1.40 released 10 Oct 1993
  357. X    Fixed the following bugs:
  358. X        The phrase "containing at least two lines" was left out of the
  359. X            definition of "vacant line" in par.doc and par.1, although
  360. X            the code implemented the correct definition.  (The phrase now
  361. X            appears in the definition of "order <k> bodiless line".)
  362. X        There was still a flaw in the design of the <quote> feature.  If two
  363. X            adjacent lines had quoteprefixes neither of which was a prefix
  364. X            of the other, no line would be inserted between them, possibly
  365. X            causing other inserted lines not to be vacant.  Now it should
  366. X            be true that every line inserted or truncated by the <quote>
  367. X            feature will turn out to be vacant.
  368. X        When <hang> began affecting the default value of <suffix> (as of Par
  369. X            1.20), it should also have begun affecting the choice of source
  370. X            of characters for the suffixes of lines in OPs.
  371. X    Added the following features:
  372. X        The i option (suggested by barrett@ee.und.ac.za (Alan Barrett)).
  373. X        The e option (inspired by a suggestion from tim@ben.dciem.dnd.ca
  374. X            (Tim Pointing)).
  375. X        The r option.
  376. X        The p, s, and w options are now accepted without numbers.
  377. X        par no longer gives up so easily when choosing default values for
  378. X            <prefix> and <suffix> for an IP with less than <hang>+2 lines,
  379. X            nor when choosing the source of characters for the prefix and
  380. X            suffix of the <i>th output line when <i> > <n> and <n> <=
  381. X            <hang>.  These are incompatible changes, but I cannot imagine
  382. X            anyone preferring the old behavior.
  383. X    Altered the terminology:
  384. X        Added the terms "bodiless line" (a generalization of "vacant line"),
  385. X            "fallback prelen", and "fallback suflen".
  386. X    Made miscellaneous changes to the documentation, including the addition
  387. X        of the Quick Start section.
  388. X
  389. XPar 1.41 released 31 Oct 1993
  390. X    Moved the former Release Notes section of par.doc into its own file.
  391. X    Fixed the following bugs:
  392. X        A couple of inconsistencies of style in the code (pointed out by
  393. X            dickey@software.org (Thomas E. Dickey).
  394. X        If <quote> and <hang> were 1 and a one-line paragraph beginning with
  395. X            quote characters was reformatted into a multi-line paragraph,
  396. X            the new lines began with spaces instead of quote characters.
  397. X            This was because the policy for copying prefixes used the
  398. X            fallback prelen rather than the augmented version of it used
  399. X            for computing the default value of <prefix>.  Now both use the
  400. X            same formula.  Thanks go to king@rtsg.mot.com (Steven King) for
  401. X            reporting the problem.
  402. X        If the t option were given without a number, it was unset instead of
  403. X            being set to 1.
  404. X    Added the following feature:
  405. X        The E option (suggested by alex@bilver.oau.org (Alex Matulich)).
  406. X    Altered the terminology:
  407. X        Added the term "augmented fallback prelen".
  408. X    Added to the Rights and Responsibilities section of par.doc a guideline
  409. X        for handling version numbers in patched versions of Par.
  410. X    Added a useful suggestion to protoMakefile.
  411. END_OF_FILE
  412. if test 8910 -ne `wc -c <'Par141/releasenotes'`; then
  413.     echo shar: \"'Par141/releasenotes'\" unpacked with wrong size!
  414. fi
  415. # end of 'Par141/releasenotes'
  416. fi
  417. if test -f 'Par141/reformat.c' -a "${1}" != "-c" ; then 
  418.   echo shar: Will not clobber existing file \"'Par141/reformat.c'\"
  419. else
  420. echo shar: Extracting \"'Par141/reformat.c'\" \(14681 characters\)
  421. sed "s/^X//" >'Par141/reformat.c' <<'END_OF_FILE'
  422. X/*********************/
  423. X/* reformat.c        */
  424. X/* for Par 1.41      */
  425. X/* Copyright 1993 by */
  426. X/* Adam M. Costello  */
  427. X/*********************/
  428. X
  429. X/* This is ANSI C code. */
  430. X
  431. X
  432. X#include "reformat.h"  /* Makes sure we're consistent with the  */
  433. X                       /* prototype.  Also includes "errmsg.h". */
  434. X#include "buffer.h"    /* Also includes <stddef.h>.             */
  435. X
  436. X#include <stdio.h>
  437. X#include <stdlib.h>
  438. X#include <ctype.h>
  439. X#include <string.h>
  440. X
  441. X#undef NULL
  442. X#define NULL ((void *) 0)
  443. X
  444. X#ifdef DONTFREE
  445. X#define free(ptr)
  446. X#endif
  447. X
  448. X
  449. Xtypedef unsigned char wflag_t;
  450. X
  451. Xtypedef struct word {
  452. X  const char *chrs;       /* Pointer to the characters in the word */
  453. X                          /* (NOT terminated by '\0').             */
  454. X  struct word *prev,      /* Pointer to previous word.             */
  455. X              *next,      /* Pointer to next word.                 */
  456. X                          /* Supposing this word were the first... */
  457. X              *nextline;  /*   Pointer to first word in next line. */
  458. X  int score,              /*   Value of the objective function.    */
  459. X      length;             /* Length of this word.                  */
  460. X  wflag_t flags;          /* Notable properties of this word.      */
  461. X} word;
  462. X
  463. X/* The following may be bitwise-OR'd together */
  464. X/* to set the flags field of a word:          */
  465. X
  466. Xstatic const wflag_t
  467. X  W_SHIFTED = 1,  /* This word should have an extra space before */
  468. X                  /* it unless it's the first word in the line.  */
  469. X  W_CURIOUS = 2,  /* This is a curious word (see par.doc).       */
  470. X  W_CAPITAL = 4;  /* This is a capitalized word (see par.doc).   */
  471. X
  472. X#define isshifted(w) ( (w)->flags & 1)
  473. X#define iscurious(w) (((w)->flags & 2) != 0)
  474. X#define iscapital(w) (((w)->flags & 4) != 0)
  475. X
  476. X
  477. Xstatic int checkcapital(word *w)
  478. X/* Returns 1 if *w is capitalized according to the definition */
  479. X/* in par.doc (assuming <cap> is 0), or 0 if not.             */
  480. X{
  481. X  const char *p, *end;
  482. X
  483. X  for (p = w->chrs, end = p + w->length;  p < end && !isalnum(*p);  ++p);
  484. X  return p < end && !islower(*p);
  485. X}
  486. X
  487. X
  488. Xstatic int checkcurious(word *w)
  489. X/* Returns 1 if *w is curious according to */
  490. X/* the definition in par.doc, or 0 if not. */
  491. X{
  492. X  const char *start, *p;
  493. X  char ch;
  494. X
  495. X  for (start = w->chrs, p = start + w->length;  p > start;  --p) {
  496. X    ch = p[-1];
  497. X    if (isalnum(ch)) return 0;
  498. X    if (ch == '.' || ch == '?' || ch == '!' || ch == ':') break;
  499. X  }
  500. X
  501. X  if (p <= start + 1) return 0;
  502. X
  503. X  --p;
  504. X  do if (isalnum(*--p)) return 1;
  505. X  while (p > start);
  506. X
  507. X  return 0;
  508. X}
  509. X
  510. X
  511. Xstatic int simplebreaks(word *head, word *tail, int L, int last)
  512. X
  513. X/* Chooses line breaks in a list of words which maximize the length of the   */
  514. X/* shortest line.  L is the maximum line length.  The last line counts as a  */
  515. X/* line only if last is non-zero. _head must point to a dummy word, and tail */
  516. X/* must point to the last word, whose next field must be NULL.  Returns the  */
  517. X/* length of the shortest line on success, -1 if there is a word of length   */
  518. X/* greater than L, or L if there are no lines.                               */
  519. X{
  520. X  word *w1, *w2;
  521. X  int linelen, score;
  522. X
  523. X  if (!head->next) return L;
  524. X
  525. X  for (w1 = tail, linelen = w1->length;
  526. X       w1 != head && linelen <= L;
  527. X       linelen += isshifted(w1), w1 = w1->prev, linelen += 1 + w1->length) {
  528. X    w1->score = last ? linelen : L;
  529. X    w1->nextline = NULL;
  530. X  }
  531. X
  532. X  for ( ;  w1 != head;  w1 = w1->prev) {
  533. X    w1->score = -1;
  534. X    for (linelen = w1->length,  w2 = w1->next;
  535. X         linelen <= L;
  536. X         linelen += 1 + isshifted(w2) + w2->length,  w2 = w2->next) {
  537. X      score = w2->score;
  538. X      if (linelen < score) score = linelen;
  539. X      if (score >= w1->score) {
  540. X        w1->nextline = w2;
  541. X        w1->score = score;
  542. X      }
  543. X    }
  544. X  }
  545. X
  546. X  return head->next->score;
  547. X}
  548. X
  549. X
  550. Xstatic void normalbreaks(
  551. X  word *head, word *tail, int L, int fit, int last, errmsg_t errmsg
  552. X)
  553. X/* Chooses line breaks in a list of words according to the policy   */
  554. X/* in "par.doc" for <just> = 0 (L is <L>, fit is <fit>, and last is */
  555. X/* <last>).  head must point to a dummy word, and tail must point   */
  556. X/* to the last word, whose next field must be NULL.                 */
  557. X{
  558. X  word *w1, *w2;
  559. X  int tryL, shortest, score, target, linelen, extra, minlen;
  560. X
  561. X  *errmsg = '\0';
  562. X  if (!head->next) return;
  563. X
  564. X  target = L;
  565. X
  566. X/* Determine minimum possible difference between  */
  567. X/* the lengths of the shortest and longest lines: */
  568. X
  569. X  if (fit) {
  570. X    score = L + 1;
  571. X    for (tryL = L;  ;  --tryL) {
  572. X      shortest = simplebreaks(head,tail,tryL,last);
  573. X      if (shortest < 0) break;
  574. X      if (tryL - shortest < score) {
  575. X        target = tryL;
  576. X        score = target - shortest;
  577. X      }
  578. X    }
  579. X  }
  580. X
  581. X/* Determine maximum possible length of the shortest line: */
  582. X
  583. X  shortest = simplebreaks(head,tail,target,last);
  584. X  if (shortest < 0) {
  585. X    sprintf(errmsg,impossibility,1);
  586. X    return;
  587. X  }
  588. X
  589. X/* Minimize the sum of the squares of the differences */
  590. X/* between target and the lengths of the lines:       */
  591. X
  592. X  w1 = tail;
  593. X  do {
  594. X    w1->score = -1;
  595. X    for (linelen = w1->length,  w2 = w1->next;
  596. X         linelen <= target;
  597. X         linelen += 1 + isshifted(w2) + w2->length,  w2 = w2->next) {
  598. X      extra = target - linelen;
  599. X      minlen = shortest;
  600. X      if (w2)
  601. X        score = w2->score;
  602. X      else {
  603. X        score = 0;
  604. X        if (!last) extra = minlen = 0;
  605. X      }
  606. X      if (linelen >= minlen  &&  score >= 0) {
  607. X        score += extra * extra;
  608. X        if (w1->score < 0  ||  score <= w1->score) {
  609. X          w1->nextline = w2;
  610. X          w1->score = score;
  611. X        }
  612. X      }
  613. X      if (!w2) break;
  614. X    }
  615. X    w1 = w1->prev;
  616. X  } while (w1 != head);
  617. X
  618. X  if (head->next->score < 0)
  619. X    sprintf(errmsg,impossibility,2);
  620. X}
  621. X
  622. X
  623. Xstatic void justbreaks(
  624. X  word *head, word *tail, int L, int last, errmsg_t errmsg
  625. X)
  626. X/* Chooses line breaks in a list of words according to the  */
  627. X/* policy in "par.doc" for <just> = 1 (L is <L> and last is */
  628. X/* <last>).  head must point to a dummy word, and tail must */
  629. X/* point to the last word, whose next field must be NULL.   */
  630. X{
  631. X  word *w1, *w2;
  632. X  int numgaps, extra, score, gap, maxgap, numbiggaps;
  633. X
  634. X  *errmsg = '\0';
  635. X  if (!head->next) return;
  636. X
  637. X/* Determine the minimum possible largest inter-word gap: */
  638. X
  639. X  w1 = tail;
  640. X  do {
  641. X    w1->score = L;
  642. X    for (numgaps = 0, extra = L - w1->length, w2 = w1->next;
  643. X         extra >= 0;
  644. X         ++numgaps, extra -= 1 + isshifted(w2) + w2->length, w2 = w2->next) {
  645. X      gap = numgaps ? (extra + numgaps - 1) / numgaps : L;
  646. X      if (w2)
  647. X        score = w2->score;
  648. X      else {
  649. X        score = 0;
  650. X        if (!last) gap = 0;
  651. X      }
  652. X      if (gap > score) score = gap;
  653. X      if (score < w1->score) {
  654. X        w1->nextline = w2;
  655. X        w1->score = score;
  656. X      }
  657. X      if (!w2) break;
  658. X    }
  659. X    w1 = w1->prev;
  660. X  } while (w1 != head);
  661. X
  662. X  maxgap = head->next->score;
  663. X  if (maxgap >= L) {
  664. X    strcpy(errmsg, "Cannot justify.\n");
  665. X    return;
  666. X  }
  667. X
  668. X/* Minimize the sum of the squares of the numbers   */
  669. X/* of extra spaces required in each inter-word gap: */
  670. X
  671. X  w1 = tail;
  672. X  do {
  673. X    w1->score = -1;
  674. X    for (numgaps = 0, extra = L - w1->length, w2 = w1->next;
  675. X         extra >= 0;
  676. X         ++numgaps, extra -= 1 + isshifted(w2) + w2->length, w2 = w2->next) {
  677. X      gap = numgaps ? (extra + numgaps - 1) / numgaps : L;
  678. X      if (w2)
  679. X        score = w2->score;
  680. X      else {
  681. X        if (!last) {
  682. X          w1->nextline = NULL;
  683. X          w1->score = 0;
  684. X          break;
  685. X        }
  686. X        score = 0;
  687. X      }
  688. X      if (gap <= maxgap && score >= 0) {
  689. X        numbiggaps = extra % numgaps;
  690. X        score += (extra / numgaps) * (extra + numbiggaps) + numbiggaps;
  691. X        /* The above may not look like the sum of the squares of the numbers */
  692. X        /* of extra spaces required in each inter-word gap, but trust me, it */
  693. X        /* is.  It's easier to prove graphically than algebraicly.           */
  694. X        if (w1->score < 0  ||  score <= w1->score) {
  695. X          w1->nextline = w2;
  696. X          w1->score = score;
  697. X        }
  698. X      }
  699. X      if (!w2) break;
  700. X    }
  701. X    w1 = w1->prev;
  702. X  } while (w1 != head);
  703. X
  704. X  if (head->next->score < 0)
  705. X    sprintf(errmsg,impossibility,3);
  706. X}
  707. X
  708. X
  709. Xchar **reformat(
  710. X  const char * const *inlines, const char * const *endline, int afp, int fs,
  711. X  int hang, int prefix, int suffix, int width, int cap, int fit, int guess,
  712. X  int just, int last, int Report, int touch, errmsg_t errmsg
  713. X)
  714. X{
  715. X  int numin, affix, L, onfirstword = 1, linelen, numout, numgaps, extra, phase;
  716. X  const char * const *line, **suffixes = NULL, **suf, *end, *p1, *p2;
  717. X  char *q1, *q2, **outlines = NULL;
  718. X  word dummy, *head, *tail, *w1, *w2;
  719. X  buffer *pbuf = NULL;
  720. X
  721. X/* Initialization: */
  722. X
  723. X  *errmsg = '\0';
  724. X  dummy.next = dummy.prev = NULL;
  725. X  dummy.flags = 0;
  726. X  head = tail = &dummy;
  727. X  numin = endline - inlines;
  728. X  if (numin <= 0) {
  729. X    sprintf(errmsg,impossibility,4);
  730. X    goto rfcleanup;
  731. X  }
  732. X
  733. X/* Allocate space for pointers to the suffixes: */
  734. X
  735. X  suffixes = malloc(numin * sizeof (const char *));
  736. X  if (!suffixes) {
  737. X    strcpy(errmsg,outofmem);
  738. X    goto rfcleanup;
  739. X  }
  740. X
  741. X/* Set the pointers to the suffixes, and create the words: */
  742. X
  743. X  affix = prefix + suffix;
  744. X  L = width - prefix - suffix;
  745. X
  746. X  line = inlines, suf = suffixes;
  747. X  do {
  748. X    for (end = *line;  *end;  ++end);
  749. X    if (end - *line < affix) {
  750. X      sprintf(errmsg,
  751. X              "Line %d shorter than <prefix> + <suffix> = %d + %d = %d\n",
  752. X              line - inlines + 1, prefix, suffix, affix);
  753. X      goto rfcleanup;
  754. X    }
  755. X    end -= suffix;
  756. X    *suf = end;
  757. X    p1 = *line + prefix;
  758. X    for (;;) {
  759. X      while (p1 < end && *p1 == ' ') ++p1;
  760. X      if (p1 == end) break;
  761. X      p2 = p1;
  762. X      if (onfirstword) {
  763. X        p1 = *line + prefix;
  764. X        onfirstword = 0;
  765. X      }
  766. X      while (p2 < end && *p2 != ' ') ++p2;
  767. X      w1 = malloc(sizeof (word));
  768. X      if (!w1) {
  769. X        strcpy(errmsg,outofmem);
  770. X        goto rfcleanup;
  771. X      }
  772. X      w1->next = NULL;
  773. X      w1->prev = tail;
  774. X      tail = tail->next = w1;
  775. X      w1->chrs = p1;
  776. X      w1->length = p2 - p1;
  777. X      w1->flags = 0;
  778. X      p1 = p2;
  779. X    }
  780. X    ++line, ++suf;
  781. X  } while (line < endline);
  782. X
  783. X/* If guess is 1, set flag values and merge words: */
  784. X
  785. X  if (guess) {
  786. X    for (w1 = head, w2 = head->next;  w2;  w1 = w2, w2 = w2->next) {
  787. X      if (checkcurious(w2)) w2->flags |= W_CURIOUS;
  788. X      if (cap || checkcapital(w2)) {
  789. X        w2->flags |= W_CAPITAL;
  790. X        if (iscurious(w1))
  791. X          if (w1->chrs[w1->length] && w1->chrs + w1->length + 1 == w2->chrs) {
  792. X            w2->length += w1->length + 1;
  793. X            w2->chrs = w1->chrs;
  794. X            w2->prev = w1->prev;
  795. X            w2->prev->next = w2;
  796. X            if (iscapital(w1)) w2->flags |= W_CAPITAL;
  797. X            else w2->flags &= ~W_CAPITAL;
  798. X            if (isshifted(w1)) w2->flags |= W_SHIFTED;
  799. X            else w2->flags &= ~W_SHIFTED;
  800. X            free(w1);
  801. X          }
  802. X          else
  803. X            w2->flags |= W_SHIFTED;
  804. X      }
  805. X    }
  806. X    tail = w1;
  807. X  }
  808. X
  809. X/* Check for too-long words: */
  810. X
  811. X  if (Report)
  812. X    for (w2 = head->next;  w2;  w2 = w2->next) {
  813. X      if (w2->length > L) {
  814. X        linelen = w2->length;
  815. X        if (linelen > errmsg_size - 17)
  816. X          linelen = errmsg_size - 17;
  817. X        sprintf(errmsg, "Word too long: %.*s\n", linelen, w2->chrs);
  818. X        goto rfcleanup;
  819. X      }
  820. X    }
  821. X  else
  822. X    for (w2 = head->next;  w2;  w2 = w2->next)
  823. X      while (w2->length > L) {
  824. X        w1 = malloc(sizeof (word));
  825. X        if (!w1) {
  826. X          strcpy(errmsg,outofmem);
  827. X          goto rfcleanup;
  828. X        }
  829. X        w1->next = w2;
  830. X        w1->prev = w2->prev;
  831. X        w1->prev->next = w1;
  832. X        w2->prev = w1;
  833. X        w1->chrs = w2->chrs;
  834. X        w2->chrs += L;
  835. X        w1->length = L;
  836. X        w2->length -= L;
  837. X        w1->flags = 0;
  838. X        if (iscapital(w2)) {
  839. X          w1->flags |= W_CAPITAL;
  840. X          w2->flags &= ~W_CAPITAL;
  841. X        }
  842. X        if (isshifted(w2)) {
  843. X          w1->flags |= W_SHIFTED;
  844. X          w2->flags &= ~W_SHIFTED;
  845. X        }
  846. X      }
  847. X
  848. X/* Choose line breaks according to policy in "par.doc": */
  849. X
  850. X  if (just) justbreaks(head,tail,L,last,errmsg);
  851. X  else normalbreaks(head,tail,L,fit,last,errmsg);
  852. X  if (*errmsg) goto rfcleanup;
  853. X
  854. X/* Change L to the length of the longest line if required: */
  855. X
  856. X  if (!just && touch) {
  857. X    L = 0;
  858. X    w1 = head->next;
  859. X    while (w1) {
  860. X      for (linelen = w1->length, w2 = w1->next;
  861. X           w2 != w1->nextline;
  862. X           linelen += 1 + isshifted(w2) + w2->length, w2 = w2->next);
  863. X      if (linelen > L) L = linelen;
  864. X      w1 = w2;
  865. X    }
  866. X  }
  867. X
  868. X/* Construct the lines: */
  869. X
  870. X  pbuf = newbuffer(sizeof (char *), errmsg);
  871. X  if (*errmsg) goto rfcleanup;
  872. X
  873. X  numout = 0;
  874. X  w1 = head->next;
  875. X  while (numout < hang || w1) {
  876. X    if (w1)
  877. X      for (w2 = w1->next, numgaps = 0, extra = L - w1->length;
  878. X           w2 != w1->nextline;
  879. X           ++numgaps, extra -= 1 + isshifted(w2) + w2->length, w2 = w2->next);
  880. X    linelen = suffix  ||  just && (w2 || last) ?
  881. X                L + affix :
  882. X                w1 ? prefix + L - extra : prefix;
  883. X    q1 = malloc((linelen + 1) * sizeof (char));
  884. X    if (!q1) {
  885. X      strcpy(errmsg,outofmem);
  886. X      goto rfcleanup;
  887. X    }
  888. X    additem(pbuf, &q1, errmsg);
  889. X    if (*errmsg) goto rfcleanup;
  890. X    ++numout;
  891. X    q2 = q1 + prefix;
  892. X    if      (numout <= numin) memcpy(q1, inlines[numout - 1], prefix);
  893. X    else if (numin  >  hang ) memcpy(q1, endline[-1],         prefix);
  894. X    else {
  895. X      if (afp > prefix) afp = prefix;
  896. X      memcpy(q1, endline[-1], afp);
  897. X      q1 += afp;
  898. X      while (q1 < q2) *q1++ = ' ';
  899. X    }
  900. X    q1 = q2;
  901. X    if (w1) {
  902. X      phase = numgaps / 2;
  903. X      for (w2 = w1;  ;  ) {
  904. X        memcpy(q1, w2->chrs, w2->length);
  905. X        q1 += w2->length;
  906. X        w2 = w2->next;
  907. X        if (w2 == w1->nextline) break;
  908. X        *q1++ = ' ';
  909. X        if (just && (w1->nextline || last)) {
  910. X          phase += extra;
  911. X          while (phase >= numgaps) {
  912. X            *q1++ = ' ';
  913. X            phase -= numgaps;
  914. X          }
  915. X        }
  916. X        if (isshifted(w2)) *q1++ = ' ';
  917. X      }
  918. X    }
  919. X    q2 += linelen - affix;
  920. X    while (q1 < q2) *q1++ = ' ';
  921. X    q2 = q1 + suffix;
  922. X    if      (numout <= numin) memcpy(q1, suffixes[numout - 1], suffix);
  923. X    else if (numin  >  hang ) memcpy(q1, suffixes[numin  - 1], suffix);
  924. X    else {
  925. X      if (fs > suffix) fs = suffix;
  926. X      memcpy(q1, suffixes[numin - 1], fs);
  927. X      q1 += fs;
  928. X      while(q1 < q2) *q1++ = ' ';
  929. X    }
  930. X    *q2 = '\0';
  931. X    if (w1) w1 = w1->nextline;
  932. X  }
  933. X
  934. X  q1 = NULL;
  935. X  additem(pbuf, &q1, errmsg);
  936. X  if (*errmsg) goto rfcleanup;
  937. X
  938. X  outlines = copyitems(pbuf,errmsg);
  939. X
  940. Xrfcleanup:
  941. X
  942. X  if (suffixes) free(suffixes);
  943. X
  944. X  while (tail != head) {
  945. X    tail = tail->prev;
  946. X    free(tail->next);
  947. X  }
  948. X
  949. X  if (pbuf) {
  950. X    if (!outlines)
  951. X      for (;;) {
  952. X        outlines = nextitem(pbuf);
  953. X        if (!outlines) break;
  954. X        free(*outlines);
  955. X      }
  956. X    freebuffer(pbuf);
  957. X  }
  958. X
  959. X  return outlines;
  960. X}
  961. END_OF_FILE
  962. if test 14681 -ne `wc -c <'Par141/reformat.c'`; then
  963.     echo shar: \"'Par141/reformat.c'\" unpacked with wrong size!
  964. fi
  965. # end of 'Par141/reformat.c'
  966. fi
  967. if test -f 'Par141/par.c' -a "${1}" != "-c" ; then 
  968.   echo shar: Will not clobber existing file \"'Par141/par.c'\"
  969. else
  970. echo shar: Extracting \"'Par141/par.c'\" \(24516 characters\)
  971. sed "s/^X//" >'Par141/par.c' <<'END_OF_FILE'
  972. X/*********************/
  973. X/* par.c             */
  974. X/* for Par 1.41      */
  975. X/* Copyright 1993 by */
  976. X/* Adam M. Costello  */
  977. X/*********************/
  978. X
  979. X/* This is ANSI C code. */
  980. X
  981. X
  982. X#include "charset.h"   /* Also includes "errmsg.h". */
  983. X#include "buffer.h"    /* Also includes <stddef.h>. */
  984. X#include "reformat.h"
  985. X
  986. X#include <stdio.h>
  987. X#include <string.h>
  988. X#include <stdlib.h>
  989. X#include <ctype.h>
  990. X
  991. X#undef NULL
  992. X#define NULL ((void *) 0)
  993. X
  994. X#ifdef DONTFREE
  995. X#define free(ptr)
  996. X#endif
  997. X
  998. X
  999. Xstatic const char * const usagemsg =
  1000. X"\n"
  1001. X"Usage:\n"
  1002. X"\n"
  1003. X"par [help] [version] [B<op><set>] [P<op><set>] [Q<op><set>] [h[<hang>]]\n"
  1004. X"    [p[<prefix>]] [r[<repeat>]] [s[<suffix>]] [w[<width>]] [c[<cap>]]\n"
  1005. X"    [d[<div>]] [E[<Err>]] [e[<expel>]] [f[<fit>]] [g[<guess>]] [i[<invis>]]\n"
  1006. X"    [j[<just>]] [l[<last>]] [q[<quote>]] [R[<Report>]] [t[<touch>]]\n"
  1007. X"\n"
  1008. X"help       print usage message       "
  1009. X                                 "  ---------- Boolean parameters: ---------\n"
  1010. X"version    print version number      "
  1011. X                                 "  Option:   If 1:\n"
  1012. X"B<op><set> as <op> is =/+/-,         "
  1013. X                                 "  c<cap>    count all words as capitalized\n"
  1014. X"           replace/augment/diminish  "
  1015. X                                 "  d<div>    use indentation as a delimiter\n"
  1016. X"           body chars by <set>       "
  1017. X                                 "  E<Err>    send messages to stderr\n"
  1018. X"P<op><set> ditto for protective chars"
  1019. X                                 "  e<expel>  discard superfluous lines\n"
  1020. X"Q<op><set> ditto for quote chars     "
  1021. X                                 "  f<fit>    narrow paragraph for best fit\n"
  1022. X"-------- Integer parameters: --------"
  1023. X                                 "  g<guess>  preserve wide sentence breaks\n"
  1024. X"h<hang>    skip IP's 1st <hang> lines"
  1025. X                                 "  i<invis>  hide lines inserted by <quote>\n"
  1026. X"           in scan for common affixes"
  1027. X                                 "  j<just>   justify paragraphs\n"
  1028. X"p<prefix>  prefix length             "
  1029. X                                 "  l<last>   treat last lines like others\n"
  1030. X"r<repeat>  if not 0, force bodiless  "
  1031. X                                 "  q<quote>  supply vacant lines between\n"
  1032. X"           lines to length <width>   "
  1033. X                                 "            different quote nesting levels\n"
  1034. X"s<suffix>  suffix length             "
  1035. X                                 "  R<Report> print error for too-long words\n"
  1036. X"w<width>   max output line length    "
  1037. X                                 "  t<touch>  move suffixes left\n"
  1038. X"\n"
  1039. X"See par.doc or par.1 (the man page) for more information.\n"
  1040. X;
  1041. X
  1042. X
  1043. X/* Structure for recording properties of lines within segments: */
  1044. X
  1045. Xtypedef unsigned char lflag_t;
  1046. X
  1047. Xtypedef struct lineprop {
  1048. X  short p, s;     /* Length of the prefix and suffix of a bodiless */
  1049. X                  /* line, or the fallback prelen and suflen       */
  1050. X                  /* of the IP containing a non-bodiless line.     */
  1051. X  lflag_t flags;  /* Boolean properties (see below).               */
  1052. X  char rc;        /* The repeated character of a bodiless line.    */
  1053. X} lineprop;
  1054. X
  1055. X/* Flags for marking boolean properties: */
  1056. X
  1057. Xstatic const lflag_t L_BODILESS = 1,  /* Bodiless line.             */
  1058. X                     L_INVIS    = 2,  /* Invisible line.            */
  1059. X                     L_FIRST    = 4,  /* First line of a paragraph. */
  1060. X                     L_SUPERF   = 8;  /* Superfluous line.          */
  1061. X
  1062. X#define isbodiless(prop) ( (prop)->flags & 1)
  1063. X#define    isinvis(prop) (((prop)->flags & 2) != 0)
  1064. X#define    isfirst(prop) (((prop)->flags & 4) != 0)
  1065. X#define   issuperf(prop) (((prop)->flags & 8) != 0)
  1066. X#define   isvacant(prop) (isbodiless(prop) && (prop)->rc == ' ')
  1067. X
  1068. X
  1069. Xstatic int digtoint(char c)
  1070. X
  1071. X/* Returns the value represented by the digit c, or -1 if c is not a digit. */
  1072. X{
  1073. X  const char *p, * const digits = "0123456789";
  1074. X
  1075. X  if (!c) return -1;
  1076. X  p = strchr(digits,c);
  1077. X  return  p  ?  p - digits  :  -1;
  1078. X
  1079. X  /* We can't simply return c - '0' because this is ANSI C code,  */
  1080. X  /* so it has to work for any character set, not just ones which */
  1081. X  /* put the digits together in order.  Also, an array that could */
  1082. X  /* be referenced as digtoint[c] might be bad because there's no */
  1083. X  /* upper limit on CHAR_MAX.                                     */
  1084. X}
  1085. X
  1086. X
  1087. Xstatic int strtoudec(const char *s, int *pn)
  1088. X
  1089. X/* Converts the longest prefix of string s consisting of decimal   */
  1090. X/* digits to an integer, which is stored in *pn.  Normally returns */
  1091. X/* 1.  If *s is not a digit, then *pn is not changed, but 1 is     */
  1092. X/* still returned.  If the integer represented is greater than     */
  1093. X/* 9999, then *pn is not changed and 0 is returned.                */
  1094. X{
  1095. X  int n = 0, d;
  1096. X
  1097. X  d = digtoint(*s);
  1098. X  if (d < 0) return 1;
  1099. X
  1100. X  do {
  1101. X    if (n >= 1000) return 0;
  1102. X    n = 10 * n + d;
  1103. X    d = digtoint(*++s);
  1104. X  } while (d >= 0);
  1105. X
  1106. X  *pn = n;
  1107. X
  1108. X  return 1;
  1109. X}
  1110. X
  1111. X
  1112. Xstatic void parsearg(
  1113. X  const char *arg, int *phelp, int *pversion, charset *bodychars,
  1114. X  charset *protectchars, charset *quotechars, int *phang, int *pprefix,
  1115. X  int *prepeat, int *psuffix, int *pwidth, int *pcap, int *pdiv, int *pErr,
  1116. X  int *pexpel, int *pfit, int *pguess, int *pinvis, int *pjust, int *plast,
  1117. X  int *pquote, int *pReport, int *ptouch, errmsg_t errmsg
  1118. X)
  1119. X/* Parses the command line argument in *arg, setting the objects pointed to */
  1120. X/* by the other pointers as appropriate.  *phelp and *pversion are boolean  */
  1121. X/* flags indicating whether the help and version options were supplied.     */
  1122. X{
  1123. X  const char *savearg = arg;
  1124. X  charset *chars, *change;
  1125. X  char oc;
  1126. X  int n;
  1127. X
  1128. X  *errmsg = '\0';
  1129. X
  1130. X  if (*arg == '-') ++arg;
  1131. X
  1132. X  if (!strcmp(arg, "help")) {
  1133. X    *phelp = 1;
  1134. X    return;
  1135. X  }
  1136. X
  1137. X  if (!strcmp(arg, "version")) {
  1138. X    *pversion = 1;
  1139. X    return;
  1140. X  }
  1141. X
  1142. X  if (*arg == 'B' || *arg == 'P' || *arg == 'Q' ) {
  1143. X    chars =  *arg == 'B'  ?  bodychars    :
  1144. X             *arg == 'P'  ?  protectchars :
  1145. X          /* *arg == 'Q' */  quotechars   ;
  1146. X    ++arg;
  1147. X    if (*arg != '='  &&  *arg != '+'  &&  *arg != '-') goto badarg;
  1148. X    change = parsecharset(arg + 1, errmsg);
  1149. X    if (change) {
  1150. X      if      (*arg == '=')   csswap(chars,change);
  1151. X      else if (*arg == '+')   csadd(chars,change,errmsg);
  1152. X      else  /* *arg == '-' */ csremove(chars,change,errmsg);
  1153. X      freecharset(change);
  1154. X    }
  1155. X    return;
  1156. X  }
  1157. X
  1158. X  if (isdigit(*arg)) {
  1159. X    if (!strtoudec(arg, &n)) goto badarg;
  1160. X    if (n <= 8) *pprefix = n;
  1161. X    else *pwidth = n;
  1162. X  }
  1163. X
  1164. X  for (;;) {
  1165. X    while (isdigit(*arg)) ++arg;
  1166. X    oc = *arg;
  1167. X    if (!oc) break;
  1168. X    n = -1;
  1169. X    if (!strtoudec(++arg, &n)) goto badarg;
  1170. X    if (oc == 'h' || oc == 'p' || oc == 'r' || oc == 's' || oc == 'w') {
  1171. X      if      (oc == 'h')   *phang   =  n >= 0 ? n :  1;
  1172. X      else if (oc == 'w')   *pwidth  =  n >= 0 ? n : 79;
  1173. X      else if (oc == 'p')   *pprefix =  n;
  1174. X      else if (oc == 'r')   *prepeat =  n >= 0 ? n :  3;
  1175. X      else  /* oc == 's' */ *psuffix =  n;
  1176. X    }
  1177. X    else {
  1178. X      if (n < 0) n = 1;
  1179. X      if (n > 1) goto badarg;
  1180. X      if      (oc == 'c') *pcap    = n;
  1181. X      else if (oc == 'd') *pdiv    = n;
  1182. X      else if (oc == 'E') *pErr    = n;
  1183. X      else if (oc == 'e') *pexpel  = n;
  1184. X      else if (oc == 'f') *pfit    = n;
  1185. X      else if (oc == 'g') *pguess  = n;
  1186. X      else if (oc == 'i') *pinvis  = n;
  1187. X      else if (oc == 'j') *pjust   = n;
  1188. X      else if (oc == 'l') *plast   = n;
  1189. X      else if (oc == 'q') *pquote  = n;
  1190. X      else if (oc == 'R') *pReport = n;
  1191. X      else if (oc == 't') *ptouch  = n;
  1192. X      else goto badarg;
  1193. X    }
  1194. X  }
  1195. X
  1196. X  return;
  1197. X
  1198. Xbadarg:
  1199. X
  1200. X  sprintf(errmsg, "Bad argument: %.*s\n", errmsg_size - 16, savearg);
  1201. X  *phelp = 1;
  1202. X}
  1203. X
  1204. X
  1205. Xstatic char **readlines(
  1206. X  lineprop **pprops, const charset *protectchars,
  1207. X  const charset *quotechars, int invis, int quote, errmsg_t errmsg
  1208. X)
  1209. X/* Reads lines from stdin until EOF, or until a line beginning with a   */
  1210. X/* protective character is encountered (in which case the protective    */
  1211. X/* character is pushed back onto the input stream), or until a blank    */
  1212. X/* line is encountered (in which case the newline is pushed back onto   */
  1213. X/* the input stream).  Returns a NULL-terminated array of pointers to   */
  1214. X/* individual lines, stripped of their newline characters.  Every NUL   */
  1215. X/* character is stripped, and every white character is changed to a     */
  1216. X/* space unless it is a newline.  If quote is 1, vacant lines will be   */
  1217. X/* supplied as described for the q option in par.doc.  *pprops is set   */
  1218. X/* to an array of lineprop structures, one for each line, each of whose */
  1219. X/* flags field is either 0 or L_INVIS (the other fields are 0).  If     */
  1220. X/* there are no lines, *pprops is set to NULL.  The returned array may  */
  1221. X/* be freed with freelines().  *pprops may be freed with free() if      */
  1222. X/* it's not NULL.  On failure, returns NULL and sets *pprops to NULL.   */
  1223. X{
  1224. X  buffer *cbuf = NULL, *lbuf = NULL, *lpbuf = NULL;
  1225. X  int c, empty, blank, firstline, qsonly, oldqsonly = 0, vlnlen;
  1226. X  char ch, *ln = NULL, nullchar = '\0', *nullline = NULL, *qpend,
  1227. X       *oldln = NULL, *oldqpend = NULL, *p, *op, *vln = NULL, **lines = NULL;
  1228. X  lineprop vprop = { 0, 0, 0, '\0' }, iprop = { 0, 0, 0, '\0' };
  1229. X
  1230. X  /* oldqsonly, oldln, and oldquend don't really need to be initialized.   */
  1231. X  /* They are initialized only to appease compilers that try to be helpful */
  1232. X  /* by issuing warnings about unitialized automatic variables.            */
  1233. X
  1234. X  iprop.flags = L_INVIS;
  1235. X  *errmsg = '\0';
  1236. X
  1237. X  *pprops = NULL;
  1238. X
  1239. X  cbuf = newbuffer(sizeof (char), errmsg);
  1240. X  if (*errmsg) goto rlcleanup;
  1241. X  lbuf = newbuffer(sizeof (char *), errmsg);
  1242. X  if (*errmsg) goto rlcleanup;
  1243. X  lpbuf = newbuffer(sizeof (lineprop), errmsg);
  1244. X  if (*errmsg) goto rlcleanup;
  1245. X
  1246. X  for (empty = blank = firstline = 1;  ; ) {
  1247. X    c = getchar();
  1248. X    if (c == EOF) break;
  1249. X    if (c == '\n') {
  1250. X      if (blank) {
  1251. X        ungetc(c,stdin);
  1252. X        break;
  1253. X      }
  1254. X      additem(cbuf, &nullchar, errmsg);
  1255. X      if (*errmsg) goto rlcleanup;
  1256. X      ln = copyitems(cbuf,errmsg);
  1257. X      if (*errmsg) goto rlcleanup;
  1258. X      if (quote) {
  1259. X        for (qpend = ln;
  1260. X             *qpend && csmember(*qpend, quotechars);
  1261. X             ++qpend);
  1262. X        for (p = qpend;  *p == ' ' || csmember(*p, quotechars);  ++p);
  1263. X        qsonly =  *p == '\0';
  1264. X        while (qpend > ln && qpend[-1] == ' ') --qpend;
  1265. X        if (!firstline) {
  1266. X          for (p = ln, op = oldln;
  1267. X               p < qpend && op < oldqpend && *p == *op;
  1268. X               ++p, ++op);
  1269. X          if (!(p == qpend && op == oldqpend))
  1270. X            if (!invis && (oldqsonly || qsonly)) {
  1271. X              if (oldqsonly) {
  1272. X                *op = '\0';
  1273. X                oldqpend = op;
  1274. X              }
  1275. X              if (qsonly) {
  1276. X                *p = '\0';
  1277. X                qpend = p;
  1278. X              }
  1279. X            }
  1280. X            else {
  1281. X              vlnlen = p - ln;
  1282. X              vln = malloc((vlnlen + 1) * sizeof (char));
  1283. X              if (!vln) {
  1284. X                strcpy(errmsg,outofmem);
  1285. X                goto rlcleanup;
  1286. X              }
  1287. X              strncpy(vln,ln,vlnlen);
  1288. X              vln[vlnlen] = '\0';
  1289. X              additem(lbuf, &vln, errmsg);
  1290. X              if (*errmsg) goto rlcleanup;
  1291. X              additem(lpbuf,  invis ? &iprop : &vprop,  errmsg);
  1292. X              if (*errmsg) goto rlcleanup;
  1293. X              vln = NULL;
  1294. X            }
  1295. X        }
  1296. X        oldln = ln;
  1297. X        oldqpend = qpend;
  1298. X        oldqsonly = qsonly;
  1299. X      }
  1300. X      additem(lbuf, &ln, errmsg);
  1301. X      if (*errmsg) goto rlcleanup;
  1302. X      ln = NULL;
  1303. X      additem(lpbuf, &vprop, errmsg);
  1304. X      if (*errmsg) goto rlcleanup;
  1305. X      clearbuffer(cbuf);
  1306. X      empty = blank = 1;
  1307. X      firstline = 0;
  1308. X    }
  1309. X    else {
  1310. X      if (empty) {
  1311. X        if (csmember((char) c, protectchars)) {
  1312. X          ungetc(c,stdin);
  1313. X          break;
  1314. X        }
  1315. X        empty = 0;
  1316. X      }
  1317. X      if (!c) continue;
  1318. X      if (isspace(c)) c = ' ';
  1319. X      else blank = 0;
  1320. X      ch = c;
  1321. X      additem(cbuf, &ch, errmsg);
  1322. X      if (*errmsg) goto rlcleanup;
  1323. X    }
  1324. X  }
  1325. X
  1326. X  if (!blank) {
  1327. X    additem(cbuf, &nullchar, errmsg);
  1328. X    if (*errmsg) goto rlcleanup;
  1329. X    ln = copyitems(cbuf,errmsg);
  1330. X    if (*errmsg) goto rlcleanup;
  1331. X    additem(lbuf, &ln, errmsg);
  1332. X    if (*errmsg) goto rlcleanup;
  1333. X    ln = NULL;
  1334. X    additem(lpbuf, &vprop, errmsg);
  1335. X    if (*errmsg) goto rlcleanup;
  1336. X  }
  1337. X
  1338. X  additem(lbuf, &nullline, errmsg);
  1339. X  if (*errmsg) goto rlcleanup;
  1340. X  *pprops = copyitems(lpbuf,errmsg);
  1341. X  if (*errmsg) goto rlcleanup;
  1342. X  lines = copyitems(lbuf,errmsg);
  1343. X
  1344. Xrlcleanup:
  1345. X
  1346. X  if (cbuf) freebuffer(cbuf);
  1347. X  if (lpbuf) freebuffer(lpbuf);
  1348. X  if (lbuf) {
  1349. X    if (!lines)
  1350. X      for (;;) {
  1351. X        lines = nextitem(lbuf);
  1352. X        if (!lines) break;
  1353. X        free(*lines);
  1354. X      }
  1355. X    freebuffer(lbuf);
  1356. X  }
  1357. X  if (ln) free(ln);
  1358. X  if (vln) free(vln);
  1359. X
  1360. X  return lines;
  1361. X}
  1362. X
  1363. X
  1364. Xstatic void compresuflen(
  1365. X  const char * const *lines, const char * const *endline,
  1366. X  const charset *bodychars, int pre, int suf, int *ppre, int *psuf
  1367. X)
  1368. X/* lines is an array of strings, up to but not including endline.  */
  1369. X/* Writes into *ppre and *psuf the comprelen and comsuflen of the  */
  1370. X/* lines in lines.  Assumes that they have already been determined */
  1371. X/* to be at least pre and suf.  endline must not equal lines.      */
  1372. X{
  1373. X  const char *start, *end, * const *line, *p1, *p2, *start2;
  1374. X
  1375. X  start = *lines;
  1376. X  for (end = start + pre;  *end && !csmember(*end, bodychars);  ++end);
  1377. X  for (line = lines + 1;  line < endline;  ++line) {
  1378. X    for (p1 = start + pre, p2 = *line + pre;
  1379. X         p1 < end && *p1 == *p2;
  1380. X         ++p1, ++p2);
  1381. X    end = p1;
  1382. X  }
  1383. X  *ppre = end - start;
  1384. X
  1385. X  start2 = *lines + *ppre;
  1386. X  for (end = start2;  *end;  ++end);
  1387. X  for (start = end - suf;
  1388. X       start > start2 && !csmember(start[-1], bodychars);
  1389. X       --start);
  1390. X  for (line = lines + 1;  line < endline;  ++line) {
  1391. X    start2 = *line + *ppre;
  1392. X    for (p2 = start2;  *p2;  ++p2);
  1393. X    for (p1 = end - suf, p2 -= suf;
  1394. X         p1 > start && p2 > start2 && p1[-1] == p2[-1];
  1395. X         --p1, --p2);
  1396. X    start = p1;
  1397. X  }
  1398. X  while (end - start >= 2 && *start == ' ' && start[1] == ' ') ++start;
  1399. X  *psuf = end - start;
  1400. X}
  1401. X
  1402. X
  1403. Xstatic void delimit(
  1404. X  const char * const *lines, const char * const *endline,
  1405. X  const charset *bodychars, int repeat, int div,
  1406. X  int pre, int suf, lineprop *props
  1407. X)
  1408. X/* lines is an array of strings, up to but not including     */
  1409. X/* endline.  Sets fields in each lineprop in the parallel    */
  1410. X/* array props as appropriate, except for the L_SUPERF flag, */
  1411. X/* which is never set.  It is assumed that the comprelen     */
  1412. X/* and comsuflen of the lines in lines have already been     */
  1413. X/* determined to be at least pre and suf, respectively.      */
  1414. X{
  1415. X  const char * const *line, *end, *p, * const *nextline;
  1416. X  char rc;
  1417. X  lineprop *prop, *nextprop;
  1418. X  int anybodiless = 0, status;
  1419. X
  1420. X  if (endline == lines) return;
  1421. X
  1422. X  if (endline == lines + 1) {
  1423. X    props->flags |= L_FIRST;
  1424. X    props->p = pre, props->s = suf;
  1425. X    return;
  1426. X  }
  1427. X
  1428. X  compresuflen(lines, endline, bodychars, pre, suf, &pre, &suf);
  1429. X
  1430. X  line = lines, prop = props;
  1431. X  do {
  1432. X    prop->flags |= L_BODILESS;
  1433. X    prop->p = pre, prop->s = suf;
  1434. X    for (end = *line;  *end;  ++end);
  1435. X    end -= suf;
  1436. X    p = *line + pre;
  1437. X    rc =  p < end  ?  *p  :  ' ';
  1438. X    if (rc != ' ' && (!repeat || end - p < repeat))
  1439. X      prop->flags &= ~L_BODILESS;
  1440. X    else
  1441. X      while (p < end) {
  1442. X        if (*p != rc) {
  1443. X          prop->flags &= ~L_BODILESS;
  1444. X          break;
  1445. X        }
  1446. X        ++p;
  1447. X      }
  1448. X    if (isbodiless(prop)) {
  1449. X      anybodiless = 1;
  1450. X      prop->rc = rc;
  1451. X    }
  1452. X    ++line, ++prop;
  1453. X  } while (line < endline);
  1454. X
  1455. X  if (anybodiless) {
  1456. X    line = lines, prop = props;
  1457. X    do {
  1458. X      if (isbodiless(prop)) {
  1459. X        ++line, ++prop;
  1460. X        continue;
  1461. X      }
  1462. X
  1463. X      for (nextline = line + 1, nextprop = prop + 1;
  1464. X           nextline < endline && !isbodiless(nextprop);
  1465. X           ++nextline, ++nextprop);
  1466. X
  1467. X      delimit(line,nextline,bodychars,repeat,div,pre,suf,prop);
  1468. X
  1469. X      line = nextline, prop = nextprop;
  1470. X    } while (line < endline);
  1471. X
  1472. X    return;
  1473. X  }
  1474. X
  1475. X  if (!div) {
  1476. X    props->flags |= L_FIRST;
  1477. X    return;
  1478. X  }
  1479. X
  1480. X  line = lines, prop = props;
  1481. X  status = ((*lines)[pre] == ' ');
  1482. X  do {
  1483. X    if (((*line)[pre] == ' ') == status)
  1484. X      prop->flags |= L_FIRST;
  1485. X    ++line, ++prop;
  1486. X  } while (line < endline);
  1487. X}
  1488. X
  1489. X
  1490. Xstatic void marksuperf(
  1491. X  const char * const * lines, const char * const * endline, lineprop *props
  1492. X)
  1493. X/* lines points to the first line of a segment, and endline to one  */
  1494. X/* line beyond the last line in the segment.  Sets L_SUPERF bits in */
  1495. X/* the flags fields of the props array whenever the corresponding   */
  1496. X/* line is superfluous.  L_BODILESS bits must already be set.       */
  1497. X{
  1498. X  const char * const *line, *p;
  1499. X  lineprop *prop, *mprop, dummy;
  1500. X  int inbody, num, mnum;
  1501. X
  1502. X  for (line = lines, prop = props;  line < endline;  ++line, ++prop)
  1503. X    if (isvacant(prop))
  1504. X      prop->flags |= L_SUPERF;
  1505. X
  1506. X  inbody = mnum = 0;
  1507. X  mprop = &dummy;
  1508. X  for (line = lines, prop = props;  line < endline;  ++line, ++prop)
  1509. X    if (isvacant(prop)) {
  1510. X      for (num = 0, p = *line;  *p;  ++p)
  1511. X        if (*p != ' ') ++num;
  1512. X      if (inbody || num < mnum)
  1513. X        mnum = num, mprop = prop;
  1514. X      inbody = 0;
  1515. X    } else {
  1516. X      if (!inbody) mprop->flags &= ~L_SUPERF;
  1517. X      inbody = 1;
  1518. X    }
  1519. X} 
  1520. X
  1521. X
  1522. Xstatic void setaffixes(
  1523. X  const char * const *inlines, const char * const *endline,
  1524. X  const lineprop *props, const charset *bodychars, const charset *quotechars,
  1525. X  int hang, int quote, int *pafp, int *pfs, int *pprefix, int *psuffix
  1526. X)
  1527. X/* inlines is an array of strings, up to but not including endline,    */
  1528. X/* representing an IP.  inlines and endline must not be equal.  props  */
  1529. X/* is the the parallel array of lineprop structures.  *pafp and *pfs   */
  1530. X/* are set to the augmented fallback prelen and fallback suflen of the */
  1531. X/* IP.  If either of *pprefix, *psuffix is less than 0, it is set to a */
  1532. X/* default value as specified in "par.doc".                            */
  1533. X{
  1534. X  int numin, pre, suf;
  1535. X  const char *p;
  1536. X
  1537. X  numin = endline - inlines;
  1538. X
  1539. X  if ((*pprefix < 0 || *psuffix < 0)  &&  numin > hang + 1)
  1540. X    compresuflen(inlines + hang, endline, bodychars, 0, 0, &pre, &suf);
  1541. X
  1542. X  p = *inlines + props->p;
  1543. X  if (numin == 1 && quote)
  1544. X    while (*p && csmember (*p, quotechars))
  1545. X      ++p;
  1546. X  *pafp = p - *inlines;
  1547. X  *pfs = props->s;
  1548. X
  1549. X  if (*pprefix < 0)
  1550. X    *pprefix  =  numin > hang + 1  ?  pre  :  *pafp;
  1551. X
  1552. X  if (*psuffix < 0)
  1553. X    *psuffix  =  numin > hang + 1  ?  suf  :  *pfs;
  1554. X}
  1555. X
  1556. X
  1557. Xstatic void freelines(char **lines)
  1558. X/* Frees the elements of lines, and lines itself. */
  1559. X/* lines is a NULL-terminated array of strings.   */
  1560. X{
  1561. X  char **line;
  1562. X
  1563. X  for (line = lines;  *line;  ++line)
  1564. X    free(*line);
  1565. X
  1566. X  free(lines);
  1567. X}
  1568. X
  1569. X
  1570. Xint main(int argc, const char * const *argv)
  1571. X{
  1572. X  int help = 0, version = 0, hang = 0, prefix = -1, repeat = 0, suffix = -1,
  1573. X      width = 72, cap = 0, div = 0, Err = 0, expel = 0, fit = 0, guess = 0,
  1574. X      invis = 0, just = 0, last = 0, quote = 0, Report = 0, touch = -1,
  1575. X      prefixbak, suffixbak, c, sawnonblank, oweblank, n, i, afp, fs;
  1576. X  charset *bodychars = NULL, *protectchars = NULL, *quotechars = NULL;
  1577. X  char *parinit = NULL, *arg, **inlines = NULL, **endline, **firstline, *end,
  1578. X       **nextline, **outlines = NULL, **line;
  1579. X  const char *env, * const whitechars = " \f\n\r\t\v";
  1580. X  errmsg_t errmsg = { '\0' };
  1581. X  lineprop *props = NULL, *firstprop, *nextprop;
  1582. X  FILE *errout;
  1583. X
  1584. X/* Process environment variables: */
  1585. X
  1586. X  env = getenv("PARBODY");
  1587. X  if (!env) env = "";
  1588. X  bodychars = parsecharset(env,errmsg);
  1589. X  if (*errmsg) {
  1590. X    help = 1;
  1591. X    goto parcleanup;
  1592. X  }
  1593. X
  1594. X  env = getenv("PARPROTECT");
  1595. X  if (!env) env = "";
  1596. X  protectchars = parsecharset(env,errmsg);
  1597. X  if (*errmsg) {
  1598. X    help = 1;
  1599. X    goto parcleanup;
  1600. X  }
  1601. X
  1602. X  env = getenv("PARQUOTE");
  1603. X  if (!env) env = "> ";
  1604. X  quotechars = parsecharset(env,errmsg);
  1605. X  if (*errmsg) {
  1606. X    help = 1;
  1607. X    goto parcleanup;
  1608. X  }
  1609. X
  1610. X  env = getenv("PARINIT");
  1611. X  if (env) {
  1612. X    parinit = malloc((strlen(env) + 1) * sizeof (char));
  1613. X    if (!parinit) {
  1614. X      strcpy(errmsg,outofmem);
  1615. X      goto parcleanup;
  1616. X    }
  1617. X    strcpy(parinit,env);
  1618. X    arg = strtok(parinit,whitechars);
  1619. X    while (arg) {
  1620. X      parsearg(arg, &help, &version, bodychars, protectchars,
  1621. X               quotechars, &hang, &prefix, &repeat, &suffix,
  1622. X               &width, &cap, &div, &Err, &expel, &fit, &guess,
  1623. X               &invis, &just, &last, "e, &Report, &touch, errmsg);
  1624. X      if (*errmsg || help || version) goto parcleanup;
  1625. X      arg = strtok(NULL,whitechars);
  1626. X    }
  1627. X    free(parinit);
  1628. X    parinit = NULL;
  1629. X  }
  1630. X
  1631. X/* Process command line arguments: */
  1632. X
  1633. X  while (*++argv) {
  1634. X    parsearg(*argv, &help, &version, bodychars, protectchars,
  1635. X             quotechars, &hang, &prefix, &repeat, &suffix,
  1636. X             &width, &cap, &div, &Err, &expel, &fit, &guess,
  1637. X             &invis, &just, &last, "e, &Report, &touch, errmsg);
  1638. X    if (*errmsg || help || version) goto parcleanup;
  1639. X  }
  1640. X
  1641. X  if (touch < 0) touch = fit || last;
  1642. X  prefixbak = prefix;
  1643. X  suffixbak = suffix;
  1644. X
  1645. X/* Main loop: */
  1646. X
  1647. X  for (sawnonblank = 0, oweblank = 0; ; ) {
  1648. X    for (;;) {
  1649. X      c = getchar();
  1650. X      if (expel && c == '\n') {
  1651. X        oweblank = sawnonblank;
  1652. X        continue;
  1653. X      }
  1654. X      if (csmember((char) c, protectchars)) {
  1655. X        sawnonblank = 1;
  1656. X        if (oweblank) {
  1657. X          putchar('\n');
  1658. X          oweblank = 0;
  1659. X        }
  1660. X        while (c != '\n' && c != EOF) {
  1661. X          putchar(c);
  1662. X          c = getchar();
  1663. X        }
  1664. X      }
  1665. X      if (c != '\n') break;
  1666. X      putchar(c);
  1667. X    }
  1668. X    if (c == EOF) break;
  1669. X    ungetc(c,stdin);
  1670. X
  1671. X    inlines =
  1672. X      readlines(&props, protectchars, quotechars, invis, quote, errmsg);
  1673. X    if (*errmsg) goto parcleanup;
  1674. X
  1675. X    for (endline = inlines;  *endline;  ++endline);
  1676. X    if (endline == inlines) {
  1677. X      free(inlines);
  1678. X      inlines = NULL;
  1679. X      continue;
  1680. X    }
  1681. X
  1682. X    sawnonblank = 1;
  1683. X    if (oweblank) {
  1684. X      putchar('\n');
  1685. X      oweblank = 0;
  1686. X    }
  1687. X
  1688. X    delimit((const char * const *) inlines,
  1689. X            (const char * const *) endline,
  1690. X            bodychars, repeat, div, 0, 0, props);
  1691. X
  1692. X    if (expel)
  1693. X      marksuperf((const char * const *) inlines,
  1694. X                 (const char * const *) endline, props);
  1695. X
  1696. X    firstline = inlines, firstprop = props;
  1697. X    do {
  1698. X      if (isbodiless(firstprop)) {
  1699. X        if (!isinvis(firstprop) && !(expel && issuperf(firstprop))) {
  1700. X          for (end = *firstline;  *end;  ++end);
  1701. X          if (!repeat  ||  firstprop->rc == ' ' && !firstprop->s) {
  1702. X            while (end > *firstline && end[-1] == ' ') --end;
  1703. X            *end = '\0';
  1704. X            puts(*firstline);
  1705. X          }
  1706. X          else {
  1707. X            n = width - firstprop->p - firstprop->s;
  1708. X            if (n < 0) {
  1709. X              sprintf(errmsg,impossibility,5);
  1710. X              goto parcleanup;
  1711. X            }
  1712. X            printf("%.*s", firstprop->p, *firstline);
  1713. X            for (i = n;  i;  --i)
  1714. X              putchar(firstprop->rc);
  1715. X            puts(end - firstprop->s);
  1716. X          }
  1717. X        }
  1718. X        ++firstline, ++firstprop;
  1719. X        continue;
  1720. X      }
  1721. X
  1722. X      for (nextline = firstline + 1, nextprop = firstprop + 1;
  1723. X           nextline < endline && !isbodiless(nextprop) && !isfirst(nextprop);
  1724. X           ++nextline, ++nextprop);
  1725. X
  1726. X      prefix = prefixbak, suffix = suffixbak;
  1727. X      setaffixes((const char * const *) firstline,
  1728. X                 (const char * const *) nextline, firstprop, bodychars,
  1729. X                 quotechars, hang, quote, &afp, &fs, &prefix, &suffix);
  1730. X      if (width <= prefix + suffix) {
  1731. X        sprintf(errmsg,
  1732. X                "<width> (%d) <= <prefix> (%d) + <suffix> (%d)\n",
  1733. X                width, prefix, suffix);
  1734. X        goto parcleanup;
  1735. X      }
  1736. X
  1737. X      outlines =
  1738. X        reformat((const char * const *) firstline,
  1739. X                 (const char * const *) nextline,
  1740. X                 afp, fs, hang, prefix, suffix, width, cap,
  1741. X                 fit, guess, just, last, Report, touch, errmsg);
  1742. X      if (*errmsg) goto parcleanup;
  1743. X
  1744. X      for (line = outlines;  *line;  ++line)
  1745. X        puts(*line);
  1746. X
  1747. X      freelines(outlines);
  1748. X      outlines = NULL;
  1749. X
  1750. X      firstline = nextline, firstprop = nextprop;
  1751. X    } while (firstline < endline);
  1752. X
  1753. X    freelines(inlines);
  1754. X    inlines = NULL;
  1755. X
  1756. X    free(props);
  1757. X    props = NULL;
  1758. X  }
  1759. X
  1760. Xparcleanup:
  1761. X
  1762. X  if (bodychars) freecharset(bodychars);
  1763. X  if (protectchars) freecharset(protectchars);
  1764. X  if (quotechars) freecharset(quotechars);
  1765. X  if (parinit) free(parinit);
  1766. X  if (inlines) freelines(inlines);
  1767. X  if (props) free(props);
  1768. X  if (outlines) freelines(outlines);
  1769. X
  1770. X  errout = Err ? stderr : stdout;
  1771. X  if (*errmsg) fprintf(errout, "par error:\n%.*s", errmsg_size, errmsg);
  1772. X  if (version) fputs("par 1.41\n",errout);
  1773. X  if (help)    fputs(usagemsg,errout);
  1774. X
  1775. X  return *errmsg ? EXIT_FAILURE : EXIT_SUCCESS;
  1776. X}
  1777. END_OF_FILE
  1778. if test 24516 -ne `wc -c <'Par141/par.c'`; then
  1779.     echo shar: \"'Par141/par.c'\" unpacked with wrong size!
  1780. fi
  1781. # end of 'Par141/par.c'
  1782. fi
  1783. echo shar: End of shell archive.
  1784. exit 0
  1785.  
  1786. exit 0 # Just in case...
  1787.