118
110
raise BzrError("lstat/stat of (%r): %r" % (f, e))
120
def fancy_rename(old, new, rename_func, unlink_func):
121
"""A fancy rename, when you don't have atomic rename.
123
:param old: The old path, to rename from
124
:param new: The new path, to rename to
125
:param rename_func: The potentially non-atomic rename function
126
:param unlink_func: A way to delete the target file if the full rename succeeds
129
# sftp rename doesn't allow overwriting, so play tricks:
131
base = os.path.basename(new)
132
dirname = os.path.dirname(new)
133
tmp_name = u'tmp.%s.%.9f.%d.%s' % (base, time.time(), os.getpid(), rand_chars(10))
134
tmp_name = pathjoin(dirname, tmp_name)
136
# Rename the file out of the way, but keep track if it didn't exist
137
# We don't want to grab just any exception
138
# something like EACCES should prevent us from continuing
139
# The downside is that the rename_func has to throw an exception
140
# with an errno = ENOENT, or NoSuchFile
143
rename_func(new, tmp_name)
144
except (NoSuchFile,), e:
147
# RBC 20060103 abstraction leakage: the paramiko SFTP clients rename
148
# function raises an IOError with errno == None when a rename fails.
149
# This then gets caught here.
150
if e.errno not in (None, errno.ENOENT, errno.ENOTDIR):
153
if (not hasattr(e, 'errno')
154
or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
161
# This may throw an exception, in which case success will
163
rename_func(old, new)
167
# If the file used to exist, rename it back into place
168
# otherwise just delete it from the tmp location
170
unlink_func(tmp_name)
172
rename_func(tmp_name, new)
174
# Default is to just use the python builtins
175
abspath = os.path.abspath
176
realpath = os.path.realpath
177
pathjoin = os.path.join
178
normpath = os.path.normpath
180
mkdtemp = tempfile.mkdtemp
182
dirname = os.path.dirname
183
basename = os.path.basename
185
MIN_ABS_PATHLENGTH = 1
187
if os.name == "posix":
188
# In Python 2.4.2 and older, os.path.abspath and os.path.realpath
189
# choke on a Unicode string containing a relative path if
190
# os.getcwd() returns a non-sys.getdefaultencoding()-encoded
192
_fs_enc = sys.getfilesystemencoding()
194
return os.path.abspath(path.encode(_fs_enc)).decode(_fs_enc)
197
return os.path.realpath(path.encode(_fs_enc)).decode(_fs_enc)
199
if sys.platform == 'win32':
200
# We need to use the Unicode-aware os.path.abspath and
201
# os.path.realpath on Windows systems.
203
return os.path.abspath(path).replace('\\', '/')
206
return os.path.realpath(path).replace('\\', '/')
209
return os.path.join(*args).replace('\\', '/')
212
return os.path.normpath(path).replace('\\', '/')
215
return os.getcwdu().replace('\\', '/')
217
def mkdtemp(*args, **kwargs):
218
return tempfile.mkdtemp(*args, **kwargs).replace('\\', '/')
220
def rename(old, new):
221
fancy_rename(old, new, rename_func=os.rename, unlink_func=os.unlink)
223
MIN_ABS_PATHLENGTH = 3
225
112
def normalizepath(f):
226
113
if hasattr(os.path, 'realpath'):
230
117
[p,e] = os.path.split(f)
231
118
if e == "" or e == "." or e == "..":
234
return pathjoin(F(p), e)
121
return os.path.join(F(p), e)
237
124
def backup_file(fn):
238
125
"""Copy a file to a backup.
287
180
def is_inside(dir, fname):
288
181
"""True if fname is inside dir.
290
The parameters should typically be passed to osutils.normpath first, so
183
The parameters should typically be passed to os.path.normpath first, so
291
184
that . and .. and repeated slashes are eliminated, and the separators
292
185
are canonical for the platform.
294
187
The empty string as a dir name is taken as top-of-tree and matches
297
>>> is_inside('src', pathjoin('src', 'foo.c'))
190
>>> is_inside('src', os.path.join('src', 'foo.c'))
299
192
>>> is_inside('src', 'srccontrol')
301
>>> is_inside('src', pathjoin('src', 'a', 'a', 'a', 'foo.c'))
194
>>> is_inside('src', os.path.join('src', 'a', 'a', 'a', 'foo.c'))
303
196
>>> is_inside('foo.c', 'foo.c')
621
467
# XXX This should raise a NotChildPath exception, as its not tied
622
468
# to branch anymore.
623
raise PathNotChild(rp, base)
631
def safe_unicode(unicode_or_utf8_string):
632
"""Coerce unicode_or_utf8_string into unicode.
634
If it is unicode, it is returned.
635
Otherwise it is decoded from utf-8. If a decoding error
636
occurs, it is wrapped as a If the decoding fails, the exception is wrapped
637
as a BzrBadParameter exception.
639
if isinstance(unicode_or_utf8_string, unicode):
640
return unicode_or_utf8_string
642
return unicode_or_utf8_string.decode('utf8')
643
except UnicodeDecodeError:
644
raise BzrBadParameterNotUnicode(unicode_or_utf8_string)
647
def terminal_width():
648
"""Return estimated terminal width."""
650
# TODO: Do something smart on Windows?
652
# TODO: Is there anything that gets a better update when the window
653
# is resized while the program is running? We could use the Python termcap
656
return int(os.environ['COLUMNS'])
657
except (IndexError, KeyError, ValueError):
660
def supports_executable():
661
return sys.platform != "win32"
664
def strip_trailing_slash(path):
665
"""Strip trailing slash, except for root paths.
666
The definition of 'root path' is platform-dependent.
668
if len(path) != MIN_ABS_PATHLENGTH and path[-1] == '/':
674
_validWin32PathRE = re.compile(r'^([A-Za-z]:[/\\])?[^:<>*"?\|]*$')
677
def check_legal_path(path):
678
"""Check whether the supplied path is legal.
679
This is only required on Windows, so we don't test on other platforms
682
if sys.platform != "win32":
684
if _validWin32PathRE.match(path) is None:
685
raise IllegalPath(path)
469
raise NotBranchError("path %r is not within branch %r" % (rp, base))
471
return os.sep.join(s)