mmv-1.01b.orig/ 40750 1750 1750 0 6145353423 12317 5ustar meskesmeskesmmv-1.01b.orig/mmv.1100640 1750 1750 41303 4673241034 13315 0ustar meskesmeskes.\" Under BSD, just give to nroff or troff (with -man). .\" To print the MS-DOS version, use option -rO2. .\" Under System V, take out the '.\" ' from the next line. .\" .nr O 1 .TH MMV 1 "November 20, 1989 (v1.0)" .ie !'\nO'2' \{\ .SH NAME mmv \- move/copy/append/link multiple files by wildcard patterns \} .el \{ .SH NAME mmv \- move/copy/append multiple files by wildcard patterns \} .ie '\nO'2' \{\ .ds SL \\\\ .ds ES ' \} .el \{\ .ds SL / .ds ES \\\\ \} .SH SYNOPSIS .B mmv .if '\nO'2' [\fB-m\fP|\fBx\fP|\fBr\fP|\fBc\fP|\fBo\fP|\fBa\fP|\fBz\fP] .if '\nO'0' [\fB-m\fP|\fBx\fP|\fBr\fP|\fBc\fP|\fBo\fP|\fBa\fP|\fBl\fP|\fBs\fP] .if '\nO'1' [\fB-m\fP|\fBx\fP|\fBr\fP|\fBc\fP|\fBo\fP|\fBa\fP|\fBl\fP] [\fB-h\fP] [\fB-d\fP|\fBp\fP] [\fB-g\fP|\fBt\fP] [\fB-v\fP|\fBn\fP] [\fBfrom to\fP] .if '\nO'2' \{\ .br .B mmvpatch [\fBexecutable\fP] \} .SH "DESCRIPTION" .I Mmv moves (or copies, .ie '\nO'2' or appends, .el appends, or links, as specified) each source file matching a .I from pattern to the target name specified by the .I to pattern. This multiple action is performed safely, i.e. without any unexpected deletion of files due to collisions of target names with existing filenames or with other target names. Furthermore, before doing anything, .I mmv attempts to detect any errors that would result from the entire set of actions specified and gives the user the choice of either proceeding by avoiding the offending parts or aborting. .ce The Task Options .PP Whether .I mmv moves, copies, .ie '\nO'2' or appends .el appends, or links is governed by the first set of options given above. If none of these are specified, .ie '\nO'2' \{\ a default (patchable by .IR mmvpatch , and initially -x) determines the task. \} .el \{\ the task is given by the command name under which .I mmv was invoked (argv[0]): command name default task mmv -x .br mcp -c .br mad -a .br mln -l \} .PP The task option choices are: .TP -m : move source file to target name. Both must be on the same device. Will not move directories. .if '\nO'0' \{\ If the source file is a symbolic link, moves the link without checking if the link's target from the new directory is different than the old. \} .TP -x : same as -m, except cross-device moves are done by copying, then deleting source. When copying, sets the .ie !'\nO'2' permission bits .el attributes and file modification time of the target file to that of the source file. .TP -r : rename source file or directory to target name. The target name must not include a path: the file remains in the same directory in all cases. This option is the only way of renaming directories under .IR mmv . .if '\nO'2' It is only available under DOS version 3.0 or higher. .TP -c : copy source file to target name. Sets the file modification time and .ie !'\nO'2' permission bits .el attributes of the target file to that of the source file, regardless of whether the target file already exists. Chains and cycles (to be explained below) are not allowed. .TP -o : overwrite target name with source file. .ie '\nO'2' \{\ If target file exists, its attributes are left unchanged. If not, it is created with ordinary attributes unrelated to the source file's attributes. In either case, the file modification time is set to the current time. \} .el \{\ If target file exists, it is overwritten, keeping its original owner and permission bits. If it does not exist, it is created, with read-write permission bits set according to .IR umask (1), and the execute permission bits copied from the source file. In either case, the file modification time is set to the current time. \} .TP -a : append contents of source file to target name. Target file modification time is set to the current time. If target file does not exist, it is created with .ie '\nO'2' attributes .el permission bits set as under -o. Unlike all other options, -a allows multiple source files to have the same target name, e.g. "mmv -a .ie '\nO'2' *.c .el \\*.c big" will append all ".c" files to "big". Chains and cycles are also allowed, so "mmv -a f f" will double up "f". .ie '\nO'2' \{\ .TP -z : same as -a, but if the target file exists, and its last character is a ^Z, and the source file is not empty, this ^Z is truncated before doing the append. \} .el \{\ .TP -l : link target name to source file. Both must be on the same device, and the source must not be a directory. Chains and cycles are not allowed. .if '\nO'0' \{\ .TP -s : same as -l, but use symbolic links instead of hard links. For the resulting link to aim back at the source, either the source name must begin with a '/', or the target must reside in either the current or the source directory. If none of these conditions are met, the link is refused. However, source and target can reside on different devices, and the source can be a directory. \} \} .PP Only one of these option may be given, and it applies to all matching files. Remaining options need not be given separately, i.e. "mmv -mk" is allowed. .ce Multiple Pattern Pairs .PP Multiple .I from -- .I to pattern pairs may be specified by omitting the pattern pair on the command line, and entering them on the standard input, one pair per line. (If a pattern pair is given on the command line, the standard input is not read.) Thus, .in +3 mmv .br a b .br c d .in -3 would rename "a" to "b" and "c" to "d". If a file can be matched to several of the given .I from patterns, the .I to pattern of the first matching pair is used. Thus, .in +3 mmv .br a b .br a c .in -3 would give the error message "a -> c : no match" because file "a" (even if it exists) was already matched by the first pattern pair. .ce The \fIFrom\fP Pattern .PP The .I from pattern is a filename with embedded wildcards: '*', '?', '['...']', .if '\nO'2' \{\ \'!', \} and ';'. The first three have their usual .IR sh (1) meanings of, respectively, matching any string of characters, matching any single character, and matching any one of a set of characters. .PP Between the '[' and ']', a range from character 'a' through character 'z' is specified with "a-z". The set of matching characters can be negated by inserting a '^' after the '['. Thus, "[^b-e2-5_]" will match any character but 'b' through 'e', '2' through '5', and '_'. .if '\nO'2' \{\ .PP Unlike DOS wildcards, all mmv wildcards (except for cases listed below) can occur anywhere in the pattern, whether preceding or following explicit characters or other wildcards. For example, the pattern "*z\\foo.bar" will search for files named "foo.bar" in all subdirectories whose names end in 'z'. However, no wildcards can occur in the drive letter. .PP The character '.' is not matched by any of '*', '?', or '['...']'. Thus, the pattern "*" will only match files with a null extension. To save yourself some typing, use the '!' wildcard instead, which matches the same as "*.*", except it is assigned only one wildcard index (see below). Thus, both "f!" and "f*.*" will match all of "f", "f.ext", "foo", and "foo.ext", while "f*" will match only the first and the third. \} .PP Note that paths are allowed in the patterns, and wildcards may be intermingled with slashes arbitrarily. The ';' wildcard is useful for matching files at any depth in the directory tree. It matches the same as "*\*(SL" repeated any number of times, including zero, and can only occur either at the beginning of the pattern or following a '\*(SL'. Thus ";*.c" will match all ".c" files in or below the current directory, while "\*(SL;*.c" will match them anywhere on the file system. .if !'\nO'2' \{\ .PP In addition, if the .I from pattern (or the .I to pattern) begins with "~/", the '~' is replaced with the home directory name. (Note that the "~user" feature of .IR csh (1) is not implemented.) However, the '~' is not treated as a wildcard, in the sense that it is not assigned a wildcard index (see below). \} .PP Since matching a directory under a task option other than -r or -s would result in an error, tasks other than -r and -s match directories only against completely explicit .I from patterns (i.e. not containing wildcards). Under -r and -s, this applies only to "." and "..". .PP .ie '\nO'2' \{\ Hidden and system files are also only matched against completely explicit .I from patterns. \} .el \{\ Files beginning with '.' are only matched against .I from patterns that begin with an explicit '.'. \} However, if -h is specified, they are matched normally. .if !'\nO'2' \{\ .PP Warning: since the shell normally expands wildcards before passing the command-line arguments to .IR mmv , it is usually necessary to enclose the command-line .I from pattern in quotes. \} .ce The \fITo\fP Pattern .PP The .I to pattern is a filename with embedded .I wildcard .IR indexes , where an index consists of the character '#' followed by a string of digits. When a source file matches a .I from pattern, a target name for the file is constructed out of the .I to pattern by replacing the wildcard indexes by the actual characters that matched the referenced wildcards in the source name. Thus, if the .I from pattern is "abc*.*" and the .I to pattern is "xyz#2.#1", then "abc.txt" is targeted to "xyztxt.". (The first '*' matched "", and the second matched "txt".) Similarly, for the pattern pair ";*.[clp]" -> "#1#3\*(SL#2", "foo1\*(SLfoo2\*(SLprog.c" is targeted to "foo1\*(SLfoo2\*(SLc\*(SLprog". Note that there is no '\*(SL' following the "#1" in the .I to pattern, since the string matched by any ';' is always either empty or ends in a '\*(SL'. In this case, it matches "foo1\*(SLfoo2\*(SL". .if !'\nO'2' \{\ .PP To convert the string matched by a wildcard to either lowercase or uppercase before embedding it in the target name, insert 'l' or 'u', respectively, between the '#' and the string of digits. .PP The .I to pattern, like the .I from pattern, can begin with a "~/" (see above). This does not necessitate enclosing the .I to pattern in quotes on the command line since .IR csh (1) expands the '~' in the exact same manner as .I mmv (or, in the case of .IR sh (1), does not expand it at all). \} .PP For all task options other than -r, if the target name is a directory, the real target name is formed by appending a '\*(SL' followed by the last component of the source file name. For example, "mmv dir1\*(SLa dir2" will, if "dir2" is indeed a directory, actually move "dir1\*(SLa" to "dir2\*(SLa". However, if "dir2\*(SLa" already exists and is itself a directory, this is considered an error. .PP To strip any character (e.g. '*', '?', or '#') of its special meaning to .IR mmv , as when the actual replacement name must contain the character '#', precede the special character with a .ie '\nO'2' \{\ single quote ('). \} .el \{\ \'\\' (and enclose the argument in quotes because of the shell). \} This also works to terminate a wildcard index when it has to be followed by a digit in the filename, e.g. "a#1\*(ES1". .ce Chains and Cycles .PP A chain is a sequence of specified actions where the target name of one action refers to the source file of another action. For example, mmv .br a b .br b c specifies the chain "a" -> "b" -> "c". A cycle is a chain where the last target name refers back to the first source file, e.g. "mmv a a". .I Mmv detects chains and cycles regardless of the order in which their constituent actions are actually given. Where allowed, i.e. in moving, renaming, and appending files, chains and cycles are handled gracefully, by performing them in the proper order. Cycles are broken by first renaming one of the files to a temporary name (or just remembering its original size when doing appends). .ce Collisions and Deletions .PP When any two or more matching files would have to be .ie '\nO'2' moved or copied .el moved, copied, or linked to the same target filename, .I mmv detects the condition as an error before performing any actions. Furthermore, .I mmv checks if any of its actions will result in the destruction of existing files. If the -d (delete) option is specified, all file deletions or overwrites are done silently. Under -p (protect), all deletions or overwrites (except those specified with "(*)" on the standard input, see below) are treated as errors. And if neither option is specified, the user is queried about each deletion or overwrite separately. (A new stream to .ie '\nO'2' "\\dev\\con" .el "/dev/tty" is used for all interactive queries, not the standard input.) .ce Error Handling .PP Whenever any error in the user's action specifications is detected, an error message is given on the standard output, and .I mmv proceeds to check the rest of the specified actions. Once all errors are detected, .I mmv queries the user whether he wishes to continue by avoiding the erroneous actions or to abort altogether. This and all other queries may be avoided by specifying either the -g (go) or -t (terminate) option. The former will resolve all difficulties by avoiding the erroneous actions; the latter will abort .I mmv if any errors are detected. Specifying either of them defaults .I mmv to -p, unless -d is specified (see above). Thus, -g and -t are most useful when running .I mmv in the background or in a shell script, when interactive queries are undesirable. .ce Reports .PP Once the actions to be performed are determined, .I mmv performs them silently, unless either the -v (verbose) or -n (no-execute) option is specified. The former causes .I mmv to report each performed action on the standard output as a -> b : done. Here, "a" and "b" would be replaced by the source and target names, respectively. If the action deletes the old target, a "(*)" is inserted after the the target name. Also, the "->" symbol is modified when a cycle has to be broken: the '>' is changed to a '^' on the action prior to which the old target is renamed to a temporary, and the '-' is changed to a '=' on the action where the temporary is used. .PP Under -n, none of the actions are performed, but messages like the above are printed on the standard output with the ": done." omitted. .PP The output generated by -n can (after editing, if desired) be fed back to .I mmv on the standard input (by omitting the .I from -- .I to pair on the .I mmv command line). To facilitate this, .I mmv ignores lines on the standard input that look like its own error and "done" messages, as well as all lines beginning with white space, and will accept pattern pairs with or without the intervening "->" (or "-^", "=>", or "=^"). Lines with "(*)" after the target pattern have the effect of enabling -d for the files matching this pattern only, so that such deletions are done silently. When feeding .I mmv its own output, one must remember to specify again the task option (if any) originally used to generate it. .PP Although .I mmv attempts to predict all mishaps prior to performing any specified actions, accidents may happen. For example, .I mmv does not check for adequate free space when copying. Thus, despite all efforts, it is still possible for an action to fail after some others have already been done. To make recovery as easy as possible, .I mmv reports which actions have already been done and which are still to be performed after such a failure occurs. It then aborts, not attempting to do anything else. Once the user has cleared up the problem, he can feed this report back to .I mmv on the standard input to have it complete the task. (The user is queried for a file name to dump this report if the standard output has not been redirected.) .if '\nO'2' \{\ .ce \fIMmvpatch\fP .PP You can customize a copy of .I mmv via the .I mmvpatch utility. If you wish to change the default task option, run .I mmvpatch on a copy of .I mmv named as follows: -x, -m, -r mmv.exe .br -c, -o mcp.exe .br -a, -z mad.exe .PP .I Mmvpatch also determines the best way to uniquely identify directories. As distributed, .I mmv is set to use a method that is guaranteed to work the same way for all versions of DOS, but is both slow and unable to correctly handle drives affected by the .I join and .I subst DOS commands. Alternatively, there is a method that is fast and correct, but uses an undocumented DOS feature that may not work properly under all versions of DOS. (However, 2.0 and 3.3 are known to work.) .I Mmv does .I not determine the best method to use on your system at run-time since this is too slow. The choice is left to .I mmvpatch, which determines if the fast method works, but also allows you to return to the slow method. \} .SH "EXIT STATUS" .I Mmv exits with status 1 if it aborts before doing anything, with status 2 if it aborts due to failure after completing some of the actions, and with status 0 otherwise. .if !'\nO'2' \{\ .SH "SEE ALSO" mv(1), cp(1), ln(1), umask(1) \} .SH "AUTHOR" Vladimir Lanin .br lanin@csd2.nyu.edu .SH "BUGS" .if !'\nO'2' \{\ If the search pattern is not quoted, the shell expands the wildcards. .I Mmv then (usually) gives some error message, but can not determine that the lack of quotes is the cause. .PP \}\ To avoid difficulties in semantics and error checking, .I mmv refuses to move or create directories. mmv-1.01b.orig/mmv.c100640 1750 1750 164224 5462725223 13433 0ustar meskesmeskes/* mmv 1.01b Copyright (c) 1990 Vladimir Lanin. This program may be freely used and copied on a non-commercial basis. Author may be reached at: lanin@csd2.nyu.edu Vladimir Lanin 330 Wadsworth Ave, Apt 6F, New York, NY 10040 Many thanks to those who have to contributed to the design and/or coding of this program: Tom Albrecht: initial Sys V adaptation, consultation, and testing Carl Mascott: V7 adaptation Mark Lewis: -n flag idea, consultation. Dave Bernhold: upper/lowercase conversion idea. Paul Stodghill: copy option, argv[0] checking. Frank Fiamingo: consultation and testing. Tom Jordahl: bug reports and testing. John Lukas, Hugh Redelmeyer, Barry Nelson, John Sauter, Phil Dench, John Nelson: bug reports. */ /* Define SYSV to compile under System V. Define both SYSV and V7 to compile under V7. If your System V has a rename() call, define RENAME. Otherwise, mmv will only be able to rename directories (via option -r) when running as the super-user. There is no reason to set the suid bit on mmv if rename() is available. It is important that mmv not be run with effective uid set to any value other than either the real uid or the super-user. Even when running with effective uid set to super-user, mmv will only perform actions permitted to the real uid. Define MSDOS to compile under MS-D*S Turbo C 1.5. If you prefer mmv's output to use /'s instead of \'s under MS-D*S, define SLASH. When neither MSDOS nor SYSV are defined, compiles under BSD. RENAME is automatically defined under MSDOS and BSD. If you are running a (UN*X) system that provides the "struct dirent" readdir() directory reading standard, define DIRENT. Otherwise, mmv uses the BSD-like "struct direct" readdir(). If your (UN*X) system has neither of these, get the "dirent" by Doug Gwyn, available as gwyn-dir-lib in volume 9 of the comp.sources.unix archives. */ static char USAGE[] = #ifdef IS_MSDOS "Usage: \ %s [-m|x%s|c|o|a|z] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\ \n\ Use #N in the ``to'' pattern to get the string matched\n\ by the N'th ``from'' pattern wildcard.\n"; #define OTHEROPT (_osmajor < 3 ? "" : "|r") #else "Usage: \ %s [-m|x|r|c|o|a|l%s] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\ \n\ Use #[l|u]N in the ``to'' pattern to get the [lowercase|uppercase of the]\n\ string matched by the N'th ``from'' pattern wildcard.\n\ \n\ A ``from'' pattern containing wildcards should be quoted when given\n\ on the command line.\n"; #ifdef IS_SYSV #define OTHEROPT "" #else #define OTHEROPT "|s" #endif #endif #include #include #ifdef IS_MSDOS /* for MS-DOS (under Turbo C 1.5)*/ #include #include #include #include #include #include #include #define ESC '\'' #ifdef SLASH #define SLASH '\\' #define OTHERSLASH '/' #else #define SLASH '/' #define OTHERSLASH '\\' #endif typedef int DIRID; typedef int DEVID; static char TTY[] = "/dev/con"; extern unsigned _stklen = 10000; #undef HAS_RENAME #define HAS_RENAME 1 #else /* for various flavors of UN*X */ #include #include #include extern char *getenv(); extern long lseek(); extern char *malloc(); #ifdef HAS_DIRENT #include typedef struct dirent DIRENTRY; #else #ifdef IS_SYSV #include /* might need to be changed to */ #else #include #endif typedef struct direct DIRENTRY; #endif #ifndef __STDC__ #ifndef __GNUC__ #ifndef IS_SYSV #ifndef IS_BSD #define void char /* might want to remove this line */ #endif #endif #endif #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef R_OK #define R_OK 4 #define W_OK 2 #define X_OK 1 #endif #define ESC '\\' #define SLASH '/' typedef ino_t DIRID; typedef dev_t DEVID; #define MAXPATH 1024 static char TTY[] = "/dev/tty"; #ifdef IS_V7 /* for Version 7 */ #include extern int errno; #define strchr index extern char *strcpy(), *strchr(); #include #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #else /* for System V and BSD */ #include #include #include #endif #ifdef IS_SYSV /* for System V and Version 7*/ struct utimbuf { time_t actime; time_t modtime; }; #define utimes(f, t) utime((f), &(t)) #ifndef HAS_RENAME #ifndef MV_DIR # define MV_DIR "/usr/lib/mv_dir" #endif #endif #ifdef MV_DIR # define HAS_RENAME #endif #else /* for BSD */ #undef HAS_RENAME #define HAS_RENAME 1 #include #endif #endif #define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c)) #define myupper(c) (islower(c) ? (c)-'a'+'A' : (c)) #define STRLEN(s) (sizeof(s) - 1) #define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s))) #define DFLT 0x001 #define NORMCOPY 0x002 #define OVERWRITE 0x004 #define NORMMOVE 0x008 #define XMOVE 0x010 #define DIRMOVE 0x020 #define NORMAPPEND 0x040 #define ZAPPEND 0x080 #define HARDLINK 0x100 #define SYMLINK 0x200 #define COPY (NORMCOPY | OVERWRITE) #define MOVE (NORMMOVE | XMOVE | DIRMOVE) #define APPEND (NORMAPPEND | ZAPPEND) #define LINK (HARDLINK | SYMLINK) static char MOVENAME[] = "mmv"; static char COPYNAME[] = "mcp"; static char APPENDNAME[] = "mad"; static char LINKNAME[] = "mln"; #define ASKDEL 0 #define ALLDEL 1 #define NODEL 2 #define ASKBAD 0 #define SKIPBAD 1 #define ABORTBAD 2 #define STAY 0 #define LOWER 1 #define UPPER 2 #define MAXWILD 20 #define MAXPATLEN MAXPATH #define INITROOM 10 #define CHUNKSIZE 2048 #define BUFSIZE 4096 #define FI_STTAKEN 0x01 #define FI_LINKERR 0x02 #define FI_INSTICKY 0x04 #define FI_NODEL 0x08 #define FI_KNOWWRITE 0x010 #define FI_CANWRITE 0x20 #define FI_ISDIR 0x40 #define FI_ISLNK 0x80 typedef struct { char *fi_name; struct rep *fi_rep; #ifdef IS_MSDOS char fi_attrib; #else short fi_mode; char fi_stflags; #endif } FILEINFO; #define DI_KNOWWRITE 0x01 #define DI_CANWRITE 0x02 #define DI_CLEANED 0x04 typedef struct { DEVID di_vid; DIRID di_did; unsigned di_nfils; FILEINFO **di_fils; char di_flags; } DIRINFO; #define H_NODIR 1 #define H_NOREADDIR 2 typedef struct { char *h_name; DIRINFO *h_di; char h_err; } HANDLE; #define R_ISX 0x01 #define R_SKIP 0x02 #define R_DELOK 0x04 #define R_ISALIASED 0x08 #define R_ISCYCLE 0x10 #define R_ONEDIRLINK 0x20 typedef struct rep { HANDLE *r_hfrom; FILEINFO *r_ffrom; HANDLE *r_hto; char *r_nto; /* non-path part of new name */ FILEINFO *r_fdel; struct rep *r_first; struct rep *r_thendo; struct rep *r_next; char r_flags; } REP; typedef struct { REP *rd_p; DIRINFO *rd_dto; char *rd_nto; unsigned rd_i; } REPDICT; typedef struct chunk { struct chunk *ch_next; unsigned ch_len; } CHUNK; typedef struct { CHUNK *sl_first; char *sl_unused; int sl_len; } SLICER; static void init(/* */); static void procargs(/* int argc, char **argv, char **pfrompat, char **ptopat */); static void domatch(/* char *cfrom, char *cto */); static int getpat(/* */); static int getword(/* char *buf */); static void matchpat(/* */); static int parsepat(/* */); static int dostage(/* char *lastend, char *pathend, char **start1, int *len1, int stage, int anylev */); static int trymatch(/* FILEINFO *ffrom, char *pat */); static int keepmatch(/* FILEINFO *ffrom, char *pathend, int *pk, int needslash, int dirs, int fils */); static int badrep(/* HANDLE *hfrom, FILEINFO *ffrom, HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */); static int checkto(/* HANDLE *hfrom, char *f, HANDLE **phto, char **pnto, FILEINFO **pfdel */); static char *getpath(/* char *tpath */); static int badname(/* char *s */); static FILEINFO *fsearch(/* char *s, DIRINFO *d */); static int ffirst(/* char *s, int n, DIRINFO *d */); static HANDLE *checkdir(/* char *p, char *pathend, int which */); static void takedir(/* char *p, DIRINFO *di, int sticky or struct ffblk *pff, DIRINFO *di */); static int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */); static HANDLE *hadd(/* char *n */); static int hsearch(/* char *n, int which, HANDLE **ph */); static DIRINFO *dadd(/* DEVID v, DIRID d */); static DIRINFO *dsearch(/* DEVID v, DIRID d */); static int match(/* char *pat, char *s, char **start1, int *len1 */); static void makerep(/* */); static void checkcollisions(/* */); static int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */); static void findorder(/* */); static void scandeletes(/* int (*pkilldel)(REP *p) */); static int baddel(/* REP *p */); static int skipdel(/* REP *p */); static void nochains(/* */); static void printchain(/* REP *p */); static void goonordie(/* */); static void doreps(/* */); static long appendalias(/* REP *first, REP *p, int *pprintaliased */); static int movealias(/* REP *first, REP *p, int *pprintaliased */); static int snap(/* REP *first, REP *p */); static void showdone(/* REP *fin */); static void breakout(/* */); static int breakrep(/* */); static void breakstat(/* */); static void quit(/* */); static int copymove(/* REP *p */); static int copy(/* FILENFO *f, long len */); static int myunlink(/* char *n, FILEINFO *f */); static int getreply(/* char *m, int failact */); static void *myalloc(/* unsigned k */); static void *challoc(/* int k, int which */); static void chgive(/* void *p, unsigned k */); static int mygetc(/* */); static char *mygets(/* char *s, int l */); #ifdef IS_MSDOS static int leave(/* */); static void cleanup(/* */); #else static int getstat(/* char *full, FILEINFO *f */); static int dwritable(/* HANDLE *h */); static int fwritable(/* char *hname, FILEINFO *f */); #ifndef __STDC__ #ifndef IS_MSDOS #ifndef IS_SYSV static void memmove(/* void *to, void *from, int k */); #endif #endif #endif #endif #ifndef HAS_RENAME static int rename(/* char *from, char *to */); #endif static int op, badstyle, delstyle, verbose, noex, matchall; static int patflags; static unsigned ndirs = 0, dirroom; static DIRINFO **dirs; static unsigned nhandles = 0, handleroom; static HANDLE **handles; static HANDLE badhandle = {"\200", NULL, 0}; static HANDLE *(lasthandle[2]) = {&badhandle, &badhandle}; static unsigned nreps = 0; static REP hrep, *lastrep = &hrep; static CHUNK *freechunks = NULL; static SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}}; static int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad; static FILE *outfile = stdout; static char IDF[] = "$$mmvdid."; static char TEMP[] = "$$mmvtmp."; static char TOOLONG[] = "(too long)"; static char EMPTY[] = "(empty)"; static char SLASHSTR[] = {SLASH, '\0'}; static char PATLONG[] = "%.40s... : pattern too long.\n"; char from[MAXPATLEN], to[MAXPATLEN]; static int fromlen, tolen; static char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]); static int nwilds[MAXWILD]; static int nstages; char pathbuf[MAXPATH]; char fullrep[MAXPATH + 1]; static char *(start[MAXWILD]); static int len[MAXWILD]; static char hasdot[MAXWILD]; static REP mistake; #define MISTAKE (&mistake) #ifdef IS_MSDOS static int olddevflag, curdisk, maxdisk; static struct { char ph_banner[30]; char ph_name[9]; int ph_dfltop; int ph_safeid; int ph_clustoff; int ph_driveoff; int ph_drivea; } patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0}; #define DFLTOP (patch.ph_dfltop) #define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff)) #define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea) #else #define DFLTOP XMOVE static char *home; static int homelen; static int uid, euid, oldumask; static DIRID cwdd = -1; static DEVID cwdv = -1; #endif int main(argc, argv) int argc; char *(argv[]); { char *frompat, *topat; init(); procargs(argc, argv, &frompat, &topat); domatch(frompat, topat); if (!(op & APPEND)) checkcollisions(); findorder(); if (op & (COPY | LINK)) nochains(); scandeletes(baddel); goonordie(); if (!(op & APPEND) && delstyle == ASKDEL) scandeletes(skipdel); doreps(); return(failed ? 2 : nreps == 0 && (paterr || badreps)); } static void init() { #ifdef IS_MSDOS curdisk = getdisk(); maxdisk = setdisk(curdisk); /* Read device availability : undocumented internal MS-DOS function. If (_DX == 0) then \dev\ must precede device names. */ bdos(0x37, 0, 2); olddevflag = _DX; /* Write device availability: undocumented internal MS-DOS function. Specify \dev\ must precede device names. */ bdos(0x37, 0, 3); atexit((atexit_t)cleanup); ctrlbrk((int (*)())breakout); #else struct stat dstat; if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0) home = ""; if (!stat(".", &dstat)) { cwdd = dstat.st_ino; cwdv = dstat.st_dev; } oldumask = umask(0); euid = geteuid(); uid = getuid(); signal(SIGINT, breakout); #endif dirroom = handleroom = INITROOM; dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *)); handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *)); ndirs = nhandles = 0; } static void procargs(argc, argv, pfrompat, ptopat) int argc; char **argv; char **pfrompat, **ptopat; { char *p, c; char *cmdname = argv[0]; #ifdef IS_MSDOS #define CMDNAME (patch.ph_name) #else #define CMDNAME cmdname #endif op = DFLT; verbose = noex = matchall = 0; delstyle = ASKDEL; badstyle = ASKBAD; for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) for (p = *argv + 1; *p != '\0'; p++) { c = mylower(*p); if (c == 'v' && !noex) verbose = 1; else if (c == 'n' && !verbose) noex = 1; else if (c == 'h') matchall = 1; else if (c == 'd' && delstyle == ASKDEL) delstyle = ALLDEL; else if (c == 'p' && delstyle == ASKDEL) delstyle = NODEL; else if (c == 'g' && badstyle == ASKBAD) badstyle = SKIPBAD; else if (c == 't' && badstyle == ASKBAD) badstyle = ABORTBAD; else if (c == 'm' && op == DFLT) op = NORMMOVE; else if (c == 'x' && op == DFLT) op = XMOVE; else if (c == 'r' && op == DFLT) op = DIRMOVE; else if (c == 'c' && op == DFLT) op = NORMCOPY; else if (c == 'o' && op == DFLT) op = OVERWRITE; else if (c == 'a' && op == DFLT) op = NORMAPPEND; #ifdef IS_MSDOS else if (c == 'z' && op == DFLT) op = ZAPPEND; #else else if (c == 'l' && op == DFLT) op = HARDLINK; #ifdef S_IFLNK else if (c == 's' && op == DFLT) op = SYMLINK; #endif #endif else { fprintf(stderr, USAGE, CMDNAME, OTHEROPT); exit(1); } } if (op == DFLT) if (strcmp(cmdname, MOVENAME) == 0) op = XMOVE; else if (strcmp(cmdname, COPYNAME) == 0) op = NORMCOPY; else if (strcmp(cmdname, APPENDNAME) == 0) op = NORMAPPEND; else if (strcmp(cmdname, LINKNAME) == 0) op = HARDLINK; else op = DFLTOP; if ( op & DIRMOVE && #ifdef IS_MSDOS _osmajor < 3 #else #ifndef HAS_RENAME euid != 0 #else 0 #endif #endif ) { fprintf(stderr, "Unable to do directory renames. Option -r refused.\n"); quit(); } #ifndef IS_MSDOS if (euid != uid && !(op & DIRMOVE)) { setuid(uid); setgid(getgid()); } #endif if (badstyle != ASKBAD && delstyle == ASKDEL) delstyle = NODEL; if (argc == 0) *pfrompat = NULL; else if (argc == 2) { *pfrompat = *(argv++); *ptopat = *(argv++); } else { fprintf(stderr, USAGE, CMDNAME, OTHEROPT); exit(1); } } static void domatch(cfrom, cto) char *cfrom, *cto; { if (cfrom == NULL) while (getpat()) matchpat(); else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) { printf(PATLONG, cfrom); paterr = 1; } else if ((tolen = strlen(cto)) >= MAXPATLEN) { printf(PATLONG, cto); paterr = 1; } else { strcpy(from, cfrom); strcpy(to, cto); matchpat(); } } static int getpat() { int c, gotit = 0; char extra[MAXPATLEN]; patflags = 0; do { if ((fromlen = getword(from)) == 0 || fromlen == -1) goto nextline; do { if ((tolen = getword(to)) == 0) { printf("%s -> ? : missing replacement pattern.\n", from); goto nextline; } if (tolen == -1) goto nextline; } while ( tolen == 2 && (to[0] == '-' || to[0] == '=') && (to[1] == '>' || to[1] == '^') ); if (getword(extra) == 0) gotit = 1; else if (strcmp(extra, "(*)") == 0) { patflags |= R_DELOK; gotit = (getword(extra) == 0); } nextline: while ((c = mygetc()) != '\n' && c != EOF) ; if (c == EOF) return(0); } while (!gotit); return(1); } static int getword(buf) char *buf; { int c, prevc, n; char *p; p = buf; prevc = ' '; n = 0; while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) { if (n == -1) continue; if (n == MAXPATLEN - 1) { *p = '\0'; printf(PATLONG, buf); n = -1; } *(p++) = c; n++; prevc = c; } *p = '\0'; while (c != EOF && isspace(c) && c != '\n') c = mygetc(); if (c != EOF) ungetc(c, stdin); return(n); } static void matchpat() { if (parsepat()) paterr = 1; else if (dostage(from, pathbuf, start, len, 0, 0)) { printf("%s -> %s : no match.\n", from, to); paterr = 1; } } static int parsepat() { char *p, *lastname, c; int totwilds, instage, x, havedot; static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n"; lastname = from; #ifdef IS_MSDOS havedot = 0; if (from[0] != '\0' && from[1] == ':') lastname += 2; #else if (from[0] == '~' && from[1] == SLASH) { if ((homelen = strlen(home)) + fromlen > MAXPATLEN) { printf(PATLONG, from); return(-1); } memmove(from + homelen, from + 1, fromlen); memmove(from, home, homelen); lastname += homelen + 1; } #endif totwilds = nstages = instage = 0; for (p = lastname; (c = *p) != '\0'; p++) switch (c) { #ifdef IS_MSDOS case '.': havedot = 1; break; case OTHERSLASH: *p = SLASH; #endif case SLASH: #ifdef IS_MSDOS if (!havedot && lastname != p) { if (fromlen++ == MAXPATLEN) { printf(PATLONG, from); return(-1); } memmove(p + 1, p, strlen(p) + 1); *(p++) = '.'; } else havedot = 0; #endif lastname = p + 1; if (instage) { if (firstwild[nstages] == NULL) firstwild[nstages] = p; stager[nstages++] = p; instage = 0; } break; case ';': if (lastname != p) { printf("%s -> %s : badly placed ;.\n", from, to); return(-1); } case '!': case '*': case '?': case '[': #ifdef IS_MSDOS if ((hasdot[totwilds] = (c == '!')) != 0) havedot = 1; #endif if (totwilds++ == MAXWILD) { printf("%s -> %s : too many wildcards.\n", from, to); return(-1); } if (instage) { nwilds[nstages]++; if (firstwild[nstages] == NULL) firstwild[nstages] = p; } else { stagel[nstages] = lastname; firstwild[nstages] = (c == ';' ? NULL : p); nwilds[nstages] = 1; instage = 1; } if (c != '[') break; while ((c = *(++p)) != ']') { switch (c) { case '\0': printf("%s -> %s : missing ].\n", from, to); return(-1); #ifdef IS_MSDOS case '.': case ':': case OTHERSLASH: #endif case SLASH: printf("%s -> %s : '%c' can not be part of [].\n", from, to, c); return(-1); case ESC: if ((c = *(++p)) == '\0') { printf(TRAILESC, from, to, ESC); return(-1); } #ifdef IS_MSDOS default: if (isupper(c)) *p = c + ('a' - 'A'); #endif } } break; case ESC: if ((c = *(++p)) == '\0') { printf(TRAILESC, from, to, ESC); return(-1); } #ifdef IS_MSDOS default: if (isupper(c)) *p = c + ('a' - 'A'); #endif } #ifdef IS_MSDOS if (!havedot && lastname != p) { if (fromlen++ == MAXPATLEN) { printf(PATLONG, from); return(-1); } strcpy(p++, "."); } #endif if (instage) { if (firstwild[nstages] == NULL) firstwild[nstages] = p; stager[nstages++] = p; } else { stagel[nstages] = lastname; nwilds[nstages] = 0; firstwild[nstages] = p; stager[nstages++] = p; } lastname = to; #ifdef IS_MSDOS havedot = 0; if (to[0] != '\0' && to[1] == ':') lastname += 2; #else if (to[0] == '~' && to[1] == SLASH) { if ((homelen = strlen(home)) + tolen > MAXPATLEN) { printf(PATLONG, to); return(-1); } memmove(to + homelen, to + 1, tolen); memmove(to, home, homelen); lastname += homelen + 1; } #endif for (p = lastname; (c = *p) != '\0'; p++) switch (c) { #ifdef IS_MSDOS case '.': havedot = 1; break; case OTHERSLASH: *p = SLASH; #endif case SLASH: if (op & DIRMOVE) { printf("%s -> %s : no path allowed in target under -r.\n", from, to); return(-1); } #ifdef IS_MSDOS if (!havedot && lastname != p) { if (tolen++ == MAXPATLEN) { printf(PATLONG, to); return(-1); } memmove(p + 1, p, strlen(p) + 1); *(p++) = '.'; } else havedot = 0; #endif lastname = p + 1; break; case '#': c = *(++p); if (c == 'l' || c == 'u') { #ifdef IS_MSDOS strcpy(p, p + 1); c = *p; #else c = *(++p); #endif } if (!isdigit(c)) { printf("%s -> %s : expected digit (not '%c') after #.\n", from, to, c); return(-1); } for(x = 0; ;x *= 10) { x += c - '0'; c = *(p+1); if (!isdigit(c)) break; p++; } if (x < 1 || x > totwilds) { printf("%s -> %s : wildcard #%d does not exist.\n", from, to, x); return(-1); } #ifdef IS_MSDOS if (hasdot[x - 1]) havedot = 1; #endif break; case ESC: if ((c = *(++p)) == '\0') { printf(TRAILESC, from, to, ESC); return(-1); } default: if ( #ifdef IS_MSDOS c <= ' ' || c >= 127 || strchr(":/\\*?[]=+;,\"|<>", c) != NULL #else c & 0x80 #endif ) { printf("%s -> %s : illegal character '%c' (0x%02X).\n", from, to, c, c); return(-1); } #ifdef IS_MSDOS if (isupper(c)) *p = c + ('a' - 'A'); #endif } #ifdef IS_MSDOS if (!havedot && lastname != p) { if (tolen++ == MAXPATLEN) { printf(PATLONG, to); return(-1); } strcpy(p++, "."); } #endif return(0); } static int dostage(lastend, pathend, start1, len1, stage, anylev) char *lastend, *pathend; char **start1; int *len1; int stage; int anylev; { DIRINFO *di; HANDLE *h, *hto; int prelen, litlen, nfils, i, k, flags, try; FILEINFO **pf, *fdel; char *nto, *firstesc; REP *p; int wantdirs, ret = 1, laststage = (stage + 1 == nstages); wantdirs = !laststage || (op & (DIRMOVE | SYMLINK)) || (nwilds[nstages - 1] == 0); if (!anylev) { prelen = stagel[stage] - lastend; if (pathend - pathbuf + prelen >= MAXPATH) { printf("%s -> %s : search path after %s too long.\n", from, to, pathbuf); paterr = 1; return(1); } memmove(pathend, lastend, prelen); pathend += prelen; *pathend = '\0'; lastend = stagel[stage]; } if ((h = checkdir(pathbuf, pathend, 0)) == NULL) { if (stage == 0 || direrr == H_NOREADDIR) { printf("%s -> %s : directory %s does not %s.\n", from, to, pathbuf, direrr == H_NOREADDIR ? "allow reads/searches" : "exist"); paterr = 1; } return(stage); } di = h->h_di; if (*lastend == ';') { anylev = 1; *start1 = pathend; *len1 = 0; lastend++; } nfils = di->di_nfils; #ifndef IS_MSDOS if ((op & MOVE) && !dwritable(h)) { printf("%s -> %s : directory %s does not allow writes.\n", from, to, pathbuf); paterr = 1; goto skiplev; } #endif firstesc = strchr(lastend, ESC); if (firstesc == NULL || firstesc > firstwild[stage]) firstesc = firstwild[stage]; litlen = firstesc - lastend; pf = di->di_fils + (i = ffirst(lastend, litlen, di)); if (i < nfils) do { if ( (try = trymatch(*pf, lastend)) != 0 && ( try == 1 || match(lastend + litlen, (*pf)->fi_name + litlen, start1 + anylev, len1 + anylev) ) && keepmatch(*pf, pathend, &k, 0, wantdirs, laststage) ) { if (!laststage) ret &= dostage(stager[stage], pathend + k, start1 + nwilds[stage], len1 + nwilds[stage], stage + 1, 0); else { ret = 0; makerep(); if (badrep(h, *pf, &hto, &nto, &fdel, &flags)) (*pf)->fi_rep = MISTAKE; else { (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1); p->r_flags = flags | patflags; p->r_hfrom = h; p->r_ffrom = *pf; p->r_hto = hto; p->r_nto = nto; p->r_fdel = fdel; p->r_first = p; p->r_thendo = NULL; p->r_next = NULL; lastrep->r_next = p; lastrep = p; nreps++; } } } i++, pf++; } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0); skiplev: if (anylev) for (pf = di->di_fils, i = 0; i < nfils; i++, pf++) if ( *((*pf)->fi_name) != '.' && #ifdef IS_MSDOS ((*pf)->fi_attrib & FA_DIREC) && #endif keepmatch(*pf, pathend, &k, 1, 1, 0) ) { *len1 = pathend - *start1 + k; ret &= dostage(lastend, pathend + k, start1, len1, stage, 1); } return(ret); } static int trymatch(ffrom, pat) FILEINFO *ffrom; char *pat; { char *p; if (ffrom->fi_rep != NULL) return(0); p = ffrom->fi_name; #ifdef IS_MSDOS if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM))) return(strcmp(pat, p) == 0); #else if (*p == '.') if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0')) return(strcmp(pat, p) == 0); else if (!matchall && *pat != '.') return(0); #endif return(-1); } static int keepmatch(ffrom, pathend, pk, needslash, dirs, fils) FILEINFO *ffrom; char *pathend; int *pk; int needslash; int dirs, fils; { *pk = strlen(ffrom->fi_name); if (pathend - pathbuf + *pk + needslash >= MAXPATH) { *pathend = '\0'; printf("%s -> %s : search path %s%s too long.\n", from, to, pathbuf, ffrom->fi_name); paterr = 1; return(0); } strcpy(pathend, ffrom->fi_name); #ifdef IS_MSDOS if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils) #else getstat(pathbuf, ffrom); if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils) #endif return(0); if (needslash) { strcpy(pathend + *pk, SLASHSTR); (*pk)++; } return(1); } static int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags) HANDLE *hfrom; FILEINFO *ffrom; HANDLE **phto; char **pnto; FILEINFO **pfdel; int *pflags; { char *f = ffrom->fi_name; *pflags = 0; if ( #ifdef IS_MSDOS (ffrom->fi_attrib & FA_DIREC) && #else (ffrom->fi_stflags & FI_ISDIR) && #endif !(op & (DIRMOVE | SYMLINK)) ) printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep); #ifndef IS_MSDOS #ifdef S_IFLNK else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK))) printf("%s -> %s : source file is a badly aimed symbolic link.\n", pathbuf, fullrep); #endif #ifndef IS_SYSV else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE)) printf("%s -> %s : no delete permission for source file.\n", pathbuf, fullrep); #endif else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK)) printf("%s -> %s : no read permission for source file.\n", pathbuf, fullrep); #endif else if ( *f == '.' && (f[1] == '\0' || strcmp(f, "..") == 0) && !(op & SYMLINK) ) printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep); else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto)) printf("%s -> %s : bad new name.\n", pathbuf, fullrep); else if (*phto == NULL) printf("%s -> %s : %s.\n", pathbuf, fullrep, #ifndef IS_MSDOS direrr == H_NOREADDIR ? "no read or search permission for target directory" : #endif "target directory does not exist"); #ifndef IS_MSDOS else if (!dwritable(*phto)) printf("%s -> %s : no write permission for target directory.\n", pathbuf, fullrep); #endif else if ( (*phto)->h_di->di_vid != hfrom->h_di->di_vid && (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK))) ) printf("%s -> %s : cross-device move.\n", pathbuf, fullrep); #ifndef IS_MSDOS else if ( *pflags && (op & MOVE) && !(ffrom->fi_stflags & FI_ISLNK) && access(pathbuf, R_OK) ) printf("%s -> %s : no read permission for source file.\n", pathbuf, fullrep); #ifdef S_IFLNK else if ( (op & SYMLINK) && !( ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) || *(hfrom->h_name) == SLASH || (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di) ) ) printf("%s -> %s : symbolic link would be badly aimed.\n", pathbuf, fullrep); #endif #endif else return(0); badreps++; return(-1); } static int checkto(hfrom, f, phto, pnto, pfdel) HANDLE *hfrom; char *f; HANDLE **phto; char **pnto; FILEINFO **pfdel; { char tpath[MAXPATH + 1]; char *pathend; FILEINFO *fdel; int hlen, tlen; if (op & DIRMOVE) { *phto = hfrom; hlen = strlen(hfrom->h_name); pathend = fullrep + hlen; memmove(pathend, fullrep, strlen(fullrep) + 1); memmove(fullrep, hfrom->h_name, hlen); if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) { *pnto = fdel->fi_name; #ifndef IS_MSDOS getstat(fullrep, fdel); #endif } else *pnto = mydup(pathend); } else { pathend = getpath(tpath); hlen = pathend - fullrep; *phto = checkdir(tpath, tpath + hlen, 1); if ( *phto != NULL && *pathend != '\0' && (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL && #ifdef IS_MSDOS (fdel->fi_attrib & FA_DIREC) #else (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR) #endif ) { tlen = strlen(pathend); strcpy(pathend + tlen, SLASHSTR); tlen++; strcpy(tpath + hlen, pathend); pathend += tlen; hlen += tlen; *phto = checkdir(tpath, tpath + hlen, 1); } if (*pathend == '\0') { *pnto = f; if (pathend - fullrep + strlen(f) >= MAXPATH) { strcpy(fullrep, TOOLONG); return(-1); } strcat(pathend, f); if (*phto != NULL) { fdel = *pfdel = fsearch(f, (*phto)->h_di); #ifndef IS_MSDOS if (fdel != NULL) getstat(fullrep, fdel); #endif } } else if (fdel != NULL) *pnto = fdel->fi_name; else *pnto = mydup(pathend); } return(0); } static char *getpath(tpath) char *tpath; { char *pathstart, *pathend, c; #ifdef IS_MSDOS if (*fullrep != '\0' && fullrep[1] == ':') pathstart = fullrep + 2; else #endif pathstart = fullrep; pathend = pathstart + strlen(pathstart) - 1; while (pathend >= pathstart && *pathend != SLASH) --pathend; pathend++; c = *pathend; *pathend = '\0'; strcpy(tpath, fullrep); *pathend = c; return(pathend); } static int badname(s) char *s; { char *ext; return ( #ifdef IS_MSDOS *s == ' ' || *s == '.' || (ext = strchr(s, '.')) - s >= MAXFILE || (*ext == '.' && strchr(ext + 1, '.') != NULL) || strlen(ext) >= MAXEXT || strncmp(s, IDF, STRLEN(IDF)) == 0 #else (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) || strlen(s) > MAXNAMLEN #endif ); } #ifndef IS_MSDOS static int getstat(ffull, f) char *ffull; FILEINFO *f; { struct stat fstat; int flags; if ((flags = f->fi_stflags) & FI_STTAKEN) return(flags & FI_LINKERR); flags |= FI_STTAKEN; #ifndef S_IFLNK if (stat(ffull, &fstat)) { fprintf(stderr, "Strange, couldn't stat %s.\n", ffull); quit(); } #else if (lstat(ffull, &fstat)) { fprintf(stderr, "Strange, couldn't lstat %s.\n", ffull); quit(); } if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0) flags |= FI_NODEL; if ((fstat.st_mode & S_IFMT) == S_IFLNK) { flags |= FI_ISLNK; if (stat(ffull, &fstat)) { f->fi_stflags = flags | FI_LINKERR; return(1); } } #endif if ((fstat.st_mode & S_IFMT) == S_IFDIR) flags |= FI_ISDIR; f->fi_stflags = flags; f->fi_mode = fstat.st_mode; return(0); } static int dwritable(h) HANDLE *h; { char *p = h->h_name, *myp, *lastslash = NULL, *pathend; char *pw = &(h->h_di->di_flags), r; if (uid == 0) return(1); if (*pw & DI_KNOWWRITE) return(*pw & DI_CANWRITE); pathend = p + strlen(p); if (*p == '\0') myp = "."; else if (pathend == p + 1) myp = SLASHSTR; else { lastslash = pathend - 1; *lastslash = '\0'; myp = p; } r = !access(myp, W_OK) ? DI_CANWRITE : 0; *pw |= DI_KNOWWRITE | r; if (lastslash != NULL) *lastslash = SLASH; return(r); } static int fwritable(hname, f) char *hname; FILEINFO *f; { int r; if (f->fi_stflags & FI_KNOWWRITE) return(f->fi_stflags & FI_CANWRITE); strcpy(fullrep, hname); strcat(fullrep, f->fi_name); r = !access(fullrep, W_OK) ? FI_CANWRITE : 0; f->fi_stflags |= FI_KNOWWRITE | r; return(r); } #endif static FILEINFO *fsearch(s, d) char *s; DIRINFO *d; { FILEINFO **fils = d->di_fils; int nfils = d->di_nfils; int first, k, last, res; for(first = 0, last = nfils - 1;;) { if (last < first) return(NULL); k = (first + last) >> 1; if ((res = strcmp(s, fils[k]->fi_name)) == 0) return(fils[k]); if (res < 0) last = k - 1; else first = k + 1; } } static int ffirst(s, n, d) char *s; int n; DIRINFO *d; { int first, k, last, res; FILEINFO **fils = d->di_fils; int nfils = d->di_nfils; if (nfils == 0 || n == 0) return(0); first = 0; last = nfils - 1; for(;;) { k = (first + last) >> 1; res = strncmp(s, fils[k]->fi_name, n); if (first == last) return(res == 0 ? k : nfils); else if (res > 0) first = k + 1; else last = k; } } #ifdef IS_MSDOS /* checkdir and takedir for MS-D*S */ static HANDLE *checkdir(p, pathend, which) char *p, *pathend; int which; { struct ffblk de; DIRID d; DEVID v; HANDLE *h; char *dirstart = p; int fd; int firstfound; DIRINFO *di; if (hsearch(p, which, &h)) if (h->h_di == NULL) { direrr = h->h_err; return(NULL); } else return(h); if (*p == '\0' || p[1] != ':') v = curdisk; else { dirstart += 2; v = mylower(p[0]) - 'a'; if (v < 0 || v >= maxdisk) return(NULL); } if (patch.ph_safeid) { strcpy(pathend, IDF); strcpy(pathend + STRLEN(IDF), "*"); if (findfirst(p, &de, 0)) { if ((d = ndirs) == 1000) { fprintf(stderr, "Too many different directories.\n"); quit(); } sprintf(pathend + STRLEN(IDF), "%03d", d); if ((fd = _creat(p, 0)) < 0) { direrr = h->h_err = H_NODIR; return(NULL); } _close(fd); strcpy(pathend, "*.*"); if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN)) h->h_di = dadd(v, d); else takedir(&de, h->h_di = dadd(v, d)); } else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs) h->h_di = dirs[d]; else { strcpy(pathend, de.ff_name); fprintf(stderr, "Strange dir-id file encountered: %s.\n", p); quit(); } *pathend = '\0'; } else { strcpy(pathend, "*.*"); firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN); *pathend = '\0'; if (firstfound) { v = DRIVENO(&de); d = CLUSTNO(&de); } else { strcpy(pathend, "T.D"); if (mkdir(p)) { *pathend = '\0'; direrr = h->h_err = H_NODIR; return(NULL); } strcpy(pathend, "*.*"); firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN); *pathend = '\0'; v = DRIVENO(&de); d = CLUSTNO(&de); rmdir(p); if (!firstfound || d != 0) { fprintf(stderr, "Strange, %s does not seem to be a root dir.\n", p); quit(); } } if ((di = dsearch(v, d)) == NULL) if (firstfound) takedir(&de, h->h_di = dadd(v, d)); else h->h_di = dadd(v, d); else h->h_di = di; } return(h); } static void takedir(pff, di) struct ffblk *pff; DIRINFO *di; { int cnt, room, namlen, needdot; FILEINFO **fils, *f; char c, *p, *p1; room = INITROOM; di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *)); cnt = 0; do { if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0) continue; if (cnt == room) { room *= 2; fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *)); memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *)); chgive(di->di_fils, cnt * sizeof(FILEINFO *)); di->di_fils = fils; fils = di->di_fils + cnt; } needdot = 1; for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++) if (c == '.') needdot = 0; *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1); f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0); for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++) *(p++) = mylower(c); if (needdot) *(p++) = '.'; *p = '\0'; f->fi_attrib = pff->ff_attrib; f->fi_rep = NULL; cnt++; fils++; } while (findnext(pff) == 0); qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp); di->di_nfils = cnt; } #else /* checkdir, takedir for Un*x */ static HANDLE *checkdir(p, pathend, which) char *p, *pathend; int which; { struct stat dstat; DIRID d; DEVID v; DIRINFO **newdirs, *di; int nfils; FILEINFO **fils; char *myp, *lastslash = NULL; int sticky; HANDLE *h; if (hsearch(p, which, &h)) if (h->h_di == NULL) { direrr = h->h_err; return(NULL); } else return(h); if (*p == '\0') myp = "."; else if (pathend == p + 1) myp = SLASHSTR; else { lastslash = pathend - 1; *lastslash = '\0'; myp = p; } if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR) direrr = h->h_err = H_NODIR; else if (access(myp, R_OK | X_OK)) direrr = h->h_err = H_NOREADDIR; else { direrr = 0; sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ? FI_INSTICKY : 0; v = dstat.st_dev; d = dstat.st_ino; if ((di = dsearch(v, d)) == NULL) takedir(myp, di = dadd(v, d), sticky); } if (lastslash != NULL) *lastslash = SLASH; if (direrr != 0) return(NULL); h->h_di = di; return(h); } static void takedir(p, di, sticky) char *p; DIRINFO *di; int sticky; { int cnt, room; DIRENTRY *dp; FILEINFO *f, **fils; DIR *dirp; if ((dirp = opendir(p)) == NULL) { fprintf(stderr, "Strange, can't scan %s.\n", p); quit(); } room = INITROOM; di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *)); cnt = 0; while ((dp = readdir(dirp)) != NULL) { if (cnt == room) { room *= 2; fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *)); memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *)); chgive(di->di_fils, cnt * sizeof(FILEINFO *)); di->di_fils = fils; fils = di->di_fils + cnt; } *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1); f->fi_name = mydup(dp->d_name); f->fi_stflags = sticky; f->fi_rep = NULL; cnt++; fils++; } closedir(dirp); qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp); di->di_nfils = cnt; } /* end of Un*x checkdir, takedir; back to general program */ #endif static int fcmp(pf1, pf2) FILEINFO **pf1, **pf2; { return(strcmp((*pf1)->fi_name, (*pf2)->fi_name)); } static HANDLE *hadd(n) char *n; { HANDLE **newhandles, *h; if (nhandles == handleroom) { handleroom *= 2; newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *)); memcpy(newhandles, handles, nhandles * sizeof(HANDLE *)); chgive(handles, nhandles * sizeof(HANDLE *)); handles = newhandles; } handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1); h->h_name = (char *)challoc(strlen(n) + 1, 0); strcpy(h->h_name, n); h->h_di = NULL; return(h); } static int hsearch(n, which, pret) char *n; int which; HANDLE **pret; { int i; HANDLE **ph; if (strcmp(n, lasthandle[which]->h_name) == 0) { *pret = lasthandle[which]; return(1); } for(i = 0, ph = handles; i < nhandles; i++, ph++) if (strcmp(n, (*ph)->h_name) == 0) { lasthandle[which] = *pret = *ph; return(1); } lasthandle[which] = *pret = hadd(n); return(0); } static DIRINFO *dadd(v, d) DEVID v; DIRID d; { DIRINFO *di; DIRINFO **newdirs; if (ndirs == dirroom) { dirroom *= 2; newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *)); memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *)); chgive(dirs, ndirs * sizeof(DIRINFO *)); dirs = newdirs; } dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1); di->di_vid = v; di->di_did = d; di->di_nfils = 0; di->di_fils = NULL; di->di_flags = 0; return(di); } static DIRINFO *dsearch(v, d) DEVID v; DIRID d; { int i; DIRINFO *di; for(i = 0, di = *dirs; i < ndirs; i++, di++) if (v == di->di_vid && d == di->di_did) return(di); return(NULL); } static int match(pat, s, start1, len1) char *pat, *s, **start1; int *len1; { char c, *olds; *start1 = 0; for(;;) switch (c = *pat) { case '\0': case SLASH: return(*s == '\0'); #ifdef IS_MSDOS case '!': *start1 = olds = s; if ((s = strchr(s, '.')) == NULL) return(0); s++; *len1 = s - olds; if ((c = *(++pat)) == '\0') { *len1 += strlen(s); return(1); } for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++) if (*s == '\0') return(0); return(1); #endif case '*': *start1 = s; if ((c = *(++pat)) == '\0') { *len1 = strlen(s); return(1); } else { for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++) if ( #ifdef IS_MSDOS *s == '.' || #endif *s == '\0' ) return(0); return(1); } case '?': if ( #ifdef IS_MSDOS *s == '.' || #endif *s == '\0' ) return(0); *(start1++) = s; *(len1++) = 1; pat++; s++; break; case '[': { int matched = 0, notin = 0, inrange = 0; char prevc = '\0'; if ((c = *(++pat)) == '^') { notin = 1; c = *(++pat); } while (c != ']') { if (c == '-' && !inrange) inrange = 1; else { if (c == ESC) { c = *(++pat); } if (inrange) { if (*s >= prevc && *s <= c) matched = 1; inrange = 0; } else if (c == *s) matched = 1; prevc = c; } c = *(++pat); } if (inrange && *s >= prevc) matched = 1; if (!(matched ^ notin)) return(0); *(start1++) = s; *(len1++) = 1; pat++; s++; } break; case ESC: c = *(++pat); default: if (c == *s) { pat++; s++; } else return(0); } } static void makerep() { int l, x; #ifndef IS_MSDOS int i, cnv; char *q; #endif char *p, *pat, c, pc; repbad = 0; p = fullrep; for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) { if (c == '#') { c = *(++pat); #ifndef IS_MSDOS if (c == 'l') { cnv = LOWER; c = *(++pat); } else if (c == 'u') { cnv = UPPER; c = *(++pat); } else cnv = STAY; #endif for(x = 0; ;x *= 10) { x += c - '0'; c = *(pat+1); if (!isdigit(c)) break; pat++; } --x; if (l + len[x] >= MAXPATH) goto toolong; #ifdef IS_MSDOS if ( *(start[x]) == '.' && ( p == fullrep || *(p - 1) == SLASH ) ) { repbad = 1; if (l + STRLEN(EMPTY) >= MAXPATH) goto toolong; strcpy(p, EMPTY); p += STRLEN(EMPTY); l += STRLEN(EMPTY); } #else switch (cnv) { case STAY: #endif memmove(p, start[x], len[x]); p += len[x]; #ifndef IS_MSDOS break; case LOWER: for (i = len[x], q = start[x]; i > 0; i--, p++, q++) *p = mylower(*q); break; case UPPER: for (i = len[x], q = start[x]; i > 0; i--, p++, q++) *p = myupper(*q); } #endif } else { if (c == ESC) c = *(++pat); if (l == MAXPATH) goto toolong; if ( ( #ifdef IS_MSDOS c == '.' || #endif c == SLASH ) && ( p == fullrep ? pat != to : ( ( (pc = *(p - 1)) == SLASH #ifdef IS_MSDOS || pc == ':' #endif ) && *(pat - 1) != pc ) ) ) { repbad = 1; if (l + STRLEN(EMPTY) >= MAXPATH) goto toolong; strcpy(p, EMPTY); p += STRLEN(EMPTY); l += STRLEN(EMPTY); } *(p++)= c; } } if (p == fullrep) { strcpy(fullrep, EMPTY); repbad = 1; } *(p++) = '\0'; return; toolong: repbad = 1; strcpy(fullrep, TOOLONG); } static void checkcollisions() { REPDICT *rd, *prd; REP *p, *q; int i, mult, oldnreps; if (nreps == 0) return; rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT)); for ( q = &hrep, p = q->r_next, prd = rd, i = 0; p != NULL; q = p, p = p->r_next, prd++, i++ ) { prd->rd_p = p; prd->rd_dto = p->r_hto->h_di; prd->rd_nto = p->r_nto; prd->rd_i = i; } qsort(rd, nreps, sizeof(REPDICT), rdcmp); mult = 0; for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++) if ( i < oldnreps - 1 && prd->rd_dto == (prd + 1)->rd_dto && strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0 ) { if (!mult) mult = 1; else printf(" , "); printf("%s%s", prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name); prd->rd_p->r_flags |= R_SKIP; prd->rd_p->r_ffrom->fi_rep = MISTAKE; nreps--; badreps++; } else if (mult) { prd->rd_p->r_flags |= R_SKIP; prd->rd_p->r_ffrom->fi_rep = MISTAKE; nreps--; badreps++; printf(" , %s%s -> %s%s : collision.\n", prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name, prd->rd_p->r_hto->h_name, prd->rd_nto); mult = 0; } chgive(rd, oldnreps * sizeof(REPDICT)); } static int rdcmp(rd1, rd2) REPDICT *rd1, *rd2; { int ret; if ( (ret = rd1->rd_dto - rd2->rd_dto) == 0 && (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0 ) ret = rd1->rd_i - rd2->rd_i; return(ret); } static void findorder() { REP *p, *q, *t, *first, *pred; FILEINFO *fi; for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) if (p->r_flags & R_SKIP) { q->r_next = p->r_next; p = q; } else if ( (fi = p->r_fdel) == NULL || (pred = fi->fi_rep) == NULL || pred == MISTAKE ) continue; else if ((first = pred->r_first) == p) { p->r_flags |= R_ISCYCLE; pred->r_flags |= R_ISALIASED; if (op & MOVE) p->r_fdel = NULL; } else { if (op & MOVE) p->r_fdel = NULL; while (pred->r_thendo != NULL) pred = pred->r_thendo; pred->r_thendo = p; for (t = p; t != NULL; t = t->r_thendo) t->r_first = first; q->r_next = p->r_next; p = q; } } static void nochains() { REP *p, *q; for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) { printchain(p); printf("%s%s : no chain copies allowed.\n", p->r_hto->h_name, p->r_nto); q->r_next = p->r_next; p = q; } } static void printchain(p) REP *p; { if (p->r_thendo != NULL) printchain(p->r_thendo); printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name); badreps++; nreps--; p->r_ffrom->fi_rep = MISTAKE; } static void scandeletes(pkilldel) int (*pkilldel)(); { REP *p, *q, *n; for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) { if (p->r_fdel != NULL) while ((*pkilldel)(p)) { nreps--; p->r_ffrom->fi_rep = MISTAKE; if ((n = p->r_thendo) != NULL) { if (op & MOVE) n->r_fdel = p->r_ffrom; n->r_next = p->r_next; q->r_next = p = n; } else { q->r_next = p->r_next; p = q; break; } } } } static int baddel(p) REP *p; { HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto; FILEINFO *fto = p->r_fdel; char *t = fto->fi_name, *f = p->r_ffrom->fi_name; char *hnf = hfrom->h_name, *hnt = hto->h_name; if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND)) printf("%s%s -> %s%s : old %s%s would have to be %s.\n", hnf, f, hnt, t, hnt, t, (op & OVERWRITE) ? "overwritten" : "deleted"); else if (fto->fi_rep == MISTAKE) printf("%s%s -> %s%s : old %s%s was to be done first.\n", hnf, f, hnt, t, hnt, t); else if ( #ifdef IS_MSDOS fto->fi_attrib & FA_DIREC #else fto->fi_stflags & FI_ISDIR #endif ) printf("%s%s -> %s%s : %s%s%s is a directory.\n", hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t); #ifndef IS_MSDOS else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE))) printf("%s%s -> %s%s : old %s%s lacks delete permission.\n", hnf, f, hnt, t, hnt, t); #endif else if ( (op & (APPEND | OVERWRITE)) && #ifdef IS_MSDOS fto->fi_attrib & FA_RDONLY #else !fwritable(hnt, fto) #endif ) { printf("%s%s -> %s%s : %s%s %s.\n", hnf, f, hnt, t, hnt, t, #ifndef IS_MSDOS #ifdef S_IFLNK fto->fi_stflags & FI_LINKERR ? "is a badly aimed symbolic link" : #endif #endif "lacks write permission"); } else return(0); badreps++; return(1); } static int skipdel(p) REP *p; { if (p->r_flags & R_DELOK) return(0); fprintf(stderr, "%s%s -> %s%s : ", p->r_hfrom->h_name, p->r_ffrom->fi_name, p->r_hto->h_name, p->r_nto); if ( #ifdef IS_MSDOS p->r_fdel->fi_attrib & FA_RDONLY #else #ifdef S_IFLNK !(p->r_ffrom->fi_stflags & FI_ISLNK) && #endif !fwritable(p->r_hto->h_name, p->r_fdel) #endif ) fprintf(stderr, "old %s%s lacks write permission. delete it", p->r_hto->h_name, p->r_nto); else fprintf(stderr, "%s old %s%s", (op & OVERWRITE) ? "overwrite" : "delete", p->r_hto->h_name, p->r_nto); return(!getreply("? ", -1)); } static void goonordie() { if ((paterr || badreps) && nreps > 0) { fprintf(stderr, "Not everything specified can be done."); if (badstyle == ABORTBAD) { fprintf(stderr, " Aborting.\n"); exit(1); } else if (badstyle == SKIPBAD) fprintf(stderr, " Proceeding with the rest.\n"); else if (!getreply(" Proceed with the rest? ", -1)) exit(1); } } static void doreps() { char *fstart; int k, printaliased = 0, alias; REP *first, *p; long aliaslen; #ifdef IS_MSDOS ctrlbrk(breakrep); #else signal(SIGINT, breakrep); #endif for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) { for (p = first; p != NULL; p = p->r_thendo, k++) { if (gotsig) { fflush(stdout); fprintf(stderr, "User break.\n"); printaliased = snap(first, p); gotsig = 0; } strcpy(fullrep, p->r_hto->h_name); strcat(fullrep, p->r_nto); if (!noex && (p->r_flags & R_ISCYCLE)) if (op & APPEND) aliaslen = appendalias(first, p, &printaliased); else alias = movealias(first, p, &printaliased); strcpy(pathbuf, p->r_hfrom->h_name); fstart = pathbuf + strlen(pathbuf); if ((p->r_flags & R_ISALIASED) && !(op & APPEND)) sprintf(fstart, "%s%03d", TEMP, alias); else strcpy(fstart, p->r_ffrom->fi_name); if (!noex) { if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE))) myunlink(fullrep, p->r_fdel); if ( (op & (COPY | APPEND)) ? copy(p->r_ffrom, p->r_flags & R_ISALIASED ? aliaslen : -1L) : #ifndef IS_MSDOS (op & HARDLINK) ? link(pathbuf, fullrep) : #ifdef S_IFLNK (op & SYMLINK) ? symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf, fullrep) : #endif #endif p->r_flags & R_ISX ? copymove(p) : /* move */ rename(pathbuf, fullrep) ) { fprintf(stderr, "%s -> %s has failed.\n", pathbuf, fullrep); printaliased = snap(first, p); } } if (verbose || noex) { if (p->r_flags & R_ISALIASED && !printaliased) strcpy(fstart, p->r_ffrom->fi_name); fprintf(outfile, "%s %c%c %s%s%s\n", pathbuf, p->r_flags & R_ISALIASED ? '=' : '-', p->r_flags & R_ISCYCLE ? '^' : '>', fullrep, (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "", noex ? "" : " : done"); } } printaliased = 0; } if (k != nreps) fprintf(stderr, "Strange, did %d reps; %d were expected.\n", k, nreps); if (k == 0) fprintf(stderr, "Nothing done.\n"); } static long appendalias(first, p, pprintaliased) REP *first, *p; int *pprintaliased; { long ret; #ifdef IS_MSDOS int fd; if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) { fprintf(stderr, "stat on %s has failed.\n", fullrep); *pprintaliased = snap(first, p); } else { ret = filelength(fd); close(fd); } #else struct stat fstat; if (stat(fullrep, &fstat)) { fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep); *pprintaliased = snap(first, p); } else ret = fstat.st_size; #endif return(ret); } static int movealias(first, p, pprintaliased) REP *first, *p; int *pprintaliased; { char *fstart; int ret; strcpy(pathbuf, p->r_hto->h_name); fstart = pathbuf + strlen(pathbuf); strcpy(fstart, TEMP); for ( ret = 0; sprintf(fstart + STRLEN(TEMP), "%03d", ret), fsearch(fstart, p->r_hto->h_di) != NULL; ret++ ) ; if (rename(fullrep, pathbuf)) { fprintf(stderr, "%s -> %s has failed.\n", fullrep, pathbuf); *pprintaliased = snap(first, p); } return(ret); } static int snap(first, p) REP *first, *p; { char fname[80]; int redirected = 0; if (noex) exit(1); failed = 1; #ifdef IS_MSDOS ctrlbrk((int (*)())breakstat); #else signal(SIGINT, breakstat); #endif if ( badstyle == ASKBAD && isatty(fileno(stdout)) && getreply("Redirect standard output to file? ", 0) ) { redirected = 1; #ifndef IS_MSDOS umask(oldumask); #endif while ( fprintf(stderr, "File name> "), (outfile = fopen(mygets(fname, 80), "w")) == NULL ) fprintf(stderr, "Can't open %s.\n", fname); } if (redirected || !verbose) showdone(p); fprintf(outfile, "The following left undone:\n"); noex = 1; return(first != p); } static void showdone(fin) REP *fin; { REP *first, *p; for (first = hrep.r_next; ; first = first->r_next) for (p = first; p != NULL; p = p->r_thendo) { if (p == fin) return; fprintf(outfile, "%s%s %c%c %s%s : done%s\n", p->r_hfrom->h_name, p->r_ffrom->fi_name, p->r_flags & R_ISALIASED ? '=' : '-', p->r_flags & R_ISCYCLE ? '^' : '>', p->r_hto->h_name, p->r_nto, (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : ""); } } static void breakout() { fflush(stdout); fprintf(stderr, "Aborting, nothing done.\n"); exit(1); } static int breakrep() { gotsig = 1; return(1); } static void breakstat() { exit(1); } static void quit() { fprintf(stderr, "Aborting, nothing done.\n"); exit(1); } static int copymove(p) REP *p; { #ifndef IS_MSDOS #ifndef IS_SYSV { int llen; char linkbuf[MAXPATH]; if ((llen = readlink(pathbuf, linkbuf, MAXPATH - 1)) >= 0) { linkbuf[llen] = '\0'; return(symlink(linkbuf, fullrep) || myunlink(pathbuf, p->r_ffrom)); } } #endif #endif return(copy(p->r_ffrom, -1L) || myunlink(pathbuf, p->r_ffrom)); } #define IRWMASK (S_IREAD | S_IWRITE) #define RWMASK (IRWMASK | (IRWMASK >> 3) | (IRWMASK >> 6)) static int copy(ff, len) FILEINFO *ff; long len; { char buf[BUFSIZE], c; int f, t, k, mode, perm; #ifdef IS_MSDOS struct ftime tim; #else #ifdef IS_SYSV struct utimbuf tim; #else struct timeval tim[2]; #endif struct stat fstat; #endif if ((f = open(pathbuf, O_RDONLY | O_BINARY, 0)) < 0) return(-1); perm = #ifdef IS_MSDOS IRWMASK /* will _chmod it later (to get all the attributes) */ #else (op & (APPEND | OVERWRITE)) ? (~oldumask & RWMASK) | (ff->fi_mode & ~RWMASK) : ff->fi_mode #endif ; #ifdef IS_V7 if ( !(op & APPEND) || (((t = open(fullrep, O_RDWR)) < 0 && errno == ENOENT) ) t = creat(fullrep, perm); #else mode = O_CREAT | (op & APPEND ? 0 : O_TRUNC) | #ifdef IS_MSDOS O_BINARY | (op & ZAPPEND ? O_RDWR : O_WRONLY) #else O_WRONLY #endif ; t = open(fullrep, mode, perm); #endif if (t < 0) { close(f); return(-1); } if (op & APPEND) lseek(t, 0L, 2); #ifdef IS_MSDOS if (op & ZAPPEND && filelength(t) != 0) { if (lseek(t, -1L, 1) == -1L || read(t, &c, 1) != 1) { close(f); close(t); return(-1); } if (c == 26) lseek(t, -1L, 1); } #endif if ((op & APPEND) && len != -1L) { while ( len != 0 && (k = read(f, buf, len > BUFSIZE ? BUFSIZE : (unsigned)len)) > 0 && write(t, buf, k) == k ) len -= k; if (len == 0) k = 0; } else while ((k = read(f, buf, BUFSIZE)) > 0 && write(t, buf, k) == k) ; if (!(op & (APPEND | OVERWRITE))) if ( #ifdef IS_MSDOS getftime(f, &tim) || setftime(t, &tim) || _chmod(fullrep, 1, ff->fi_attrib) == -1 #else stat(pathbuf, &fstat) || ( #ifdef IS_SYSV tim.actime = fstat.st_atime, tim.modtime = fstat.st_mtime, #else tim[0].tv_sec = fstat.st_atime, tim[1].tv_sec = fstat.st_mtime, #endif utimes(fullrep, tim) ) #endif ) fprintf(stderr, "Strange, couldn't transfer time from %s to %s.\n", pathbuf, fullrep); close(f); close(t); if (k != 0) { if (!(op & APPEND)) unlink(fullrep); return(-1); } return(0); } #ifdef MV_DIR #include extern int errno; static int rename(from, to) char *from, *to; { int pid; if (link(from, to) == 0 && unlink(from) == 0) return(0); else { struct stat s; if (stat(from, &s) < 0 || (s.st_mode&S_IFMT) != S_IFDIR) return(-1); } do pid = fork(); while (pid >= 0 && errno == EAGAIN); if (pid < 0) return(-1); else if (pid == 0) { execl(MV_DIR, "mv_dir", from, to, (char *) 0); perror(MV_DIR); exit(errno); } else if (pid > 0) { int wid; int status; do wid = wait(&status); while (wid != pid && wid >= 0); return(status == 0 ? 0 : -1); } } #else #ifndef HAS_RENAME static int rename(from, to) char *from, *to; { if (link(from, to)) return(-1); if (unlink(from)) { unlink(to); return(-1); } return(0); } #endif #endif /* MV_DIR */ static int myunlink(n, f) char *n; FILEINFO *f; { #ifdef IS_MSDOS int a; if (((a = f->fi_attrib) & FA_RDONLY) && _chmod(n, 1, a & ~FA_RDONLY) < 0) { fprintf(stderr, "Strange, can not _chmod (or unlink) %s.\n", f); return(-1); } #endif if (unlink(n)) { fprintf(stderr, "Strange, can not unlink %s.\n", n); return(-1); } return(0); } static int getreply(m, failact) char *m; int failact; { static FILE *tty = NULL; int c, r; fprintf(stderr, m); if (tty == NULL && (tty = fopen(TTY, "r")) == NULL) { fprintf(stderr, "Can not open %s to get reply.\n", TTY); if (failact == -1) quit(); else return(failact); } for (;;) { r = fgetc(tty); if (r == EOF) { fprintf(stderr, "Can not get reply.\n"); if (failact == -1) quit(); else return(failact); } if (r != '\n') while ((c = fgetc(tty)) != '\n' && c != EOF) ; r = mylower(r); if (r == 'y' || r == 'n') return(r == 'y'); fprintf(stderr, "Yes or No? "); } } static void *myalloc(k) unsigned k; { void *ret; if (k == 0) return(NULL); if ((ret = (void *)malloc(k)) == NULL) { fprintf(stderr, "Insufficient memory.\n"); quit(); } return(ret); } static void *challoc(k, which) int which; int k; { void *ret; CHUNK *p, *q; SLICER *sl = &(slicer[which]); if (k > sl->sl_len) { for ( q = NULL, p = freechunks; p != NULL && (sl->sl_len = p->ch_len) < k; q = p, p = p->ch_next ) ; if (p == NULL) { sl->sl_len = CHUNKSIZE - sizeof(CHUNK *); p = (CHUNK *)myalloc(CHUNKSIZE); } else if (q == NULL) freechunks = p->ch_next; else q->ch_next = p->ch_next; p->ch_next = sl->sl_first; sl->sl_first = p; sl->sl_unused = (char *)&(p->ch_len); } sl->sl_len -= k; ret = (void *)sl->sl_unused; sl->sl_unused += k; return(ret); } static void chgive(p, k) void *p; unsigned k; { ((CHUNK *)p)->ch_len = k - sizeof(CHUNK *); ((CHUNK *)p)->ch_next = freechunks; freechunks = (CHUNK *)p; } #ifndef __STDC__ #ifndef IS_MSDOS #ifndef IS_SYSV static void memmove(to, from, k) char *to, *from; unsigned k; { if (from > to) while (k-- != 0) *(to++) = *(from++); else { from += k; to += k; while (k-- != 0) *(--to) = *(--from); } } #endif #endif #endif static int mygetc() { static int lastc = 0; if (lastc == EOF) return(EOF); return(lastc = getchar()); } static char *mygets(s, l) char *s; int l; { char *nl; for (;;) { if (fgets(s, l, stdin) == NULL) return(NULL); if ((nl = strchr(s, '\n')) != NULL) break; fprintf(stderr, "Input string too long. Try again> "); } *nl = '\0'; return(s); } #ifdef IS_MSDOS static int leave() { return(0); } static void cleanup() { int i; if (patch.ph_safeid) { for (i = 0; i < nhandles; i++) { if (!(handles[i]->h_di->di_flags & DI_CLEANED)) { sprintf(pathbuf, "%s%s%03d", handles[i]->h_name, IDF, handles[i]->h_di->di_did); if (unlink(pathbuf)) fprintf(stderr, "Strange, couldn't unlink %s.\n", pathbuf); handles[i]->h_di->di_flags |= DI_CLEANED; } } } /* Write device availability: undocumented internal MS-D*S function. Restore previous value. */ bdos(0x37, olddevflag, 3); } #endif mmv-1.01b.orig/ANNOUNCE100640 1750 1750 3064 4673241030 13543 0ustar meskesmeskesCopyright (c) 1989 Vladimir Lanin This is mmv, a program to move/copy/append/link multiple files according to a set of wildcard patterns. This multiple action is performed safely, i.e. without any unexpected deletion of files due to collisions of target names with existing filenames or with other target names. Furthermore, before doing anything, mmv attempts to detect any errors that would result from the entire set of actions specified and gives the user the choice of either aborting before beginning, or proceeding by avoiding the offending parts. Improvements over mmv's predecessor, ren: . support for BSD, System 5, and MS-DOS . source and target files may (usually) reside in different directories . paths may contain wildcards . supports all csh wildcards: '*', '?', '['...']', and '~' . the ';' wildcard finds files at any level in the tree . can copy, append, or link instead of moving/renaming . reads multiple patterns from standard input (or one from command line) . no-execute option (whose output can be fed back in on standard input) Note to users familiar with ren: the -a and -k options have been renamed to -t and -g, respectively, and their semantics have somewhat changed. Mmv is freeware. That means that the entire package of software and documentation is copyrighted, and may not be distributed with any modifications or for any charge (without the author's explicit written permission). Other than that, it may be used and distributed freely. Vladimir Lanin 330 Wadsworth Ave, Apt 6F New York, NY 10040 lanin@csd2.nyu.edu ...!cmcl2!csd2!lanin mmv-1.01b.orig/READ.ME100640 1750 1750 705 4673241157 13341 0ustar meskesmeskesFiles: mmv.exe -- The mmv program, set for -x default and slow id method. mmvpatch.exe -- Patches default task option and dir id method into mmv. mmv.man -- Printable documentation. Edit to remove _^H's to display on the screen. mmv.c -- Source for mmv, compatible with Turbo C 1.5. Compile in compact model with MSDOS defined. mmvpatch.c -- Source for mmvpatch. Compile in small model. mmv.1 -- Source for mmv.man. Use nroff -man -rO2. mmv-1.01b.orig/ARTICLE100640 1750 1750 4667 4673242342 13441 0ustar meskesmeskesFrom ibmbin-request@crdgw1.crd.ge.com Wed Aug 22 16:41:24 1990 From: ibmbin-request@crdgw1.crd.ge.com Newsgroups: comp.binaries.ibm.pc Subject: v07i204: mmv10src, source for mmv10 file mover (part 01/02) Summary: Move files Date: 21 Aug 90 05:07:19 GMT Followup-To: comp.binaries.ibm.pc.d X-Submissions-to: ibmbin@crdgw1.crd.ge.com X-Questions-to: ibmbin-request@crdgw1.crd.ge.com Checksum: 3252004976 (Verify with "brik -cv") Posting-number: Volume 07, Issue 204 Submitted-by: lanin@csd4.cs.nyu.edu Archive-name: mmv10src/part01 This is the source for mmv10. [ Checksums obtained with the 4.3BSD "sum" or System V "sum -r" command. checksum size (bytes) file (between BEGIN--cut and END--cut lines) 65341 28171 part01 8782 27866 part02 checksum size (bytes) file 53647 40648 mmv10src.zoo -- bill ] mmv10src part01/02 BEGIN--cut here--cut here END--cut here--cut here From ibmbin-request@crdgw1.crd.ge.com Wed Aug 22 16:41:30 1990 From: ibmbin-request@crdgw1.crd.ge.com Newsgroups: comp.binaries.ibm.pc Subject: v07i205: mmv10src, source for mmv10 file mover (part 02/02) Summary: Move files Date: 21 Aug 90 05:07:27 GMT Followup-To: comp.binaries.ibm.pc.d X-Submissions-to: ibmbin@crdgw1.crd.ge.com X-Questions-to: ibmbin-request@crdgw1.crd.ge.com Checksum: 550153189 (Verify with "brik -cv") Posting-number: Volume 07, Issue 205 Submitted-by: lanin@csd4.cs.nyu.edu Archive-name: mmv10src/part02 mmv10src part02/02 BEGIN--cut here--cut here END--cut here--cut here From murthy@algron.cs.cornell.edu Mon Jun 18 15:35:14 1990 From: murthy@algron.cs.cornell.edu (Chet Murthy) Newsgroups: comp.sources.bugs Subject: MMV patch HIGH priority Date: 9 Jun 90 21:03:03 GMT Distribution: comp Organization: Cornell Univ. CS Dept. Ithaca NY Status: RO I'm posting this for Vladimir Lanin: The following patch fixes a number of bugs in mmv 1.0 (as distributed on comp.sources.unix) and provides V7 compatibility. The new version is known as mmv 1.01b. Since one of the bugs can result in the unintended DELETION of the source file, it is imperative that the old version be replaced with the new one. The two major bugs fixed are: 1) in its BSD reincarnation, whenever a file had to be moved between devices (by copying, then deleting), mmv 1.0 made a symbolic link to "/" instead of copying the file. It still deleted the original, though. Oops! 2) the uppercase/lowercase conversion feature did not work. Vladimir Lanin mmv-1.01b.orig/Makefile100640 1750 1750 760 5656550327 14050 0ustar meskesmeskes# Possible defines in CONF: # IS_MSDOS IS_SYSV IS_V7 IS_BSD HAS_DIRENT HAS_RENAME MV_DIR CC =gcc -traditional LD =$(CC) CONF =-DIS_SYSV -DHAS_DIRENT -DHAS_RENAME CFLAGS =-O2 -m486 $(CONF) LDFLAGS =-s -N #IBIN =$(LOCAL)$(ARCH)/bin #IMAN =$(LOCAL)$(ANY)/man IBIN=$(DESTDIR)/usr/bin/ IMAN=$(DESTDIR)/usr/man/ mmv: mmv.o clean: rm -f mmv mmv.o install: $(DEST)$(IBIN)/mmv install: $(DEST)$(IMAN)/man1/mmv.1 $(DEST)$(IBIN)/mmv: mmv; cp $? $@ $(DEST)$(IMAN)/man1/mmv.1: mmv.1; cp $? $@