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