v21i088: Safely rename wildcarded files, Part02/02
Rich Salz
rsalz at uunet.uu.net
Tue Apr 10 07:08:55 AEST 1990
Submitted-by: Vladimir Lanin <lanin at 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 at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list