home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume38 / par / part02 < prev    next >
Encoding:
Text File  |  1993-08-12  |  30.7 KB  |  1,114 lines

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