22
22
import filecmp, os, shutil
23
from walker import Walker
24
from stdhome.file_matcher import FileMatcher
25
import stdhome.the as the
23
from .walker import Walker
24
from stdhome import the
25
from stdhome import util
28
28
class CopyBaseWalker( Walker ):
29
29
"""The copy-base walker traverses a walklist ruthlessly mirroring src to dst.
30
30
It is designed to be the base class of both the copy-in and copy-out walker,
31
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:
35
def __init__( self, updated_files = None ):
36
self.accept_list = FileMatcher()
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 ))
39
63
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 )
41
70
# src entity is directory
42
71
if src.type == 'd':
44
# if dst entity doesn't exist, create directory (no need to recurse,
45
# since we're copying the whole directory)
73
# if dst entity doesn't exist, create it (and recurse)
46
74
if dst.type == '_':
47
if the.verbose > 1: self.print_cp( rel_file, 'd', '_' )
75
self.print_op( rel_file, 'd', '*', '_' )
48
76
os.mkdir( dst.file )
49
77
shutil.copystat( src.file, dst.file )
52
80
# if dst entity is a directory, copy permissions, as required (and
54
82
elif dst.type == 'd':
55
# TODO: should check permission and only do as necessary
56
if the.verbose > 1: self.print_cp( rel_file, 'd', 'd' )
57
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' )
60
# TODO: if dst entity is a symlink to a directory, and rel_file is
61
# matched by the acceptable symlinks file matcher, just recurse
62
# elif dst.link_type == 'd' and self.accept_list.match( rel_file ):
63
# if the.verbose > 1: self.print_cp( rel_file, 'd', 'd', '@' )
90
# if dst entity is a symlink to a directory, and this is an
91
# 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' )
66
97
# if dst entity is a file or symlink in home dir, replace it with
67
98
# directory (no need to recurse, since we're copying the whole
69
100
elif dst.type == 'f' or dst.type == 'l':
70
if the.verbose > 1: self.print_cp( rel_file, 'd', dst.type )
101
self.print_op( rel_file, 'd', '*', dst.type )
71
102
os.unlink( dst.file )
73
shutil.copystat( src.file, dst.file )
103
shutil.copytree( src.file, dst.file, True )
77
106
raise NotImplementedError()
79
108
# src entity is file
109
elif src.type == 'f':
82
111
# if dst entity doesn't exist, copy file
83
112
if dst.type == '_':
84
if the.verbose > 1: self.print_cp( rel_file, 'f', '_' )
113
self.print_op( rel_file, 'f', '*', '_' )
85
114
shutil.copy( src.file, dst.file )
86
115
shutil.copystat( src.file, dst.file )
88
117
# if dst entity is a file, replace it only if it differs
89
118
elif dst.type == 'f':
90
119
if not filecmp.cmp( src.file, dst.file ):
91
if the.verbose > 1: self.print_cp( rel_file, 'f', 'f' )
120
self.print_op( rel_file, 'f', '*', 'f' )
92
121
os.unlink( dst.file )
93
122
shutil.copy( src.file, dst.file )
94
123
shutil.copystat( src.file, dst.file )
96
if the.verbose > 1: self.print_cp( rel_file, 'f', 'f', '=' )
125
self.print_op( rel_file, 'f', '=', 'f' )
98
127
# if dst entity is a directory, replace it with file
99
128
elif dst.type == 'd':
100
if the.verbose > 1: self.print_cp( rel_file, 'f', 'd' )
129
self.print_op( rel_file, 'f', '*', 'd' )
101
130
shutil.rmtree( dst.file )
102
131
shutil.copy( src.file, dst.file )
103
132
shutil.copystat( src.file, dst.file )
105
134
# if dst entity is a symlink, replace it with file
106
135
elif dst.type == 'l':
107
if the.verbose > 1: self.print_cp( rel_file, 'f', 'l' )
136
self.print_op( rel_file, 'f', '*', 'l' )
108
137
os.unlink( dst.file )
109
138
shutil.copy( src.file, dst.file )
110
139
shutil.copystat( src.file, dst.file )
113
142
raise NotImplementedError()
115
144
# src entity is a symlink
145
elif src.type == 'l':
118
147
# if dst entity doesn't exist, copy symlink
119
148
if dst.type == '_':
120
if the.verbose > 1: self.print_cp( rel_file, 'l', '_' )
149
self.print_op( rel_file, 'l', '*', '_' )
121
150
os.symlink( os.readlink( src.file ), dst.file )
123
152
# if dst entity is a symlink, replace it only if it differs
124
153
elif dst.type == 'l':
125
154
if os.readlink( src.file ) != os.readlink( dst.file ):
126
if the.verbose > 1: self.print_cp( rel_file, 'l', 'l' )
155
self.print_op( rel_file, 'l', '*', 'l' )
127
156
os.unlink( dst.file )
128
157
os.symlink( os.readlink( src.file ), dst.file )
130
if the.verbose > 1: self.print_cp( rel_file, 'l', 'l', '=' )
159
self.print_op( rel_file, 'l', '=', 'l' )
132
161
# if dst entity is a file, replace it with symlink
133
162
elif dst.type == 'f':
134
if the.verbose > 1: self.print_cp( rel_file, 'l', 'f' )
163
self.print_op( rel_file, 'l', '*', 'f' )
135
164
os.unlink( dst.file )
136
165
os.symlink( os.readlink( src.file ), dst.file )
138
# if dst entity is a directory, replace it with symlink
167
# if dst entity is a directory...
139
168
elif dst.type == 'd':
140
if the.verbose > 1: self.print_cp( rel_file, 'l', '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' )
141
179
shutil.rmtree( dst.file )
142
180
os.symlink( os.readlink( src.file ), dst.file )