22
22
import filecmp, os, shutil
23
from .walker import Walker
24
from stdhome import the
25
from stdhome import util
23
from walker import Walker
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
36
self.check_src_symlinks = False
44
37
self.check_dst_symlinks = False
45
38
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
41
def process( self, rel_file, src, dst ):
66
44
if self.check_dst_ignores and the.config.ignores.matches( rel_file ):
67
self.print_op( rel_file, src.type, '#', dst.type )
46
self.print_op( rel_file, '%s#%s' % ( src.type, dst.type ) )
70
49
# src entity is directory
71
50
if src.type == 'd':
73
# if dst entity doesn't exist, create it (and recurse)
52
# if dst entity doesn't exist, create directory (no need to recurse,
53
# since we're copying the whole directory)
74
54
if dst.type == '_':
75
self.print_op( rel_file, 'd', '*', '_' )
77
shutil.copystat( src.file, dst.file )
55
if the.verbose >= 2: self.print_op( rel_file, 'd>_' )
56
shutil.copytree( src.file, dst.file, True )
80
58
# if dst entity is a directory, copy permissions, as required (and
82
60
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' )
61
# TODO: should check permission and only do as necessary
62
if the.verbose >= 2: self.print_op( rel_file, 'd>d' )
63
shutil.copystat( src.file, dst.file )
90
66
# if dst entity is a symlink to a directory, and this is an
91
67
# acceptable substitute, just recurse
92
68
elif self.check_dst_symlinks and dst.link_type == 'd' and \
93
69
the.config.symlinks.matches( rel_file ):
94
self.print_op( rel_file, 'd', '@', 'd' )
70
if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
97
73
# if dst entity is a file or symlink in home dir, replace it with
98
74
# directory (no need to recurse, since we're copying the whole
100
76
elif dst.type == 'f' or dst.type == 'l':
101
self.print_op( rel_file, 'd', '*', dst.type )
77
if the.verbose >= 2: self.print_op( rel_file, 'd>' + dst.type )
102
78
os.unlink( dst.file )
103
79
shutil.copytree( src.file, dst.file, True )
111
87
# if dst entity doesn't exist, copy file
112
88
if dst.type == '_':
113
self.print_op( rel_file, 'f', '*', '_' )
89
if the.verbose >= 2: self.print_op( rel_file, 'f>_' )
114
90
shutil.copy( src.file, dst.file )
115
91
shutil.copystat( src.file, dst.file )
117
93
# if dst entity is a file, replace it only if it differs
118
94
elif dst.type == 'f':
119
95
if not filecmp.cmp( src.file, dst.file ):
120
self.print_op( rel_file, 'f', '*', 'f' )
96
if the.verbose >= 2: self.print_op( rel_file, 'f>f' )
121
97
os.unlink( dst.file )
122
98
shutil.copy( src.file, dst.file )
123
99
shutil.copystat( src.file, dst.file )
125
self.print_op( rel_file, 'f', '=', 'f' )
101
if the.verbose >= 2: self.print_op( rel_file, 'f=f' )
127
103
# if dst entity is a directory, replace it with file
128
104
elif dst.type == 'd':
129
self.print_op( rel_file, 'f', '*', 'd' )
105
if the.verbose >= 2: self.print_op( rel_file, 'f>d' )
130
106
shutil.rmtree( dst.file )
131
107
shutil.copy( src.file, dst.file )
132
108
shutil.copystat( src.file, dst.file )
134
110
# if dst entity is a symlink, replace it with file
135
111
elif dst.type == 'l':
136
self.print_op( rel_file, 'f', '*', 'l' )
112
if the.verbose >= 2: self.print_op( rel_file, 'f>l' )
137
113
os.unlink( dst.file )
138
114
shutil.copy( src.file, dst.file )
139
115
shutil.copystat( src.file, dst.file )
147
123
# if dst entity doesn't exist, copy symlink
148
124
if dst.type == '_':
149
self.print_op( rel_file, 'l', '*', '_' )
125
if the.verbose >= 2: self.print_op( rel_file, 'l>_' )
150
126
os.symlink( os.readlink( src.file ), dst.file )
152
128
# if dst entity is a symlink, replace it only if it differs
153
129
elif dst.type == 'l':
154
130
if os.readlink( src.file ) != os.readlink( dst.file ):
155
self.print_op( rel_file, 'l', '*', 'l' )
131
if the.verbose >= 2: self.print_op( rel_file, 'l>l' )
156
132
os.unlink( dst.file )
157
133
os.symlink( os.readlink( src.file ), dst.file )
159
self.print_op( rel_file, 'l', '=', 'l' )
135
if the.verbose >= 2: self.print_op( rel_file, 'l=l' )
161
137
# if dst entity is a file, replace it with symlink
162
138
elif dst.type == 'f':
163
self.print_op( rel_file, 'l', '*', 'f' )
139
if the.verbose >= 2: self.print_op( rel_file, 'l>f' )
164
140
os.unlink( dst.file )
165
141
os.symlink( os.readlink( src.file ), dst.file )
171
147
# acceptable substitute, just recurse
172
148
if self.check_src_symlinks and src.link_type == 'd' and \
173
149
the.config.symlinks.matches( rel_file ):
174
self.print_op( rel_file, 'd', '@', 'd' )
150
if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
177
153
# else replace it with a symlink
178
self.print_op( rel_file, 'l', '*', 'd' )
154
if the.verbose >= 2: self.print_op( rel_file, 'l>d' )
179
155
shutil.rmtree( dst.file )
180
156
os.symlink( os.readlink( src.file ), dst.file )
192
168
# if dst entity is a file or symlink, delete it
193
169
elif dst.type == 'f' or dst.type == 'l':
194
self.print_op( rel_file, '_', '*', dst.type )
170
if the.verbose >= 2: self.print_op( rel_file, '_>' + dst.type )
195
171
os.unlink( dst.file )
197
173
# if dst entity is a directory, delete it
198
174
elif dst.type == 'd':
199
self.print_op( rel_file, '_', '*', 'd' )
175
if the.verbose >= 2: self.print_op( rel_file, '_>d' )
200
176
shutil.rmtree( dst.file )