/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome

« back to all changes in this revision

Viewing changes to lib/stdhome/walker/copy_base.py

  • Committer: Tim Marston
  • Date: 2013-12-08 19:28:11 UTC
  • Revision ID: tim@ed.am-20131208192811-r20qj7cgmn4duw11
initial commit; basic app startup and initial command-line processing

Show diffs side-by-side

added added

removed removed

1
 
# copy_base.py
2
 
#
3
 
# Copyright (C) 2013 to 2014 Tim Marston <tim@edm.am>
4
 
#
5
 
# This file is part of stdhome (hereafter referred to as "this program").
6
 
# See http://ed.am/dev/stdhome for more information.
7
 
#
8
 
# This program is free software: you can redistribute it and/or modify
9
 
# it under the terms of the GNU General Public License as published by
10
 
# the Free Software Foundation, either version 3 of the License, or
11
 
# (at your option) any later version.
12
 
#
13
 
# This program is distributed in the hope that it will be useful,
14
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 
# GNU General Public License for more details.
17
 
#
18
 
# You should have received a copy of the GNU General Public License
19
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 
 
21
 
 
22
 
import filecmp, os, shutil
23
 
from walker import Walker
24
 
from stdhome import the
25
 
from stdhome import util
26
 
 
27
 
 
28
 
class CopyBaseWalker( Walker ):
29
 
        """The copy-base walker traverses a walklist ruthlessly mirroring src to dst.
30
 
        It is designed to be the base class of both the copy-in and copy-out walker,
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:
37
 
        ignored).
38
 
 
39
 
        """
40
 
 
41
 
 
42
 
        def __init__( self ):
43
 
                self.check_src_symlinks = False
44
 
                self.check_dst_symlinks = False
45
 
                self.check_dst_ignores = False
46
 
                self.report = False
47
 
 
48
 
 
49
 
        def print_op( self, rel_file, src, op, dst ):
50
 
 
51
 
                # report changes
52
 
                if self.report and op == '*':
53
 
                        if dst == '_':
54
 
                                print " N  %s" % ( rel_file )
55
 
                        elif src == '_':
56
 
                                print " D  %s" % ( rel_file )
57
 
                        elif src == dst:
58
 
                                print " M  %s" % ( rel_file )
59
 
                        else:
60
 
                                print " K  %s" % ( rel_file )
61
 
 
62
 
 
63
 
        def process( self, rel_file, src, dst ):
64
 
 
65
 
                # ignore?
66
 
                if self.check_dst_ignores and the.config.ignores.matches( rel_file ):
67
 
                        self.print_op( rel_file, src.type, '#', dst.type )
68
 
                        return True
69
 
 
70
 
                # src entity is directory
71
 
                if src.type == 'd':
72
 
 
73
 
                        # if dst entity doesn't exist, create it (and recurse)
74
 
                        if dst.type == '_':
75
 
                                self.print_op( rel_file, 'd', '*', '_' )
76
 
                                os.mkdir( dst.file )
77
 
                                shutil.copystat( src.file, dst.file )
78
 
                                return True
79
 
 
80
 
                        # if dst entity is a directory, copy permissions, as required (and
81
 
                        # recurse)
82
 
                        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 )
86
 
                                else:
87
 
                                        self.print_op( rel_file, 'd', '=', 'd' )
88
 
                                return True
89
 
 
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' )
95
 
                                return True
96
 
 
97
 
                        # if dst entity is a file or symlink in home dir, replace it with
98
 
                        # directory (no need to recurse, since we're copying the whole
99
 
                        # directory)
100
 
                        elif dst.type == 'f' or dst.type == 'l':
101
 
                                self.print_op( rel_file, 'd', '*', dst.type )
102
 
                                os.unlink( dst.file )
103
 
                                shutil.copytree( src.file, dst.file, True )
104
 
 
105
 
                        else:
106
 
                                raise NotImplementedError()
107
 
 
108
 
                # src entity is file
109
 
                elif src.type == 'f':
110
 
 
111
 
                        # if dst entity doesn't exist, copy file
112
 
                        if dst.type == '_':
113
 
                                self.print_op( rel_file, 'f', '*', '_' )
114
 
                                shutil.copy( src.file, dst.file )
115
 
                                shutil.copystat( src.file, dst.file )
116
 
 
117
 
                        # if dst entity is a file, replace it only if it differs
118
 
                        elif dst.type == 'f':
119
 
                                if not filecmp.cmp( src.file, dst.file ):
120
 
                                        self.print_op( rel_file, 'f', '*', 'f' )
121
 
                                        os.unlink( dst.file )
122
 
                                        shutil.copy( src.file, dst.file )
123
 
                                        shutil.copystat( src.file, dst.file )
124
 
                                else:
125
 
                                        self.print_op( rel_file, 'f', '=', 'f' )
126
 
 
127
 
                        # if dst entity is a directory, replace it with file
128
 
                        elif dst.type == 'd':
129
 
                                self.print_op( rel_file, 'f', '*', 'd' )
130
 
                                shutil.rmtree( dst.file )
131
 
                                shutil.copy( src.file, dst.file )
132
 
                                shutil.copystat( src.file, dst.file )
133
 
 
134
 
                        # if dst entity is a symlink, replace it with file
135
 
                        elif dst.type == 'l':
136
 
                                self.print_op( rel_file, 'f', '*', 'l' )
137
 
                                os.unlink( dst.file )
138
 
                                shutil.copy( src.file, dst.file )
139
 
                                shutil.copystat( src.file, dst.file )
140
 
 
141
 
                        else:
142
 
                                raise NotImplementedError()
143
 
 
144
 
                # src entity is a symlink
145
 
                elif src.type == 'l':
146
 
 
147
 
                        # if dst entity doesn't exist, copy symlink
148
 
                        if dst.type == '_':
149
 
                                self.print_op( rel_file, 'l', '*', '_' )
150
 
                                os.symlink( os.readlink( src.file ), dst.file )
151
 
 
152
 
                        # if dst entity is a symlink, replace it only if it differs
153
 
                        elif dst.type == 'l':
154
 
                                if os.readlink( src.file ) != os.readlink( dst.file ):
155
 
                                        self.print_op( rel_file, 'l', '*', 'l' )
156
 
                                        os.unlink( dst.file )
157
 
                                        os.symlink( os.readlink( src.file ), dst.file )
158
 
                                else:
159
 
                                        self.print_op( rel_file, 'l', '=', 'l' )
160
 
 
161
 
                        # if dst entity is a file, replace it with symlink
162
 
                        elif dst.type == 'f':
163
 
                                self.print_op( rel_file, 'l', '*', 'f' )
164
 
                                os.unlink( dst.file )
165
 
                                os.symlink( os.readlink( src.file ), dst.file )
166
 
 
167
 
                        # if dst entity is a directory...
168
 
                        elif dst.type == 'd':
169
 
 
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' )
175
 
                                        return True
176
 
 
177
 
                                # else replace it with a symlink
178
 
                                self.print_op( rel_file, 'l', '*', 'd' )
179
 
                                shutil.rmtree( dst.file )
180
 
                                os.symlink( os.readlink( src.file ), dst.file )
181
 
 
182
 
                        else:
183
 
                                raise NotImplementedError()
184
 
 
185
 
                # src entity is missing
186
 
                elif src.type == '_':
187
 
 
188
 
                        # if dst entity doesn't exist, we're good
189
 
                        if dst.type == '_':
190
 
                                pass;
191
 
 
192
 
                        # if dst entity is a file or symlink, delete it
193
 
                        elif dst.type == 'f' or dst.type == 'l':
194
 
                                self.print_op( rel_file, '_', '*', dst.type )
195
 
                                os.unlink( dst.file )
196
 
 
197
 
                        # if dst entity is a directory, delete it
198
 
                        elif dst.type == 'd':
199
 
                                self.print_op( rel_file, '_', '*', 'd' )
200
 
                                shutil.rmtree( dst.file )
201
 
 
202
 
                        else:
203
 
                                raise NotImplementedError()
204
 
 
205
 
                # if we got here, we don't want to recurse...
206
 
                return False