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