##// END OF EJS Templates
windows: import WinIOError from win32 module (issue1707)...
Dirkjan Ochtman -
r8930:ae275ad4 default
parent child Browse files
Show More
@@ -1,284 +1,285 b''
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import osutil, error
9 import osutil, error
10 import errno, msvcrt, os, re, sys
10 import errno, msvcrt, os, re, sys
11 from mercurial import win32
11
12
12 nulldev = 'NUL:'
13 nulldev = 'NUL:'
13 umask = 002
14 umask = 002
14
15
15 # wrap osutil.posixfile to provide friendlier exceptions
16 # wrap osutil.posixfile to provide friendlier exceptions
16 def posixfile(name, mode='r', buffering=-1):
17 def posixfile(name, mode='r', buffering=-1):
17 try:
18 try:
18 return osutil.posixfile(name, mode, buffering)
19 return osutil.posixfile(name, mode, buffering)
19 except WindowsError, err:
20 except WindowsError, err:
20 raise WinIOError(err)
21 raise win32.WinIOError(err)
21 posixfile.__doc__ = osutil.posixfile.__doc__
22 posixfile.__doc__ = osutil.posixfile.__doc__
22
23
23 class winstdout(object):
24 class winstdout(object):
24 '''stdout on windows misbehaves if sent through a pipe'''
25 '''stdout on windows misbehaves if sent through a pipe'''
25
26
26 def __init__(self, fp):
27 def __init__(self, fp):
27 self.fp = fp
28 self.fp = fp
28
29
29 def __getattr__(self, key):
30 def __getattr__(self, key):
30 return getattr(self.fp, key)
31 return getattr(self.fp, key)
31
32
32 def close(self):
33 def close(self):
33 try:
34 try:
34 self.fp.close()
35 self.fp.close()
35 except: pass
36 except: pass
36
37
37 def write(self, s):
38 def write(self, s):
38 try:
39 try:
39 # This is workaround for "Not enough space" error on
40 # This is workaround for "Not enough space" error on
40 # writing large size of data to console.
41 # writing large size of data to console.
41 limit = 16000
42 limit = 16000
42 l = len(s)
43 l = len(s)
43 start = 0
44 start = 0
44 while start < l:
45 while start < l:
45 end = start + limit
46 end = start + limit
46 self.fp.write(s[start:end])
47 self.fp.write(s[start:end])
47 start = end
48 start = end
48 except IOError, inst:
49 except IOError, inst:
49 if inst.errno != 0: raise
50 if inst.errno != 0: raise
50 self.close()
51 self.close()
51 raise IOError(errno.EPIPE, 'Broken pipe')
52 raise IOError(errno.EPIPE, 'Broken pipe')
52
53
53 def flush(self):
54 def flush(self):
54 try:
55 try:
55 return self.fp.flush()
56 return self.fp.flush()
56 except IOError, inst:
57 except IOError, inst:
57 if inst.errno != errno.EINVAL: raise
58 if inst.errno != errno.EINVAL: raise
58 self.close()
59 self.close()
59 raise IOError(errno.EPIPE, 'Broken pipe')
60 raise IOError(errno.EPIPE, 'Broken pipe')
60
61
61 sys.stdout = winstdout(sys.stdout)
62 sys.stdout = winstdout(sys.stdout)
62
63
63 def _is_win_9x():
64 def _is_win_9x():
64 '''return true if run on windows 95, 98 or me.'''
65 '''return true if run on windows 95, 98 or me.'''
65 try:
66 try:
66 return sys.getwindowsversion()[3] == 1
67 return sys.getwindowsversion()[3] == 1
67 except AttributeError:
68 except AttributeError:
68 return 'command' in os.environ.get('comspec', '')
69 return 'command' in os.environ.get('comspec', '')
69
70
70 def openhardlinks():
71 def openhardlinks():
71 return not _is_win_9x() and "win32api" in globals()
72 return not _is_win_9x() and "win32api" in globals()
72
73
73 def system_rcpath():
74 def system_rcpath():
74 try:
75 try:
75 return system_rcpath_win32()
76 return system_rcpath_win32()
76 except:
77 except:
77 return [r'c:\mercurial\mercurial.ini']
78 return [r'c:\mercurial\mercurial.ini']
78
79
79 def user_rcpath():
80 def user_rcpath():
80 '''return os-specific hgrc search path to the user dir'''
81 '''return os-specific hgrc search path to the user dir'''
81 try:
82 try:
82 path = user_rcpath_win32()
83 path = user_rcpath_win32()
83 except:
84 except:
84 home = os.path.expanduser('~')
85 home = os.path.expanduser('~')
85 path = [os.path.join(home, 'mercurial.ini'),
86 path = [os.path.join(home, 'mercurial.ini'),
86 os.path.join(home, '.hgrc')]
87 os.path.join(home, '.hgrc')]
87 userprofile = os.environ.get('USERPROFILE')
88 userprofile = os.environ.get('USERPROFILE')
88 if userprofile:
89 if userprofile:
89 path.append(os.path.join(userprofile, 'mercurial.ini'))
90 path.append(os.path.join(userprofile, 'mercurial.ini'))
90 path.append(os.path.join(userprofile, '.hgrc'))
91 path.append(os.path.join(userprofile, '.hgrc'))
91 return path
92 return path
92
93
93 def parse_patch_output(output_line):
94 def parse_patch_output(output_line):
94 """parses the output produced by patch and returns the filename"""
95 """parses the output produced by patch and returns the filename"""
95 pf = output_line[14:]
96 pf = output_line[14:]
96 if pf[0] == '`':
97 if pf[0] == '`':
97 pf = pf[1:-1] # Remove the quotes
98 pf = pf[1:-1] # Remove the quotes
98 return pf
99 return pf
99
100
100 def sshargs(sshcmd, host, user, port):
101 def sshargs(sshcmd, host, user, port):
101 '''Build argument list for ssh or Plink'''
102 '''Build argument list for ssh or Plink'''
102 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
103 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
103 args = user and ("%s@%s" % (user, host)) or host
104 args = user and ("%s@%s" % (user, host)) or host
104 return port and ("%s %s %s" % (args, pflag, port)) or args
105 return port and ("%s %s %s" % (args, pflag, port)) or args
105
106
106 def testpid(pid):
107 def testpid(pid):
107 '''return False if pid dead, True if running or not known'''
108 '''return False if pid dead, True if running or not known'''
108 return True
109 return True
109
110
110 def set_flags(f, l, x):
111 def set_flags(f, l, x):
111 pass
112 pass
112
113
113 def set_binary(fd):
114 def set_binary(fd):
114 # When run without console, pipes may expose invalid
115 # When run without console, pipes may expose invalid
115 # fileno(), usually set to -1.
116 # fileno(), usually set to -1.
116 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
117 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
117 msvcrt.setmode(fd.fileno(), os.O_BINARY)
118 msvcrt.setmode(fd.fileno(), os.O_BINARY)
118
119
119 def pconvert(path):
120 def pconvert(path):
120 return '/'.join(path.split(os.sep))
121 return '/'.join(path.split(os.sep))
121
122
122 def localpath(path):
123 def localpath(path):
123 return path.replace('/', '\\')
124 return path.replace('/', '\\')
124
125
125 def normpath(path):
126 def normpath(path):
126 return pconvert(os.path.normpath(path))
127 return pconvert(os.path.normpath(path))
127
128
128 def samestat(s1, s2):
129 def samestat(s1, s2):
129 return False
130 return False
130
131
131 # A sequence of backslashes is special iff it precedes a double quote:
132 # A sequence of backslashes is special iff it precedes a double quote:
132 # - if there's an even number of backslashes, the double quote is not
133 # - if there's an even number of backslashes, the double quote is not
133 # quoted (i.e. it ends the quoted region)
134 # quoted (i.e. it ends the quoted region)
134 # - if there's an odd number of backslashes, the double quote is quoted
135 # - if there's an odd number of backslashes, the double quote is quoted
135 # - in both cases, every pair of backslashes is unquoted into a single
136 # - in both cases, every pair of backslashes is unquoted into a single
136 # backslash
137 # backslash
137 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
138 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
138 # So, to quote a string, we must surround it in double quotes, double
139 # So, to quote a string, we must surround it in double quotes, double
139 # the number of backslashes that preceed double quotes and add another
140 # the number of backslashes that preceed double quotes and add another
140 # backslash before every double quote (being careful with the double
141 # backslash before every double quote (being careful with the double
141 # quote we've appended to the end)
142 # quote we've appended to the end)
142 _quotere = None
143 _quotere = None
143 def shellquote(s):
144 def shellquote(s):
144 global _quotere
145 global _quotere
145 if _quotere is None:
146 if _quotere is None:
146 _quotere = re.compile(r'(\\*)("|\\$)')
147 _quotere = re.compile(r'(\\*)("|\\$)')
147 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
148 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
148
149
149 def quotecommand(cmd):
150 def quotecommand(cmd):
150 """Build a command string suitable for os.popen* calls."""
151 """Build a command string suitable for os.popen* calls."""
151 # The extra quotes are needed because popen* runs the command
152 # The extra quotes are needed because popen* runs the command
152 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
153 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
153 return '"' + cmd + '"'
154 return '"' + cmd + '"'
154
155
155 def popen(command, mode='r'):
156 def popen(command, mode='r'):
156 # Work around "popen spawned process may not write to stdout
157 # Work around "popen spawned process may not write to stdout
157 # under windows"
158 # under windows"
158 # http://bugs.python.org/issue1366
159 # http://bugs.python.org/issue1366
159 command += " 2> %s" % nulldev
160 command += " 2> %s" % nulldev
160 return os.popen(quotecommand(command), mode)
161 return os.popen(quotecommand(command), mode)
161
162
162 def explain_exit(code):
163 def explain_exit(code):
163 return _("exited with status %d") % code, code
164 return _("exited with status %d") % code, code
164
165
165 # if you change this stub into a real check, please try to implement the
166 # if you change this stub into a real check, please try to implement the
166 # username and groupname functions above, too.
167 # username and groupname functions above, too.
167 def isowner(st):
168 def isowner(st):
168 return True
169 return True
169
170
170 def find_exe(command):
171 def find_exe(command):
171 '''Find executable for command searching like cmd.exe does.
172 '''Find executable for command searching like cmd.exe does.
172 If command is a basename then PATH is searched for command.
173 If command is a basename then PATH is searched for command.
173 PATH isn't searched if command is an absolute or relative path.
174 PATH isn't searched if command is an absolute or relative path.
174 An extension from PATHEXT is found and added if not present.
175 An extension from PATHEXT is found and added if not present.
175 If command isn't found None is returned.'''
176 If command isn't found None is returned.'''
176 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
177 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
177 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
178 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
178 if os.path.splitext(command)[1].lower() in pathexts:
179 if os.path.splitext(command)[1].lower() in pathexts:
179 pathexts = ['']
180 pathexts = ['']
180
181
181 def findexisting(pathcommand):
182 def findexisting(pathcommand):
182 'Will append extension (if needed) and return existing file'
183 'Will append extension (if needed) and return existing file'
183 for ext in pathexts:
184 for ext in pathexts:
184 executable = pathcommand + ext
185 executable = pathcommand + ext
185 if os.path.exists(executable):
186 if os.path.exists(executable):
186 return executable
187 return executable
187 return None
188 return None
188
189
189 if os.sep in command:
190 if os.sep in command:
190 return findexisting(command)
191 return findexisting(command)
191
192
192 for path in os.environ.get('PATH', '').split(os.pathsep):
193 for path in os.environ.get('PATH', '').split(os.pathsep):
193 executable = findexisting(os.path.join(path, command))
194 executable = findexisting(os.path.join(path, command))
194 if executable is not None:
195 if executable is not None:
195 return executable
196 return executable
196 return None
197 return None
197
198
198 def set_signal_handler():
199 def set_signal_handler():
199 try:
200 try:
200 set_signal_handler_win32()
201 set_signal_handler_win32()
201 except NameError:
202 except NameError:
202 pass
203 pass
203
204
204 def statfiles(files):
205 def statfiles(files):
205 '''Stat each file in files and yield stat or None if file does not exist.
206 '''Stat each file in files and yield stat or None if file does not exist.
206 Cluster and cache stat per directory to minimize number of OS stat calls.'''
207 Cluster and cache stat per directory to minimize number of OS stat calls.'''
207 ncase = os.path.normcase
208 ncase = os.path.normcase
208 sep = os.sep
209 sep = os.sep
209 dircache = {} # dirname -> filename -> status | None if file does not exist
210 dircache = {} # dirname -> filename -> status | None if file does not exist
210 for nf in files:
211 for nf in files:
211 nf = ncase(nf)
212 nf = ncase(nf)
212 pos = nf.rfind(sep)
213 pos = nf.rfind(sep)
213 if pos == -1:
214 if pos == -1:
214 dir, base = '.', nf
215 dir, base = '.', nf
215 else:
216 else:
216 dir, base = nf[:pos+1], nf[pos+1:]
217 dir, base = nf[:pos+1], nf[pos+1:]
217 cache = dircache.get(dir, None)
218 cache = dircache.get(dir, None)
218 if cache is None:
219 if cache is None:
219 try:
220 try:
220 dmap = dict([(ncase(n), s)
221 dmap = dict([(ncase(n), s)
221 for n, k, s in osutil.listdir(dir, True)])
222 for n, k, s in osutil.listdir(dir, True)])
222 except OSError, err:
223 except OSError, err:
223 # handle directory not found in Python version prior to 2.5
224 # handle directory not found in Python version prior to 2.5
224 # Python <= 2.4 returns native Windows code 3 in errno
225 # Python <= 2.4 returns native Windows code 3 in errno
225 # Python >= 2.5 returns ENOENT and adds winerror field
226 # Python >= 2.5 returns ENOENT and adds winerror field
226 # EINVAL is raised if dir is not a directory.
227 # EINVAL is raised if dir is not a directory.
227 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
228 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
228 errno.ENOTDIR):
229 errno.ENOTDIR):
229 raise
230 raise
230 dmap = {}
231 dmap = {}
231 cache = dircache.setdefault(dir, dmap)
232 cache = dircache.setdefault(dir, dmap)
232 yield cache.get(base, None)
233 yield cache.get(base, None)
233
234
234 def getuser():
235 def getuser():
235 '''return name of current user'''
236 '''return name of current user'''
236 raise error.Abort(_('user name not available - set USERNAME '
237 raise error.Abort(_('user name not available - set USERNAME '
237 'environment variable'))
238 'environment variable'))
238
239
239 def username(uid=None):
240 def username(uid=None):
240 """Return the name of the user with the given uid.
241 """Return the name of the user with the given uid.
241
242
242 If uid is None, return the name of the current user."""
243 If uid is None, return the name of the current user."""
243 return None
244 return None
244
245
245 def groupname(gid=None):
246 def groupname(gid=None):
246 """Return the name of the group with the given gid.
247 """Return the name of the group with the given gid.
247
248
248 If gid is None, return the name of the current group."""
249 If gid is None, return the name of the current group."""
249 return None
250 return None
250
251
251 def _removedirs(name):
252 def _removedirs(name):
252 """special version of os.removedirs that does not remove symlinked
253 """special version of os.removedirs that does not remove symlinked
253 directories or junction points if they actually contain files"""
254 directories or junction points if they actually contain files"""
254 if osutil.listdir(name):
255 if osutil.listdir(name):
255 return
256 return
256 os.rmdir(name)
257 os.rmdir(name)
257 head, tail = os.path.split(name)
258 head, tail = os.path.split(name)
258 if not tail:
259 if not tail:
259 head, tail = os.path.split(head)
260 head, tail = os.path.split(head)
260 while head and tail:
261 while head and tail:
261 try:
262 try:
262 if osutil.listdir(name):
263 if osutil.listdir(name):
263 return
264 return
264 os.rmdir(head)
265 os.rmdir(head)
265 except:
266 except:
266 break
267 break
267 head, tail = os.path.split(head)
268 head, tail = os.path.split(head)
268
269
269 def unlink(f):
270 def unlink(f):
270 """unlink and remove the directory if it is empty"""
271 """unlink and remove the directory if it is empty"""
271 os.unlink(f)
272 os.unlink(f)
272 # try removing directories that might now be empty
273 # try removing directories that might now be empty
273 try:
274 try:
274 _removedirs(os.path.dirname(f))
275 _removedirs(os.path.dirname(f))
275 except OSError:
276 except OSError:
276 pass
277 pass
277
278
278 try:
279 try:
279 # override functions with win32 versions if possible
280 # override functions with win32 versions if possible
280 from win32 import *
281 from win32 import *
281 except ImportError:
282 except ImportError:
282 pass
283 pass
283
284
284 expandglobs = True
285 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now