/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# walker.py
#
# Copyright (C) 2013 to 2014 Tim Marston <tim@edm.am>
#
# This file is part of stdhome (hereafter referred to as "this program").
# See http://ed.am/dev/stdhome for more information.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import os
import stdhome.the as the


class Walker:
	"""Classes which derive from the Walker class are intended to walk through
	(traverse) a single list of filenames in two locations.  They must provide
	the two locations (src_dir and dst_dir) and a list of relative filenames
	(walk_list).  For each files in the walk list, the process() method is
	called, allowing the deriving class to perform some kind of processing.  See
	process() for more information.
	"""


	def walk( self ):
		"""Iterates over self.walk_list, calling process() for each entry in turn.  For
		directory entries, where process() returns False, subsequent entries in
		walk_list that fall under the directory are skipped.
		"""

		if the.verbose >= 3:
			print("walking [%s]" % self.__class__.__name__)

		skip = ''
		for rel_file in self.walk_list:

			# if we're skipping, skip entries in subdirectories, or turn off
			# skipping if it no longer matches
			if skip:
				if skip == rel_file[ : len( skip ) ]:
					continue
				else:
					skip = ''

			src = Walker.File( os.path.join( self.src_dir, rel_file ) )
			dst = Walker.File( os.path.join( self.dst_dir, rel_file ) )

			# process ths entry
			recurse = self.process( rel_file, src, dst )

			# Set up skipping, as required.  Note that we don't check to see if
			# we're dealing with a directory here.  We can't, because we've no
			# way of knowing what to check.  It could be src_type or dst_type
			# (if src_dir or dst_dir was used to generate the walk list) or it
			# could be neither (if the walk list came from somewhere else).  But
			# it shouldn't matter: we add a path seperateor (os.sep) to the end
			# of the filename, so it wuill only match files that are descendents
			# of a directory with the name of this file.
			if not recurse: skip = rel_file + os.sep


	class File:

		def __init__( self, full_file ):
			self.file = full_file
			if not os.path.exists( self.file ):
				self.type = '_'
			elif os.path.isfile( self.file ):
				self.type = 'f'
			elif os.path.isdir( self.file ):
				self.type = 'd'
			else:
				self.type = '?'
			if os.path.islink( self.file ):
				self.link_type = self.type
				self.type = 'l'
			else:
				self.link_type = False

		def get_type_name( self ):
			if self.type == 'l': return 'symlink'
			elif self.type == 'f': return 'file'
			elif self.type == 'd': return 'directory'
			elif self.type == '_': return 'missing'
			else: return 'unknown'

		def __str__( self ):
			type = self.type
			if( self.link_type ): type += '/' + self.link_type
			return 'File( %s (%s) )' % ( self.file, type )


	@staticmethod
	def generate_walk_list( full_dir, rel_file = '', recurse = True ):
		"""Returns a list of files and directories in full_dir, specified as relative
		files (relative to full_dir), breadth first.

		"""

		# ignore some files
		static_ignores = [ '.stdhome', '.stdhomerc' ] + \
						 the.repo.vcs.ignored_files
		if rel_file in static_ignores:
			return list()

		full_file = os.path.join( full_dir, rel_file )

		# files and links are returned
		if os.path.isfile( full_file ) or os.path.islink( full_file ):
			return [ rel_file ]

		# directories are returned and recursed in to
		elif os.path.isdir( full_file ):
			ret = [ rel_file ] if rel_file != '' else []
			if recurse:
				for file in os.listdir( full_file ):
					ret.extend( Walker.generate_walk_list(
						full_dir, os.path.join( rel_file, file ) ) )
			return sorted( ret )

		# other kinds are invalid
		else:
			raise RuntimeError(
				'unknown/exotic file: %s' % full_file )