home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume9 / cpr_dt < prev    next >
Text File  |  1989-11-26  |  49KB  |  1,887 lines

  1. Newsgroups: comp.sources.misc
  2. organization: AT&T Bell Labs - Lincroft, NJ
  3. subject: v09i010: New version of CPR - C Pretty-printer
  4. from: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5. Reply-To: dmt@pegasus.ATT.COM (Dave Tutelman)
  6.  
  7. Posting-number: Volume 9, Issue 10
  8. Submitted-by: dmt@pegasus.ATT.COM (Dave Tutelman)
  9. Archive-name: cpr_dt
  10.  
  11. : This is a shar archive.  Extract with sh, not csh.
  12. : The rest of this file will extract:
  13. : readme cpr.c parsargs.c list.c fold.c report.c wildfile.c cpr.h makefile
  14. echo extracting - readme
  15. sed 's/^X//' > readme << '!EOR!'
  16. X
  17. X                README FILE FOR CPR
  18. X                ^^^^^^^^^^^^^^^^^^^
  19. X
  20. XOK, we gotta have a README file, so you can tell what this does and
  21. Xwhere it came from.  Well, I pulled it off the net myself early this
  22. Xyear (1989), and found it sort of useful.  It's a pretty-printer for
  23. XC programs.
  24. X
  25. XWHAT IT DOES:
  26. X   -    Paginates, with meaningful page headers.
  27. X   -    Makes a table of contents for files and functions.
  28. X   -    Won't start a function at the bottom of a page.  (In fact,
  29. X        has an option to print only one function per page.)
  30. X
  31. XWHAT IT DOESN'T DO:
  32. X   -    Indent.
  33. X   -    Put brackets on separate lines.
  34. X   -    Other stuff that "C beautifiers" do.
  35. X
  36. XWhen I inherited a couple of pretty big C programs to maintain, the
  37. Xfirst thing I did was to try to get a bunch of listings with CPR.  I
  38. Xfound that CPR as I received it wasn't up to the job.  It certainly
  39. Xwas after I added a few enhancements.  Here's the enhanced version,
  40. Xcast back upon the waters of the net.  The enhancements are:
  41. X
  42. X   -    Any arguments can appear in a file rather than on the command
  43. X        line, and in pretty free form.  (As received, you could only have
  44. X        filename arguments in a file, one name per line.)
  45. X   -    The table of contents can contain a single sorted index of
  46. X        all functions across all files.
  47. X   -    The option "-?" will give a help screen for the options
  48. X        (more like the PC idiom than the usual UNIX utility).
  49. X   -    Lines can be "intelligently" folded if they go beyond the
  50. X        width of a page.  (You wouldn't believe this program I
  51. X        inherited, and its 160-column lines.)
  52. X
  53. XThis remains public domain, which is as I found it.  (It's free, and
  54. Xworth every penny  :-)
  55. X
  56. X                            Dave Tutelman
  57. XPHYSICAL:       16 Tilton Drive         AT&T Bell Labs
  58. X                Wayside, NJ 07712       Lincroft, NJ 07738
  59. XAUDIBLE:        (201) 922-9576          (201) 576-2194
  60. XLOGICAL:                   att!pegasus!dmt
  61. X
  62. XContributors to the code include:
  63. X *    Written by:
  64. X *        Paul Breslin
  65. X *        Human Computing Resources Corp.
  66. X *        10 St. Mary St.
  67. X *        Toronto, Ontario
  68. X *        Canada, M4Y 1P9
  69. X *
  70. X *        -- ...!decvax!utcsrgv!hcr!phb
  71. X *
  72. X *      Sorting and standard input reading from:
  73. X *        Rick Wise, CALCULON Corp., Rockville, MD.
  74. X *        -- ...!decvax!harpo!seismo!rlgvax!cvl!umcp-cs!cal-unix!wise
  75. X *
  76. X *    File modified time,
  77. X *    numbered output,
  78. X *    optional white space,
  79. X *    improved function start tests from:
  80. X *        David Wasley, U.C.Berkeley
  81. X *        -- ...!ucbvax!topaz.dlw
  82. X *
  83. X *    Modified the -r to leave variable amounts of space
  84. X *        Patrick Powell, U. Waterloo
  85. X *
  86. X *      Changed handling of form feeds to start a new page AND print heading:
  87. X *        Terry Doner, U of Waterloo
  88. X *
  89. X *    Fixed up to locate more functions, and added -p option
  90. X *        Dennis Vadura, U of Waterloo
  91. X *
  92. X *        It will find things like  struct foo *f()...
  93. X *        but not things like     int
  94. X *                    f
  95. X *                    ()...
  96. X *        ie. the constraint is that the () must appear on the same line
  97. X *        as the function name.
  98. X *
  99. X *  Clean up a bit for 80286 machines (lints a bit cleaner, too)
  100. X *      Dan Frank, Prairie Computing
  101. X *
  102. X *  Fixed a whole bunch of stuff and added lots of new flags.
  103. X *      -S       sort and be case insensitive.
  104. X *      -N       start numbering pages at 1 for each new file
  105. X *      -T title cat the file title before the table of contents.
  106. X *      -C       print only the table of contents
  107. X *      -c       only try to look for function names in files whose suffix ends
  108. X *             in .c
  109. X *      -f file  to handle file containing list of files to print. (for MSDOS)
  110. X *      Dennis Vadura
  111. X *
  112. X *  Set to work better in MS-DOS (and Turbo C).
  113. X *    Ported to Turbo C on MS-DOS.
  114. X *    Enabled wildcard expansion for systems that don't support it.
  115. X *    Broke BIG source file into several C files plus header.
  116. X *    Fixed argument parsing to be more forgiving.
  117. X *    Changed -f option to handle all args in a free-form file,
  118. X *        not just filenames.
  119. X *    Added options:
  120. X *      -?    Help screen.
  121. X *    -x,-X    Sort TOC across files, giving function index (xref-like?).
  122. X *    -w (n)    Fold lines to a page width (fairly good-looking line folding).
  123. X *        Dave Tutelman  - att!pegasus!dmt -  10/89
  124. X
  125. X
  126. !EOR!
  127. echo extracting - cpr.c
  128. sed 's/^X//' > cpr.c << '!EOR!'
  129. X/*    CPR   -   Pretty-Printer for C programs
  130. X *
  131. X *    This program prints the files named in its argument list, preceding
  132. X *    the output with a table of contents. Each file is assumed to be C
  133. X *    source code (but doesn't have to be) in that the program searches
  134. X *    for the beginning and end of functions. Function names are added to
  135. X *    the table of contents, provided the name starts at the beginning of
  136. X *    a line. The function name in the output is double striken.
  137. X *
  138. X *    PAGE SIZE
  139. X *    The option "-l" indicates that the following argument is to be
  140. X *    the page length used for output (changing the page length hasn't been
  141. X *    tested much).
  142. X *    The option "-w" indicates that the following argument is to be
  143. X *    the page width (and lines are folded to that width, using a
  144. X *    reasonably aesthetic folding algorithm).  The default, in the
  145. X *    absence of "-w" is not to fold lines.
  146. X *
  147. X *    NUMBERING OF LINES & PAGES
  148. X *    The option "-n" indicates that output lines should be numbered with
  149. X *    the corresponding line number from the input file.
  150. X *    The option "-N" indicates that page numbers start at 1 within each
  151. X *    file.
  152. X *
  153. X *    FORMATTING THE PROGRAM
  154. X *    The option "-p" indicates what proportion of the page in steps of 16
  155. X *    should be used for deciding if a new function needs a new page.
  156. X *    That is -p12 (the default) indicates that if a function starts
  157. X *    within the top 12/16 (3/4) of the page then do it, otherwise put it
  158. X *    on a new page.  Thus the higher the number (upto 16) the closer to
  159. X *    the bottom of the page will functions be started. -p0 says put each
  160. X *    func on a new page.
  161. X *
  162. X *    By default blank space is inserted after every closing '}'
  163. X *    character. Thus functions and structure declarations are nicely
  164. X *    isolated in the output. The only drawback to this is that structure
  165. X *    initialization tables sometimes produce lots of white space.
  166. X *    The "-r" option removes this space, or changes it to the indicated
  167. X *    length.
  168. X *
  169. X *    TABLE OF CONTENTS OPTIONS
  170. X *    The option "-s" indicates that the table of contents should be sorted
  171. X *    by function name within each file.
  172. X *    The option "-S" also calls for a sorted table of contents, but with
  173. X *    a case-insensitive sort.
  174. X *    The option "-x" indicates that the table of contents should include
  175. X *    a sorted index of functions across all files.
  176. X *    The option "-X" also calls for a sorted index of functions, but with
  177. X *    a case-insensitive sort.
  178. X *
  179. X *    The option "-C" indicates that ONLY the table of contents is to
  180. X *    be printed, not the source files themselves.
  181. X *
  182. X *    The option "-c" indicates that only files with the extension ".c"
  183. X *    should be examined for files to list in the table of contents.
  184. X *
  185. X *    The option "-T" indicates that the following argument is a file
  186. X *    name; the contents of that file is to be printed verbatim in the
  187. X *    output before the table of contents, as a Title Paragraph.
  188. X *
  189. X *    MISCELLANEOUS OPTIONS
  190. X *    The option "-?" indicates that a help screen for CPR be sent
  191. X *    to stderr (generally the screen), but no processing done.
  192. X *    The help screen is more verbose than the usage message that's
  193. X *    printed on error, and summarizes all options of CPR.
  194. X *
  195. X *    The option "-f" indicates that the following argument is a file
  196. X *    name; the contents of that file are to be treated just as command
  197. X *    line arguments.  (Thus you can, for example,  make a ".cprrc" file
  198. X *    with the right options and source files, in each of your
  199. X *    source directories.)
  200. X *
  201. X *  HISTORY
  202. X *
  203. X *    Written by:
  204. X *        Paul Breslin
  205. X *        Human Computing Resources Corp.
  206. X *        10 St. Mary St.
  207. X *        Toronto, Ontario
  208. X *        Canada, M4Y 1P9
  209. X *
  210. X *        -- ...!decvax!utcsrgv!hcr!phb
  211. X *
  212. X *      Sorting and standard input reading from:
  213. X *        Rick Wise, CALCULON Corp., Rockville, MD.
  214. X *        -- ...!decvax!harpo!seismo!rlgvax!cvl!umcp-cs!cal-unix!wise
  215. X *
  216. X *    File modified time,
  217. X *    numbered output,
  218. X *    optional white space,
  219. X *    improved function start tests from:
  220. X *        David Wasley, U.C.Berkeley
  221. X *        -- ...!ucbvax!topaz.dlw
  222. X *    Modified the -r to leave variable amounts of space
  223. X *        Patrick Powell, U. Waterloo
  224. X *
  225. X *      Changed handling of form feeds to start a new page AND print heading:
  226. X *        Terry Doner, U of Waterloo
  227. X *
  228. X *    Fixed up to locate more functions, and added -p option
  229. X *        Dennis Vadura, U of Waterloo
  230. X *
  231. X *        It will find things like  struct foo *f()...
  232. X *        but not things like     int
  233. X *                    f
  234. X *                    ()...
  235. X *        ie. the constraint is that the () must appear on the same line
  236. X *        as the function name.
  237. X *
  238. X *  Clean up a bit for 80286 machines (lints a bit cleaner, too)
  239. X *      Dan Frank, Prairie Computing
  240. X *
  241. X *  Fixed a whole bunch of stuff and added lots of new flags.
  242. X *      -S       sort and be case insensitive.
  243. X *      -N       start numbering pages at 1 for each new file
  244. X *      -T title cat the file title before the table of contents.
  245. X *      -C       print only the table of contents
  246. X *      -c       only try to look for function names in files whose suffix ends
  247. X *             in .c
  248. X *      -f file  to handle file containing list of files to print. (for MSDOS)
  249. X *      Dennis Vadura
  250. X *
  251. X *  Set to work better in MS-DOS (and Turbo C).
  252. X *    Ported to Turbo C on MS-DOS.
  253. X *    Enabled wildcard expansion for systems that don't support it.
  254. X *    Broke BIG source file into several C files plus header.
  255. X *    Fixed argument parsing to be more forgiving.
  256. X *    Changed -f option to handle all args in a free-form file,
  257. X *        not just filenames.
  258. X *    Added options:
  259. X *      -?    Help screen.
  260. X *    -x,-X    Sort TOC across files, giving function index (xref-like?).
  261. X *    -w (n)    Fold lines to a page width (fairly good-looking line folding).
  262. X *        Dave Tutelman  - att!pegasus!dmt -  10/89
  263. X */
  264. X
  265. X#include <sys/types.h>
  266. X#include <ctype.h>
  267. X#include <stdio.h>
  268. X#include <signal.h>
  269. X#include <string.h>
  270. X
  271. Xchar *synopsis [] = {
  272. X"OPTIONS:",
  273. X"-c    Look for functions only if file is a '.c' file.",
  274. X"-C    Produce ONLY table of contents, no listing.",
  275. X"-n    Number lines in the listing.",
  276. X"-N    Start each file listing on page 1.",
  277. X"-s    Sort table of contents by function name (in each file).",
  278. X"-S    Like -s, but case-insensitive.",
  279. X"-x,-X    Like -s, -S, but sorts across files (Xref-like function index).",
  280. X"-l n    Page length.  (Default = 66)",
  281. X"-w n    Page width, at which to fold lines.  (Default = don't fold lines)",
  282. X"-p n    How many 16ths of a page can be used, and still start a new function",
  283. X"    on this page?  (-p0 puts a new function on each page.  Default = 12)",
  284. X"-r n    How many blank lines to leave after a '}'.  (Default = 5)",
  285. X"-t n    Width of a tab stop.  (Default = 8)",
  286. X"-T title    Print contents of file 'title' before table of contents.",
  287. X"-f argfile    Take next few arguments from file 'argfile'.",
  288. X"-    File name for the Standard Input.",
  289. X'\0'
  290. X};
  291. X
  292. X#define MAIN
  293. X#include "cpr.h"
  294. X
  295. X/* Some macros for parsing parameters from the argument list */
  296. X#define NEXPARM        NexParm (&i,&parm, argc,argv)
  297. X#define    NUMPARM        NumParm (&i,&parm, argc,argv)
  298. X#define    STRPARM        StrParm (&i,&parm, argc,argv)
  299. X
  300. X#ifdef NOWILD
  301. Xint NextName=0;        /* Use next_file function to expand wildcard */
  302. Xchar *first_file(), *next_file();
  303. X#endif
  304. X
  305. Xchar *GetProgName();
  306. Xchar *StrParm();
  307. X
  308. Xmain(argc, argv)
  309. Xchar **argv;
  310. X{
  311. X   int i;
  312. X   char *ctime();
  313. X   time_t thetime, time();
  314. X   char *parm;        /* points to next thing to do in an arg */
  315. X   int  num;
  316. X   int    FirstFile=1;
  317. X
  318. X   ProgName = GetProgName (argv[0]);
  319. X   thetime = time((time_t *)0);
  320. X   strcpy(Today,ctime(&thetime));
  321. X
  322. X   if (argc < 2)  Synop();
  323. X
  324. X   i = 0;
  325. X   while ((i = NEXPARM) >0)    /* get next argument if there is one */
  326. X   {
  327. X      if( arg[0] == '?'
  328. X     && arg[1] == '\0' ) Synop();
  329. X
  330. X      if( arg[0] != '-'    || arg[1] == '\0' ) {        /* file name */
  331. X        parm--;        /* back off to get whole name */
  332. X        Name = STRPARM;
  333. X        if (NULLSTR( Name ))  Usage();
  334. X        if (FirstFile) {
  335. X            FirstFile=0;
  336. X            StartTempFile();
  337. X        }
  338. X        DoFile (Name);
  339. X      }
  340. X
  341. X      else {            /* this arg is options */
  342. X        switch( *parm ) {
  343. X          case '-':
  344. X         break;        /* skip. just leading - for options */
  345. X
  346. X          case '\0':    /* check for end of this argument */
  347. X          case ' ':        /* just for safety.          */
  348. X          case '\t':
  349. X          case '\n':
  350. X          case '\r':
  351. X         break;
  352. X
  353. X          case '?': Synop();
  354. X
  355. X          case 'f':        /* get next args from a file */
  356. X         listn = STRPARM;    /* get next string parameter */
  357. X         if (NULLSTR( listn ))  Usage();
  358. X         if ((listf = fopen (listn, "r")) == NULL) {
  359. X            fprintf(stderr,"%s: list file '%s' not found\n",
  360. X                ProgName, listn);
  361. X            exit (1);
  362. X         }
  363. X         InArgFile = 1;
  364. X         break;
  365. X
  366. X          case 't':
  367. X         TabWidth = NUMPARM;
  368. X         if( TabWidth == -1) TabWidth = 0;
  369. X         if( TabWidth < 0 )  TabWidth = 0;
  370. X         break;
  371. X
  372. X          case 'T':
  373. X         TitleFile = STRPARM;
  374. X         if (NULLSTR( TitleFile ))   Usage();
  375. X         if ((Tfile = fopen (TitleFile, "r")) == NULL) {
  376. X            fprintf(stderr,"%s: title file '%s' not found\n",
  377. X                ProgName, TitleFile);
  378. X            exit (1);
  379. X         }
  380. X         ++Title;
  381. X         break;
  382. X
  383. X          case 'l':
  384. X         PageLength = NUMPARM;
  385. X         if( PageLength == -1)  Usage();
  386. X         if( PageLength < 10) PageLength = 10;
  387. X         PageEnd = PageLength - ((PageLength > 30) ? 7 : 1);
  388. X         break;
  389. X
  390. X          case 'w':
  391. X         PageWidth = NUMPARM;
  392. X         if( PageWidth == -1 ) Usage();
  393. X         if( PageWidth < 0 ) PageWidth = 0;
  394. X         break;
  395. X
  396. X          case 'S':
  397. X         ++CaseInsensitive;
  398. X          case 's':
  399. X         ++SortFlag;
  400. X         break;
  401. X
  402. X          case 'X':
  403. X         ++CaseInsensitive;
  404. X          case 'x':
  405. X         ++SortFlag;
  406. X         ++XrefFlag;
  407. X         if (ResetPage) {
  408. X            fprintf(stderr,"%s: options -X and -N are incompatible\n",
  409. X            ProgName);
  410. X            exit (1);
  411. X         }
  412. X         break;
  413. X
  414. X          case 'C':
  415. X         ++ContentsOnly;
  416. X         break;
  417. X
  418. X          case 'c':
  419. X         ++OnlyCFiles;
  420. X         break;
  421. X
  422. X          case 'n':
  423. X         ++NumberFlag;
  424. X         FirstCol = FORMATWIDTH;    /* leave room for line numbering */
  425. X         break;
  426. X
  427. X          case 'N':
  428. X         ++ResetPage;
  429. X         if (XrefFlag) {
  430. X            fprintf(stderr,"%s: options -X and -N are incompatible\n",
  431. X            ProgName);
  432. X            exit (1);
  433. X         }
  434. X         break;
  435. X
  436. X          case 'r':
  437. X         Space_to_leave = NUMPARM;
  438. X         if (Space_to_leave == -1)  Space_to_leave = 0;
  439. X         if (Space_to_leave < 0)    Space_to_leave = 0;
  440. X         break;
  441. X
  442. X          case 'p':
  443. X         PagePart = NUMPARM;
  444. X         if ( PagePart == -1)  Usage();
  445. X         if ( PagePart < 0)  PagePart = 0;
  446. X         if ( PagePart > 16) PagePart = 16;
  447. X         break;
  448. X
  449. X          default:
  450. X         Usage();
  451. X         break;
  452. X        }  /* end 'switch' */
  453. X      }    /* end 'else' */
  454. X   }  /* end 'while' */
  455. X
  456. X   /* All files processed.  Print the listings. */
  457. X   if( PageNumber > 1 || LineNumber > 0 )
  458. X      putchar(BP);
  459. X   EndTempFile();
  460. X
  461. X   DumpTableOfContents();
  462. X   DumpTempFiles();
  463. X   Done();
  464. X}
  465. X
  466. XUsage()
  467. X{
  468. X   fprintf(stderr, "%s: Problem with argument %s", ProgName, arg);
  469. X   if (InArgFile) fprintf(stderr, " in argument file\n");
  470. X      else      fprintf(stderr, "\n");    /* yeah, I know ... */
  471. X   fprintf(stderr, "Usage: %s [-?cCnNsSxX] [-t tabwidth] [-l pagelength] ",ProgName);
  472. X   fprintf(stderr, "[-w pagewidth]\n\t[-p[num]] [-r[num]] [-T title] [[-f] file] ...");
  473. X   exit(1);
  474. X}
  475. X
  476. XSynop()
  477. X{
  478. X   int i;
  479. X
  480. X    fprintf(stderr,"Synopsis:\n%s   [options]  [filenames]\n\n", ProgName);
  481. X    for (i=0; *synopsis[i] != '\0'; i++)
  482. X        fprintf (stderr,"%s\n",synopsis[i]);
  483. X   exit(1);
  484. X}
  485. X
  486. Xchar *
  487. XGetProgName (s)        /* We need to strip off path information, if there */
  488. Xchar *s;
  489. X{
  490. X   char *p;
  491. X
  492. X    for (p=s; *p!='\0'; p++);    /* get end of string */
  493. X    while (--p >= s) {
  494. X#ifdef MSDOS
  495. X        if (*p == '.')
  496. X            *p='\0';    /* strip off .EXE extension */
  497. X#endif
  498. X        if ((*p=='/') || (*p=='\\'))
  499. X            break;
  500. X    }
  501. X    return (++p);
  502. X}
  503. X
  504. X
  505. X/*  Do the processing necessary to format one file  */
  506. XDoFile (fname)
  507. X  char *fname;
  508. X{
  509. X    if (*fname=='-' && *(fname+1)=='\0') {
  510. X         File = stdin;
  511. X         Name = "Standard Input";
  512. X     List();
  513. X     return;
  514. X    }
  515. X#ifndef NOWILD
  516. X     if( (File = fopen( fname, "r" )) == NULL )
  517. X         {
  518. X            fprintf(stderr,"%s: Can't open file '%s': %s\n",
  519. X            ProgName, Name, sys_errlist[errno] );
  520. X         }
  521. X         List();
  522. X     fclose(File);
  523. X
  524. X#else        /* expand wildcards explicitly */
  525. X    Name = first_file (fname);
  526. X    while ((Name != NULL) && (*Name != '\0')) {
  527. X        if( (File = fopen( Name, "r" )) == NULL )
  528. X        {
  529. X            fprintf(stderr,"%s: Can't open file '%s': %s\n",
  530. X            ProgName, Name, sys_errlist[errno] );
  531. X            continue;
  532. X        }
  533. X        List();
  534. X        fclose(File);
  535. X
  536. X        Name = next_file ();
  537. X    }
  538. X#endif
  539. X}
  540. X
  541. X
  542. Xint SaveOut;
  543. Xchar *TempName;
  544. Xchar *Temp2Name;
  545. X
  546. XStartTempFile()
  547. X{
  548. X   int Done();
  549. X   extern char *mktemp();
  550. X
  551. X   CatchSignalsPlease(Done);
  552. X
  553. X   SaveOut = dup(1);
  554. X#ifdef MSDOS
  555. X   TempName = "cpr0001.tmp";
  556. X#else
  557. X   TempName = mktemp("/tmp/cprXXXXXX");
  558. X#endif
  559. X   if( freopen(TempName, "w", stdout) == NULL )
  560. X   {
  561. X      fprintf(stderr, "%s: Can't open temp file '%s': %s\n", ProgName,
  562. X      TempName, sys_errlist[errno]);
  563. X      exit(1);
  564. X   }
  565. X}
  566. X
  567. XEndTempFile()
  568. X{
  569. X#ifdef MSDOS
  570. X   Temp2Name = "cpr0002.tmp";
  571. X#else
  572. X   Temp2Name = mktemp("/tmp/cprXXXXXX");
  573. X#endif
  574. X   if( freopen(Temp2Name, "w", stdout) == NULL )
  575. X   {
  576. X      fprintf(stderr, "%s: Can't open temp file '%s': %s\n", ProgName,
  577. X      Temp2Name, sys_errlist[errno]);
  578. X      exit(1);
  579. X   }
  580. X}
  581. X
  582. XDumpTempFiles()
  583. X{
  584. X   FILE *f;
  585. X   char b[256];
  586. X   register int pid, w;
  587. X
  588. X   fclose(stdout);
  589. X
  590. X#ifndef MSDOS
  591. X   dup(SaveOut);
  592. X   while( (pid = fork()) < 0 ) sleep(1);
  593. X   if( pid )
  594. X      while ((w = wait((int *)0)) != pid && w != -1);
  595. X   else
  596. X      {
  597. X      CatchSignalsPlease(SIG_DFL);
  598. X
  599. X      if( ContentsOnly )
  600. X         execl( "/bin/cat", "cat", Temp2Name, (char *)0 );
  601. X      else
  602. X         execl( "/bin/cat", "cat", Temp2Name, TempName, (char *)0 );
  603. X      fprintf(stderr, "%s: exec of /bin/cat failed: %s\n", ProgName,
  604. X      sys_errlist[errno]);
  605. X      exit(0);
  606. X   }
  607. X#else
  608. X   CatchSignalsPlease(SIG_DFL);
  609. X   if( (f=fopen(Temp2Name,"r")) == NULL )
  610. X      fprintf(stderr,"%s: Can't open file '%s': %s\n",
  611. X      ProgName, TitleFile, sys_errlist[errno] );
  612. X   else
  613. X   {
  614. X      while( fgets(b, 256, f) != NULL )
  615. X         write(SaveOut,b,strlen(b));
  616. X
  617. X      fclose(f);
  618. X   }
  619. X
  620. X   if( !ContentsOnly )
  621. X      if( (f=fopen(TempName,"r")) == NULL )
  622. X         fprintf(stderr,"%s: Can't open file '%s': %s\n",
  623. X         ProgName, TitleFile, sys_errlist[errno] );
  624. X      else
  625. X      {
  626. X         while( fgets(b, 256, f) != NULL )
  627. X            write(SaveOut,b,strlen(b));
  628. X
  629. X         fclose(f);
  630. X      }
  631. X#endif
  632. X}
  633. X
  634. XDone()
  635. X{
  636. X   CatchSignalsPlease(SIG_DFL);
  637. X
  638. X   fclose( stdout );
  639. X   if( TempName ) unlink( TempName );
  640. X   if( Temp2Name ) unlink( Temp2Name );
  641. X
  642. X   exit(0);
  643. X}
  644. X
  645. XCatchSignalsPlease(action)
  646. Xvoid (*action)();
  647. X{
  648. X#ifndef TURBOC
  649. X   if( signal(SIGINT, SIG_IGN) != SIG_IGN ) signal(SIGINT, action);
  650. X#else    /* TURBOC */
  651. X   signal (SIGINT, action);
  652. X#endif
  653. X#ifndef MSDOS
  654. X   if( signal(SIGQUIT, SIG_IGN) != SIG_IGN ) signal(SIGQUIT, action);
  655. X   if( signal(SIGHUP, SIG_IGN) != SIG_IGN ) signal(SIGHUP, action);
  656. X#endif
  657. X}
  658. X
  659. !EOR!
  660. echo extracting - parsargs.c
  661. sed 's/^X//' > parsargs.c << '!EOR!'
  662. X/*        PARSARGS
  663. X *
  664. X *    Functions to get parameters from:
  665. X *       -    The command line.
  666. X *       -    The argument file (if -f option is used).
  667. X */
  668. X
  669. X#include <stdio.h>
  670. X#include <ctype.h>
  671. X#include "cpr.h"
  672. X
  673. X
  674. X/* Get the next parameter, and point to it with *pp.
  675. X * Return current index into argv[], or -1 if no more parameters.
  676. X * Put NEW argument into arg[], but just bump *pp if there's still
  677. X *    more to do in arg[].
  678. X * Get NEW argument from argv[] or list file, depending on InArgFile.
  679. X */
  680. X
  681. Xint
  682. XNexParm (ip,pp, argc,argv)
  683. X  int    *ip;        /* pointer to arg[] index */
  684. X  char    **pp;        /* pointer to parm, which is (char *) */
  685. X  int    argc;
  686. X  char    *argv[];
  687. X{
  688. X    int    i = *ip;
  689. X    char    *p = *pp;
  690. X    int    c;
  691. X    int    InArg = 0;    /* state variable */
  692. X    int    GotArg = 0;
  693. X
  694. X    if (i==0) {    /* need to initialize */
  695. X        strcpy (arg, argv[1]);
  696. X        *pp = arg;
  697. X        *ip = 1;
  698. X        return (1);
  699. X    }
  700. X
  701. X    p++;        /* bump the parameter pointer */
  702. X
  703. X    if (WHITESPACE( *p )) {        /* need a new arg */
  704. X        arg[0] = '\0';    /* blank the beginning of the arg[] array */
  705. X        if (InArgFile) {    /* get the new arg from listf */
  706. X            p = arg;
  707. X            while (!GotArg) {
  708. X            c=getc (listf);
  709. X            if (!InArg) {
  710. X                if (c == EOF) {    /* no more in listf */
  711. X                InArgFile = 0;
  712. X                GotArg++;
  713. X                }
  714. X                else if (!WHITESPACE( (char)c ))  InArg++;
  715. X            }
  716. X            if (InArg) {
  717. X                if (WHITESPACE( (char)c ) || c==EOF) {
  718. X                *p = '\0';
  719. X                GotArg++;
  720. X                }
  721. X                else if (!WHITESPACE( (char)c ))  *p++ = c;
  722. X            }
  723. X            }  /* end while (!GotArg) */
  724. X        }  /* end if (InArgFile)  */
  725. X
  726. X        if (arg[0]=='\0') {    /* get the new arg from command line */
  727. X            if (++i >= argc) return (-1);
  728. X            strcpy (arg, argv[i]);
  729. X            *ip = i;
  730. X            GotArg++;
  731. X        }
  732. X        *pp = arg;
  733. X    }  /* end of "need new arg" */
  734. X
  735. X    else if (!GotArg)    /* not a new arg, just bump pointer */
  736. X        *pp = p;
  737. X
  738. X    return ( i );
  739. X}
  740. X
  741. Xint
  742. XNumParm (ip, pp, argc,argv)    /* Returns next parameter as an integer if it
  743. X                 * is a number.  Otherwise returns -1.    */
  744. X  int  *ip;    /* pointer to argument counter in main program */
  745. X  char **pp;    /* pointer to "parm" pointer in main program */
  746. X  int  argc;
  747. X  char *argv[];
  748. X{
  749. X    int    num=0;        /* number accumulator */
  750. X    int    i;
  751. X    char    *p;
  752. X
  753. X    /* Bump to next parm, if there is one */
  754. X    i = NexParm( ip, pp, argc, argv );
  755. X    if (i <= 0)  return (-1);
  756. X
  757. X    p = *pp;    /* use updated parm pointer */
  758. X
  759. X    /* is there at least one digit? */
  760. X    if (!isdigit( *p )) {
  761. X        *pp = p-1;    /* back off to previous position */
  762. X        return (-1);
  763. X    }
  764. X
  765. X        /* accumulate a decimal number */
  766. X    while (isdigit( *p ))
  767. X        num = 10*num + (int)(*p++ - '0');
  768. X
  769. X    /* fix up pointers before returning */
  770. X    *pp = p-1;    /* back off to end of number */
  771. X    return (num);
  772. X}
  773. X
  774. X
  775. Xchar *
  776. XStrParm (ip, pp, argc,argv)    /* Returns next string parameter if there
  777. X                 * is one.  Otherwise returns -1.    */
  778. X  int  *ip;    /* pointer to argument counter in main program */
  779. X  char **pp;    /* pointer to "parm" pointer in main program */
  780. X  int  argc;
  781. X  char *argv[];
  782. X{
  783. X    int    i;
  784. X    char    *p, *s;
  785. X
  786. X    /* Bump to next parm, if there is one */
  787. X    i = NexParm( ip, pp, argc, argv );
  788. X    if (i <= 0)  return (NULL);
  789. X
  790. X    s = p = *pp;    /* use updated parm pointer */
  791. X    if (WHITESPACE( *p ))  return (NULL);
  792. X
  793. X    /* Find end of string and null it out */
  794. X    while (! WHITESPACE( *p ))  p++;
  795. X    *p = '\0';    /* now null-terminate the string */
  796. X
  797. X    /* fix up pointers before returning */
  798. X    *pp = p-1;    /* back off to end of string */
  799. X    return (s);
  800. X}
  801. X
  802. !EOR!
  803. echo extracting - list.c
  804. sed 's/^X//' > list.c << '!EOR!'
  805. X/*            LIST.C
  806. X *
  807. X *    Functions that go through the .C files,
  808. X *    parse them, and make entries in Table of Contents.
  809. X */
  810. X
  811. X#include <stdio.h>
  812. X#include <ctype.h>
  813. X#include <string.h>
  814. X#include "cpr.h"
  815. X
  816. Xint SawFunction;
  817. Xint Braces;
  818. X
  819. X
  820. XList()
  821. X{
  822. X   register int bp;
  823. X   register char *bufp;
  824. X   char buffer[256];
  825. X
  826. X   NewFile();
  827. X   bp = Braces = 0;
  828. X   InString = InComment = 0; /* reset for new file -DV */
  829. X   SawFunction = 0;
  830. X   bufp = buffer;
  831. X   while( fgets(bufp, sizeof(buffer), File) != NULL )
  832. X   {
  833. X      ++FileLineNumber;
  834. X      if( bp ) NewFunction();
  835. X
  836. X      if( ++LineNumber >= PageEnd ) NewPage();
  837. X
  838. X      if( bufp[0] == '\f'
  839. X         && bufp[1] == '\n'
  840. X         && bufp[2] == '\0' )
  841. X      {
  842. X         NewPage(); /* was strcpy(bufp, "^L\n");*/
  843. X         continue;
  844. X      }
  845. X
  846. X      if( NumberFlag )
  847. X      {
  848. X         if( *bufp == '\n' )
  849. X        printf(BLANKFORMAT);
  850. X         else
  851. X        printf(NUMFORMAT, FileLineNumber);
  852. X      }
  853. X      if( (Braces == 0) && LooksLikeFunction(bufp) )
  854. X         AddToTableOfContents(NEWFUNCTION);
  855. X
  856. X      bp = ScanLine(buffer);
  857. X   }
  858. X}
  859. X
  860. XScanLine(l)
  861. Xregister char *l;
  862. X{
  863. X   extern char *EndComment();
  864. X   extern char *EndString();
  865. X   register char c;
  866. X   int bp;
  867. X   char *save;
  868. X
  869. X   /* initialize line-checking variables */
  870. X   bp = 0;
  871. X
  872. X   /* Now scan the line */
  873. X   for( save = l; c = *l; ++l )
  874. X      if( InComment )
  875. X         l = EndComment(l);
  876. X      else if( InString )
  877. X         l = EndString(l);
  878. X      else
  879. X         switch(c)
  880. X         {
  881. X         case '{':
  882. X            ++Braces;
  883. X            break;
  884. X
  885. X         case '}':
  886. X            if( --Braces == 0 )
  887. X               bp = 1;
  888. X            break;
  889. X
  890. X         case '\'':
  891. X        for( ++l; *l && *l != '\''; ++l )
  892. X           if( *l == '\\' && *(l+1) )  ++l;
  893. X            break;
  894. X
  895. X         case '"':
  896. X            InString = 1;
  897. X            break;
  898. X
  899. X         case '/':
  900. X            if( *(l+1) == '*' )
  901. X            {
  902. X               InComment = 1;
  903. X        ++l;
  904. X            }
  905. X            break;
  906. X     }
  907. X   if (PageWidth)  FoldLine( save );
  908. X   OutputLine( save );
  909. X   return(bp);
  910. X}
  911. X
  912. Xchar *
  913. XEndComment(p)
  914. Xregister char *p;
  915. X{
  916. X   register char c;
  917. X
  918. X   /*
  919. X            * Always return pointer to last non-null char looked at.
  920. X            */
  921. X   while( c = *p++ ) {
  922. X      if( c == '*' && *p == '/' )
  923. X      {
  924. X         InComment = 0;
  925. X         return(p);
  926. X      }
  927. X   }
  928. X   return(p-2);
  929. X}
  930. X
  931. Xchar *
  932. XEndString(p)
  933. Xregister char *p;
  934. X{
  935. X   register char c;
  936. X
  937. X   /*
  938. X            * Always return pointer to last non-null char looked at.
  939. X            */
  940. X   while( c = *p++ ) {
  941. X      if( c == '\\' && *p )
  942. X      {
  943. X         ++p;
  944. X         continue;
  945. X      }
  946. X      else if( c == '"' )
  947. X      {
  948. X         InString = 0;
  949. X         return(p-1);
  950. X      }
  951. X   }
  952. X   return(p-2);
  953. X}
  954. X
  955. XNewFunction()
  956. X{
  957. X   register int i;
  958. X
  959. X   if( Space_to_leave <= 0 || !SawFunction ) return;
  960. X   if( LineNumber + Space_to_leave > (PageLength * PagePart /16) )
  961. X      NewPage();
  962. X   else
  963. X      {
  964. X      for( i=0; i < (Space_to_leave); ++i ) putchar('\n');
  965. X      LineNumber += Space_to_leave;
  966. X   }
  967. X
  968. X   SawFunction = 0;
  969. X}
  970. X
  971. X#define isidchr(c)    (isalnum(c) || (c == '_'))
  972. X
  973. X/* This used to incorrectly identify a declaration such as
  974. X *     int (*name[])() = { initializers ... }
  975. X * as a function.  It also picked up this in an assembler file:
  976. X *     #define MACRO(x) stuff
  977. X *     MACRO(x):
  978. X * Fixed both of these.   -IAN!
  979. X */
  980. XLooksLikeFunction(s)
  981. Xregister char *s;
  982. X{
  983. X   register char *p;
  984. X   register int i;
  985. X   char *save;
  986. X   int t = TabWidth;
  987. X
  988. X   if( InComment || InString ) return(0);
  989. X   if( OnlyCFiles )
  990. X   {
  991. X      char *e = Name+strlen(Name)-1;
  992. X      if(( *e != 'c'
  993. X#ifdef MSDOS
  994. X        && *e != 'C'
  995. X#endif
  996. X          ) || e[-1] != '.' ) return(0);
  997. X   }
  998. X
  999. X   if( !t ) t = 8;
  1000. X   save = s;
  1001. X
  1002. X   i = 0;
  1003. X   do
  1004. X      {
  1005. X      p = FunctionName;
  1006. X
  1007. X      while( *s && (*s == ' ') || (*s == '\t') ) ++s;
  1008. X      if( *s == '*' ) ++s;
  1009. X      if( *s && (*s == ' ') || (*s == '\t') ) continue;
  1010. X      if( !*s || ((*s != '_') && !isalpha(*s)) ) return(0);
  1011. X
  1012. X      while( isidchr(*s) )
  1013. X         *p++ = *s++;
  1014. X      *p = '\0';
  1015. X
  1016. X      while( *s && (*s == ' ') || (*s == '\t') ) ++s;
  1017. X      i++;
  1018. X   }
  1019. X   while ( *s && *s != '(' && i < 4 );
  1020. X
  1021. X   if( *s != '(' || *(s+1) == '*' ) return(0);
  1022. X
  1023. X   for (i = 0; *s; s++)
  1024. X   {
  1025. X      switch( *s )
  1026. X      {
  1027. X      case '(':
  1028. X         ++i;
  1029. X         continue;
  1030. X
  1031. X      case ')':
  1032. X         --i;
  1033. X         break;
  1034. X
  1035. X      default:
  1036. X         break;
  1037. X      }
  1038. X      if( i == 0 ) break;
  1039. X   }
  1040. X   if( !*s ) return(0);
  1041. X
  1042. X   while( *s )
  1043. X   {
  1044. X      if( *s == '{') break;
  1045. X      if( *s == ';' || *s == ':' ) return(0);
  1046. X      ++s;
  1047. X   }
  1048. X
  1049. X   /*
  1050. X            * This will cause the function name part of the line to
  1051. X            * be double striken.  Note that this assumes the name and the opening
  1052. X            * parentheses are on the same line...
  1053. X            */
  1054. X
  1055. X   if( p = strchr( save, '(' ) )
  1056. X   {
  1057. X      p--;
  1058. X      while( p != save && isidchr( *(p-1) ) ) p--;
  1059. X      for( i=0; save != p; save++ )
  1060. X         if( *save == '\t' )
  1061. X         {
  1062. X            putchar('\t');
  1063. X            i = ((i+t)/t)*t;
  1064. X         }
  1065. X         else
  1066. X         {
  1067. X            putchar(' ');
  1068. X            i++;
  1069. X         }
  1070. X
  1071. X      for( ; *p != '('; p++ )
  1072. X         if( *p == '\t' )
  1073. X         {
  1074. X            putchar('\t');
  1075. X            i = ((i+t)/t)*t;
  1076. X         }
  1077. X         else
  1078. X         {
  1079. X            putchar(*p);
  1080. X            i++;
  1081. X         }
  1082. X   }
  1083. X   else
  1084. X      for( i=0; *save && (*save == '*' || isidchr(*save)); ++save)
  1085. X         if( *save == '*' )
  1086. X         {
  1087. X            putchar(' ');
  1088. X            i++;
  1089. X         }
  1090. X         else
  1091. X         {
  1092. X            if( *save == '\t' )
  1093. X               i = ((i+t)/t)*t;
  1094. X            else
  1095. X               i++;
  1096. X            putchar(*save);
  1097. X         }
  1098. X
  1099. X   while( i --> 0 ) putchar('\b');
  1100. X
  1101. X   SawFunction = 1;
  1102. X   return(1);
  1103. X}
  1104. !EOR!
  1105. echo extracting - fold.c
  1106. sed 's/^X//' > fold.c << '!EOR!'
  1107. X/*            FOLD
  1108. X *
  1109. X *    Functions to fold an output line.
  1110. X */
  1111. X
  1112. X#include <stdio.h>
  1113. X#include <string.h>
  1114. X#include "cpr.h"
  1115. X
  1116. X/* The criteria for folding a line are, in order of aethetic desirability: */
  1117. X#define    COMMENT        0    /* Comment starts here */
  1118. X#define    BRACKET        1    /* Curly open bracket */
  1119. X#define    SEMICOLON    2    /* First non-blank after semicolon */
  1120. X#define    BLANK        3    /* First non-blank after whitespace */
  1121. X#define    PAREN        4    /* Open parenthesis */
  1122. X#define    N_CRIT        5    /* Number of criteria for folding a line */
  1123. X#define    CLEAR_CRIT    { int i; for(i=0;i<N_CRIT;i++) crit[i]=NULL; }
  1124. X
  1125. Xint NeedFold = 0;    /* Number of folds needed for this line */
  1126. Xint FoldCol = -1;    /* Proposed left edge of folded line */
  1127. Xchar *folds [64];    /* Places to fold the current line */
  1128. X
  1129. Xchar *FoldHeuristic ();
  1130. X
  1131. X
  1132. XFoldLine (s)    /* Put Fold Marks in the right place in the line */
  1133. X  char *s;
  1134. X{
  1135. X    char *p;
  1136. X    int i;
  1137. X    int col = 0;        /* Current column in output line */
  1138. X    char *NonBlank = NULL;    /* First non-blank character */
  1139. X    int CantFold = 0;    /* Set it if we have trouble folding */
  1140. X
  1141. X    /* Set up array of pointers into the line of possible places to fold,
  1142. X     * in order of aesthetic desirability. */
  1143. X    char *crit [N_CRIT];
  1144. X    char *SemiWait = NULL, *BlankWait = NULL;
  1145. X
  1146. X    CLEAR_CRIT;
  1147. X    FoldCol = -1;
  1148. X    NeedFold = 0;    /* assume we won't need to fold line */
  1149. X
  1150. X    if (! PageWidth) return;    /* Don't bother folding lines */
  1151. X
  1152. X    /* Step through line, looking for good places to fold */
  1153. X    for (p=s; *p; p++) {
  1154. X        switch (*p) {
  1155. X          case '\n':    /* ignore */
  1156. X            break;
  1157. X
  1158. X          case ' ':    /* Whitespace is harmless */
  1159. X            col++;
  1160. X            BlankWait = p;    /* remember we've seen a blank */
  1161. X            break;
  1162. X
  1163. X          case '\t':    /* Space out a tab's worth */
  1164. X            while ((++col) % TabWidth);
  1165. X            col++;
  1166. X            BlankWait = p;    /* remember we've seen a blank */
  1167. X            break;
  1168. X
  1169. X          case '/':
  1170. X            if (*(p+1) == '*')    /* beginning of a comment */
  1171. X                crit [COMMENT] = p;
  1172. X            col++;
  1173. X            break;
  1174. X
  1175. X          case '{':
  1176. X            if (*(p-1) != '\'')
  1177. X                crit [BRACKET] = p;
  1178. X            col++;
  1179. X            break;
  1180. X
  1181. X          case ';':    /* Remember we've seen a semicolon */
  1182. X            col++;
  1183. X            if (*(p-1) != '\'')
  1184. X                SemiWait = p;
  1185. X            break;
  1186. X
  1187. X          case '(':
  1188. X            if (*(p-1) != '\'')
  1189. X                crit [PAREN] = p;
  1190. X            col++;
  1191. X            break;
  1192. X
  1193. X          default:
  1194. X            col++;
  1195. X            break;
  1196. X        }
  1197. X
  1198. X        /* If non-blank, do a few housekeeping things */
  1199. X        if (*p!=' ' && *p!='\t' && *p!='\n') {
  1200. X            if (FoldCol == -1) {
  1201. X                NonBlank = p;
  1202. X                FoldCol = col + 1;
  1203. X            }
  1204. X            if (BlankWait) {    /* waiting after blank */
  1205. X                crit [BLANK] = p;
  1206. X                BlankWait = 0;
  1207. X            }
  1208. X            if (SemiWait && SemiWait!=p) {
  1209. X                crit [SEMICOLON] = p;
  1210. X                if (*p!=';')    SemiWait = 0;
  1211. X            }
  1212. X        }
  1213. X
  1214. X        /* Do we need to fold yet ? */
  1215. X        if (col+FirstCol >= PageWidth  &&  *p!=' '  &&  *p!='\t') {
  1216. X            /* Can we fold?  We can tell by checking our pointers
  1217. X             * in order of decreasing aesthetic result of fold.
  1218. X             */
  1219. X            char *FoldHere;
  1220. X
  1221. X            FoldHere = FoldHeuristic (s, NonBlank, crit);
  1222. X            if (FoldHere)
  1223. X                folds [NeedFold++] = FoldHere;
  1224. X            else
  1225. X                CantFold++;
  1226. X
  1227. X            /* Do some cleaning up after folding */
  1228. X            if (FoldHere) {
  1229. X                col = FoldCol;
  1230. X                p = FoldHere;
  1231. X
  1232. X                CLEAR_CRIT;
  1233. X                BlankWait = SemiWait = NULL;
  1234. X            }
  1235. X        }
  1236. X    }
  1237. X    if (CantFold)    /* complain, but print anyway and continue */
  1238. X        fprintf(stderr,"%s: trouble folding line %ld of file %s\n",
  1239. X            ProgName, FileLineNumber, Name);
  1240. X}
  1241. X
  1242. X/*    Output the line, folding where indicated in folds[] */
  1243. X
  1244. XOutputLine (p)
  1245. X  char *p;
  1246. X{
  1247. X    int i = 0;
  1248. X
  1249. X    if (! NeedFold)        /* don't bother folding lines */
  1250. X        printf ("%s",p);
  1251. X
  1252. X    else {            /* gotta fold lines */
  1253. X        for (; *p; p++) {
  1254. X        if (p==folds[i] && i<NeedFold) {    /* fold to a new line */
  1255. X            putchar ('\n');
  1256. X            if (++LineNumber >= PageEnd)  NewPage();
  1257. X            if (NumberFlag)
  1258. X                printf (BLANKFORMAT);
  1259. X            GoToColumn (FirstCol, FoldCol+FirstCol);
  1260. X            i++;
  1261. X        }
  1262. X        putchar (*p);
  1263. X        }
  1264. X    }
  1265. X}
  1266. X
  1267. X/*  Heuristic for determining where to fold  */
  1268. X
  1269. X#define    MAX( x, y )    ((x) > (y)) ? x : y
  1270. X#define    MIN( x, y )    ((x) < (y)) ? x : y
  1271. X#define    MAXC( c, j )    for(i=j=0;i<N_CRIT;i++) if(c[i]>c[j]) j=i
  1272. X
  1273. Xchar *
  1274. XFoldHeuristic (s, NonBlank, crit)
  1275. X  char *s;        /* pointer to line */
  1276. X  char *NonBlank;    /* pointer to first non-blank character */
  1277. X  char *crit[];        /* pointers to likely places to fold, based on
  1278. X             * aesthetic criteria */
  1279. X{
  1280. X    int    i,j;
  1281. X    int    score [N_CRIT];        /* keep some "scores" here */
  1282. X    int    credit;    /* score value increments (in columns) */
  1283. X    char    *lptr;        /* points to start of partial line */
  1284. X    char    *p;
  1285. X
  1286. X    /* For each crit[] pointer, it's worthless as a folding point if it's
  1287. X     * the first non-blank in the line. */
  1288. X    for (i=0; i<N_CRIT; i++)
  1289. X        if (crit[i]==NonBlank)  crit[i] = NULL;
  1290. X
  1291. X    /* Fold on a comment, if we've got one */
  1292. X    if (crit [COMMENT])  return (crit [COMMENT]);
  1293. X
  1294. X    /* For the rest, we'll compute some heuristic "scores".
  1295. X     * Start by seeing how much of the partial line each crit picks up. */
  1296. X    score [COMMENT] = 0;
  1297. X    if (NeedFold)        /* line already folded */
  1298. X        lptr = folds [NeedFold-1];
  1299. X    else
  1300. X        lptr =  NonBlank;
  1301. X    for (i=1; i<N_CRIT; i++)
  1302. X        if (crit [i])
  1303. X            score [i] = crit [i] - lptr;
  1304. X        else    score [i] = 0;
  1305. X
  1306. X    /* Adjust the scores according to some "feel for aesthetics".
  1307. X     * Start by giving each criterion credit for its order in the array;
  1308. X     * "credit" is measured by the number of columns it's worth.
  1309. X     * We'll estimate a unit of "credit" as a fraction of the line
  1310. X     * that we're trying to fill.
  1311. X     */
  1312. X
  1313. X    credit = PageWidth - (lptr - s);    /* width of current partial */
  1314. X    credit = credit / 10;        /* "arbitrary" fraction of line */
  1315. X    for (i=1; i<N_CRIT; i++)
  1316. X        if (score [i])
  1317. X            score [i] += (N_CRIT - i - 1) * credit;
  1318. X
  1319. X    /* Next, see how "valuable" a paren is from its context. */
  1320. X    if (p = crit [PAREN]) {
  1321. X        char *pp;
  1322. X        pp = p-1;
  1323. X        if (*pp==' ' || *pp=='\t')
  1324. X            score[PAREN] += 2 * credit;
  1325. X        else if (*pp=='(') {
  1326. X            /* Find the left edge of string of parens */
  1327. X            while (--pp) {
  1328. X            if (pp < lptr) {
  1329. X                /* Parens all the way left. Quit */
  1330. X                score[PAREN] -= credit;
  1331. X                break;
  1332. X            }
  1333. X            else if (*pp==' ' || *pp=='\t') {
  1334. X                /* Isolated! Point to it & give bonus */
  1335. X                crit [PAREN] = pp+1;
  1336. X                score[PAREN] = (pp - lptr) + 1 + 2*credit;
  1337. X                break;
  1338. X            }
  1339. X            else if (*pp!='(') {
  1340. X                /* Nothing special. Point & give small bonus */
  1341. X                crit [PAREN] = pp+1;
  1342. X                score[PAREN] = (pp - lptr) + credit;
  1343. X                break;
  1344. X            }
  1345. X            /* If we got here, keep searching */
  1346. X            }
  1347. X        }
  1348. X        else    /* Nothing special.  Small bonus */
  1349. X            score [PAREN] += credit - 1;
  1350. X    }
  1351. X
  1352. X    /* Now, just return the highest score. */
  1353. X    MAXC( score, j );
  1354. X    return ( crit[j] );
  1355. X}
  1356. X
  1357. !EOR!
  1358. echo extracting - report.c
  1359. sed 's/^X//' > report.c << '!EOR!'
  1360. X/*            REPORT.C
  1361. X *
  1362. X *    Prints out the table of contents and listed .C files.
  1363. X */
  1364. X
  1365. X#include <sys/types.h>
  1366. X#include <sys/stat.h>
  1367. X#include <stdio.h>
  1368. X#include <ctype.h>
  1369. X#include <string.h>
  1370. X
  1371. X#include "cpr.h"
  1372. X
  1373. X#define HEADER_SIZE    3
  1374. X#define TOC_SIZE    4096
  1375. X
  1376. Xstatic char *Toc[TOC_SIZE];    /* array of pointers to malloc-ed TOC strings */
  1377. Xstatic int TocPages[TOC_SIZE];    /* page on which the item occurs */
  1378. Xstatic int TocCount;        /* how many TOC lines so far */
  1379. X
  1380. Xchar FileDate[24]; /* Last modified time of file    */
  1381. X
  1382. X
  1383. XNewPage()
  1384. X    /* Note that PageNumber is -1 during TableOfCont processing */
  1385. X{
  1386. X   static int CountNewPage=0;    /* suppress blank pre-page */
  1387. X
  1388. X   if( PageNumber >= 0 ) {
  1389. X    ++PageNumber;
  1390. X    putchar(BP);
  1391. X   }
  1392. X   else            /* we're doing TOC */
  1393. X    if( CountNewPage++ ) putchar(BP);    /* don't put first BP */
  1394. X   LineNumber = 0;
  1395. X
  1396. X   PutHeader();
  1397. X}
  1398. X
  1399. XPutHeader()
  1400. X{
  1401. X   register int i, l, j;
  1402. X
  1403. X   putchar('\n');
  1404. X   ++LineNumber;
  1405. X   l = strlen(Name);
  1406. X   for( j=0; j < l; ++j )
  1407. X      printf("%c\b%c\b%c", Name[j], Name[j], Name[j]);
  1408. X
  1409. X   if( PageNumber > 0 )
  1410. X   {
  1411. X      printf("  %.17s", FileDate);
  1412. X      GoToColumn(l+19, 70);
  1413. X      printf("Page:%4d\n\n", PageNumber);
  1414. X      ++LineNumber;
  1415. X      ++LineNumber;
  1416. X   }
  1417. X   else
  1418. X      {
  1419. X      GoToColumn(l, 55);
  1420. X      printf("%s\n\n", Today);
  1421. X      ++LineNumber;
  1422. X      ++LineNumber;
  1423. X   }
  1424. X}
  1425. X
  1426. XGoToColumn(from, to)
  1427. Xregister int from, to;
  1428. X{
  1429. X   if( from < to)
  1430. X   {
  1431. X      if( TabWidth > 0 ){
  1432. X         from &= ~(TabWidth-1);
  1433. X         for( ; (from + TabWidth) <= to; from += TabWidth )
  1434. X            putchar('\t');
  1435. X      }
  1436. X      for( ; from < to; from++ )
  1437. X         putchar(' ');
  1438. X   }
  1439. X}
  1440. X
  1441. X
  1442. XAddToTableOfContents(type)
  1443. X{
  1444. X   if( TocCount > TOC_SIZE )
  1445. X      return;
  1446. X   if( TocCount == TOC_SIZE )
  1447. X   {
  1448. X      fprintf(stderr, "%s: More than %d Table of contents entries; others ignored.\n",
  1449. X      ProgName, TOC_SIZE);
  1450. X      ++TocCount;
  1451. X      return;
  1452. X   }
  1453. X
  1454. X   if( type == NEWFILE )
  1455. X      AddFile();
  1456. X   else
  1457. X      AddFunction();
  1458. X}
  1459. X
  1460. XAddFunction()
  1461. X{
  1462. X   register int l;
  1463. X   register char *p;
  1464. X
  1465. X   /* This heuristic stops multiple occurrences of a function,
  1466. X            * selected by #ifdefs, to all end up many times over in the
  1467. X            * Table of Contents.  One only needs to see it once.  -IAN!
  1468. X            */
  1469. X   if( TocCount > 0 && TocPages[TocCount-1] == PageNumber
  1470. X      && strcmp(Toc[TocCount-1],FunctionName) == 0 )
  1471. X      return;
  1472. X   l = strlen(FunctionName);
  1473. X   p = Toc[TocCount] = (char *)malloc(l+1);
  1474. X   strcpy(p, FunctionName);
  1475. X   TocPages[TocCount] = PageNumber;
  1476. X   ++TocCount;
  1477. X}
  1478. X
  1479. XAddFile()
  1480. X{
  1481. X   register int i, l;
  1482. X   register int len;
  1483. X   char temp[20];
  1484. X
  1485. X   len = strlen(Name) + 20;
  1486. X   len = (len < 130) ? 130 : len;
  1487. X   Toc[TocCount] = (char *)malloc(len);
  1488. X   sprintf(Toc[TocCount], "\n    File: %s ", Name);
  1489. X   l = strlen(Toc[TocCount]);
  1490. X   if( l < 64 )
  1491. X   {
  1492. X      if( TabWidth > 0 ){
  1493. X         i = ((64 - l) /TabWidth) + 1;
  1494. X         while( i-- > 0 )
  1495. X            Toc[TocCount][l++] = '\t';
  1496. X      }
  1497. X      else{
  1498. X         while( l < 64 )
  1499. X            Toc[TocCount][l++] = ' ';
  1500. X      }
  1501. X      Toc[TocCount][l++] = '\0';
  1502. X   }
  1503. X   sprintf(temp, "  Page %4d\n", PageNumber);
  1504. X   strcat(Toc[TocCount], temp);
  1505. X   ++TocCount;
  1506. X}
  1507. X
  1508. XNewFile()
  1509. X{
  1510. X   GetFileTime();
  1511. X   if( ResetPage ) PageNumber=0;
  1512. X   NewPage();
  1513. X   AddToTableOfContents(NEWFILE);
  1514. X   FileLineNumber = 0;
  1515. X}
  1516. X
  1517. XGetFileTime()
  1518. X{
  1519. X   struct stat st;
  1520. X   extern char *ctime();
  1521. X
  1522. X   if( File == stdin )
  1523. X      strncpy(FileDate, &Today[4], 20);
  1524. X   else
  1525. X      {
  1526. X      fstat(fileno(File), &st);
  1527. X      strncpy(FileDate, ctime((time_t *)&st.st_mtime) + 4, 20);
  1528. X   }
  1529. X   strncpy(&FileDate[12], &FileDate[15], 5);
  1530. X   FileDate[18] = '\0';
  1531. X}
  1532. X
  1533. XDumpTableOfContents()
  1534. X{
  1535. X   register int i, j;
  1536. X   int TocIncr = 0;
  1537. X   int index[TOC_SIZE];
  1538. X   int Files = 0;    /* How many files reported so far */
  1539. X   int Functions = 0;    /* How many functions reported so far? */
  1540. X   int EveryFourth;
  1541. X
  1542. X   if( TocCount == 0 ) return;
  1543. X
  1544. X   for (i = 0; i < TocCount; i++) index[i] = i;
  1545. X
  1546. X   if( XrefFlag ) {    /* XREF - put files as first entries.
  1547. X             *   SortTable() will do the rest.    */
  1548. X      FilesFirst ();
  1549. X      TocIncr = 1;    /* start printing AFTER the \n */
  1550. X   }
  1551. X
  1552. X   if( SortFlag )
  1553. X      SortTable(index);
  1554. X
  1555. X   Name = "Table of  Contents";
  1556. X
  1557. X   PageNumber = -1;
  1558. X   LineNumber = 0;
  1559. X
  1560. X   if( Title )
  1561. X   {
  1562. X      FILE *f;
  1563. X      int n;
  1564. X      char b[256];
  1565. X
  1566. X      if( (f=fopen(TitleFile,"r")) == NULL )
  1567. X         fprintf(stderr,"%s: Can't open file '%s': %s\n",
  1568. X         ProgName, TitleFile, sys_errlist[errno] );
  1569. X      else
  1570. X      {
  1571. X         while( fgets(b, 256, f) != NULL )
  1572. X         {
  1573. X            if( strlen(b) ) b[strlen(b)-1]=0;
  1574. X            puts(b);
  1575. X            LineNumber++;
  1576. X            if( ++LineNumber >= PageEnd ) NewPage();
  1577. X         }
  1578. X
  1579. X         fclose(f);
  1580. X      }
  1581. X   }
  1582. X   else
  1583. X      NewPage();
  1584. X
  1585. X   for( i=0; i < TocCount; ++i )
  1586. X   {
  1587. X      if( Toc[index[i]][0] == '\n' )        /* File entry */
  1588. X      {
  1589. X         if( (LineNumber + 5) >= PageEnd ) NewPage();
  1590. X
  1591. X     /* Play with the leading \n in file entry */
  1592. X     EveryFourth = ( !(Files++ & 3) && XrefFlag );
  1593. X     printf("%s", Toc[index[i]]+TocIncr-EveryFourth);
  1594. X     LineNumber += 2 - TocIncr + EveryFourth;
  1595. X         continue;
  1596. X      }
  1597. X      else                /* Function entry */
  1598. X      {
  1599. X     if (! Functions++ && XrefFlag) {
  1600. X        printf( "\nFunction Index:\n");
  1601. X        LineNumber += 2;
  1602. X     }
  1603. X      }
  1604. X      if( ++LineNumber >= PageEnd ) NewPage();
  1605. X
  1606. X      printf("        %s ", Toc[index[i]]);
  1607. X      for( j=strlen(Toc[index[i]]); j < 48; ++j ) putchar('.');
  1608. X      printf(" %4d\n", TocPages[index[i]]);
  1609. X   }
  1610. X
  1611. X   if( ContentsOnly ) NewPage();
  1612. X}
  1613. X
  1614. XFilesFirst ()
  1615. X{
  1616. X    int    i;
  1617. X    int    FirstNonFile = 0;
  1618. X    char    *Ctemp;
  1619. X    int    Itemp;
  1620. X
  1621. X    for (i=0; i<TocCount; i++)
  1622. X        if (Toc [i][0] == '\n') {    /* swap file entry down */
  1623. X            Ctemp = Toc [i];
  1624. X            Toc [i] = Toc [FirstNonFile];
  1625. X            Toc [FirstNonFile] = Ctemp;
  1626. X
  1627. X            Itemp = TocPages [i];
  1628. X            TocPages [i] = TocPages [FirstNonFile];
  1629. X            TocPages [FirstNonFile] = Itemp;
  1630. X
  1631. X            FirstNonFile++;
  1632. X        }
  1633. X}
  1634. X
  1635. XSortTable(index)
  1636. Xregister int *index;
  1637. X{
  1638. X   register int i, temp, flag;
  1639. X   char name1[256];
  1640. X   char name2[256];
  1641. X
  1642. X   do {
  1643. X      flag = 0;
  1644. X      for (i = 0; i < TocCount - 1; i++)
  1645. X      {
  1646. X         if( Toc[index[i]][0] == '\n' || Toc[index[i+1]][0] == '\n' )
  1647. X            continue; /* don't sort across file names */
  1648. X         strcpy( name1, Toc[index[i]] );
  1649. X         strcpy( name2, Toc[index[i+1]] );
  1650. X
  1651. X         if( CaseInsensitive )
  1652. X         {
  1653. X            char *p;
  1654. X            char c;
  1655. X            for(p=name1; c=*p; p++ )
  1656. X               if( islower(c) ) *p=toupper(c);
  1657. X            for(p=name2; c=*p; p++ )
  1658. X               if( islower(c) ) *p=toupper(c);
  1659. X         }
  1660. X
  1661. X         if( strcmp(name1, name2) > 0)
  1662. X         {
  1663. X            temp = index[i];
  1664. X            index[i] = index[i+1];
  1665. X            index[i+1] = temp;
  1666. X            flag = 1;
  1667. X         }
  1668. X      }
  1669. X   }
  1670. X   while( flag );
  1671. X}
  1672. X
  1673. !EOR!
  1674. echo extracting - wildfile.c
  1675. sed 's/^X//' > wildfile.c << '!EOR!'
  1676. X/*    Expand wildcards (e.g., for MSDOS, which doesn't do it for you).
  1677. X *    Note that the MSDOS wildcard conventions are used, not the
  1678. X *    UNIX conventions.
  1679. X *
  1680. X *    The following code is a prototype of the use of these functions,
  1681. X *    if 'fname' points to the [wildcarded] name of the file:
  1682. X *
  1683. X *    #ifdef NOWILD
  1684. X *        strncpy (fname, first_file (fname), MAXNAME);
  1685. X *        while ((fname!=NULL) && (*fname!='\0')) {
  1686. X *            process_file (fname);
  1687. X *            strncpy (fname, next_file (fname), MAXNAME);
  1688. X *        }
  1689. X *    #else
  1690. X *        process_file (fname);
  1691. X *    #endif
  1692. X *
  1693. X *      This file is an example of these functions for Turbo C.
  1694. X *    (It isn't needed for Turbo C 2.0, but may be for earlier ones.)
  1695. X */
  1696. X
  1697. X#include <stdio.h>
  1698. X
  1699. X#ifdef NOWILD
  1700. X#ifdef TURBOC
  1701. X
  1702. X#include <dos.h>
  1703. X#include <dir.h>
  1704. X
  1705. Xstruct ffblk fb;
  1706. Xchar    fullname[MAXPATH];
  1707. Xint    dirlen;
  1708. X
  1709. X/* Find the first file meeting the wildcard spec, and return its name */
  1710. Xchar *
  1711. Xfirst_file (fn)
  1712. X  char    *fn;        /* filename (perhaps complete pathname */
  1713. X{
  1714. X    int    ffflag;
  1715. X    int    splitflag;
  1716. X    char    drive[MAXDRIVE], dir[MAXDIR],
  1717. X        name[MAXFILE], ext[MAXEXT];
  1718. X
  1719. X    /* Get path part, if any */
  1720. X    splitflag = fnsplit (fn, drive, dir, name, ext);
  1721. X    strcpy (fullname, drive);
  1722. X    strcat (fullname, dir);
  1723. X    dirlen = strlen (fullname);
  1724. X
  1725. X    ffflag = findfirst (fn, &fb, FA_RDONLY+FA_ARCH);
  1726. X    if (ffflag) {        /* No files match */
  1727. X        if (splitflag & WILDCARDS)    /* done if wildcard */
  1728. X            return (NULL);
  1729. X        else                /* complain if not wild */
  1730. X            return (fn);
  1731. X    }
  1732. X    else {
  1733. X        if (dirlen) {
  1734. X            strcpy (fullname+dirlen, fb.ff_name);
  1735. X            return (fullname);
  1736. X        }
  1737. X        else
  1738. X            return (fb.ff_name);
  1739. X    }
  1740. X}
  1741. X
  1742. X/* Find the next file meeting the wildcard spec, and return its name */
  1743. Xchar *
  1744. Xnext_file ()
  1745. X{
  1746. X    int    ffflag;
  1747. X
  1748. X    ffflag = findnext (&fb);
  1749. X    if (ffflag)        /* No more matching files */
  1750. X        return (NULL);
  1751. X    else {
  1752. X        if (dirlen) {
  1753. X            strcpy (fullname+dirlen, fb.ff_name);
  1754. X            return (fullname);
  1755. X        }
  1756. X        else
  1757. X            return (fb.ff_name);
  1758. X    }
  1759. X}
  1760. X
  1761. X#endif
  1762. X#endif
  1763. !EOR!
  1764. echo extracting - cpr.h
  1765. sed 's/^X//' > cpr.h << '!EOR!'
  1766. X/**************  Header File For CPR  ************************/
  1767. X
  1768. X#ifdef MAIN
  1769. X#  define EXTERN
  1770. X#  define INIT(x)    =x
  1771. X#else
  1772. X#  define EXTERN    extern
  1773. X#  define INIT(x)
  1774. X#endif
  1775. X
  1776. Xextern int errno; /* system error number */
  1777. Xextern char *sys_errlist[]; /* error message */
  1778. Xextern char *malloc() ; /* important for 8086-like systems */
  1779. X
  1780. X#define BP        0xC        /* Form feed            */
  1781. X
  1782. X#define    NEWFILE        1
  1783. X#define NEWFUNCTION    2
  1784. X
  1785. X/* Define formats for beginning of line */
  1786. X#define    NUMFORMAT    "%6ld  "
  1787. X#define    BLANKFORMAT    "        "
  1788. X#define    FORMATWIDTH    8
  1789. X
  1790. X#define    NULLSTR( x )    ((x == NULL) || (*x == '\0'))
  1791. X#define    WHITESPACE(x)    (strchr (" \n\r\t", x))
  1792. X
  1793. X/* Option flag variables */
  1794. XEXTERN    int PageLength INIT( 66 );    /* -l, <len> Page length */
  1795. XEXTERN    int PageWidth INIT( 0 );    /* -w, page width for line folding */
  1796. XEXTERN    int PagePart INIT( 12 );    /* -p, Decision on paging for new fn*/
  1797. XEXTERN    int PageEnd INIT( 59 );        /* Accounts for space at bottom (7 lines) */
  1798. XEXTERN    int Title INIT( 0 );        /* -T, print a title file */
  1799. XEXTERN    int ResetPage INIT( 0 );    /* -N, each file resets page # */
  1800. XEXTERN    int ContentsOnly INIT( 0 );    /* -C, only ToC, no listings */
  1801. XEXTERN    int SortFlag INIT( 0 );        /* -s, sort table of contents    */
  1802. XEXTERN    int CaseInsensitive INIT( 0 );    /* -S, case-insens sort */
  1803. XEXTERN    int XrefFlag INIT( 0 );        /* -x, cross-reference */
  1804. XEXTERN    int NumberFlag INIT( 0 );    /* -n, line numbers in output */
  1805. XEXTERN    int FirstCol INIT( 0 );        /* starting column for listing (-n) */
  1806. XEXTERN    int Space_to_leave INIT( 5 );    /* -r<number> space to leave */
  1807. XEXTERN    int TabWidth INIT( 8 );        /* -t <number> width of tabs */
  1808. XEXTERN    int OnlyCFiles INIT( 0 );    /* -c, only look in C files */
  1809. X
  1810. X/* State variables for the program */
  1811. XEXTERN    long FileLineNumber;    /* Input file line number    */
  1812. XEXTERN    int LineNumber;        /* Output line    on this page */
  1813. XEXTERN    int PageNumber INIT( 0 ); /* You figure this one out    */
  1814. XEXTERN    int InComment;
  1815. XEXTERN    int InString;
  1816. X
  1817. XEXTERN    char *TitleFile;
  1818. XEXTERN    char *ProgName;
  1819. XEXTERN    char Today[30];
  1820. XEXTERN    char *Name;     /* Current file name */
  1821. XEXTERN    char FunctionName[80];
  1822. X
  1823. X/* Help with parsing the command line and argument file */
  1824. XEXTERN    int InArgFile INIT( 0 );
  1825. XEXTERN    char arg[1024];        /* buffer for current argument */
  1826. XEXTERN    char *listn;        /* argument file name */
  1827. XEXTERN    FILE *listf;        /* argument file */
  1828. XEXTERN    FILE *File;
  1829. XEXTERN    FILE *Tfile;        /* title file */
  1830. X
  1831. !EOR!
  1832. echo extracting - makefile
  1833. sed 's/^X//' > makefile << '!EOR!'
  1834. X# MAKEFILES FOR CPR:
  1835. X# Pick out your system, extract the sub-makefile, and go.
  1836. X# Note: this makefile is untested, and probably won't work as is;
  1837. X# use it as a guide.
  1838. X
  1839. X# For UNIX System V.
  1840. XSRC=cpr.c parsargs.c list.c fold.c report.c
  1841. XOBJ=cpr.o parsargs.o list.o fold.o report.o
  1842. XHDR=cpr.h
  1843. XTARGET=cpr
  1844. XCC=cc -c -O
  1845. XLINK=cc -o cpr
  1846. X
  1847. X# For MSDOS using Turbo C 2.0 or later
  1848. XSRC=cpr.c parsargs.c list.c fold.c report.c
  1849. XOBJ=cpr.obj parsargs.obj list.obj fold.obj report.obj \tc\lib\wildargs.obj
  1850. XHDR=cpr.h
  1851. XTARGET=cpr.exe
  1852. XCC=tcc -O -DMSDOS -DTURBOC
  1853. XLINK=tcc
  1854. X
  1855. X# For MSDOS using Microsoft C 5.0 or later
  1856. X# (Note: in the "rules" section, end each command line with ';')
  1857. XSRC=cpr.c parsargs.c list.c fold.c report.c
  1858. XOBJ=cpr.obj parsargs.obj list.obj fold.obj report.obj \msc\lib\setargv.obj
  1859. XHDR=cpr.h
  1860. XTARGET=CPR.EXE
  1861. XCC=msc -O -DMSDOS
  1862. XLINK=link
  1863. X
  1864. X# For systems that don't expand wildcards on command line
  1865. X# (including earlier MS C and Turbo C),
  1866. X# replace the "special" .obj with "wildfile.obj", which you will write
  1867. X# yourself.  A sample wildfile.c is given for early Turbo C.
  1868. X# Also, compile with the additional flag NOWILD.
  1869. X
  1870. X# Generic Makefile section
  1871. X
  1872. X.c.o:
  1873. X        $(CC) $*.c
  1874. X
  1875. X$(TARGET) : $(OBJ)
  1876. X        $(LINK) $(OBJ)
  1877. X
  1878. X$(OBJ) : $(HDR)
  1879. X
  1880. X# Make a listing using cpr.  This "bootstraps" from a good cpr.
  1881. Xlisting : cpr.cpr
  1882. Xcpr.cpr : $(SRC) $(HDR)
  1883. X        cpr -Xp9w83 $(SRC) $(HDR) > cpr.cpr
  1884. X
  1885. !EOR!
  1886.  
  1887.