[comp.sources.unix] v21i088: Safely rename wildcarded files, Part02/02

rsalz@uunet.uu.net (Rich Salz) (04/10/90)

Submitted-by: Vladimir Lanin <lanin@csd4.cs.nyu.edu>
Posting-number: Volume 21, Issue 88
Archive-name: mmv/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  mmv.c.1
# Wrapped by rsalz@litchi.bbn.com on Mon Apr  9 17:05:23 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'mmv.c.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mmv.c.1'\"
else
echo shar: Extracting \"'mmv.c.1'\" \(49841 characters\)
sed "s/^X//" >'mmv.c.1' <<'END_OF_FILE'
X/*
X	mmv 1.0
X	Copyright (c) 1990 Vladimir Lanin.
X	This program may be freely used and copied on a non-commercial basis.
X	The author assumes no responsibility for any damage or data loss that may
X	result from the use of this program.
X
X	Author may be reached at:
X
X	lanin@csd4.nyu.edu
X
X	Vladimir Lanin
X	330 Wadsworth Ave, Apt 6F,
X	New York, NY 10040
X*/
X
X/*
X	Define SYSV to compile under System V.
X	If your System V has a rename() call, define RENAME.
X	Otherwise, mmv will only be able to rename directories (via option -r)
X	when running as the super-user.
X	There is no reason to set the suid bit on mmv if rename() is available.
X	It is important that mmv not be run with effective uid set
X	to any value other than either the real uid or the super-user.
X	Even when running with effective uid set to super-user,
X	mmv will only perform actions permitted to the real uid.
X
X	Define MSDOS to compile under MS-D*S Turbo C 1.5.
X	If you prefer mmv's output to use /'s instead of \'s under MS-D*S,
X	define SLASH.
X
X	When neither MSDOS nor SYSV are defined, compiles under BSD.
X
X	RENAME is automatically defined under MSDOS and BSD.
X
X	If you are running a (UN*X) system that provides the
X	"struct dirent" readdir() directory reading standard,
X	define DIRENT. Otherwise, mmv uses the BSD-like
X	"struct direct" readdir().
X	If your (UN*X) system has neither of these, get the "dirent"
X	by Doug Gwyn, available as gwyn-dir-lib in volume 9
X	of the comp.sources.unix archives.
X*/
X
Xstatic char USAGE[] =
X#ifdef MSDOS
X
X"Usage: \
X%s [-m|x%s|c|o|a|z] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
X\n\
XUse =N in the ``to'' pattern to get the string matched\n\
Xby the N'th ``from'' pattern wildcard.\n";
X
X#define OTHEROPT (_osmajor < 3 ? "" : "|r")
X
X#else
X
X"Usage: \
X%s [-m|x|r|c|o|a|l%s] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
X\n\
XUse =[l|u]N in the ``to'' pattern to get the [lowercase|uppercase of the]\n\
Xstring matched by the N'th ``from'' pattern wildcard.\n\
X\n\
XA ``from'' pattern containing wildcards should be quoted when given\n\
Xon the command line.\n";
X
X#ifdef SYSV
X#define OTHEROPT ""
X#else
X#define OTHEROPT "|s"
X#endif
X
X#endif
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X
X#ifdef MSDOS
X/* for MS-DOS (under Turbo C 1.5)*/
X
X#include <stdlib.h>
X#include <sys/stat.h>
X#include <dos.h>
X#include <dir.h>
X#include <io.h>
X#include <fcntl.h>
X
X#define ESC '\''
X#ifdef SLASH
X#define SLASH '/'
X#define OTHERSLASH '\\'
X#else
X#define SLASH '\\'
X#define OTHERSLASH '/'
X#endif
X
Xtypedef int DIRID;
Xtypedef int DEVID;
X
Xstatic char TTY[] = "/dev/con";
Xextern unsigned _stklen = 10000;
X
X#define RENAME
X
X#else
X/* for various flavors of UN*X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#include <sys/signal.h>
X#include <fcntl.h>
Xextern char *getenv();
Xextern long lseek();
Xextern char *malloc();
X
X#ifdef DIRENT
X#include <dirent.h>
Xtypedef struct dirent DIRENTRY;
X#else
X#ifdef SYSV
X#include <sys/dir.h>
X/* might need to be changed to <dir.h> */
X#else
X#include <sys/dir.h>
X#endif
Xtypedef struct direct DIRENTRY;
X#endif
X
X#define void char	/* might want to remove this line */
X
X#ifndef O_BINARY
X#define O_BINARY 0
X#endif
X#ifndef R_OK
X#define R_OK 4
X#define W_OK 2
X#define X_OK 1
X#endif
X
X#define ESC '\\'
X#define SLASH '/'
X
Xtypedef ino_t DIRID;
Xtypedef dev_t DEVID;
X
X#define MAXPATH 1024
X
Xstatic char TTY[] = "/dev/tty";
X
X#ifdef SYSV
X/* for System V */
X
Xstruct utimbuf {
X	time_t actime;
X	time_t modtime;
X};
X#define utimes(f, t) utime((f), (t))
X
X
X#else
X/* for BSD */
X
X#define RENAME
X
X#include <sys/time.h>
X
X#endif
X
X#endif
X
X
X#define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c))
X#define myupper(c) (islower(c) ? (c)-'a'+'A' : (c))
X#define STRLEN(s) (sizeof(s) - 1)
X#define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s)))
X
X
X#define DFLT 0x001
X#define NORMCOPY 0x002
X#define OVERWRITE 0x004
X#define NORMMOVE 0x008
X#define XMOVE 0x010
X#define DIRMOVE 0x020
X#define NORMAPPEND 0x040
X#define ZAPPEND 0x080
X#define HARDLINK 0x100
X#define SYMLINK 0x200
X
X#define COPY (NORMCOPY | OVERWRITE)
X#define MOVE (NORMMOVE | XMOVE | DIRMOVE)
X#define APPEND (NORMAPPEND | ZAPPEND)
X#define LINK (HARDLINK | SYMLINK)
X
Xstatic char MOVENAME[] = "mmv";
Xstatic char COPYNAME[] = "mcp";
Xstatic char APPENDNAME[] = "mad";
Xstatic char LINKNAME[] = "mln";
X
X#define ASKDEL 0
X#define ALLDEL 1
X#define NODEL 2
X
X#define ASKBAD 0
X#define SKIPBAD 1
X#define ABORTBAD 2
X
X#define STAY 0
X#define LOWER 1
X#define UPPER 2
X
X#define MAXWILD 20
X#define MAXPATLEN MAXPATH
X#define INITROOM 10
X#define CHUNKSIZE 2048
X#define BUFSIZE 4096
X
X#define FI_STTAKEN 0x01
X#define FI_LINKERR 0x02
X#define FI_INSTICKY 0x04
X#define FI_NODEL 0x08
X#define FI_KNOWWRITE 0x010
X#define FI_CANWRITE 0x20
X#define FI_ISDIR 0x40
X#define FI_ISLNK 0x80
X
Xtypedef struct {
X	char *fi_name;
X	struct rep *fi_rep;
X#ifdef MSDOS
X	char fi_attrib;
X#else
X	short fi_mode;
X	char fi_stflags;
X#endif
X} FILEINFO;
X
X#define DI_KNOWWRITE 0x01
X#define DI_CANWRITE 0x02
X#define DI_CLEANED 0x04
X
Xtypedef struct {
X	DEVID di_vid;
X	DIRID di_did;
X	unsigned di_nfils;
X	FILEINFO **di_fils;
X	char di_flags;
X} DIRINFO;
X
X#define H_NODIR 1
X#define H_NOREADDIR 2
X
Xtypedef struct {
X	char *h_name;
X	DIRINFO *h_di;
X	char h_err;
X} HANDLE;
X
X#define R_ISX 0x01
X#define R_SKIP 0x02
X#define R_DELOK 0x04
X#define R_ISALIASED 0x08
X#define R_ISCYCLE 0x10
X#define R_ONEDIRLINK 0x20
X
Xtypedef struct rep {
X	HANDLE *r_hfrom;
X	FILEINFO *r_ffrom;
X	HANDLE *r_hto;
X	char *r_nto;			/* non-path part of new name */
X	FILEINFO *r_fdel;
X	struct rep *r_first;
X	struct rep *r_thendo;
X	struct rep *r_next;
X	char r_flags;
X} REP;
X
Xtypedef struct {
X	REP *rd_p;
X	DIRINFO *rd_dto;
X	char *rd_nto;
X	unsigned rd_i;
X} REPDICT;
X
Xtypedef struct chunk {
X	struct chunk *ch_next;
X	unsigned ch_len;
X} CHUNK;
X
Xtypedef struct {
X	CHUNK *sl_first;
X	char *sl_unused;
X	int sl_len;
X} SLICER;
X
X
Xstatic void init(/* */);
Xstatic void procargs(/* int argc, char **argv,
X	char **pfrompat, char **ptopat */);
Xstatic void matchpats(/* char *cfrom, char *cto */);
Xstatic int getpat(/* */);
Xstatic int getword(/* char *buf */);
Xstatic void matchpat(/*  */);
Xstatic int parsepat(/*  */);
Xstatic int dostage(/* char *lastend, char *pathend,
X	char **start1, int *len1, int stage, int anylev */);
Xstatic int trymatch(/* FILEINFO *ffrom, char *pat */);
Xstatic int keepmatch(/* FILEINFO *ffrom, char *pathend,
X	int *pk, int needslash, int dirs, int fils */);
Xstatic int badrep(/* HANDLE *hfrom, FILEINFO *ffrom,
X	HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */);
Xstatic int checkto(/* HANDLE *hfrom, char *f,
X	HANDLE **phto, char **pnto, FILEINFO **pfdel */);
Xstatic char *getpath(/* char *tpath */);
Xstatic int badname(/* char *s */);
Xstatic FILEINFO *fsearch(/* char *s, DIRINFO *d */);
Xstatic int ffirst(/* char *s, int n, DIRINFO *d */);
Xstatic HANDLE *checkdir(/* char *p, char *pathend, int which */);
Xstatic void takedir(/*
X	char *p, DIRINFO *di, int sticky
Xor
X	struct ffblk *pff, DIRINFO *di
X*/);
Xstatic int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */);
Xstatic HANDLE *hadd(/* char *n */);
Xstatic int hsearch(/* char *n, int which, HANDLE **ph */);
Xstatic DIRINFO *dadd(/* DEVID v, DIRID d */);
Xstatic DIRINFO *dsearch(/* DEVID v, DIRID d */);
Xstatic int match(/* char *pat, char *s, char **start1, int *len1 */);
Xstatic void makerep(/*  */);
Xstatic void checkcollisions(/*  */);
Xstatic int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */);
Xstatic void findorder(/*  */);
Xstatic void scandeletes(/* int (*pkilldel)(REP *p) */);
Xstatic int baddel(/* REP *p */);
Xstatic int skipdel(/* REP *p */);
Xstatic void nochains(/*  */);
Xstatic void printchain(/* REP *p */);
Xstatic void goonordie(/*  */);
Xstatic void doreps(/*  */);
Xstatic long appendalias(/* REP *first, REP *p, int *pprintaliased */);
Xstatic int movealias(/* REP *first, REP *p, int *pprintaliased */);
Xstatic int snap(/* REP *first, REP *p */);
Xstatic void showdone(/* REP *fin */);
Xstatic void breakout(/*  */);
Xstatic int breakrep(/* */);
Xstatic void breakstat(/* */);
Xstatic void quit(/*  */);
Xstatic int copymove(/* REP *p */);
Xstatic int copy(/* FILENFO *f, long len */);
Xstatic int myunlink(/* char *n, FILEINFO *f */);
Xstatic int getreply(/* char *m, int failact */);
Xstatic void *myalloc(/* unsigned k */);
Xstatic void *challoc(/* int k, int which */);
Xstatic void chgive(/* void *p, unsigned k */);
Xstatic int mygetc(/* */);
X#ifdef MSDOS
Xstatic int leave(/*  */);
Xstatic void cleanup(/*  */);
X#else
Xstatic int getstat(/* char *full, FILEINFO *f */);
Xstatic int dwritable(/* HANDLE *h */);
Xstatic int fwritable(/* char *hname, FILEINFO *f */);
Xstatic void memmove(/* void *to, void *from, int k */);
X#endif
X#ifndef RENAME
Xstatic int rename(/* char *from, char *to */);
X#endif
X
Xstatic int op, badstyle, delstyle, verbose, noex, matchall;
Xstatic int patflags;
X
Xstatic unsigned ndirs = 0, dirroom;
Xstatic DIRINFO **dirs;
Xstatic unsigned nhandles = 0, handleroom;
Xstatic HANDLE **handles;
Xstatic HANDLE badhandle = {"\200", NULL, 0};
Xstatic HANDLE *(lasthandle[2]) = {&badhandle, &badhandle};
Xstatic unsigned nreps = 0;
Xstatic REP hrep, *lastrep = &hrep;
Xstatic CHUNK *freechunks = NULL;
Xstatic SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}};
X
Xstatic int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad;
Xstatic FILE *outfile = stdout;
X
Xstatic char IDF[] = "$$mmvdid.";
Xstatic char TEMP[] = "$$mmvtmp.";
Xstatic char TOOLONG[] = "(too long)";
Xstatic char EMPTY[] = "(empty)";
X
Xstatic char SLASHSTR[] = {SLASH, '\0'};
X
Xstatic char PATLONG[] = "%.40s... : pattern too long.\n";
X
Xstatic char from[MAXPATLEN], to[MAXPATLEN];
Xstatic int fromlen, tolen;
Xstatic char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]);
Xstatic int nwilds[MAXWILD];
Xstatic int nstages;
Xstatic char pathbuf[MAXPATH];
Xstatic char fullrep[MAXPATH + 1];
Xstatic char *(start[MAXWILD]);
Xstatic int len[MAXWILD];
Xstatic char hasdot[MAXWILD];
Xstatic REP mistake;
X#define MISTAKE (&mistake)
X
X#ifdef MSDOS
X
Xstatic int olddevflag, curdisk, maxdisk;
Xstatic struct {
X	char ph_banner[30];
X	char ph_name[9];
X	int ph_dfltop;
X	int ph_safeid;
X	int ph_clustoff;
X	int ph_driveoff;
X	int ph_drivea;
X} patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0};
X
X#define DFLTOP (patch.ph_dfltop)
X#define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff))
X#define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea)
X
X
X#else
X
X#define DFLTOP XMOVE
X
Xstatic char *home;
Xstatic int homelen;
Xstatic int uid, euid, oldumask;
Xstatic DIRID cwdd = -1;
Xstatic DEVID cwdv = -1;
X
X#endif
X
X
Xint main(argc, argv)
X	int argc;
X	char *(argv[]);
X{
X	char *frompat, *topat;
X
X	init();
X	procargs(argc, argv, &frompat, &topat);
X	matchpats(frompat, topat);
X	if (!(op & APPEND))
X		checkcollisions();
X	findorder();
X	if (op & (COPY | LINK))
X		nochains();
X	scandeletes(baddel);
X	goonordie();
X	if (!(op & APPEND) && delstyle == ASKDEL)
X		scandeletes(skipdel);
X	doreps();
X	return(failed ? 2 : nreps == 0 && (paterr || badreps));
X}
X
X
Xstatic void init()
X{
X#ifdef MSDOS
X	curdisk = getdisk();
X	maxdisk = setdisk(curdisk);
X/*
X	Read device availability : undocumented internal MS-DOS function.
X	If (_DX == 0) then \dev\ must precede device names.
X*/
X	bdos(0x37, 0, 2);
X	olddevflag = _DX;
X/*
X	Write device availability: undocumented internal MS-DOS function.
X	Specify \dev\ must precede device names.
X*/
X	bdos(0x37, 0, 3);
X	atexit((atexit_t)cleanup);
X	ctrlbrk((int (*)())breakout);
X#else
X	struct stat dstat;
X
X	if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
X		home = "";
X	if (!stat(".", &dstat)) {
X		cwdd = dstat.st_ino;
X		cwdv = dstat.st_dev;
X	}
X	oldumask = umask(0);
X	euid = geteuid();
X	uid = getuid();
X	signal(SIGINT, breakout);
X#endif
X
X	dirroom = handleroom = INITROOM;
X	dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
X	handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
X	ndirs = nhandles = 0;
X}
X
X
Xstatic void procargs(argc, argv, pfrompat, ptopat)
X	int argc;
X	char **argv;
X	char **pfrompat, **ptopat;
X{
X	char *p, c;
X	char *cmdname = argv[0];
X
X#ifdef MSDOS
X#define CMDNAME (patch.ph_name)
X#else
X#define CMDNAME cmdname
X#endif
X
X	op = DFLT;
X	verbose = noex = matchall = 0;
X	delstyle = ASKDEL;
X	badstyle = ASKBAD;
X	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
X		for (p = *argv + 1; *p != '\0'; p++) {
X			c = mylower(*p);
X			if (c == 'v' && !noex)
X				verbose = 1;
X			else if (c == 'n' && !verbose)
X				noex = 1;
X			else if (c == 'h')
X				matchall = 1;
X			else if (c == 'd' && delstyle == ASKDEL)
X				delstyle = ALLDEL;
X			else if (c == 'p' && delstyle == ASKDEL)
X				delstyle = NODEL;
X			else if (c == 'g' && badstyle == ASKBAD)
X				badstyle = SKIPBAD;
X			else if (c == 't' && badstyle == ASKBAD)
X				badstyle = ABORTBAD;
X			else if (c == 'm' && op == DFLT)
X				op = NORMMOVE;
X			else if (c == 'x' && op == DFLT)
X				op = XMOVE;
X			else if (c == 'r' && op == DFLT)
X				op = DIRMOVE;
X			else if (c == 'c' && op == DFLT)
X				op = NORMCOPY;
X			else if (c == 'o' && op == DFLT)
X				op = OVERWRITE;
X			else if (c == 'a' && op == DFLT)
X				op = NORMAPPEND;
X#ifdef MSDOS
X			else if (c == 'z' && op == DFLT)
X				op = ZAPPEND;
X#else
X			else if (c == 'l' && op == DFLT)
X				op = HARDLINK;
X#ifndef SYSV
X			else if (c == 's' && op == DFLT)
X				op = SYMLINK;
X#endif
X#endif
X			else {
X				fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
X				exit(1);
X			}
X		}
X
X	if (op == DFLT)
X		if (strcmp(cmdname, MOVENAME) == 0)
X			op = XMOVE;
X		else if (strcmp(cmdname, COPYNAME) == 0)
X			op = NORMCOPY;
X		else if (strcmp(cmdname, APPENDNAME) == 0)
X			op = NORMAPPEND;
X		else if (strcmp(cmdname, LINKNAME) == 0)
X			op = HARDLINK;
X		else
X			op = DFLTOP;
X	if (
X		op & DIRMOVE &&
X#ifdef MSDOS
X		_osmajor < 3
X#else
X#ifndef RENAME
X		euid != 0
X#else
X		0
X#endif
X#endif
X	) {
X		fprintf(stderr,
X			"Unable to do directory renames. Option -r refused.\n");
X		quit();
X	}
X
X#ifndef MSDOS
X	if (euid != uid && !(op & DIRMOVE)) {
X		setuid(uid);
X		setgid(getgid());
X	}
X#endif
X
X	if (badstyle != ASKBAD && delstyle == ASKDEL)
X		delstyle = NODEL;
X
X	if (argc == 0)
X		*pfrompat = NULL;
X	else if (argc == 2) {
X		*pfrompat = *(argv++);
X		*ptopat = *(argv++);
X	}
X	else {
X		fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
X		exit(1);
X	}
X}
X
X
Xstatic void matchpats(cfrom, cto)
X	char *cfrom, *cto;
X{
X	if (cfrom == NULL)
X		while (getpat())
X			matchpat();
X	else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
X		printf(PATLONG, cfrom);
X		paterr = 1;
X	}
X	else if ((tolen = strlen(cto)) >= MAXPATLEN) {
X		printf(PATLONG, cto);
X		paterr = 1;
X	}
X	else {
X		strcpy(from, cfrom);
X		strcpy(to, cto);
X		matchpat();
X	}
X}
X
X
Xstatic int getpat()
X{
X	int c, gotit = 0;
X	char extra[MAXPATLEN];
X
X	patflags = 0;
X	do {
X		if ((fromlen = getword(from)) == 0 || fromlen == -1)
X			goto nextline;
X
X		do {
X			if ((tolen = getword(to)) == 0) {
X				printf("%s -> ? : missing replacement pattern.\n", from);
X				goto nextline;
X			}
X			if (tolen == -1)
X				goto nextline;
X		} while (
X			tolen == 2 &&
X			(to[0] == '-' || to[0] == '=') &&
X			(to[1] == '>' || to[1] == '^')
X		);
X		if (getword(extra) == 0)
X			gotit = 1;
X		else if (strcmp(extra, "(*)") == 0) {
X			patflags |= R_DELOK;
X            gotit = (getword(extra) == 0);
X		}
X
Xnextline:
X		while ((c = mygetc()) != '\n' && c != EOF)
X			;
X		if (c == EOF)
X			return(0);
X	} while (!gotit);
X
X	return(1);
X}
X
X
Xstatic int getword(buf)
X	char *buf;
X{
X	int c, prevc, n;
X	char *p;
X
X	p = buf;
X	prevc = ' ';
X	n = 0;
X	while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
X		if (n == -1)
X			continue;
X		if (n == MAXPATLEN - 1) {
X			*p = '\0';
X			printf(PATLONG, buf);
X			n = -1;
X		}
X		*(p++) = c;
X		n++;
X		prevc = c;
X	}
X	*p = '\0';
X	while (c != EOF && isspace(c) && c != '\n')
X		c = mygetc();
X	if (c != EOF)
X		ungetc(c, stdin);
X	return(n);
X}
X
X
Xstatic void matchpat()
X{
X	if (parsepat())
X		paterr = 1;
X	else if (dostage(from, pathbuf, start, len, 0, 0)) {
X		printf("%s -> %s : no match.\n", from, to);
X		paterr = 1;
X	}
X}
X
X
Xstatic int parsepat()
X{
X	char *p, *lastname, c;
X	int totwilds, instage, x, havedot;
X	static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
X
X	lastname = from;
X#ifdef MSDOS
X	havedot = 0;
X	if (from[0] != '\0' && from[1] == ':')
X		lastname += 2;
X#else
X	if (from[0] == '~' && from[1] == SLASH) {
X		if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
X			printf(PATLONG, from);
X			return(-1);
X		}
X		memmove(from + homelen, from + 1, fromlen);
X		memmove(from, home, homelen);
X		lastname += homelen + 1;
X	}
X#endif
X	totwilds = nstages = instage = 0;
X	for (p = lastname; (c = *p) != '\0'; p++)
X		switch (c) {
X#ifdef MSDOS
X		case '.':
X			havedot = 1;
X			break;
X		case OTHERSLASH:
X			*p = SLASH;
X#endif
X 		case SLASH:
X#ifdef MSDOS
X			if (!havedot && lastname != p) {
X				if (fromlen++ == MAXPATLEN) {
X					printf(PATLONG, from);
X					return(-1);
X				}
X				memmove(p + 1, p, strlen(p) + 1);
X				*(p++) = '.';
X			}
X			else
X				havedot = 0;
X#endif
X			lastname = p + 1;
X			if (instage) {
X				if (firstwild[nstages] == NULL)
X					firstwild[nstages] = p;
X				stager[nstages++] = p;
X				instage = 0;
X			}
X			break;
X		case ';':
X			if (lastname != p) {
X				printf("%s -> %s : badly placed ;.\n", from, to);
X				return(-1);
X			}
X		case '!':
X		case '*':
X		case '?':
X		case '[':
X#ifdef MSDOS
X			if ((hasdot[totwilds] = (c == '!')) != 0)
X				havedot = 1;
X#endif
X			if (totwilds++ == MAXWILD) {
X				printf("%s -> %s : too many wildcards.\n", from, to);
X				return(-1);
X			}
X			if (instage) {
X				nwilds[nstages]++;
X				if (firstwild[nstages] == NULL)
X					firstwild[nstages] = p;
X			}
X			else {
X				stagel[nstages] = lastname;
X				firstwild[nstages] = (c == ';' ? NULL : p);
X				nwilds[nstages] = 1;
X				instage = 1;
X			}
X			if (c != '[')
X				break;
X			while ((c = *(++p)) != ']') {
X				switch (c) {
X				case '\0':
X					printf("%s -> %s : missing ].\n", from, to);
X					return(-1);
X#ifdef MSDOS
X				case '.':
X				case ':':
X				case OTHERSLASH:
X#endif
X				case SLASH:
X					printf("%s -> %s : '%c' can not be part of [].\n",
X						from, to, c);
X					return(-1);
X				case ESC:
X					if ((c = *(++p)) == '\0') {
X						printf(TRAILESC, from, to, ESC);
X						return(-1);
X					}
X#ifdef MSDOS
X				default:
X					if (isupper(c))
X						*p = c + ('a' - 'A');
X#endif
X				}
X			}
X			break;
X		case ESC:
X			if ((c = *(++p)) == '\0') {
X				printf(TRAILESC, from, to, ESC);
X				return(-1);
X			}
X#ifdef MSDOS
X		default:
X			if (isupper(c))
X				*p = c + ('a' - 'A');
X#endif
X		}
X
X#ifdef MSDOS
X	if (!havedot && lastname != p) {
X		if (fromlen++ == MAXPATLEN) {
X			printf(PATLONG, from);
X			return(-1);
X		}
X		strcpy(p++, ".");
X	}
X#endif
X
X	if (instage) {
X		if (firstwild[nstages] == NULL)
X			firstwild[nstages] = p;
X		stager[nstages++] = p;
X	}
X	else {
X		stagel[nstages] = lastname;
X		nwilds[nstages] = 0;
X		firstwild[nstages] = p;
X		stager[nstages++] = p;
X	}
X
X	lastname = to;
X#ifdef MSDOS
X	havedot = 0;
X	if (to[0] != '\0' && to[1] == ':')
X		lastname += 2;
X#else
X	if (to[0] == '~' && to[1] == SLASH) {
X		if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
X			printf(PATLONG, to);
X				return(-1);
X		}
X		memmove(to + homelen, to + 1, tolen);
X		memmove(to, home, homelen);
X		lastname += homelen + 1;
X	}
X#endif
X
X	for (p = lastname; (c = *p) != '\0'; p++)
X		switch (c) {
X#ifdef MSDOS
X		case '.':
X			havedot = 1;
X			break;
X		case OTHERSLASH:
X			*p = SLASH;
X#endif
X		case SLASH:
X			if (op & DIRMOVE) {
X				printf("%s -> %s : no path allowed in target under -r.\n",
X					from, to);
X				return(-1);
X			}
X#ifdef MSDOS
X			if (!havedot && lastname != p) {
X				if (tolen++ == MAXPATLEN) {
X					printf(PATLONG, to);
X					return(-1);
X				}
X				memmove(p + 1, p, strlen(p) + 1);
X				*(p++) = '.';
X			}
X			else
X				havedot = 0;
X#endif
X			lastname = p + 1;
X			break;
X		case '=':
X			c = *(++p);
X			if (c == 'l' || c == 'u') {
X#ifdef MSDOS
X				strcpy(p, p + 1);
X				c = *p;
X#else
X				c = *(++p);
X#endif
X			}
X			if (!isdigit(c)) {
X				printf("%s -> %s : expected digit (not '%c') after =.\n",
X					from, to, c);
X				return(-1);
X			}
X			for(x = 0; ;x *= 10) {
X				x += c - '0';
X				c = *(p+1);
X				if (!isdigit(c))
X					break;
X				p++;
X			}
X			if (x < 1 || x > totwilds) {
X				printf("%s -> %s : wildcard %d does not exist.\n",
X					from, to, x);
X				return(-1);
X			}
X#ifdef MSDOS
X			if (hasdot[x - 1])
X				havedot = 1;
X#endif
X			break;
X		case ESC:
X			if ((c = *(++p)) == '\0') {
X				printf(TRAILESC, from, to, ESC);
X				return(-1);
X			}
X		default:
X			if (
X#ifdef MSDOS
X				c <= ' ' || c >= 127 ||
X				strchr(":/\\*?[]=+;,\"|<>", c) != NULL
X#else
X				c & 0x80
X#endif
X			) {
X				printf("%s -> %s : illegal character '%c' (0x%02X).\n",
X					from, to, c, c);
X				return(-1);
X			}
X#ifdef MSDOS
X			if (isupper(c))
X				*p = c + ('a' - 'A');
X#endif
X		}
X
X#ifdef MSDOS
X	if (!havedot && lastname != p) {
X		if (tolen++ == MAXPATLEN) {
X			printf(PATLONG, to);
X			return(-1);
X		}
X		strcpy(p++, ".");
X	}
X#endif
X
X	return(0);
X}
X
X
Xstatic int dostage(lastend, pathend, start1, len1, stage, anylev)
X	char *lastend, *pathend;
X	char **start1;
X	int *len1;
X	int stage;
X	int anylev;
X{
X	DIRINFO *di;
X	HANDLE *h, *hto;
X	int prelen, litlen, nfils, i, k, flags, try;
X	FILEINFO **pf, *fdel;
X	char *nto, *firstesc;
X	REP *p;
X	int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
X
X	wantdirs = !laststage ||
X		(op & (DIRMOVE | SYMLINK)) ||
X		(nwilds[nstages - 1] == 0);
X
X	if (!anylev) {
X		prelen = stagel[stage] - lastend;
X		if (pathend - pathbuf + prelen >= MAXPATH) {
X			printf("%s -> %s : search path after %s too long.\n",
X				from, to, pathbuf);
X			paterr = 1;
X			return(1);
X		}
X		memmove(pathend, lastend, prelen);
X		pathend += prelen;
X		*pathend = '\0';
X		lastend = stagel[stage];
X	}
X
X	if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
X		if (stage == 0 || direrr == H_NOREADDIR) {
X			printf("%s -> %s : directory %s does not %s.\n",
X				from, to, pathbuf, direrr == H_NOREADDIR ?
X				"allow reads/searches" : "exist");
X			paterr = 1;
X		}
X		return(stage);
X	}
X	di = h->h_di;
X
X	if (*lastend == ';') {
X		anylev = 1;
X		*start1 = pathend;
X		*len1 = 0;
X		lastend++;
X	}
X
X	nfils = di->di_nfils;
X
X#ifndef MSDOS
X	if ((op & MOVE) && !dwritable(h)) {
X		printf("%s -> %s : directory %s does not allow writes.\n",
X			from, to, pathbuf);
X		paterr = 1;
X		goto skiplev;
X	}
X#endif
X
X	firstesc = strchr(lastend, ESC);
X	if (firstesc == NULL || firstesc > firstwild[stage])
X		firstesc = firstwild[stage];
X	litlen = firstesc - lastend;
X	pf = di->di_fils + (i = ffirst(lastend, litlen, di));
X	if (i < nfils)
X	do {
X		if (
X			(try = trymatch(*pf, lastend)) != 0 &&
X			(
X				try == 1 ||
X				match(lastend + litlen, (*pf)->fi_name + litlen,
X					start1 + anylev, len1 + anylev)
X			) &&
X			keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
X		) {
X			if (!laststage)
X				ret &= dostage(stager[stage], pathend + k,
X					start1 + nwilds[stage], len1 + nwilds[stage],
X					stage + 1, 0);
X			else {
X				ret = 0;
X				makerep();
X				if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
X					(*pf)->fi_rep = MISTAKE;
X				else {
X					(*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
X					p->r_flags = flags | patflags;
X					p->r_hfrom = h;
X					p->r_ffrom = *pf;
X					p->r_hto = hto;
X					p->r_nto = nto;
X					p->r_fdel = fdel;
X					p->r_first = p;
X					p->r_thendo = NULL;
X					p->r_next = NULL;
X					lastrep->r_next = p;
X					lastrep = p;
X					nreps++;
X				}
X			}
X		}
X		i++, pf++;
X	} while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
X
Xskiplev:
X	if (anylev)
X		for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
X			if (
X				*((*pf)->fi_name) != '.' &&
X#ifdef MSDOS
X				((*pf)->fi_attrib & FA_DIREC) &&
X#endif
X				keepmatch(*pf, pathend, &k, 1, 1, 0)
X			) {
X				*len1 = pathend - *start1 + k;
X				ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
X			}
X
X	return(ret);
X}
X
X
Xstatic int trymatch(ffrom, pat)
X	FILEINFO *ffrom;
X	char *pat;
X{
X	char *p;
X
X	if (ffrom->fi_rep != NULL)
X		return(0);
X
X	p = ffrom->fi_name;
X
X#ifdef MSDOS
X	if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
X		return(strcmp(pat, p) == 0);
X#else
X	if (*p == '.')
X		if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
X			return(strcmp(pat, p) == 0);
X		else if (!matchall && *pat != '.')
X			return(0);
X#endif
X	return(-1);
X}
X
X
Xstatic int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
X	FILEINFO *ffrom;
X	char *pathend;
X	int *pk;
X	int needslash;
X	int dirs, fils;
X{
X	*pk = strlen(ffrom->fi_name);
X	if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
X		*pathend = '\0';
X		printf("%s -> %s : search path %s%s too long.\n",
X			from, to, pathbuf, ffrom->fi_name);
X		paterr = 1;
X		return(0);
X	}
X	strcpy(pathend, ffrom->fi_name);
X#ifdef MSDOS
X	if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
X#else
X	getstat(pathbuf, ffrom);
X	if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
X#endif
X		return(0);
X
X	if (needslash) {
X		strcpy(pathend + *pk, SLASHSTR);
X		(*pk)++;
X	}
X	return(1);
X}
X
X
Xstatic int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
X	HANDLE *hfrom;
X	FILEINFO *ffrom;
X	HANDLE **phto;
X	char **pnto;
X	FILEINFO **pfdel;
X	int *pflags;
X{
X	char *f = ffrom->fi_name;
X
X	*pflags = 0;
X	if (
X#ifdef MSDOS
X		(ffrom->fi_attrib & FA_DIREC) &&
X#else
X		(ffrom->fi_stflags & FI_ISDIR) &&
X#endif
X		!(op & (DIRMOVE | SYMLINK))
X	)
X		printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
X#ifndef MSDOS
X#ifndef SYSV
X	else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
X		printf("%s -> %s : source file is a badly aimed symbolic link.\n",
X			pathbuf, fullrep);
X	else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE)) 
X		printf("%s -> %s : no delete permission for source file.\n",
X			pathbuf, fullrep);
X#endif
X	else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
X		printf("%s -> %s : no read permission for source file.\n",
X			pathbuf, fullrep);
X#endif
X	else if (
X		*f == '.' &&
X		(f[1] == '\0' || strcmp(f, "..") == 0) &&
X		!(op & SYMLINK)
X	)
X		printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
X	else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
X		printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
X	else if (*phto == NULL)
X		printf("%s -> %s : %s.\n", pathbuf, fullrep,
X#ifndef MSDOS
X			direrr == H_NOREADDIR ?
X			"no read or search permission for target directory" :
X#endif
X			"target directory does not exist");
X#ifndef MSDOS
X	else if (!dwritable(*phto))
X		printf("%s -> %s : no write permission for target directory.\n",
X			pathbuf, fullrep);
X#endif
X	else if (
X		(*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
X		(*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
X	)
X		printf("%s -> %s : cross-device move.\n",
X			pathbuf, fullrep);
X#ifndef MSDOS
X	else if (
X		*pflags && (op & MOVE) &&
X		!(ffrom->fi_stflags & FI_ISLNK) &&
X		access(pathbuf, R_OK)
X	)
X		printf("%s -> %s : no read permission for source file.\n",
X			pathbuf, fullrep);
X#ifndef SYSV
X	else if (
X		(op & SYMLINK) &&
X		!(
X			((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
X			*(hfrom->h_name) == SLASH ||
X			(*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
X		)
X	)
X		printf("%s -> %s : symbolic link would be badly aimed.\n",
X			pathbuf, fullrep);
X#endif
X#endif
X	else
X		return(0);
X	badreps++;
X	return(-1);
X}
X
X
Xstatic int checkto(hfrom, f, phto, pnto, pfdel)
X	HANDLE *hfrom;
X	char *f;
X	HANDLE **phto;
X	char **pnto;
X	FILEINFO **pfdel;
X{
X	char tpath[MAXPATH + 1];
X	char *pathend;
X	FILEINFO *fdel;
X	int hlen, tlen;
X
X	if (op & DIRMOVE) {
X		*phto = hfrom;
X		hlen = strlen(hfrom->h_name);
X		pathend = fullrep + hlen;
X		memmove(pathend, fullrep, strlen(fullrep) + 1);
X		memmove(fullrep, hfrom->h_name, hlen);
X		if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
X			*pnto = fdel->fi_name;
X#ifndef MSDOS
X			getstat(fullrep, fdel);
X#endif
X		}
X		else
X			*pnto = mydup(pathend);
X	}
X	else {
X		pathend = getpath(tpath);
X		hlen = pathend - fullrep;
X		*phto = checkdir(tpath, tpath + hlen, 1);
X		if (
X			*phto != NULL &&
X			*pathend != '\0' &&
X			(fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
X#ifdef MSDOS
X			(fdel->fi_attrib & FA_DIREC)
X#else
X			(getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
X#endif
X		) {
X			tlen = strlen(pathend);
X			strcpy(pathend + tlen, SLASHSTR);
X			tlen++;
X			strcpy(tpath + hlen, pathend);
X			pathend += tlen;
X			hlen += tlen;
X			*phto = checkdir(tpath, tpath + hlen, 1);
X		}
X
X		if (*pathend == '\0') {
X			*pnto = f;
X			if (pathend - fullrep + strlen(f) >= MAXPATH) {
X				strcpy(fullrep, TOOLONG);
X				return(-1);
X			}
X			strcat(pathend, f);
X			if (*phto != NULL) {
X				fdel = *pfdel = fsearch(f, (*phto)->h_di);
X#ifndef MSDOS
X				if (fdel != NULL)
X					getstat(fullrep, fdel);
X#endif
X			}
X		}
X		else if (fdel != NULL)
X			*pnto = fdel->fi_name;
X		else
X			*pnto = mydup(pathend);
X	}
X	return(0);
X}
X
X
Xstatic char *getpath(tpath)
X	char *tpath;
X{
X	char *pathstart, *pathend, c;
X
X#ifdef MSDOS
X	if (*fullrep != '\0' && fullrep[1] == ':')
X		pathstart = fullrep + 2;
X	else
X#endif
X		pathstart = fullrep;
X
X	pathend = pathstart + strlen(pathstart) - 1;
X	while (pathend >= pathstart && *pathend != SLASH)
X		--pathend;
X	pathend++;
X
X	c = *pathend;
X	*pathend = '\0';
X	strcpy(tpath, fullrep);
X	*pathend = c;
X	return(pathend);
X}
X
X
Xstatic int badname(s)
X	char *s;
X{
X	char *ext;
X
X	return (
X#ifdef MSDOS
X		*s == ' ' ||
X		*s == '.' ||
X		(ext = strchr(s, '.')) - s >= MAXFILE ||
X		(*ext == '.' && strchr(ext + 1, '.') != NULL) ||
X		strlen(ext) >= MAXEXT ||
X		strncmp(s, IDF, STRLEN(IDF)) == 0
X#else
X		(*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
X		strlen(s) > MAXNAMLEN
X#endif
X	);
X}
X
X
X#ifndef MSDOS
Xstatic int getstat(ffull, f)
X	char *ffull;
X	FILEINFO *f;
X{
X	struct stat fstat;
X	int flags;
X
X	if ((flags = f->fi_stflags) & FI_STTAKEN)
X		return(flags & FI_LINKERR);
X	flags |= FI_STTAKEN;
X#ifdef SYSV
X	if (stat(ffull, &fstat)) {
X		fprintf("Strange, couldn't stat %s.\n", ffull);
X		quit();
X	}
X#else
X	if (lstat(ffull, &fstat)) {
X		fprintf("Strange, couldn't lstat %s.\n", ffull);
X		quit();
X	}
X	if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
X		flags |= FI_NODEL;
X	if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
X		flags |= FI_ISLNK;
X		if (stat(ffull, &fstat)) {
X			f->fi_stflags = flags | FI_LINKERR;
X			return(1);
X		}
X	}
X#endif
X	if ((fstat.st_mode & S_IFMT) == S_IFDIR)
X		flags |= FI_ISDIR;
X	f->fi_stflags = flags;
X	f->fi_mode = fstat.st_mode;
X	return(0);
X}
X
X
Xstatic int dwritable(h)
X	HANDLE *h;
X{
X	char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
X	char *pw = &(h->h_di->di_flags), r;
X
X	if (uid == 0)
X		return(1);
X
X	if (*pw & DI_KNOWWRITE)
X		return(*pw & DI_CANWRITE);
X
X	pathend = p + strlen(p);
X	if (*p == '\0')
X		myp = ".";
X	else if (pathend == p + 1)
X		myp = SLASHSTR;
X	else {
X		lastslash = pathend - 1;
X		*lastslash = '\0';
X		myp = p;
X	}
X	r = !access(myp, W_OK) ? DI_CANWRITE : 0;
X	*pw |= DI_KNOWWRITE | r;
X
X	if (lastslash != NULL)
X		*lastslash = SLASH;
X	return(r);
X}
X
X
Xstatic int fwritable(hname, f)
X	char *hname;
X	FILEINFO *f;
X{
X	int r;
X
X	if (f->fi_stflags & FI_KNOWWRITE)
X		return(f->fi_stflags & FI_CANWRITE);
X
X	strcpy(fullrep, hname);
X	strcat(fullrep, f->fi_name);
X	r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
X	f->fi_stflags |= FI_KNOWWRITE | r;
X	return(r);
X}
X#endif
X
X
Xstatic FILEINFO *fsearch(s, d)
X	char *s;
X	DIRINFO *d;
X{
X	FILEINFO **fils = d->di_fils;
X	int nfils = d->di_nfils;
X	int first, k, last, res;
X
X	for(first = 0, last = nfils - 1;;) {
X		if (last < first)
X			return(NULL);
X		k = (first + last) >> 1;
X		if ((res = strcmp(s, fils[k]->fi_name)) == 0)
X			return(fils[k]);
X		if (res < 0)
X			last = k - 1;
X		else
X			first = k + 1;
X	}
X}
X
X
Xstatic int ffirst(s, n, d)
X	char *s;
X	int n;
X	DIRINFO *d;
X{
X	int first, k, last, res;
X	FILEINFO **fils = d->di_fils;
X	int nfils = d->di_nfils;
X
X	if (nfils == 0 || n == 0)
X		return(0);
X	first = 0;
X	last = nfils - 1;
X	for(;;) {
X		k = (first + last) >> 1;
X		res = strncmp(s, fils[k]->fi_name, n);
X		if (first == last)
X			return(res == 0 ? k : nfils);
X		else if (res > 0)
X			first = k + 1;
X		else
X			last = k;
X	}
X}
X
X
X#ifdef MSDOS
X/* checkdir and takedir for MS-D*S */
X
Xstatic HANDLE *checkdir(p, pathend, which)
X	char *p, *pathend;
X	int which;
X{
X	struct ffblk de;
X	DIRID d;
X	DEVID v;
X	HANDLE *h;
X	char *dirstart = p;
X	int fd;
X	int firstfound;
X	DIRINFO *di;
X
X	if (hsearch(p, which, &h))
X		if (h->h_di == NULL) {
X			direrr = h->h_err;
X			return(NULL);
X		}
X		else
X			return(h);
X
X	if (*p == '\0' || p[1] != ':')
X		v = curdisk;
X	else {
X		dirstart += 2;
X		v = mylower(p[0]) - 'a';
X		if (v < 0 || v >= maxdisk)
X			return(NULL);
X	}
X
X	if (patch.ph_safeid) {
X		strcpy(pathend, IDF);
X		strcpy(pathend + STRLEN(IDF), "*");
X		if (findfirst(p, &de, 0)) {
X			if ((d = ndirs) == 1000) {
X				fprintf(stderr, "Too many different directories.\n");
X				quit();
X			}
X			sprintf(pathend + STRLEN(IDF), "%03d", d);
X			if ((fd = _creat(p, 0)) < 0) {
X				direrr = h->h_err = H_NODIR;
X				return(NULL);
X			}
X			_close(fd);
X			strcpy(pathend, "*.*");
X			if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
X				h->h_di = dadd(v, d);
X			else
X				takedir(&de, h->h_di = dadd(v, d));
X		}
X		else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
X			h->h_di = dirs[d];
X		else {
X			strcpy(pathend, de.ff_name);
X			fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
X			quit();
X		}
X		*pathend = '\0';
X	}
X	else {
X		strcpy(pathend, "*.*");
X		firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
X		*pathend = '\0';
X		if (firstfound) {
X			v = DRIVENO(&de);
X			d = CLUSTNO(&de);
X		}
X		else {
X			strcpy(pathend, "T.D");
X			if (mkdir(p)) {
X				*pathend = '\0';
X				direrr = h->h_err = H_NODIR;
X				return(NULL);
X			}
X			strcpy(pathend, "*.*");
X			firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
X			*pathend = '\0';
X			v = DRIVENO(&de);
X			d = CLUSTNO(&de);
X			rmdir(p);
X			if (!firstfound || d != 0) {
X				fprintf(stderr,
X					"Strange, %s does not seem to be a root dir.\n",
X					p);
X				quit();
X			}
X		}
X
X		if ((di = dsearch(v, d)) == NULL)
X			if (firstfound)
X				takedir(&de, h->h_di = dadd(v, d));
X			else
X				h->h_di = dadd(v, d);
X		else
X			h->h_di = di;
X	}
X
X	return(h);
X}
X
X
Xstatic void takedir(pff, di)
X	struct ffblk *pff;
X	DIRINFO *di;
X{
X	int cnt, room, namlen, needdot;
X	FILEINFO **fils, *f;
X	char c, *p, *p1;
X
X	room = INITROOM;
X	di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
X	cnt = 0;
X	do {
X		if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
X			continue;
X		if (cnt == room) {
X			room *= 2;
X			fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
X			memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
X			chgive(di->di_fils, cnt * sizeof(FILEINFO *));
X			di->di_fils = fils;
X			fils = di->di_fils + cnt;
X		}
X		needdot = 1;
X		for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
X			if (c == '.')
X				needdot = 0;
X		*fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
X		f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
X		for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
X			*(p++) = mylower(c);
X		if (needdot)
X			*(p++) = '.';
X		*p = '\0';
X		f->fi_attrib = pff->ff_attrib;
X		f->fi_rep = NULL;
X		cnt++;
X		fils++;
X	} while (findnext(pff) == 0);
X	qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
X	di->di_nfils = cnt;
X}
X
X#else
X/* checkdir, takedir for Un*x */
X
Xstatic HANDLE *checkdir(p, pathend, which)
X	char *p, *pathend;
X	int which;
X{
X	struct stat dstat;
X	DIRID d;
X	DEVID v;
X	DIRINFO **newdirs, *di;
X	int nfils;
X	FILEINFO **fils;
X	char *myp, *lastslash = NULL;
X	int sticky;
X	HANDLE *h;
X
X	if (hsearch(p, which, &h))
X		if (h->h_di == NULL) {
X			direrr = h->h_err;
X			return(NULL);
X		}
X		else
X			return(h);
X
X	if (*p == '\0')
X		myp = ".";
X	else if (pathend == p + 1)
X		myp = SLASHSTR;
X	else {
X		lastslash = pathend - 1;
X		*lastslash = '\0';
X		myp = p;
X	}
X
X	if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
X		direrr = h->h_err = H_NODIR;
X	else if (access(myp, R_OK | X_OK))
X		direrr = h->h_err = H_NOREADDIR;
X	else {
X		direrr = 0;
X		sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
X			FI_INSTICKY : 0;
X		v = dstat.st_dev;
X		d = dstat.st_ino;
X
X		if ((di = dsearch(v, d)) == NULL)
X			takedir(myp, di = dadd(v, d), sticky);
X	}
X
X	if (lastslash != NULL)
X		*lastslash = SLASH;
X	if (direrr != 0)
X		return(NULL);
X	h->h_di = di;
X	return(h);
X}
X
X
Xstatic void takedir(p, di, sticky)
X	char *p;
X	DIRINFO *di;
X	int sticky;
X{
X	int cnt, room;
X	DIRENTRY *dp;
X	FILEINFO *f, **fils;
X	DIR *dirp;
X
X	if ((dirp = opendir(p)) == NULL) {
X		fprintf(stderr, "Strange, can't scan %s.\n", p);
X		quit();
X	}
X	room = INITROOM;
X	di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
X	cnt = 0;
X	while ((dp = readdir(dirp)) != NULL) {
X		if (cnt == room) {
X			room *= 2;
X			fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
X			memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
X			chgive(di->di_fils, cnt * sizeof(FILEINFO *));
X			di->di_fils = fils;
X			fils = di->di_fils + cnt;
X		}
X		*fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
X		f->fi_name = mydup(dp->d_name);
X		f->fi_stflags = sticky;
X		f->fi_rep = NULL;
X		cnt++;
X		fils++;
X	}
X	closedir(dirp);
X	qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
X	di->di_nfils = cnt;
X}
X
X/* end of Un*x checkdir, takedir; back to general program */
X#endif
X
X
Xstatic int fcmp(pf1, pf2)
X	FILEINFO **pf1, **pf2;
X{
X        return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
X}
X
X
Xstatic HANDLE *hadd(n)
X	char *n;
X{
X	HANDLE **newhandles, *h;
X
X	if (nhandles == handleroom) {
X		handleroom *= 2;
X		newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
X		memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
X		chgive(handles, nhandles * sizeof(HANDLE *));
X		handles = newhandles;
X	}
X	handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
X	h->h_name = (char *)challoc(strlen(n) + 1, 0);
X	strcpy(h->h_name, n);
X	h->h_di = NULL;
X	return(h);
X}
X
X
Xstatic int hsearch(n, which, pret)
X	char *n;
X	int which;
X	HANDLE **pret;
X{
X	int i;
X	HANDLE **ph;
X
X	if (strcmp(n, lasthandle[which]->h_name) == 0) {
X		*pret = lasthandle[which];
X		return(1);
X	}
X
X	for(i = 0, ph = handles; i < nhandles; i++, ph++)
X		if (strcmp(n, (*ph)->h_name) == 0) {
X			lasthandle[which] = *pret = *ph;
X			return(1);
X		}
X
X	lasthandle[which] = *pret = hadd(n);
X	return(0);
X}
X
X
Xstatic DIRINFO *dadd(v, d)
X	DEVID v;
X	DIRID d;
X{
X	DIRINFO *di;
X	DIRINFO **newdirs;
X
X	if (ndirs == dirroom) {
X		dirroom *= 2;
X		newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
X		memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
X		chgive(dirs, ndirs * sizeof(DIRINFO *));
X		dirs = newdirs;
X	}
X	dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
X	di->di_vid = v;
X	di->di_did = d;
X	di->di_nfils = 0;
X	di->di_fils = NULL;
X	di->di_flags = 0;
X	return(di);
X}
X
X
Xstatic DIRINFO *dsearch(v, d)
X	DEVID v;
X	DIRID d;
X{
X	int i;
X	DIRINFO *di;
X
X	for(i = 0, di = *dirs; i < ndirs; i++, di++)
X		if (v == di->di_vid && d == di->di_did)
X			return(di);
X	return(NULL);
X}
X
X
Xstatic int match(pat, s, start1, len1)
X	char *pat, *s, **start1;
X	int *len1;
X{
X	char c, *olds;
X
X	*start1 = 0;
X	for(;;)
X		switch (c = *pat) {
X		case '\0':
X		case SLASH:
X			return(*s == '\0');
X#ifdef MSDOS
X		case '!':
X			*start1 = olds = s;
X			if ((s = strchr(s, '.')) == NULL)
X				return(0);
X			s++;
X			*len1 = s - olds;
X			if ((c = *(++pat)) == '\0') {
X				*len1 += strlen(s);
X				return(1);
X			}
X			for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
X				if (*s == '\0')
X					return(0);
X			return(1);
X#endif
X		case '*':
X			*start1 = s;
X			if ((c = *(++pat)) == '\0') {
X				*len1 = strlen(s);
X				return(1);
X			}
X			else {
X				for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
X					if (
X#ifdef MSDOS
X						*s == '.' ||
X#endif
X						*s == '\0'
X					)
X						return(0);
X				return(1);
X			}
X		case '?':
X			if (
X#ifdef MSDOS
X				*s == '.' ||
X#endif
X				*s == '\0'
X			)
X				return(0);
X			*(start1++) = s;
X			*(len1++) = 1;
X			pat++;
X			s++;
X			break;
X		case '[':
X			{
X				int matched = 0, notin = 0, inrange = 0;
X				char prevc = '\0';
X
X				if ((c = *(++pat)) == '^') {
X					notin = 1;
X					c = *(++pat);
X				}
X				while (c != ']') {
X					if (c == '-' && !inrange)
X						inrange = 1;
X					else {
X						if (c == ESC) {
X							c = *(++pat);
X						}
X						if (inrange) {
X							if (*s >= prevc && *s <= c)
X								matched = 1;
X							inrange = 0;
X						}
X						else if (c == *s)
X							matched = 1;
X						prevc = c;
X					}
X					c = *(++pat);
X				}
X				if (inrange && *s >= prevc)
X					matched = 1;
X				if (!(matched ^ notin))
X					return(0);
X				*(start1++) = s;
X				*(len1++) = 1;
X				pat++;
X				s++;
X			}
X			break;
X		case ESC:
X			c = *(++pat);
X		default:
X			if (c == *s) {
X 				pat++;
X				s++;
X			}
X			else
X				return(0);
X		}
X}
X
X
Xstatic void makerep()
X{
X	int l, x;
X#ifndef MSDOS
X	int i, cnv;
X	char *q;
X#endif
X	char *p, *pat, c, pc;
X
X	repbad = 0;
X	p = fullrep;
X	for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
X		if (c == '=') {
X			c = *(++pat);
X#ifndef MSDOS
X			if (c == 'l') {
X				cnv = LOWER;
X				c = *(++pat);
X			}
X			if (c == 'u') {
X				cnv = UPPER;
X				c = *(++pat);
X			}
X			else
X				cnv = STAY;
X#endif
X			for(x = 0; ;x *= 10) {
X				x += c - '0';
X				c = *(pat+1);
X				if (!isdigit(c))
X					break;
X				pat++;
X			}
X			--x;
X			if (l + len[x] >= MAXPATH)
X				goto toolong;
X#ifdef MSDOS
X			if (
X				*(start[x]) == '.' &&
X				(
X					p == fullrep ||
X					*(p - 1) == SLASH
X				)
X			) {
X				repbad = 1;
X				if (l + STRLEN(EMPTY) >= MAXPATH)
X					goto toolong;
X				strcpy(p, EMPTY);
X				p += STRLEN(EMPTY);
X				l += STRLEN(EMPTY);
X			}
X#else
X			switch (cnv) {
X			case STAY:
X#endif
X				memmove(p, start[x], len[x]);
X				p += len[x];
X#ifndef MSDOS
X				break;
X			case LOWER:
X				for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
X					*p = mylower(*q);
X				break;
X			case UPPER:
X				for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
X					*p = myupper(*q);
X			}
X#endif
X		}
X		else {
X			if (c == ESC)
X				c = *(++pat);
X			if (l == MAXPATH)
X				goto toolong;
X			if (
X				(
X#ifdef MSDOS
X					c == '.' ||
X#endif
X					c == SLASH
X				) &&
X				(
X					p == fullrep ? pat != to :
X					(
X						(
X							(pc = *(p - 1)) == SLASH
X#ifdef MSDOS
X							|| pc == ':'
X#endif
X						) &&
X					 	*(pat - 1) != pc
X					)
X				)
X			) {
X				repbad = 1;
X				if (l + STRLEN(EMPTY) >= MAXPATH)
X					goto toolong;
X				strcpy(p, EMPTY);
X				p += STRLEN(EMPTY);
X				l += STRLEN(EMPTY);
X			}
X			*(p++)= c;
X		}
X	}
X	if (p == fullrep) {
X		strcpy(fullrep, EMPTY);
X		repbad = 1;
X	}
X	*(p++) = '\0';
X	return;
X
Xtoolong:
X	repbad = 1;
X	strcpy(fullrep, TOOLONG);
X}
X
X
Xstatic void checkcollisions()
X{
X	REPDICT *rd, *prd;
X	REP *p, *q;
X	int i, mult, oldnreps;
X
X	if (nreps == 0)
X		return;
X	rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
X	for (
X		q = &hrep, p = q->r_next, prd = rd, i = 0;
X		p != NULL;
X		q = p, p = p->r_next, prd++, i++
X	) {
X		prd->rd_p = p;
X		prd->rd_dto = p->r_hto->h_di;
X		prd->rd_nto = p->r_nto;
X		prd->rd_i = i;
X	}
X	qsort(rd, nreps, sizeof(REPDICT), rdcmp);
X	mult = 0;
X	for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
X		if (
X			i < oldnreps - 1 &&
X			prd->rd_dto == (prd + 1)->rd_dto &&
X			strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
X		) {
X			if (!mult)
X				mult = 1;
X			else
X				printf(" , ");
X			printf("%s%s", prd->rd_p->r_hfrom->h_name,
X				prd->rd_p->r_ffrom->fi_name);
X			prd->rd_p->r_flags |= R_SKIP;
X			prd->rd_p->r_ffrom->fi_rep = MISTAKE;
X			nreps--;
X			badreps++;
X		}
X		else if (mult) {
X			prd->rd_p->r_flags |= R_SKIP;
X			prd->rd_p->r_ffrom->fi_rep = MISTAKE;
X			nreps--;
X			badreps++;
X			printf(" , %s%s -> %s%s : collision.\n",
X				prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
X				prd->rd_p->r_hto->h_name, prd->rd_nto);
X			mult = 0;
X		}
X	chgive(rd, oldnreps * sizeof(REPDICT));
X}
X
X
Xstatic int rdcmp(rd1, rd2)
X	REPDICT *rd1, *rd2;
X{
X	int ret;
X
X	if (
X		(ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
X		(ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
X	)
X		ret = rd1->rd_i - rd2->rd_i;
X	return(ret);
X}
X
X
Xstatic void findorder()
X{
X	REP *p, *q, *t, *first, *pred;
X	FILEINFO *fi;
X
X	for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
X		if (p->r_flags & R_SKIP) {
X			q->r_next = p->r_next;
X			p = q;
X		}
X		else if (
X			(fi = p->r_fdel) == NULL ||
X			(pred = fi->fi_rep) == NULL ||
X			pred == MISTAKE
X		)
X			continue;
X		else if ((first = pred->r_first) == p) {
X			p->r_flags |= R_ISCYCLE;
X			pred->r_flags |= R_ISALIASED;
X			if (op & MOVE)
X				p->r_fdel = NULL;
X		}
X		else {
X			if (op & MOVE)
X				p->r_fdel = NULL;
X			while (pred->r_thendo != NULL)
X				pred = pred->r_thendo;
X			pred->r_thendo = p;
X			for (t = p; t != NULL; t = t->r_thendo)
X				t->r_first = first;
X			q->r_next = p->r_next;
X			p = q;
X		}
X}
X
X
Xstatic void nochains()
X{
X	REP *p, *q;
X
X	for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
X		if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
X			printchain(p);
X			printf("%s%s : no chain copies allowed.\n",
X				p->r_hto->h_name, p->r_nto);
X			q->r_next = p->r_next;
X			p = q;
X		}
X}
X
X
Xstatic void printchain(p)
X	REP *p;
X{
X	if (p->r_thendo != NULL)
X		printchain(p->r_thendo);
X	printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
X	badreps++;
X	nreps--;
X	p->r_ffrom->fi_rep = MISTAKE;
X}
X
X
Xstatic void scandeletes(pkilldel)
X	int (*pkilldel)();
X{
X	REP *p, *q, *n;
X
X	for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
X		if (p->r_fdel != NULL)
X			while ((*pkilldel)(p)) {
X				nreps--;
X				p->r_ffrom->fi_rep = MISTAKE;
X				if ((n = p->r_thendo) != NULL) {
X					if (op & MOVE)
X						n->r_fdel = p->r_ffrom;
X					n->r_next = p->r_next;
X					q->r_next = p = n;
X				}
X				else {
X					q->r_next = p->r_next;
X					p = q;
X					break;
X				}
X			}
X	}
X}
X
X
Xstatic int baddel(p)
X	REP *p;
X{
X	HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
X	FILEINFO *fto = p->r_fdel;
X	char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
X	char *hnf = hfrom->h_name, *hnt = hto->h_name;
X
X	if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
X		printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
X			hnf, f, hnt, t, hnt, t,
X			(op & OVERWRITE) ? "overwritten" : "deleted");
X	else if (fto->fi_rep == MISTAKE)
X		printf("%s%s -> %s%s : old %s%s was to be done first.\n",
X			hnf, f, hnt, t, hnt, t);
X	else if (
X#ifdef MSDOS
X		fto->fi_attrib & FA_DIREC
X#else
X		fto->fi_stflags & FI_ISDIR
X#endif
X	)
X		printf("%s%s -> %s%s : %s%s%s is a directory.\n",
X			hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
X#ifndef MSDOS
X	else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
X		printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
X			hnf, f, hnt, t, hnt, t);
X#endif
X	else if (
X		(op & (APPEND | OVERWRITE)) &&
X#ifdef MSDOS
X		fto->fi_attrib & FA_RDONLY
X#else
X		!fwritable(hnt, fto)
X#endif
X	) {
X		printf("%s%s -> %s%s : %s%s %s.\n",
X			hnf, f, hnt, t, hnt, t,
X#ifndef MSDOS
X#ifndef SYSV
X			fto->fi_stflags & FI_LINKERR ?
X			"is a badly aimed symbolic link" :
X#endif
X#endif
X			"lacks write permission");
X	}
X	else
X		return(0);
X	badreps++;
X	return(1);
X}
X
X
Xstatic int skipdel(p)
X	REP *p;
X{
X	if (p->r_flags & R_DELOK)
X		return(0);
X	fprintf(stderr, "%s%s -> %s%s : ",
X		p->r_hfrom->h_name, p->r_ffrom->fi_name,
X		p->r_hto->h_name, p->r_nto);
X	if (
X#ifdef MSDOS
X		p->r_fdel->fi_attrib & FA_RDONLY
X#else
X#ifndef SYSV
X		!(p->r_ffrom->fi_stflags & FI_ISLNK) &&
X#endif
X		!fwritable(p->r_hto->h_name, p->r_fdel)
X#endif
X	)
X		fprintf(stderr, "old %s%s lacks write permission. delete it",
X			p->r_hto->h_name, p->r_nto);
X	else
X		fprintf(stderr, "%s old %s%s",
X			(op & OVERWRITE) ? "overwrite" : "delete",
X			p->r_hto->h_name, p->r_nto);
X	return(!getreply("? ", -1));
X}
X
X
Xstatic void goonordie()
X{
X	if ((paterr || badreps) && nreps > 0) {
X		fprintf(stderr, "Not everything specified can be done.");
X		if (badstyle == ABORTBAD) {
X			fprintf(stderr, " Aborting.\n");
X			exit(1);
X		}
X		else if (badstyle == SKIPBAD)
X			fprintf(stderr, " Proceeding with the rest.\n");
X		else if (!getreply(" Proceed with the rest? ", -1))
X			exit(1);
X	}
X}
X
X
Xstatic void doreps()
X{
X	char *fstart;
X	int k, printaliased = 0, alias;
X	REP *first, *p;
X	long aliaslen;
X
X#ifdef MSDOS
X	ctrlbrk(breakrep);
X#else
X	signal(SIGINT, breakrep);
X#endif
X
X	for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
X		for (p = first; p != NULL; p = p->r_thendo, k++) {
X			if (gotsig) {
X				fflush(stdout);
X				fprintf(stderr, "User break.\n");
X				printaliased = snap(first, p);
X				gotsig = 0;
X			}
X			strcpy(fullrep, p->r_hto->h_name);
X			strcat(fullrep, p->r_nto);
X			if (!noex && (p->r_flags & R_ISCYCLE))
X				if (op & APPEND)
X					aliaslen = appendalias(first, p, &printaliased);
X				else
X					alias = movealias(first, p, &printaliased);
X			strcpy(pathbuf, p->r_hfrom->h_name);
X			fstart = pathbuf + strlen(pathbuf);
X			if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
X				sprintf(fstart, "%s%03d", TEMP, alias);
X			else
X				strcpy(fstart, p->r_ffrom->fi_name);
X			if (!noex) {
X				if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
X					myunlink(fullrep, p->r_fdel);
X				if (
X					(op & (COPY | APPEND)) ?
X						copy(p->r_ffrom,
X							p->r_flags & R_ISALIASED ? aliaslen : -1) :
X#ifndef MSDOS
X					(op & HARDLINK) ?
X						link(pathbuf, fullrep) :
X#ifndef SYSV
X					(op & SYMLINK) ?
X						symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
X							fullrep) :
X#endif
X#endif
X					p->r_flags & R_ISX ?
X						copymove(p) :
X					/* move */
X						rename(pathbuf, fullrep)
X				) {
X					fprintf(stderr,
X						"%s -> %s has failed.\n", pathbuf, fullrep);
X					printaliased = snap(first, p);
X				}
X			}
X			if (verbose || noex) {
X				if (p->r_flags & R_ISALIASED && !printaliased)
X					strcpy(fstart, p->r_ffrom->fi_name);
X				fprintf(outfile, "%s %c%c %s%s%s\n",
X					pathbuf,
X					p->r_flags & R_ISALIASED ? '=' : '-',
X					p->r_flags & R_ISCYCLE ? '^' : '>',
X					fullrep,
X					(p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
X					noex ? "" : " : done");
X			}
X		}
X		printaliased = 0;
X	}
X	if (k != nreps)
X		fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
X			k, nreps);
X	if (k == 0)
X		fprintf(stderr, "Nothing done.\n");
X}
X
X
Xstatic long appendalias(first, p, pprintaliased)
X	REP *first, *p;
X	int *pprintaliased;
X{
X	long ret;
X
X#ifdef MSDOS
X	int fd;
X
X	if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
X		fprintf(stderr, "stat on %s has failed.\n", fullrep);
X		*pprintaliased = snap(first, p);
X	}
X	else {
X		ret = filelength(fd);
X		close(fd);
X	}
X#else
X	struct stat fstat;
X
X	if (stat(fullrep, &fstat)) {
X		fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
X		*pprintaliased = snap(first, p);
X	}
X	else
X		ret = fstat.st_size;
X#endif
X
X	return(ret);
X}
X
X
END_OF_FILE
if test 49841 -ne `wc -c <'mmv.c.1'`; then
    echo shar: \"'mmv.c.1'\" unpacked with wrong size!
fi
# end of 'mmv.c.1'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.