22
22
import filecmp, os, shutil
23
23
from walker import Walker
24
from stdhome import the
25
from stdhome import util
24
import stdhome.the as the
28
27
class CopyBaseWalker( Walker ):
29
28
"""The copy-base walker traverses a walklist ruthlessly mirroring src to dst.
30
29
It is designed to be the base class of both the copy-in and copy-out walker,
31
30
both of which are specialisations of this purpose. See them for more
32
information. The report method, which can be overridden in derived classes,
33
takes, in addition to the relative filename, a source file type, an
34
operation, and a destination file type. Valid file types are f (file), l
35
(symlink), d (directory) and _ (non-existant). Valid operations are *
36
(modify), = (skip: same), @ (skip: symlink substitute) and # (skip:
43
self.check_src_symlinks = False
44
self.check_dst_symlinks = False
45
self.check_dst_ignores = False
49
def print_op( self, rel_file, src, op, dst ):
52
if self.report and op == '*':
54
print " N %s" % ( rel_file )
56
print " D %s" % ( rel_file )
58
print " M %s" % ( rel_file )
60
print " K %s" % ( rel_file )
63
37
def process( self, rel_file, src, dst ):
66
if self.check_dst_ignores and the.config.ignores.matches( rel_file ):
67
self.print_op( rel_file, src.type, '#', dst.type )
70
39
# src entity is directory
71
40
if src.type == 'd':
73
# if dst entity doesn't exist, create it (and recurse)
42
# if dst entity doesn't exist, create directory (no need to recurse,
43
# since we're copying the whole directory)
74
44
if dst.type == '_':
75
self.print_op( rel_file, 'd', '*', '_' )
77
shutil.copystat( src.file, dst.file )
45
if the.verbose >= 2: self.print_op( rel_file, 'd>_' )
46
shutil.copytree( src.file, dst.file, True )
80
48
# if dst entity is a directory, copy permissions, as required (and
82
50
elif dst.type == 'd':
83
if os.stat( src.file ).st_mode != os.stat( dst.file ).st_mode:
84
self.print_op( rel_file, 'd', '*', 'd' )
85
shutil.copystat( src.file, dst.file )
87
self.print_op( rel_file, 'd', '=', 'd' )
51
# TODO: should check permission and only do as necessary
52
if the.verbose >= 2: self.print_op( rel_file, 'd>d' )
53
shutil.copystat( src.file, dst.file )
90
56
# if dst entity is a symlink to a directory, and this is an
91
57
# acceptable substitute, just recurse
92
elif self.check_dst_symlinks and dst.link_type == 'd' and \
93
the.config.symlinks.matches( rel_file ):
94
self.print_op( rel_file, 'd', '@', 'd' )
58
elif dst.link_type == 'd' and \
59
self.accept_list and self.accept_list.match( rel_file ):
60
if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
97
63
# if dst entity is a file or symlink in home dir, replace it with
98
64
# directory (no need to recurse, since we're copying the whole
100
66
elif dst.type == 'f' or dst.type == 'l':
101
self.print_op( rel_file, 'd', '*', dst.type )
67
if the.verbose >= 2: self.print_op( rel_file, 'd>' + dst.type )
102
68
os.unlink( dst.file )
103
69
shutil.copytree( src.file, dst.file, True )
111
77
# if dst entity doesn't exist, copy file
112
78
if dst.type == '_':
113
self.print_op( rel_file, 'f', '*', '_' )
79
if the.verbose >= 2: self.print_op( rel_file, 'f>_' )
114
80
shutil.copy( src.file, dst.file )
115
81
shutil.copystat( src.file, dst.file )
117
83
# if dst entity is a file, replace it only if it differs
118
84
elif dst.type == 'f':
119
85
if not filecmp.cmp( src.file, dst.file ):
120
self.print_op( rel_file, 'f', '*', 'f' )
86
if the.verbose >= 2: self.print_op( rel_file, 'f>f' )
121
87
os.unlink( dst.file )
122
88
shutil.copy( src.file, dst.file )
123
89
shutil.copystat( src.file, dst.file )
125
self.print_op( rel_file, 'f', '=', 'f' )
91
if the.verbose >= 2: self.print_op( rel_file, 'f=f' )
127
93
# if dst entity is a directory, replace it with file
128
94
elif dst.type == 'd':
129
self.print_op( rel_file, 'f', '*', 'd' )
95
if the.verbose >= 2: self.print_op( rel_file, 'f>d' )
130
96
shutil.rmtree( dst.file )
131
97
shutil.copy( src.file, dst.file )
132
98
shutil.copystat( src.file, dst.file )
134
100
# if dst entity is a symlink, replace it with file
135
101
elif dst.type == 'l':
136
self.print_op( rel_file, 'f', '*', 'l' )
102
if the.verbose >= 2: self.print_op( rel_file, 'f>l' )
137
103
os.unlink( dst.file )
138
104
shutil.copy( src.file, dst.file )
139
105
shutil.copystat( src.file, dst.file )
147
113
# if dst entity doesn't exist, copy symlink
148
114
if dst.type == '_':
149
self.print_op( rel_file, 'l', '*', '_' )
115
if the.verbose >= 2: self.print_op( rel_file, 'l>_' )
150
116
os.symlink( os.readlink( src.file ), dst.file )
152
118
# if dst entity is a symlink, replace it only if it differs
153
119
elif dst.type == 'l':
154
120
if os.readlink( src.file ) != os.readlink( dst.file ):
155
self.print_op( rel_file, 'l', '*', 'l' )
121
if the.verbose >= 2: self.print_op( rel_file, 'l>l' )
156
122
os.unlink( dst.file )
157
123
os.symlink( os.readlink( src.file ), dst.file )
159
self.print_op( rel_file, 'l', '=', 'l' )
125
if the.verbose >= 2: self.print_op( rel_file, 'l=l' )
161
127
# if dst entity is a file, replace it with symlink
162
128
elif dst.type == 'f':
163
self.print_op( rel_file, 'l', '*', 'f' )
129
if the.verbose >= 2: self.print_op( rel_file, 'l>f' )
164
130
os.unlink( dst.file )
165
131
os.symlink( os.readlink( src.file ), dst.file )
167
# if dst entity is a directory...
133
# if dst entity is a directory, and src entity is a symlink to a
134
# directory, and this is an acceptable substitute, just recurse
135
elif dst.type == 'd' and src.link_type == 'd' and \
136
self.accept_list and self.accept_list.match( rel_file ):
137
if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
140
# if dst entity is a directory, replace it with symlink
168
141
elif dst.type == 'd':
170
# if src entity is a symlink to a directory, and this is an
171
# acceptable substitute, just recurse
172
if self.check_src_symlinks and src.link_type == 'd' and \
173
the.config.symlinks.matches( rel_file ):
174
self.print_op( rel_file, 'd', '@', 'd' )
177
# else replace it with a symlink
178
self.print_op( rel_file, 'l', '*', 'd' )
142
if the.verbose >= 2: self.print_op( rel_file, 'l>d' )
179
143
shutil.rmtree( dst.file )
180
144
os.symlink( os.readlink( src.file ), dst.file )