home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume10 / nrchbar < prev    next >
Text File  |  1987-07-29  |  15KB  |  596 lines

  1. Path: uunet!rs
  2. From: rs@uunet.UU.NET (Rich Salz)
  3. Newsgroups: comp.sources.unix
  4. Subject: v10i070:  A "changebar" interface for *roff
  5. Message-ID: <735@uunet.UU.NET>
  6. Date: 30 Jul 87 22:21:03 GMT
  7. Organization: UUNET Communications Services, Arlington, VA
  8. Lines: 585
  9. Approved: rs@uunet.UU.NET
  10.  
  11. Submitted-by: tektronix!tessi!bobl (Bob Lewis)
  12. Posting-number: Volume 10, Issue 70
  13. Archive-name: nrchbar
  14.  
  15. [  This program works with diff on ostensible Unix formatter input to
  16.    output formatting codes to do changebars.  Very nice.  --r$  ]
  17.  
  18. #!/bin/sh
  19. # to extract, remove the header and type "sh filename"
  20. if `test ! -s ./README`
  21. then
  22. echo "writing ./README"
  23. cat > ./README << '\Rogue\Monster\'
  24. The "nrchbar" program effectively inserts n/troff-style change bar commands
  25. into files which are (presumably) n/troff source.  For more information,
  26. consult the man page.
  27.  
  28. To install "nrchbar":
  29.  
  30. 1)    Inspect "Makefile" for proper destinations (currently,
  31.     "/usr/local/bin" for the binary and "/usr/man/manl" for the man
  32.     page).  Correct these if necessary.  If "diff" on your system isn't
  33.     "/bin/diff", you'll also need to modify the "DIFFPROG" #define in
  34.     "nrchbar.c".  "nrchbar" assumes "getopt" is present, so you'll need
  35.     to contact your friendly neighborhood comp.sources.unix archive if it
  36.     isn't.
  37.  
  38. 2)    In the source directory:
  39.         
  40.         % make install
  41.  
  42. 3)    If everything goes well, still in the source directory:
  43.  
  44.         % make clean
  45.  
  46. I claim that this program was written entirely by me, entitling me (with the
  47. approval of my management) to release it into the public domain, which I now
  48. do.  However, I make no warrantees, either express or implied, on "nrchbar"
  49. or its associated files and I do not assume responsiblity for any damages
  50. resulting from its installation or use.
  51.  
  52. The software has only been compiled and tested on a Sun 3/50, but I don't
  53. expect any major problems on other UNIX machines, including System V.
  54.  
  55. In spite of the disclaimer above, I'd like to hear about bugs and/or feature
  56. requests.
  57.  
  58. Acknowledgements and thanks to Robert Reed of Tektronix, who wrote the
  59. original shell script; and to Jeff Hahs, Ron Lunde, Charlie Mills, and Bill
  60. den Beste of TSSI for encouragement, ideas, and testing.
  61.  
  62.     - Bob Lewis
  63.       Test Systems Strategies, Inc.
  64.       8205 SW Creekside Pl.
  65.       Beaverton, OR  97005
  66.       (503) 643-9281
  67.  
  68.       ...!tektronix!tessi!bobl   or   bobl@tessi.UUCP
  69.  
  70. \Rogue\Monster\
  71. else
  72.   echo "will not over write ./README"
  73. fi
  74. if `test ! -s ./Makefile`
  75. then
  76. echo "writing ./Makefile"
  77. cat > ./Makefile << '\Rogue\Monster\'
  78. SRC = nrchbar.c
  79.  
  80. BIN = nrchbar
  81. BINDEST = /usr/local/bin
  82.  
  83. MAN = nrchbar.1
  84. MANDEST = /usr/man/manl
  85.  
  86. SHARNAME = nrchbar
  87.  
  88. $(BIN):    $(SRC)
  89.     cc -O -o $(BIN) $(SRC)
  90.  
  91. clean:
  92.     rm -f $(BIN) *.o core a.out
  93.  
  94. install:    $(BIN) $(MAN)
  95.     install -s $(BIN) $(BINDEST)
  96.     cp $(MAN) $(MANDEST)
  97.  
  98. lint:    $(SRC)
  99.     lint -bchx $(SRC)
  100.  
  101. shar:    README Makefile $(SRC) $(MAN)
  102.     shar -f $(SHARNAME) README Makefile $(SRC) $(MAN)
  103. \Rogue\Monster\
  104. else
  105.   echo "will not over write ./Makefile"
  106. fi
  107. if `test ! -s ./nrchbar.c`
  108. then
  109. echo "writing ./nrchbar.c"
  110. cat > ./nrchbar.c << '\Rogue\Monster\'
  111. /* nrchbar -- insert nroff change bar commands into a file */
  112.  
  113. #include <stdio.h>
  114.  
  115. #define DIFFPROG "/bin/diff"
  116.  
  117. typedef int bool;
  118. #define TRUE 1
  119. #define FALSE 0
  120.  
  121. #define ADD        'a'
  122. #define CHANGE    'c'
  123. #define DELETE    'd'
  124.  
  125. typedef struct {
  126.     int lnOldFrom;
  127.     int lnOldTo;
  128.     int cmd;
  129.     int lnNewFrom;
  130.     int lnNewTo;
  131. } diffdescr;
  132.  
  133. char *progname;
  134. bool flagAll = FALSE;
  135. bool flagBreakAfter = TRUE;
  136. bool flagDelete = FALSE;
  137.  
  138. /* forward declarations */
  139. FILE *fopenOrElse();
  140. void nrchbar();
  141. FILE *pipeDiffs();
  142. void skipLinesOrElse();
  143. void userErr();
  144.  
  145. #ifndef lint
  146. char *rcsid = "$Header: nrchbar.c,v 1.3 87/03/31 16:33:16 bobl Exp $";
  147. #endif
  148.  
  149. main(argc, argv)
  150.     int argc;
  151.     char *argv[];
  152. {
  153.     int c;
  154.     extern int optind;
  155.     FILE *fpDiff, *fpNew;
  156.     char *fnOld, *fnNew;
  157.     
  158.     progname = argv[0];
  159.     while ((c = getopt(argc, argv, "abd")) != EOF)
  160.         switch (c) {
  161.  
  162.         case 'a':
  163.             flagAll = TRUE;
  164.             break;
  165.  
  166.         case 'b':
  167.             flagBreakAfter = FALSE;
  168.             break;
  169.  
  170.         case 'd':
  171.             flagDelete = TRUE;
  172.             break;
  173.  
  174.         case '?':
  175.             (void) fprintf(stderr, "%s: unknown flag \"-%c\"\n", c);
  176.             exit(1);
  177.             break;
  178.         }
  179.  
  180.  
  181.     /*
  182.      *    Depending on the number of files on the command line.
  183.      */
  184.     switch (argc - optind) {
  185.     
  186.     case 1:
  187.         fpDiff = stdin;
  188.         fnNew = argv[optind++];
  189.         fpNew = fopenOrElse(fnNew, "r");
  190.         break;
  191.  
  192.     case 2:
  193.         fnOld = argv[optind++];
  194.         fnNew = argv[optind++];
  195.         fpDiff = pipeDiffs(fnOld, fnNew);
  196.         fpNew = fopenOrElse(fnNew, "r");
  197.         break;
  198.  
  199.     default:
  200.         (void) fprintf(stderr, "Usage: %s [ -a ] [ -b ] [ -d ] [ <old> ] new\n", progname);
  201.         exit(1);
  202.     }
  203.     /*
  204.      *    Now the real work begins.
  205.      */
  206.     nrchbar(fpDiff, fpNew);
  207.  
  208.     exit(0);
  209. }
  210.  
  211. /* beginChbar -- put out a *roff command to begin a change bar */
  212. void beginChbar(chMark)
  213.     int chMark;    /* in: use this character */
  214. {
  215.     (void) printf(".mc %c\n", chMark);
  216.     return;
  217. }
  218.  
  219. /* copyLines -- copy a given number of lines from one file to standard output */
  220. bool copyLines(nLn, nSkip, fpFrom)
  221.     int nLn;        /* in: copy this many lines ... */
  222.     int nSkip;        /* in: ... skipping this many characters at the start of each one ... */
  223.     FILE *fpFrom;    /* in: ... from this file to stdout */
  224. {
  225.     int ch;
  226.     int iSkip;
  227.  
  228.     while (nLn-- > 0) {
  229.         for (iSkip = 0; iSkip < nSkip; iSkip++)
  230.             if (getc(fpFrom) == EOF)
  231.                 return FALSE;
  232.         do {
  233.             ch = getc(fpFrom);
  234.             if (ch == EOF)
  235.                 return FALSE;
  236.             putchar(ch);
  237.         } while (ch != '\n');
  238.     }
  239.  
  240.     return TRUE;
  241. }
  242.  
  243. /* copyLinesOrElse -- copy a given number of lines from one file to standard output; exit if unable */
  244. void copyLinesOrElse(nLn, nSkip, fpFrom)
  245.     int nLn;        /* in: copy this many lines ... */
  246.     int nSkip;        /* in: ... skipping this many characters at the start of each one ... */
  247.     FILE *fpFrom;    /* in: ... from this file to stdout */
  248. {
  249.     if (!copyLines(nLn, nSkip, fpFrom))
  250.         userErr("unexpected EOF");
  251.     return;
  252. }
  253.  
  254. /* endChbar -- put out a *roff command to end a change bar section */
  255. void endChbar()
  256. {
  257.     if (flagBreakAfter)
  258.         (void) printf(".br\n");    /* make sure the previous change bar is visible */
  259.     (void) printf(".mc\n");
  260.     return;
  261. }
  262.  
  263. /* fopenOrElse -- open a file or else exit with an error message */
  264. FILE *fopenOrElse(fname, type)
  265.     char *fname;    /* in: file name to open */
  266.     char *type;        /* in: way in which to open fname */
  267. {
  268.     FILE *fp = fopen(fname, type);
  269.  
  270.     if (fp == NULL) {
  271.         (void) fprintf(stderr, "%s: can't open \"%s\" for mode \"%s\"\n",
  272.             progname, fname, type);
  273.         exit(1);
  274.     }
  275.     return fp;
  276. }
  277.  
  278. /* getDiff -- get a "diff" description line */
  279. int getDiff(fp, ddscr)
  280.     FILE *fp;            /* in: file containing "diff" output */
  281.     diffdescr *ddscr;    /* in: a diff section descriptor */
  282. {
  283. #define EOA                0
  284. #define START_ACCUM        1
  285. #define ACCUM            2
  286. #define SET_OLD_FROM    3
  287. #define SET_OLD_TO        4
  288. #define SET_CMD            5
  289. #define SET_NEW_FROM    6
  290. #define SET_NEW_TO        7
  291. #define MXN_ACTION        8
  292.     static struct {
  293.         int state;
  294.         char *trig;
  295.         int stateNext;
  296.         int action[MXN_ACTION];
  297.     } fsm[] = {
  298.         { 0,    "0123456789",    1,    { START_ACCUM, EOA } },
  299.         { 1,    "0123456789",    1,    { ACCUM, EOA } },
  300.         { 1,    ",",            2,    { SET_OLD_FROM, EOA } },
  301.         { 1,    "acd",            4,    { SET_OLD_FROM, SET_OLD_TO, SET_CMD, EOA } },
  302.         { 2,    "123456789",    3,    { START_ACCUM, EOA } },
  303.         { 3,    "0123456789",    3,    { ACCUM, EOA } },
  304.         { 3,    "acd",            4,    { SET_OLD_TO, SET_CMD, EOA } },
  305.         { 4,    "0123456789",    5,    { START_ACCUM, EOA } },
  306.         { 5,    "0123456789",    5,    { ACCUM, EOA } },
  307.         { 5,    ",",            6,    { SET_NEW_FROM, EOA } },
  308.         { 5,    "\n",            -1,    { SET_NEW_FROM, SET_NEW_TO, EOA } },
  309.         { 6,    "123456789",    7,    { START_ACCUM, EOA } },
  310.         { 7,    "0123456789",    7,    { ACCUM, EOA } },
  311.         { 7,    "\n",            -1,    { SET_NEW_TO, EOA } },
  312.         { -1,    (char *) NULL,    -1, { EOA } }    /* sentinal */
  313.     }, *fsmCur;
  314.     int stateCur = 0;
  315.     int ch, accum = 0;
  316.     int *action;
  317.     int len = 0;
  318.     extern char *index();
  319.  
  320.     do {
  321.         ch = getc(fp);
  322.         if (ch == EOF)
  323.             return EOF;
  324.         len++;
  325.         fsmCur = &fsm[0];
  326.         while (fsmCur->state != -1
  327.                 && (fsmCur->state != stateCur
  328.                     || index(fsmCur->trig, ch) == NULL))
  329.             fsmCur++;
  330.         if (fsmCur->state == -1)
  331.             userErr("illegal syntax in 'diff' output");
  332.         for (action = fsmCur->action; *action != EOA; action++)
  333.             switch (*action) {
  334.  
  335.             case START_ACCUM:
  336.                 accum = ch - '0';    /* assume ASCII */
  337.                 break;                
  338.  
  339.             case ACCUM:
  340.                 accum = 10*accum + (ch - '0');    /* assume ASCII */
  341.                 break;
  342.                 
  343.             case SET_CMD:
  344.                 ddscr->cmd = ch;
  345.                 break;
  346.                 
  347.             case SET_NEW_FROM:
  348.                 ddscr->lnNewFrom = accum;
  349.                 break;
  350.                 
  351.             case SET_NEW_TO:
  352.                 ddscr->lnNewTo = accum;
  353.                 break;
  354.                 
  355.             case SET_OLD_FROM:
  356.                 ddscr->lnOldFrom = accum;
  357.                 break;
  358.                 
  359.             case SET_OLD_TO:
  360.                 ddscr->lnOldTo = accum;
  361.                 break;
  362.             }
  363.         stateCur = fsmCur->stateNext;
  364.     } while (stateCur != -1);
  365.     
  366.     return len;
  367. }
  368.  
  369. /* isRoffCmd -- return TRUE iff the next line begins with a *roff command */
  370. bool isRoffCmd(fp)
  371.     FILE *fp;    /* in: file to examine for *roff command */
  372. {
  373.     int ch;
  374.     
  375.     if ((ch = getc(fp)) == EOF)
  376.         userErr("unexpected EOF");
  377.     (void) ungetc(ch, fp);
  378.     return (ch == '.');    /* don't know about ".cc" command */
  379. }
  380.  
  381. /* markDiff -- note a changed section of a file; return # of lines copied from fpNew */
  382. void markDiff(ddscr, fpDiff, fpNew)
  383.     diffdescr *ddscr;    /* in: descriptor of the differing sectinos */
  384.     FILE *fpDiff;        /* in: the diff file */
  385.     FILE *fpNew;        /* in: the new file */
  386. {
  387.     int nLnNew = ddscr->lnNewTo - ddscr->lnNewFrom + 1;
  388.     int nLnOld = ddscr->lnOldTo - ddscr->lnOldFrom + 1;
  389.     int nLnNewCopy = nLnNew;
  390.  
  391.     switch (ddscr->cmd) {
  392.  
  393.     case ADD:
  394.         if (!flagAll) {
  395.             while (nLnNewCopy > 0 && isRoffCmd(fpNew)) {
  396.                 copyLinesOrElse(1, 0, fpNew);
  397.                 nLnNewCopy--;
  398.             }
  399.         }
  400.         if (nLnNewCopy > 0) {
  401.             beginChbar('+');
  402.             copyLinesOrElse(nLnNewCopy, 0, fpNew);
  403.             endChbar();
  404.         }
  405.         skipLinesOrElse(nLnNew, fpDiff);
  406.         return;
  407.         
  408.     case CHANGE:
  409.         if (!flagAll) {
  410.             while (nLnNewCopy > 0 && isRoffCmd(fpNew)) {
  411.                 copyLinesOrElse(1, 0, fpNew);
  412.                 nLnNewCopy--;
  413.             }
  414.         }
  415.         if (nLnNewCopy > 0) {
  416.             beginChbar('|');
  417.             copyLinesOrElse(nLnNewCopy, 0, fpNew);
  418.             endChbar();
  419.         }
  420.         skipLinesOrElse(nLnNew + 1 + nLnOld, fpDiff);    /* allow for "---" in diff */
  421.         return;
  422.         
  423.     case DELETE:
  424.         beginChbar('-');
  425.         if (flagDelete) {
  426.             putchar('[');
  427.             putchar('[');
  428.             putchar('\n');
  429.             copyLinesOrElse(nLnOld, 2, fpDiff);    /* strip off the "< " */
  430.             putchar(']');
  431.             putchar(']');
  432.             putchar('\n');
  433.         } else
  434.             skipLinesOrElse(nLnOld, fpDiff);
  435.         endChbar();
  436.         return;
  437.     }
  438. }
  439.  
  440. /* nrchbar -- produce *roff change bar file */
  441. void nrchbar(fpDiff, fpNew)
  442.     FILE *fpDiff;    /* in: diff file */
  443.     FILE *fpNew;    /* in: original file */
  444. {
  445.     int lnNew = 1;
  446.     int nLines;
  447.     diffdescr ddscr;
  448.  
  449.     while (getDiff(fpDiff, &ddscr) != EOF) {
  450.         nLines = ddscr.lnNewFrom - lnNew;
  451.         if (ddscr.cmd == DELETE)
  452.             nLines++;
  453.         copyLinesOrElse(nLines, 0, fpNew);
  454.         markDiff(&ddscr, fpDiff, fpNew);
  455.         lnNew = ddscr.lnNewTo + 1;
  456.     }
  457.     while (copyLines(1, 0, fpNew))
  458.         ;
  459.     return;
  460. }
  461.  
  462. /* pipeDiffs -- return a pipe with a diff run on the other end */
  463. FILE *pipeDiffs(fnOld, fnNew)
  464.     char *fnOld;    /* in: old file name */
  465.     char *fnNew;    /* in: new file name */
  466. {
  467.     char *cmd;
  468.     FILE *fpPipe;
  469.  
  470.     cmd = (char *) malloc((unsigned int) (strlen(DIFFPROG) + 1 + strlen(fnOld) + 1 + strlen(fnNew) + 1));
  471.     (void) sprintf(cmd, "%s %s %s", DIFFPROG, fnOld, fnNew);
  472.     fpPipe = popen(cmd, "r");
  473.     if (fpPipe == NULL) {
  474.         (void) fprintf(stderr, "%s: can't open pipe for \"%s\"\n",
  475.             progname, cmd);
  476.         exit(1);
  477.     }
  478.     free(cmd);
  479.     return fpPipe;
  480. }
  481.  
  482. /* skipLinesOrElse -- skip a given number of lines in a file; exit if errors */
  483. void skipLinesOrElse(nLn, fp)
  484.     int nLn;    /* in: skip this many lines ... */
  485.     FILE *fp;    /* in: ... in this file */
  486. {
  487.     int ch;
  488.  
  489.     while (nLn-- > 0) {
  490.         do {
  491.             if ((ch = getc(fp)) == EOF)
  492.                 userErr("unexpected EOF");
  493.         } while (ch != '\n');
  494.     }
  495.     return;
  496. }
  497.  
  498. /* userErr -- note an error and politely exude */
  499. void userErr(msg)
  500.     char *msg;
  501. {
  502.     (void) fprintf(stderr, "%s: %s\n", progname, msg);
  503.     exit(1);
  504. }
  505. \Rogue\Monster\
  506. else
  507.   echo "will not over write ./nrchbar.c"
  508. fi
  509. if `test ! -s ./nrchbar.1`
  510. then
  511. echo "writing ./nrchbar.1"
  512. cat > ./nrchbar.1 << '\Rogue\Monster\'
  513. .\" $Header: nrchbar.1,v 1.2 87/03/31 16:33:02 bobl Exp $
  514. .TH NRCHBAR 1 "March 19, 1987"
  515. .SH NAME
  516. nrchbar \- insert n/troff-style change bars in a file
  517. .SH SYNOPSIS
  518. \fBnrchbar\fP [ \fB\-a\fP ] [ \fB\-b\fP ] [ \fB\-d\fP ] [ \fIoldfile\fP ] \fInewfile\fP
  519. .SH DESCRIPTION
  520. \fINrchbar\fP inserts commands suitable for
  521. .IR nroff (1)
  522. or
  523. .IR troff (1)
  524. (hereafter, "\fI*roff\fP") to produce "change bars" denoting
  525. differences between \fIoldfile\fP and \fInewfile\fP.
  526. It sends the result to standard output.
  527. .LP
  528. If \fIoldfile\fP is defaulted, \fInrchbar\fP assumes that its standard
  529. input will contain differences between \fIoldfile\fP and \fInewfile\fP
  530. in
  531. .IR diff (1)
  532. format.
  533. .LP
  534. By default, \fInrchbar\fP ignores changes to sections containing nothing
  535. but \fI*roff\fP commands, which it takes to be any lines beginning with
  536. ".".
  537. You can override this with the \fB\-a\fP option.
  538. .LP
  539. \fINrchbar\fP inserts \fI*roff\fP ".mc" commands to provide the change
  540. bars.
  541. This will place a character to the far right of each changed section.
  542. This character is a "+" for added sections, a "|" for
  543. modified sections, and a "-" for deleted sections.
  544. .LP
  545. By default, the text of deleted sections does not appear, but the
  546. \fB\-d\fP option will cause it to be inserted, surrounded by "[[" and "]]".
  547. .LP
  548. Also by default, \fInrchbar\fP puts a break (".br") command after each
  549. changed section.
  550. This is the only way to guarantee that deletions and small changes
  551. get flagged.
  552. The \fB\-b\fP option directs the program not to insert these breaks.
  553. It makes the text look more like \fI*roff\fPed
  554. \fInewfile\fP, but means that some change bars won't appear where
  555. they should.
  556. .SH PRONUNCIATION
  557. NERCH-bar
  558. .SH ETYMOLOGY
  559. NRoff CHange BAR
  560. .SH OPTIONS
  561. .IP \fB\-a\fP
  562. Put change bars around all changed sections, even if they consist of
  563. nothing but \fI*roff\fP commands.
  564. .IP \fB\-b\fP
  565. Do not insert breaks after changed sections.
  566. .IP \fB\-d\fP
  567. Show deleted text by preceding it with "[[" and following it with "]]".
  568. .SH SEE\ ALSO
  569. nroff(1), troff(1)
  570. .SH BUGS
  571. "." is always assumed to denote \fI*roff\fP commands.
  572. \fINrchbar\fP knows nothing about the ".cc" command.
  573. .LP
  574. The need for the \fB\-b\fP hack would disappear if there were a way
  575. to tell \fI*roff\fP "Turn off the change bar character at
  576. immediately after you put out this one.".
  577. A horizontal ".wh", perhaps?
  578. .LP
  579. The ".mc" command doesn't always work, especially with diversions.
  580. This is actually a \fI*roff\fP bug, but you ought to know about it.
  581. .LP
  582. \fINrchbar\fP may mess up tables, lists, or displays.
  583. For a really polished change bar document, you may want to edit
  584. its output before sending it to \fI*roff\fP.
  585. \Rogue\Monster\
  586. else
  587.   echo "will not over write ./nrchbar.1"
  588. fi
  589. echo "Finished archive 1 of 1"
  590. exit
  591. -- 
  592.  
  593. Rich $alz            "Anger is an energy"
  594. Cronus Project, BBN Labs    rsalz@bbn.com
  595. Moderator, comp.sources.unix    sources@uunet.uu.net
  596.