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