##// END OF EJS Templates
fix handling of daylight saving time
Benoit Boissinot -
r1482:4d38b85e default
parent child Browse files
Show More
@@ -1,626 +1,628 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8
8
9 This contains helper routines that are independent of the SCM core and hide
9 This contains helper routines that are independent of the SCM core and hide
10 platform-specific details from the core.
10 platform-specific details from the core.
11 """
11 """
12
12
13 import os, errno
13 import os, errno
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import *
15 from demandload import *
16 demandload(globals(), "re cStringIO shutil popen2 tempfile threading time")
16 demandload(globals(), "re cStringIO shutil popen2 tempfile threading time")
17
17
18 def pipefilter(s, cmd):
18 def pipefilter(s, cmd):
19 '''filter string S through command CMD, returning its output'''
19 '''filter string S through command CMD, returning its output'''
20 (pout, pin) = popen2.popen2(cmd, -1, 'b')
20 (pout, pin) = popen2.popen2(cmd, -1, 'b')
21 def writer():
21 def writer():
22 pin.write(s)
22 pin.write(s)
23 pin.close()
23 pin.close()
24
24
25 # we should use select instead on UNIX, but this will work on most
25 # we should use select instead on UNIX, but this will work on most
26 # systems, including Windows
26 # systems, including Windows
27 w = threading.Thread(target=writer)
27 w = threading.Thread(target=writer)
28 w.start()
28 w.start()
29 f = pout.read()
29 f = pout.read()
30 pout.close()
30 pout.close()
31 w.join()
31 w.join()
32 return f
32 return f
33
33
34 def tempfilter(s, cmd):
34 def tempfilter(s, cmd):
35 '''filter string S through a pair of temporary files with CMD.
35 '''filter string S through a pair of temporary files with CMD.
36 CMD is used as a template to create the real command to be run,
36 CMD is used as a template to create the real command to be run,
37 with the strings INFILE and OUTFILE replaced by the real names of
37 with the strings INFILE and OUTFILE replaced by the real names of
38 the temporary files generated.'''
38 the temporary files generated.'''
39 inname, outname = None, None
39 inname, outname = None, None
40 try:
40 try:
41 infd, inname = tempfile.mkstemp(prefix='hgfin')
41 infd, inname = tempfile.mkstemp(prefix='hgfin')
42 fp = os.fdopen(infd, 'wb')
42 fp = os.fdopen(infd, 'wb')
43 fp.write(s)
43 fp.write(s)
44 fp.close()
44 fp.close()
45 outfd, outname = tempfile.mkstemp(prefix='hgfout')
45 outfd, outname = tempfile.mkstemp(prefix='hgfout')
46 os.close(outfd)
46 os.close(outfd)
47 cmd = cmd.replace('INFILE', inname)
47 cmd = cmd.replace('INFILE', inname)
48 cmd = cmd.replace('OUTFILE', outname)
48 cmd = cmd.replace('OUTFILE', outname)
49 code = os.system(cmd)
49 code = os.system(cmd)
50 if code: raise Abort(_("command '%s' failed: %s") %
50 if code: raise Abort(_("command '%s' failed: %s") %
51 (cmd, explain_exit(code)))
51 (cmd, explain_exit(code)))
52 return open(outname, 'rb').read()
52 return open(outname, 'rb').read()
53 finally:
53 finally:
54 try:
54 try:
55 if inname: os.unlink(inname)
55 if inname: os.unlink(inname)
56 except: pass
56 except: pass
57 try:
57 try:
58 if outname: os.unlink(outname)
58 if outname: os.unlink(outname)
59 except: pass
59 except: pass
60
60
61 filtertable = {
61 filtertable = {
62 'tempfile:': tempfilter,
62 'tempfile:': tempfilter,
63 'pipe:': pipefilter,
63 'pipe:': pipefilter,
64 }
64 }
65
65
66 def filter(s, cmd):
66 def filter(s, cmd):
67 "filter a string through a command that transforms its input to its output"
67 "filter a string through a command that transforms its input to its output"
68 for name, fn in filtertable.iteritems():
68 for name, fn in filtertable.iteritems():
69 if cmd.startswith(name):
69 if cmd.startswith(name):
70 return fn(s, cmd[len(name):].lstrip())
70 return fn(s, cmd[len(name):].lstrip())
71 return pipefilter(s, cmd)
71 return pipefilter(s, cmd)
72
72
73 def patch(strip, patchname, ui):
73 def patch(strip, patchname, ui):
74 """apply the patch <patchname> to the working directory.
74 """apply the patch <patchname> to the working directory.
75 a list of patched files is returned"""
75 a list of patched files is returned"""
76 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
76 fp = os.popen('patch -p%d < "%s"' % (strip, patchname))
77 files = {}
77 files = {}
78 for line in fp:
78 for line in fp:
79 line = line.rstrip()
79 line = line.rstrip()
80 ui.status("%s\n" % line)
80 ui.status("%s\n" % line)
81 if line.startswith('patching file '):
81 if line.startswith('patching file '):
82 pf = parse_patch_output(line)
82 pf = parse_patch_output(line)
83 files.setdefault(pf, 1)
83 files.setdefault(pf, 1)
84 code = fp.close()
84 code = fp.close()
85 if code:
85 if code:
86 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
86 raise Abort(_("patch command failed: %s") % explain_exit(code)[0])
87 return files.keys()
87 return files.keys()
88
88
89 def binary(s):
89 def binary(s):
90 """return true if a string is binary data using diff's heuristic"""
90 """return true if a string is binary data using diff's heuristic"""
91 if s and '\0' in s[:4096]:
91 if s and '\0' in s[:4096]:
92 return True
92 return True
93 return False
93 return False
94
94
95 def unique(g):
95 def unique(g):
96 """return the uniq elements of iterable g"""
96 """return the uniq elements of iterable g"""
97 seen = {}
97 seen = {}
98 for f in g:
98 for f in g:
99 if f not in seen:
99 if f not in seen:
100 seen[f] = 1
100 seen[f] = 1
101 yield f
101 yield f
102
102
103 class Abort(Exception):
103 class Abort(Exception):
104 """Raised if a command needs to print an error and exit."""
104 """Raised if a command needs to print an error and exit."""
105
105
106 def always(fn): return True
106 def always(fn): return True
107 def never(fn): return False
107 def never(fn): return False
108
108
109 def globre(pat, head='^', tail='$'):
109 def globre(pat, head='^', tail='$'):
110 "convert a glob pattern into a regexp"
110 "convert a glob pattern into a regexp"
111 i, n = 0, len(pat)
111 i, n = 0, len(pat)
112 res = ''
112 res = ''
113 group = False
113 group = False
114 def peek(): return i < n and pat[i]
114 def peek(): return i < n and pat[i]
115 while i < n:
115 while i < n:
116 c = pat[i]
116 c = pat[i]
117 i = i+1
117 i = i+1
118 if c == '*':
118 if c == '*':
119 if peek() == '*':
119 if peek() == '*':
120 i += 1
120 i += 1
121 res += '.*'
121 res += '.*'
122 else:
122 else:
123 res += '[^/]*'
123 res += '[^/]*'
124 elif c == '?':
124 elif c == '?':
125 res += '.'
125 res += '.'
126 elif c == '[':
126 elif c == '[':
127 j = i
127 j = i
128 if j < n and pat[j] in '!]':
128 if j < n and pat[j] in '!]':
129 j += 1
129 j += 1
130 while j < n and pat[j] != ']':
130 while j < n and pat[j] != ']':
131 j += 1
131 j += 1
132 if j >= n:
132 if j >= n:
133 res += '\\['
133 res += '\\['
134 else:
134 else:
135 stuff = pat[i:j].replace('\\','\\\\')
135 stuff = pat[i:j].replace('\\','\\\\')
136 i = j + 1
136 i = j + 1
137 if stuff[0] == '!':
137 if stuff[0] == '!':
138 stuff = '^' + stuff[1:]
138 stuff = '^' + stuff[1:]
139 elif stuff[0] == '^':
139 elif stuff[0] == '^':
140 stuff = '\\' + stuff
140 stuff = '\\' + stuff
141 res = '%s[%s]' % (res, stuff)
141 res = '%s[%s]' % (res, stuff)
142 elif c == '{':
142 elif c == '{':
143 group = True
143 group = True
144 res += '(?:'
144 res += '(?:'
145 elif c == '}' and group:
145 elif c == '}' and group:
146 res += ')'
146 res += ')'
147 group = False
147 group = False
148 elif c == ',' and group:
148 elif c == ',' and group:
149 res += '|'
149 res += '|'
150 else:
150 else:
151 res += re.escape(c)
151 res += re.escape(c)
152 return head + res + tail
152 return head + res + tail
153
153
154 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
154 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
155
155
156 def pathto(n1, n2):
156 def pathto(n1, n2):
157 '''return the relative path from one place to another.
157 '''return the relative path from one place to another.
158 this returns a path in the form used by the local filesystem, not hg.'''
158 this returns a path in the form used by the local filesystem, not hg.'''
159 if not n1: return localpath(n2)
159 if not n1: return localpath(n2)
160 a, b = n1.split('/'), n2.split('/')
160 a, b = n1.split('/'), n2.split('/')
161 a.reverse(), b.reverse()
161 a.reverse(), b.reverse()
162 while a and b and a[-1] == b[-1]:
162 while a and b and a[-1] == b[-1]:
163 a.pop(), b.pop()
163 a.pop(), b.pop()
164 b.reverse()
164 b.reverse()
165 return os.sep.join((['..'] * len(a)) + b)
165 return os.sep.join((['..'] * len(a)) + b)
166
166
167 def canonpath(root, cwd, myname):
167 def canonpath(root, cwd, myname):
168 """return the canonical path of myname, given cwd and root"""
168 """return the canonical path of myname, given cwd and root"""
169 rootsep = root + os.sep
169 rootsep = root + os.sep
170 name = myname
170 name = myname
171 if not name.startswith(os.sep):
171 if not name.startswith(os.sep):
172 name = os.path.join(root, cwd, name)
172 name = os.path.join(root, cwd, name)
173 name = os.path.normpath(name)
173 name = os.path.normpath(name)
174 if name.startswith(rootsep):
174 if name.startswith(rootsep):
175 return pconvert(name[len(rootsep):])
175 return pconvert(name[len(rootsep):])
176 elif name == root:
176 elif name == root:
177 return ''
177 return ''
178 else:
178 else:
179 raise Abort('%s not under root' % myname)
179 raise Abort('%s not under root' % myname)
180
180
181 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
181 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
182 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob')
182 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob')
183
183
184 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
184 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head=''):
185 if os.name == 'nt':
185 if os.name == 'nt':
186 dflt_pat = 'glob'
186 dflt_pat = 'glob'
187 else:
187 else:
188 dflt_pat = 'relpath'
188 dflt_pat = 'relpath'
189 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat)
189 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat)
190
190
191 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat):
191 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat):
192 """build a function to match a set of file patterns
192 """build a function to match a set of file patterns
193
193
194 arguments:
194 arguments:
195 canonroot - the canonical root of the tree you're matching against
195 canonroot - the canonical root of the tree you're matching against
196 cwd - the current working directory, if relevant
196 cwd - the current working directory, if relevant
197 names - patterns to find
197 names - patterns to find
198 inc - patterns to include
198 inc - patterns to include
199 exc - patterns to exclude
199 exc - patterns to exclude
200 head - a regex to prepend to patterns to control whether a match is rooted
200 head - a regex to prepend to patterns to control whether a match is rooted
201
201
202 a pattern is one of:
202 a pattern is one of:
203 'glob:<rooted glob>'
203 'glob:<rooted glob>'
204 're:<rooted regexp>'
204 're:<rooted regexp>'
205 'path:<rooted path>'
205 'path:<rooted path>'
206 'relglob:<relative glob>'
206 'relglob:<relative glob>'
207 'relpath:<relative path>'
207 'relpath:<relative path>'
208 'relre:<relative regexp>'
208 'relre:<relative regexp>'
209 '<rooted path or regexp>'
209 '<rooted path or regexp>'
210
210
211 returns:
211 returns:
212 a 3-tuple containing
212 a 3-tuple containing
213 - list of explicit non-pattern names passed in
213 - list of explicit non-pattern names passed in
214 - a bool match(filename) function
214 - a bool match(filename) function
215 - a bool indicating if any patterns were passed in
215 - a bool indicating if any patterns were passed in
216
216
217 todo:
217 todo:
218 make head regex a rooted bool
218 make head regex a rooted bool
219 """
219 """
220
220
221 def patkind(name, dflt_pat='glob'):
221 def patkind(name, dflt_pat='glob'):
222 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
222 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
223 if name.startswith(prefix + ':'): return name.split(':', 1)
223 if name.startswith(prefix + ':'): return name.split(':', 1)
224 return dflt_pat, name
224 return dflt_pat, name
225
225
226 def contains_glob(name):
226 def contains_glob(name):
227 for c in name:
227 for c in name:
228 if c in _globchars: return True
228 if c in _globchars: return True
229 return False
229 return False
230
230
231 def regex(kind, name, tail):
231 def regex(kind, name, tail):
232 '''convert a pattern into a regular expression'''
232 '''convert a pattern into a regular expression'''
233 if kind == 're':
233 if kind == 're':
234 return name
234 return name
235 elif kind == 'path':
235 elif kind == 'path':
236 return '^' + re.escape(name) + '(?:/|$)'
236 return '^' + re.escape(name) + '(?:/|$)'
237 elif kind == 'relglob':
237 elif kind == 'relglob':
238 return head + globre(name, '(?:|.*/)', tail)
238 return head + globre(name, '(?:|.*/)', tail)
239 elif kind == 'relpath':
239 elif kind == 'relpath':
240 return head + re.escape(name) + tail
240 return head + re.escape(name) + tail
241 elif kind == 'relre':
241 elif kind == 'relre':
242 if name.startswith('^'):
242 if name.startswith('^'):
243 return name
243 return name
244 return '.*' + name
244 return '.*' + name
245 return head + globre(name, '', tail)
245 return head + globre(name, '', tail)
246
246
247 def matchfn(pats, tail):
247 def matchfn(pats, tail):
248 """build a matching function from a set of patterns"""
248 """build a matching function from a set of patterns"""
249 if not pats:
249 if not pats:
250 return
250 return
251 matches = []
251 matches = []
252 for k, p in pats:
252 for k, p in pats:
253 try:
253 try:
254 pat = '(?:%s)' % regex(k, p, tail)
254 pat = '(?:%s)' % regex(k, p, tail)
255 matches.append(re.compile(pat).match)
255 matches.append(re.compile(pat).match)
256 except re.error, inst:
256 except re.error, inst:
257 raise Abort("invalid pattern: %s:%s" % (k, p))
257 raise Abort("invalid pattern: %s:%s" % (k, p))
258
258
259 def buildfn(text):
259 def buildfn(text):
260 for m in matches:
260 for m in matches:
261 r = m(text)
261 r = m(text)
262 if r:
262 if r:
263 return r
263 return r
264
264
265 return buildfn
265 return buildfn
266
266
267 def globprefix(pat):
267 def globprefix(pat):
268 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
268 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
269 root = []
269 root = []
270 for p in pat.split(os.sep):
270 for p in pat.split(os.sep):
271 if contains_glob(p): break
271 if contains_glob(p): break
272 root.append(p)
272 root.append(p)
273 return '/'.join(root)
273 return '/'.join(root)
274
274
275 pats = []
275 pats = []
276 files = []
276 files = []
277 roots = []
277 roots = []
278 for kind, name in [patkind(p, dflt_pat) for p in names]:
278 for kind, name in [patkind(p, dflt_pat) for p in names]:
279 if kind in ('glob', 'relpath'):
279 if kind in ('glob', 'relpath'):
280 name = canonpath(canonroot, cwd, name)
280 name = canonpath(canonroot, cwd, name)
281 if name == '':
281 if name == '':
282 kind, name = 'glob', '**'
282 kind, name = 'glob', '**'
283 if kind in ('glob', 'path', 're'):
283 if kind in ('glob', 'path', 're'):
284 pats.append((kind, name))
284 pats.append((kind, name))
285 if kind == 'glob':
285 if kind == 'glob':
286 root = globprefix(name)
286 root = globprefix(name)
287 if root: roots.append(root)
287 if root: roots.append(root)
288 elif kind == 'relpath':
288 elif kind == 'relpath':
289 files.append((kind, name))
289 files.append((kind, name))
290 roots.append(name)
290 roots.append(name)
291
291
292 patmatch = matchfn(pats, '$') or always
292 patmatch = matchfn(pats, '$') or always
293 filematch = matchfn(files, '(?:/|$)') or always
293 filematch = matchfn(files, '(?:/|$)') or always
294 incmatch = always
294 incmatch = always
295 if inc:
295 if inc:
296 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
296 incmatch = matchfn(map(patkind, inc), '(?:/|$)')
297 excmatch = lambda fn: False
297 excmatch = lambda fn: False
298 if exc:
298 if exc:
299 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
299 excmatch = matchfn(map(patkind, exc), '(?:/|$)')
300
300
301 return (roots,
301 return (roots,
302 lambda fn: (incmatch(fn) and not excmatch(fn) and
302 lambda fn: (incmatch(fn) and not excmatch(fn) and
303 (fn.endswith('/') or
303 (fn.endswith('/') or
304 (not pats and not files) or
304 (not pats and not files) or
305 (pats and patmatch(fn)) or
305 (pats and patmatch(fn)) or
306 (files and filematch(fn)))),
306 (files and filematch(fn)))),
307 (inc or exc or (pats and pats != [('glob', '**')])) and True)
307 (inc or exc or (pats and pats != [('glob', '**')])) and True)
308
308
309 def system(cmd, errprefix=None):
309 def system(cmd, errprefix=None):
310 """execute a shell command that must succeed"""
310 """execute a shell command that must succeed"""
311 rc = os.system(cmd)
311 rc = os.system(cmd)
312 if rc:
312 if rc:
313 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
313 errmsg = "%s %s" % (os.path.basename(cmd.split(None, 1)[0]),
314 explain_exit(rc)[0])
314 explain_exit(rc)[0])
315 if errprefix:
315 if errprefix:
316 errmsg = "%s: %s" % (errprefix, errmsg)
316 errmsg = "%s: %s" % (errprefix, errmsg)
317 raise Abort(errmsg)
317 raise Abort(errmsg)
318
318
319 def rename(src, dst):
319 def rename(src, dst):
320 """forcibly rename a file"""
320 """forcibly rename a file"""
321 try:
321 try:
322 os.rename(src, dst)
322 os.rename(src, dst)
323 except:
323 except:
324 os.unlink(dst)
324 os.unlink(dst)
325 os.rename(src, dst)
325 os.rename(src, dst)
326
326
327 def unlink(f):
327 def unlink(f):
328 """unlink and remove the directory if it is empty"""
328 """unlink and remove the directory if it is empty"""
329 os.unlink(f)
329 os.unlink(f)
330 # try removing directories that might now be empty
330 # try removing directories that might now be empty
331 try: os.removedirs(os.path.dirname(f))
331 try: os.removedirs(os.path.dirname(f))
332 except: pass
332 except: pass
333
333
334 def copyfiles(src, dst, hardlink=None):
334 def copyfiles(src, dst, hardlink=None):
335 """Copy a directory tree using hardlinks if possible"""
335 """Copy a directory tree using hardlinks if possible"""
336
336
337 if hardlink is None:
337 if hardlink is None:
338 hardlink = (os.stat(src).st_dev ==
338 hardlink = (os.stat(src).st_dev ==
339 os.stat(os.path.dirname(dst)).st_dev)
339 os.stat(os.path.dirname(dst)).st_dev)
340
340
341 if os.path.isdir(src):
341 if os.path.isdir(src):
342 os.mkdir(dst)
342 os.mkdir(dst)
343 for name in os.listdir(src):
343 for name in os.listdir(src):
344 srcname = os.path.join(src, name)
344 srcname = os.path.join(src, name)
345 dstname = os.path.join(dst, name)
345 dstname = os.path.join(dst, name)
346 copyfiles(srcname, dstname, hardlink)
346 copyfiles(srcname, dstname, hardlink)
347 else:
347 else:
348 if hardlink:
348 if hardlink:
349 try:
349 try:
350 os_link(src, dst)
350 os_link(src, dst)
351 except:
351 except:
352 hardlink = False
352 hardlink = False
353 shutil.copy2(src, dst)
353 shutil.copy2(src, dst)
354 else:
354 else:
355 shutil.copy2(src, dst)
355 shutil.copy2(src, dst)
356
356
357 def opener(base):
357 def opener(base):
358 """
358 """
359 return a function that opens files relative to base
359 return a function that opens files relative to base
360
360
361 this function is used to hide the details of COW semantics and
361 this function is used to hide the details of COW semantics and
362 remote file access from higher level code.
362 remote file access from higher level code.
363 """
363 """
364 p = base
364 p = base
365 def o(path, mode="r", text=False):
365 def o(path, mode="r", text=False):
366 f = os.path.join(p, path)
366 f = os.path.join(p, path)
367
367
368 if not text:
368 if not text:
369 mode += "b" # for that other OS
369 mode += "b" # for that other OS
370
370
371 if mode[0] != "r":
371 if mode[0] != "r":
372 try:
372 try:
373 nlink = nlinks(f)
373 nlink = nlinks(f)
374 except OSError:
374 except OSError:
375 d = os.path.dirname(f)
375 d = os.path.dirname(f)
376 if not os.path.isdir(d):
376 if not os.path.isdir(d):
377 os.makedirs(d)
377 os.makedirs(d)
378 else:
378 else:
379 if nlink > 1:
379 if nlink > 1:
380 d, fn = os.path.split(f)
380 d, fn = os.path.split(f)
381 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
381 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
382 fp = os.fdopen(fd, "wb")
382 fp = os.fdopen(fd, "wb")
383 try:
383 try:
384 fp.write(file(f, "rb").read())
384 fp.write(file(f, "rb").read())
385 except:
385 except:
386 try: os.unlink(temp)
386 try: os.unlink(temp)
387 except: pass
387 except: pass
388 raise
388 raise
389 fp.close()
389 fp.close()
390 rename(temp, f)
390 rename(temp, f)
391
391
392 return file(f, mode)
392 return file(f, mode)
393
393
394 return o
394 return o
395
395
396 def _makelock_file(info, pathname):
396 def _makelock_file(info, pathname):
397 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
397 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
398 os.write(ld, info)
398 os.write(ld, info)
399 os.close(ld)
399 os.close(ld)
400
400
401 def _readlock_file(pathname):
401 def _readlock_file(pathname):
402 return file(pathname).read()
402 return file(pathname).read()
403
403
404 def nlinks(pathname):
404 def nlinks(pathname):
405 """Return number of hardlinks for the given file."""
405 """Return number of hardlinks for the given file."""
406 return os.stat(pathname).st_nlink
406 return os.stat(pathname).st_nlink
407
407
408 if hasattr(os, 'link'):
408 if hasattr(os, 'link'):
409 os_link = os.link
409 os_link = os.link
410 else:
410 else:
411 def os_link(src, dst):
411 def os_link(src, dst):
412 raise OSError(0, _("Hardlinks not supported"))
412 raise OSError(0, _("Hardlinks not supported"))
413
413
414 # Platform specific variants
414 # Platform specific variants
415 if os.name == 'nt':
415 if os.name == 'nt':
416 demandload(globals(), "msvcrt")
416 demandload(globals(), "msvcrt")
417 nulldev = 'NUL:'
417 nulldev = 'NUL:'
418
418
419 try:
419 try:
420 import win32api, win32process
420 import win32api, win32process
421 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
421 filename = win32process.GetModuleFileNameEx(win32api.GetCurrentProcess(), 0)
422 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
422 systemrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
423
423
424 except ImportError:
424 except ImportError:
425 systemrc = r'c:\mercurial\mercurial.ini'
425 systemrc = r'c:\mercurial\mercurial.ini'
426 pass
426 pass
427
427
428 rcpath = (systemrc,
428 rcpath = (systemrc,
429 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
429 os.path.join(os.path.expanduser('~'), 'mercurial.ini'))
430
430
431 def parse_patch_output(output_line):
431 def parse_patch_output(output_line):
432 """parses the output produced by patch and returns the file name"""
432 """parses the output produced by patch and returns the file name"""
433 pf = output_line[14:]
433 pf = output_line[14:]
434 if pf[0] == '`':
434 if pf[0] == '`':
435 pf = pf[1:-1] # Remove the quotes
435 pf = pf[1:-1] # Remove the quotes
436 return pf
436 return pf
437
437
438 try: # ActivePython can create hard links using win32file module
438 try: # ActivePython can create hard links using win32file module
439 import win32file
439 import win32file
440
440
441 def os_link(src, dst): # NB will only succeed on NTFS
441 def os_link(src, dst): # NB will only succeed on NTFS
442 win32file.CreateHardLink(dst, src)
442 win32file.CreateHardLink(dst, src)
443
443
444 def nlinks(pathname):
444 def nlinks(pathname):
445 """Return number of hardlinks for the given file."""
445 """Return number of hardlinks for the given file."""
446 try:
446 try:
447 fh = win32file.CreateFile(pathname,
447 fh = win32file.CreateFile(pathname,
448 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
448 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
449 None, win32file.OPEN_EXISTING, 0, None)
449 None, win32file.OPEN_EXISTING, 0, None)
450 res = win32file.GetFileInformationByHandle(fh)
450 res = win32file.GetFileInformationByHandle(fh)
451 fh.Close()
451 fh.Close()
452 return res[7]
452 return res[7]
453 except:
453 except:
454 return os.stat(pathname).st_nlink
454 return os.stat(pathname).st_nlink
455
455
456 except ImportError:
456 except ImportError:
457 pass
457 pass
458
458
459 def is_exec(f, last):
459 def is_exec(f, last):
460 return last
460 return last
461
461
462 def set_exec(f, mode):
462 def set_exec(f, mode):
463 pass
463 pass
464
464
465 def set_binary(fd):
465 def set_binary(fd):
466 msvcrt.setmode(fd.fileno(), os.O_BINARY)
466 msvcrt.setmode(fd.fileno(), os.O_BINARY)
467
467
468 def pconvert(path):
468 def pconvert(path):
469 return path.replace("\\", "/")
469 return path.replace("\\", "/")
470
470
471 def localpath(path):
471 def localpath(path):
472 return path.replace('/', '\\')
472 return path.replace('/', '\\')
473
473
474 def normpath(path):
474 def normpath(path):
475 return pconvert(os.path.normpath(path))
475 return pconvert(os.path.normpath(path))
476
476
477 makelock = _makelock_file
477 makelock = _makelock_file
478 readlock = _readlock_file
478 readlock = _readlock_file
479
479
480 def explain_exit(code):
480 def explain_exit(code):
481 return _("exited with status %d") % code, code
481 return _("exited with status %d") % code, code
482
482
483 else:
483 else:
484 nulldev = '/dev/null'
484 nulldev = '/dev/null'
485
485
486 hgrcd = '/etc/mercurial/hgrc.d'
486 hgrcd = '/etc/mercurial/hgrc.d'
487 hgrcs = []
487 hgrcs = []
488 if os.path.isdir(hgrcd):
488 if os.path.isdir(hgrcd):
489 hgrcs = [f for f in os.listdir(hgrcd) if f.endswith(".rc")]
489 hgrcs = [f for f in os.listdir(hgrcd) if f.endswith(".rc")]
490 rcpath = map(os.path.normpath, hgrcs +
490 rcpath = map(os.path.normpath, hgrcs +
491 ['/etc/mercurial/hgrc', os.path.expanduser('~/.hgrc')])
491 ['/etc/mercurial/hgrc', os.path.expanduser('~/.hgrc')])
492
492
493 def parse_patch_output(output_line):
493 def parse_patch_output(output_line):
494 """parses the output produced by patch and returns the file name"""
494 """parses the output produced by patch and returns the file name"""
495 return output_line[14:]
495 return output_line[14:]
496
496
497 def is_exec(f, last):
497 def is_exec(f, last):
498 """check whether a file is executable"""
498 """check whether a file is executable"""
499 return (os.stat(f).st_mode & 0100 != 0)
499 return (os.stat(f).st_mode & 0100 != 0)
500
500
501 def set_exec(f, mode):
501 def set_exec(f, mode):
502 s = os.stat(f).st_mode
502 s = os.stat(f).st_mode
503 if (s & 0100 != 0) == mode:
503 if (s & 0100 != 0) == mode:
504 return
504 return
505 if mode:
505 if mode:
506 # Turn on +x for every +r bit when making a file executable
506 # Turn on +x for every +r bit when making a file executable
507 # and obey umask.
507 # and obey umask.
508 umask = os.umask(0)
508 umask = os.umask(0)
509 os.umask(umask)
509 os.umask(umask)
510 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
510 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
511 else:
511 else:
512 os.chmod(f, s & 0666)
512 os.chmod(f, s & 0666)
513
513
514 def set_binary(fd):
514 def set_binary(fd):
515 pass
515 pass
516
516
517 def pconvert(path):
517 def pconvert(path):
518 return path
518 return path
519
519
520 def localpath(path):
520 def localpath(path):
521 return path
521 return path
522
522
523 normpath = os.path.normpath
523 normpath = os.path.normpath
524
524
525 def makelock(info, pathname):
525 def makelock(info, pathname):
526 try:
526 try:
527 os.symlink(info, pathname)
527 os.symlink(info, pathname)
528 except OSError, why:
528 except OSError, why:
529 if why.errno == errno.EEXIST:
529 if why.errno == errno.EEXIST:
530 raise
530 raise
531 else:
531 else:
532 _makelock_file(info, pathname)
532 _makelock_file(info, pathname)
533
533
534 def readlock(pathname):
534 def readlock(pathname):
535 try:
535 try:
536 return os.readlink(pathname)
536 return os.readlink(pathname)
537 except OSError, why:
537 except OSError, why:
538 if why.errno == errno.EINVAL:
538 if why.errno == errno.EINVAL:
539 return _readlock_file(pathname)
539 return _readlock_file(pathname)
540 else:
540 else:
541 raise
541 raise
542
542
543 def explain_exit(code):
543 def explain_exit(code):
544 """return a 2-tuple (desc, code) describing a process's status"""
544 """return a 2-tuple (desc, code) describing a process's status"""
545 if os.WIFEXITED(code):
545 if os.WIFEXITED(code):
546 val = os.WEXITSTATUS(code)
546 val = os.WEXITSTATUS(code)
547 return _("exited with status %d") % val, val
547 return _("exited with status %d") % val, val
548 elif os.WIFSIGNALED(code):
548 elif os.WIFSIGNALED(code):
549 val = os.WTERMSIG(code)
549 val = os.WTERMSIG(code)
550 return _("killed by signal %d") % val, val
550 return _("killed by signal %d") % val, val
551 elif os.WIFSTOPPED(code):
551 elif os.WIFSTOPPED(code):
552 val = os.WSTOPSIG(code)
552 val = os.WSTOPSIG(code)
553 return _("stopped by signal %d") % val, val
553 return _("stopped by signal %d") % val, val
554 raise ValueError(_("invalid exit code"))
554 raise ValueError(_("invalid exit code"))
555
555
556 class chunkbuffer(object):
556 class chunkbuffer(object):
557 """Allow arbitrary sized chunks of data to be efficiently read from an
557 """Allow arbitrary sized chunks of data to be efficiently read from an
558 iterator over chunks of arbitrary size."""
558 iterator over chunks of arbitrary size."""
559
559
560 def __init__(self, in_iter, targetsize = 2**16):
560 def __init__(self, in_iter, targetsize = 2**16):
561 """in_iter is the iterator that's iterating over the input chunks.
561 """in_iter is the iterator that's iterating over the input chunks.
562 targetsize is how big a buffer to try to maintain."""
562 targetsize is how big a buffer to try to maintain."""
563 self.in_iter = iter(in_iter)
563 self.in_iter = iter(in_iter)
564 self.buf = ''
564 self.buf = ''
565 self.targetsize = int(targetsize)
565 self.targetsize = int(targetsize)
566 if self.targetsize <= 0:
566 if self.targetsize <= 0:
567 raise ValueError(_("targetsize must be greater than 0, was %d") %
567 raise ValueError(_("targetsize must be greater than 0, was %d") %
568 targetsize)
568 targetsize)
569 self.iterempty = False
569 self.iterempty = False
570
570
571 def fillbuf(self):
571 def fillbuf(self):
572 """Ignore target size; read every chunk from iterator until empty."""
572 """Ignore target size; read every chunk from iterator until empty."""
573 if not self.iterempty:
573 if not self.iterempty:
574 collector = cStringIO.StringIO()
574 collector = cStringIO.StringIO()
575 collector.write(self.buf)
575 collector.write(self.buf)
576 for ch in self.in_iter:
576 for ch in self.in_iter:
577 collector.write(ch)
577 collector.write(ch)
578 self.buf = collector.getvalue()
578 self.buf = collector.getvalue()
579 self.iterempty = True
579 self.iterempty = True
580
580
581 def read(self, l):
581 def read(self, l):
582 """Read L bytes of data from the iterator of chunks of data.
582 """Read L bytes of data from the iterator of chunks of data.
583 Returns less than L bytes if the iterator runs dry."""
583 Returns less than L bytes if the iterator runs dry."""
584 if l > len(self.buf) and not self.iterempty:
584 if l > len(self.buf) and not self.iterempty:
585 # Clamp to a multiple of self.targetsize
585 # Clamp to a multiple of self.targetsize
586 targetsize = self.targetsize * ((l // self.targetsize) + 1)
586 targetsize = self.targetsize * ((l // self.targetsize) + 1)
587 collector = cStringIO.StringIO()
587 collector = cStringIO.StringIO()
588 collector.write(self.buf)
588 collector.write(self.buf)
589 collected = len(self.buf)
589 collected = len(self.buf)
590 for chunk in self.in_iter:
590 for chunk in self.in_iter:
591 collector.write(chunk)
591 collector.write(chunk)
592 collected += len(chunk)
592 collected += len(chunk)
593 if collected >= targetsize:
593 if collected >= targetsize:
594 break
594 break
595 if collected < targetsize:
595 if collected < targetsize:
596 self.iterempty = True
596 self.iterempty = True
597 self.buf = collector.getvalue()
597 self.buf = collector.getvalue()
598 s, self.buf = self.buf[:l], buffer(self.buf, l)
598 s, self.buf = self.buf[:l], buffer(self.buf, l)
599 return s
599 return s
600
600
601 def filechunkiter(f, size = 65536):
601 def filechunkiter(f, size = 65536):
602 """Create a generator that produces all the data in the file size
602 """Create a generator that produces all the data in the file size
603 (default 65536) bytes at a time. Chunks may be less than size
603 (default 65536) bytes at a time. Chunks may be less than size
604 bytes if the chunk is the last chunk in the file, or the file is a
604 bytes if the chunk is the last chunk in the file, or the file is a
605 socket or some other type of file that sometimes reads less data
605 socket or some other type of file that sometimes reads less data
606 than is requested."""
606 than is requested."""
607 s = f.read(size)
607 s = f.read(size)
608 while len(s) > 0:
608 while len(s) > 0:
609 yield s
609 yield s
610 s = f.read(size)
610 s = f.read(size)
611
611
612 def makedate():
612 def makedate():
613 t = time.time()
613 lt = time.localtime()
614 if time.daylight: tz = time.altzone
614 if lt[8] == 1 and time.daylight:
615 else: tz = time.timezone
615 tz = time.altzone
616 return t, tz
616 else:
617 tz = time.timezone
618 return time.mktime(lt), tz
617
619
618 def datestr(date=None, format='%c'):
620 def datestr(date=None, format='%c'):
619 """represent a (unixtime, offset) tuple as a localized time.
621 """represent a (unixtime, offset) tuple as a localized time.
620 unixtime is seconds since the epoch, and offset is the time zone's
622 unixtime is seconds since the epoch, and offset is the time zone's
621 number of seconds away from UTC."""
623 number of seconds away from UTC."""
622 t, tz = date or makedate()
624 t, tz = date or makedate()
623 return ("%s %+03d%02d" %
625 return ("%s %+03d%02d" %
624 (time.strftime(format, time.gmtime(float(t) - tz)),
626 (time.strftime(format, time.gmtime(float(t) - tz)),
625 -tz / 3600,
627 -tz / 3600,
626 ((-tz % 3600) / 60)))
628 ((-tz % 3600) / 60)))
General Comments 0
You need to be logged in to leave comments. Login now