2
# rdiffdir -- Extend rdiff functionality to directories
3
# Version $version released June 30, 2002
5
# Copyright (C) 2002 Ben Escoto <bescoto@stanford.edu>
7
# This program is licensed under the GNU General Public License (GPL).
8
# you can redistribute it and/or modify it under the terms of the GNU
9
# General Public License as published by the Free Software Foundation,
10
# Inc., 675 Mass Ave, Cambridge MA 02139, USA; either version 2 of the
11
# License, or (at your option) any later version. Distributions of
12
# rdiff-backup should include a copy of the GPL in a file called
13
# COPYING. The GPL is also available online at
14
# http://www.gnu.org/copyleft/gpl.html.
16
# See http://rdiff-backup.stanford.edu/duplicity for more information.
17
# Please send mail to me or the mailing list if you find bugs or have
20
import sys, getopt, gzip, os
21
from duplicity import diffdir, patchdir, log, tarfile, globals, selection, path
23
# If set, compress diff and delta files using gzip
26
# If set, when computing delta, also compute signature and write to
30
# Add selection argument tuples to this
33
def parse_cmdline_options(arglist):
34
"""Parse argument list"""
35
global gzip_compress, select_opts, sig_fileobj
36
try: optlist, args = getopt.getopt(arglist, "v:Vz",
37
["gzip-compress", "exclude=", "exclude-device-files",
38
"exclude-filelist=", "exclude-filelist-stdin",
39
"exclude-other-filesystems", "exclude-regexp=", "include=",
40
"include-filelist=", "include-filelist-stdin",
41
"include-regexp=", "null-separator", "verbosity=",
43
except getopt.error, e:
44
command_line_error("Bad command line option: %s" % (str(e),))
46
for opt, arg in optlist:
47
if opt == "--gzip_compress" or opt == "-z": gzip_compress = 1
48
elif (opt == "--exclude" or opt == "--exclude-regexp" or
49
opt == "--include" or opt == "--include-regexp"):
50
select_opts.append((opt, arg))
51
elif (opt == "--exclude-device-files" or
52
opt == "--exclude-other-filesystems"):
53
select_opts.append((opt, None))
54
elif opt == "--exclude-filelist" or opt == "--include-filelist":
55
select_opts.append((opt, (arg, open(arg, "rb"))))
56
elif (opt == "--exclude-filelist-stdin" or
57
opt == "--include-filelist-stdin"):
58
select_opts.append((opt, ("stdin", sys.stdin)))
59
elif opt == "--null-separator": globals.null_separator = 1
61
print "rdiffdir", str(globals.version)
63
elif opt == "-v" or opt == "--verbosity": log.setverbosity(int(arg))
64
elif opt == "--write-sig-to" or opt == "--write-signature-to":
65
sig_fileobj = get_fileobj(arg, "wb")
66
else: command_line_error("Unknown option %s" % opt)
70
def command_line_error(message):
71
"""Indicate a command line error and exit"""
72
sys.stderr.write("Error: %s\n" % (message,))
73
sys.stderr.write("See the rdiffdir manual page for instructions\n")
76
def check_does_not_exist(filename):
77
"""Exit with error message if filename already exists"""
78
try: os.lstat(filename)
80
else: log.FatalError("File %s already exists, will not "
81
"overwrite." % filename)
84
"""Figure out the main action from the arguments"""
85
def require_args(num):
86
if len(args)-1 < num: command_line_error("Too few arguments")
87
elif len(args)-1 > num: command_line_error("Too many arguments")
89
if not args: command_line_error("No arguments found")
91
if command == "sig" or command == "signature":
94
elif command == "tar": require_args(2)
95
elif command == "delta": require_args(3)
96
elif command == "patch": require_args(2)
97
return command, args[1:]
99
def get_selection(filename):
100
"""Return selection iter starting at path with arguments applied"""
101
sel = selection.Select(path.Path(filename))
102
sel.ParseArgs(select_opts)
103
return sel.set_iter()
105
def get_fileobj(filename, mode):
106
"""Get file object or stdin/stdout from filename"""
107
if mode == "r" or mode == "rb":
108
if filename == "-": fp = sys.stdin
109
else: fp = open(filename, mode)
110
elif mode == "w" or mode == "wb":
111
if filename == "-": fp = sys.stdout
113
check_does_not_exist(filename)
114
fp = open(filename, mode)
115
else: assert 0, "Unknown mode " + str(mode)
117
if gzip_compress: return gzip.GzipFile(None, fp.mode, 9, fp)
120
def write_sig(dirname, outfp):
121
"""Write signature of dirname into file object outfp"""
122
diffdir.write_block_iter(diffdir.DirSig(get_selection(dirname)), outfp)
124
def write_delta(dirname, sig_infp, outfp):
125
"""Write delta to fileobj outfp, reading from dirname and sig_infp"""
126
delta_iter = diffdir.DirDelta(get_selection(dirname), sig_infp)
127
diffdir.write_block_iter(delta_iter, outfp)
128
assert not outfp.close()
130
def write_delta_and_sig(filename, sig_infp, delta_outfp, sig_outfp):
131
"""Write delta and also signature of filename"""
132
sel = get_selection(filename)
133
delta_iter = diffdir.DirDelta_WriteSig(sel, sig_infp, sig_outfp)
134
diffdir.write_block_iter(delta_iter, outfp)
135
assert not sig_outfp.close()
137
def patch(dirname, deltafp):
138
"""Patch dirname, reading delta tar from deltafp"""
139
patchdir.Patch(path.Path(dirname), deltafp)
141
def write_tar(dirname, outfp):
142
"""Store dirname into a tarfile, write to outfp"""
143
diffdir.write_block_iter(diffdir.DirFull(get_selection(filename)), outfp)
145
def write_tar_and_sig(dirname, outfp, sig_outfp):
146
"""Write tar of dirname to outfp, signature of same to sig_outfp"""
147
full_iter = diffdir.DirFull_WriteSig(get_selection(filename), sig_outfp)
148
diffdir.write_block_iter(full_iter, outfp)
153
args = parse_cmdline_options(sys.argv[1:])
154
action, file_args = get_action(args)
156
write_sig(file_args[0], get_fileobj(file_args[1], "wb"))
157
elif action == "delta":
158
sig_infp = get_fileobj(file_args[0], "rb")
159
delta_outfp = get_fileobj(file_args[2], "wb")
160
if sig_fileobj: write_delta_and_sig(file_args[1], sig_infp,
161
delta_outfp, sig_fileobj)
162
else: write_delta(file_args[1], sig_infp, delta_outfp)
163
elif action == "patch":
164
patch(file_args[0], get_fileobj(file_args[1], "rb"))
165
elif action == "tar":
166
if sig_fileobj: write_tar_and_sig(file_args[0],
167
get_fileobj(file_args[1], "wb"),
169
else: write_tar(file_args[0], get_fileobj(file_args[1], "wb"))
170
else: command_line_error("Bad command " + action)
173
if __name__ == "__main__": main()