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