/* nrchbar -- insert nroff change bar commands into a file */ #include #include #define DIFFPROG "/bin/diff" typedef int bool; #define TRUE 1 #define FALSE 0 #define ADD 'a' #define CHANGE 'c' #define DELETE 'd' typedef struct { int lnOldFrom; int lnOldTo; int cmd; int lnNewFrom; int lnNewTo; } diffdescr; char *progname; bool flagAll = FALSE; bool flagBreakAfter = TRUE; bool flagDelete = FALSE; /* forward declarations */ FILE *fopenOrElse(); void nrchbar(); FILE *pipeDiffs(); void skipLinesOrElse(); void userErr(); #ifndef lint char *rcsid = "$Header: nrchbar.c,v 1.3 87/03/31 16:33:16 bobl Exp $"; #endif main(argc, argv) int argc; char *argv[]; { int c; extern int optind; FILE *fpDiff, *fpNew; char *fnOld, *fnNew; progname = argv[0]; while ((c = getopt(argc, argv, "abd")) != EOF) switch (c) { case 'a': flagAll = TRUE; break; case 'b': flagBreakAfter = FALSE; break; case 'd': flagDelete = TRUE; break; case '?': (void) fprintf(stderr, "%s: unknown flag \"-%c\"\n", c); exit(1); break; } /* * Depending on the number of files on the command line. */ switch (argc - optind) { case 1: fpDiff = stdin; fnNew = argv[optind++]; fpNew = fopenOrElse(fnNew, "r"); break; case 2: fnOld = argv[optind++]; fnNew = argv[optind++]; fpDiff = pipeDiffs(fnOld, fnNew); fpNew = fopenOrElse(fnNew, "r"); break; default: (void) fprintf(stderr, "Usage: %s [ -a ] [ -b ] [ -d ] [ ] new\n", progname); exit(1); } /* * Now the real work begins. */ nrchbar(fpDiff, fpNew); exit(0); } /* beginChbar -- put out a *roff command to begin a change bar */ void beginChbar(chMark) int chMark; /* in: use this character */ { (void) printf(".mc %c\n", chMark); return; } /* copyLines -- copy a given number of lines from one file to standard output */ bool copyLines(nLn, nSkip, fpFrom) int nLn; /* in: copy this many lines ... */ int nSkip; /* in: ... skipping this many characters at the start of each one ... */ FILE *fpFrom; /* in: ... from this file to stdout */ { int ch; int iSkip; while (nLn-- > 0) { for (iSkip = 0; iSkip < nSkip; iSkip++) if (getc(fpFrom) == EOF) return FALSE; do { ch = getc(fpFrom); if (ch == EOF) return FALSE; putchar(ch); } while (ch != '\n'); } return TRUE; } /* copyLinesOrElse -- copy a given number of lines from one file to standard output; exit if unable */ void copyLinesOrElse(nLn, nSkip, fpFrom) int nLn; /* in: copy this many lines ... */ int nSkip; /* in: ... skipping this many characters at the start of each one ... */ FILE *fpFrom; /* in: ... from this file to stdout */ { if (!copyLines(nLn, nSkip, fpFrom)) userErr("unexpected EOF"); return; } /* endChbar -- put out a *roff command to end a change bar section */ void endChbar() { if (flagBreakAfter) (void) printf(".br\n"); /* make sure the previous change bar is visible */ (void) printf(".mc\n"); return; } /* fopenOrElse -- open a file or else exit with an error message */ FILE *fopenOrElse(fname, type) char *fname; /* in: file name to open */ char *type; /* in: way in which to open fname */ { FILE *fp = fopen(fname, type); if (fp == NULL) { (void) fprintf(stderr, "%s: can't open \"%s\" for mode \"%s\"\n", progname, fname, type); exit(1); } return fp; } /* getDiff -- get a "diff" description line */ int getDiff(fp, ddscr) FILE *fp; /* in: file containing "diff" output */ diffdescr *ddscr; /* in: a diff section descriptor */ { #define EOA 0 #define START_ACCUM 1 #define ACCUM 2 #define SET_OLD_FROM 3 #define SET_OLD_TO 4 #define SET_CMD 5 #define SET_NEW_FROM 6 #define SET_NEW_TO 7 #define MXN_ACTION 8 static struct { int state; char *trig; int stateNext; int action[MXN_ACTION]; } fsm[] = { { 0, "0123456789", 1, { START_ACCUM, EOA } }, { 1, "0123456789", 1, { ACCUM, EOA } }, { 1, ",", 2, { SET_OLD_FROM, EOA } }, { 1, "acd", 4, { SET_OLD_FROM, SET_OLD_TO, SET_CMD, EOA } }, { 2, "123456789", 3, { START_ACCUM, EOA } }, { 3, "0123456789", 3, { ACCUM, EOA } }, { 3, "acd", 4, { SET_OLD_TO, SET_CMD, EOA } }, { 4, "0123456789", 5, { START_ACCUM, EOA } }, { 5, "0123456789", 5, { ACCUM, EOA } }, { 5, ",", 6, { SET_NEW_FROM, EOA } }, { 5, "\n", -1, { SET_NEW_FROM, SET_NEW_TO, EOA } }, { 6, "123456789", 7, { START_ACCUM, EOA } }, { 7, "0123456789", 7, { ACCUM, EOA } }, { 7, "\n", -1, { SET_NEW_TO, EOA } }, { -1, (char *) NULL, -1, { EOA } } /* sentinal */ }, *fsmCur; int stateCur = 0; int ch, accum = 0; int *action; int len = 0; extern char *strchr(); do { ch = getc(fp); if (ch == EOF) return EOF; len++; fsmCur = &fsm[0]; while (fsmCur->state != -1 && (fsmCur->state != stateCur || strchr(fsmCur->trig, ch) == NULL)) fsmCur++; if (fsmCur->state == -1) userErr("illegal syntax in 'diff' output"); for (action = fsmCur->action; *action != EOA; action++) switch (*action) { case START_ACCUM: accum = ch - '0'; /* assume ASCII */ break; case ACCUM: accum = 10*accum + (ch - '0'); /* assume ASCII */ break; case SET_CMD: ddscr->cmd = ch; break; case SET_NEW_FROM: ddscr->lnNewFrom = accum; break; case SET_NEW_TO: ddscr->lnNewTo = accum; break; case SET_OLD_FROM: ddscr->lnOldFrom = accum; break; case SET_OLD_TO: ddscr->lnOldTo = accum; break; } stateCur = fsmCur->stateNext; } while (stateCur != -1); return len; } /* isRoffCmd -- return TRUE iff the next line begins with a *roff command */ bool isRoffCmd(fp) FILE *fp; /* in: file to examine for *roff command */ { int ch; if ((ch = getc(fp)) == EOF) userErr("unexpected EOF"); (void) ungetc(ch, fp); return (ch == '.'); /* don't know about ".cc" command */ } /* markDiff -- note a changed section of a file; return # of lines copied from fpNew */ void markDiff(ddscr, fpDiff, fpNew) diffdescr *ddscr; /* in: descriptor of the differing sectinos */ FILE *fpDiff; /* in: the diff file */ FILE *fpNew; /* in: the new file */ { int nLnNew = ddscr->lnNewTo - ddscr->lnNewFrom + 1; int nLnOld = ddscr->lnOldTo - ddscr->lnOldFrom + 1; int nLnNewCopy = nLnNew; switch (ddscr->cmd) { case ADD: if (!flagAll) { while (nLnNewCopy > 0 && isRoffCmd(fpNew)) { copyLinesOrElse(1, 0, fpNew); nLnNewCopy--; } } if (nLnNewCopy > 0) { beginChbar('+'); copyLinesOrElse(nLnNewCopy, 0, fpNew); endChbar(); } skipLinesOrElse(nLnNew, fpDiff); return; case CHANGE: if (!flagAll) { while (nLnNewCopy > 0 && isRoffCmd(fpNew)) { copyLinesOrElse(1, 0, fpNew); nLnNewCopy--; } } if (nLnNewCopy > 0) { beginChbar('|'); copyLinesOrElse(nLnNewCopy, 0, fpNew); endChbar(); } skipLinesOrElse(nLnNew + 1 + nLnOld, fpDiff); /* allow for "---" in diff */ return; case DELETE: beginChbar('-'); if (flagDelete) { putchar('['); putchar('['); putchar('\n'); copyLinesOrElse(nLnOld, 2, fpDiff); /* strip off the "< " */ putchar(']'); putchar(']'); putchar('\n'); } else skipLinesOrElse(nLnOld, fpDiff); endChbar(); return; } } /* nrchbar -- produce *roff change bar file */ void nrchbar(fpDiff, fpNew) FILE *fpDiff; /* in: diff file */ FILE *fpNew; /* in: original file */ { int lnNew = 1; int nLines; diffdescr ddscr; while (getDiff(fpDiff, &ddscr) != EOF) { nLines = ddscr.lnNewFrom - lnNew; if (ddscr.cmd == DELETE) nLines++; copyLinesOrElse(nLines, 0, fpNew); markDiff(&ddscr, fpDiff, fpNew); lnNew = ddscr.lnNewTo + 1; } while (copyLines(1, 0, fpNew)) ; return; } /* pipeDiffs -- return a pipe with a diff run on the other end */ FILE *pipeDiffs(fnOld, fnNew) char *fnOld; /* in: old file name */ char *fnNew; /* in: new file name */ { char *cmd; FILE *fpPipe; cmd = (char *) malloc((unsigned int) (strlen(DIFFPROG) + 1 + strlen(fnOld) + 1 + strlen(fnNew) + 1)); (void) sprintf(cmd, "%s %s %s", DIFFPROG, fnOld, fnNew); fpPipe = popen(cmd, "r"); if (fpPipe == NULL) { (void) fprintf(stderr, "%s: can't open pipe for \"%s\"\n", progname, cmd); exit(1); } free(cmd); return fpPipe; } /* skipLinesOrElse -- skip a given number of lines in a file; exit if errors */ void skipLinesOrElse(nLn, fp) int nLn; /* in: skip this many lines ... */ FILE *fp; /* in: ... in this file */ { int ch; while (nLn-- > 0) { do { if ((ch = getc(fp)) == EOF) userErr("unexpected EOF"); } while (ch != '\n'); } return; } /* userErr -- note an error and politely exude */ void userErr(msg) char *msg; { (void) fprintf(stderr, "%s: %s\n", progname, msg); exit(1); }