]>
Commit | Line | Data |
---|---|---|
ccc6cda3 JA |
1 | #! /bin/bash |
2 | # | |
3 | # original from: | |
4 | # @(#) ncp.ksh,nmv.ksh 1.1 94/07/23 | |
5 | # 92/01/18 john h. dubois iii (john@armory.com) | |
6 | # 92/01/31 added check for no args left after shifts | |
7 | # 92/02/17 added help | |
8 | # 92/02/25 remove path component from filename before tacking it onto dest. | |
9 | # 92/03/15 exec mv or cp | |
10 | # 93/07/13 Added -i | |
11 | # 93/09/29 Made abort if file exists optional. | |
12 | # 93/11/19 Exit before invoking mv if no files to move | |
13 | # 94/01/03 Added o option | |
14 | # 94/04/13 Added x option. | |
15 | # Fixed appending of source filename, broken by earlier change. | |
16 | # 94/07/23 Append only the filename part of the source path. | |
17 | # | |
18 | # conversion to bash v2 syntax done by Chet Ramey | |
19 | ||
20 | false() | |
21 | { | |
22 | return 1 | |
23 | } | |
24 | ||
25 | true() | |
26 | { | |
27 | return 0 | |
28 | } | |
29 | ||
30 | phelp() | |
31 | { | |
32 | echo "$name: do a $cmd with extra checking and options. | |
33 | $Usage | |
34 | $name is used as a front end for $cmd to get the [icfo] options, and so | |
35 | that a trailing / will force the last component of the path to be | |
36 | interpreted as a directory, so that $name foo bar/ will fail if bar is | |
37 | not an existing directory, instead of changing the name of foo to bar. | |
38 | Effectively, $name foo bar/ is short for $name foo bar/foo | |
39 | Options: | |
40 | -h prints this help. | |
41 | -c checks first for the existence of each file, and fails if it exists. | |
42 | -i is like -c except that if the file exists and stdin and stdout are a | |
43 | tty, a query is printed and a reply is read; a file is overwritten only | |
44 | if the reply begins with 'y'. | |
45 | -f unsets -c and -i (in case $cmd is aliased to $name). | |
46 | -o (overwrite only) checks that the named file(s) exist and fails for any | |
47 | that do not. It is the complement of the -c option. | |
48 | Whichever of [cifo] comes later on the command line determines the behaviour. | |
49 | Any of these options must come before any standard $cmd options." | |
50 | } | |
51 | ||
52 | # interactive: Attempt to overwrite file should result in interactive | |
53 | # query rather than automatic failure. | |
54 | # noover: Do not overwrite files (if interactive is true, query, else fail) | |
55 | # overwrite: Only overwriting is allowed, not creation of new files. | |
56 | # debug: Print debugging info. | |
57 | typeset interactive=false noover=false overwrite=false debug=false | |
58 | name=${0##*/} | |
59 | ||
60 | case "$name" in | |
61 | ncp|nmv) cmd=/bin/${name#?} ;; | |
62 | *) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;; | |
63 | esac | |
64 | ||
65 | Usage="Usage: $name [-cfhio] $cmd-cmd-line" | |
66 | ||
67 | while getopts :cfhiox opt; do | |
68 | case $opt in | |
69 | h) phelp; exit 0;; | |
70 | x) debug=true ;; | |
71 | c) noover=true ;; | |
72 | i) noover=true ; interactive=true ;; | |
73 | f) noover=false ; interactive=false ;; | |
74 | o) overwrite=true ; noover=false ; interactive=false;; | |
75 | +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; | |
76 | ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;; | |
77 | esac | |
78 | done | |
79 | ||
80 | # remove args that were options | |
81 | shift $((OPTIND - 1)) | |
82 | ||
83 | if [ $# -lt 2 ]; then | |
84 | echo -e "$Usage\nUse -h for help." | |
85 | exit | |
86 | fi | |
87 | ||
88 | Check() | |
89 | { | |
90 | if [ ! -f "$1" ] && $overwrite; then | |
91 | echo "$name: $1: File does not exist." 1>&2 | |
92 | return 1 | |
93 | elif [ -f "$1" ] && $noover; then | |
94 | if [ $interactive = false ] || [ ! -t 0 ] || [ ! -t 1 ]; then | |
95 | echo "$name: $1: File exists." 1>&2 | |
96 | return 1 | |
97 | else | |
98 | while :; do | |
99 | echo -n \ | |
100 | "$name: $1: File exists. Overwrite? (y)es/(n)o/(a)bort/(Y)es for all: " 1>&2 | |
101 | read reply | |
102 | case "$reply" in | |
103 | y*) | |
104 | echo "$name: Overwriting $1." | |
105 | return 0 | |
106 | ;; | |
107 | Y*) | |
108 | echo "$name: Overwriting $1." | |
109 | interactive=false | |
110 | noover=false | |
111 | return 0 | |
112 | ;; | |
113 | [nN]*) | |
114 | echo "$name: Skipping $2." | |
115 | return 1 | |
116 | ;; | |
117 | [aA]*) | |
118 | echo "$name: Aborting." | |
119 | exit 1 | |
120 | ;; | |
121 | *) | |
122 | echo "$name: Invalid response." 1>&2 | |
123 | ;; | |
124 | esac | |
125 | done | |
126 | fi | |
127 | else | |
128 | return 0 | |
129 | fi | |
130 | } | |
131 | ||
132 | # i is the index of the filename being examined | |
133 | # lastarg is the index of the last filename before the dest directory name | |
134 | typeset -i i=0 lastarg=$(($#-1)) | |
135 | ||
136 | # Sets argv[0..$#-1] | |
137 | argv=("$@") | |
138 | $debug && echo argv = "${argv[@]}" 1>&2 | |
139 | dest=${argv[lastarg]} | |
140 | ||
141 | if $debug; then | |
142 | echo \ | |
143 | "interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug | |
144 | lastarg=$lastarg dest=$dest name=$name cmd=$cmd | |
145 | files=$*" 1>&2 | |
146 | fi | |
147 | ||
148 | if $noover || $overwrite; then | |
149 | $debug && echo "checking for existance of directories..." 1>&2 | |
150 | # If the destination is not intended to be a directory... | |
151 | if [ $# -eq 2 ] && [ ! -d "$dest" ]; then | |
152 | Check "$dest" "$1" || exit 0 # No files to copy | |
153 | else | |
154 | while [ $i -lt $lastarg ]; do | |
155 | Check "$dest/${argv[i]##*/}" "${argv[i]}" || unset argv[i] | |
156 | let i+=1 | |
157 | done | |
158 | fi | |
159 | fi | |
160 | ||
161 | [ ${#argv[@]} -lt 2 ] && exit 0 | |
162 | ||
163 | # If only 2 args are given, mv/cp will not insist that the destination | |
164 | # be a directory, which we want if the destination ends in "/" or if | |
165 | # the original number of args was >2. | |
166 | # $# is still the original number of args. | |
167 | # Tack the file name onto the destination to force this behaviour. | |
168 | ||
169 | lastisslash() | |
170 | { | |
171 | case "$1" in | |
172 | */) return 0;; | |
173 | *) return 1;; | |
174 | esac | |
175 | } | |
176 | ||
177 | if [ ${#argv[@]} = 2 ] && { lastisslash "$2" || [ $# -gt 2 ]; }; then | |
178 | $debug && echo "Appending filename." 1>&2 | |
179 | # Don't know which element of argv[] holds the source filename, | |
180 | # since may have started with more than 1 source file & had some unset. | |
181 | # So, compact args to make it easy to find the set one. | |
182 | argv=("${argv[@]}") | |
183 | argv[1]="${argv[1]}/${argv[0]##*/}" | |
184 | fi | |
185 | ||
186 | $debug && echo "Executing command: $cmd ${argv[@]}" 1>&2 | |
187 | exec $cmd "${argv[@]}" |