[BACK]Return to diffdir.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / diff

File: [local] / src / usr.bin / diff / diffdir.c (download)

Revision 1.13, Wed Jun 25 22:14:43 2003 UTC (20 years, 11 months ago) by millert
Branch: MAIN
Changes since 1.12: +2 -2 lines

Add unidiff support and try to pretty up usage() a bit

/*	$OpenBSD: diffdir.c,v 1.13 2003/06/25 22:14:43 millert Exp $	*/

/*
 * Copyright (C) Caldera International Inc.  2001-2002.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code and documentation must retain the above
 *    copyright notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed or owned by Caldera
 *	International, Inc.
 * 4. Neither the name of Caldera International, Inc. nor the names of other
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
 * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/wait.h>

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "diff.h"

#if 0
static const char sccsid[] = "@(#)diffdir.c	4.12 (Berkeley) 4/30/89";
#endif

/*
 * diff - directory comparison
 */
#define	d_flags	d_ino

#define	ONLY	1		/* Only in this directory */
#define	SAME	2		/* Both places and same */
#define	DIFFER	4		/* Both places and different */
#define	DIRECT	8		/* Directory */

struct dir {
	u_long d_ino;
	short d_reclen;
	short d_namlen;
	char *d_entry;
};

int header;
static int dirstatus;		/* exit status from diffdir */
extern int status;
char title[2 * BUFSIZ];
char *etitle;
char *prargs[] = {"pr", "-h", 0, "-f", 0, 0};


static struct dir *setupdir(char *);
static int ascii(int);
static void compare(struct dir *);
static void calldiff(char *);
static void setfile(char **fpp, char **epp, char *file);
static int useless(char *);
static void only(struct dir *dp, int which);
static void scanpr(struct dir *, int, char *, char *, char *, char *, char *);
static int entcmp(const void *, const void *);

void
diffdir(char **argv)
{
	struct dir *dir1, *dir2;
	struct dir *d1, *d2;
	int i, cmp;

	if (opt == D_IFDEF)
		warnx("can't specify -I with directories");
	if (opt == D_EDIT && (sflag || lflag))
		warnx("warning: shouldn't give -s or -l with -e");
	strlcpy(title, "diff ", sizeof title);
	for (i = 1; diffargv[i + 2]; i++) {
		if (!strcmp(diffargv[i], "-"))
			continue;	/* was -S, dont look silly */
		strlcat(title, diffargv[i], sizeof title);
		strlcat(title, " ", sizeof title);
	}
	for (etitle = title; *etitle; etitle++)
		;
	setfile(&file1, &efile1, file1);
	setfile(&file2, &efile2, file2);
	argv[0] = file1;
	argv[1] = file2;
	dir1 = setupdir(file1);
	dir2 = setupdir(file2);
	d1 = dir1;
	d2 = dir2;
	while (d1->d_entry != 0 || d2->d_entry != 0) {
		if (d1->d_entry && useless(d1->d_entry)) {
			d1++;
			continue;
		}
		if (d2->d_entry && useless(d2->d_entry)) {
			d2++;
			continue;
		}
		if (d1->d_entry == 0)
			cmp = 1;
		else if (d2->d_entry == 0)
			cmp = -1;
		else
			cmp = strcmp(d1->d_entry, d2->d_entry);
		if (cmp < 0) {
			if (lflag)
				d1->d_flags |= ONLY;
			else if (opt == 0 || opt == 2)
				only(d1, 1);
			d1++;
			dirstatus |= 1;
		} else if (cmp == 0) {
			compare(d1);
			d1++;
			d2++;
		} else {
			if (lflag)
				d2->d_flags |= ONLY;
			else if (opt == 0 || opt == 2)
				only(d2, 2);
			d2++;
			dirstatus |= 1;
		}
	}
	if (lflag) {
		scanpr(dir1, ONLY, "Only in %.*s", file1, efile1, 0, 0);
		scanpr(dir2, ONLY, "Only in %.*s", file2, efile2, 0, 0);
		scanpr(dir1, SAME, "Common identical files in %.*s and %.*s",
		    file1, efile1, file2, efile2);
		scanpr(dir1, DIFFER, "Binary files which differ in %.*s and %.*s",
		    file1, efile1, file2, efile2);
		scanpr(dir1, DIRECT, "Common subdirectories of %.*s and %.*s",
		    file1, efile1, file2, efile2);
	}
	if (rflag) {
		if (header && lflag)
			printf("\f");
		for (d1 = dir1; d1->d_entry; d1++) {
			if ((d1->d_flags & DIRECT) == 0)
				continue;
			strlcpy(efile1, d1->d_entry,
			    file1 + MAXPATHLEN - efile1);
			strlcpy(efile2, d1->d_entry,
			    file2 + MAXPATHLEN - efile2);
			calldiff(0);
		}
	}
	status = dirstatus;
}

void
setfile(char **fpp, char **epp, char *file)
{
	char *cp;
	size_t len;

	if (*file == '\0')
		file = ".";
	*fpp = emalloc(MAXPATHLEN);
	len = strlcpy(*fpp, file, MAXPATHLEN);
	if (len >= MAXPATHLEN - 1)
		errx(1, "%s: %s", file, strerror(ENAMETOOLONG));
	cp = *fpp + len - 1;
	if (*cp == '/')
		++cp;
	else {
		*++cp = '/';
		*++cp = '\0';
	}
	*epp = cp;
}

static void
scanpr(struct dir *dp, int test, char *title, char *file1, char *efile1,
    char *file2, char *efile2)
{
	int titled = 0;

	for (; dp->d_entry; dp++) {
		if ((dp->d_flags & test) == 0)
			continue;
		if (titled == 0) {
			if (header == 0)
				header = 1;
			else
				printf("\n");
			printf(title,
			    efile1 - file1 - 1, file1,
			    efile2 - file2 - 1, file2);
			printf(":\n");
			titled = 1;
		}
		printf("\t%s\n", dp->d_entry);
	}
}

void
only(struct dir *dp, int which)
{
	char *file = which == 1 ? file1 : file2;
	char *efile = which == 1 ? efile1 : efile2;

	printf("Only in %.*s: %s\n", (int)(efile - file - 1), file, dp->d_entry);
}

struct dir *
setupdir(char *cp)
{
	struct dir *dp, *ep;
	struct dirent *rp;
	int nitems;
	DIR *dirp;

	dirp = opendir(cp);
	if (dirp == NULL) {
		warn("%s", cp);
		done(0);
	}
	nitems = 0;
	dp = emalloc(sizeof(struct dir));
	while ((rp = readdir(dirp))) {
		ep = &dp[nitems++];
		ep->d_reclen = rp->d_reclen;
		ep->d_namlen = rp->d_namlen;
		ep->d_entry = 0;
		ep->d_flags = 0;
		if (ep->d_namlen > 0) {
			ep->d_entry = emalloc(ep->d_namlen + 1);
			strlcpy(ep->d_entry, rp->d_name, ep->d_namlen + 1);
		}
		dp = erealloc(dp, (nitems + 1) * sizeof(struct dir));
	}
	dp[nitems].d_entry = 0;	/* delimiter */
	closedir(dirp);
	qsort(dp, nitems, sizeof(struct dir), entcmp);
	return (dp);
}

static int
entcmp(const void *v1, const void *v2)
{
	const struct dir *d1, *d2;

	d1 = v1;
	d2 = v2;
	return (strcmp(d1->d_entry, d2->d_entry));
}

static void
compare(struct dir *dp)
{
	char buf1[BUFSIZ], buf2[BUFSIZ];
	int i, j, f1, f2, fmt1, fmt2;
	struct stat stb1, stb2;

	strlcpy(efile1, dp->d_entry, file1 + MAXPATHLEN - efile1);
	strlcpy(efile2, dp->d_entry, file2 + MAXPATHLEN - efile2);
	f1 = open(file1, 0);
	if (f1 < 0) {
		perror(file1);
		return;
	}
	f2 = open(file2, 0);
	if (f2 < 0) {
		perror(file2);
		close(f1);
		return;
	}
	fstat(f1, &stb1);
	fstat(f2, &stb2);
	fmt1 = stb1.st_mode & S_IFMT;
	fmt2 = stb2.st_mode & S_IFMT;
	if (fmt1 != S_IFREG || fmt2 != S_IFREG) {
		if (fmt1 == fmt2) {
			if (fmt1 != S_IFDIR && stb1.st_rdev == stb2.st_rdev)
				goto same;
			if (fmt1 == S_IFDIR) {
				dp->d_flags = DIRECT;
				if (lflag || opt == D_EDIT)
					goto closem;
				printf("Common subdirectories: %s and %s\n",
				    file1, file2);
				goto closem;
			}
		}
		goto notsame;
	}
	if (stb1.st_size != stb2.st_size)
		goto notsame;
	for (;;) {
		i = read(f1, buf1, BUFSIZ);
		j = read(f2, buf2, BUFSIZ);
		if (i < 0 || j < 0 || i != j)
			goto notsame;
		if (i == 0 && j == 0)
			goto same;
		for (j = 0; j < i; j++)
			if (buf1[j] != buf2[j])
				goto notsame;
	}
same:
	if (sflag == 0)
		goto closem;
	if (lflag)
		dp->d_flags = SAME;
	else
		printf("Files %s and %s are identical\n", file1, file2);
	goto closem;
notsame:
	dirstatus |= 1;
	if (!ascii(f1) || !ascii(f2)) {
		if (lflag)
			dp->d_flags |= DIFFER;
		else if (opt == D_NORMAL || opt == D_CONTEXT || opt == D_UNIFIED)
			printf("Binary files %s and %s differ\n",
			    file1, file2);
		goto closem;
	}
	close(f1);
	close(f2);
	anychange = 1;
	if (lflag)
		calldiff(title);
	else {
		if (opt == D_EDIT) {
			printf("ed - %s << '-*-END-*-'\n", dp->d_entry);
			calldiff(0);
		} else {
			printf("%s%s %s\n", title, file1, file2);
			calldiff(0);
		}
		if (opt == D_EDIT)
			printf("w\nq\n-*-END-*-\n");
	}
	return;
closem:
	close(f1);
	close(f2);
}

static void
calldiff(char *wantpr)
{
	int lstatus, lstatus2, pv[2];
	pid_t pid;

	prargs[2] = wantpr;
	fflush(stdout);
	if (wantpr) {
		snprintf(etitle, title + sizeof title - etitle,
		    "%s %s", file1, file2);
		pipe(pv);
		pid = fork();
		if (pid == -1) {
			warnx("No more processes");
			done(0);
		}
		if (pid == 0) {
			close(0);
			dup(pv[0]);
			close(pv[0]);
			close(pv[1]);
			execv(pr + 4, prargs);
			execv(pr, prargs);
			perror(pr);
			done(0);
		}
	}
	pid = fork();
	if (pid == -1) {
		warnx("No more processes");
		done(0);
	}
	if (pid == 0) {
		if (wantpr) {
			close(1);
			dup(pv[1]);
			close(pv[0]);
			close(pv[1]);
		}
		execv(diff + 4, diffargv);
		execv(diff, diffargv);
		perror(diff);
		done(0);
	}
	if (wantpr) {
		close(pv[0]);
		close(pv[1]);
	}
	while (wait(&lstatus) != pid)
		continue;
	while (wait(&lstatus2) != -1)
		continue;
	/*
		if ((lstatus >> 8) >= 2)
			done(0);
	*/
	dirstatus |= lstatus >> 8;
}

int
ascii(int f)
{
	char buf[BUFSIZ], *cp;
	int cnt;

	lseek(f, (off_t)0, SEEK_SET);
	cnt = read(f, buf, BUFSIZ);
	cp = buf;
	while (--cnt >= 0)
		if (*cp++ & 0200)
			return (0);
	return (1);
}

/*
 * THIS IS CRUDE.
 */
int
useless(char *cp)
{
	if (cp[0] == '.') {
		if (cp[1] == '\0')
			return (1);	/* directory "." */
		if (cp[1] == '.' && cp[2] == '\0')
			return (1);	/* directory ".." */
	}
	if (start && strcmp(start, cp) > 0)
		return (1);
	return (0);
}