##// END OF EJS Templates
util: split out posix, windows, and win32 modules
Matt Mackall -
r7890:e710f0f5 default
parent child Browse files
Show More
@@ -0,0 +1,222 b''
1 """
2 posix.py - Posix utility function implementations for Mercurial
3
4 Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
5
6 This software may be used and distributed according to the terms of
7 the GNU General Public License version 2, incorporated herein by
8 reference.
9 """
10
11 from i18n import _
12 import os, sys, osutil, errno, stat, getpass
13
14 posixfile = file
15 nulldev = '/dev/null'
16 normpath = os.path.normpath
17 samestat = os.path.samestat
18
19 umask = os.umask(0)
20 os.umask(umask)
21
22 def openhardlinks():
23 '''return true if it is safe to hold open file handles to hardlinks'''
24 return True
25
26 def rcfiles(path):
27 rcs = [os.path.join(path, 'hgrc')]
28 rcdir = os.path.join(path, 'hgrc.d')
29 try:
30 rcs.extend([os.path.join(rcdir, f)
31 for f, kind in osutil.listdir(rcdir)
32 if f.endswith(".rc")])
33 except OSError:
34 pass
35 return rcs
36
37 def system_rcpath():
38 path = []
39 # old mod_python does not set sys.argv
40 if len(getattr(sys, 'argv', [])) > 0:
41 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
42 '/../etc/mercurial'))
43 path.extend(rcfiles('/etc/mercurial'))
44 return path
45
46 def user_rcpath():
47 return [os.path.expanduser('~/.hgrc')]
48
49 def parse_patch_output(output_line):
50 """parses the output produced by patch and returns the file name"""
51 pf = output_line[14:]
52 if os.sys.platform == 'OpenVMS':
53 if pf[0] == '`':
54 pf = pf[1:-1] # Remove the quotes
55 else:
56 if pf.startswith("'") and pf.endswith("'") and " " in pf:
57 pf = pf[1:-1] # Remove the quotes
58 return pf
59
60 def sshargs(sshcmd, host, user, port):
61 '''Build argument list for ssh'''
62 args = user and ("%s@%s" % (user, host)) or host
63 return port and ("%s -p %s" % (args, port)) or args
64
65 def is_exec(f):
66 """check whether a file is executable"""
67 return (os.lstat(f).st_mode & 0100 != 0)
68
69 def set_flags(f, l, x):
70 s = os.lstat(f).st_mode
71 if l:
72 if not stat.S_ISLNK(s):
73 # switch file to link
74 data = file(f).read()
75 os.unlink(f)
76 try:
77 os.symlink(data, f)
78 except:
79 # failed to make a link, rewrite file
80 file(f, "w").write(data)
81 # no chmod needed at this point
82 return
83 if stat.S_ISLNK(s):
84 # switch link to file
85 data = os.readlink(f)
86 os.unlink(f)
87 file(f, "w").write(data)
88 s = 0666 & ~umask # avoid restatting for chmod
89
90 sx = s & 0100
91 if x and not sx:
92 # Turn on +x for every +r bit when making a file executable
93 # and obey umask.
94 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
95 elif not x and sx:
96 # Turn off all +x bits
97 os.chmod(f, s & 0666)
98
99 def set_binary(fd):
100 pass
101
102 def pconvert(path):
103 return path
104
105 def localpath(path):
106 return path
107
108 def shellquote(s):
109 if os.sys.platform == 'OpenVMS':
110 return '"%s"' % s
111 else:
112 return "'%s'" % s.replace("'", "'\\''")
113
114 def quotecommand(cmd):
115 return cmd
116
117 def popen(command, mode='r'):
118 return os.popen(command, mode)
119
120 def testpid(pid):
121 '''return False if pid dead, True if running or not sure'''
122 if os.sys.platform == 'OpenVMS':
123 return True
124 try:
125 os.kill(pid, 0)
126 return True
127 except OSError, inst:
128 return inst.errno != errno.ESRCH
129
130 def explain_exit(code):
131 """return a 2-tuple (desc, code) describing a process's status"""
132 if os.WIFEXITED(code):
133 val = os.WEXITSTATUS(code)
134 return _("exited with status %d") % val, val
135 elif os.WIFSIGNALED(code):
136 val = os.WTERMSIG(code)
137 return _("killed by signal %d") % val, val
138 elif os.WIFSTOPPED(code):
139 val = os.WSTOPSIG(code)
140 return _("stopped by signal %d") % val, val
141 raise ValueError(_("invalid exit code"))
142
143 def isowner(fp, st=None):
144 """Return True if the file object f belongs to the current user.
145
146 The return value of a util.fstat(f) may be passed as the st argument.
147 """
148 if st is None:
149 st = fstat(fp)
150 return st.st_uid == os.getuid()
151
152 def find_exe(command):
153 '''Find executable for command searching like which does.
154 If command is a basename then PATH is searched for command.
155 PATH isn't searched if command is an absolute or relative path.
156 If command isn't found None is returned.'''
157 if sys.platform == 'OpenVMS':
158 return command
159
160 def findexisting(executable):
161 'Will return executable if existing file'
162 if os.path.exists(executable):
163 return executable
164 return None
165
166 if os.sep in command:
167 return findexisting(command)
168
169 for path in os.environ.get('PATH', '').split(os.pathsep):
170 executable = findexisting(os.path.join(path, command))
171 if executable is not None:
172 return executable
173 return None
174
175 def set_signal_handler():
176 pass
177
178 def statfiles(files):
179 'Stat each file in files and yield stat or None if file does not exist.'
180 lstat = os.lstat
181 for nf in files:
182 try:
183 st = lstat(nf)
184 except OSError, err:
185 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
186 raise
187 st = None
188 yield st
189
190 def getuser():
191 '''return name of current user'''
192 return getpass.getuser()
193
194 def expand_glob(pats):
195 '''On Windows, expand the implicit globs in a list of patterns'''
196 return list(pats)
197
198 def username(uid=None):
199 """Return the name of the user with the given uid.
200
201 If uid is None, return the name of the current user."""
202
203 if uid is None:
204 uid = os.getuid()
205 try:
206 return pwd.getpwuid(uid)[0]
207 except KeyError:
208 return str(uid)
209
210 def groupname(gid=None):
211 """Return the name of the group with the given gid.
212
213 If gid is None, return the name of the current group."""
214
215 if gid is None:
216 gid = os.getgid()
217 try:
218 return grp.getgrgid(gid)[0]
219 except KeyError:
220 return str(gid)
221
222
@@ -0,0 +1,266 b''
1 """
2 windows.py - Windows utility function implementations for Mercurial
3
4 Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
5
6 This software may be used and distributed according to the terms of
7 the GNU General Public License version 2, incorporated herein by
8 reference.
9 """
10
11 import msvcrt, sys, os
12 nulldev = 'NUL:'
13
14 umask = 002
15
16 class winstdout:
17 '''stdout on windows misbehaves if sent through a pipe'''
18
19 def __init__(self, fp):
20 self.fp = fp
21
22 def __getattr__(self, key):
23 return getattr(self.fp, key)
24
25 def close(self):
26 try:
27 self.fp.close()
28 except: pass
29
30 def write(self, s):
31 try:
32 # This is workaround for "Not enough space" error on
33 # writing large size of data to console.
34 limit = 16000
35 l = len(s)
36 start = 0
37 while start < l:
38 end = start + limit
39 self.fp.write(s[start:end])
40 start = end
41 except IOError, inst:
42 if inst.errno != 0: raise
43 self.close()
44 raise IOError(errno.EPIPE, 'Broken pipe')
45
46 def flush(self):
47 try:
48 return self.fp.flush()
49 except IOError, inst:
50 if inst.errno != errno.EINVAL: raise
51 self.close()
52 raise IOError(errno.EPIPE, 'Broken pipe')
53
54 sys.stdout = winstdout(sys.stdout)
55
56 def _is_win_9x():
57 '''return true if run on windows 95, 98 or me.'''
58 try:
59 return sys.getwindowsversion()[3] == 1
60 except AttributeError:
61 return 'command' in os.environ.get('comspec', '')
62
63 def openhardlinks():
64 return not _is_win_9x and "win32api" in locals()
65
66 def system_rcpath():
67 try:
68 return system_rcpath_win32()
69 except:
70 return [r'c:\mercurial\mercurial.ini']
71
72 def user_rcpath():
73 '''return os-specific hgrc search path to the user dir'''
74 try:
75 path = user_rcpath_win32()
76 except:
77 home = os.path.expanduser('~')
78 path = [os.path.join(home, 'mercurial.ini'),
79 os.path.join(home, '.hgrc')]
80 userprofile = os.environ.get('USERPROFILE')
81 if userprofile:
82 path.append(os.path.join(userprofile, 'mercurial.ini'))
83 path.append(os.path.join(userprofile, '.hgrc'))
84 return path
85
86 def parse_patch_output(output_line):
87 """parses the output produced by patch and returns the file name"""
88 pf = output_line[14:]
89 if pf[0] == '`':
90 pf = pf[1:-1] # Remove the quotes
91 return pf
92
93 def sshargs(sshcmd, host, user, port):
94 '''Build argument list for ssh or Plink'''
95 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
96 args = user and ("%s@%s" % (user, host)) or host
97 return port and ("%s %s %s" % (args, pflag, port)) or args
98
99 def testpid(pid):
100 '''return False if pid dead, True if running or not known'''
101 return True
102
103 def set_flags(f, l, x):
104 pass
105
106 def set_binary(fd):
107 # When run without console, pipes may expose invalid
108 # fileno(), usually set to -1.
109 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
110 msvcrt.setmode(fd.fileno(), os.O_BINARY)
111
112 def pconvert(path):
113 return '/'.join(path.split(os.sep))
114
115 def localpath(path):
116 return path.replace('/', '\\')
117
118 def normpath(path):
119 return pconvert(os.path.normpath(path))
120
121 def samestat(s1, s2):
122 return False
123
124 # A sequence of backslashes is special iff it precedes a double quote:
125 # - if there's an even number of backslashes, the double quote is not
126 # quoted (i.e. it ends the quoted region)
127 # - if there's an odd number of backslashes, the double quote is quoted
128 # - in both cases, every pair of backslashes is unquoted into a single
129 # backslash
130 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
131 # So, to quote a string, we must surround it in double quotes, double
132 # the number of backslashes that preceed double quotes and add another
133 # backslash before every double quote (being careful with the double
134 # quote we've appended to the end)
135 _quotere = None
136 def shellquote(s):
137 global _quotere
138 if _quotere is None:
139 _quotere = re.compile(r'(\\*)("|\\$)')
140 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
141
142 def quotecommand(cmd):
143 """Build a command string suitable for os.popen* calls."""
144 # The extra quotes are needed because popen* runs the command
145 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
146 return '"' + cmd + '"'
147
148 def popen(command, mode='r'):
149 # Work around "popen spawned process may not write to stdout
150 # under windows"
151 # http://bugs.python.org/issue1366
152 command += " 2> %s" % nulldev
153 return os.popen(quotecommand(command), mode)
154
155 def explain_exit(code):
156 return _("exited with status %d") % code, code
157
158 # if you change this stub into a real check, please try to implement the
159 # username and groupname functions above, too.
160 def isowner(fp, st=None):
161 return True
162
163 def find_exe(command):
164 '''Find executable for command searching like cmd.exe does.
165 If command is a basename then PATH is searched for command.
166 PATH isn't searched if command is an absolute or relative path.
167 An extension from PATHEXT is found and added if not present.
168 If command isn't found None is returned.'''
169 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
170 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
171 if os.path.splitext(command)[1].lower() in pathexts:
172 pathexts = ['']
173
174 def findexisting(pathcommand):
175 'Will append extension (if needed) and return existing file'
176 for ext in pathexts:
177 executable = pathcommand + ext
178 if os.path.exists(executable):
179 return executable
180 return None
181
182 if os.sep in command:
183 return findexisting(command)
184
185 for path in os.environ.get('PATH', '').split(os.pathsep):
186 executable = findexisting(os.path.join(path, command))
187 if executable is not None:
188 return executable
189 return None
190
191 def set_signal_handler():
192 try:
193 set_signal_handler_win32()
194 except NameError:
195 pass
196
197 def statfiles(files):
198 '''Stat each file in files and yield stat or None if file does not exist.
199 Cluster and cache stat per directory to minimize number of OS stat calls.'''
200 ncase = os.path.normcase
201 sep = os.sep
202 dircache = {} # dirname -> filename -> status | None if file does not exist
203 for nf in files:
204 nf = ncase(nf)
205 pos = nf.rfind(sep)
206 if pos == -1:
207 dir, base = '.', nf
208 else:
209 dir, base = nf[:pos+1], nf[pos+1:]
210 cache = dircache.get(dir, None)
211 if cache is None:
212 try:
213 dmap = dict([(ncase(n), s)
214 for n, k, s in osutil.listdir(dir, True)])
215 except OSError, err:
216 # handle directory not found in Python version prior to 2.5
217 # Python <= 2.4 returns native Windows code 3 in errno
218 # Python >= 2.5 returns ENOENT and adds winerror field
219 # EINVAL is raised if dir is not a directory.
220 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
221 errno.ENOTDIR):
222 raise
223 dmap = {}
224 cache = dircache.setdefault(dir, dmap)
225 yield cache.get(base, None)
226
227 def getuser():
228 '''return name of current user'''
229 raise Abort(_('user name not available - set USERNAME '
230 'environment variable'))
231
232 def expand_glob(pats):
233 '''On Windows, expand the implicit globs in a list of patterns'''
234 ret = []
235 for p in pats:
236 kind, name = patkind(p, None)
237 if kind is None:
238 globbed = glob.glob(name)
239 if globbed:
240 ret.extend(globbed)
241 continue
242 # if we couldn't expand the glob, just keep it around
243 ret.append(p)
244 return ret
245
246 def username(uid=None):
247 """Return the name of the user with the given uid.
248
249 If uid is None, return the name of the current user."""
250 return None
251
252 def groupname(gid=None):
253 """Return the name of the group with the given gid.
254
255 If gid is None, return the name of the current group."""
256 return None
257
258 posixfile = file
259 try:
260 # override functions with win32 versions if possible
261 from util_win32 import *
262 if not _is_win_9x():
263 posixfile = posixfile_nt
264 except ImportError:
265 pass
266
@@ -1,297 +1,297 b''
1 # store.py - repository store handling for Mercurial
1 # store.py - repository store handling for Mercurial
2 #
2 #
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import os, stat, osutil, util
9 import os, stat, osutil, util
10
10
11 _sha = util.sha1
11 _sha = util.sha1
12
12
13 def _buildencodefun():
13 def _buildencodefun():
14 e = '_'
14 e = '_'
15 win_reserved = [ord(x) for x in '\\:*?"<>|']
15 win_reserved = [ord(x) for x in '\\:*?"<>|']
16 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
16 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
17 for x in (range(32) + range(126, 256) + win_reserved):
17 for x in (range(32) + range(126, 256) + win_reserved):
18 cmap[chr(x)] = "~%02x" % x
18 cmap[chr(x)] = "~%02x" % x
19 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
19 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
20 cmap[chr(x)] = e + chr(x).lower()
20 cmap[chr(x)] = e + chr(x).lower()
21 dmap = {}
21 dmap = {}
22 for k, v in cmap.iteritems():
22 for k, v in cmap.iteritems():
23 dmap[v] = k
23 dmap[v] = k
24 def decode(s):
24 def decode(s):
25 i = 0
25 i = 0
26 while i < len(s):
26 while i < len(s):
27 for l in xrange(1, 4):
27 for l in xrange(1, 4):
28 try:
28 try:
29 yield dmap[s[i:i+l]]
29 yield dmap[s[i:i+l]]
30 i += l
30 i += l
31 break
31 break
32 except KeyError:
32 except KeyError:
33 pass
33 pass
34 else:
34 else:
35 raise KeyError
35 raise KeyError
36 return (lambda s: "".join([cmap[c] for c in s]),
36 return (lambda s: "".join([cmap[c] for c in s]),
37 lambda s: "".join(list(decode(s))))
37 lambda s: "".join(list(decode(s))))
38
38
39 encodefilename, decodefilename = _buildencodefun()
39 encodefilename, decodefilename = _buildencodefun()
40
40
41 def _build_lower_encodefun():
41 def _build_lower_encodefun():
42 win_reserved = [ord(x) for x in '\\:*?"<>|']
42 win_reserved = [ord(x) for x in '\\:*?"<>|']
43 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
43 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
44 for x in (range(32) + range(126, 256) + win_reserved):
44 for x in (range(32) + range(126, 256) + win_reserved):
45 cmap[chr(x)] = "~%02x" % x
45 cmap[chr(x)] = "~%02x" % x
46 for x in range(ord("A"), ord("Z")+1):
46 for x in range(ord("A"), ord("Z")+1):
47 cmap[chr(x)] = chr(x).lower()
47 cmap[chr(x)] = chr(x).lower()
48 return lambda s: "".join([cmap[c] for c in s])
48 return lambda s: "".join([cmap[c] for c in s])
49
49
50 lowerencode = _build_lower_encodefun()
50 lowerencode = _build_lower_encodefun()
51
51
52 _windows_reserved_filenames = '''con prn aux nul
52 _windows_reserved_filenames = '''con prn aux nul
53 com1 com2 com3 com4 com5 com6 com7 com8 com9
53 com1 com2 com3 com4 com5 com6 com7 com8 com9
54 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
54 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
55 def auxencode(path):
55 def auxencode(path):
56 res = []
56 res = []
57 for n in path.split('/'):
57 for n in path.split('/'):
58 if n:
58 if n:
59 base = n.split('.')[0]
59 base = n.split('.')[0]
60 if base and (base in _windows_reserved_filenames):
60 if base and (base in _windows_reserved_filenames):
61 # encode third letter ('aux' -> 'au~78')
61 # encode third letter ('aux' -> 'au~78')
62 ec = "~%02x" % ord(n[2])
62 ec = "~%02x" % ord(n[2])
63 n = n[0:2] + ec + n[3:]
63 n = n[0:2] + ec + n[3:]
64 if n[-1] in '. ':
64 if n[-1] in '. ':
65 # encode last period or space ('foo...' -> 'foo..~2e')
65 # encode last period or space ('foo...' -> 'foo..~2e')
66 n = n[:-1] + "~%02x" % ord(n[-1])
66 n = n[:-1] + "~%02x" % ord(n[-1])
67 res.append(n)
67 res.append(n)
68 return '/'.join(res)
68 return '/'.join(res)
69
69
70 MAX_PATH_LEN_IN_HGSTORE = 120
70 MAX_PATH_LEN_IN_HGSTORE = 120
71 DIR_PREFIX_LEN = 8
71 DIR_PREFIX_LEN = 8
72 _MAX_SHORTENED_DIRS_LEN = 8 * (DIR_PREFIX_LEN + 1) - 4
72 _MAX_SHORTENED_DIRS_LEN = 8 * (DIR_PREFIX_LEN + 1) - 4
73 def hybridencode(path):
73 def hybridencode(path):
74 '''encodes path with a length limit
74 '''encodes path with a length limit
75
75
76 Encodes all paths that begin with 'data/', according to the following.
76 Encodes all paths that begin with 'data/', according to the following.
77
77
78 Default encoding (reversible):
78 Default encoding (reversible):
79
79
80 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
80 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
81 characters are encoded as '~xx', where xx is the two digit hex code
81 characters are encoded as '~xx', where xx is the two digit hex code
82 of the character (see encodefilename).
82 of the character (see encodefilename).
83 Relevant path components consisting of Windows reserved filenames are
83 Relevant path components consisting of Windows reserved filenames are
84 masked by encoding the third character ('aux' -> 'au~78', see auxencode).
84 masked by encoding the third character ('aux' -> 'au~78', see auxencode).
85
85
86 Hashed encoding (not reversible):
86 Hashed encoding (not reversible):
87
87
88 If the default-encoded path is longer than MAX_PATH_LEN_IN_HGSTORE, a
88 If the default-encoded path is longer than MAX_PATH_LEN_IN_HGSTORE, a
89 non-reversible hybrid hashing of the path is done instead.
89 non-reversible hybrid hashing of the path is done instead.
90 This encoding uses up to DIR_PREFIX_LEN characters of all directory
90 This encoding uses up to DIR_PREFIX_LEN characters of all directory
91 levels of the lowerencoded path, but not more levels than can fit into
91 levels of the lowerencoded path, but not more levels than can fit into
92 _MAX_SHORTENED_DIRS_LEN.
92 _MAX_SHORTENED_DIRS_LEN.
93 Then follows the filler followed by the sha digest of the full path.
93 Then follows the filler followed by the sha digest of the full path.
94 The filler is the beginning of the basename of the lowerencoded path
94 The filler is the beginning of the basename of the lowerencoded path
95 (the basename is everything after the last path separator). The filler
95 (the basename is everything after the last path separator). The filler
96 is as long as possible, filling in characters from the basename until
96 is as long as possible, filling in characters from the basename until
97 the encoded path has MAX_PATH_LEN_IN_HGSTORE characters (or all chars
97 the encoded path has MAX_PATH_LEN_IN_HGSTORE characters (or all chars
98 of the basename have been taken).
98 of the basename have been taken).
99 The extension (e.g. '.i' or '.d') is preserved.
99 The extension (e.g. '.i' or '.d') is preserved.
100
100
101 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
101 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
102 encoding was used.
102 encoding was used.
103 '''
103 '''
104 if not path.startswith('data/'):
104 if not path.startswith('data/'):
105 return path
105 return path
106 ndpath = path[len('data/'):]
106 ndpath = path[len('data/'):]
107 res = 'data/' + auxencode(encodefilename(ndpath))
107 res = 'data/' + auxencode(encodefilename(ndpath))
108 if len(res) > MAX_PATH_LEN_IN_HGSTORE:
108 if len(res) > MAX_PATH_LEN_IN_HGSTORE:
109 digest = _sha(path).hexdigest()
109 digest = _sha(path).hexdigest()
110 aep = auxencode(lowerencode(ndpath))
110 aep = auxencode(lowerencode(ndpath))
111 _root, ext = os.path.splitext(aep)
111 _root, ext = os.path.splitext(aep)
112 parts = aep.split('/')
112 parts = aep.split('/')
113 basename = parts[-1]
113 basename = parts[-1]
114 sdirs = []
114 sdirs = []
115 for p in parts[:-1]:
115 for p in parts[:-1]:
116 d = p[:DIR_PREFIX_LEN]
116 d = p[:DIR_PREFIX_LEN]
117 if d[-1] in '. ':
117 if d[-1] in '. ':
118 # Windows can't access dirs ending in period or space
118 # Windows can't access dirs ending in period or space
119 d = d[:-1] + '_'
119 d = d[:-1] + '_'
120 t = '/'.join(sdirs) + '/' + d
120 t = '/'.join(sdirs) + '/' + d
121 if len(t) > _MAX_SHORTENED_DIRS_LEN:
121 if len(t) > _MAX_SHORTENED_DIRS_LEN:
122 break
122 break
123 sdirs.append(d)
123 sdirs.append(d)
124 dirs = '/'.join(sdirs)
124 dirs = '/'.join(sdirs)
125 if len(dirs) > 0:
125 if len(dirs) > 0:
126 dirs += '/'
126 dirs += '/'
127 res = 'dh/' + dirs + digest + ext
127 res = 'dh/' + dirs + digest + ext
128 space_left = MAX_PATH_LEN_IN_HGSTORE - len(res)
128 space_left = MAX_PATH_LEN_IN_HGSTORE - len(res)
129 if space_left > 0:
129 if space_left > 0:
130 filler = basename[:space_left]
130 filler = basename[:space_left]
131 res = 'dh/' + dirs + filler + digest + ext
131 res = 'dh/' + dirs + filler + digest + ext
132 return res
132 return res
133
133
134 def _calcmode(path):
134 def _calcmode(path):
135 try:
135 try:
136 # files in .hg/ will be created using this mode
136 # files in .hg/ will be created using this mode
137 mode = os.stat(path).st_mode
137 mode = os.stat(path).st_mode
138 # avoid some useless chmods
138 # avoid some useless chmods
139 if (0777 & ~util._umask) == (0777 & mode):
139 if (0777 & ~util.umask) == (0777 & mode):
140 mode = None
140 mode = None
141 except OSError:
141 except OSError:
142 mode = None
142 mode = None
143 return mode
143 return mode
144
144
145 _data = 'data 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
145 _data = 'data 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
146
146
147 class basicstore:
147 class basicstore:
148 '''base class for local repository stores'''
148 '''base class for local repository stores'''
149 def __init__(self, path, opener, pathjoiner):
149 def __init__(self, path, opener, pathjoiner):
150 self.pathjoiner = pathjoiner
150 self.pathjoiner = pathjoiner
151 self.path = path
151 self.path = path
152 self.createmode = _calcmode(path)
152 self.createmode = _calcmode(path)
153 self.opener = opener(self.path)
153 self.opener = opener(self.path)
154 self.opener.createmode = self.createmode
154 self.opener.createmode = self.createmode
155
155
156 def join(self, f):
156 def join(self, f):
157 return self.pathjoiner(self.path, f)
157 return self.pathjoiner(self.path, f)
158
158
159 def _walk(self, relpath, recurse):
159 def _walk(self, relpath, recurse):
160 '''yields (unencoded, encoded, size)'''
160 '''yields (unencoded, encoded, size)'''
161 path = self.pathjoiner(self.path, relpath)
161 path = self.pathjoiner(self.path, relpath)
162 striplen = len(self.path) + len(os.sep)
162 striplen = len(self.path) + len(os.sep)
163 l = []
163 l = []
164 if os.path.isdir(path):
164 if os.path.isdir(path):
165 visit = [path]
165 visit = [path]
166 while visit:
166 while visit:
167 p = visit.pop()
167 p = visit.pop()
168 for f, kind, st in osutil.listdir(p, stat=True):
168 for f, kind, st in osutil.listdir(p, stat=True):
169 fp = self.pathjoiner(p, f)
169 fp = self.pathjoiner(p, f)
170 if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'):
170 if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'):
171 n = util.pconvert(fp[striplen:])
171 n = util.pconvert(fp[striplen:])
172 l.append((n, n, st.st_size))
172 l.append((n, n, st.st_size))
173 elif kind == stat.S_IFDIR and recurse:
173 elif kind == stat.S_IFDIR and recurse:
174 visit.append(fp)
174 visit.append(fp)
175 return util.sort(l)
175 return util.sort(l)
176
176
177 def datafiles(self):
177 def datafiles(self):
178 return self._walk('data', True)
178 return self._walk('data', True)
179
179
180 def walk(self):
180 def walk(self):
181 '''yields (unencoded, encoded, size)'''
181 '''yields (unencoded, encoded, size)'''
182 # yield data files first
182 # yield data files first
183 for x in self.datafiles():
183 for x in self.datafiles():
184 yield x
184 yield x
185 # yield manifest before changelog
185 # yield manifest before changelog
186 meta = self._walk('', False)
186 meta = self._walk('', False)
187 meta.reverse()
187 meta.reverse()
188 for x in meta:
188 for x in meta:
189 yield x
189 yield x
190
190
191 def copylist(self):
191 def copylist(self):
192 return ['requires'] + _data.split()
192 return ['requires'] + _data.split()
193
193
194 class encodedstore(basicstore):
194 class encodedstore(basicstore):
195 def __init__(self, path, opener, pathjoiner):
195 def __init__(self, path, opener, pathjoiner):
196 self.pathjoiner = pathjoiner
196 self.pathjoiner = pathjoiner
197 self.path = self.pathjoiner(path, 'store')
197 self.path = self.pathjoiner(path, 'store')
198 self.createmode = _calcmode(self.path)
198 self.createmode = _calcmode(self.path)
199 op = opener(self.path)
199 op = opener(self.path)
200 op.createmode = self.createmode
200 op.createmode = self.createmode
201 self.opener = lambda f, *args, **kw: op(encodefilename(f), *args, **kw)
201 self.opener = lambda f, *args, **kw: op(encodefilename(f), *args, **kw)
202
202
203 def datafiles(self):
203 def datafiles(self):
204 for a, b, size in self._walk('data', True):
204 for a, b, size in self._walk('data', True):
205 try:
205 try:
206 a = decodefilename(a)
206 a = decodefilename(a)
207 except KeyError:
207 except KeyError:
208 a = None
208 a = None
209 yield a, b, size
209 yield a, b, size
210
210
211 def join(self, f):
211 def join(self, f):
212 return self.pathjoiner(self.path, encodefilename(f))
212 return self.pathjoiner(self.path, encodefilename(f))
213
213
214 def copylist(self):
214 def copylist(self):
215 return (['requires', '00changelog.i'] +
215 return (['requires', '00changelog.i'] +
216 [self.pathjoiner('store', f) for f in _data.split()])
216 [self.pathjoiner('store', f) for f in _data.split()])
217
217
218 def fncache(opener):
218 def fncache(opener):
219 '''yields the entries in the fncache file'''
219 '''yields the entries in the fncache file'''
220 try:
220 try:
221 fp = opener('fncache', mode='rb')
221 fp = opener('fncache', mode='rb')
222 except IOError:
222 except IOError:
223 # skip nonexistent file
223 # skip nonexistent file
224 return
224 return
225 for n, line in enumerate(fp):
225 for n, line in enumerate(fp):
226 if (len(line) < 2) or (line[-1] != '\n'):
226 if (len(line) < 2) or (line[-1] != '\n'):
227 t = _('invalid entry in fncache, line %s') % (n + 1)
227 t = _('invalid entry in fncache, line %s') % (n + 1)
228 raise util.Abort(t)
228 raise util.Abort(t)
229 yield line[:-1]
229 yield line[:-1]
230 fp.close()
230 fp.close()
231
231
232 class fncacheopener(object):
232 class fncacheopener(object):
233 def __init__(self, opener):
233 def __init__(self, opener):
234 self.opener = opener
234 self.opener = opener
235 self.entries = None
235 self.entries = None
236
236
237 def loadfncache(self):
237 def loadfncache(self):
238 self.entries = {}
238 self.entries = {}
239 for f in fncache(self.opener):
239 for f in fncache(self.opener):
240 self.entries[f] = True
240 self.entries[f] = True
241
241
242 def __call__(self, path, mode='r', *args, **kw):
242 def __call__(self, path, mode='r', *args, **kw):
243 if mode not in ('r', 'rb') and path.startswith('data/'):
243 if mode not in ('r', 'rb') and path.startswith('data/'):
244 if self.entries is None:
244 if self.entries is None:
245 self.loadfncache()
245 self.loadfncache()
246 if path not in self.entries:
246 if path not in self.entries:
247 self.opener('fncache', 'ab').write(path + '\n')
247 self.opener('fncache', 'ab').write(path + '\n')
248 # fncache may contain non-existent files after rollback / strip
248 # fncache may contain non-existent files after rollback / strip
249 self.entries[path] = True
249 self.entries[path] = True
250 return self.opener(hybridencode(path), mode, *args, **kw)
250 return self.opener(hybridencode(path), mode, *args, **kw)
251
251
252 class fncachestore(basicstore):
252 class fncachestore(basicstore):
253 def __init__(self, path, opener, pathjoiner):
253 def __init__(self, path, opener, pathjoiner):
254 self.pathjoiner = pathjoiner
254 self.pathjoiner = pathjoiner
255 self.path = self.pathjoiner(path, 'store')
255 self.path = self.pathjoiner(path, 'store')
256 self.createmode = _calcmode(self.path)
256 self.createmode = _calcmode(self.path)
257 self._op = opener(self.path)
257 self._op = opener(self.path)
258 self._op.createmode = self.createmode
258 self._op.createmode = self.createmode
259 self.opener = fncacheopener(self._op)
259 self.opener = fncacheopener(self._op)
260
260
261 def join(self, f):
261 def join(self, f):
262 return self.pathjoiner(self.path, hybridencode(f))
262 return self.pathjoiner(self.path, hybridencode(f))
263
263
264 def datafiles(self):
264 def datafiles(self):
265 rewrite = False
265 rewrite = False
266 existing = []
266 existing = []
267 pjoin = self.pathjoiner
267 pjoin = self.pathjoiner
268 spath = self.path
268 spath = self.path
269 for f in fncache(self._op):
269 for f in fncache(self._op):
270 ef = hybridencode(f)
270 ef = hybridencode(f)
271 try:
271 try:
272 st = os.stat(pjoin(spath, ef))
272 st = os.stat(pjoin(spath, ef))
273 yield f, ef, st.st_size
273 yield f, ef, st.st_size
274 existing.append(f)
274 existing.append(f)
275 except OSError:
275 except OSError:
276 # nonexistent entry
276 # nonexistent entry
277 rewrite = True
277 rewrite = True
278 if rewrite:
278 if rewrite:
279 # rewrite fncache to remove nonexistent entries
279 # rewrite fncache to remove nonexistent entries
280 # (may be caused by rollback / strip)
280 # (may be caused by rollback / strip)
281 fp = self._op('fncache', mode='wb')
281 fp = self._op('fncache', mode='wb')
282 for p in existing:
282 for p in existing:
283 fp.write(p + '\n')
283 fp.write(p + '\n')
284 fp.close()
284 fp.close()
285
285
286 def copylist(self):
286 def copylist(self):
287 d = _data + ' dh fncache'
287 d = _data + ' dh fncache'
288 return (['requires', '00changelog.i'] +
288 return (['requires', '00changelog.i'] +
289 [self.pathjoiner('store', f) for f in d.split()])
289 [self.pathjoiner('store', f) for f in d.split()])
290
290
291 def store(requirements, path, opener, pathjoiner=None):
291 def store(requirements, path, opener, pathjoiner=None):
292 pathjoiner = pathjoiner or os.path.join
292 pathjoiner = pathjoiner or os.path.join
293 if 'store' in requirements:
293 if 'store' in requirements:
294 if 'fncache' in requirements:
294 if 'fncache' in requirements:
295 return fncachestore(path, opener, pathjoiner)
295 return fncachestore(path, opener, pathjoiner)
296 return encodedstore(path, opener, pathjoiner)
296 return encodedstore(path, opener, pathjoiner)
297 return basicstore(path, opener, pathjoiner)
297 return basicstore(path, opener, pathjoiner)
This diff has been collapsed as it changes many lines, (517 lines changed) Show them Hide them
@@ -1,2024 +1,1555 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback, error
16 import cStringIO, errno, re, shutil, sys, tempfile, traceback, error
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp, unicodedata
18 import imp, unicodedata
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 import subprocess
53 import subprocess
54 subprocess.Popen # trigger ImportError early
54 subprocess.Popen # trigger ImportError early
55 closefds = os.name == 'posix'
55 closefds = os.name == 'posix'
56 def popen2(cmd, mode='t', bufsize=-1):
56 def popen2(cmd, mode='t', bufsize=-1):
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
58 close_fds=closefds,
58 close_fds=closefds,
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
60 return p.stdin, p.stdout
60 return p.stdin, p.stdout
61 def popen3(cmd, mode='t', bufsize=-1):
61 def popen3(cmd, mode='t', bufsize=-1):
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
63 close_fds=closefds,
63 close_fds=closefds,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 stderr=subprocess.PIPE)
65 stderr=subprocess.PIPE)
66 return p.stdin, p.stdout, p.stderr
66 return p.stdin, p.stdout, p.stderr
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
68 stderr = capturestderr and subprocess.PIPE or None
68 stderr = capturestderr and subprocess.PIPE or None
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
70 close_fds=closefds,
70 close_fds=closefds,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
72 stderr=stderr)
72 stderr=stderr)
73 p.fromchild = p.stdout
73 p.fromchild = p.stdout
74 p.tochild = p.stdin
74 p.tochild = p.stdin
75 p.childerr = p.stderr
75 p.childerr = p.stderr
76 return p
76 return p
77 except ImportError:
77 except ImportError:
78 subprocess = None
78 subprocess = None
79 from popen2 import Popen3
79 from popen2 import Popen3
80 popen2 = os.popen2
80 popen2 = os.popen2
81 popen3 = os.popen3
81 popen3 = os.popen3
82
82
83
83
84 _encodingfixup = {'646': 'ascii', 'ANSI_X3.4-1968': 'ascii'}
84 _encodingfixup = {'646': 'ascii', 'ANSI_X3.4-1968': 'ascii'}
85
85
86 try:
86 try:
87 _encoding = os.environ.get("HGENCODING")
87 _encoding = os.environ.get("HGENCODING")
88 if sys.platform == 'darwin' and not _encoding:
88 if sys.platform == 'darwin' and not _encoding:
89 # On darwin, getpreferredencoding ignores the locale environment and
89 # On darwin, getpreferredencoding ignores the locale environment and
90 # always returns mac-roman. We override this if the environment is
90 # always returns mac-roman. We override this if the environment is
91 # not C (has been customized by the user).
91 # not C (has been customized by the user).
92 locale.setlocale(locale.LC_CTYPE, '')
92 locale.setlocale(locale.LC_CTYPE, '')
93 _encoding = locale.getlocale()[1]
93 _encoding = locale.getlocale()[1]
94 if not _encoding:
94 if not _encoding:
95 _encoding = locale.getpreferredencoding() or 'ascii'
95 _encoding = locale.getpreferredencoding() or 'ascii'
96 _encoding = _encodingfixup.get(_encoding, _encoding)
96 _encoding = _encodingfixup.get(_encoding, _encoding)
97 except locale.Error:
97 except locale.Error:
98 _encoding = 'ascii'
98 _encoding = 'ascii'
99 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
99 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
100 _fallbackencoding = 'ISO-8859-1'
100 _fallbackencoding = 'ISO-8859-1'
101
101
102 def tolocal(s):
102 def tolocal(s):
103 """
103 """
104 Convert a string from internal UTF-8 to local encoding
104 Convert a string from internal UTF-8 to local encoding
105
105
106 All internal strings should be UTF-8 but some repos before the
106 All internal strings should be UTF-8 but some repos before the
107 implementation of locale support may contain latin1 or possibly
107 implementation of locale support may contain latin1 or possibly
108 other character sets. We attempt to decode everything strictly
108 other character sets. We attempt to decode everything strictly
109 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
109 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
110 replace unknown characters.
110 replace unknown characters.
111 """
111 """
112 for e in ('UTF-8', _fallbackencoding):
112 for e in ('UTF-8', _fallbackencoding):
113 try:
113 try:
114 u = s.decode(e) # attempt strict decoding
114 u = s.decode(e) # attempt strict decoding
115 return u.encode(_encoding, "replace")
115 return u.encode(_encoding, "replace")
116 except LookupError, k:
116 except LookupError, k:
117 raise Abort(_("%s, please check your locale settings") % k)
117 raise Abort(_("%s, please check your locale settings") % k)
118 except UnicodeDecodeError:
118 except UnicodeDecodeError:
119 pass
119 pass
120 u = s.decode("utf-8", "replace") # last ditch
120 u = s.decode("utf-8", "replace") # last ditch
121 return u.encode(_encoding, "replace")
121 return u.encode(_encoding, "replace")
122
122
123 def fromlocal(s):
123 def fromlocal(s):
124 """
124 """
125 Convert a string from the local character encoding to UTF-8
125 Convert a string from the local character encoding to UTF-8
126
126
127 We attempt to decode strings using the encoding mode set by
127 We attempt to decode strings using the encoding mode set by
128 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
128 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
129 characters will cause an error message. Other modes include
129 characters will cause an error message. Other modes include
130 'replace', which replaces unknown characters with a special
130 'replace', which replaces unknown characters with a special
131 Unicode character, and 'ignore', which drops the character.
131 Unicode character, and 'ignore', which drops the character.
132 """
132 """
133 try:
133 try:
134 return s.decode(_encoding, _encodingmode).encode("utf-8")
134 return s.decode(_encoding, _encodingmode).encode("utf-8")
135 except UnicodeDecodeError, inst:
135 except UnicodeDecodeError, inst:
136 sub = s[max(0, inst.start-10):inst.start+10]
136 sub = s[max(0, inst.start-10):inst.start+10]
137 raise Abort("decoding near '%s': %s!" % (sub, inst))
137 raise Abort("decoding near '%s': %s!" % (sub, inst))
138 except LookupError, k:
138 except LookupError, k:
139 raise Abort(_("%s, please check your locale settings") % k)
139 raise Abort(_("%s, please check your locale settings") % k)
140
140
141 def colwidth(s):
141 def colwidth(s):
142 "Find the column width of a UTF-8 string for display"
142 "Find the column width of a UTF-8 string for display"
143 d = s.decode(_encoding, 'replace')
143 d = s.decode(_encoding, 'replace')
144 if hasattr(unicodedata, 'east_asian_width'):
144 if hasattr(unicodedata, 'east_asian_width'):
145 w = unicodedata.east_asian_width
145 w = unicodedata.east_asian_width
146 return sum([w(c) in 'WF' and 2 or 1 for c in d])
146 return sum([w(c) in 'WF' and 2 or 1 for c in d])
147 return len(d)
147 return len(d)
148
148
149 def version():
149 def version():
150 """Return version information if available."""
150 """Return version information if available."""
151 try:
151 try:
152 import __version__
152 import __version__
153 return __version__.version
153 return __version__.version
154 except ImportError:
154 except ImportError:
155 return 'unknown'
155 return 'unknown'
156
156
157 # used by parsedate
157 # used by parsedate
158 defaultdateformats = (
158 defaultdateformats = (
159 '%Y-%m-%d %H:%M:%S',
159 '%Y-%m-%d %H:%M:%S',
160 '%Y-%m-%d %I:%M:%S%p',
160 '%Y-%m-%d %I:%M:%S%p',
161 '%Y-%m-%d %H:%M',
161 '%Y-%m-%d %H:%M',
162 '%Y-%m-%d %I:%M%p',
162 '%Y-%m-%d %I:%M%p',
163 '%Y-%m-%d',
163 '%Y-%m-%d',
164 '%m-%d',
164 '%m-%d',
165 '%m/%d',
165 '%m/%d',
166 '%m/%d/%y',
166 '%m/%d/%y',
167 '%m/%d/%Y',
167 '%m/%d/%Y',
168 '%a %b %d %H:%M:%S %Y',
168 '%a %b %d %H:%M:%S %Y',
169 '%a %b %d %I:%M:%S%p %Y',
169 '%a %b %d %I:%M:%S%p %Y',
170 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
170 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
171 '%b %d %H:%M:%S %Y',
171 '%b %d %H:%M:%S %Y',
172 '%b %d %I:%M:%S%p %Y',
172 '%b %d %I:%M:%S%p %Y',
173 '%b %d %H:%M:%S',
173 '%b %d %H:%M:%S',
174 '%b %d %I:%M:%S%p',
174 '%b %d %I:%M:%S%p',
175 '%b %d %H:%M',
175 '%b %d %H:%M',
176 '%b %d %I:%M%p',
176 '%b %d %I:%M%p',
177 '%b %d %Y',
177 '%b %d %Y',
178 '%b %d',
178 '%b %d',
179 '%H:%M:%S',
179 '%H:%M:%S',
180 '%I:%M:%SP',
180 '%I:%M:%SP',
181 '%H:%M',
181 '%H:%M',
182 '%I:%M%p',
182 '%I:%M%p',
183 )
183 )
184
184
185 extendeddateformats = defaultdateformats + (
185 extendeddateformats = defaultdateformats + (
186 "%Y",
186 "%Y",
187 "%Y-%m",
187 "%Y-%m",
188 "%b",
188 "%b",
189 "%b %Y",
189 "%b %Y",
190 )
190 )
191
191
192 # differences from SafeConfigParser:
192 # differences from SafeConfigParser:
193 # - case-sensitive keys
193 # - case-sensitive keys
194 # - allows values that are not strings (this means that you may not
194 # - allows values that are not strings (this means that you may not
195 # be able to save the configuration to a file)
195 # be able to save the configuration to a file)
196 class configparser(ConfigParser.SafeConfigParser):
196 class configparser(ConfigParser.SafeConfigParser):
197 def optionxform(self, optionstr):
197 def optionxform(self, optionstr):
198 return optionstr
198 return optionstr
199
199
200 def set(self, section, option, value):
200 def set(self, section, option, value):
201 return ConfigParser.ConfigParser.set(self, section, option, value)
201 return ConfigParser.ConfigParser.set(self, section, option, value)
202
202
203 def _interpolate(self, section, option, rawval, vars):
203 def _interpolate(self, section, option, rawval, vars):
204 if not isinstance(rawval, basestring):
204 if not isinstance(rawval, basestring):
205 return rawval
205 return rawval
206 return ConfigParser.SafeConfigParser._interpolate(self, section,
206 return ConfigParser.SafeConfigParser._interpolate(self, section,
207 option, rawval, vars)
207 option, rawval, vars)
208
208
209 def cachefunc(func):
209 def cachefunc(func):
210 '''cache the result of function calls'''
210 '''cache the result of function calls'''
211 # XXX doesn't handle keywords args
211 # XXX doesn't handle keywords args
212 cache = {}
212 cache = {}
213 if func.func_code.co_argcount == 1:
213 if func.func_code.co_argcount == 1:
214 # we gain a small amount of time because
214 # we gain a small amount of time because
215 # we don't need to pack/unpack the list
215 # we don't need to pack/unpack the list
216 def f(arg):
216 def f(arg):
217 if arg not in cache:
217 if arg not in cache:
218 cache[arg] = func(arg)
218 cache[arg] = func(arg)
219 return cache[arg]
219 return cache[arg]
220 else:
220 else:
221 def f(*args):
221 def f(*args):
222 if args not in cache:
222 if args not in cache:
223 cache[args] = func(*args)
223 cache[args] = func(*args)
224 return cache[args]
224 return cache[args]
225
225
226 return f
226 return f
227
227
228 def pipefilter(s, cmd):
228 def pipefilter(s, cmd):
229 '''filter string S through command CMD, returning its output'''
229 '''filter string S through command CMD, returning its output'''
230 (pin, pout) = popen2(cmd, 'b')
230 (pin, pout) = popen2(cmd, 'b')
231 def writer():
231 def writer():
232 try:
232 try:
233 pin.write(s)
233 pin.write(s)
234 pin.close()
234 pin.close()
235 except IOError, inst:
235 except IOError, inst:
236 if inst.errno != errno.EPIPE:
236 if inst.errno != errno.EPIPE:
237 raise
237 raise
238
238
239 # we should use select instead on UNIX, but this will work on most
239 # we should use select instead on UNIX, but this will work on most
240 # systems, including Windows
240 # systems, including Windows
241 w = threading.Thread(target=writer)
241 w = threading.Thread(target=writer)
242 w.start()
242 w.start()
243 f = pout.read()
243 f = pout.read()
244 pout.close()
244 pout.close()
245 w.join()
245 w.join()
246 return f
246 return f
247
247
248 def tempfilter(s, cmd):
248 def tempfilter(s, cmd):
249 '''filter string S through a pair of temporary files with CMD.
249 '''filter string S through a pair of temporary files with CMD.
250 CMD is used as a template to create the real command to be run,
250 CMD is used as a template to create the real command to be run,
251 with the strings INFILE and OUTFILE replaced by the real names of
251 with the strings INFILE and OUTFILE replaced by the real names of
252 the temporary files generated.'''
252 the temporary files generated.'''
253 inname, outname = None, None
253 inname, outname = None, None
254 try:
254 try:
255 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
255 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
256 fp = os.fdopen(infd, 'wb')
256 fp = os.fdopen(infd, 'wb')
257 fp.write(s)
257 fp.write(s)
258 fp.close()
258 fp.close()
259 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
259 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
260 os.close(outfd)
260 os.close(outfd)
261 cmd = cmd.replace('INFILE', inname)
261 cmd = cmd.replace('INFILE', inname)
262 cmd = cmd.replace('OUTFILE', outname)
262 cmd = cmd.replace('OUTFILE', outname)
263 code = os.system(cmd)
263 code = os.system(cmd)
264 if sys.platform == 'OpenVMS' and code & 1:
264 if sys.platform == 'OpenVMS' and code & 1:
265 code = 0
265 code = 0
266 if code: raise Abort(_("command '%s' failed: %s") %
266 if code: raise Abort(_("command '%s' failed: %s") %
267 (cmd, explain_exit(code)))
267 (cmd, explain_exit(code)))
268 return open(outname, 'rb').read()
268 return open(outname, 'rb').read()
269 finally:
269 finally:
270 try:
270 try:
271 if inname: os.unlink(inname)
271 if inname: os.unlink(inname)
272 except: pass
272 except: pass
273 try:
273 try:
274 if outname: os.unlink(outname)
274 if outname: os.unlink(outname)
275 except: pass
275 except: pass
276
276
277 filtertable = {
277 filtertable = {
278 'tempfile:': tempfilter,
278 'tempfile:': tempfilter,
279 'pipe:': pipefilter,
279 'pipe:': pipefilter,
280 }
280 }
281
281
282 def filter(s, cmd):
282 def filter(s, cmd):
283 "filter a string through a command that transforms its input to its output"
283 "filter a string through a command that transforms its input to its output"
284 for name, fn in filtertable.iteritems():
284 for name, fn in filtertable.iteritems():
285 if cmd.startswith(name):
285 if cmd.startswith(name):
286 return fn(s, cmd[len(name):].lstrip())
286 return fn(s, cmd[len(name):].lstrip())
287 return pipefilter(s, cmd)
287 return pipefilter(s, cmd)
288
288
289 def binary(s):
289 def binary(s):
290 """return true if a string is binary data"""
290 """return true if a string is binary data"""
291 if s and '\0' in s:
291 if s and '\0' in s:
292 return True
292 return True
293 return False
293 return False
294
294
295 def unique(g):
295 def unique(g):
296 """return the uniq elements of iterable g"""
296 """return the uniq elements of iterable g"""
297 return dict.fromkeys(g).keys()
297 return dict.fromkeys(g).keys()
298
298
299 def sort(l):
299 def sort(l):
300 if not isinstance(l, list):
300 if not isinstance(l, list):
301 l = list(l)
301 l = list(l)
302 l.sort()
302 l.sort()
303 return l
303 return l
304
304
305 def increasingchunks(source, min=1024, max=65536):
305 def increasingchunks(source, min=1024, max=65536):
306 '''return no less than min bytes per chunk while data remains,
306 '''return no less than min bytes per chunk while data remains,
307 doubling min after each chunk until it reaches max'''
307 doubling min after each chunk until it reaches max'''
308 def log2(x):
308 def log2(x):
309 if not x:
309 if not x:
310 return 0
310 return 0
311 i = 0
311 i = 0
312 while x:
312 while x:
313 x >>= 1
313 x >>= 1
314 i += 1
314 i += 1
315 return i - 1
315 return i - 1
316
316
317 buf = []
317 buf = []
318 blen = 0
318 blen = 0
319 for chunk in source:
319 for chunk in source:
320 buf.append(chunk)
320 buf.append(chunk)
321 blen += len(chunk)
321 blen += len(chunk)
322 if blen >= min:
322 if blen >= min:
323 if min < max:
323 if min < max:
324 min = min << 1
324 min = min << 1
325 nmin = 1 << log2(blen)
325 nmin = 1 << log2(blen)
326 if nmin > min:
326 if nmin > min:
327 min = nmin
327 min = nmin
328 if min > max:
328 if min > max:
329 min = max
329 min = max
330 yield ''.join(buf)
330 yield ''.join(buf)
331 blen = 0
331 blen = 0
332 buf = []
332 buf = []
333 if buf:
333 if buf:
334 yield ''.join(buf)
334 yield ''.join(buf)
335
335
336 class Abort(Exception):
336 class Abort(Exception):
337 """Raised if a command needs to print an error and exit."""
337 """Raised if a command needs to print an error and exit."""
338
338
339 def always(fn): return True
339 def always(fn): return True
340 def never(fn): return False
340 def never(fn): return False
341
341
342 def expand_glob(pats):
343 '''On Windows, expand the implicit globs in a list of patterns'''
344 if os.name != 'nt':
345 return list(pats)
346 ret = []
347 for p in pats:
348 kind, name = patkind(p, None)
349 if kind is None:
350 globbed = glob.glob(name)
351 if globbed:
352 ret.extend(globbed)
353 continue
354 # if we couldn't expand the glob, just keep it around
355 ret.append(p)
356 return ret
357
358 def patkind(name, default):
342 def patkind(name, default):
359 """Split a string into an optional pattern kind prefix and the
343 """Split a string into an optional pattern kind prefix and the
360 actual pattern."""
344 actual pattern."""
361 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
345 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
362 if name.startswith(prefix + ':'): return name.split(':', 1)
346 if name.startswith(prefix + ':'): return name.split(':', 1)
363 return default, name
347 return default, name
364
348
365 def globre(pat, head='^', tail='$'):
349 def globre(pat, head='^', tail='$'):
366 "convert a glob pattern into a regexp"
350 "convert a glob pattern into a regexp"
367 i, n = 0, len(pat)
351 i, n = 0, len(pat)
368 res = ''
352 res = ''
369 group = 0
353 group = 0
370 def peek(): return i < n and pat[i]
354 def peek(): return i < n and pat[i]
371 while i < n:
355 while i < n:
372 c = pat[i]
356 c = pat[i]
373 i = i+1
357 i = i+1
374 if c == '*':
358 if c == '*':
375 if peek() == '*':
359 if peek() == '*':
376 i += 1
360 i += 1
377 res += '.*'
361 res += '.*'
378 else:
362 else:
379 res += '[^/]*'
363 res += '[^/]*'
380 elif c == '?':
364 elif c == '?':
381 res += '.'
365 res += '.'
382 elif c == '[':
366 elif c == '[':
383 j = i
367 j = i
384 if j < n and pat[j] in '!]':
368 if j < n and pat[j] in '!]':
385 j += 1
369 j += 1
386 while j < n and pat[j] != ']':
370 while j < n and pat[j] != ']':
387 j += 1
371 j += 1
388 if j >= n:
372 if j >= n:
389 res += '\\['
373 res += '\\['
390 else:
374 else:
391 stuff = pat[i:j].replace('\\','\\\\')
375 stuff = pat[i:j].replace('\\','\\\\')
392 i = j + 1
376 i = j + 1
393 if stuff[0] == '!':
377 if stuff[0] == '!':
394 stuff = '^' + stuff[1:]
378 stuff = '^' + stuff[1:]
395 elif stuff[0] == '^':
379 elif stuff[0] == '^':
396 stuff = '\\' + stuff
380 stuff = '\\' + stuff
397 res = '%s[%s]' % (res, stuff)
381 res = '%s[%s]' % (res, stuff)
398 elif c == '{':
382 elif c == '{':
399 group += 1
383 group += 1
400 res += '(?:'
384 res += '(?:'
401 elif c == '}' and group:
385 elif c == '}' and group:
402 res += ')'
386 res += ')'
403 group -= 1
387 group -= 1
404 elif c == ',' and group:
388 elif c == ',' and group:
405 res += '|'
389 res += '|'
406 elif c == '\\':
390 elif c == '\\':
407 p = peek()
391 p = peek()
408 if p:
392 if p:
409 i += 1
393 i += 1
410 res += re.escape(p)
394 res += re.escape(p)
411 else:
395 else:
412 res += re.escape(c)
396 res += re.escape(c)
413 else:
397 else:
414 res += re.escape(c)
398 res += re.escape(c)
415 return head + res + tail
399 return head + res + tail
416
400
417 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
401 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
418
402
419 def pathto(root, n1, n2):
403 def pathto(root, n1, n2):
420 '''return the relative path from one place to another.
404 '''return the relative path from one place to another.
421 root should use os.sep to separate directories
405 root should use os.sep to separate directories
422 n1 should use os.sep to separate directories
406 n1 should use os.sep to separate directories
423 n2 should use "/" to separate directories
407 n2 should use "/" to separate directories
424 returns an os.sep-separated path.
408 returns an os.sep-separated path.
425
409
426 If n1 is a relative path, it's assumed it's
410 If n1 is a relative path, it's assumed it's
427 relative to root.
411 relative to root.
428 n2 should always be relative to root.
412 n2 should always be relative to root.
429 '''
413 '''
430 if not n1: return localpath(n2)
414 if not n1: return localpath(n2)
431 if os.path.isabs(n1):
415 if os.path.isabs(n1):
432 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
416 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
433 return os.path.join(root, localpath(n2))
417 return os.path.join(root, localpath(n2))
434 n2 = '/'.join((pconvert(root), n2))
418 n2 = '/'.join((pconvert(root), n2))
435 a, b = splitpath(n1), n2.split('/')
419 a, b = splitpath(n1), n2.split('/')
436 a.reverse()
420 a.reverse()
437 b.reverse()
421 b.reverse()
438 while a and b and a[-1] == b[-1]:
422 while a and b and a[-1] == b[-1]:
439 a.pop()
423 a.pop()
440 b.pop()
424 b.pop()
441 b.reverse()
425 b.reverse()
442 return os.sep.join((['..'] * len(a)) + b) or '.'
426 return os.sep.join((['..'] * len(a)) + b) or '.'
443
427
444 def canonpath(root, cwd, myname):
428 def canonpath(root, cwd, myname):
445 """return the canonical path of myname, given cwd and root"""
429 """return the canonical path of myname, given cwd and root"""
446 if root == os.sep:
430 if root == os.sep:
447 rootsep = os.sep
431 rootsep = os.sep
448 elif endswithsep(root):
432 elif endswithsep(root):
449 rootsep = root
433 rootsep = root
450 else:
434 else:
451 rootsep = root + os.sep
435 rootsep = root + os.sep
452 name = myname
436 name = myname
453 if not os.path.isabs(name):
437 if not os.path.isabs(name):
454 name = os.path.join(root, cwd, name)
438 name = os.path.join(root, cwd, name)
455 name = os.path.normpath(name)
439 name = os.path.normpath(name)
456 audit_path = path_auditor(root)
440 audit_path = path_auditor(root)
457 if name != rootsep and name.startswith(rootsep):
441 if name != rootsep and name.startswith(rootsep):
458 name = name[len(rootsep):]
442 name = name[len(rootsep):]
459 audit_path(name)
443 audit_path(name)
460 return pconvert(name)
444 return pconvert(name)
461 elif name == root:
445 elif name == root:
462 return ''
446 return ''
463 else:
447 else:
464 # Determine whether `name' is in the hierarchy at or beneath `root',
448 # Determine whether `name' is in the hierarchy at or beneath `root',
465 # by iterating name=dirname(name) until that causes no change (can't
449 # by iterating name=dirname(name) until that causes no change (can't
466 # check name == '/', because that doesn't work on windows). For each
450 # check name == '/', because that doesn't work on windows). For each
467 # `name', compare dev/inode numbers. If they match, the list `rel'
451 # `name', compare dev/inode numbers. If they match, the list `rel'
468 # holds the reversed list of components making up the relative file
452 # holds the reversed list of components making up the relative file
469 # name we want.
453 # name we want.
470 root_st = os.stat(root)
454 root_st = os.stat(root)
471 rel = []
455 rel = []
472 while True:
456 while True:
473 try:
457 try:
474 name_st = os.stat(name)
458 name_st = os.stat(name)
475 except OSError:
459 except OSError:
476 break
460 break
477 if samestat(name_st, root_st):
461 if samestat(name_st, root_st):
478 if not rel:
462 if not rel:
479 # name was actually the same as root (maybe a symlink)
463 # name was actually the same as root (maybe a symlink)
480 return ''
464 return ''
481 rel.reverse()
465 rel.reverse()
482 name = os.path.join(*rel)
466 name = os.path.join(*rel)
483 audit_path(name)
467 audit_path(name)
484 return pconvert(name)
468 return pconvert(name)
485 dirname, basename = os.path.split(name)
469 dirname, basename = os.path.split(name)
486 rel.append(basename)
470 rel.append(basename)
487 if dirname == name:
471 if dirname == name:
488 break
472 break
489 name = dirname
473 name = dirname
490
474
491 raise Abort('%s not under root' % myname)
475 raise Abort('%s not under root' % myname)
492
476
493 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
477 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
494 """build a function to match a set of file patterns
478 """build a function to match a set of file patterns
495
479
496 arguments:
480 arguments:
497 canonroot - the canonical root of the tree you're matching against
481 canonroot - the canonical root of the tree you're matching against
498 cwd - the current working directory, if relevant
482 cwd - the current working directory, if relevant
499 names - patterns to find
483 names - patterns to find
500 inc - patterns to include
484 inc - patterns to include
501 exc - patterns to exclude
485 exc - patterns to exclude
502 dflt_pat - if a pattern in names has no explicit type, assume this one
486 dflt_pat - if a pattern in names has no explicit type, assume this one
503 src - where these patterns came from (e.g. .hgignore)
487 src - where these patterns came from (e.g. .hgignore)
504
488
505 a pattern is one of:
489 a pattern is one of:
506 'glob:<glob>' - a glob relative to cwd
490 'glob:<glob>' - a glob relative to cwd
507 're:<regexp>' - a regular expression
491 're:<regexp>' - a regular expression
508 'path:<path>' - a path relative to canonroot
492 'path:<path>' - a path relative to canonroot
509 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
493 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
510 'relpath:<path>' - a path relative to cwd
494 'relpath:<path>' - a path relative to cwd
511 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
495 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
512 '<something>' - one of the cases above, selected by the dflt_pat argument
496 '<something>' - one of the cases above, selected by the dflt_pat argument
513
497
514 returns:
498 returns:
515 a 3-tuple containing
499 a 3-tuple containing
516 - list of roots (places where one should start a recursive walk of the fs);
500 - list of roots (places where one should start a recursive walk of the fs);
517 this often matches the explicit non-pattern names passed in, but also
501 this often matches the explicit non-pattern names passed in, but also
518 includes the initial part of glob: patterns that has no glob characters
502 includes the initial part of glob: patterns that has no glob characters
519 - a bool match(filename) function
503 - a bool match(filename) function
520 - a bool indicating if any patterns were passed in
504 - a bool indicating if any patterns were passed in
521 """
505 """
522
506
523 # a common case: no patterns at all
507 # a common case: no patterns at all
524 if not names and not inc and not exc:
508 if not names and not inc and not exc:
525 return [], always, False
509 return [], always, False
526
510
527 def contains_glob(name):
511 def contains_glob(name):
528 for c in name:
512 for c in name:
529 if c in _globchars: return True
513 if c in _globchars: return True
530 return False
514 return False
531
515
532 def regex(kind, name, tail):
516 def regex(kind, name, tail):
533 '''convert a pattern into a regular expression'''
517 '''convert a pattern into a regular expression'''
534 if not name:
518 if not name:
535 return ''
519 return ''
536 if kind == 're':
520 if kind == 're':
537 return name
521 return name
538 elif kind == 'path':
522 elif kind == 'path':
539 return '^' + re.escape(name) + '(?:/|$)'
523 return '^' + re.escape(name) + '(?:/|$)'
540 elif kind == 'relglob':
524 elif kind == 'relglob':
541 return globre(name, '(?:|.*/)', tail)
525 return globre(name, '(?:|.*/)', tail)
542 elif kind == 'relpath':
526 elif kind == 'relpath':
543 return re.escape(name) + '(?:/|$)'
527 return re.escape(name) + '(?:/|$)'
544 elif kind == 'relre':
528 elif kind == 'relre':
545 if name.startswith('^'):
529 if name.startswith('^'):
546 return name
530 return name
547 return '.*' + name
531 return '.*' + name
548 return globre(name, '', tail)
532 return globre(name, '', tail)
549
533
550 def matchfn(pats, tail):
534 def matchfn(pats, tail):
551 """build a matching function from a set of patterns"""
535 """build a matching function from a set of patterns"""
552 if not pats:
536 if not pats:
553 return
537 return
554 try:
538 try:
555 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
539 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
556 if len(pat) > 20000:
540 if len(pat) > 20000:
557 raise OverflowError()
541 raise OverflowError()
558 return re.compile(pat).match
542 return re.compile(pat).match
559 except OverflowError:
543 except OverflowError:
560 # We're using a Python with a tiny regex engine and we
544 # We're using a Python with a tiny regex engine and we
561 # made it explode, so we'll divide the pattern list in two
545 # made it explode, so we'll divide the pattern list in two
562 # until it works
546 # until it works
563 l = len(pats)
547 l = len(pats)
564 if l < 2:
548 if l < 2:
565 raise
549 raise
566 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
550 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
567 return lambda s: a(s) or b(s)
551 return lambda s: a(s) or b(s)
568 except re.error:
552 except re.error:
569 for k, p in pats:
553 for k, p in pats:
570 try:
554 try:
571 re.compile('(?:%s)' % regex(k, p, tail))
555 re.compile('(?:%s)' % regex(k, p, tail))
572 except re.error:
556 except re.error:
573 if src:
557 if src:
574 raise Abort("%s: invalid pattern (%s): %s" %
558 raise Abort("%s: invalid pattern (%s): %s" %
575 (src, k, p))
559 (src, k, p))
576 else:
560 else:
577 raise Abort("invalid pattern (%s): %s" % (k, p))
561 raise Abort("invalid pattern (%s): %s" % (k, p))
578 raise Abort("invalid pattern")
562 raise Abort("invalid pattern")
579
563
580 def globprefix(pat):
564 def globprefix(pat):
581 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
565 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
582 root = []
566 root = []
583 for p in pat.split('/'):
567 for p in pat.split('/'):
584 if contains_glob(p): break
568 if contains_glob(p): break
585 root.append(p)
569 root.append(p)
586 return '/'.join(root) or '.'
570 return '/'.join(root) or '.'
587
571
588 def normalizepats(names, default):
572 def normalizepats(names, default):
589 pats = []
573 pats = []
590 roots = []
574 roots = []
591 anypats = False
575 anypats = False
592 for kind, name in [patkind(p, default) for p in names]:
576 for kind, name in [patkind(p, default) for p in names]:
593 if kind in ('glob', 'relpath'):
577 if kind in ('glob', 'relpath'):
594 name = canonpath(canonroot, cwd, name)
578 name = canonpath(canonroot, cwd, name)
595 elif kind in ('relglob', 'path'):
579 elif kind in ('relglob', 'path'):
596 name = normpath(name)
580 name = normpath(name)
597
581
598 pats.append((kind, name))
582 pats.append((kind, name))
599
583
600 if kind in ('glob', 're', 'relglob', 'relre'):
584 if kind in ('glob', 're', 'relglob', 'relre'):
601 anypats = True
585 anypats = True
602
586
603 if kind == 'glob':
587 if kind == 'glob':
604 root = globprefix(name)
588 root = globprefix(name)
605 roots.append(root)
589 roots.append(root)
606 elif kind in ('relpath', 'path'):
590 elif kind in ('relpath', 'path'):
607 roots.append(name or '.')
591 roots.append(name or '.')
608 elif kind == 'relglob':
592 elif kind == 'relglob':
609 roots.append('.')
593 roots.append('.')
610 return roots, pats, anypats
594 return roots, pats, anypats
611
595
612 roots, pats, anypats = normalizepats(names, dflt_pat)
596 roots, pats, anypats = normalizepats(names, dflt_pat)
613
597
614 patmatch = matchfn(pats, '$') or always
598 patmatch = matchfn(pats, '$') or always
615 incmatch = always
599 incmatch = always
616 if inc:
600 if inc:
617 dummy, inckinds, dummy = normalizepats(inc, 'glob')
601 dummy, inckinds, dummy = normalizepats(inc, 'glob')
618 incmatch = matchfn(inckinds, '(?:/|$)')
602 incmatch = matchfn(inckinds, '(?:/|$)')
619 excmatch = never
603 excmatch = never
620 if exc:
604 if exc:
621 dummy, exckinds, dummy = normalizepats(exc, 'glob')
605 dummy, exckinds, dummy = normalizepats(exc, 'glob')
622 excmatch = matchfn(exckinds, '(?:/|$)')
606 excmatch = matchfn(exckinds, '(?:/|$)')
623
607
624 if not names and inc and not exc:
608 if not names and inc and not exc:
625 # common case: hgignore patterns
609 # common case: hgignore patterns
626 match = incmatch
610 match = incmatch
627 else:
611 else:
628 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
612 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
629
613
630 return (roots, match, (inc or exc or anypats) and True)
614 return (roots, match, (inc or exc or anypats) and True)
631
615
632 _hgexecutable = None
616 _hgexecutable = None
633
617
634 def main_is_frozen():
618 def main_is_frozen():
635 """return True if we are a frozen executable.
619 """return True if we are a frozen executable.
636
620
637 The code supports py2exe (most common, Windows only) and tools/freeze
621 The code supports py2exe (most common, Windows only) and tools/freeze
638 (portable, not much used).
622 (portable, not much used).
639 """
623 """
640 return (hasattr(sys, "frozen") or # new py2exe
624 return (hasattr(sys, "frozen") or # new py2exe
641 hasattr(sys, "importers") or # old py2exe
625 hasattr(sys, "importers") or # old py2exe
642 imp.is_frozen("__main__")) # tools/freeze
626 imp.is_frozen("__main__")) # tools/freeze
643
627
644 def hgexecutable():
628 def hgexecutable():
645 """return location of the 'hg' executable.
629 """return location of the 'hg' executable.
646
630
647 Defaults to $HG or 'hg' in the search path.
631 Defaults to $HG or 'hg' in the search path.
648 """
632 """
649 if _hgexecutable is None:
633 if _hgexecutable is None:
650 hg = os.environ.get('HG')
634 hg = os.environ.get('HG')
651 if hg:
635 if hg:
652 set_hgexecutable(hg)
636 set_hgexecutable(hg)
653 elif main_is_frozen():
637 elif main_is_frozen():
654 set_hgexecutable(sys.executable)
638 set_hgexecutable(sys.executable)
655 else:
639 else:
656 set_hgexecutable(find_exe('hg') or 'hg')
640 set_hgexecutable(find_exe('hg') or 'hg')
657 return _hgexecutable
641 return _hgexecutable
658
642
659 def set_hgexecutable(path):
643 def set_hgexecutable(path):
660 """set location of the 'hg' executable"""
644 """set location of the 'hg' executable"""
661 global _hgexecutable
645 global _hgexecutable
662 _hgexecutable = path
646 _hgexecutable = path
663
647
664 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
648 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
665 '''enhanced shell command execution.
649 '''enhanced shell command execution.
666 run with environment maybe modified, maybe in different dir.
650 run with environment maybe modified, maybe in different dir.
667
651
668 if command fails and onerr is None, return status. if ui object,
652 if command fails and onerr is None, return status. if ui object,
669 print error message and return status, else raise onerr object as
653 print error message and return status, else raise onerr object as
670 exception.'''
654 exception.'''
671 def py2shell(val):
655 def py2shell(val):
672 'convert python object into string that is useful to shell'
656 'convert python object into string that is useful to shell'
673 if val in (None, False):
657 if val in (None, False):
674 return '0'
658 return '0'
675 if val == True:
659 if val == True:
676 return '1'
660 return '1'
677 return str(val)
661 return str(val)
678 oldenv = {}
662 oldenv = {}
679 for k in environ:
663 for k in environ:
680 oldenv[k] = os.environ.get(k)
664 oldenv[k] = os.environ.get(k)
681 if cwd is not None:
665 if cwd is not None:
682 oldcwd = os.getcwd()
666 oldcwd = os.getcwd()
683 origcmd = cmd
667 origcmd = cmd
684 if os.name == 'nt':
668 if os.name == 'nt':
685 cmd = '"%s"' % cmd
669 cmd = '"%s"' % cmd
686 try:
670 try:
687 for k, v in environ.iteritems():
671 for k, v in environ.iteritems():
688 os.environ[k] = py2shell(v)
672 os.environ[k] = py2shell(v)
689 os.environ['HG'] = hgexecutable()
673 os.environ['HG'] = hgexecutable()
690 if cwd is not None and oldcwd != cwd:
674 if cwd is not None and oldcwd != cwd:
691 os.chdir(cwd)
675 os.chdir(cwd)
692 rc = os.system(cmd)
676 rc = os.system(cmd)
693 if sys.platform == 'OpenVMS' and rc & 1:
677 if sys.platform == 'OpenVMS' and rc & 1:
694 rc = 0
678 rc = 0
695 if rc and onerr:
679 if rc and onerr:
696 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
680 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
697 explain_exit(rc)[0])
681 explain_exit(rc)[0])
698 if errprefix:
682 if errprefix:
699 errmsg = '%s: %s' % (errprefix, errmsg)
683 errmsg = '%s: %s' % (errprefix, errmsg)
700 try:
684 try:
701 onerr.warn(errmsg + '\n')
685 onerr.warn(errmsg + '\n')
702 except AttributeError:
686 except AttributeError:
703 raise onerr(errmsg)
687 raise onerr(errmsg)
704 return rc
688 return rc
705 finally:
689 finally:
706 for k, v in oldenv.iteritems():
690 for k, v in oldenv.iteritems():
707 if v is None:
691 if v is None:
708 del os.environ[k]
692 del os.environ[k]
709 else:
693 else:
710 os.environ[k] = v
694 os.environ[k] = v
711 if cwd is not None and oldcwd != cwd:
695 if cwd is not None and oldcwd != cwd:
712 os.chdir(oldcwd)
696 os.chdir(oldcwd)
713
697
714 def checksignature(func):
698 def checksignature(func):
715 '''wrap a function with code to check for calling errors'''
699 '''wrap a function with code to check for calling errors'''
716 def check(*args, **kwargs):
700 def check(*args, **kwargs):
717 try:
701 try:
718 return func(*args, **kwargs)
702 return func(*args, **kwargs)
719 except TypeError:
703 except TypeError:
720 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
704 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
721 raise error.SignatureError
705 raise error.SignatureError
722 raise
706 raise
723
707
724 return check
708 return check
725
709
726 # os.path.lexists is not available on python2.3
710 # os.path.lexists is not available on python2.3
727 def lexists(filename):
711 def lexists(filename):
728 "test whether a file with this name exists. does not follow symlinks"
712 "test whether a file with this name exists. does not follow symlinks"
729 try:
713 try:
730 os.lstat(filename)
714 os.lstat(filename)
731 except:
715 except:
732 return False
716 return False
733 return True
717 return True
734
718
735 def rename(src, dst):
719 def rename(src, dst):
736 """forcibly rename a file"""
720 """forcibly rename a file"""
737 try:
721 try:
738 os.rename(src, dst)
722 os.rename(src, dst)
739 except OSError, err: # FIXME: check err (EEXIST ?)
723 except OSError, err: # FIXME: check err (EEXIST ?)
740 # on windows, rename to existing file is not allowed, so we
724 # on windows, rename to existing file is not allowed, so we
741 # must delete destination first. but if file is open, unlink
725 # must delete destination first. but if file is open, unlink
742 # schedules it for delete but does not delete it. rename
726 # schedules it for delete but does not delete it. rename
743 # happens immediately even for open files, so we rename
727 # happens immediately even for open files, so we rename
744 # destination to a temporary name, then delete that. then
728 # destination to a temporary name, then delete that. then
745 # rename is safe to do.
729 # rename is safe to do.
746 temp = dst + "-force-rename"
730 temp = dst + "-force-rename"
747 os.rename(dst, temp)
731 os.rename(dst, temp)
748 os.unlink(temp)
732 os.unlink(temp)
749 os.rename(src, dst)
733 os.rename(src, dst)
750
734
751 def unlink(f):
735 def unlink(f):
752 """unlink and remove the directory if it is empty"""
736 """unlink and remove the directory if it is empty"""
753 os.unlink(f)
737 os.unlink(f)
754 # try removing directories that might now be empty
738 # try removing directories that might now be empty
755 try:
739 try:
756 os.removedirs(os.path.dirname(f))
740 os.removedirs(os.path.dirname(f))
757 except OSError:
741 except OSError:
758 pass
742 pass
759
743
760 def copyfile(src, dest):
744 def copyfile(src, dest):
761 "copy a file, preserving mode and atime/mtime"
745 "copy a file, preserving mode and atime/mtime"
762 if os.path.islink(src):
746 if os.path.islink(src):
763 try:
747 try:
764 os.unlink(dest)
748 os.unlink(dest)
765 except:
749 except:
766 pass
750 pass
767 os.symlink(os.readlink(src), dest)
751 os.symlink(os.readlink(src), dest)
768 else:
752 else:
769 try:
753 try:
770 shutil.copyfile(src, dest)
754 shutil.copyfile(src, dest)
771 shutil.copystat(src, dest)
755 shutil.copystat(src, dest)
772 except shutil.Error, inst:
756 except shutil.Error, inst:
773 raise Abort(str(inst))
757 raise Abort(str(inst))
774
758
775 def copyfiles(src, dst, hardlink=None):
759 def copyfiles(src, dst, hardlink=None):
776 """Copy a directory tree using hardlinks if possible"""
760 """Copy a directory tree using hardlinks if possible"""
777
761
778 if hardlink is None:
762 if hardlink is None:
779 hardlink = (os.stat(src).st_dev ==
763 hardlink = (os.stat(src).st_dev ==
780 os.stat(os.path.dirname(dst)).st_dev)
764 os.stat(os.path.dirname(dst)).st_dev)
781
765
782 if os.path.isdir(src):
766 if os.path.isdir(src):
783 os.mkdir(dst)
767 os.mkdir(dst)
784 for name, kind in osutil.listdir(src):
768 for name, kind in osutil.listdir(src):
785 srcname = os.path.join(src, name)
769 srcname = os.path.join(src, name)
786 dstname = os.path.join(dst, name)
770 dstname = os.path.join(dst, name)
787 copyfiles(srcname, dstname, hardlink)
771 copyfiles(srcname, dstname, hardlink)
788 else:
772 else:
789 if hardlink:
773 if hardlink:
790 try:
774 try:
791 os_link(src, dst)
775 os_link(src, dst)
792 except (IOError, OSError):
776 except (IOError, OSError):
793 hardlink = False
777 hardlink = False
794 shutil.copy(src, dst)
778 shutil.copy(src, dst)
795 else:
779 else:
796 shutil.copy(src, dst)
780 shutil.copy(src, dst)
797
781
798 class path_auditor(object):
782 class path_auditor(object):
799 '''ensure that a filesystem path contains no banned components.
783 '''ensure that a filesystem path contains no banned components.
800 the following properties of a path are checked:
784 the following properties of a path are checked:
801
785
802 - under top-level .hg
786 - under top-level .hg
803 - starts at the root of a windows drive
787 - starts at the root of a windows drive
804 - contains ".."
788 - contains ".."
805 - traverses a symlink (e.g. a/symlink_here/b)
789 - traverses a symlink (e.g. a/symlink_here/b)
806 - inside a nested repository'''
790 - inside a nested repository'''
807
791
808 def __init__(self, root):
792 def __init__(self, root):
809 self.audited = set()
793 self.audited = set()
810 self.auditeddir = set()
794 self.auditeddir = set()
811 self.root = root
795 self.root = root
812
796
813 def __call__(self, path):
797 def __call__(self, path):
814 if path in self.audited:
798 if path in self.audited:
815 return
799 return
816 normpath = os.path.normcase(path)
800 normpath = os.path.normcase(path)
817 parts = splitpath(normpath)
801 parts = splitpath(normpath)
818 if (os.path.splitdrive(path)[0]
802 if (os.path.splitdrive(path)[0]
819 or parts[0].lower() in ('.hg', '.hg.', '')
803 or parts[0].lower() in ('.hg', '.hg.', '')
820 or os.pardir in parts):
804 or os.pardir in parts):
821 raise Abort(_("path contains illegal component: %s") % path)
805 raise Abort(_("path contains illegal component: %s") % path)
822 if '.hg' in path.lower():
806 if '.hg' in path.lower():
823 lparts = [p.lower() for p in parts]
807 lparts = [p.lower() for p in parts]
824 for p in '.hg', '.hg.':
808 for p in '.hg', '.hg.':
825 if p in lparts[1:]:
809 if p in lparts[1:]:
826 pos = lparts.index(p)
810 pos = lparts.index(p)
827 base = os.path.join(*parts[:pos])
811 base = os.path.join(*parts[:pos])
828 raise Abort(_('path %r is inside repo %r') % (path, base))
812 raise Abort(_('path %r is inside repo %r') % (path, base))
829 def check(prefix):
813 def check(prefix):
830 curpath = os.path.join(self.root, prefix)
814 curpath = os.path.join(self.root, prefix)
831 try:
815 try:
832 st = os.lstat(curpath)
816 st = os.lstat(curpath)
833 except OSError, err:
817 except OSError, err:
834 # EINVAL can be raised as invalid path syntax under win32.
818 # EINVAL can be raised as invalid path syntax under win32.
835 # They must be ignored for patterns can be checked too.
819 # They must be ignored for patterns can be checked too.
836 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
820 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
837 raise
821 raise
838 else:
822 else:
839 if stat.S_ISLNK(st.st_mode):
823 if stat.S_ISLNK(st.st_mode):
840 raise Abort(_('path %r traverses symbolic link %r') %
824 raise Abort(_('path %r traverses symbolic link %r') %
841 (path, prefix))
825 (path, prefix))
842 elif (stat.S_ISDIR(st.st_mode) and
826 elif (stat.S_ISDIR(st.st_mode) and
843 os.path.isdir(os.path.join(curpath, '.hg'))):
827 os.path.isdir(os.path.join(curpath, '.hg'))):
844 raise Abort(_('path %r is inside repo %r') %
828 raise Abort(_('path %r is inside repo %r') %
845 (path, prefix))
829 (path, prefix))
846 parts.pop()
830 parts.pop()
847 prefixes = []
831 prefixes = []
848 for n in range(len(parts)):
832 for n in range(len(parts)):
849 prefix = os.sep.join(parts)
833 prefix = os.sep.join(parts)
850 if prefix in self.auditeddir:
834 if prefix in self.auditeddir:
851 break
835 break
852 check(prefix)
836 check(prefix)
853 prefixes.append(prefix)
837 prefixes.append(prefix)
854 parts.pop()
838 parts.pop()
855
839
856 self.audited.add(path)
840 self.audited.add(path)
857 # only add prefixes to the cache after checking everything: we don't
841 # only add prefixes to the cache after checking everything: we don't
858 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
842 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
859 self.auditeddir.update(prefixes)
843 self.auditeddir.update(prefixes)
860
844
861 def _makelock_file(info, pathname):
845 if os.name == 'nt':
846 from windows import *
847 else:
848 from posix import *
849
850 def makelock(info, pathname):
851 try:
852 return os.symlink(info, pathname)
853 except OSError, why:
854 if why.errno == errno.EEXIST:
855 raise
856 except AttributeError: # no symlink in os
857 pass
858
862 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
859 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
863 os.write(ld, info)
860 os.write(ld, info)
864 os.close(ld)
861 os.close(ld)
865
862
866 def _readlock_file(pathname):
863 def readlock(pathname):
864 try:
865 return os.readlink(pathname)
866 except OSError, why:
867 if why.errno not in (errno.EINVAL, errno.ENOSYS):
868 raise
869 except AttributeError: # no symlink in os
870 pass
867 return posixfile(pathname).read()
871 return posixfile(pathname).read()
868
872
869 def nlinks(pathname):
873 def nlinks(pathname):
870 """Return number of hardlinks for the given file."""
874 """Return number of hardlinks for the given file."""
871 return os.lstat(pathname).st_nlink
875 return os.lstat(pathname).st_nlink
872
876
873 if hasattr(os, 'link'):
877 if hasattr(os, 'link'):
874 os_link = os.link
878 os_link = os.link
875 else:
879 else:
876 def os_link(src, dst):
880 def os_link(src, dst):
877 raise OSError(0, _("Hardlinks not supported"))
881 raise OSError(0, _("Hardlinks not supported"))
878
882
879 def fstat(fp):
883 def fstat(fp):
880 '''stat file object that may not have fileno method.'''
884 '''stat file object that may not have fileno method.'''
881 try:
885 try:
882 return os.fstat(fp.fileno())
886 return os.fstat(fp.fileno())
883 except AttributeError:
887 except AttributeError:
884 return os.stat(fp.name)
888 return os.stat(fp.name)
885
889
886 posixfile = file
887
888 def openhardlinks():
889 '''return true if it is safe to hold open file handles to hardlinks'''
890 return True
891
892 def _statfiles(files):
893 'Stat each file in files and yield stat or None if file does not exist.'
894 lstat = os.lstat
895 for nf in files:
896 try:
897 st = lstat(nf)
898 except OSError, err:
899 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
900 raise
901 st = None
902 yield st
903
904 def _statfiles_clustered(files):
905 '''Stat each file in files and yield stat or None if file does not exist.
906 Cluster and cache stat per directory to minimize number of OS stat calls.'''
907 ncase = os.path.normcase
908 sep = os.sep
909 dircache = {} # dirname -> filename -> status | None if file does not exist
910 for nf in files:
911 nf = ncase(nf)
912 pos = nf.rfind(sep)
913 if pos == -1:
914 dir, base = '.', nf
915 else:
916 dir, base = nf[:pos+1], nf[pos+1:]
917 cache = dircache.get(dir, None)
918 if cache is None:
919 try:
920 dmap = dict([(ncase(n), s)
921 for n, k, s in osutil.listdir(dir, True)])
922 except OSError, err:
923 # handle directory not found in Python version prior to 2.5
924 # Python <= 2.4 returns native Windows code 3 in errno
925 # Python >= 2.5 returns ENOENT and adds winerror field
926 # EINVAL is raised if dir is not a directory.
927 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
928 errno.ENOTDIR):
929 raise
930 dmap = {}
931 cache = dircache.setdefault(dir, dmap)
932 yield cache.get(base, None)
933
934 if sys.platform == 'win32':
935 statfiles = _statfiles_clustered
936 else:
937 statfiles = _statfiles
938
939 getuser_fallback = None
940
941 def getuser():
942 '''return name of current user'''
943 try:
944 return getpass.getuser()
945 except ImportError:
946 # import of pwd will fail on windows - try fallback
947 if getuser_fallback:
948 return getuser_fallback()
949 # raised if win32api not available
950 raise Abort(_('user name not available - set USERNAME '
951 'environment variable'))
952
953 def username(uid=None):
954 """Return the name of the user with the given uid.
955
956 If uid is None, return the name of the current user."""
957 try:
958 import pwd
959 if uid is None:
960 uid = os.getuid()
961 try:
962 return pwd.getpwuid(uid)[0]
963 except KeyError:
964 return str(uid)
965 except ImportError:
966 return None
967
968 def groupname(gid=None):
969 """Return the name of the group with the given gid.
970
971 If gid is None, return the name of the current group."""
972 try:
973 import grp
974 if gid is None:
975 gid = os.getgid()
976 try:
977 return grp.getgrgid(gid)[0]
978 except KeyError:
979 return str(gid)
980 except ImportError:
981 return None
982
983 # File system features
890 # File system features
984
891
985 def checkcase(path):
892 def checkcase(path):
986 """
893 """
987 Check whether the given path is on a case-sensitive filesystem
894 Check whether the given path is on a case-sensitive filesystem
988
895
989 Requires a path (like /foo/.hg) ending with a foldable final
896 Requires a path (like /foo/.hg) ending with a foldable final
990 directory component.
897 directory component.
991 """
898 """
992 s1 = os.stat(path)
899 s1 = os.stat(path)
993 d, b = os.path.split(path)
900 d, b = os.path.split(path)
994 p2 = os.path.join(d, b.upper())
901 p2 = os.path.join(d, b.upper())
995 if path == p2:
902 if path == p2:
996 p2 = os.path.join(d, b.lower())
903 p2 = os.path.join(d, b.lower())
997 try:
904 try:
998 s2 = os.stat(p2)
905 s2 = os.stat(p2)
999 if s2 == s1:
906 if s2 == s1:
1000 return False
907 return False
1001 return True
908 return True
1002 except:
909 except:
1003 return True
910 return True
1004
911
1005 _fspathcache = {}
912 _fspathcache = {}
1006 def fspath(name, root):
913 def fspath(name, root):
1007 '''Get name in the case stored in the filesystem
914 '''Get name in the case stored in the filesystem
1008
915
1009 The name is either relative to root, or it is an absolute path starting
916 The name is either relative to root, or it is an absolute path starting
1010 with root. Note that this function is unnecessary, and should not be
917 with root. Note that this function is unnecessary, and should not be
1011 called, for case-sensitive filesystems (simply because it's expensive).
918 called, for case-sensitive filesystems (simply because it's expensive).
1012 '''
919 '''
1013 # If name is absolute, make it relative
920 # If name is absolute, make it relative
1014 if name.lower().startswith(root.lower()):
921 if name.lower().startswith(root.lower()):
1015 l = len(root)
922 l = len(root)
1016 if name[l] == os.sep or name[l] == os.altsep:
923 if name[l] == os.sep or name[l] == os.altsep:
1017 l = l + 1
924 l = l + 1
1018 name = name[l:]
925 name = name[l:]
1019
926
1020 if not os.path.exists(os.path.join(root, name)):
927 if not os.path.exists(os.path.join(root, name)):
1021 return None
928 return None
1022
929
1023 seps = os.sep
930 seps = os.sep
1024 if os.altsep:
931 if os.altsep:
1025 seps = seps + os.altsep
932 seps = seps + os.altsep
1026 # Protect backslashes. This gets silly very quickly.
933 # Protect backslashes. This gets silly very quickly.
1027 seps.replace('\\','\\\\')
934 seps.replace('\\','\\\\')
1028 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
935 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1029 dir = os.path.normcase(os.path.normpath(root))
936 dir = os.path.normcase(os.path.normpath(root))
1030 result = []
937 result = []
1031 for part, sep in pattern.findall(name):
938 for part, sep in pattern.findall(name):
1032 if sep:
939 if sep:
1033 result.append(sep)
940 result.append(sep)
1034 continue
941 continue
1035
942
1036 if dir not in _fspathcache:
943 if dir not in _fspathcache:
1037 _fspathcache[dir] = os.listdir(dir)
944 _fspathcache[dir] = os.listdir(dir)
1038 contents = _fspathcache[dir]
945 contents = _fspathcache[dir]
1039
946
1040 lpart = part.lower()
947 lpart = part.lower()
1041 for n in contents:
948 for n in contents:
1042 if n.lower() == lpart:
949 if n.lower() == lpart:
1043 result.append(n)
950 result.append(n)
1044 break
951 break
1045 else:
952 else:
1046 # Cannot happen, as the file exists!
953 # Cannot happen, as the file exists!
1047 result.append(part)
954 result.append(part)
1048 dir = os.path.join(dir, lpart)
955 dir = os.path.join(dir, lpart)
1049
956
1050 return ''.join(result)
957 return ''.join(result)
1051
958
1052 def checkexec(path):
959 def checkexec(path):
1053 """
960 """
1054 Check whether the given path is on a filesystem with UNIX-like exec flags
961 Check whether the given path is on a filesystem with UNIX-like exec flags
1055
962
1056 Requires a directory (like /foo/.hg)
963 Requires a directory (like /foo/.hg)
1057 """
964 """
1058
965
1059 # VFAT on some Linux versions can flip mode but it doesn't persist
966 # VFAT on some Linux versions can flip mode but it doesn't persist
1060 # a FS remount. Frequently we can detect it if files are created
967 # a FS remount. Frequently we can detect it if files are created
1061 # with exec bit on.
968 # with exec bit on.
1062
969
1063 try:
970 try:
1064 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
971 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1065 fh, fn = tempfile.mkstemp("", "", path)
972 fh, fn = tempfile.mkstemp("", "", path)
1066 try:
973 try:
1067 os.close(fh)
974 os.close(fh)
1068 m = os.stat(fn).st_mode & 0777
975 m = os.stat(fn).st_mode & 0777
1069 new_file_has_exec = m & EXECFLAGS
976 new_file_has_exec = m & EXECFLAGS
1070 os.chmod(fn, m ^ EXECFLAGS)
977 os.chmod(fn, m ^ EXECFLAGS)
1071 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
978 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1072 finally:
979 finally:
1073 os.unlink(fn)
980 os.unlink(fn)
1074 except (IOError, OSError):
981 except (IOError, OSError):
1075 # we don't care, the user probably won't be able to commit anyway
982 # we don't care, the user probably won't be able to commit anyway
1076 return False
983 return False
1077 return not (new_file_has_exec or exec_flags_cannot_flip)
984 return not (new_file_has_exec or exec_flags_cannot_flip)
1078
985
1079 def checklink(path):
986 def checklink(path):
1080 """check whether the given path is on a symlink-capable filesystem"""
987 """check whether the given path is on a symlink-capable filesystem"""
1081 # mktemp is not racy because symlink creation will fail if the
988 # mktemp is not racy because symlink creation will fail if the
1082 # file already exists
989 # file already exists
1083 name = tempfile.mktemp(dir=path)
990 name = tempfile.mktemp(dir=path)
1084 try:
991 try:
1085 os.symlink(".", name)
992 os.symlink(".", name)
1086 os.unlink(name)
993 os.unlink(name)
1087 return True
994 return True
1088 except (OSError, AttributeError):
995 except (OSError, AttributeError):
1089 return False
996 return False
1090
997
1091 _umask = os.umask(0)
1092 os.umask(_umask)
1093
1094 def needbinarypatch():
998 def needbinarypatch():
1095 """return True if patches should be applied in binary mode by default."""
999 """return True if patches should be applied in binary mode by default."""
1096 return os.name == 'nt'
1000 return os.name == 'nt'
1097
1001
1098 def endswithsep(path):
1002 def endswithsep(path):
1099 '''Check path ends with os.sep or os.altsep.'''
1003 '''Check path ends with os.sep or os.altsep.'''
1100 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1004 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1101
1005
1102 def splitpath(path):
1006 def splitpath(path):
1103 '''Split path by os.sep.
1007 '''Split path by os.sep.
1104 Note that this function does not use os.altsep because this is
1008 Note that this function does not use os.altsep because this is
1105 an alternative of simple "xxx.split(os.sep)".
1009 an alternative of simple "xxx.split(os.sep)".
1106 It is recommended to use os.path.normpath() before using this
1010 It is recommended to use os.path.normpath() before using this
1107 function if need.'''
1011 function if need.'''
1108 return path.split(os.sep)
1012 return path.split(os.sep)
1109
1013
1110 def gui():
1014 def gui():
1111 '''Are we running in a GUI?'''
1015 '''Are we running in a GUI?'''
1112 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1016 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1113
1017
1114 def lookup_reg(key, name=None, scope=None):
1018 def lookup_reg(key, name=None, scope=None):
1115 return None
1019 return None
1116
1020
1117 # Platform specific variants
1118 if os.name == 'nt':
1119 import msvcrt
1120 nulldev = 'NUL:'
1121
1122 class winstdout:
1123 '''stdout on windows misbehaves if sent through a pipe'''
1124
1125 def __init__(self, fp):
1126 self.fp = fp
1127
1128 def __getattr__(self, key):
1129 return getattr(self.fp, key)
1130
1131 def close(self):
1132 try:
1133 self.fp.close()
1134 except: pass
1135
1136 def write(self, s):
1137 try:
1138 # This is workaround for "Not enough space" error on
1139 # writing large size of data to console.
1140 limit = 16000
1141 l = len(s)
1142 start = 0
1143 while start < l:
1144 end = start + limit
1145 self.fp.write(s[start:end])
1146 start = end
1147 except IOError, inst:
1148 if inst.errno != 0: raise
1149 self.close()
1150 raise IOError(errno.EPIPE, 'Broken pipe')
1151
1152 def flush(self):
1153 try:
1154 return self.fp.flush()
1155 except IOError, inst:
1156 if inst.errno != errno.EINVAL: raise
1157 self.close()
1158 raise IOError(errno.EPIPE, 'Broken pipe')
1159
1160 sys.stdout = winstdout(sys.stdout)
1161
1162 def _is_win_9x():
1163 '''return true if run on windows 95, 98 or me.'''
1164 try:
1165 return sys.getwindowsversion()[3] == 1
1166 except AttributeError:
1167 return 'command' in os.environ.get('comspec', '')
1168
1169 def openhardlinks():
1170 return not _is_win_9x and "win32api" in locals()
1171
1172 def system_rcpath():
1173 try:
1174 return system_rcpath_win32()
1175 except:
1176 return [r'c:\mercurial\mercurial.ini']
1177
1178 def user_rcpath():
1179 '''return os-specific hgrc search path to the user dir'''
1180 try:
1181 path = user_rcpath_win32()
1182 except:
1183 home = os.path.expanduser('~')
1184 path = [os.path.join(home, 'mercurial.ini'),
1185 os.path.join(home, '.hgrc')]
1186 userprofile = os.environ.get('USERPROFILE')
1187 if userprofile:
1188 path.append(os.path.join(userprofile, 'mercurial.ini'))
1189 path.append(os.path.join(userprofile, '.hgrc'))
1190 return path
1191
1192 def parse_patch_output(output_line):
1193 """parses the output produced by patch and returns the file name"""
1194 pf = output_line[14:]
1195 if pf[0] == '`':
1196 pf = pf[1:-1] # Remove the quotes
1197 return pf
1198
1199 def sshargs(sshcmd, host, user, port):
1200 '''Build argument list for ssh or Plink'''
1201 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1202 args = user and ("%s@%s" % (user, host)) or host
1203 return port and ("%s %s %s" % (args, pflag, port)) or args
1204
1205 def testpid(pid):
1206 '''return False if pid dead, True if running or not known'''
1207 return True
1208
1209 def set_flags(f, l, x):
1210 pass
1211
1212 def set_binary(fd):
1213 # When run without console, pipes may expose invalid
1214 # fileno(), usually set to -1.
1215 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1216 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1217
1218 def pconvert(path):
1219 return '/'.join(splitpath(path))
1220
1221 def localpath(path):
1222 return path.replace('/', '\\')
1223
1224 def normpath(path):
1225 return pconvert(os.path.normpath(path))
1226
1227 makelock = _makelock_file
1228 readlock = _readlock_file
1229
1230 def samestat(s1, s2):
1231 return False
1232
1233 # A sequence of backslashes is special iff it precedes a double quote:
1234 # - if there's an even number of backslashes, the double quote is not
1235 # quoted (i.e. it ends the quoted region)
1236 # - if there's an odd number of backslashes, the double quote is quoted
1237 # - in both cases, every pair of backslashes is unquoted into a single
1238 # backslash
1239 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1240 # So, to quote a string, we must surround it in double quotes, double
1241 # the number of backslashes that preceed double quotes and add another
1242 # backslash before every double quote (being careful with the double
1243 # quote we've appended to the end)
1244 _quotere = None
1245 def shellquote(s):
1246 global _quotere
1247 if _quotere is None:
1248 _quotere = re.compile(r'(\\*)("|\\$)')
1249 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1250
1251 def quotecommand(cmd):
1252 """Build a command string suitable for os.popen* calls."""
1253 # The extra quotes are needed because popen* runs the command
1254 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1255 return '"' + cmd + '"'
1256
1257 def popen(command, mode='r'):
1258 # Work around "popen spawned process may not write to stdout
1259 # under windows"
1260 # http://bugs.python.org/issue1366
1261 command += " 2> %s" % nulldev
1262 return os.popen(quotecommand(command), mode)
1263
1264 def explain_exit(code):
1265 return _("exited with status %d") % code, code
1266
1267 # if you change this stub into a real check, please try to implement the
1268 # username and groupname functions above, too.
1269 def isowner(fp, st=None):
1270 return True
1271
1272 def find_exe(command):
1273 '''Find executable for command searching like cmd.exe does.
1274 If command is a basename then PATH is searched for command.
1275 PATH isn't searched if command is an absolute or relative path.
1276 An extension from PATHEXT is found and added if not present.
1277 If command isn't found None is returned.'''
1278 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1279 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
1280 if os.path.splitext(command)[1].lower() in pathexts:
1281 pathexts = ['']
1282
1283 def findexisting(pathcommand):
1284 'Will append extension (if needed) and return existing file'
1285 for ext in pathexts:
1286 executable = pathcommand + ext
1287 if os.path.exists(executable):
1288 return executable
1289 return None
1290
1291 if os.sep in command:
1292 return findexisting(command)
1293
1294 for path in os.environ.get('PATH', '').split(os.pathsep):
1295 executable = findexisting(os.path.join(path, command))
1296 if executable is not None:
1297 return executable
1298 return None
1299
1300 def set_signal_handler():
1301 try:
1302 set_signal_handler_win32()
1303 except NameError:
1304 pass
1305
1306 try:
1307 # override functions with win32 versions if possible
1308 from util_win32 import *
1309 if not _is_win_9x():
1310 posixfile = posixfile_nt
1311 except ImportError:
1312 pass
1313
1314 else:
1315 nulldev = '/dev/null'
1316
1317 def rcfiles(path):
1318 rcs = [os.path.join(path, 'hgrc')]
1319 rcdir = os.path.join(path, 'hgrc.d')
1320 try:
1321 rcs.extend([os.path.join(rcdir, f)
1322 for f, kind in osutil.listdir(rcdir)
1323 if f.endswith(".rc")])
1324 except OSError:
1325 pass
1326 return rcs
1327
1328 def system_rcpath():
1329 path = []
1330 # old mod_python does not set sys.argv
1331 if len(getattr(sys, 'argv', [])) > 0:
1332 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1333 '/../etc/mercurial'))
1334 path.extend(rcfiles('/etc/mercurial'))
1335 return path
1336
1337 def user_rcpath():
1338 return [os.path.expanduser('~/.hgrc')]
1339
1340 def parse_patch_output(output_line):
1341 """parses the output produced by patch and returns the file name"""
1342 pf = output_line[14:]
1343 if os.sys.platform == 'OpenVMS':
1344 if pf[0] == '`':
1345 pf = pf[1:-1] # Remove the quotes
1346 else:
1347 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1348 pf = pf[1:-1] # Remove the quotes
1349 return pf
1350
1351 def sshargs(sshcmd, host, user, port):
1352 '''Build argument list for ssh'''
1353 args = user and ("%s@%s" % (user, host)) or host
1354 return port and ("%s -p %s" % (args, port)) or args
1355
1356 def is_exec(f):
1357 """check whether a file is executable"""
1358 return (os.lstat(f).st_mode & 0100 != 0)
1359
1360 def set_flags(f, l, x):
1361 s = os.lstat(f).st_mode
1362 if l:
1363 if not stat.S_ISLNK(s):
1364 # switch file to link
1365 data = file(f).read()
1366 os.unlink(f)
1367 try:
1368 os.symlink(data, f)
1369 except:
1370 # failed to make a link, rewrite file
1371 file(f, "w").write(data)
1372 # no chmod needed at this point
1373 return
1374 if stat.S_ISLNK(s):
1375 # switch link to file
1376 data = os.readlink(f)
1377 os.unlink(f)
1378 file(f, "w").write(data)
1379 s = 0666 & ~_umask # avoid restatting for chmod
1380
1381 sx = s & 0100
1382 if x and not sx:
1383 # Turn on +x for every +r bit when making a file executable
1384 # and obey umask.
1385 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1386 elif not x and sx:
1387 # Turn off all +x bits
1388 os.chmod(f, s & 0666)
1389
1390 def set_binary(fd):
1391 pass
1392
1393 def pconvert(path):
1394 return path
1395
1396 def localpath(path):
1397 return path
1398
1399 normpath = os.path.normpath
1400 samestat = os.path.samestat
1401
1402 def makelock(info, pathname):
1403 try:
1404 os.symlink(info, pathname)
1405 except OSError, why:
1406 if why.errno == errno.EEXIST:
1407 raise
1408 else:
1409 _makelock_file(info, pathname)
1410
1411 def readlock(pathname):
1412 try:
1413 return os.readlink(pathname)
1414 except OSError, why:
1415 if why.errno in (errno.EINVAL, errno.ENOSYS):
1416 return _readlock_file(pathname)
1417 else:
1418 raise
1419
1420 def shellquote(s):
1421 if os.sys.platform == 'OpenVMS':
1422 return '"%s"' % s
1423 else:
1424 return "'%s'" % s.replace("'", "'\\''")
1425
1426 def quotecommand(cmd):
1427 return cmd
1428
1429 def popen(command, mode='r'):
1430 return os.popen(command, mode)
1431
1432 def testpid(pid):
1433 '''return False if pid dead, True if running or not sure'''
1434 if os.sys.platform == 'OpenVMS':
1435 return True
1436 try:
1437 os.kill(pid, 0)
1438 return True
1439 except OSError, inst:
1440 return inst.errno != errno.ESRCH
1441
1442 def explain_exit(code):
1443 """return a 2-tuple (desc, code) describing a process's status"""
1444 if os.WIFEXITED(code):
1445 val = os.WEXITSTATUS(code)
1446 return _("exited with status %d") % val, val
1447 elif os.WIFSIGNALED(code):
1448 val = os.WTERMSIG(code)
1449 return _("killed by signal %d") % val, val
1450 elif os.WIFSTOPPED(code):
1451 val = os.WSTOPSIG(code)
1452 return _("stopped by signal %d") % val, val
1453 raise ValueError(_("invalid exit code"))
1454
1455 def isowner(fp, st=None):
1456 """Return True if the file object f belongs to the current user.
1457
1458 The return value of a util.fstat(f) may be passed as the st argument.
1459 """
1460 if st is None:
1461 st = fstat(fp)
1462 return st.st_uid == os.getuid()
1463
1464 def find_exe(command):
1465 '''Find executable for command searching like which does.
1466 If command is a basename then PATH is searched for command.
1467 PATH isn't searched if command is an absolute or relative path.
1468 If command isn't found None is returned.'''
1469 if sys.platform == 'OpenVMS':
1470 return command
1471
1472 def findexisting(executable):
1473 'Will return executable if existing file'
1474 if os.path.exists(executable):
1475 return executable
1476 return None
1477
1478 if os.sep in command:
1479 return findexisting(command)
1480
1481 for path in os.environ.get('PATH', '').split(os.pathsep):
1482 executable = findexisting(os.path.join(path, command))
1483 if executable is not None:
1484 return executable
1485 return None
1486
1487 def set_signal_handler():
1488 pass
1489
1490 def mktempcopy(name, emptyok=False, createmode=None):
1021 def mktempcopy(name, emptyok=False, createmode=None):
1491 """Create a temporary file with the same contents from name
1022 """Create a temporary file with the same contents from name
1492
1023
1493 The permission bits are copied from the original file.
1024 The permission bits are copied from the original file.
1494
1025
1495 If the temporary file is going to be truncated immediately, you
1026 If the temporary file is going to be truncated immediately, you
1496 can use emptyok=True as an optimization.
1027 can use emptyok=True as an optimization.
1497
1028
1498 Returns the name of the temporary file.
1029 Returns the name of the temporary file.
1499 """
1030 """
1500 d, fn = os.path.split(name)
1031 d, fn = os.path.split(name)
1501 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1032 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1502 os.close(fd)
1033 os.close(fd)
1503 # Temporary files are created with mode 0600, which is usually not
1034 # Temporary files are created with mode 0600, which is usually not
1504 # what we want. If the original file already exists, just copy
1035 # what we want. If the original file already exists, just copy
1505 # its mode. Otherwise, manually obey umask.
1036 # its mode. Otherwise, manually obey umask.
1506 try:
1037 try:
1507 st_mode = os.lstat(name).st_mode & 0777
1038 st_mode = os.lstat(name).st_mode & 0777
1508 except OSError, inst:
1039 except OSError, inst:
1509 if inst.errno != errno.ENOENT:
1040 if inst.errno != errno.ENOENT:
1510 raise
1041 raise
1511 st_mode = createmode
1042 st_mode = createmode
1512 if st_mode is None:
1043 if st_mode is None:
1513 st_mode = ~_umask
1044 st_mode = ~umask
1514 st_mode &= 0666
1045 st_mode &= 0666
1515 os.chmod(temp, st_mode)
1046 os.chmod(temp, st_mode)
1516 if emptyok:
1047 if emptyok:
1517 return temp
1048 return temp
1518 try:
1049 try:
1519 try:
1050 try:
1520 ifp = posixfile(name, "rb")
1051 ifp = posixfile(name, "rb")
1521 except IOError, inst:
1052 except IOError, inst:
1522 if inst.errno == errno.ENOENT:
1053 if inst.errno == errno.ENOENT:
1523 return temp
1054 return temp
1524 if not getattr(inst, 'filename', None):
1055 if not getattr(inst, 'filename', None):
1525 inst.filename = name
1056 inst.filename = name
1526 raise
1057 raise
1527 ofp = posixfile(temp, "wb")
1058 ofp = posixfile(temp, "wb")
1528 for chunk in filechunkiter(ifp):
1059 for chunk in filechunkiter(ifp):
1529 ofp.write(chunk)
1060 ofp.write(chunk)
1530 ifp.close()
1061 ifp.close()
1531 ofp.close()
1062 ofp.close()
1532 except:
1063 except:
1533 try: os.unlink(temp)
1064 try: os.unlink(temp)
1534 except: pass
1065 except: pass
1535 raise
1066 raise
1536 return temp
1067 return temp
1537
1068
1538 class atomictempfile(posixfile):
1069 class atomictempfile(posixfile):
1539 """file-like object that atomically updates a file
1070 """file-like object that atomically updates a file
1540
1071
1541 All writes will be redirected to a temporary copy of the original
1072 All writes will be redirected to a temporary copy of the original
1542 file. When rename is called, the copy is renamed to the original
1073 file. When rename is called, the copy is renamed to the original
1543 name, making the changes visible.
1074 name, making the changes visible.
1544 """
1075 """
1545 def __init__(self, name, mode, createmode):
1076 def __init__(self, name, mode, createmode):
1546 self.__name = name
1077 self.__name = name
1547 self.temp = mktempcopy(name, emptyok=('w' in mode),
1078 self.temp = mktempcopy(name, emptyok=('w' in mode),
1548 createmode=createmode)
1079 createmode=createmode)
1549 posixfile.__init__(self, self.temp, mode)
1080 posixfile.__init__(self, self.temp, mode)
1550
1081
1551 def rename(self):
1082 def rename(self):
1552 if not self.closed:
1083 if not self.closed:
1553 posixfile.close(self)
1084 posixfile.close(self)
1554 rename(self.temp, localpath(self.__name))
1085 rename(self.temp, localpath(self.__name))
1555
1086
1556 def __del__(self):
1087 def __del__(self):
1557 if not self.closed:
1088 if not self.closed:
1558 try:
1089 try:
1559 os.unlink(self.temp)
1090 os.unlink(self.temp)
1560 except: pass
1091 except: pass
1561 posixfile.close(self)
1092 posixfile.close(self)
1562
1093
1563 def makedirs(name, mode=None):
1094 def makedirs(name, mode=None):
1564 """recursive directory creation with parent mode inheritance"""
1095 """recursive directory creation with parent mode inheritance"""
1565 try:
1096 try:
1566 os.mkdir(name)
1097 os.mkdir(name)
1567 if mode is not None:
1098 if mode is not None:
1568 os.chmod(name, mode)
1099 os.chmod(name, mode)
1569 return
1100 return
1570 except OSError, err:
1101 except OSError, err:
1571 if err.errno == errno.EEXIST:
1102 if err.errno == errno.EEXIST:
1572 return
1103 return
1573 if err.errno != errno.ENOENT:
1104 if err.errno != errno.ENOENT:
1574 raise
1105 raise
1575 parent = os.path.abspath(os.path.dirname(name))
1106 parent = os.path.abspath(os.path.dirname(name))
1576 makedirs(parent, mode)
1107 makedirs(parent, mode)
1577 makedirs(name, mode)
1108 makedirs(name, mode)
1578
1109
1579 class opener(object):
1110 class opener(object):
1580 """Open files relative to a base directory
1111 """Open files relative to a base directory
1581
1112
1582 This class is used to hide the details of COW semantics and
1113 This class is used to hide the details of COW semantics and
1583 remote file access from higher level code.
1114 remote file access from higher level code.
1584 """
1115 """
1585 def __init__(self, base, audit=True):
1116 def __init__(self, base, audit=True):
1586 self.base = base
1117 self.base = base
1587 if audit:
1118 if audit:
1588 self.audit_path = path_auditor(base)
1119 self.audit_path = path_auditor(base)
1589 else:
1120 else:
1590 self.audit_path = always
1121 self.audit_path = always
1591 self.createmode = None
1122 self.createmode = None
1592
1123
1593 def __getattr__(self, name):
1124 def __getattr__(self, name):
1594 if name == '_can_symlink':
1125 if name == '_can_symlink':
1595 self._can_symlink = checklink(self.base)
1126 self._can_symlink = checklink(self.base)
1596 return self._can_symlink
1127 return self._can_symlink
1597 raise AttributeError(name)
1128 raise AttributeError(name)
1598
1129
1599 def _fixfilemode(self, name):
1130 def _fixfilemode(self, name):
1600 if self.createmode is None:
1131 if self.createmode is None:
1601 return
1132 return
1602 os.chmod(name, self.createmode & 0666)
1133 os.chmod(name, self.createmode & 0666)
1603
1134
1604 def __call__(self, path, mode="r", text=False, atomictemp=False):
1135 def __call__(self, path, mode="r", text=False, atomictemp=False):
1605 self.audit_path(path)
1136 self.audit_path(path)
1606 f = os.path.join(self.base, path)
1137 f = os.path.join(self.base, path)
1607
1138
1608 if not text and "b" not in mode:
1139 if not text and "b" not in mode:
1609 mode += "b" # for that other OS
1140 mode += "b" # for that other OS
1610
1141
1611 nlink = -1
1142 nlink = -1
1612 if mode not in ("r", "rb"):
1143 if mode not in ("r", "rb"):
1613 try:
1144 try:
1614 nlink = nlinks(f)
1145 nlink = nlinks(f)
1615 except OSError:
1146 except OSError:
1616 nlink = 0
1147 nlink = 0
1617 d = os.path.dirname(f)
1148 d = os.path.dirname(f)
1618 if not os.path.isdir(d):
1149 if not os.path.isdir(d):
1619 makedirs(d, self.createmode)
1150 makedirs(d, self.createmode)
1620 if atomictemp:
1151 if atomictemp:
1621 return atomictempfile(f, mode, self.createmode)
1152 return atomictempfile(f, mode, self.createmode)
1622 if nlink > 1:
1153 if nlink > 1:
1623 rename(mktempcopy(f), f)
1154 rename(mktempcopy(f), f)
1624 fp = posixfile(f, mode)
1155 fp = posixfile(f, mode)
1625 if nlink == 0:
1156 if nlink == 0:
1626 self._fixfilemode(f)
1157 self._fixfilemode(f)
1627 return fp
1158 return fp
1628
1159
1629 def symlink(self, src, dst):
1160 def symlink(self, src, dst):
1630 self.audit_path(dst)
1161 self.audit_path(dst)
1631 linkname = os.path.join(self.base, dst)
1162 linkname = os.path.join(self.base, dst)
1632 try:
1163 try:
1633 os.unlink(linkname)
1164 os.unlink(linkname)
1634 except OSError:
1165 except OSError:
1635 pass
1166 pass
1636
1167
1637 dirname = os.path.dirname(linkname)
1168 dirname = os.path.dirname(linkname)
1638 if not os.path.exists(dirname):
1169 if not os.path.exists(dirname):
1639 makedirs(dirname, self.createmode)
1170 makedirs(dirname, self.createmode)
1640
1171
1641 if self._can_symlink:
1172 if self._can_symlink:
1642 try:
1173 try:
1643 os.symlink(src, linkname)
1174 os.symlink(src, linkname)
1644 except OSError, err:
1175 except OSError, err:
1645 raise OSError(err.errno, _('could not symlink to %r: %s') %
1176 raise OSError(err.errno, _('could not symlink to %r: %s') %
1646 (src, err.strerror), linkname)
1177 (src, err.strerror), linkname)
1647 else:
1178 else:
1648 f = self(dst, "w")
1179 f = self(dst, "w")
1649 f.write(src)
1180 f.write(src)
1650 f.close()
1181 f.close()
1651 self._fixfilemode(dst)
1182 self._fixfilemode(dst)
1652
1183
1653 class chunkbuffer(object):
1184 class chunkbuffer(object):
1654 """Allow arbitrary sized chunks of data to be efficiently read from an
1185 """Allow arbitrary sized chunks of data to be efficiently read from an
1655 iterator over chunks of arbitrary size."""
1186 iterator over chunks of arbitrary size."""
1656
1187
1657 def __init__(self, in_iter):
1188 def __init__(self, in_iter):
1658 """in_iter is the iterator that's iterating over the input chunks.
1189 """in_iter is the iterator that's iterating over the input chunks.
1659 targetsize is how big a buffer to try to maintain."""
1190 targetsize is how big a buffer to try to maintain."""
1660 self.iter = iter(in_iter)
1191 self.iter = iter(in_iter)
1661 self.buf = ''
1192 self.buf = ''
1662 self.targetsize = 2**16
1193 self.targetsize = 2**16
1663
1194
1664 def read(self, l):
1195 def read(self, l):
1665 """Read L bytes of data from the iterator of chunks of data.
1196 """Read L bytes of data from the iterator of chunks of data.
1666 Returns less than L bytes if the iterator runs dry."""
1197 Returns less than L bytes if the iterator runs dry."""
1667 if l > len(self.buf) and self.iter:
1198 if l > len(self.buf) and self.iter:
1668 # Clamp to a multiple of self.targetsize
1199 # Clamp to a multiple of self.targetsize
1669 targetsize = max(l, self.targetsize)
1200 targetsize = max(l, self.targetsize)
1670 collector = cStringIO.StringIO()
1201 collector = cStringIO.StringIO()
1671 collector.write(self.buf)
1202 collector.write(self.buf)
1672 collected = len(self.buf)
1203 collected = len(self.buf)
1673 for chunk in self.iter:
1204 for chunk in self.iter:
1674 collector.write(chunk)
1205 collector.write(chunk)
1675 collected += len(chunk)
1206 collected += len(chunk)
1676 if collected >= targetsize:
1207 if collected >= targetsize:
1677 break
1208 break
1678 if collected < targetsize:
1209 if collected < targetsize:
1679 self.iter = False
1210 self.iter = False
1680 self.buf = collector.getvalue()
1211 self.buf = collector.getvalue()
1681 if len(self.buf) == l:
1212 if len(self.buf) == l:
1682 s, self.buf = str(self.buf), ''
1213 s, self.buf = str(self.buf), ''
1683 else:
1214 else:
1684 s, self.buf = self.buf[:l], buffer(self.buf, l)
1215 s, self.buf = self.buf[:l], buffer(self.buf, l)
1685 return s
1216 return s
1686
1217
1687 def filechunkiter(f, size=65536, limit=None):
1218 def filechunkiter(f, size=65536, limit=None):
1688 """Create a generator that produces the data in the file size
1219 """Create a generator that produces the data in the file size
1689 (default 65536) bytes at a time, up to optional limit (default is
1220 (default 65536) bytes at a time, up to optional limit (default is
1690 to read all data). Chunks may be less than size bytes if the
1221 to read all data). Chunks may be less than size bytes if the
1691 chunk is the last chunk in the file, or the file is a socket or
1222 chunk is the last chunk in the file, or the file is a socket or
1692 some other type of file that sometimes reads less data than is
1223 some other type of file that sometimes reads less data than is
1693 requested."""
1224 requested."""
1694 assert size >= 0
1225 assert size >= 0
1695 assert limit is None or limit >= 0
1226 assert limit is None or limit >= 0
1696 while True:
1227 while True:
1697 if limit is None: nbytes = size
1228 if limit is None: nbytes = size
1698 else: nbytes = min(limit, size)
1229 else: nbytes = min(limit, size)
1699 s = nbytes and f.read(nbytes)
1230 s = nbytes and f.read(nbytes)
1700 if not s: break
1231 if not s: break
1701 if limit: limit -= len(s)
1232 if limit: limit -= len(s)
1702 yield s
1233 yield s
1703
1234
1704 def makedate():
1235 def makedate():
1705 lt = time.localtime()
1236 lt = time.localtime()
1706 if lt[8] == 1 and time.daylight:
1237 if lt[8] == 1 and time.daylight:
1707 tz = time.altzone
1238 tz = time.altzone
1708 else:
1239 else:
1709 tz = time.timezone
1240 tz = time.timezone
1710 return time.mktime(lt), tz
1241 return time.mktime(lt), tz
1711
1242
1712 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1243 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1713 """represent a (unixtime, offset) tuple as a localized time.
1244 """represent a (unixtime, offset) tuple as a localized time.
1714 unixtime is seconds since the epoch, and offset is the time zone's
1245 unixtime is seconds since the epoch, and offset is the time zone's
1715 number of seconds away from UTC. if timezone is false, do not
1246 number of seconds away from UTC. if timezone is false, do not
1716 append time zone to string."""
1247 append time zone to string."""
1717 t, tz = date or makedate()
1248 t, tz = date or makedate()
1718 if "%1" in format or "%2" in format:
1249 if "%1" in format or "%2" in format:
1719 sign = (tz > 0) and "-" or "+"
1250 sign = (tz > 0) and "-" or "+"
1720 minutes = abs(tz) / 60
1251 minutes = abs(tz) / 60
1721 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1252 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1722 format = format.replace("%2", "%02d" % (minutes % 60))
1253 format = format.replace("%2", "%02d" % (minutes % 60))
1723 s = time.strftime(format, time.gmtime(float(t) - tz))
1254 s = time.strftime(format, time.gmtime(float(t) - tz))
1724 return s
1255 return s
1725
1256
1726 def shortdate(date=None):
1257 def shortdate(date=None):
1727 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1258 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1728 return datestr(date, format='%Y-%m-%d')
1259 return datestr(date, format='%Y-%m-%d')
1729
1260
1730 def strdate(string, format, defaults=[]):
1261 def strdate(string, format, defaults=[]):
1731 """parse a localized time string and return a (unixtime, offset) tuple.
1262 """parse a localized time string and return a (unixtime, offset) tuple.
1732 if the string cannot be parsed, ValueError is raised."""
1263 if the string cannot be parsed, ValueError is raised."""
1733 def timezone(string):
1264 def timezone(string):
1734 tz = string.split()[-1]
1265 tz = string.split()[-1]
1735 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1266 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1736 sign = (tz[0] == "+") and 1 or -1
1267 sign = (tz[0] == "+") and 1 or -1
1737 hours = int(tz[1:3])
1268 hours = int(tz[1:3])
1738 minutes = int(tz[3:5])
1269 minutes = int(tz[3:5])
1739 return -sign * (hours * 60 + minutes) * 60
1270 return -sign * (hours * 60 + minutes) * 60
1740 if tz == "GMT" or tz == "UTC":
1271 if tz == "GMT" or tz == "UTC":
1741 return 0
1272 return 0
1742 return None
1273 return None
1743
1274
1744 # NOTE: unixtime = localunixtime + offset
1275 # NOTE: unixtime = localunixtime + offset
1745 offset, date = timezone(string), string
1276 offset, date = timezone(string), string
1746 if offset != None:
1277 if offset != None:
1747 date = " ".join(string.split()[:-1])
1278 date = " ".join(string.split()[:-1])
1748
1279
1749 # add missing elements from defaults
1280 # add missing elements from defaults
1750 for part in defaults:
1281 for part in defaults:
1751 found = [True for p in part if ("%"+p) in format]
1282 found = [True for p in part if ("%"+p) in format]
1752 if not found:
1283 if not found:
1753 date += "@" + defaults[part]
1284 date += "@" + defaults[part]
1754 format += "@%" + part[0]
1285 format += "@%" + part[0]
1755
1286
1756 timetuple = time.strptime(date, format)
1287 timetuple = time.strptime(date, format)
1757 localunixtime = int(calendar.timegm(timetuple))
1288 localunixtime = int(calendar.timegm(timetuple))
1758 if offset is None:
1289 if offset is None:
1759 # local timezone
1290 # local timezone
1760 unixtime = int(time.mktime(timetuple))
1291 unixtime = int(time.mktime(timetuple))
1761 offset = unixtime - localunixtime
1292 offset = unixtime - localunixtime
1762 else:
1293 else:
1763 unixtime = localunixtime + offset
1294 unixtime = localunixtime + offset
1764 return unixtime, offset
1295 return unixtime, offset
1765
1296
1766 def parsedate(date, formats=None, defaults=None):
1297 def parsedate(date, formats=None, defaults=None):
1767 """parse a localized date/time string and return a (unixtime, offset) tuple.
1298 """parse a localized date/time string and return a (unixtime, offset) tuple.
1768
1299
1769 The date may be a "unixtime offset" string or in one of the specified
1300 The date may be a "unixtime offset" string or in one of the specified
1770 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1301 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1771 """
1302 """
1772 if not date:
1303 if not date:
1773 return 0, 0
1304 return 0, 0
1774 if isinstance(date, tuple) and len(date) == 2:
1305 if isinstance(date, tuple) and len(date) == 2:
1775 return date
1306 return date
1776 if not formats:
1307 if not formats:
1777 formats = defaultdateformats
1308 formats = defaultdateformats
1778 date = date.strip()
1309 date = date.strip()
1779 try:
1310 try:
1780 when, offset = map(int, date.split(' '))
1311 when, offset = map(int, date.split(' '))
1781 except ValueError:
1312 except ValueError:
1782 # fill out defaults
1313 # fill out defaults
1783 if not defaults:
1314 if not defaults:
1784 defaults = {}
1315 defaults = {}
1785 now = makedate()
1316 now = makedate()
1786 for part in "d mb yY HI M S".split():
1317 for part in "d mb yY HI M S".split():
1787 if part not in defaults:
1318 if part not in defaults:
1788 if part[0] in "HMS":
1319 if part[0] in "HMS":
1789 defaults[part] = "00"
1320 defaults[part] = "00"
1790 else:
1321 else:
1791 defaults[part] = datestr(now, "%" + part[0])
1322 defaults[part] = datestr(now, "%" + part[0])
1792
1323
1793 for format in formats:
1324 for format in formats:
1794 try:
1325 try:
1795 when, offset = strdate(date, format, defaults)
1326 when, offset = strdate(date, format, defaults)
1796 except (ValueError, OverflowError):
1327 except (ValueError, OverflowError):
1797 pass
1328 pass
1798 else:
1329 else:
1799 break
1330 break
1800 else:
1331 else:
1801 raise Abort(_('invalid date: %r ') % date)
1332 raise Abort(_('invalid date: %r ') % date)
1802 # validate explicit (probably user-specified) date and
1333 # validate explicit (probably user-specified) date and
1803 # time zone offset. values must fit in signed 32 bits for
1334 # time zone offset. values must fit in signed 32 bits for
1804 # current 32-bit linux runtimes. timezones go from UTC-12
1335 # current 32-bit linux runtimes. timezones go from UTC-12
1805 # to UTC+14
1336 # to UTC+14
1806 if abs(when) > 0x7fffffff:
1337 if abs(when) > 0x7fffffff:
1807 raise Abort(_('date exceeds 32 bits: %d') % when)
1338 raise Abort(_('date exceeds 32 bits: %d') % when)
1808 if offset < -50400 or offset > 43200:
1339 if offset < -50400 or offset > 43200:
1809 raise Abort(_('impossible time zone offset: %d') % offset)
1340 raise Abort(_('impossible time zone offset: %d') % offset)
1810 return when, offset
1341 return when, offset
1811
1342
1812 def matchdate(date):
1343 def matchdate(date):
1813 """Return a function that matches a given date match specifier
1344 """Return a function that matches a given date match specifier
1814
1345
1815 Formats include:
1346 Formats include:
1816
1347
1817 '{date}' match a given date to the accuracy provided
1348 '{date}' match a given date to the accuracy provided
1818
1349
1819 '<{date}' on or before a given date
1350 '<{date}' on or before a given date
1820
1351
1821 '>{date}' on or after a given date
1352 '>{date}' on or after a given date
1822
1353
1823 """
1354 """
1824
1355
1825 def lower(date):
1356 def lower(date):
1826 d = dict(mb="1", d="1")
1357 d = dict(mb="1", d="1")
1827 return parsedate(date, extendeddateformats, d)[0]
1358 return parsedate(date, extendeddateformats, d)[0]
1828
1359
1829 def upper(date):
1360 def upper(date):
1830 d = dict(mb="12", HI="23", M="59", S="59")
1361 d = dict(mb="12", HI="23", M="59", S="59")
1831 for days in "31 30 29".split():
1362 for days in "31 30 29".split():
1832 try:
1363 try:
1833 d["d"] = days
1364 d["d"] = days
1834 return parsedate(date, extendeddateformats, d)[0]
1365 return parsedate(date, extendeddateformats, d)[0]
1835 except:
1366 except:
1836 pass
1367 pass
1837 d["d"] = "28"
1368 d["d"] = "28"
1838 return parsedate(date, extendeddateformats, d)[0]
1369 return parsedate(date, extendeddateformats, d)[0]
1839
1370
1840 if date[0] == "<":
1371 if date[0] == "<":
1841 when = upper(date[1:])
1372 when = upper(date[1:])
1842 return lambda x: x <= when
1373 return lambda x: x <= when
1843 elif date[0] == ">":
1374 elif date[0] == ">":
1844 when = lower(date[1:])
1375 when = lower(date[1:])
1845 return lambda x: x >= when
1376 return lambda x: x >= when
1846 elif date[0] == "-":
1377 elif date[0] == "-":
1847 try:
1378 try:
1848 days = int(date[1:])
1379 days = int(date[1:])
1849 except ValueError:
1380 except ValueError:
1850 raise Abort(_("invalid day spec: %s") % date[1:])
1381 raise Abort(_("invalid day spec: %s") % date[1:])
1851 when = makedate()[0] - days * 3600 * 24
1382 when = makedate()[0] - days * 3600 * 24
1852 return lambda x: x >= when
1383 return lambda x: x >= when
1853 elif " to " in date:
1384 elif " to " in date:
1854 a, b = date.split(" to ")
1385 a, b = date.split(" to ")
1855 start, stop = lower(a), upper(b)
1386 start, stop = lower(a), upper(b)
1856 return lambda x: x >= start and x <= stop
1387 return lambda x: x >= start and x <= stop
1857 else:
1388 else:
1858 start, stop = lower(date), upper(date)
1389 start, stop = lower(date), upper(date)
1859 return lambda x: x >= start and x <= stop
1390 return lambda x: x >= start and x <= stop
1860
1391
1861 def shortuser(user):
1392 def shortuser(user):
1862 """Return a short representation of a user name or email address."""
1393 """Return a short representation of a user name or email address."""
1863 f = user.find('@')
1394 f = user.find('@')
1864 if f >= 0:
1395 if f >= 0:
1865 user = user[:f]
1396 user = user[:f]
1866 f = user.find('<')
1397 f = user.find('<')
1867 if f >= 0:
1398 if f >= 0:
1868 user = user[f+1:]
1399 user = user[f+1:]
1869 f = user.find(' ')
1400 f = user.find(' ')
1870 if f >= 0:
1401 if f >= 0:
1871 user = user[:f]
1402 user = user[:f]
1872 f = user.find('.')
1403 f = user.find('.')
1873 if f >= 0:
1404 if f >= 0:
1874 user = user[:f]
1405 user = user[:f]
1875 return user
1406 return user
1876
1407
1877 def email(author):
1408 def email(author):
1878 '''get email of author.'''
1409 '''get email of author.'''
1879 r = author.find('>')
1410 r = author.find('>')
1880 if r == -1: r = None
1411 if r == -1: r = None
1881 return author[author.find('<')+1:r]
1412 return author[author.find('<')+1:r]
1882
1413
1883 def ellipsis(text, maxlength=400):
1414 def ellipsis(text, maxlength=400):
1884 """Trim string to at most maxlength (default: 400) characters."""
1415 """Trim string to at most maxlength (default: 400) characters."""
1885 if len(text) <= maxlength:
1416 if len(text) <= maxlength:
1886 return text
1417 return text
1887 else:
1418 else:
1888 return "%s..." % (text[:maxlength-3])
1419 return "%s..." % (text[:maxlength-3])
1889
1420
1890 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1421 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1891 '''yield every hg repository under path, recursively.'''
1422 '''yield every hg repository under path, recursively.'''
1892 def errhandler(err):
1423 def errhandler(err):
1893 if err.filename == path:
1424 if err.filename == path:
1894 raise err
1425 raise err
1895 if followsym and hasattr(os.path, 'samestat'):
1426 if followsym and hasattr(os.path, 'samestat'):
1896 def _add_dir_if_not_there(dirlst, dirname):
1427 def _add_dir_if_not_there(dirlst, dirname):
1897 match = False
1428 match = False
1898 samestat = os.path.samestat
1429 samestat = os.path.samestat
1899 dirstat = os.stat(dirname)
1430 dirstat = os.stat(dirname)
1900 for lstdirstat in dirlst:
1431 for lstdirstat in dirlst:
1901 if samestat(dirstat, lstdirstat):
1432 if samestat(dirstat, lstdirstat):
1902 match = True
1433 match = True
1903 break
1434 break
1904 if not match:
1435 if not match:
1905 dirlst.append(dirstat)
1436 dirlst.append(dirstat)
1906 return not match
1437 return not match
1907 else:
1438 else:
1908 followsym = False
1439 followsym = False
1909
1440
1910 if (seen_dirs is None) and followsym:
1441 if (seen_dirs is None) and followsym:
1911 seen_dirs = []
1442 seen_dirs = []
1912 _add_dir_if_not_there(seen_dirs, path)
1443 _add_dir_if_not_there(seen_dirs, path)
1913 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1444 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1914 if '.hg' in dirs:
1445 if '.hg' in dirs:
1915 yield root # found a repository
1446 yield root # found a repository
1916 qroot = os.path.join(root, '.hg', 'patches')
1447 qroot = os.path.join(root, '.hg', 'patches')
1917 if os.path.isdir(os.path.join(qroot, '.hg')):
1448 if os.path.isdir(os.path.join(qroot, '.hg')):
1918 yield qroot # we have a patch queue repo here
1449 yield qroot # we have a patch queue repo here
1919 if recurse:
1450 if recurse:
1920 # avoid recursing inside the .hg directory
1451 # avoid recursing inside the .hg directory
1921 dirs.remove('.hg')
1452 dirs.remove('.hg')
1922 else:
1453 else:
1923 dirs[:] = [] # don't descend further
1454 dirs[:] = [] # don't descend further
1924 elif followsym:
1455 elif followsym:
1925 newdirs = []
1456 newdirs = []
1926 for d in dirs:
1457 for d in dirs:
1927 fname = os.path.join(root, d)
1458 fname = os.path.join(root, d)
1928 if _add_dir_if_not_there(seen_dirs, fname):
1459 if _add_dir_if_not_there(seen_dirs, fname):
1929 if os.path.islink(fname):
1460 if os.path.islink(fname):
1930 for hgname in walkrepos(fname, True, seen_dirs):
1461 for hgname in walkrepos(fname, True, seen_dirs):
1931 yield hgname
1462 yield hgname
1932 else:
1463 else:
1933 newdirs.append(d)
1464 newdirs.append(d)
1934 dirs[:] = newdirs
1465 dirs[:] = newdirs
1935
1466
1936 _rcpath = None
1467 _rcpath = None
1937
1468
1938 def os_rcpath():
1469 def os_rcpath():
1939 '''return default os-specific hgrc search path'''
1470 '''return default os-specific hgrc search path'''
1940 path = system_rcpath()
1471 path = system_rcpath()
1941 path.extend(user_rcpath())
1472 path.extend(user_rcpath())
1942 path = [os.path.normpath(f) for f in path]
1473 path = [os.path.normpath(f) for f in path]
1943 return path
1474 return path
1944
1475
1945 def rcpath():
1476 def rcpath():
1946 '''return hgrc search path. if env var HGRCPATH is set, use it.
1477 '''return hgrc search path. if env var HGRCPATH is set, use it.
1947 for each item in path, if directory, use files ending in .rc,
1478 for each item in path, if directory, use files ending in .rc,
1948 else use item.
1479 else use item.
1949 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1480 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1950 if no HGRCPATH, use default os-specific path.'''
1481 if no HGRCPATH, use default os-specific path.'''
1951 global _rcpath
1482 global _rcpath
1952 if _rcpath is None:
1483 if _rcpath is None:
1953 if 'HGRCPATH' in os.environ:
1484 if 'HGRCPATH' in os.environ:
1954 _rcpath = []
1485 _rcpath = []
1955 for p in os.environ['HGRCPATH'].split(os.pathsep):
1486 for p in os.environ['HGRCPATH'].split(os.pathsep):
1956 if not p: continue
1487 if not p: continue
1957 if os.path.isdir(p):
1488 if os.path.isdir(p):
1958 for f, kind in osutil.listdir(p):
1489 for f, kind in osutil.listdir(p):
1959 if f.endswith('.rc'):
1490 if f.endswith('.rc'):
1960 _rcpath.append(os.path.join(p, f))
1491 _rcpath.append(os.path.join(p, f))
1961 else:
1492 else:
1962 _rcpath.append(p)
1493 _rcpath.append(p)
1963 else:
1494 else:
1964 _rcpath = os_rcpath()
1495 _rcpath = os_rcpath()
1965 return _rcpath
1496 return _rcpath
1966
1497
1967 def bytecount(nbytes):
1498 def bytecount(nbytes):
1968 '''return byte count formatted as readable string, with units'''
1499 '''return byte count formatted as readable string, with units'''
1969
1500
1970 units = (
1501 units = (
1971 (100, 1<<30, _('%.0f GB')),
1502 (100, 1<<30, _('%.0f GB')),
1972 (10, 1<<30, _('%.1f GB')),
1503 (10, 1<<30, _('%.1f GB')),
1973 (1, 1<<30, _('%.2f GB')),
1504 (1, 1<<30, _('%.2f GB')),
1974 (100, 1<<20, _('%.0f MB')),
1505 (100, 1<<20, _('%.0f MB')),
1975 (10, 1<<20, _('%.1f MB')),
1506 (10, 1<<20, _('%.1f MB')),
1976 (1, 1<<20, _('%.2f MB')),
1507 (1, 1<<20, _('%.2f MB')),
1977 (100, 1<<10, _('%.0f KB')),
1508 (100, 1<<10, _('%.0f KB')),
1978 (10, 1<<10, _('%.1f KB')),
1509 (10, 1<<10, _('%.1f KB')),
1979 (1, 1<<10, _('%.2f KB')),
1510 (1, 1<<10, _('%.2f KB')),
1980 (1, 1, _('%.0f bytes')),
1511 (1, 1, _('%.0f bytes')),
1981 )
1512 )
1982
1513
1983 for multiplier, divisor, format in units:
1514 for multiplier, divisor, format in units:
1984 if nbytes >= divisor * multiplier:
1515 if nbytes >= divisor * multiplier:
1985 return format % (nbytes / float(divisor))
1516 return format % (nbytes / float(divisor))
1986 return units[-1][2] % nbytes
1517 return units[-1][2] % nbytes
1987
1518
1988 def drop_scheme(scheme, path):
1519 def drop_scheme(scheme, path):
1989 sc = scheme + ':'
1520 sc = scheme + ':'
1990 if path.startswith(sc):
1521 if path.startswith(sc):
1991 path = path[len(sc):]
1522 path = path[len(sc):]
1992 if path.startswith('//'):
1523 if path.startswith('//'):
1993 path = path[2:]
1524 path = path[2:]
1994 return path
1525 return path
1995
1526
1996 def uirepr(s):
1527 def uirepr(s):
1997 # Avoid double backslash in Windows path repr()
1528 # Avoid double backslash in Windows path repr()
1998 return repr(s).replace('\\\\', '\\')
1529 return repr(s).replace('\\\\', '\\')
1999
1530
2000 def termwidth():
1531 def termwidth():
2001 if 'COLUMNS' in os.environ:
1532 if 'COLUMNS' in os.environ:
2002 try:
1533 try:
2003 return int(os.environ['COLUMNS'])
1534 return int(os.environ['COLUMNS'])
2004 except ValueError:
1535 except ValueError:
2005 pass
1536 pass
2006 try:
1537 try:
2007 import termios, array, fcntl
1538 import termios, array, fcntl
2008 for dev in (sys.stdout, sys.stdin):
1539 for dev in (sys.stdout, sys.stdin):
2009 try:
1540 try:
2010 fd = dev.fileno()
1541 fd = dev.fileno()
2011 if not os.isatty(fd):
1542 if not os.isatty(fd):
2012 continue
1543 continue
2013 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1544 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
2014 return array.array('h', arri)[1]
1545 return array.array('h', arri)[1]
2015 except ValueError:
1546 except ValueError:
2016 pass
1547 pass
2017 except ImportError:
1548 except ImportError:
2018 pass
1549 pass
2019 return 80
1550 return 80
2020
1551
2021 def iterlines(iterator):
1552 def iterlines(iterator):
2022 for chunk in iterator:
1553 for chunk in iterator:
2023 for line in chunk.splitlines():
1554 for line in chunk.splitlines():
2024 yield line
1555 yield line
@@ -1,375 +1,379 b''
1 # util_win32.py - utility functions that use win32 API
1 '''
2 #
2 win32.py - utility functions that use win32 API
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
5 #
6 # This software may be used and distributed according to the terms of
7 # the GNU General Public License, incorporated herein by reference.
8
5
9 # Mark Hammond's win32all package allows better functionality on
6 This software may be used and distributed according to the terms of
10 # Windows. this module overrides definitions in util.py. if not
7 the GNU General Public License, incorporated herein by reference.
11 # available, import of this module will fail, and generic code will be
8
12 # used.
9 Mark Hammond's win32all package allows better functionality on
10 Windows. this module overrides definitions in util.py. if not
11 available, import of this module will fail, and generic code will be
12 used.
13 '''
13
14
14 import win32api
15 import win32api
15
16
16 import errno, os, sys, pywintypes, win32con, win32file, win32process
17 import errno, os, sys, pywintypes, win32con, win32file, win32process
17 import cStringIO, winerror
18 import cStringIO, winerror
18 import osutil
19 import osutil
19 import util
20 import util
20 from win32com.shell import shell,shellcon
21 from win32com.shell import shell,shellcon
21
22
22 class WinError(Exception):
23 class WinError(Exception):
23 winerror_map = {
24 winerror_map = {
24 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
25 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
25 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
26 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
26 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
27 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
27 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
28 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
28 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
29 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
29 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
30 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
30 winerror.ERROR_BAD_COMMAND: errno.EIO,
31 winerror.ERROR_BAD_COMMAND: errno.EIO,
31 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
32 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
32 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
33 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
33 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
34 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
34 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
35 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
35 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
36 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
36 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
37 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
37 winerror.ERROR_BAD_PIPE: errno.EPIPE,
38 winerror.ERROR_BAD_PIPE: errno.EPIPE,
38 winerror.ERROR_BAD_UNIT: errno.ENODEV,
39 winerror.ERROR_BAD_UNIT: errno.ENODEV,
39 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
40 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
40 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
41 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
41 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
42 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
42 winerror.ERROR_BUSY: errno.EBUSY,
43 winerror.ERROR_BUSY: errno.EBUSY,
43 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
44 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
44 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
45 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
45 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
46 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
46 winerror.ERROR_CANTOPEN: errno.EIO,
47 winerror.ERROR_CANTOPEN: errno.EIO,
47 winerror.ERROR_CANTREAD: errno.EIO,
48 winerror.ERROR_CANTREAD: errno.EIO,
48 winerror.ERROR_CANTWRITE: errno.EIO,
49 winerror.ERROR_CANTWRITE: errno.EIO,
49 winerror.ERROR_CRC: errno.EIO,
50 winerror.ERROR_CRC: errno.EIO,
50 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
51 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
51 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
52 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
52 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
53 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
53 winerror.ERROR_DIRECTORY: errno.EINVAL,
54 winerror.ERROR_DIRECTORY: errno.EINVAL,
54 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
55 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
55 winerror.ERROR_DISK_CHANGE: errno.EIO,
56 winerror.ERROR_DISK_CHANGE: errno.EIO,
56 winerror.ERROR_DISK_FULL: errno.ENOSPC,
57 winerror.ERROR_DISK_FULL: errno.ENOSPC,
57 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
58 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
58 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
59 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
59 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
60 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
60 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
61 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
61 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
62 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
62 winerror.ERROR_FILE_INVALID: errno.ENODEV,
63 winerror.ERROR_FILE_INVALID: errno.ENODEV,
63 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
64 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
64 winerror.ERROR_GEN_FAILURE: errno.EIO,
65 winerror.ERROR_GEN_FAILURE: errno.EIO,
65 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
66 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
66 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
67 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
67 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
68 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
68 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
69 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
69 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
70 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
70 winerror.ERROR_INVALID_DATA: errno.EINVAL,
71 winerror.ERROR_INVALID_DATA: errno.EINVAL,
71 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
72 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
72 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
73 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
73 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
74 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
74 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
75 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
75 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
76 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
76 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
77 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
77 winerror.ERROR_INVALID_NAME: errno.EINVAL,
78 winerror.ERROR_INVALID_NAME: errno.EINVAL,
78 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
79 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
79 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
80 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
80 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
81 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
81 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
82 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
82 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
83 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
83 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
84 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
84 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
85 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
85 winerror.ERROR_IO_DEVICE: errno.EIO,
86 winerror.ERROR_IO_DEVICE: errno.EIO,
86 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
87 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
87 winerror.ERROR_LOCKED: errno.EBUSY,
88 winerror.ERROR_LOCKED: errno.EBUSY,
88 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
89 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
89 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
90 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
90 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
91 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
91 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
92 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
92 winerror.ERROR_MORE_DATA: errno.EPIPE,
93 winerror.ERROR_MORE_DATA: errno.EPIPE,
93 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
94 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
94 winerror.ERROR_NOACCESS: errno.EFAULT,
95 winerror.ERROR_NOACCESS: errno.EFAULT,
95 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
96 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
96 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
97 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
97 winerror.ERROR_NOT_READY: errno.EAGAIN,
98 winerror.ERROR_NOT_READY: errno.EAGAIN,
98 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
99 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
99 winerror.ERROR_NO_DATA: errno.EPIPE,
100 winerror.ERROR_NO_DATA: errno.EPIPE,
100 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
101 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
101 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
102 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
102 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
103 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
103 winerror.ERROR_OPEN_FAILED: errno.EIO,
104 winerror.ERROR_OPEN_FAILED: errno.EIO,
104 winerror.ERROR_OPEN_FILES: errno.EBUSY,
105 winerror.ERROR_OPEN_FILES: errno.EBUSY,
105 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
106 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
106 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
107 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
107 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
108 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
108 winerror.ERROR_PATH_BUSY: errno.EBUSY,
109 winerror.ERROR_PATH_BUSY: errno.EBUSY,
109 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
110 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
110 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
111 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
111 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
112 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
112 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
113 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
113 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
114 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
114 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
115 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
115 winerror.ERROR_READ_FAULT: errno.EIO,
116 winerror.ERROR_READ_FAULT: errno.EIO,
116 winerror.ERROR_SEEK: errno.EIO,
117 winerror.ERROR_SEEK: errno.EIO,
117 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
118 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
118 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
119 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
119 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
120 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
120 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
121 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
121 winerror.ERROR_SWAPERROR: errno.ENOENT,
122 winerror.ERROR_SWAPERROR: errno.ENOENT,
122 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
123 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
123 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
124 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
124 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
125 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
125 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
126 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
126 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
127 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
127 winerror.ERROR_WRITE_FAULT: errno.EIO,
128 winerror.ERROR_WRITE_FAULT: errno.EIO,
128 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
129 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
129 }
130 }
130
131
131 def __init__(self, err):
132 def __init__(self, err):
132 self.win_errno, self.win_function, self.win_strerror = err
133 self.win_errno, self.win_function, self.win_strerror = err
133 if self.win_strerror.endswith('.'):
134 if self.win_strerror.endswith('.'):
134 self.win_strerror = self.win_strerror[:-1]
135 self.win_strerror = self.win_strerror[:-1]
135
136
136 class WinIOError(WinError, IOError):
137 class WinIOError(WinError, IOError):
137 def __init__(self, err, filename=None):
138 def __init__(self, err, filename=None):
138 WinError.__init__(self, err)
139 WinError.__init__(self, err)
139 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
140 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
140 self.win_strerror)
141 self.win_strerror)
141 self.filename = filename
142 self.filename = filename
142
143
143 class WinOSError(WinError, OSError):
144 class WinOSError(WinError, OSError):
144 def __init__(self, err):
145 def __init__(self, err):
145 WinError.__init__(self, err)
146 WinError.__init__(self, err)
146 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
147 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
147 self.win_strerror)
148 self.win_strerror)
148
149
149 def os_link(src, dst):
150 def os_link(src, dst):
150 try:
151 try:
151 win32file.CreateHardLink(dst, src)
152 win32file.CreateHardLink(dst, src)
152 # CreateHardLink sometimes succeeds on mapped drives but
153 # CreateHardLink sometimes succeeds on mapped drives but
153 # following nlinks() returns 1. Check it now and bail out.
154 # following nlinks() returns 1. Check it now and bail out.
154 if nlinks(src) < 2:
155 if nlinks(src) < 2:
155 try:
156 try:
156 win32file.DeleteFile(dst)
157 win32file.DeleteFile(dst)
157 except:
158 except:
158 pass
159 pass
159 # Fake hardlinking error
160 # Fake hardlinking error
160 raise WinOSError((18, 'CreateHardLink', 'The system cannot '
161 raise WinOSError((18, 'CreateHardLink', 'The system cannot '
161 'move the file to a different disk drive'))
162 'move the file to a different disk drive'))
162 except pywintypes.error, details:
163 except pywintypes.error, details:
163 raise WinOSError(details)
164 raise WinOSError(details)
164 except NotImplementedError: # Another fake error win Win98
165 except NotImplementedError: # Another fake error win Win98
165 raise WinOSError((18, 'CreateHardLink', 'Hardlinking not supported'))
166 raise WinOSError((18, 'CreateHardLink', 'Hardlinking not supported'))
166
167
167 def nlinks(pathname):
168 def nlinks(pathname):
168 """Return number of hardlinks for the given file."""
169 """Return number of hardlinks for the given file."""
169 try:
170 try:
170 fh = win32file.CreateFile(pathname,
171 fh = win32file.CreateFile(pathname,
171 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
172 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
172 None, win32file.OPEN_EXISTING, 0, None)
173 None, win32file.OPEN_EXISTING, 0, None)
173 res = win32file.GetFileInformationByHandle(fh)
174 res = win32file.GetFileInformationByHandle(fh)
174 fh.Close()
175 fh.Close()
175 return res[7]
176 return res[7]
176 except pywintypes.error:
177 except pywintypes.error:
177 return os.lstat(pathname).st_nlink
178 return os.lstat(pathname).st_nlink
178
179
179 def testpid(pid):
180 def testpid(pid):
180 '''return True if pid is still running or unable to
181 '''return True if pid is still running or unable to
181 determine, False otherwise'''
182 determine, False otherwise'''
182 try:
183 try:
183 handle = win32api.OpenProcess(
184 handle = win32api.OpenProcess(
184 win32con.PROCESS_QUERY_INFORMATION, False, pid)
185 win32con.PROCESS_QUERY_INFORMATION, False, pid)
185 if handle:
186 if handle:
186 status = win32process.GetExitCodeProcess(handle)
187 status = win32process.GetExitCodeProcess(handle)
187 return status == win32con.STILL_ACTIVE
188 return status == win32con.STILL_ACTIVE
188 except pywintypes.error, details:
189 except pywintypes.error, details:
189 return details[0] != winerror.ERROR_INVALID_PARAMETER
190 return details[0] != winerror.ERROR_INVALID_PARAMETER
190 return True
191 return True
191
192
192 def lookup_reg(key, valname=None, scope=None):
193 def lookup_reg(key, valname=None, scope=None):
193 ''' Look up a key/value name in the Windows registry.
194 ''' Look up a key/value name in the Windows registry.
194
195
195 valname: value name. If unspecified, the default value for the key
196 valname: value name. If unspecified, the default value for the key
196 is used.
197 is used.
197 scope: optionally specify scope for registry lookup, this can be
198 scope: optionally specify scope for registry lookup, this can be
198 a sequence of scopes to look up in order. Default (CURRENT_USER,
199 a sequence of scopes to look up in order. Default (CURRENT_USER,
199 LOCAL_MACHINE).
200 LOCAL_MACHINE).
200 '''
201 '''
201 try:
202 try:
202 from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
203 from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
203 QueryValueEx, OpenKey
204 QueryValueEx, OpenKey
204 except ImportError:
205 except ImportError:
205 return None
206 return None
206
207
207 if scope is None:
208 if scope is None:
208 scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
209 scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
209 elif not isinstance(scope, (list, tuple)):
210 elif not isinstance(scope, (list, tuple)):
210 scope = (scope,)
211 scope = (scope,)
211 for s in scope:
212 for s in scope:
212 try:
213 try:
213 val = QueryValueEx(OpenKey(s, key), valname)[0]
214 val = QueryValueEx(OpenKey(s, key), valname)[0]
214 # never let a Unicode string escape into the wild
215 # never let a Unicode string escape into the wild
215 return util.tolocal(val.encode('UTF-8'))
216 return util.tolocal(val.encode('UTF-8'))
216 except EnvironmentError:
217 except EnvironmentError:
217 pass
218 pass
218
219
219 def system_rcpath_win32():
220 def system_rcpath_win32():
220 '''return default os-specific hgrc search path'''
221 '''return default os-specific hgrc search path'''
221 proc = win32api.GetCurrentProcess()
222 proc = win32api.GetCurrentProcess()
222 try:
223 try:
223 # This will fail on windows < NT
224 # This will fail on windows < NT
224 filename = win32process.GetModuleFileNameEx(proc, 0)
225 filename = win32process.GetModuleFileNameEx(proc, 0)
225 except:
226 except:
226 filename = win32api.GetModuleFileName(0)
227 filename = win32api.GetModuleFileName(0)
227 # Use mercurial.ini found in directory with hg.exe
228 # Use mercurial.ini found in directory with hg.exe
228 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
229 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
229 if os.path.isfile(progrc):
230 if os.path.isfile(progrc):
230 return [progrc]
231 return [progrc]
231 # else look for a system rcpath in the registry
232 # else look for a system rcpath in the registry
232 try:
233 try:
233 value = win32api.RegQueryValue(
234 value = win32api.RegQueryValue(
234 win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
235 win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
235 rcpath = []
236 rcpath = []
236 for p in value.split(os.pathsep):
237 for p in value.split(os.pathsep):
237 if p.lower().endswith('mercurial.ini'):
238 if p.lower().endswith('mercurial.ini'):
238 rcpath.append(p)
239 rcpath.append(p)
239 elif os.path.isdir(p):
240 elif os.path.isdir(p):
240 for f, kind in osutil.listdir(p):
241 for f, kind in osutil.listdir(p):
241 if f.endswith('.rc'):
242 if f.endswith('.rc'):
242 rcpath.append(os.path.join(p, f))
243 rcpath.append(os.path.join(p, f))
243 return rcpath
244 return rcpath
244 except pywintypes.error:
245 except pywintypes.error:
245 return []
246 return []
246
247
247 def user_rcpath_win32():
248 def user_rcpath_win32():
248 '''return os-specific hgrc search path to the user dir'''
249 '''return os-specific hgrc search path to the user dir'''
249 userdir = os.path.expanduser('~')
250 userdir = os.path.expanduser('~')
250 if sys.getwindowsversion()[3] != 2 and userdir == '~':
251 if sys.getwindowsversion()[3] != 2 and userdir == '~':
251 # We are on win < nt: fetch the APPDATA directory location and use
252 # We are on win < nt: fetch the APPDATA directory location and use
252 # the parent directory as the user home dir.
253 # the parent directory as the user home dir.
253 appdir = shell.SHGetPathFromIDList(
254 appdir = shell.SHGetPathFromIDList(
254 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
255 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
255 userdir = os.path.dirname(appdir)
256 userdir = os.path.dirname(appdir)
256 return [os.path.join(userdir, 'mercurial.ini'),
257 return [os.path.join(userdir, 'mercurial.ini'),
257 os.path.join(userdir, '.hgrc')]
258 os.path.join(userdir, '.hgrc')]
258
259
259 class posixfile_nt(object):
260 class posixfile_nt(object):
260 '''file object with posix-like semantics. on windows, normal
261 '''file object with posix-like semantics. on windows, normal
261 files can not be deleted or renamed if they are open. must open
262 files can not be deleted or renamed if they are open. must open
262 with win32file.FILE_SHARE_DELETE. this flag does not exist on
263 with win32file.FILE_SHARE_DELETE. this flag does not exist on
263 windows < nt, so do not use this class there.'''
264 windows < nt, so do not use this class there.'''
264
265
265 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
266 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
266 # but does not work at all. wrap win32 file api instead.
267 # but does not work at all. wrap win32 file api instead.
267
268
268 def __init__(self, name, mode='rb'):
269 def __init__(self, name, mode='rb'):
269 self.closed = False
270 self.closed = False
270 self.name = name
271 self.name = name
271 self.mode = mode
272 self.mode = mode
272 access = 0
273 access = 0
273 if 'r' in mode or '+' in mode:
274 if 'r' in mode or '+' in mode:
274 access |= win32file.GENERIC_READ
275 access |= win32file.GENERIC_READ
275 if 'w' in mode or 'a' in mode or '+' in mode:
276 if 'w' in mode or 'a' in mode or '+' in mode:
276 access |= win32file.GENERIC_WRITE
277 access |= win32file.GENERIC_WRITE
277 if 'r' in mode:
278 if 'r' in mode:
278 creation = win32file.OPEN_EXISTING
279 creation = win32file.OPEN_EXISTING
279 elif 'a' in mode:
280 elif 'a' in mode:
280 creation = win32file.OPEN_ALWAYS
281 creation = win32file.OPEN_ALWAYS
281 else:
282 else:
282 creation = win32file.CREATE_ALWAYS
283 creation = win32file.CREATE_ALWAYS
283 try:
284 try:
284 self.handle = win32file.CreateFile(name,
285 self.handle = win32file.CreateFile(name,
285 access,
286 access,
286 win32file.FILE_SHARE_READ |
287 win32file.FILE_SHARE_READ |
287 win32file.FILE_SHARE_WRITE |
288 win32file.FILE_SHARE_WRITE |
288 win32file.FILE_SHARE_DELETE,
289 win32file.FILE_SHARE_DELETE,
289 None,
290 None,
290 creation,
291 creation,
291 win32file.FILE_ATTRIBUTE_NORMAL,
292 win32file.FILE_ATTRIBUTE_NORMAL,
292 0)
293 0)
293 except pywintypes.error, err:
294 except pywintypes.error, err:
294 raise WinIOError(err, name)
295 raise WinIOError(err, name)
295
296
296 def __iter__(self):
297 def __iter__(self):
297 for line in self.readlines():
298 for line in self.readlines():
298 yield line
299 yield line
299
300
300 def read(self, count=-1):
301 def read(self, count=-1):
301 try:
302 try:
302 cs = cStringIO.StringIO()
303 cs = cStringIO.StringIO()
303 while count:
304 while count:
304 wincount = int(count)
305 wincount = int(count)
305 if wincount == -1:
306 if wincount == -1:
306 wincount = 1048576
307 wincount = 1048576
307 val, data = win32file.ReadFile(self.handle, wincount)
308 val, data = win32file.ReadFile(self.handle, wincount)
308 if not data: break
309 if not data: break
309 cs.write(data)
310 cs.write(data)
310 if count != -1:
311 if count != -1:
311 count -= len(data)
312 count -= len(data)
312 return cs.getvalue()
313 return cs.getvalue()
313 except pywintypes.error, err:
314 except pywintypes.error, err:
314 raise WinIOError(err)
315 raise WinIOError(err)
315
316
316 def readlines(self, sizehint=None):
317 def readlines(self, sizehint=None):
317 # splitlines() splits on single '\r' while readlines()
318 # splitlines() splits on single '\r' while readlines()
318 # does not. cStringIO has a well behaving readlines() and is fast.
319 # does not. cStringIO has a well behaving readlines() and is fast.
319 return cStringIO.StringIO(self.read()).readlines()
320 return cStringIO.StringIO(self.read()).readlines()
320
321
321 def write(self, data):
322 def write(self, data):
322 try:
323 try:
323 if 'a' in self.mode:
324 if 'a' in self.mode:
324 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
325 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
325 nwrit = 0
326 nwrit = 0
326 while nwrit < len(data):
327 while nwrit < len(data):
327 val, nwrit = win32file.WriteFile(self.handle, data)
328 val, nwrit = win32file.WriteFile(self.handle, data)
328 data = data[nwrit:]
329 data = data[nwrit:]
329 except pywintypes.error, err:
330 except pywintypes.error, err:
330 raise WinIOError(err)
331 raise WinIOError(err)
331
332
332 def writelines(self, sequence):
333 def writelines(self, sequence):
333 for s in sequence:
334 for s in sequence:
334 self.write(s)
335 self.write(s)
335
336
336 def seek(self, pos, whence=0):
337 def seek(self, pos, whence=0):
337 try:
338 try:
338 win32file.SetFilePointer(self.handle, int(pos), whence)
339 win32file.SetFilePointer(self.handle, int(pos), whence)
339 except pywintypes.error, err:
340 except pywintypes.error, err:
340 raise WinIOError(err)
341 raise WinIOError(err)
341
342
342 def tell(self):
343 def tell(self):
343 try:
344 try:
344 return win32file.SetFilePointer(self.handle, 0,
345 return win32file.SetFilePointer(self.handle, 0,
345 win32file.FILE_CURRENT)
346 win32file.FILE_CURRENT)
346 except pywintypes.error, err:
347 except pywintypes.error, err:
347 raise WinIOError(err)
348 raise WinIOError(err)
348
349
349 def close(self):
350 def close(self):
350 if not self.closed:
351 if not self.closed:
351 self.handle = None
352 self.handle = None
352 self.closed = True
353 self.closed = True
353
354
354 def flush(self):
355 def flush(self):
355 # we have no application-level buffering
356 # we have no application-level buffering
356 pass
357 pass
357
358
358 def truncate(self, pos=0):
359 def truncate(self, pos=0):
359 try:
360 try:
360 win32file.SetFilePointer(self.handle, int(pos),
361 win32file.SetFilePointer(self.handle, int(pos),
361 win32file.FILE_BEGIN)
362 win32file.FILE_BEGIN)
362 win32file.SetEndOfFile(self.handle)
363 win32file.SetEndOfFile(self.handle)
363 except pywintypes.error, err:
364 except pywintypes.error, err:
364 raise WinIOError(err)
365 raise WinIOError(err)
365
366
366 getuser_fallback = win32api.GetUserName
367 def getuser():
368 '''return name of current user'''
369 return win32api.GetUserName()
367
370
368 def set_signal_handler_win32():
371 def set_signal_handler_win32():
369 """Register a termination handler for console events including
372 """Register a termination handler for console events including
370 CTRL+C. python signal handlers do not work well with socket
373 CTRL+C. python signal handlers do not work well with socket
371 operations.
374 operations.
372 """
375 """
373 def handler(event):
376 def handler(event):
374 win32process.ExitProcess(1)
377 win32process.ExitProcess(1)
375 win32api.SetConsoleCtrlHandler(handler)
378 win32api.SetConsoleCtrlHandler(handler)
379
General Comments 0
You need to be logged in to leave comments. Login now