[comp.sources.misc] v14i070: Unified context diff tools

davison@dri.com (Wayne Davison) (08/31/90)

Posting-number: Volume 14, Issue 70
Submitted-by: davison@dri.com (Wayne Davison)
Archive-name: unidiff/part01

[A brief excerpt from the PROPAGANDA file:]

I've created a new context diff format that combines the old and new hunks into
one unified hunk.  The result?  The unified context diff, or "unidiff."

Posting your patch using a unidiff will usually cut its size down by around
25% (I've seen from 12% to 48%, depending on how many redundant context lines
are removed).  Even if the diffs are generated with only 2 lines of context,
the savings still average around 20%.

Keep in mind that *no information is lost* by the conversion process.  Only
the redundancy of having multiple identical context lines.  [...]

I've included:
   o	a patch to make gnudiff (v1.14) generate a unidiff.
   o	a patch to make patch (patchlevel 12) accept a unidiff.
   o	a versatile program called "unify" that can translate from a context
	diff (new- or old-style) into a unidiff, and from a unidiff into a
	true new-style context diff.
   o	a man page for unify.
   o	a 1.3k bandaid called "unipatch" that translates a unidiff into a
	context diff format that older versions of patch can understand.
	(It outputs a slightly degenerate form of a context diff (no '!'s)
	but it works great with patch.)
   o	a Makefile to get you going quickly.
-- 
 \  /| / /|\/ /| /(_)     Wayne Davison
(_)/ |/ /\|/ / |/  \      davison@dri.com
   (W   A  Y   N   e)     ...!uunet!drivax!davison

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	PROPAGANDA
#	README
#	Makefile
#	unify.c
#	unify.1
#	unipatch.c
#	gnudiff.uni
#	patch.uni
# This archive created: Mon Aug 20 22:23:52 1990
# By:	Wayne Davison (Digital Research, Monterey CA)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'PROPAGANDA'" '(1966 characters)'
if test -f 'PROPAGANDA'
then
	echo shar: "will not over-write existing file 'PROPAGANDA'"
else
sed 's/^X//' << \SHAR_EOF > 'PROPAGANDA'
XI've created a new context diff format that combines the old and new hunks into
Xone unified hunk.  The result?  The unified context diff, or "unidiff."
X
XPosting your patch using a unidiff will usually cut its size down by around
X25% (I've seen from 12% to 48%, depending on how many redundant context lines
Xare removed).  Even if the diffs are generated with only 2 lines of context,
Xthe savings still average around 20%.
X
XKeep in mind that *no information is lost* by the conversion process.  Only
Xthe redundancy of having multiple identical context lines.
X
XIf you're worried that some people will be unable to apply a patch released
Xin unidiff format then you could include the "unipatch.c" program -- 1.3k of C
Xsource that converts a unidiff into a context diff that any version of patch
Xcan understand.  If your patch is at least 10k, you'll STILL be saving space.
X
XNot worth the hassle?  I disagree.  The goal of saving net bandwidth (not to
Xmention storage space) is a good one.  The conversion process will take some
Xtime, but I've attempted to make it as painless as possible.  I've included:
X
X   o	a patch to make gnudiff (v1.14) generate a unidiff.
X   o	a patch to make patch (patchlevel 12) accept a unidiff.
X   o	a versatile program called "unify" that can translate from a context
X	diff (new- or old-style) into a unidiff, and from a unidiff into a
X	true new-style context diff.
X   o	a man page for unify.
X   o	a 1.3k bandaid called "unipatch" that translates a unidiff into a
X	context diff format that older versions of patch can understand.
X	(It outputs a slightly degenerate form of a context diff (no '!'s)
X	but it works great with patch.)
X   o	a Makefile to get you going quickly.
X
XUse this package to "unify" your site, and then save it to help others.  Mail
Xthe 1.3k bandaid to anyone who complains about being unable to apply a unidiff,
Xand also offer to supply the whole conversion package if desired.  Soon, the
Xwhole net will be unified. ;-)
SHAR_EOF
fi
echo shar: "extracting 'README'" '(1811 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XThe two patches included in this package are in unidiff format, but they are
Xvery easy to apply with the included utilities.
X
XThe easiest way to get going is to give the included Makefile the once-over
Xand then type:
X
X	make
X
Xand you'll get a runnable version of unify which is used to translate the
Xunidiffs into their context diff counterparts (patch.uni into patch.diff,
Xand gnudiff.uni into gnudiff.diff).  It's all done automatically for you.
X
XThe other alternative is to:
X
X	make unipatch
X
Xand patch the source to patch with:
X
X	unipatch <patch.uni | patch
X
Xand then apply the gnudiff.uni patch using the same method or the new version
Xof patch.
X
XThe file patch.diff expects to find patchlevel 12 of patch, changing the
Xpachlevel to 12u.  The only added option is -u, which forces a patch to
Xbe interpreted as a unidiff.
X
XThe file gnudiff.diff expects to find version 1.14 of gnudiff, changing the
Xversion to 1.14u.  The patch adds the following options:
X
X  -u	generates a unidiff.
X  -P	turns on patch-output mode:
X	o  Outputs an "Index: filename" line instead of the ***/--- header
X	   lines (even for normal and context diffs).  The filename chosen
X	   is the shorter of the two we're diff'ing, and the characters "./"
X	   are skipped if present at the start of the name.
X	o  Doesn't print "Only in <dir>", "Common subdirectories", or
X	   "diff <-opts> <file1> <file2>" messages.
X	o  Turns on the '=' prefix in a unidiff instead of the (more
X	   readable) ' ' for lines that are in common.
X  -U	same as -uP.
X
X  +unidiff  equivilent to 'u'
X  +patch    equivilent to 'P'
X
XSwitches for the unify program are very similar to those for diff, and are
Xspecified in the man page.
X
X \  /| / /|\/ /| /(_)     Wayne Davison
X(_)/ |/ /\|/ / |/  \      davison@dri.com
X   (W   A  Y   N   e)     ...!uunet!drivax!davison
SHAR_EOF
fi
echo shar: "extracting 'Makefile'" '(353 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# A simple makefile for unify, unipatch, and the unidiff patches.
X#
X
XCC= cc
XCFLAGS= -O
X
Xall: patch.diff gnudiff.diff
X
Xpatch.diff: patch.uni unify
X	unify patch.uni >patch.diff
X
Xgnudiff.diff: gnudiff.uni unify
X	unify gnudiff.uni >gnudiff.diff
X
Xunify: unify.c
X	$(CC) $(CFLAGS) -o unify unify.c
X
Xunipatch: unipatch.c
X	$(CC) $(CFLAGS) -o unipatch unipatch.c
SHAR_EOF
fi
echo shar: "extracting 'unify.c'" '(10829 characters)'
if test -f 'unify.c'
then
	echo shar: "will not over-write existing file 'unify.c'"
else
sed 's/^X//' << \SHAR_EOF > 'unify.c'
X/*
X** unify.c - change a diff to/from a context diff from/to a unidiff.
X**
X** Author:  Wayne Davison <davison@dri.com> (uunet!drivax!davison)
X**
X** Feel free to use this code in any way you desire.
X*/
X
X#include <stdio.h>
X
Xextern char *malloc();
X
X#define FIND_NEXT	0
X#define PARSE_UNIDIFF	1
X#define UNI_LINES	2
X#define PARSE_CDIFF	3
X#define PARSE_OLD	4
X#define CHECK_OLD	5
X#define OLD_LINES	6
X#define PARSE_NEW	7
X#define NEW_LINES	8
X
X#define strnEQ(s1,s2,n) (!strncmp(s1,s2,n))
X#define strnNE(s1,s2,n) strncmp(s1,s2,n)
X
Xchar buf[2048];
X
Xstruct liner {
X    struct liner *link;
X    char type;
X    int num;
X    char str[1];
X} root, *head = &root, *hold = &root, *line;
X
Xlong o_first = 0, o_last = -1;
Xlong n_first = 0, n_last = 0;
X
Xlong o_start, o_end, o_line;
Xlong n_start, n_end, n_line;
X
Xlong line_num = 0;
Xint input_type = 0;
Xint output_type = 0;
Xint echo_comments = 0;
Xint strip_comments = 0;
Xint patch_format = 0;
X
Xint state = FIND_NEXT;
Xint found_index = 0;
Xchar name[256] = { '\0' };
X
Xvoid ensure_name(), add_line(), generate_output();
X
Xint
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    char type;
X    char ndiff;		/* Equals '*' when we have a new-style context diff */
X    FILE *fp_in = stdin;
X
X    while (--argc) {
X	if (**++argv == '-') {
X	    while (*++*argv) {
X		switch (**argv) {
X		case 'c':		/* force context diff output */
X		    output_type = 2;
X		    break;
X		case 'e':		/* echo comments to stderr */
X		    echo_comments = 1;
X		    break;
X		case 'p':		/* generate patch format */
X		case 'P':
X		    patch_format = 1;
X		    break;
X		case 's':		/* strip comment lines */
X		    strip_comments = 1;
X		    break;
X		case 'U':		/* force patch-unidiff output */
X		    patch_format = 1;
X		case 'u':		/* force unidiff output */
X		    output_type = 1;
X		    break;
X		default:
X		    fprintf(stderr, "Unknown option: '%c'\n", **argv);
X		    exit(1);
X		}
X	    }
X	} else {
X	    if (fp_in != stdin) {
X		fprintf(stderr, "Only one filename allowed.\n", *argv);
X		exit(1);
X	    }
X	    if ((fp_in = fopen(*argv, "r")) == NULL) {
X		fprintf(stderr, "Unable to open '%s'.\n", *argv);
X		exit(1);
X	    }
X	}
X    }
X
X    while (fgets(buf, sizeof buf, fp_in)) {
X	line_num++;
X      reprocess:
X	switch (state) {
X	case FIND_NEXT:
X	    if (input_type < 2 && strnEQ(buf, "@@ -", 4)) {
X		input_type = 1;
X		if (!output_type) {
X		    output_type = 2;
X		}
X		ensure_name();
X		state = PARSE_UNIDIFF;
X		goto reprocess;
X	    }
X	    if (!(input_type & 1) && strnEQ(buf, "********", 8)) {
X		input_type = 2;
X		if (!output_type) {
X		    output_type = 1;
X		}
X		ensure_name();
X		state = PARSE_OLD;
X		break;
X	    }
X	    if (strnEQ(buf, "Index: ", 7)) {
X		found_index = 1;
X		printf("%s", buf);
X	    } else if (strnEQ(buf, "Prereq: ", 8)) {
X		printf("%s", buf);
X	    } else if (strnEQ(buf, "*** ", 4) || strnEQ(buf, "--- ", 4)) {
X		if (!found_index) {
X		    char *cp;
X		    int len;
X
X		    for (cp=buf+4,len=0; *cp>' ' && len<255; cp++,len++) {
X			;
X		    }
X		    if (!*name || len < strlen(name)) {
X			strncpy(name, buf+4, len);
X			name[len] = '\0';
X		    }
X		}
X		if (!patch_format) {
X		    printf("%s", buf);
X		}
X	    } else if( patch_format
X	     && (strnEQ(buf, "Only in ", 8) || strnEQ(buf, "Common subdir", 13)
X	      || strnEQ(buf, "diff -", 6))) {
X		if (echo_comments) {
X		    fprintf(stderr, "%s%s", strip_comments ? "" : "!!! ", buf);
X		}
X	    } else {
X		if (echo_comments) {
X		    fprintf(stderr, "%s", buf);
X		}
X		if (!strip_comments) {
X		    printf("%s", buf);
X		}
X	    }
X	    break;
X	case PARSE_UNIDIFF:
X	    if (strnNE(buf, "@@ -", 4)) {
X		found_index = 0;
X		*name = '\0';
X		state = FIND_NEXT;
X		goto reprocess;
X	    }
X	    if (sscanf(buf+4, "%ld,%ld +%ld,%ld %c",
X	      &o_start, &o_end, &n_start, &n_end, &type) != 5 || type != '@') {
X		fprintf(stderr, "Invalid unidiff header at line %ld.\n",
X			line_num);
X		exit(1);
X	    }
X	    o_end = (o_start ? o_start + o_end - 1 : 0);
X	    n_end = (n_start ? n_start + n_end - 1 : 0);
X	    o_first = o_start;
X	    n_first = n_start;
X	    if (o_start) {
X		o_line = o_start-1;
X	    } else {
X		o_line = o_last = 0;
X	    }
X	    if (n_start) {
X		n_line = n_start-1;
X	    } else {
X		n_line = n_last = 0;
X	    }
X	    state = UNI_LINES;
X	    break;
X	case UNI_LINES:
X	    switch (*buf) {
X	    case ' ':
X	    case '=':
X		*buf = ' ';
X		o_last = ++o_line;
X		n_last = ++n_line;
X		break;
X	    case '-':
X		o_last = ++o_line;
X		break;
X	    case '+':
X		n_last = ++n_line;
X		break;
X	    default:
X		fprintf(stderr, "Malformed unidiff at line %ld.\n",
X			line_num);
X		exit(1);
X	    }
X	    add_line(*buf, 0, buf+1);
X	    if (o_line == o_end && n_line == n_end) {
X		generate_output();
X		state = PARSE_UNIDIFF;
X	    }
X	    break;
X	case PARSE_CDIFF:
X	    if (strnNE(buf, "********", 8)) {
X		generate_output();
X		found_index = 0;
X		*name = '\0';
X		state = FIND_NEXT;
X		goto reprocess;
X	    }
X	    state = PARSE_OLD;
X	    break;
X	case PARSE_OLD:
X	    ndiff = ' ';
X	    o_start = -1;
X	    if (sscanf(buf, "*** %ld,%ld %c", &o_start, &o_end, &ndiff) < 2) {
X		if (o_start < 0) {
X		    fprintf(stderr,
X			"Context diff missing 'old' header at line %ld.\n",
X			line_num);
X		    exit(1);
X		}
X		o_end = o_start;
X		ndiff = ' ';
X	    }
X	    if (o_last >= 0) {
X		if (o_start > o_last) {
X		    generate_output();
X		} else {
X		    ndiff = ' ';
X		    while (head->link && head->link->num != o_start) {
X			head = head->link;
X		    }
X		}
X	    }
X	    o_line = o_start-1;
X	    n_line = 0;
X	    if (!o_first) {
X		o_first = o_start;
X	    }
X	    if (!o_start) {
X		state = PARSE_NEW;
X	    } else {
X		state = CHECK_OLD;
X	    }
X	    break;
X	case CHECK_OLD:
X	    if (strnEQ(buf, "--- ", 4)) {
X		state = PARSE_NEW;
X	    } else {
X		state = OLD_LINES;
X		hold = head;
X	    }
X	    goto reprocess;
X	case OLD_LINES:
X	    if (buf[0] == '\n') {
X		strcpy(buf, "  \n");
X	    }
X	    if (buf[1] == '\n') {
X		strcpy(buf+1, " \n");
X	    }
X	    if (buf[1] != ' ') {
X		fprintf(stderr, "Malformed context diff at line %ld.\n",
X			line_num);
X		exit(1);
X	    }
X	    switch (*buf) {
X	    case ' ':
X		type = ' ';
X		n_line++;
X		o_line++;
X		break;
X	    case '-':
X	    case '!':
X		type = '-';
X		o_line++;
X		break;
X	    default:
X		fprintf(stderr, "Malformed context diff at line %ld.\n",
X			line_num);
X		exit(1);
X	    }
X	    if (o_line > o_last) {
X		add_line(type, 0, buf+2);
X		o_last = o_line;
X		n_last = n_line;
X	    } else {
X		do {
X		    hold = hold->link;
X		} while (hold->type == '+');
X		if (type != ' ') {
X		    hold->type = type;
X		    hold->num = 0;
X		}
X	    }
X	    if (o_line == o_end) {
X		state = PARSE_NEW;
X	    }
X	    break;
X	case PARSE_NEW:
X	    if (*buf == '\n') {
X		break;
X	    }
X	    n_start = -1;
X	    if (sscanf(buf, "--- %ld,%ld", &n_start, &n_end) != 2) {
X		if (n_start < 0) {
X		    fprintf(stderr,
X			"Context diff missing 'new' header at line %ld.\n",
X			line_num);
X		    exit(1);
X		}
X		n_end = n_start;
X	    }
X	    n_last = n_line;
X	    o_line = o_start ? o_start-1 : 0;
X	    n_line = n_start ? n_start-1 : 0;
X	    n_last += n_line;
X	    hold = head;
X	    if (!n_first) {
X		n_first = n_start;
X		while (hold->link && hold->link->type == '-') {
X		    hold = hold->link;
X		    hold->num = ++o_line;
X		}
X	    }
X	    if (ndiff == '*' && n_last == n_end) {
X		state = PARSE_CDIFF;
X		break;
X	    }
X	    state = NEW_LINES;
X	    break;
X	case NEW_LINES:
X	    if (buf[0] == '\n') {
X		strcpy(buf, "  \n");
X	    }
X	    if (buf[1] == '\n') {
X		strcpy(buf+1, " \n");
X	    }
X	    if (buf[1] != ' ') {
X		fprintf(stderr, "Malformed context diff at line %ld.\n",
X			line_num);
X		exit(1);
X	    }
X	    switch (*buf) {
X	    case ' ':
X		type = ' ';
X		n_line++;
X		o_line++;
X		break;
X	    case '+':
X	    case '!':
X		type = '+';
X		n_line++;
X		break;
X	    default:
X		fprintf(stderr, "Malformed context diff at line %ld.\n",
X			line_num);
X		exit(1);
X	    }
X	    if (o_line > o_last) {
X		o_last = o_line;
X		add_line(type, o_line, buf+2);
X		n_last++;
X	    } else if (type != ' ') {
X		add_line(type, 0, buf+2);
X		n_last++;
X	    } else {
X		hold = hold->link;
X		hold->num = o_line;
X		while (hold->link && !hold->link->num
X		    && hold->link->type != ' ') {
X		    hold = hold->link;
X		    if (hold->type == '-') {
X			hold->num = ++o_line;
X		    }
X		}
X	    }
X	    if (o_line == o_end && n_line == n_end) {
X		state = PARSE_CDIFF;
X	    }
X	    break;
X	}/* switch */
X    }/* while */
X    generate_output();
X
X    return 0;
X}
X
Xvoid
Xensure_name()
X{
X    char *cp = name;
X
X    if (!found_index) {
X	if (!*name) {
X	    fprintf(stderr,
X		"Couldn't find a name for the diff at line %ld.\n",
X		line_num);
X	} else if (patch_format) {
X	    if (cp[0] == '.' && cp[1] == '/') {
X		cp += 2;
X	    }
X	    printf("Index: %s\n", cp);
X	}
X    }
X}
X
Xvoid
Xadd_line(type, num, str)
Xchar type;
Xint num;
Xchar *str;
X{
X    line = (struct liner *)malloc(sizeof (struct liner) + strlen(str));
X    if (!line) {
X	fprintf(stderr, "Out of memory!\n");
X	exit(1);
X    }
X    line->type = type;
X    line->num = num;
X    strcpy(line->str, str);
X    line->link = hold->link;
X    hold->link = line;
X    hold = line;
X}
X
Xvoid
Xgenerate_output()
X{
X    if (o_last < 0) {
X	return;
X    }
X    if (output_type == 1) {
X	int i, j;
X
X	i = o_first ? o_last - o_first + 1 : 0;
X	j = n_first ? n_last - n_first + 1 : 0;
X	printf("@@ -%ld,%ld +%ld,%ld @@\n", o_first, i, n_first, j);
X	for (line = root.link; line; line = hold) {
X	    printf("%c%s", patch_format && line->type == ' '? '=' : line->type,
X		line->str);
X	    hold = line->link;
X	    free(line);
X	}
X    } else { /* if output == 2 */
X	struct liner *scan;
X	int found_plus = 1;
X	char ch;
X
X	printf("***************\n*** %ld", o_first);
X	if (o_first == o_last) {
X	    printf(" ****\n");
X	} else {
X	    printf(",%ld ****\n", o_last);
X	}
X	for (line = root.link; line; line = line->link) {
X	    if (line->type == '-') {
X		break;
X	    }
X	}
X	if (line) {
X	    found_plus = 0;
X	    ch = ' ';
X	    for (line = root.link; line; line = line->link) {
X		switch (line->type) {
X		case '-':
X		    if (ch != ' ') {
X			break;
X		    }
X		    scan = line;
X		    while ((scan = scan->link) != NULL && scan->type == '-') {
X			;
X		    }
X		    if (scan && scan->type == '+') {
X			do {
X			    scan->type = '!';
X			} while ((scan = scan->link) && scan->type == '+');
X			ch = '!';
X		    } else {
X			ch = '-';
X		    }
X		    break;
X		case '+':
X		case '!':
X		    found_plus = 1;
X		    continue;
X		case ' ':
X		    ch = ' ';
X		    break;
X		}
X		printf("%c %s", ch, line->str);
X	    }/* for */
X	}/* if */
X	if (n_first == n_last) {
X	    printf("--- %ld ----\n", n_first);
X	} else {
X	    printf("--- %ld,%ld ----\n", n_first, n_last);
X	}
X	if (found_plus) {
X	    for (line = root.link; line; line = line->link) {
X		if (line->type != '-') {
X		    printf("%c %s", line->type, line->str);
X		}
X	    }
X	}
X	for (line = root.link; line; line = hold) {
X	    hold = line->link;
X	    free(line);
X	}
X    }/* if output == 2 */
X
X    root.link = NULL;
X    head = &root;
X    hold = &root;
X
X    o_first = 0;
X    n_first = 0;
X    o_last = -1;
X    n_last = 0;
X}
SHAR_EOF
fi
echo shar: "extracting 'unify.1'" '(2370 characters)'
if test -f 'unify.1'
then
	echo shar: "will not over-write existing file 'unify.1'"
else
sed 's/^X//' << \SHAR_EOF > 'unify.1'
X'''
X''' unify.1
X'''
X.de Sp
X.if t .sp .5v
X.if n .sp
X..
X'''
X'''     Set up \*(-- to give an unbreakable dash;
X'''     string Tr holds user defined translation string.
X'''     Bell System Logo is used as a dummy character.
X'''
X.tr \(bs-|\(bv\*(Tr
X.ie n \{\
X.ds -- \(bs-
X.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
X.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
X.ds L" ""
X.ds R" ""
X.ds L' '
X.ds R' '
X'br\}
X.el\{\
X.ds -- \(em\|
X.tr \*(Tr
X.ds L" ``
X.ds R" ''
X.ds L' `
X.ds R' '
X'br\}
X.TH UNIFY 1 LOCAL
X.SH NAME
Xunify - turns context diffs into unidiffs and visa versa
X.SH SYNOPSIS
X.B unify
X[-cepPsuU] [filename]
X.SH DESCRIPTION
X.I Unify
Xwill accept either a regular context diff (old- or new-style) or a unified
Xcontext diff (aka unidiff) as input, and generate either a unidiff or a
Xnew-style context diff as output.
XThe default is to output the opposite style of whatever was input, but this
Xcan be overridden by the
X.B \-c
Xor
X.B \-u
Xoptions.
XIf the source file is not mentioned, it will be read from the standard input.
X.Sp
XVarious other options allow you to echo the non-diff (comment) lines, modify
Xthe diff by removing the comment lines, and/or tweak the diff into a format
Xthat is good for releasing patches.
X.SH OPTIONS
X.TP 5
X.B \-c
Xforces context diff output.
X.TP 5
X.B \-e
Xechoes non-diff (comment) lines.
XIf a comment line is being stripped via the
X.B \-p
Xoption, it is echoed with a preceding \*(L"!!! \*(R".
XIf all comments are being stripped (via the
X.B \-s
Xoption), no special designation is given.
X.TP 5
X.B \-p
Xturns on patch-output mode.  This will do three things:
X.Sp
Xa) transform a header like:
X.Sp
X	*** orig/file	Sat May  5 02:59:37 1990
X.br
X	--- ./file	Sat May  5 03:00:08 1990
X.Sp
Xinto a line of \*(L"Index: file\*(R" -- we choose the shorter name and strip
Xa leading \*(L"./\*(R" sequence if present.
X.Sp
Xb) strip lines that begin with:
X.Sp
X	\*(L"Only in \*(R"
X.br
X	\*(L"Common subdir\*(R"
X.br
X	\*(L"diff -\*(R"
X.Sp
Xc) turn on the \*(L'=\*(R' prefix in a unidiff for lines that are common
Xto both files (instead of the leading space).
X.TP 5
X.B \-P
Xis the same as
X.B \-p
X(for compatibility with gnu diff options).
X.TP 5
X.B \-s
Xstrips non-diff lines (comments).
X.TP 5
X.B \-u
Xforces unidiff output.
X.TP 5
X.B \-U
Xis the same as \-uP.
X.SH AUTHOR
XWayne Davison <davison@dri.com> (uunet!drivax!davison)
SHAR_EOF
fi
echo shar: "extracting 'unipatch.c'" '(1358 characters)'
if test -f 'unipatch.c'
then
	echo shar: "will not over-write existing file 'unipatch.c'"
else
sed 's/^X//' << \SHAR_EOF > 'unipatch.c'
X/*
XA filter to turn a unidiff into a degenerate context diff (no '!'s)
Xfor patch. Author: davison@dri.com (uunet!drivax!davison).
X*/
X#include <stdio.h>
X#define ERR(a) {fputs(a,stderr);exit(1);}
Xstruct Ln {
X	struct Ln *lk;
X	char t;
X	char s[1];
X} r,*h,*ln;
Xchar *malloc();
Xmain()
X{
Xchar bf[2048],*cp,ch;
Xlong os,ol,ns,nl,ne,lncnt=0;
Xfor(;;){
X for(;;){
X	if(!fgets(bf,sizeof bf,stdin)) exit(0);
X	lncnt++;
X	if(!strncmp(bf,"@@ -",4)) break;
X	fputs(bf,stdout);
X }
X if(sscanf(bf+4,"%ld,%ld +%ld,%ld %c",&os,&ol,&ns,&nl,&ch)!=5||ch!='@')
X	goto bad;
X r.lk=0, h= &r, ne=ns+nl-1;
X printf("***************\n*** %ld,%ld ****\n",os,os+ol-(os>0));
X while(ol||nl){
X	if(!fgets(bf,sizeof bf,stdin)){
X		if(nl>2) ERR("Unexpected end of file.\n");
X		strcpy(bf," \n");
X	}
X	lncnt++;
X	if(*bf=='\t'||*bf=='\n')
X		ch=' ', cp=bf;
X	else
X		ch= *bf, cp=bf+1;
X	switch(ch){
X	case'-':if(!ol--) goto bad;
X		printf("- %s",cp);
X		break;
X	case'=':ch=' ';
X	case' ':if(!ol--) goto bad;
X		printf("  %s",cp);
X	case'+':if(!nl--) goto bad;
X		ln = (struct Ln*)malloc(sizeof(*ln)+strlen(cp));
X		if(!ln) ERR("Out of memory!\n");
X		ln->lk=0, ln->t=ch, strcpy(ln->s,cp);
X		h->lk=ln, h=ln;
X		break;
X	default:
X	bad:	fprintf(stderr,"Malformed unidiff at line %ld: ",lncnt);
X		ERR(bf);
X	}
X }
X printf("--- %ld,%ld ----\n",ns,ne);
X for(ln=r.lk;ln;ln=h){
X	printf("%c %s",ln->t,ln->s);
X	h=ln->lk;
X	free(ln);
X }
X}
X}
SHAR_EOF
fi
echo shar: "extracting 'gnudiff.uni'" '(7823 characters)'
if test -f 'gnudiff.uni'
then
	echo shar: "will not over-write existing file 'gnudiff.uni'"
else
sed 's/^X//' << \SHAR_EOF > 'gnudiff.uni'
XIndex: version.c
XPrereq: 1.14";
X@@ -1,3 +1,3 @@
X=/* Version number of GNU diff.  */
X=
X-char *version_string = "1.14";
X+char *version_string = "1.14u";
XIndex: analyze.c
X@@ -751,5 +751,5 @@
X=    {
X=      setup_output (files[0].name, files[1].name, depth);
X-      if (output_style == OUTPUT_CONTEXT)
X+      if (output_style == OUTPUT_CONTEXT || output_patch_flag)
X=	print_context_header (files);
X=
XIndex: context.c
X@@ -22,4 +22,5 @@
X=
X=static void pr_context_hunk ();
X+static void pr_unidiff_hunk ();
X=static struct change *find_hunk ();
X=static void mark_ignorable ();
X@@ -38,8 +39,19 @@
X=     struct file_data *inf;
X={
X-  fprintf (outfile, "*** %s\t%s", inf[0].name,
X+  if (output_patch_flag)
X+    {
X+      char *cp = strlen (inf[0].name) <= strlen (inf[1].name)
X+		? inf[0].name : inf[1].name;
X+      if (cp[0] == '.' && cp[1] == '/')
X+	cp += 2;
X+      fprintf (outfile, "Index: %s\n", cp);
X+    }
X+  else
X+    {
X+      fprintf (outfile, "*** %s\t%s", inf[0].name,
X=	   ctime (&inf[0].stat.st_mtime));
X-  fprintf (outfile, "--- %s\t%s", inf[1].name,
X+      fprintf (outfile, "--- %s\t%s", inf[1].name,
X=	   ctime (&inf[1].stat.st_mtime));
X+    }
X=}
X=
X@@ -62,5 +74,8 @@
X=  find_function_last_match = -1;
X=
X-  print_script (script, find_hunk, pr_context_hunk);
X+  if (unidiff_flag)
X+    print_script (script, find_hunk, pr_unidiff_hunk);
X+  else
X+    print_script (script, find_hunk, pr_context_hunk);
X=}
X=
X@@ -189,4 +204,125 @@
X=
X=	  print_1_line (prefix, &files[1].linbuf[i]);
X+	}
X+    }
X+}
X+
X+/* Print a pair of line numbers with a comma, translated for file FILE.
X+   If the second number is smaller, use the first in place of it.
X+
X+   Args A and B are internal line numbers.
X+   We print the translated (real) line numbers.  */
X+
X+static void
X+print_unidiff_number_range (file, a, b)
X+     struct file_data *file;
X+     int a, b;
X+{
X+  int trans_a, trans_b;
X+  translate_range (file, a, b, &trans_a, &trans_b);
X+
X+  /* Note: we can have B < A in the case of a range of no lines.
X+     In this case, we should print the line number before the range,
X+     which is B.  */
X+  if (trans_b < trans_a)
X+    fprintf (outfile, "%d,0", trans_b);
X+  else
X+    fprintf (outfile, "%d,%d", trans_a, trans_b - trans_a + 1);
X+}
X+
X+/* Print a portion of an edit script in unidiff format.
X+   HUNK is the beginning of the portion to be printed.
X+   The end is marked by a `link' that has been nulled out.
X+
X+   Prints out lines from both files, and precedes each
X+   line with the appropriate flag-character.  */
X+
X+static void
X+pr_unidiff_hunk (hunk)
X+     struct change *hunk;
X+{
X+  int first0, last0, first1, last1, show_from, show_to, i, j, k;
X+  struct change *next;
X+  int lastline;
X+  char *function;
X+  int function_length;
X+
X+  /* Determine range of line numbers involved in each file.  */
X+
X+  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
X+
X+  if (!show_from && !show_to)
X+    return;
X+
X+  /* Include a context's width before and after.  */
X+
X+  first0 = max (first0 - context, 0);
X+  first1 = max (first1 - context, 0);
X+  last0 = min (last0 + context, files[0].buffered_lines - 1);
X+  last1 = min (last1 + context, files[1].buffered_lines - 1);
X+
X+  /* If desired, find the preceding function definition line in file 0.  */
X+  function = 0;
X+  if (function_regexp)
X+    find_function (&files[0], first0, &function, &function_length);
X+
X+  /* If we looked for and found a function this is part of,
X+     include its name in the header of the diff section.  */
X+
X+  fprintf (outfile, "@@ -");
X+  print_unidiff_number_range (&files[0], first0, last0);
X+  fprintf (outfile, " +");
X+  print_unidiff_number_range (&files[1], first1, last1);
X+  fprintf (outfile, " @@");
X+
X+  if (function)
X+    {
X+      putc (' ', outfile);
X+      fwrite (function, 1, min (function_length - 1, 40), outfile);
X+    }
X+  putc ('\n', outfile);
X+
X+  next = hunk;
X+  i = first0;
X+  j = first1;
X+
X+  while (i <= last0 || j <= last1)
X+    {
X+
X+      /* If the line isn't a difference, output the context from file 0. */
X+
X+      if (!next || i < next->line0)
X+	{
X+	  if (output_patch_flag)
X+	    putc ('=', outfile);
X+	  else
X+	    putc (' ', outfile);
X+	  print_1_line ("", &files[0].linbuf[i++]);
X+	  j++;
X+	}
X+      else
X+	{
X+	  /* For each difference, first output the deleted part. */
X+
X+	  k = next->deleted;
X+	  while (k--)
X+	    {
X+	      putc ('-', outfile);
X+	      print_1_line ("", &files[0].linbuf[i++]);
X+	    }
X+
X+	  /* Then output the inserted part. */
X+
X+	  k = next->inserted;
X+	  while (k--)
X+	    {
X+	      putc ('+', outfile);
X+	      print_1_line ("", &files[1].linbuf[j++]);
X+	    }
X+
X+	  /* We're done with this hunk, so on to the next! */
X+
X+	  next = next->link;
X+
X=	}
X=    }
XIndex: diff.c
X@@ -96,4 +96,6 @@
X=  {"expand-tabs", 0, 0, 't'},
X=  {"ignore-all-space", 0, 0, 'w'},
X+  {"unidiff", 0, 0, 'u'},
X+  {"patch", 0, 0, 'P'},
X=  {"version", 0, 0, 'v'},
X=  {0, 0, 0, 0}
X@@ -133,4 +135,6 @@
X=  ifdef_string = NULL;
X=  heuristic = FALSE;
X+  unidiff_flag = 0;
X+  output_patch_flag = 0;
X=  dir_start_file = NULL;
X=  msg_chain = NULL;
X@@ -141,5 +145,5 @@
X=
X=  while ((c = getopt_long (argc, argv,
X-			   "0123456789abBcC:dD:efF:hHiI:lnNpqrsS:tTvw",
X+			   "0123456789abBcC:dD:efF:hHiI:lnNpPqrsS:tTuUvw",
X=			   longopts, &longind)) != EOF)
X=    {
X@@ -284,4 +288,8 @@
X=	  break;
X=
X+	case 'P':
X+	  output_patch_flag = 1;
X+	  break;
X+
X=	case 'q':
X=	  no_details_flag = 1;
X@@ -322,4 +330,15 @@
X=	  break;
X=
X+	case 'U':
X+	  /* Choose patch-style unidiff. */
X+	  output_patch_flag = 1;
X+
X+	  /* Falls through.  */
X+	case 'u':
X+	  /* Output the context diff in unidiff format.  */
X+	  specify_style (OUTPUT_CONTEXT);
X+	  unidiff_flag = 1;
X+	  break;
X+
X=	case 'w':
X=	  /* Ignore horizontal whitespace when comparing lines.  */
X@@ -378,5 +397,5 @@
X={
X=  fprintf (stderr, "\
X-Usage: diff [-#] [-abBcdefhHilnNprstTvw] [-C lines] [-F regexp] [-I regexp]\n\
X+Usage: diff [-#] [-abBcdefhHilnNpPrstTuUvw] [-C lines] [-F regexp] [-I regexp]\n\
X=       [-S file] [-D symbol] [+ignore-blank-lines] [+context[=lines]]\n\
X=       [+ifdef symbol] [+show-function-line regexp] [+speed-large-files]\n");
X@@ -388,5 +407,5 @@
X=       [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]\n\
X=       [+report-identical-files] [+expand-tabs] [+ignore-all-space]\n\
X-       [+version] path1 path2\n");
X+       [+unidiff] [+patch] [+version] path1 path2\n");
X=  exit (1);
X=} 
X@@ -429,5 +448,6 @@
X=      char *name = name0 == 0 ? name1 : name0;
X=      char *dir = name0 == 0 ? dir1 : dir0;
X-      message ("Only in %s: %s\n", dir, name);
X+      if (!output_patch_flag)
X+	message ("Only in %s: %s\n", dir, name);
X=      /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
X=      return 1;
X@@ -535,5 +555,6 @@
X=	  /* But don't compare dir contents one level down
X=	     unless -r was specified.  */
X-	  message ("Common subdirectories: %s and %s\n",
X+	  if (!output_patch_flag)
X+	    message ("Common subdirectories: %s and %s\n",
X=		   inf[0].name, inf[1].name);
X=	  val = 0;
X@@ -601,5 +622,6 @@
X=	    {
X=	      char *dir = (inf[0].desc == -1) ? dir1 : dir0;
X-	      message ("Only in %s: %s\n", dir, name0);
X+	      if (!output_patch_flag)
X+		message ("Only in %s: %s\n", dir, name0);
X=	      val = 1;
X=	    }
XIndex: diff.h
X@@ -185,4 +185,10 @@
X=EXTERN int	heuristic;
X=
X+/* Do we want to output the context diff in unidiff style? */
X+EXTERN int	unidiff_flag;
X+
X+/* Reduce extraneous output when they're outputting a patch. */
X+EXTERN int	output_patch_flag;
X+
X=/* Name of program the user invoked (for error messages).  */
X=EXTERN char *	program;
XIndex: util.c
X@@ -170,5 +170,5 @@
X=      /* If handling multiple files (because scanning a directory),
X=	 print which files the following output is about.  */
X-      if (depth > 0)
X+      if (depth > 0 && !output_patch_flag)
X=	printf ("%s\n", name);
X=    }
SHAR_EOF
fi
echo shar: "extracting 'patch.uni'" '(6767 characters)'
if test -f 'patch.uni'
then
	echo shar: "will not over-write existing file 'patch.uni'"
else
sed 's/^X//' << \SHAR_EOF > 'patch.uni'
XIndex: patchlevel.h
XPrereq: 12
X@@ -1,1 +1,1 @@
X-#define PATCHLEVEL 12
X+#define PATCHLEVEL "12u"
XIndex: common.h
X@@ -135,4 +135,5 @@
X=#define ED_DIFF 3
X=#define NEW_CONTEXT_DIFF 4
X+#define UNI_DIFF 5
X=EXT int diff_type INIT(0);
X=
XIndex: patch.c
X@@ -1,4 +1,4 @@
X=char rcsid[] =
X-	"$Header: patch.c,v 2.0.1.6 88/06/22 20:46:39 lwall Locked $";
X+	"$Header: patch.c,v 2.0.2.0 90/05/01 22:17:50 davison Locked $";
X=
X=/* patch - a program to apply diffs to original files
X@@ -10,4 +10,7 @@
X= *
X= * $Log:	patch.c,v $
X+ * Revision 2.0.2.0  90/05/01  22:17:50  davison
X+ * patch12u: unidiff support added
X+ * 
X= * Revision 2.0.1.6  88/06/22  20:46:39  lwall
X= * patch12: rindex() wasn't declared
X@@ -458,4 +461,7 @@
X=		skip_rest_of_patch = TRUE;
X=		break;
X+	    case 'u':
X+		diff_type = UNI_DIFF;
X+		break;
X=	    case 'v':
X=		version();
X@@ -530,6 +536,6 @@
X=    LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
X=    LINENUM newlast = newfirst + pch_repl_lines() - 1;
X-    char *stars = (diff_type == NEW_CONTEXT_DIFF ? " ****" : "");
X-    char *minuses = (diff_type == NEW_CONTEXT_DIFF ? " ----" : " -----");
X+    char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
X+    char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
X=
X=    fprintf(rejfp, "***************\n");
XIndex: patch.man
X@@ -81,5 +81,5 @@
X=.SH DESCRIPTION
X=.I Patch
X-will take a patch file containing any of the three forms of difference
X+will take a patch file containing any of the four forms of difference
X=listing produced by the
X=.I diff
X@@ -102,8 +102,10 @@
X=.BR -c ,
X=.BR -e ,
X+.BR -n ,
X=or
X-.B -n
X+.B -u
X=switch.
X-Context diffs and normal diffs are applied by the
X+Context diffs (old-style, new-style, and unified) and
X+normal diffs are applied by the
X=.I patch
X=program itself, while ed diffs are simply fed to the
X@@ -377,4 +379,9 @@
X=.sp
X=will ignore the first and second of three patches.
X+.TP 5
X+.B \-u
X+forces
X+.I patch
X+to interpret the patch file as a unified context diff (a unidiff).
X=.TP 5
X=.B \-v
XIndex: pch.c
X@@ -2,4 +2,7 @@
X= *
X= * $Log:	pch.c,v $
X+ * Revision 2.0.2.0  90/05/01  22:17:51  davison
X+ * patch12u: unidiff support added
X+ *
X= * Revision 2.0.1.7  88/06/03  15:13:28  lwall
X= * patch10: Can now find patches in shar scripts.
X@@ -163,4 +166,5 @@
X=	say3("  %sooks like %s to me...\n",
X=	    (p_base == 0L ? "L" : "The next patch l"),
X+	    diff_type == UNI_DIFF ? "a unidiff" :
X=	    diff_type == CONTEXT_DIFF ? "a context diff" :
X=	    diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
X@@ -287,4 +291,13 @@
X=	    goto scan_exit;
X=	}
X+	if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
X+	    if (!atol(s+3))
X+		ok_to_create_file = TRUE;
X+	    p_indent = indent;
X+	    p_start = this_line;
X+	    p_sline = p_input_line;
X+	    retval = UNI_DIFF;
X+	    goto scan_exit;
X+	}
X=	stars_this_line = strnEQ(s, "********", 8);
X=	if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
X@@ -720,4 +733,146 @@
X=	    assert(filldst==p_end+1 || filldst==repl_beginning);
X=	}
X+    }
X+    else if (diff_type == UNI_DIFF) {
X+	long line_beginning = ftell(pfp);
X+					/* file pos of the current line */
X+	Reg4 LINENUM fillsrc;		/* index of old lines */
X+	Reg5 LINENUM filldst;		/* index of new lines */
X+	char ch;
X+
X+	ret = pgets(buf, sizeof buf, pfp);
X+	p_input_line++;
X+	if (ret == Nullch || strnNE(buf, "@@ -", 4)) {
X+	    next_intuit_at(line_beginning,p_input_line);
X+	    return FALSE;
X+	}
X+	s = buf+4;
X+	if (!*s)
X+	    goto malformed;
X+	p_first = (LINENUM) atol(s);
X+	while (isdigit(*s)) s++;
X+	if (*s != ',' || !*++s)
X+	    goto malformed;
X+	p_ptrn_lines = (LINENUM) atol(s);
X+	while (isdigit(*s)) s++;
X+	if (*s == ' ') s++;
X+	if (*s != '+' || !*++s)
X+	    goto malformed;
X+	p_newfirst = (LINENUM) atol(s);
X+	while (isdigit(*s)) s++;
X+	if (*s != ',' || !*++s)
X+	    goto malformed;
X+	p_repl_lines = (LINENUM) atol(s);
X+	while (isdigit(*s)) s++;
X+	if (*s == ' ') s++;
X+	if (*s != '@')
X+	    goto malformed;
X+	if (!p_first && !p_ptrn_lines)
X+	    p_first = 1;
X+	p_max = p_ptrn_lines + p_repl_lines;
X+	while (p_max >= hunkmax)
X+	    grow_hunkmax();
X+	p_max = hunkmax;
X+	fillsrc = 1;
X+	filldst = fillsrc + p_ptrn_lines;
X+	p_end = filldst + p_repl_lines;
X+	Sprintf(buf,"*** %ld,%ld ****\n",p_first,p_first + p_ptrn_lines - 1);
X+	p_line[0] = savestr(buf);
X+	if (out_of_mem) {
X+	    p_end = -1;
X+	    return FALSE;
X+	}
X+	p_char[0] = '*';
X+        Sprintf(buf,"--- %ld,%ld ----\n",p_newfirst,p_newfirst+p_repl_lines-1);
X+	p_line[filldst] = savestr(buf);
X+	if (out_of_mem) {
X+	    p_end = 0;
X+	    return FALSE;
X+	}
X+	p_char[filldst++] = '=';
X+	p_context = 100;
X+	context = 0;
X+	p_hunk_beg = p_input_line + 1;
X+	while (fillsrc <= p_ptrn_lines || filldst <= p_end) {
X+	    line_beginning = ftell(pfp);
X+	    ret = pgets(buf, sizeof buf, pfp);
X+	    p_input_line++;
X+	    if (ret == Nullch) {
X+		if (p_max - filldst < 3)
X+		    Strcpy(buf, " \n");  /* assume blank lines got chopped */
X+		else {
X+		    fatal1("Unexpected end of file in patch.\n");
X+		}
X+	    }
X+	    if (*buf == '\t' || *buf == '\n') {
X+		ch = ' ';		/* assume the space got eaten */
X+		s = savestr(buf);
X+	    }
X+	    else {
X+		ch = *buf;
X+		s = savestr(buf+1);
X+	    }
X+	    if (out_of_mem) {
X+		while (--filldst > p_ptrn_lines)
X+		    free(p_line[filldst]);
X+		p_end = fillsrc-1;
X+		return FALSE;
X+	    }
X+	    switch (ch) {
X+	    case '-':
X+		if (fillsrc > p_ptrn_lines) {
X+		    free(s);
X+		    p_end = filldst-1;
X+		    goto malformed;
X+		}
X+		p_char[fillsrc] = ch;
X+		p_line[fillsrc] = s;
X+		p_len[fillsrc++] = strlen(s);
X+		break;
X+	    case '=':
X+		ch = ' ';
X+		/* FALL THROUGH */
X+	    case ' ':
X+		if (fillsrc > p_ptrn_lines) {
X+		    free(s);
X+		    while (--filldst > p_ptrn_lines)
X+			free(p_line[filldst]);
X+		    p_end = fillsrc-1;
X+		    goto malformed;
X+		}
X+		context++;
X+		p_char[fillsrc] = ch;
X+		p_line[fillsrc] = s;
X+		p_len[fillsrc++] = strlen(s);
X+		s = savestr(s);
X+		if (out_of_mem) {
X+		    while (--filldst > p_ptrn_lines)
X+			free(p_line[filldst]);
X+		    p_end = fillsrc-1;
X+		    return FALSE;
X+		}
X+		/* FALL THROUGH */
X+	    case '+':
X+		if (filldst > p_end) {
X+		    free(s);
X+		    while (--filldst > p_ptrn_lines)
X+			free(p_line[filldst]);
X+		    p_end = fillsrc-1;
X+		    goto malformed;
X+		}
X+		p_char[filldst] = ch;
X+		p_line[filldst] = s;
X+		p_len[filldst++] = strlen(s);
X+		break;
X+	    default:
X+		p_end = filldst;
X+		goto malformed;
X+	    }
X+	    if (ch != ' ' && context > 0) {
X+		if (context < p_context)
X+		    p_context = context;
X+		context = -1000;
X+	    }
X+	}/* while */
X=    }
X=    else {				/* normal diff--fake it up */
XIndex: version.c
X@@ -24,5 +24,5 @@
X=    rcsid[0] = rcsid[0];
X=#else
X-    fatal3("%s\nPatch level: %d\n", rcsid, PATCHLEVEL);
X+    fatal3("%s\nPatch level: %s\n", rcsid, PATCHLEVEL);
X=#endif
X=}
SHAR_EOF
fi
exit 0
#	End of shell archive