]>
Commit | Line | Data |
---|---|---|
ccc6cda3 JA |
1 | #! /bin/bash |
2 | # | |
3 | # original from: | |
4 | # | |
5 | # @(#) frcp.ksh 2.2 93/11/14 | |
6 | # 92/06/29 john h. dubois iii (john@armory.com) | |
7 | # 92/10/14 Cleaned up, improved, added -d and -r options | |
8 | # 92/11/11 Made work with a dest of '.' | |
9 | # 93/07/09 Added -l and -n options, & login as anonymous if no .netrc entry | |
10 | # 93/11/14 Use either passwd or password in .netrc, since ftp does. | |
11 | # | |
12 | # conversion to bash v2 syntax by Chet Ramey | |
13 | # | |
14 | # frcp: ftp front end with rcp-like syntax. | |
15 | # Note: requires any machine names given to be listed with | |
16 | # user and password in .netrc. If not, anonymous FTP is | |
17 | # done. | |
18 | # | |
19 | # full path to ftp binary | |
20 | if [ -x /usr/bin/ftp ]; then | |
21 | FTP=/usr/bin/ftp; | |
22 | elif [ -x /usr/ucb/ftp ]; then | |
23 | FTP=/usr/ucb/ftp | |
24 | else | |
25 | FTP=ftp | |
26 | fi | |
27 | ||
28 | istrue() | |
29 | { | |
30 | test 0 -ne "$1" | |
31 | } | |
32 | isfalse() | |
33 | { | |
34 | test 0 -eq "$1" | |
35 | } | |
36 | ||
37 | # For each filename given, put the filename in filename[n] | |
38 | # and the machine it is on in machine[n]. | |
39 | function SplitNames { | |
40 | typeset file | |
41 | typeset -i i=1 | |
42 | ||
43 | unset filename[*] machine[*] | |
44 | for file; do | |
45 | case "$file" in | |
46 | *:*) machine[i]=${file%%:*} ;; | |
47 | *) machine[i]=$LocalMach ;; | |
48 | esac | |
49 | filename[i]=${file#*:} | |
50 | let i+=1 | |
51 | done | |
52 | } | |
53 | ||
54 | function verboseprint { | |
55 | echo "$@" | |
56 | echo "$@" 1>&2 | |
57 | } | |
58 | ||
59 | function MakeDir { | |
60 | OFS=$IFS | |
61 | local IFS=/ dir component | |
62 | ||
63 | case "$1" in | |
64 | /*) ;; | |
65 | *) dir=. | |
66 | esac | |
67 | set -- $1 | |
68 | IFS=$OFS | |
69 | for component; do | |
70 | dir=$dir/$component | |
71 | if [ ! -d "$dir" ]; then | |
72 | if mkdir "$dir"; then :; else | |
73 | echo "Could not make directory $dir." >&2 | |
74 | return 1 | |
75 | fi | |
76 | fi | |
77 | done | |
78 | return 0 | |
79 | } | |
80 | ||
81 | lastisdot () | |
82 | { | |
83 | case "$1" in | |
84 | */.|*/..) return 0;; | |
85 | *) return 1;; | |
86 | esac | |
87 | } | |
88 | ||
89 | # CopyFiles: issue ftp(TC) commands to copy files. | |
90 | # Usage: CopyFiles [sourcemachine:]sourcepath ... [destmachine:]destpath | |
91 | # Global vars: | |
92 | # Uses LocalMach (should be name of local machine) | |
93 | # Sets global arrs machine[]/filename[] | |
94 | function CopyFiles { | |
95 | unset machine[*] filename[*] | |
96 | ||
97 | SplitNames "$@" # split names into filename[1..n] and machine[1..n] | |
98 | ||
99 | local DestMach=${machine[$#]} # Machine to copy files to | |
100 | local DestPath=${filename[$#]} # Destination file/dir | |
101 | ||
102 | unset machine[$#] filename[$#] | |
103 | ||
104 | [ -z "$DestPath" ] && DestPath=. # dest was given as machine: | |
105 | ||
106 | # Try to determine if destination should be a directory | |
107 | # so that it can be forced to be a directory. | |
108 | ||
109 | case "$DestPath" in | |
110 | */) ;; # don't add / if trailing / already present | |
111 | *) if [ $# -gt 2 ] || # if more than two args given, last must be a dir | |
112 | # If dest in on local machine, check whether it is a directory | |
b80f6443 | 113 | [ $DestMach = $LocalMach ] && [ -d "$DestPath" ] || |
ccc6cda3 JA |
114 | # If dest ends with . or .., it is a directory |
115 | lastisdot "$DestPath" | |
116 | then | |
117 | DestPath=$DestPath/ | |
118 | fi ;; | |
119 | esac | |
120 | ||
121 | # If one of the above tests made us think dest is a directory, | |
122 | # but it isn't, complain | |
123 | case "$DestPath" in | |
124 | */) if [ "$DestMach" = "$LocalMach" ] && [ ! -d "$DestPath" ]; then | |
125 | echo "Destination is not a directory." 1>&2 | |
126 | exit 1 | |
127 | fi ;; | |
128 | esac | |
129 | ||
130 | DoCopy "$DestMach" "$DestPath" | |
131 | } | |
132 | ||
133 | # Usage: OpenMachine machine-name | |
134 | # Emits login sequence or doesn't, depending on .netrc file and global | |
135 | # variables anon and noanon | |
136 | OpenMachine () | |
137 | { | |
138 | local machine=$1 netrc=$HOME/.netrc user= password= | |
139 | ||
140 | if isfalse $anon && [ -r $netrc ]; then | |
141 | set -- $(gawk ' | |
142 | /machine (.* )?'"$machine"'($| )/,/^ *$/ { | |
143 | Fields[$1] = $2 | |
144 | if ("passwd" in Fields) | |
145 | Fields["password"] = Fields["passwd"] | |
146 | if ("login" in Fields && "password" in Fields) { | |
147 | print Fields["login"] " " Fields["password"] | |
148 | exit | |
149 | } | |
150 | } | |
151 | ' $netrc ) | |
152 | user=$1 | |
153 | password=$2 | |
154 | fi | |
155 | if [ -z "$password" ]; then | |
156 | if istrue $noanon; then | |
157 | echo "No .netrc entry for machine $machine" 1>&2 | |
158 | exit 1 | |
159 | fi | |
160 | user=anonymous | |
161 | password=$USER@$LocalMach | |
162 | fi | |
163 | verboseprint open $machine | |
164 | echo user $user "*******" 1>&2 | |
165 | echo user $user $password | |
166 | } | |
167 | ||
168 | # Usage: DoCopy destination-machine destination-path | |
169 | # Copies the files in global arrs machine[]/filename[] to the given dest | |
170 | # Global vars: | |
171 | # Uses machine[], filename[], LocalMach, check | |
172 | DoCopy () | |
173 | { | |
174 | local DestMach=$1 | |
175 | local DestPath=$2 | |
176 | local OpenMach # Machine that connection is currently open to | |
177 | local OWD=$PWD SourceMach SourceFile | |
178 | local FileName | |
179 | typeset -i i=1 | |
180 | ||
181 | while [ $i -le ${#machine[*]} ]; do | |
182 | istrue $check && verboseprint "runique" | |
183 | ||
184 | SourceMach=${machine[i]} | |
185 | SourceFile=${filename[i]} | |
186 | ||
187 | DestFile=$DestPath | |
188 | # if DestPath is a dir, | |
189 | # add source filename to it without source path | |
190 | case "$DestFile" in | |
191 | */) DestFile=$DestFile${SourceFile##*/} ;; | |
192 | esac | |
193 | ||
194 | if [ $SourceMach = $LocalMach ]; then | |
195 | if [ $DestMach != "$OpenMach" ]; then | |
196 | OpenMachine $DestMach | |
197 | OpenMach=$DestMach | |
198 | fi | |
199 | verboseprint put $SourceFile $DestFile | |
200 | elif [ $DestMach = $LocalMach ]; then | |
201 | if istrue $check && [ -f "$DestFile" ]; then | |
202 | echo "$DestFile already exists." 1>&2 | |
203 | continue | |
204 | fi | |
205 | # If destination is on local machine, | |
206 | # the dest will be a full dir/filename | |
207 | if istrue $createdirs; then | |
208 | MakeDir "${DestFile%/*}" || continue | |
209 | fi | |
210 | if [ $SourceMach != "$OpenMach" ]; then | |
211 | OpenMachine $SourceMach | |
212 | OpenMach=$SourceMach | |
213 | fi | |
214 | # If source filename has wildcards ([, ], *, ?) do an mget | |
215 | case "$SourceFile" in | |
216 | \[*\]|*\**|*\?*) | |
217 | verboseprint lcd "$DestFile" | |
218 | verboseprint mget "$SourceFile" | |
219 | verboseprint lcd $OWD ;; | |
220 | *) verboseprint get "$SourceFile" "$DestFile" ;; | |
221 | esac | |
222 | else | |
223 | echo "Neither source machine \"$SourceMach\" "\ | |
224 | "nor destination machine \"$DestMach\" is local." 1>&2 | |
225 | fi | |
226 | let i+=1 | |
227 | done | |
228 | } | |
229 | ||
230 | # Start of main program | |
231 | name=${0##*/} | |
232 | ||
233 | if [ "$1" = -h ]; then | |
234 | echo \ | |
235 | "$name: do ftp transfers using rcp-style parameters. | |
236 | Usage: $name <source> <destpath> or $name <source> [<source> ...] <destdir> | |
237 | At least one of <source> and <destpath> must be the local system. | |
238 | A remote filename is given as machinename:filename | |
239 | If remote filenames contain wildcards, they will be globbed on the remote | |
240 | machine. Make sure they are quoted when $name is invoked. | |
241 | If the invoking user's .netrc file (see ftp(TC)) contains an entry for the | |
242 | remote system with a login and password supplied, $name will log in using | |
243 | the given login and password. If not, $name will login in as user | |
244 | anonymous and with the user@localsystem as the password. | |
245 | Options: | |
246 | -c: check: do not overwrite files. | |
247 | -d: create directories as needed. | |
248 | -f: force: overwrite files (default). | |
249 | -h: print this help. | |
250 | -l: fail if there is no entry with login and password for the remote system, | |
251 | instead of logging in as anonymous. | |
252 | -n: log in as anonymous even if there is an entry for the remote system in | |
253 | the user's .netrc file. | |
254 | -r: read source/dest filename pairs from the standard input, | |
255 | one pair per line, and copy files accordingly." | |
256 | exit 0 | |
257 | fi | |
258 | ||
259 | typeset -i check=0 createdirs=0 readinput=0 anon=0 noanon=0 | |
260 | ||
261 | while getopts :cdflnr Option | |
262 | do | |
263 | case "$Option" in | |
264 | c) check=1;; | |
265 | d) createdirs=1;; | |
266 | f) check=0;; | |
267 | l) noanon=1;; | |
268 | n) anon=1;; | |
269 | r) readinput=1;; | |
270 | \?) echo "$OPTARG: invalid option."; exit 1;; | |
271 | esac | |
272 | done | |
273 | ||
274 | shift $((OPTIND-1)) | |
275 | ||
276 | LocalMach=`hostname` | |
277 | ||
278 | if istrue $readinput; then | |
279 | while read line; do | |
280 | CopyFiles $line | |
281 | done | $FTP -nv | |
282 | else | |
283 | if [ $# -lt 2 ]; then | |
284 | echo "$name: Not enough arguments. Use -h for help." 1>&2 | |
285 | exit | |
286 | fi | |
287 | CopyFiles "$@" | $FTP -nv | |
288 | fi |