Subject: v21i088: Safely rename wildcarded files, Part02/02 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: fbb8a4a2 cf48eba6 238c816b ae51c752 Submitted-by: Vladimir Lanin 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 '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 X#include X#include X X#ifdef MSDOS X/* for MS-DOS (under Turbo C 1.5)*/ X X#include X#include X#include X#include X#include X#include 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 X#include X#include X#include X#include Xextern char *getenv(); Xextern long lseek(); Xextern char *malloc(); X X#ifdef DIRENT X#include Xtypedef struct dirent DIRENTRY; X#else X#ifdef SYSV X#include X/* might need to be changed to */ X#else X#include 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 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...