# Part of the A-A-P recipe executive: copy and move files (remotely)
# Copyright (C) 2002 Stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here:
http://www.a-a-p.org/COPYING
#
# These are functions to copy and move files. Not only on the local system,
# also to remote systems, using http://, ftp://, etc.
#
import os
import os.path
import string
from Commands import aap_eval
from Dictlist import string2dictlist
from Error import *
from Process import recipe_error
from Util import *
from Work import getrpstack
from Message import *
def copy_move(line_nr, globals, raw_arg, copy):
"""Implementation of ":copy -x from to" and ":move -x from to".
When "copy" is non-zero copying is done, otherwise moving.
"raw_arg" is the whole argument.
"line_nr" is used for error messages."""
# Evaluate $VAR things
arg = aap_eval(line_nr, globals, raw_arg, Expand(0, Expand.quote_aap))
rpstack = getrpstack(globals, line_nr)
# TODO: check if all flags are handled properly
# Copy flags:
# -f forcefully overwrite
# -i interactive, ask the user before overwriting
# -L don't keep symbolic links, create new files and directories
# -p preserve last modification time and permissions as much as possible
# -r -R recursive, enter directories
# -v verbose
# Move flags:
# -f forcefully overwrite
# -i interactive, ask the user before overwriting
# -v verbose
try:
flags, i = get_flags(arg, 0, (copy and "filLprRv") or "fiv")
except UserError, e:
recipe_error(rpstack, e)
# -l: change symlinks to real files
if 'L' in flags:
keepsymlinks = 0
else:
keepsymlinks = 1
# Get the remaining arguments, should be at least two of them.
arglist = string2dictlist(rpstack, arg[i:])
if len(arglist) < 2:
recipe_error(rpstack, _(":%s command requires at least two arguments")
% (copy and "copy") or "move")
from Remote import url_split3
import glob
# Expand and check the "from" arguments.
fromlist = []
for a in arglist[:-1]:
fname = a["name"]
fscheme, fmach, fpath = url_split3(fname)
if fscheme == '':
# It's a local file, expand ~user and wildcards.
fl = glob.glob(os.path.expanduser(fname))
if len(fl) == 0:
recipe_error(rpstack, _('No match for "%s"') % fname)
# For copy without -r sources can't be a directory.
if copy and not ('r' in flags or 'R' in flags):
for l in fl:
if os.path.isdir(l):
recipe_error(rpstack,
_('Copying a directory requires -r flag: %s') % l)
fromlist.extend(fl)
else:
# It's a URL, append without expanding.
if not copy:
recipe_error(rpstack, _('Cannot move from a URL yet: "%s"')
% fname)
fromlist.append(fname)
# Expand and check the "to" argument.
tname = arglist[-1]["name"]
tscheme, tmach, tpath = url_split3(tname)
if tscheme == '':
# For a local destination file expand ~user and wildcards.
l = glob.glob(os.path.expanduser(tname))
if len(l) > 1:
recipe_error(rpstack, _('More than one match for "%s"') % tname)
if len(l) == 1:
tname = l[0]
# If there is more than one source, target must be a directory.
if tscheme == '' and len(fromlist) > 1 and not os.path.isdir(tpath):
recipe_error(rpstack, _('Destination must be a directory: "%s"')
% tpath)
if tscheme == "ftp":
#
# Prepare for uploading through ftp.
#
import ftplib
# For "
[email protected]" split at the @
passwd = ''
acct = ''
i = string.find(tmach, '@')
if i > 0:
user = tmach[:i]
machname = tmach[i+1:]
i = string.find(user, '+')
if i > 0:
passwd = user[i+1:]
user = user[:i]
else:
prompt = (_('Enter password for user %s at %s: ')
% (user, machname))
try:
import getpass
passwd = getpass.getpass(prompt)
except:
# TODO: should display stars for typed chars
passwd = raw_input(prompt)
else:
user = ''
machname = tmach
import aapnetrc
# obtain the login name and password from the netrc file
try:
n = aapnetrc.netrc()
res = n.authenticators(machname)
if res is None:
user = ''
else:
user, acct, passwd = res
except aapnetrc.NetrcParseError, e:
pass
# Try to open the connection to the ftp server.
try:
ftp = ftplib.FTP(machname)
if user != '':
if passwd != '':
if acct != '':
ftp.login(user, passwd, acct)
else:
ftp.login(user, passwd)
else:
ftp.login(user)
else:
ftp.login()
except ftplib.all_errors, e:
recipe_error(rpstack, (_('Cannot open connection for "%s"')
% tname) + str(e))
elif tscheme == "scp":
#
# TODO: Prepare for uploading through scp.
#
pass
elif tscheme != '':
recipe_error(rpstack, _('Can only upload to scp:// and ftp://'))
#
# Loop over all "from" files.
#
for fname in fromlist:
fscheme, fmach, fpath = url_split3(fname)
# If the destination is a directory, append the source file name to the
# destination directory.
if ((tscheme != '' and len(fromlist) > 1)
or (tscheme == '' and os.path.isdir(tname))):
dest = os.path.join(tname, os.path.basename(fname))
destpath = os.path.join(tpath, os.path.basename(fname))
else:
dest = tname
destpath = tpath
# If destination is a local file and "-i" flag used, ask for
# overwriting. Use a special string to allow translating the response
# characters.
if tscheme == '' and 'i' in flags:
if os.path.exists(dest):
reply = raw_input(_('"%s" exists, overwrite? (y/n) ') % dest)
if (len(reply) == 0 or not reply[0]
in _("yY up to four chars that mean yes")[:4]):
if copy:
msg_warning(_("file not copied"))
else:
msg_warning(_("file not moved"))
continue
if fscheme == '' and tscheme == '':
#
# local file copy or move
#
if not copy:
try:
os.rename(fname, dest)
done = 1
except:
done = 0 # renaming failed, try copying
if copy or not done:
try:
import shutil
if os.path.isdir(fname):
# TODO: if -p not used, set timestamps
shutil.copytree(fname, dest, keepsymlinks)
elif not copy or 'p' in flags:
shutil.copy2(fname, dest)
else:
shutil.copy(fname, dest)
except IOError, e:
recipe_error(rpstack, (_('Cannot copy "%s" to "%s"')
% (fname, dest)) + str(e))
if not copy:
os.remove(fname)
if copy:
msg_info(_('Copied "%s" to "%s"') % (fname, dest))
else:
msg_info(_('Moved "%s" to "%s"') % (fname, dest))
else:
if fscheme != '':
# download to local file
from Remote import url_download
try:
if tscheme != '':
tmpfile, rtime = url_download(fname)
else:
tmpfile, rtime = url_download(fname, dest)
except IOError, e:
recipe_error(rpstack, (_('Cannot download "%s" to "%s"')
% (fname, dest)) + str(e))
if tscheme != '':
msg_info(_('Attempting upload to "%s"' % dest))
if fscheme != '':
# use temporary file
fromfile = tmpfile
else:
fromfile = fpath
if tscheme == 'ftp':
try:
f = open(fromfile, "r")
ftp.storbinary("STOR " + destpath, f, 8192)
f.close()
except ftplib.all_errors, e:
recipe_error(rpstack, (_('Cannot upload "%s" to "%s"')
% (fname, dest)) + str(e))
elif tscheme == 'scp':
cmd = 'scp %s %s:%s' % (fromfile, tmach, destpath)
try:
logged_system(cmd)
except IOError, e:
recipe_error(rpstack, (_('Cannot upload "%s" to "%s"')
% (fname, dest)) + str(e))
if fscheme != '':
# delete temporary file
os.remove(tmpfile)
msg_info(_('Uploaded to "%s"' % dest))
else:
if not copy:
os.remove(fname)
msg_info(_('Moved "%s" to "%s"' % (fname, dest)))
else:
msg_info(_('Uploaded "%s" to "%s"' % (fname, dest)))
# End of loop over all "from" files.
if tscheme == 'ftp':
ftp.quit() # close connection to ftp server
if fscheme != '':
from Remote import url_cleanup
url_cleanup(fscheme)
# vim: set sw=4 sts=4 tw=79 fo+=l: