##// END OF EJS Templates
Fix bug with empty inc and exc...
mpm@selenic.com -
r897:fe30f543 default
parent child Browse files
Show More
@@ -1,277 +1,281 b''
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 = matchfn(map(patkind, inc), '(?:/|$)') or always
147 incmatch = always
148 excmatch = matchfn(map(patkind, exc), '(?:/|$)') or (lambda fn: False)
148 if inc:
149 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
150 excmatch = lambda fn: False
151 if exc:
152 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
149
153
150 return roots, lambda fn: (incmatch(fn) and not excmatch(fn) and
154 return roots, lambda fn: (incmatch(fn) and not excmatch(fn) and
151 (fn.endswith('/') or
155 (fn.endswith('/') or
152 (not pats and not files) or
156 (not pats and not files) or
153 (pats and patmatch(fn)) or
157 (pats and patmatch(fn)) or
154 (files and filematch(fn))))
158 (files and filematch(fn))))
155
159
156 def system(cmd, errprefix=None):
160 def system(cmd, errprefix=None):
157 """execute a shell command that must succeed"""
161 """execute a shell command that must succeed"""
158 rc = os.system(cmd)
162 rc = os.system(cmd)
159 if rc:
163 if rc:
160 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
164 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
161 explain_exit(rc)[0])
165 explain_exit(rc)[0])
162 if errprefix:
166 if errprefix:
163 errmsg = "%s: %s" % (errprefix, errmsg)
167 errmsg = "%s: %s" % (errprefix, errmsg)
164 raise Abort(errmsg)
168 raise Abort(errmsg)
165
169
166 def rename(src, dst):
170 def rename(src, dst):
167 try:
171 try:
168 os.rename(src, dst)
172 os.rename(src, dst)
169 except:
173 except:
170 os.unlink(dst)
174 os.unlink(dst)
171 os.rename(src, dst)
175 os.rename(src, dst)
172
176
173 def copytree(src, dst, copyfile):
177 def copytree(src, dst, copyfile):
174 """Copy a directory tree, files are copied using 'copyfile'."""
178 """Copy a directory tree, files are copied using 'copyfile'."""
175 names = os.listdir(src)
179 names = os.listdir(src)
176 os.mkdir(dst)
180 os.mkdir(dst)
177
181
178 for name in names:
182 for name in names:
179 srcname = os.path.join(src, name)
183 srcname = os.path.join(src, name)
180 dstname = os.path.join(dst, name)
184 dstname = os.path.join(dst, name)
181 if os.path.isdir(srcname):
185 if os.path.isdir(srcname):
182 copytree(srcname, dstname, copyfile)
186 copytree(srcname, dstname, copyfile)
183 elif os.path.isfile(srcname):
187 elif os.path.isfile(srcname):
184 copyfile(srcname, dstname)
188 copyfile(srcname, dstname)
185 else:
189 else:
186 raise IOError("Not a regular file: %r" % srcname)
190 raise IOError("Not a regular file: %r" % srcname)
187
191
188 def _makelock_file(info, pathname):
192 def _makelock_file(info, pathname):
189 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)
190 os.write(ld, info)
194 os.write(ld, info)
191 os.close(ld)
195 os.close(ld)
192
196
193 def _readlock_file(pathname):
197 def _readlock_file(pathname):
194 return file(pathname).read()
198 return file(pathname).read()
195
199
196 # Platfor specific varients
200 # Platfor specific varients
197 if os.name == 'nt':
201 if os.name == 'nt':
198 nulldev = 'NUL:'
202 nulldev = 'NUL:'
199
203
200 def is_exec(f, last):
204 def is_exec(f, last):
201 return last
205 return last
202
206
203 def set_exec(f, mode):
207 def set_exec(f, mode):
204 pass
208 pass
205
209
206 def pconvert(path):
210 def pconvert(path):
207 return path.replace("\\", "/")
211 return path.replace("\\", "/")
208
212
209 def localpath(path):
213 def localpath(path):
210 return path.replace('/', '\\')
214 return path.replace('/', '\\')
211
215
212 def normpath(path):
216 def normpath(path):
213 return pconvert(os.path.normpath(path))
217 return pconvert(os.path.normpath(path))
214
218
215 makelock = _makelock_file
219 makelock = _makelock_file
216 readlock = _readlock_file
220 readlock = _readlock_file
217
221
218 def explain_exit(code):
222 def explain_exit(code):
219 return "exited with status %d" % code, code
223 return "exited with status %d" % code, code
220
224
221 else:
225 else:
222 nulldev = '/dev/null'
226 nulldev = '/dev/null'
223
227
224 def is_exec(f, last):
228 def is_exec(f, last):
225 return (os.stat(f).st_mode & 0100 != 0)
229 return (os.stat(f).st_mode & 0100 != 0)
226
230
227 def set_exec(f, mode):
231 def set_exec(f, mode):
228 s = os.stat(f).st_mode
232 s = os.stat(f).st_mode
229 if (s & 0100 != 0) == mode:
233 if (s & 0100 != 0) == mode:
230 return
234 return
231 if mode:
235 if mode:
232 # 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
233 # and obey umask.
237 # and obey umask.
234 umask = os.umask(0)
238 umask = os.umask(0)
235 os.umask(umask)
239 os.umask(umask)
236 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
240 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
237 else:
241 else:
238 os.chmod(f, s & 0666)
242 os.chmod(f, s & 0666)
239
243
240 def pconvert(path):
244 def pconvert(path):
241 return path
245 return path
242
246
243 def localpath(path):
247 def localpath(path):
244 return path
248 return path
245
249
246 normpath = os.path.normpath
250 normpath = os.path.normpath
247
251
248 def makelock(info, pathname):
252 def makelock(info, pathname):
249 try:
253 try:
250 os.symlink(info, pathname)
254 os.symlink(info, pathname)
251 except OSError, why:
255 except OSError, why:
252 if why.errno == errno.EEXIST:
256 if why.errno == errno.EEXIST:
253 raise
257 raise
254 else:
258 else:
255 _makelock_file(info, pathname)
259 _makelock_file(info, pathname)
256
260
257 def readlock(pathname):
261 def readlock(pathname):
258 try:
262 try:
259 return os.readlink(pathname)
263 return os.readlink(pathname)
260 except OSError, why:
264 except OSError, why:
261 if why.errno == errno.EINVAL:
265 if why.errno == errno.EINVAL:
262 return _readlock_file(pathname)
266 return _readlock_file(pathname)
263 else:
267 else:
264 raise
268 raise
265
269
266 def explain_exit(code):
270 def explain_exit(code):
267 """return a 2-tuple (desc, code) describing a process's status"""
271 """return a 2-tuple (desc, code) describing a process's status"""
268 if os.WIFEXITED(code):
272 if os.WIFEXITED(code):
269 val = os.WEXITSTATUS(code)
273 val = os.WEXITSTATUS(code)
270 return "exited with status %d" % val, val
274 return "exited with status %d" % val, val
271 elif os.WIFSIGNALED(code):
275 elif os.WIFSIGNALED(code):
272 val = os.WTERMSIG(code)
276 val = os.WTERMSIG(code)
273 return "killed by signal %d" % val, val
277 return "killed by signal %d" % val, val
274 elif os.WIFSTOPPED(code):
278 elif os.WIFSTOPPED(code):
275 val = os.STOPSIG(code)
279 val = os.STOPSIG(code)
276 return "stopped by signal %d" % val, val
280 return "stopped by signal %d" % val, val
277 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