2 # Copyright 2011-2014 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
5 """Helper tool for converting installed files to custom prefixes.
7 In other words, eprefixy $D for Gentoo/Prefix."""
14 from portage.util._argparse import ArgumentParser
16 # Argument parsing compatibility for Python 2.6 using optparse.
17 if sys.hexversion < 0x2070000:
18 from optparse import OptionParser
20 from optparse import OptionError
22 CONTENT_ENCODING = 'utf_8'
32 except AttributeError:
33 # magic module seems to be broken
36 class IsTextFile(object):
40 self._call = self._is_text_magic
41 self._m = magic.open(magic.MIME_TYPE)
44 self._call = self._is_text_encoding
45 self._encoding = CONTENT_ENCODING
47 def __call__(self, filename):
49 Returns True if the given file is a text file, and False otherwise.
51 return self._call(filename)
53 def _is_text_magic(self, filename):
54 mime_type = self._m.file(filename)
55 if isinstance(mime_type, bytes):
56 mime_type = mime_type.decode('ascii', 'replace')
57 return mime_type.startswith('text/')
59 def _is_text_encoding(self, filename):
61 for line in io.open(filename, mode='r', encoding=self._encoding):
63 except UnicodeDecodeError:
67 def chpath_inplace(filename, is_text_file, old, new):
69 Returns True if any modifications were made, and False otherwise.
73 orig_stat = os.lstat(filename)
75 f = io.open(filename, buffering=0, mode='r+b')
78 orig_mode = stat.S_IMODE(os.lstat(filename).st_mode)
80 sys.stderr.write('%s: %s\n' % (e, filename))
82 temp_mode = 0o200 | orig_mode
83 os.chmod(filename, temp_mode)
85 f = io.open(filename, buffering=0, mode='r+b')
87 os.chmod(filename, orig_mode)
91 matched_byte_count = 0
98 if in_byte == old[matched_byte_count]:
99 matched_byte_count += 1
100 if matched_byte_count == len_old:
102 matched_byte_count = 0
103 end_position = f.tell()
104 start_position = end_position - len_old
106 # search backwards for leading slashes written by
107 # a previous invocation of this tool
108 num_to_write = len_old
109 f.seek(start_position - 1)
111 if f.read(1) != b'/':
116 # pad with as many leading slashes as necessary
117 while num_to_write > len_new:
123 f.seek(start_position)
128 f.seek(start_position + len_new)
129 elif matched_byte_count > 0:
130 # back up an try to start a new match after
131 # the first byte of the previous partial match
132 f.seek(f.tell() - matched_byte_count)
133 matched_byte_count = 0
137 if sys.hexversion >= 0x3030000:
138 orig_mtime = orig_stat.st_mtime_ns
139 os.utime(filename, ns=(orig_mtime, orig_mtime))
141 orig_mtime = orig_stat[stat.ST_MTIME]
142 os.utime(filename, (orig_mtime, orig_mtime))
145 def chpath_inplace_symlink(filename, st, old, new):
146 target = os.readlink(filename)
147 if target.startswith(old):
148 new_target = new + target[len(old):]
150 os.symlink(new_target, filename)
151 os.lchown(filename, st.st_uid, st.st_gid)
155 parser = ArgumentParser(description=__doc__)
157 parser.add_argument('location', default=None,
158 help='root directory (e.g. $D)')
159 parser.add_argument('old', default=None,
160 help='original build prefix (e.g. /)')
161 parser.add_argument('new', default=None,
162 help='new install prefix (e.g. $EPREFIX)')
163 opts = parser.parse_args(argv)
165 location, old, new = opts.location, opts.old, opts.new
167 # Argument parsing compatibility for Python 2.6 using optparse.
168 if sys.hexversion < 0x2070000:
169 parser = OptionParser(description=__doc__,
170 usage="usage: %prog [-h] location old new\n\n" + \
171 " location: root directory (e.g. $D)\n" + \
172 " old: original build prefix (e.g. /)\n" + \
173 " new: new install prefix (e.g. $EPREFIX)")
175 (opts, args) = parser.parse_args()
179 print("%s: error: expected 3 arguments, got %i"
180 % (__file__, len(args)))
183 location, old, new = args[0:3]
187 is_text_file = IsTextFile()
189 if not isinstance(location, bytes):
190 location = location.encode(FS_ENCODING)
191 if not isinstance(old, bytes):
192 old = old.encode(FS_ENCODING)
193 if not isinstance(new, bytes):
194 new = new.encode(FS_ENCODING)
196 st = os.lstat(location)
198 if stat.S_ISDIR(st.st_mode):
199 for parent, dirs, files in os.walk(location):
200 for filename in files:
201 filename = os.path.join(parent, filename)
203 st = os.lstat(filename)
207 if stat.S_ISREG(st.st_mode):
208 chpath_inplace(filename,
209 is_text_file(filename), old, new)
210 elif stat.S_ISLNK(st.st_mode):
211 chpath_inplace_symlink(filename, st, old, new)
213 elif stat.S_ISREG(st.st_mode):
214 chpath_inplace(location,
215 is_text_file(location), old, new)
216 elif stat.S_ISLNK(st.st_mode):
217 chpath_inplace_symlink(location, st, old, new)
221 if __name__ == '__main__':
222 sys.exit(main(sys.argv[1:]))