Plan 9 from Bell Labs’s /usr/web/sources/contrib/geoff/rmsame.bun

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


# To unbundle, run this file
mkdir rmsame
echo rmsame/README
sed 's/^X//' >rmsame/README <<'!'
cleanname.c is a Plan 9 source file, so cleanname.NOTICE
and cleanname.LICENSE apply to it.  The terms are fairly
liberal; you may redistribute it, modify it or sell it,
but read the terms to be certain.
!
echo rmsame/cleanname.LICENSE
sed 's/^X//' >rmsame/cleanname.LICENSE <<'!'
The Plan 9 software is provided under the terms of the
Lucent Public License, Version 1.02, reproduced below,
with the following exceptions:

1. No right is granted to create derivative works of or
X   to redistribute (other than with the Plan 9 Operating System)
X   the screen imprinter fonts identified in subdirectory
X   /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida
X   Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans
X   Typewriter83), identified in subdirectory /sys/lib/postscript/font.
X   These directories contain material copyrights by B&H Inc. and Y&Y Inc.

2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font
X   are subject to the GNU GPL, reproduced in the file /LICENSE.gpl.

3. The ghostscript program in the subdirectory /sys/src/cmd/gs is
X   covered by the Aladdin Free Public License, reproduced in the file
X   /LICENSE.afpl.

X===================================================================

Lucent Public License Version 1.02

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

1. DEFINITIONS

X"Contribution" means:

X  a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
X     Program, and
X  b. in the case of each Contributor,

X     i. changes to the Program, and
X    ii. additions to the Program;

X    where such changes and/or additions to the Program were added to the
X    Program by such Contributor itself or anyone acting on such
X    Contributor's behalf, and the Contributor explicitly consents, in
X    accordance with Section 3C, to characterization of the changes and/or
X    additions as Contributions.

X"Contributor" means LUCENT and any other entity that has Contributed a
Contribution to the Program.

X"Distributor" means a Recipient that distributes the Program,
modifications to the Program, or any part thereof.

X"Licensed Patents" mean patent claims licensable by a Contributor
which are necessarily infringed by the use or sale of its Contribution
alone or when combined with the Program.

X"Original Program" means the original version of the software
accompanying this Agreement as released by LUCENT, including source
code, object code and documentation, if any.

X"Program" means the Original Program and Contributions or any part
thereof

X"Recipient" means anyone who receives the Program under this
Agreement, including all Contributors.

2. GRANT OF RIGHTS

X a. Subject to the terms of this Agreement, each Contributor hereby
X    grants Recipient a non-exclusive, worldwide, royalty-free copyright
X    license to reproduce, prepare derivative works of, publicly display,
X    publicly perform, distribute and sublicense the Contribution of such
X    Contributor, if any, and such derivative works, in source code and
X    object code form.
X    
X b. Subject to the terms of this Agreement, each Contributor hereby
X    grants Recipient a non-exclusive, worldwide, royalty-free patent
X    license under Licensed Patents to make, use, sell, offer to sell,
X    import and otherwise transfer the Contribution of such Contributor, if
X    any, in source code and object code form. The patent license granted
X    by a Contributor shall also apply to the combination of the
X    Contribution of that Contributor and the Program if, at the time the
X    Contribution is added by the Contributor, such addition of the
X    Contribution causes such combination to be covered by the Licensed
X    Patents. The patent license granted by a Contributor shall not apply
X    to (i) any other combinations which include the Contribution, nor to
X    (ii) Contributions of other Contributors. No hardware per se is
X    licensed hereunder.
X    
X c. Recipient understands that although each Contributor grants the
X    licenses to its Contributions set forth herein, no assurances are
X    provided by any Contributor that the Program does not infringe the
X    patent or other intellectual property rights of any other entity. Each
X    Contributor disclaims any liability to Recipient for claims brought by
X    any other entity based on infringement of intellectual property rights
X    or otherwise. As a condition to exercising the rights and licenses
X    granted hereunder, each Recipient hereby assumes sole responsibility
X    to secure any other intellectual property rights needed, if any. For
X    example, if a third party patent license is required to allow
X    Recipient to distribute the Program, it is Recipient's responsibility
X    to acquire that license before distributing the Program.

X d. Each Contributor represents that to its knowledge it has sufficient
X    copyright rights in its Contribution, if any, to grant the copyright
X    license set forth in this Agreement.

3. REQUIREMENTS

A. Distributor may choose to distribute the Program in any form under
this Agreement or under its own license agreement, provided that:

X a. it complies with the terms and conditions of this Agreement;

X b. if the Program is distributed in source code or other tangible
X    form, a copy of this Agreement or Distributor's own license agreement
X    is included with each copy of the Program; and

X c. if distributed under Distributor's own license agreement, such
X    license agreement:

X      i. effectively disclaims on behalf of all Contributors all warranties
X         and conditions, express and implied, including warranties or
X         conditions of title and non-infringement, and implied warranties or
X         conditions of merchantability and fitness for a particular purpose;
X     ii. effectively excludes on behalf of all Contributors all liability
X         for damages, including direct, indirect, special, incidental and
X         consequential damages, such as lost profits; and
X    iii. states that any provisions which differ from this Agreement are
X         offered by that Contributor alone and not by any other party.

B. Each Distributor must include the following in a conspicuous
X   location in the Program:

X   Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
X   Reserved.

C. In addition, each Contributor must identify itself as the
originator of its Contribution in a manner that reasonably allows
subsequent Recipients to identify the originator of the Contribution.
Also, each Contributor must agree that the additions and/or changes
are intended to be a Contribution. Once a Contribution is contributed,
it may not thereafter be revoked.

4. COMMERCIAL DISTRIBUTION

Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use
of the Program, the Distributor who includes the Program in a
commercial product offering should do so in a manner which does not
create potential liability for Contributors. Therefore, if a
Distributor includes the Program in a commercial product offering,
such Distributor ("Commercial Distributor") hereby agrees to defend
and indemnify every Contributor ("Indemnified Contributor") against
any losses, damages and costs (collectively"Losses") arising from
claims, lawsuits and other legal actions brought by a third party
against the Indemnified Contributor to the extent caused by the acts
or omissions of such Commercial Distributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement.
In order to qualify, an Indemnified Contributor must: a) promptly
notify the Commercial Distributor in writing of such claim, and b)
allow the Commercial Distributor to control, and cooperate with the
Commercial Distributor in, the defense and any related settlement
negotiations. The Indemnified Contributor may participate in any such
claim at its own expense.

XFor example, a Distributor might include the Program in a commercial
product offering, Product X. That Distributor is then a Commercial
Distributor. If that Commercial Distributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Distributor's responsibility
alone. Under this section, the Commercial Distributor would have to
defend claims against the Contributors related to those performance
claims and warranties, and if a court requires any Contributor to pay
any damages as a result, the Commercial Distributor must pay those
damages.

5. NO WARRANTY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement, including but not limited to
the risks and costs of program errors, compliance with applicable
laws, damage to or loss of data, programs or equipment, and
unavailability or interruption of operations.

6. DISCLAIMER OF LIABILITY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), 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 OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. EXPORT CONTROL

Recipient agrees that Recipient alone is responsible for compliance
with the United States export administration regulations (and the
export control laws and regulation of any other countries).

8. GENERAL

If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.

If Recipient institutes patent litigation against a Contributor with
respect to a patent applicable to software (including a cross-claim or
counterclaim in a lawsuit), then any patent licenses granted by that
Contributor to such Recipient under this Agreement shall terminate as
of the date such litigation is filed. In addition, if Recipient
institutes patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Program
itself (excluding combinations of the Program with other software or
hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.

All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of
time after becoming aware of such noncompliance. If all Recipient's
rights under this Agreement terminate, Recipient agrees to cease use
and distribution of the Program as soon as reasonably practicable.
However, Recipient's obligations under this Agreement and any licenses
granted by Recipient relating to the Program shall continue and
survive.

LUCENT may publish new versions (including revisions) of this
Agreement from time to time. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new
version of the Agreement is published, Contributor may elect to
distribute the Program (including its Contributions) under the new
version. No one other than LUCENT has the right to modify this
Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
Recipient receives no rights or licenses to the intellectual property
of any Contributor under this Agreement, whether expressly, by
implication, estoppel or otherwise. All rights in the Program not
expressly granted under this Agreement are reserved.

This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No
party to this Agreement will bring a legal action under this Agreement
more than one year after the cause of action arose. Each party waives
its rights to a jury trial in any resulting litigation.

!
echo rmsame/cleanname.NOTICE
sed 's/^X//' >rmsame/cleanname.NOTICE <<'!'
Copyright © 2002 Lucent Technologies Inc.
All Rights Reserved
!
echo rmsame/cleanname.c
sed 's/^X//' >rmsame/cleanname.c <<'!'
X/*
X * In place, rewrite name to compress multiple /, eliminate ., and process ..
X */
X#define SEP(x)	((x)=='/' || (x) == 0)
char*
cleanname(char *name)
X{
X	char *p, *q, *dotdot;
X	int rooted;

X	rooted = name[0] == '/';

X	/*
X	 * invariants:
X	 *	p points at beginning of path element we're considering.
X	 *	q points just past the last path element we wrote (no slash).
X	 *	dotdot points just past the point where .. cannot backtrack
X	 *		any further (no slash).
X	 */
X	p = q = dotdot = name+rooted;
X	while(*p) {
X		if(p[0] == '/')	/* null element */
X			p++;
X		else if(p[0] == '.' && SEP(p[1]))
X			p += 1;	/* don't count the separator in case it is nul */
X		else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) {
X			p += 2;
X			if(q > dotdot) {	/* can backtrack */
X				while(--q > dotdot && *q != '/')
X					;
X			} else if(!rooted) {	/* /.. is / but ./../ is .. */
X				if(q != name)
X					*q++ = '/';
X				*q++ = '.';
X				*q++ = '.';
X				dotdot = q;
X			}
X		} else {	/* real path element */
X			if(q != name+rooted)
X				*q++ = '/';
X			while((*q = *p) != '/' && *q != 0)
X				p++, q++;
X		}
X	}
X	if(q == name)	/* empty string is really ``.'' */
X		*q++ = '.';
X	*q = '\0';
X	return name;
X}
!
echo rmsame/mkfile
sed 's/^X//' >rmsame/mkfile <<'!'
X</$objtype/mkfile

TARG=rmsame
OFILES=rmsame.$O cleanname.$O
HFILES=
BIN=$home/bin/$objtype

X</sys/src/cmd/mkone
!
echo rmsame/rmsame.c
sed 's/^X//' >rmsame/rmsame.c <<'!'
X/*
X * rmsame [-npsv] directory - remove files in . identical to those in directory.
X *
X * rmsame recursively descends the current directory subtree and
X * removes any files that are identical to files present in the
X * comparison directory.  rmsame refuses to "rmsame ." ("rm -rf ."  is
X * faster and has the same effect).  symbolic links to directories are
X * not followed.  In cases of doubt, the file is left alone.
X *
X * changed to read an entire directory's contents first, then process files,
X * instead of reading one directory entry and then processing it.
X * since rmsame may remove files as it runs, this helps to avoid missing
X * directory entries.
X *	by Geoff Collyer, April 1996
X * Copyright (C) 1986,1987	Ron Wessels
X * based on a shell script by Geoff Collyer
X *
X * Permission is hereby granted for general usage and re-distribution of
X * this program as long as:
X *	a) it is not sold for profit
X *	b) the author's name and this notice remain intact in the program
X */

X#include <u.h>
X#include <libc.h>

X#define STREQ(a, b)	(*(a) == *(b) && strcmp(a, b) == 0)

X#ifndef MAXPATHLEN
X#define MAXPATHLEN 1024
X#endif

enum {
X	Differ = 0,
X	Same,
X	Missing,
X	Ignore,

X	/*
X	 * size of comparison buffers: too large means reading blocks
X	 * unnecessarily when files differ early on; too small means
X	 * many reads when files are identical.
X	 */
X	Bufsize = 4*1024,
X};

extern	char*	cleanname(char*);

char *argv0 = "rmsame";

typedef struct dirname Dirname;
struct dirname {
X	char	*name;
X	Dirname	*next;
X};
typedef struct {
X	Dirname	*dirnm;
X	int	opened;
X	int	count;
X} Rddirres;

static char cmpfile[MAXPATHLEN+1];
X/*
X * holds full path of current directory at all times.
X * used for error messages, not traversal.
X */
static char fullcurrdir[MAXPATHLEN+1];
X/*
X * always contains current directory, but may be just the name of the last
X * component or may be the full path, depending upon the slow flag.
X */
static char currdir[MAXPATHLEN+1];
static Dir filestat, cmpstat;
static Dir *filedir, *cmpdir, *dotdir;

X/*
X * Execution flags.
X */
static int verbose = 0;		/* print out progress */
X/*
X * don't use "..".  no longer needed on Plan 9 &
X * probably only rarely needed on Unix
X */
static int slow = 0;
static int prdiff, prsame, dontrm;

static void rmsame(void);
static int cmpplain(char *file1name, char *file2name);

int
samefiledirs(Dir *da, Dir *db)
X{
X	return da != nil && db != nil &&
X		da->qid.type == db->qid.type &&
X		da->qid.path == db->qid.path &&
X		da->qid.vers == db->qid.vers &&
X		da->dev  == db->dev &&
X		da->type == db->type;
X}

int
samefile(char *a, char *b)
X{
X	int ret;
X	Dir *da, *db;

X	if(strcmp(a, b) == 0)
X		return 1;
X	da = dirstat(a);
X	db = dirstat(b);
X	ret = samefiledirs(da, db);
X	free(da);
X	free(db);
X	return ret;
X}

static int
iscurrdir(char *dir, Dir *dotstp)
X{
X	USED(dotstp);
X	return samefile(dir, ".");
X}

static int
chgdir(char *dir)
X{
X	int ret;
X	char okname[MAXPATHLEN+1];

X	if (dir[0] == '#') {  /* beware loonie component names on plan 9 */
X		sprint(okname, "./%s", dir);
X		dir = okname;
X	}
X	ret = chdir(dir);
X	if (ret < 0)
X		fprint(2, "%s: chdir(%s) in %s: %r\n", argv0, dir, fullcurrdir);
X	return ret;
X}

X/*
X * Get an absolute path for the comparison directory,
X * and make sure it is not ".".
X */
static void
setup(char *dirname)
X{
X	dotdir = dirstat(".");
X	if (dotdir == nil)
X		sysfatal("stat of . at start: %r");
X	if (verbose)
X		fprint(2, "getting current directory\n");
X	if (getwd(currdir, sizeof currdir) == nil) {
X		/* sometimes life is just too complex for getwd */
X		char *pwd = getenv("PWD");

X		fprint(2, "%s: getcwd failed\n", argv0);
X		if (pwd != nil && iscurrdir(pwd, dotdir)) {
X			strncpy(currdir, pwd, sizeof currdir);
X			/* getcwd may have changed curr. dir. */
X			if (chgdir(pwd) < 0)
X				sysfatal("can't chdir to initial dir");
X		} else
X			sysfatal("can't get current dir");
X	}
X	strcpy(fullcurrdir, currdir);

X	cmpfile[0] = '\0';
X	if (dirname[0] != '/') {
X		strcat(cmpfile, currdir);
X		strcat(cmpfile, "/");
X	}
X	strcat(cmpfile, dirname);
X	cleanname(cmpfile);
X	cmpdir = dirstat(cmpfile);
X	if (cmpdir == nil)
X		sysfatal("stat(%s) in %s: %r", cmpfile, fullcurrdir);
X	if (!(cmpdir->mode&DMDIR))
X		sysfatal("%s: not a directory", cmpfile);
X	if (iscurrdir(cmpfile, dotdir))
X		sysfatal("%s is \".\"", dirname);
X}

void
main(int argc, char *argv[])
X{
X	int errflag = 0;

X	ARGBEGIN {
X	case 'n':
X		dontrm = prdiff = 1;
X		break;
X	case 'p':
X		dontrm = prsame = 1;
X		break;
X	case 's':
X		slow++;		/* but robust */
X		break;
X	case 'v':
X		verbose++;
X		break;
X	case '?':
X	default:
X		errflag++;
X		break;
X	} ARGEND;
X	if (errflag || argc != 1)
X		sysfatal("Usage: %s [-npsv] directory", argv0);
X	setup(argv[0]);

X	if (verbose)
X		fprint(2, "beginning descent\n");
X	rmsame();
X	exits(0);
X}

static int
dispose(char *compname, int same)
X{
X	int ret = 0;

X	if (dontrm) {
X		if (prdiff && (same == Differ || Same == Missing) ||
X		    prsame && same == Same)
X			print("%s\n", cmpfile);
X	} else if (same == Same) {
X		ret = remove(compname);
X		if (ret < 0)
X			fprint(2, "%s: unlink(%s) in %s: %r\n",
X				argv0, compname, fullcurrdir);
X		else if (verbose)
X			print("rm %s\n", compname);
X	}
X	return ret;
X}

static Rddirres
readalldir(char *name)
X{
X	int dir, n;
X	Rddirres res;
X	Dir *dirs, *dp;
X	Dirname *newdir, *curdir = nil;

X	werrstr("");
X	res.dirnm = nil;
X	res.opened = res.count = 0;
X	dir = open(name, OREAD);
X	if (dir < 0)
X		return res;
X	res.opened++;

X	/* read the whole directory into memory */
X	while ((n = dirread(dir, &dirs)) > 0) {
X		for (dp = dirs; n-- > 0; dp++) {
X			newdir = malloc(sizeof *newdir);
X			if (newdir == nil)
X				abort();
X			newdir->name = strdup(dp->name);
X			newdir->next = nil;
X			if (res.dirnm == nil)
X				res.dirnm = newdir;
X			else
X				curdir->next = newdir;
X			curdir = newdir;
X			res.count++;
X		}
X		free(dirs);
X	}
X	close(dir);
X	return res;
X}

static void
freealldir(Dirname *alldir)
X{
X	Dirname *next, *dp;

X	for (dp = alldir; dp != nil; dp = next) {
X		next = dp->next;
X		dp->next = nil;
X		free(dp->name);
X		dp->name = nil;
X		free(dp);
X	}
X}

X/*
X * Recursively rmsame the sub-directory.
X */
static int
samedir(char *compname, char *cdp)
X{
X	char *dirname;

X	*cdp = '/';
X	strcpy(cdp+1, compname);	/* maintain full path to . */
X	if (slow)
X		dirname = fullcurrdir;
X	else
X		dirname = compname;
X	strcpy(currdir, dirname);
X	if (verbose)
X		fprint(2, "cd %s\n", dirname);
X	if (chgdir(dirname) < 0)	/* descend */
X		return Differ;		/* no perms, perhaps */

X	rmsame();

X	*cdp = '\0';			/* maintain full path to . */
X	if (!slow)
X		dirname = "..";
X	if (verbose)
X		fprint(2, "cd %s\n", dirname);
X	if (chgdir(dirname) < 0)	/* climb back up */
X		exits("chdir failed: %r"); /* something is ill */

X	/* Remove the directory entry if empty. */
X	if (!dontrm && remove(compname) == 0 && verbose)
X		fprint(2, "rmdir %s\n", compname);
X	return Ignore;
X}

static int
compare(char *compname, char *cmpname, char *cdp)
X{
X	/*
X	 * skip to next file if: ".", "..", either is non-existent,
X	 * differing file types, or matching device & i-number
X	 * (which means we got tricked by symbolic links or namespace
X	 * mapping) and link count is 1 (the last link).
X	 */
X	if (STREQ(compname, ".") || STREQ(compname, ".."))
X		return Ignore;
X	strcpy(cmpname, compname);

X	free(filedir);
X	filedir = dirstat(compname);
X	if (filedir == nil) {		/* vanished since dirread? */
X		fprint(2, "stat(%s) in %s: %r\n", compname, fullcurrdir);
X		return Missing;
X	}

X	free(cmpdir);
X	cmpdir = dirstat(cmpfile);
X	if (cmpdir == nil)		/* not in other tree */
X		return Missing;

X	if ((filedir->mode&DMDIR) != (cmpdir->mode&DMDIR) ||
X	    samefiledirs(filedir, cmpdir)) {
X		if (verbose)
X			print("ignoring %s\n", compname);
X		return Differ;
X	}

X	switch (filedir->mode&DMDIR) {
X	case DMDIR:
X		/* Recursively rmsame the sub-directory. */
X		return samedir(compname, cdp);
X	case 0:
X		return filedir->length == cmpdir->length &&
X		    cmpplain(compname, cmpfile) == Same;
X	}
X}

static void
examine(char *compname, char *cmpname, char *cdp)
X{
X	char okname[MAXPATHLEN+1];

X	if (compname[0] == '#') {  /* beware loonie component names on plan 9 */
X		sprint(okname, "./%s", compname);
X		compname = okname;
X	}
X	if (verbose)
X		fprint(2, "name: %s\n", compname);
X	dispose(compname, compare(compname, cmpname, cdp));
X}

static void
rmsame(void)
X{
X	char *cmpname, *cdp;
X	Dirname *dp;
X	Rddirres dirres;

X	cmpname = &cmpfile[strlen(cmpfile)];
X	*cmpname++ = '/';
X	cdp = fullcurrdir + strlen(fullcurrdir);

X	dirres = readalldir(".");
X	if (!dirres.opened) {
X		fprint(2, "%s: opendir(.) in %s: %r\n", argv0, fullcurrdir);
X		/*
X		 * this is probably a permission problem, so give up on this
X		 * directory but keep going.
X		 */
X	} else {
X		for (dp = dirres.dirnm; dp != nil; dp = dp->next)
X			examine(dp->name, cmpname, cdp);
X		freealldir(dirres.dirnm);
X	}
X	*cmpname = '\0';
X}

X/* read with warning if error */
static int
wread(int fd, void *buf, long len, char *name)
X{
X	int bytes = read(fd, buf, len);

X	if (bytes < 0)
X		fprint(2, "%s: read(%s) in %s: %r\n", argv0, name,
X			fullcurrdir);
X	return bytes;
X}

X/* open with warning if error */
static int
wopen(char *name, int mode)
X{
X	int fd = open(name, mode);

X	if (fd < 0)
X		fprint(2, "%s: open(%s) in %s: %r\n", argv0, name,
X			fullcurrdir);
X	return fd;
X}

static int
cmpfds(char *name1, char *name2, int fd1, int fd2)
X{
X	int cnt1, cnt2;
X	char buf1[Bufsize], buf2[Bufsize];

X	if (fd1 < 0 || fd2 < 0)
X		return Differ;
X	do {
X		cnt1 = wread(fd1, buf1, sizeof buf1, name1);
X		cnt2 = wread(fd2, buf2, sizeof buf2, name2);
X		if (cnt1 < 0 || cnt2 < 0)
X			return Differ;
X		if (cnt1 != cnt2) {		/* can't happen */
X			fprint(2,
X				"%s: read differing amounts from %s and %s\n",
X				argv0, name1, name2);
X			return Differ;
X		}
X		if (cnt1 == 0)			/* both at EOF? */
X			return Same;
X	} while (memcmp(buf1, buf2, cnt1) == 0);
X	return Differ;
X}

static int
cmpplain(char *name1, char *name2)
X{
X	int fd1, fd2, result = Differ;

X	fd1 = wopen(name1, OREAD);
X	fd2 = wopen(name2, OREAD);
X	if (fd1 >= 0 && fd2 >= 0)
X		result = cmpfds(name1, name2, fd1, fd2);
X	if (fd1 >= 0)
X		close(fd1);
X	if (fd2 >= 0)
X		close(fd2);
X	return result;
X}
!
echo rmsame/rmsame.man
sed 's/^X//' >rmsame/rmsame.man <<'!'
X.TH RMSAME 1
X.SH NAME
rmsame \- remove identical files
X.SH SYNOPSIS
X.B rmsame
X[
X.B \-npsv
X]
X.I directory
X.SH DESCRIPTION
X.B rmsame
will recursively compare files from the current directory
X(and below) with corresponding files from the
X.I directory
argument.
When files are identical, the local copy is deleted.
After a subdirectory has been processed, it is removed if it is empty.
X.PP
This program is useful when maintaining a system that is constantly
receiving new software releases.
Instead of installing local revisions repeatedly into the new releases,
one could load a new release into a scratch disk and run rmsame to
leave only those files that have changed.
These can then be examined and processed manually.
X.SH OPTIONS
X.PD 0
X.TP 5
X.B \-n
don't remove any files, just print the names of those which differ from those
under
X.IR directory .
X.TP 5
X.B \-p
don't remove any files, just print the names of files identical to those under
X.IR directory .
X.TP 5
X.B \-s
traverse directory trees slowly but reliably.
X.TP 5
X.B \-v
show the processing as it is taking place
X(normally processing is silent except for error messages).
X.PD
X.SH "SEE ALSO"
X.IR cmp (1),
X.IR rm (1),
X.IR rmdir (1)
X.SH DIAGNOSTICS
X.TP 5
X\fIname\fP is "."
The directory given as the comparison directory is also the current directory.
The command "rm\ \-r\ ." has the same effect, and is much faster.
X.SH BUGS
If the directory structure is too deep, the program runs out of
file descriptors and exits.
X.PP
A symbolic link is considered to be only identical to another
symbolic link pointing to the same name.
If the names are different, no check is made to see if the
paths are really the same.
X.SH AUTHORS
The original
X.B rmsame
was a shell script written by Geoff\ Collyer.
It was later re-written as a C program by Ron\ Wessels.
!

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.