From a4856fc61670fdb4e8f31d55326cfca999bd912b Mon Sep 17 00:00:00 2001 From: Axel Beckert Date: Fri, 7 Sep 2012 12:59:49 +0200 Subject: [PATCH] Imported Upstream version 1.01b --- ANNOUNCE | 45 + ARTICLE | 82 ++ Makefile | 24 + READ.ME | 15 + mmv.1 | 657 ++++++++++++ mmv.c | 2977 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 3800 insertions(+) create mode 100644 ANNOUNCE create mode 100644 ARTICLE create mode 100644 Makefile create mode 100644 READ.ME create mode 100644 mmv.1 create mode 100644 mmv.c diff --git a/ANNOUNCE b/ANNOUNCE new file mode 100644 index 0000000..122d50f --- /dev/null +++ b/ANNOUNCE @@ -0,0 +1,45 @@ +Copyright (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 diff --git a/ARTICLE b/ARTICLE new file mode 100644 index 0000000..064f4a0 --- /dev/null +++ b/ARTICLE @@ -0,0 +1,82 @@ +From 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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..84d6a73 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +# 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 $? $@ diff --git a/READ.ME b/READ.ME new file mode 100644 index 0000000..d7c151d --- /dev/null +++ b/READ.ME @@ -0,0 +1,15 @@ +Files: + +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. diff --git a/mmv.1 b/mmv.1 new file mode 100644 index 0000000..e23c5ae --- /dev/null +++ b/mmv.1 @@ -0,0 +1,657 @@ +.\" 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. diff --git a/mmv.c b/mmv.c new file mode 100644 index 0000000..2e72a73 --- /dev/null +++ b/mmv.c @@ -0,0 +1,2977 @@ +/* + 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 -- 2.39.2