##// END OF EJS Templates
Minor tweak: os.STOPSIG -> os.WSTOPSIG. Pychecker spotted this one.
mark.williamson@cl.cam.ac.uk -
r912:302f83b8 default
parent child Browse files
Show More
@@ -1,281 +1,281
1 # util.py - utility functions and platform specfic implementations
1 # util.py - utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, errno
8 import os, errno
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "re")
10 demandload(globals(), "re")
11
11
12 def unique(g):
12 def unique(g):
13 seen = {}
13 seen = {}
14 for f in g:
14 for f in g:
15 if f not in seen:
15 if f not in seen:
16 seen[f] = 1
16 seen[f] = 1
17 yield f
17 yield f
18
18
19 class Abort(Exception):
19 class Abort(Exception):
20 """Raised if a command needs to print an error and exit."""
20 """Raised if a command needs to print an error and exit."""
21
21
22 def always(fn): return True
22 def always(fn): return True
23 def never(fn): return False
23 def never(fn): return False
24
24
25 def globre(pat, head = '^', tail = '$'):
25 def globre(pat, head = '^', tail = '$'):
26 "convert a glob pattern into a regexp"
26 "convert a glob pattern into a regexp"
27 i, n = 0, len(pat)
27 i, n = 0, len(pat)
28 res = ''
28 res = ''
29 group = False
29 group = False
30 def peek(): return i < n and pat[i]
30 def peek(): return i < n and pat[i]
31 while i < n:
31 while i < n:
32 c = pat[i]
32 c = pat[i]
33 i = i+1
33 i = i+1
34 if c == '*':
34 if c == '*':
35 if peek() == '*':
35 if peek() == '*':
36 i += 1
36 i += 1
37 res += '.*'
37 res += '.*'
38 else:
38 else:
39 res += '[^/]*'
39 res += '[^/]*'
40 elif c == '?':
40 elif c == '?':
41 res += '.'
41 res += '.'
42 elif c == '[':
42 elif c == '[':
43 j = i
43 j = i
44 if j < n and pat[j] in '!]':
44 if j < n and pat[j] in '!]':
45 j += 1
45 j += 1
46 while j < n and pat[j] != ']':
46 while j < n and pat[j] != ']':
47 j += 1
47 j += 1
48 if j >= n:
48 if j >= n:
49 res += '\\['
49 res += '\\['
50 else:
50 else:
51 stuff = pat[i:j].replace('\\','\\\\')
51 stuff = pat[i:j].replace('\\','\\\\')
52 i = j + 1
52 i = j + 1
53 if stuff[0] == '!':
53 if stuff[0] == '!':
54 stuff = '^' + stuff[1:]
54 stuff = '^' + stuff[1:]
55 elif stuff[0] == '^':
55 elif stuff[0] == '^':
56 stuff = '\\' + stuff
56 stuff = '\\' + stuff
57 res = '%s[%s]' % (res, stuff)
57 res = '%s[%s]' % (res, stuff)
58 elif c == '{':
58 elif c == '{':
59 group = True
59 group = True
60 res += '(?:'
60 res += '(?:'
61 elif c == '}' and group:
61 elif c == '}' and group:
62 res += ')'
62 res += ')'
63 group = False
63 group = False
64 elif c == ',' and group:
64 elif c == ',' and group:
65 res += '|'
65 res += '|'
66 else:
66 else:
67 res += re.escape(c)
67 res += re.escape(c)
68 return head + res + tail
68 return head + res + tail
69
69
70 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
70 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
71
71
72 def pathto(n1, n2):
72 def pathto(n1, n2):
73 '''return the relative path from one place to another.
73 '''return the relative path from one place to another.
74 this returns a path in the form used by the local filesystem, not hg.'''
74 this returns a path in the form used by the local filesystem, not hg.'''
75 if not n1: return localpath(n2)
75 if not n1: return localpath(n2)
76 a, b = n1.split('/'), n2.split('/')
76 a, b = n1.split('/'), n2.split('/')
77 a.reverse(), b.reverse()
77 a.reverse(), b.reverse()
78 while a and b and a[-1] == b[-1]:
78 while a and b and a[-1] == b[-1]:
79 a.pop(), b.pop()
79 a.pop(), b.pop()
80 b.reverse()
80 b.reverse()
81 return os.sep.join((['..'] * len(a)) + b)
81 return os.sep.join((['..'] * len(a)) + b)
82
82
83 def canonpath(repo, cwd, myname):
83 def canonpath(repo, cwd, myname):
84 rootsep = repo.root + os.sep
84 rootsep = repo.root + os.sep
85 name = myname
85 name = myname
86 if not name.startswith(os.sep):
86 if not name.startswith(os.sep):
87 name = os.path.join(repo.root, cwd, name)
87 name = os.path.join(repo.root, cwd, name)
88 name = os.path.normpath(name)
88 name = os.path.normpath(name)
89 if name.startswith(rootsep):
89 if name.startswith(rootsep):
90 return pconvert(name[len(rootsep):])
90 return pconvert(name[len(rootsep):])
91 elif name == repo.root:
91 elif name == repo.root:
92 return ''
92 return ''
93 else:
93 else:
94 raise Abort('%s not under repository root' % myname)
94 raise Abort('%s not under repository root' % myname)
95
95
96 def matcher(repo, cwd, names, inc, exc, head = ''):
96 def matcher(repo, cwd, names, inc, exc, head = ''):
97 def patkind(name):
97 def patkind(name):
98 for prefix in 're:', 'glob:', 'path:', 'relpath:':
98 for prefix in 're:', 'glob:', 'path:', 'relpath:':
99 if name.startswith(prefix): return name.split(':', 1)
99 if name.startswith(prefix): return name.split(':', 1)
100 for c in name:
100 for c in name:
101 if c in _globchars: return 'glob', name
101 if c in _globchars: return 'glob', name
102 return 'relpath', name
102 return 'relpath', name
103
103
104 def regex(kind, name, tail):
104 def regex(kind, name, tail):
105 '''convert a pattern into a regular expression'''
105 '''convert a pattern into a regular expression'''
106 if kind == 're':
106 if kind == 're':
107 return name
107 return name
108 elif kind == 'path':
108 elif kind == 'path':
109 return '^' + re.escape(name) + '(?:/|$)'
109 return '^' + re.escape(name) + '(?:/|$)'
110 elif kind == 'relpath':
110 elif kind == 'relpath':
111 return head + re.escape(name) + tail
111 return head + re.escape(name) + tail
112 return head + globre(name, '', tail)
112 return head + globre(name, '', tail)
113
113
114 def matchfn(pats, tail):
114 def matchfn(pats, tail):
115 """build a matching function from a set of patterns"""
115 """build a matching function from a set of patterns"""
116 if pats:
116 if pats:
117 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
117 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
118 return re.compile(pat).match
118 return re.compile(pat).match
119
119
120 def globprefix(pat):
120 def globprefix(pat):
121 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
121 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
122 root = []
122 root = []
123 for p in pat.split(os.sep):
123 for p in pat.split(os.sep):
124 if patkind(p)[0] == 'glob': break
124 if patkind(p)[0] == 'glob': break
125 root.append(p)
125 root.append(p)
126 return '/'.join(root)
126 return '/'.join(root)
127
127
128 pats = []
128 pats = []
129 files = []
129 files = []
130 roots = []
130 roots = []
131 for kind, name in map(patkind, names):
131 for kind, name in map(patkind, names):
132 if kind in ('glob', 'relpath'):
132 if kind in ('glob', 'relpath'):
133 name = canonpath(repo, cwd, name)
133 name = canonpath(repo, cwd, name)
134 if name == '':
134 if name == '':
135 kind, name = 'glob', '**'
135 kind, name = 'glob', '**'
136 if kind in ('glob', 'path', 're'):
136 if kind in ('glob', 'path', 're'):
137 pats.append((kind, name))
137 pats.append((kind, name))
138 if kind == 'glob':
138 if kind == 'glob':
139 root = globprefix(name)
139 root = globprefix(name)
140 if root: roots.append(root)
140 if root: roots.append(root)
141 elif kind == 'relpath':
141 elif kind == 'relpath':
142 files.append((kind, name))
142 files.append((kind, name))
143 roots.append(name)
143 roots.append(name)
144
144
145 patmatch = matchfn(pats, '$') or always
145 patmatch = matchfn(pats, '$') or always
146 filematch = matchfn(files, '(?:/|$)') or always
146 filematch = matchfn(files, '(?:/|$)') or always
147 incmatch = always
147 incmatch = always
148 if inc:
148 if inc:
149 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
149 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
150 excmatch = lambda fn: False
150 excmatch = lambda fn: False
151 if exc:
151 if exc:
152 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
152 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
153
153
154 return roots, lambda fn: (incmatch(fn) and not excmatch(fn) and
154 return roots, lambda fn: (incmatch(fn) and not excmatch(fn) and
155 (fn.endswith('/') or
155 (fn.endswith('/') or
156 (not pats and not files) or
156 (not pats and not files) or
157 (pats and patmatch(fn)) or
157 (pats and patmatch(fn)) or
158 (files and filematch(fn))))
158 (files and filematch(fn))))
159
159
160 def system(cmd, errprefix=None):
160 def system(cmd, errprefix=None):
161 """execute a shell command that must succeed"""
161 """execute a shell command that must succeed"""
162 rc = os.system(cmd)
162 rc = os.system(cmd)
163 if rc:
163 if rc:
164 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
164 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
165 explain_exit(rc)[0])
165 explain_exit(rc)[0])
166 if errprefix:
166 if errprefix:
167 errmsg = "%s: %s" % (errprefix, errmsg)
167 errmsg = "%s: %s" % (errprefix, errmsg)
168 raise Abort(errmsg)
168 raise Abort(errmsg)
169
169
170 def rename(src, dst):
170 def rename(src, dst):
171 try:
171 try:
172 os.rename(src, dst)
172 os.rename(src, dst)
173 except:
173 except:
174 os.unlink(dst)
174 os.unlink(dst)
175 os.rename(src, dst)
175 os.rename(src, dst)
176
176
177 def copytree(src, dst, copyfile):
177 def copytree(src, dst, copyfile):
178 """Copy a directory tree, files are copied using 'copyfile'."""
178 """Copy a directory tree, files are copied using 'copyfile'."""
179 names = os.listdir(src)
179 names = os.listdir(src)
180 os.mkdir(dst)
180 os.mkdir(dst)
181
181
182 for name in names:
182 for name in names:
183 srcname = os.path.join(src, name)
183 srcname = os.path.join(src, name)
184 dstname = os.path.join(dst, name)
184 dstname = os.path.join(dst, name)
185 if os.path.isdir(srcname):
185 if os.path.isdir(srcname):
186 copytree(srcname, dstname, copyfile)
186 copytree(srcname, dstname, copyfile)
187 elif os.path.isfile(srcname):
187 elif os.path.isfile(srcname):
188 copyfile(srcname, dstname)
188 copyfile(srcname, dstname)
189 else:
189 else:
190 raise IOError("Not a regular file: %r" % srcname)
190 raise IOError("Not a regular file: %r" % srcname)
191
191
192 def _makelock_file(info, pathname):
192 def _makelock_file(info, pathname):
193 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
193 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
194 os.write(ld, info)
194 os.write(ld, info)
195 os.close(ld)
195 os.close(ld)
196
196
197 def _readlock_file(pathname):
197 def _readlock_file(pathname):
198 return file(pathname).read()
198 return file(pathname).read()
199
199
200 # Platfor specific varients
200 # Platfor specific varients
201 if os.name == 'nt':
201 if os.name == 'nt':
202 nulldev = 'NUL:'
202 nulldev = 'NUL:'
203
203
204 def is_exec(f, last):
204 def is_exec(f, last):
205 return last
205 return last
206
206
207 def set_exec(f, mode):
207 def set_exec(f, mode):
208 pass
208 pass
209
209
210 def pconvert(path):
210 def pconvert(path):
211 return path.replace("\\", "/")
211 return path.replace("\\", "/")
212
212
213 def localpath(path):
213 def localpath(path):
214 return path.replace('/', '\\')
214 return path.replace('/', '\\')
215
215
216 def normpath(path):
216 def normpath(path):
217 return pconvert(os.path.normpath(path))
217 return pconvert(os.path.normpath(path))
218
218
219 makelock = _makelock_file
219 makelock = _makelock_file
220 readlock = _readlock_file
220 readlock = _readlock_file
221
221
222 def explain_exit(code):
222 def explain_exit(code):
223 return "exited with status %d" % code, code
223 return "exited with status %d" % code, code
224
224
225 else:
225 else:
226 nulldev = '/dev/null'
226 nulldev = '/dev/null'
227
227
228 def is_exec(f, last):
228 def is_exec(f, last):
229 return (os.stat(f).st_mode & 0100 != 0)
229 return (os.stat(f).st_mode & 0100 != 0)
230
230
231 def set_exec(f, mode):
231 def set_exec(f, mode):
232 s = os.stat(f).st_mode
232 s = os.stat(f).st_mode
233 if (s & 0100 != 0) == mode:
233 if (s & 0100 != 0) == mode:
234 return
234 return
235 if mode:
235 if mode:
236 # Turn on +x for every +r bit when making a file executable
236 # Turn on +x for every +r bit when making a file executable
237 # and obey umask.
237 # and obey umask.
238 umask = os.umask(0)
238 umask = os.umask(0)
239 os.umask(umask)
239 os.umask(umask)
240 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
240 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
241 else:
241 else:
242 os.chmod(f, s & 0666)
242 os.chmod(f, s & 0666)
243
243
244 def pconvert(path):
244 def pconvert(path):
245 return path
245 return path
246
246
247 def localpath(path):
247 def localpath(path):
248 return path
248 return path
249
249
250 normpath = os.path.normpath
250 normpath = os.path.normpath
251
251
252 def makelock(info, pathname):
252 def makelock(info, pathname):
253 try:
253 try:
254 os.symlink(info, pathname)
254 os.symlink(info, pathname)
255 except OSError, why:
255 except OSError, why:
256 if why.errno == errno.EEXIST:
256 if why.errno == errno.EEXIST:
257 raise
257 raise
258 else:
258 else:
259 _makelock_file(info, pathname)
259 _makelock_file(info, pathname)
260
260
261 def readlock(pathname):
261 def readlock(pathname):
262 try:
262 try:
263 return os.readlink(pathname)
263 return os.readlink(pathname)
264 except OSError, why:
264 except OSError, why:
265 if why.errno == errno.EINVAL:
265 if why.errno == errno.EINVAL:
266 return _readlock_file(pathname)
266 return _readlock_file(pathname)
267 else:
267 else:
268 raise
268 raise
269
269
270 def explain_exit(code):
270 def explain_exit(code):
271 """return a 2-tuple (desc, code) describing a process's status"""
271 """return a 2-tuple (desc, code) describing a process's status"""
272 if os.WIFEXITED(code):
272 if os.WIFEXITED(code):
273 val = os.WEXITSTATUS(code)
273 val = os.WEXITSTATUS(code)
274 return "exited with status %d" % val, val
274 return "exited with status %d" % val, val
275 elif os.WIFSIGNALED(code):
275 elif os.WIFSIGNALED(code):
276 val = os.WTERMSIG(code)
276 val = os.WTERMSIG(code)
277 return "killed by signal %d" % val, val
277 return "killed by signal %d" % val, val
278 elif os.WIFSTOPPED(code):
278 elif os.WIFSTOPPED(code):
279 val = os.STOPSIG(code)
279 val = os.WSTOPSIG(code)
280 return "stopped by signal %d" % val, val
280 return "stopped by signal %d" % val, val
281 raise ValueError("invalid exit code")
281 raise ValueError("invalid exit code")
General Comments 0
You need to be logged in to leave comments. Login now