##// END OF EJS Templates
win32: move lookupreg() to windows.py...
Adrian Buehlmann -
r16807:80142f38 default
parent child Browse files
Show More
@@ -1,418 +1,396
1 # win32.py - utility functions that use win32 API
1 # win32.py - utility functions that use win32 API
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 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import encoding
8 import ctypes, errno, os, subprocess, random
9 import ctypes, errno, os, subprocess, random, _winreg
10
9
11 _kernel32 = ctypes.windll.kernel32
10 _kernel32 = ctypes.windll.kernel32
12 _advapi32 = ctypes.windll.advapi32
11 _advapi32 = ctypes.windll.advapi32
13 _user32 = ctypes.windll.user32
12 _user32 = ctypes.windll.user32
14
13
15 _BOOL = ctypes.c_long
14 _BOOL = ctypes.c_long
16 _WORD = ctypes.c_ushort
15 _WORD = ctypes.c_ushort
17 _DWORD = ctypes.c_ulong
16 _DWORD = ctypes.c_ulong
18 _UINT = ctypes.c_uint
17 _UINT = ctypes.c_uint
19 _LONG = ctypes.c_long
18 _LONG = ctypes.c_long
20 _LPCSTR = _LPSTR = ctypes.c_char_p
19 _LPCSTR = _LPSTR = ctypes.c_char_p
21 _HANDLE = ctypes.c_void_p
20 _HANDLE = ctypes.c_void_p
22 _HWND = _HANDLE
21 _HWND = _HANDLE
23
22
24 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
23 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
25
24
26 # GetLastError
25 # GetLastError
27 _ERROR_SUCCESS = 0
26 _ERROR_SUCCESS = 0
28 _ERROR_INVALID_PARAMETER = 87
27 _ERROR_INVALID_PARAMETER = 87
29 _ERROR_INSUFFICIENT_BUFFER = 122
28 _ERROR_INSUFFICIENT_BUFFER = 122
30
29
31 # WPARAM is defined as UINT_PTR (unsigned type)
30 # WPARAM is defined as UINT_PTR (unsigned type)
32 # LPARAM is defined as LONG_PTR (signed type)
31 # LPARAM is defined as LONG_PTR (signed type)
33 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
32 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
34 _WPARAM = ctypes.c_ulong
33 _WPARAM = ctypes.c_ulong
35 _LPARAM = ctypes.c_long
34 _LPARAM = ctypes.c_long
36 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
35 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
37 _WPARAM = ctypes.c_ulonglong
36 _WPARAM = ctypes.c_ulonglong
38 _LPARAM = ctypes.c_longlong
37 _LPARAM = ctypes.c_longlong
39
38
40 class _FILETIME(ctypes.Structure):
39 class _FILETIME(ctypes.Structure):
41 _fields_ = [('dwLowDateTime', _DWORD),
40 _fields_ = [('dwLowDateTime', _DWORD),
42 ('dwHighDateTime', _DWORD)]
41 ('dwHighDateTime', _DWORD)]
43
42
44 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
43 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
45 _fields_ = [('dwFileAttributes', _DWORD),
44 _fields_ = [('dwFileAttributes', _DWORD),
46 ('ftCreationTime', _FILETIME),
45 ('ftCreationTime', _FILETIME),
47 ('ftLastAccessTime', _FILETIME),
46 ('ftLastAccessTime', _FILETIME),
48 ('ftLastWriteTime', _FILETIME),
47 ('ftLastWriteTime', _FILETIME),
49 ('dwVolumeSerialNumber', _DWORD),
48 ('dwVolumeSerialNumber', _DWORD),
50 ('nFileSizeHigh', _DWORD),
49 ('nFileSizeHigh', _DWORD),
51 ('nFileSizeLow', _DWORD),
50 ('nFileSizeLow', _DWORD),
52 ('nNumberOfLinks', _DWORD),
51 ('nNumberOfLinks', _DWORD),
53 ('nFileIndexHigh', _DWORD),
52 ('nFileIndexHigh', _DWORD),
54 ('nFileIndexLow', _DWORD)]
53 ('nFileIndexLow', _DWORD)]
55
54
56 # CreateFile
55 # CreateFile
57 _FILE_SHARE_READ = 0x00000001
56 _FILE_SHARE_READ = 0x00000001
58 _FILE_SHARE_WRITE = 0x00000002
57 _FILE_SHARE_WRITE = 0x00000002
59 _FILE_SHARE_DELETE = 0x00000004
58 _FILE_SHARE_DELETE = 0x00000004
60
59
61 _OPEN_EXISTING = 3
60 _OPEN_EXISTING = 3
62
61
63 # SetFileAttributes
62 # SetFileAttributes
64 _FILE_ATTRIBUTE_NORMAL = 0x80
63 _FILE_ATTRIBUTE_NORMAL = 0x80
65 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
64 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
66
65
67 # Process Security and Access Rights
66 # Process Security and Access Rights
68 _PROCESS_QUERY_INFORMATION = 0x0400
67 _PROCESS_QUERY_INFORMATION = 0x0400
69
68
70 # GetExitCodeProcess
69 # GetExitCodeProcess
71 _STILL_ACTIVE = 259
70 _STILL_ACTIVE = 259
72
71
73 class _STARTUPINFO(ctypes.Structure):
72 class _STARTUPINFO(ctypes.Structure):
74 _fields_ = [('cb', _DWORD),
73 _fields_ = [('cb', _DWORD),
75 ('lpReserved', _LPSTR),
74 ('lpReserved', _LPSTR),
76 ('lpDesktop', _LPSTR),
75 ('lpDesktop', _LPSTR),
77 ('lpTitle', _LPSTR),
76 ('lpTitle', _LPSTR),
78 ('dwX', _DWORD),
77 ('dwX', _DWORD),
79 ('dwY', _DWORD),
78 ('dwY', _DWORD),
80 ('dwXSize', _DWORD),
79 ('dwXSize', _DWORD),
81 ('dwYSize', _DWORD),
80 ('dwYSize', _DWORD),
82 ('dwXCountChars', _DWORD),
81 ('dwXCountChars', _DWORD),
83 ('dwYCountChars', _DWORD),
82 ('dwYCountChars', _DWORD),
84 ('dwFillAttribute', _DWORD),
83 ('dwFillAttribute', _DWORD),
85 ('dwFlags', _DWORD),
84 ('dwFlags', _DWORD),
86 ('wShowWindow', _WORD),
85 ('wShowWindow', _WORD),
87 ('cbReserved2', _WORD),
86 ('cbReserved2', _WORD),
88 ('lpReserved2', ctypes.c_char_p),
87 ('lpReserved2', ctypes.c_char_p),
89 ('hStdInput', _HANDLE),
88 ('hStdInput', _HANDLE),
90 ('hStdOutput', _HANDLE),
89 ('hStdOutput', _HANDLE),
91 ('hStdError', _HANDLE)]
90 ('hStdError', _HANDLE)]
92
91
93 class _PROCESS_INFORMATION(ctypes.Structure):
92 class _PROCESS_INFORMATION(ctypes.Structure):
94 _fields_ = [('hProcess', _HANDLE),
93 _fields_ = [('hProcess', _HANDLE),
95 ('hThread', _HANDLE),
94 ('hThread', _HANDLE),
96 ('dwProcessId', _DWORD),
95 ('dwProcessId', _DWORD),
97 ('dwThreadId', _DWORD)]
96 ('dwThreadId', _DWORD)]
98
97
99 _DETACHED_PROCESS = 0x00000008
98 _DETACHED_PROCESS = 0x00000008
100 _STARTF_USESHOWWINDOW = 0x00000001
99 _STARTF_USESHOWWINDOW = 0x00000001
101 _SW_HIDE = 0
100 _SW_HIDE = 0
102
101
103 class _COORD(ctypes.Structure):
102 class _COORD(ctypes.Structure):
104 _fields_ = [('X', ctypes.c_short),
103 _fields_ = [('X', ctypes.c_short),
105 ('Y', ctypes.c_short)]
104 ('Y', ctypes.c_short)]
106
105
107 class _SMALL_RECT(ctypes.Structure):
106 class _SMALL_RECT(ctypes.Structure):
108 _fields_ = [('Left', ctypes.c_short),
107 _fields_ = [('Left', ctypes.c_short),
109 ('Top', ctypes.c_short),
108 ('Top', ctypes.c_short),
110 ('Right', ctypes.c_short),
109 ('Right', ctypes.c_short),
111 ('Bottom', ctypes.c_short)]
110 ('Bottom', ctypes.c_short)]
112
111
113 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
112 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
114 _fields_ = [('dwSize', _COORD),
113 _fields_ = [('dwSize', _COORD),
115 ('dwCursorPosition', _COORD),
114 ('dwCursorPosition', _COORD),
116 ('wAttributes', _WORD),
115 ('wAttributes', _WORD),
117 ('srWindow', _SMALL_RECT),
116 ('srWindow', _SMALL_RECT),
118 ('dwMaximumWindowSize', _COORD)]
117 ('dwMaximumWindowSize', _COORD)]
119
118
120 _STD_ERROR_HANDLE = _DWORD(-12).value
119 _STD_ERROR_HANDLE = _DWORD(-12).value
121
120
122 # types of parameters of C functions used (required by pypy)
121 # types of parameters of C functions used (required by pypy)
123
122
124 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
123 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
125 _DWORD, _DWORD, _HANDLE]
124 _DWORD, _DWORD, _HANDLE]
126 _kernel32.CreateFileA.restype = _HANDLE
125 _kernel32.CreateFileA.restype = _HANDLE
127
126
128 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
127 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
129 _kernel32.GetFileInformationByHandle.restype = _BOOL
128 _kernel32.GetFileInformationByHandle.restype = _BOOL
130
129
131 _kernel32.CloseHandle.argtypes = [_HANDLE]
130 _kernel32.CloseHandle.argtypes = [_HANDLE]
132 _kernel32.CloseHandle.restype = _BOOL
131 _kernel32.CloseHandle.restype = _BOOL
133
132
134 try:
133 try:
135 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
134 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
136 _kernel32.CreateHardLinkA.restype = _BOOL
135 _kernel32.CreateHardLinkA.restype = _BOOL
137 except AttributeError:
136 except AttributeError:
138 pass
137 pass
139
138
140 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
139 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
141 _kernel32.SetFileAttributesA.restype = _BOOL
140 _kernel32.SetFileAttributesA.restype = _BOOL
142
141
143 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
142 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
144 _kernel32.OpenProcess.restype = _HANDLE
143 _kernel32.OpenProcess.restype = _HANDLE
145
144
146 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
145 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
147 _kernel32.GetExitCodeProcess.restype = _BOOL
146 _kernel32.GetExitCodeProcess.restype = _BOOL
148
147
149 _kernel32.GetLastError.argtypes = []
148 _kernel32.GetLastError.argtypes = []
150 _kernel32.GetLastError.restype = _DWORD
149 _kernel32.GetLastError.restype = _DWORD
151
150
152 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
151 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
153 _kernel32.GetModuleFileNameA.restype = _DWORD
152 _kernel32.GetModuleFileNameA.restype = _DWORD
154
153
155 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
154 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
156 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
155 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
157 ctypes.c_void_p]
156 ctypes.c_void_p]
158 _kernel32.CreateProcessA.restype = _BOOL
157 _kernel32.CreateProcessA.restype = _BOOL
159
158
160 _kernel32.ExitProcess.argtypes = [_UINT]
159 _kernel32.ExitProcess.argtypes = [_UINT]
161 _kernel32.ExitProcess.restype = None
160 _kernel32.ExitProcess.restype = None
162
161
163 _kernel32.GetCurrentProcessId.argtypes = []
162 _kernel32.GetCurrentProcessId.argtypes = []
164 _kernel32.GetCurrentProcessId.restype = _DWORD
163 _kernel32.GetCurrentProcessId.restype = _DWORD
165
164
166 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
165 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
167 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
166 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
168 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
167 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
169
168
170 _kernel32.GetStdHandle.argtypes = [_DWORD]
169 _kernel32.GetStdHandle.argtypes = [_DWORD]
171 _kernel32.GetStdHandle.restype = _HANDLE
170 _kernel32.GetStdHandle.restype = _HANDLE
172
171
173 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
172 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
174 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
173 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
175
174
176 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
175 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
177 _advapi32.GetUserNameA.restype = _BOOL
176 _advapi32.GetUserNameA.restype = _BOOL
178
177
179 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
178 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
180 _user32.GetWindowThreadProcessId.restype = _DWORD
179 _user32.GetWindowThreadProcessId.restype = _DWORD
181
180
182 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
181 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
183 _user32.ShowWindow.restype = _BOOL
182 _user32.ShowWindow.restype = _BOOL
184
183
185 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
184 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
186 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
185 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
187 _user32.EnumWindows.restype = _BOOL
186 _user32.EnumWindows.restype = _BOOL
188
187
189 def _raiseoserror(name):
188 def _raiseoserror(name):
190 err = ctypes.WinError()
189 err = ctypes.WinError()
191 raise OSError(err.errno, '%s: %s' % (name, err.strerror))
190 raise OSError(err.errno, '%s: %s' % (name, err.strerror))
192
191
193 def _getfileinfo(name):
192 def _getfileinfo(name):
194 fh = _kernel32.CreateFileA(name, 0,
193 fh = _kernel32.CreateFileA(name, 0,
195 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
194 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
196 None, _OPEN_EXISTING, 0, None)
195 None, _OPEN_EXISTING, 0, None)
197 if fh == _INVALID_HANDLE_VALUE:
196 if fh == _INVALID_HANDLE_VALUE:
198 _raiseoserror(name)
197 _raiseoserror(name)
199 try:
198 try:
200 fi = _BY_HANDLE_FILE_INFORMATION()
199 fi = _BY_HANDLE_FILE_INFORMATION()
201 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
200 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
202 _raiseoserror(name)
201 _raiseoserror(name)
203 return fi
202 return fi
204 finally:
203 finally:
205 _kernel32.CloseHandle(fh)
204 _kernel32.CloseHandle(fh)
206
205
207 def oslink(src, dst):
206 def oslink(src, dst):
208 try:
207 try:
209 if not _kernel32.CreateHardLinkA(dst, src, None):
208 if not _kernel32.CreateHardLinkA(dst, src, None):
210 _raiseoserror(src)
209 _raiseoserror(src)
211 except AttributeError: # Wine doesn't support this function
210 except AttributeError: # Wine doesn't support this function
212 _raiseoserror(src)
211 _raiseoserror(src)
213
212
214 def nlinks(name):
213 def nlinks(name):
215 '''return number of hardlinks for the given file'''
214 '''return number of hardlinks for the given file'''
216 return _getfileinfo(name).nNumberOfLinks
215 return _getfileinfo(name).nNumberOfLinks
217
216
218 def samefile(fpath1, fpath2):
217 def samefile(fpath1, fpath2):
219 '''Returns whether fpath1 and fpath2 refer to the same file. This is only
218 '''Returns whether fpath1 and fpath2 refer to the same file. This is only
220 guaranteed to work for files, not directories.'''
219 guaranteed to work for files, not directories.'''
221 res1 = _getfileinfo(fpath1)
220 res1 = _getfileinfo(fpath1)
222 res2 = _getfileinfo(fpath2)
221 res2 = _getfileinfo(fpath2)
223 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
222 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
224 and res1.nFileIndexHigh == res2.nFileIndexHigh
223 and res1.nFileIndexHigh == res2.nFileIndexHigh
225 and res1.nFileIndexLow == res2.nFileIndexLow)
224 and res1.nFileIndexLow == res2.nFileIndexLow)
226
225
227 def samedevice(fpath1, fpath2):
226 def samedevice(fpath1, fpath2):
228 '''Returns whether fpath1 and fpath2 are on the same device. This is only
227 '''Returns whether fpath1 and fpath2 are on the same device. This is only
229 guaranteed to work for files, not directories.'''
228 guaranteed to work for files, not directories.'''
230 res1 = _getfileinfo(fpath1)
229 res1 = _getfileinfo(fpath1)
231 res2 = _getfileinfo(fpath2)
230 res2 = _getfileinfo(fpath2)
232 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
231 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
233
232
234 def testpid(pid):
233 def testpid(pid):
235 '''return True if pid is still running or unable to
234 '''return True if pid is still running or unable to
236 determine, False otherwise'''
235 determine, False otherwise'''
237 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
236 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
238 if h:
237 if h:
239 try:
238 try:
240 status = _DWORD()
239 status = _DWORD()
241 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
240 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
242 return status.value == _STILL_ACTIVE
241 return status.value == _STILL_ACTIVE
243 finally:
242 finally:
244 _kernel32.CloseHandle(h)
243 _kernel32.CloseHandle(h)
245 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
244 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
246
245
247 def lookupreg(key, valname=None, scope=None):
248 ''' Look up a key/value name in the Windows registry.
249
250 valname: value name. If unspecified, the default value for the key
251 is used.
252 scope: optionally specify scope for registry lookup, this can be
253 a sequence of scopes to look up in order. Default (CURRENT_USER,
254 LOCAL_MACHINE).
255 '''
256 if scope is None:
257 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
258 elif not isinstance(scope, (list, tuple)):
259 scope = (scope,)
260 for s in scope:
261 try:
262 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
263 # never let a Unicode string escape into the wild
264 return encoding.tolocal(val.encode('UTF-8'))
265 except EnvironmentError:
266 pass
267
268 def executablepath():
246 def executablepath():
269 '''return full path of hg.exe'''
247 '''return full path of hg.exe'''
270 size = 600
248 size = 600
271 buf = ctypes.create_string_buffer(size + 1)
249 buf = ctypes.create_string_buffer(size + 1)
272 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
250 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
273 if len == 0:
251 if len == 0:
274 raise ctypes.WinError
252 raise ctypes.WinError
275 elif len == size:
253 elif len == size:
276 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
254 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
277 return buf.value
255 return buf.value
278
256
279 def getuser():
257 def getuser():
280 '''return name of current user'''
258 '''return name of current user'''
281 size = _DWORD(300)
259 size = _DWORD(300)
282 buf = ctypes.create_string_buffer(size.value + 1)
260 buf = ctypes.create_string_buffer(size.value + 1)
283 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
261 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
284 raise ctypes.WinError
262 raise ctypes.WinError
285 return buf.value
263 return buf.value
286
264
287 _signalhandler = []
265 _signalhandler = []
288
266
289 def setsignalhandler():
267 def setsignalhandler():
290 '''Register a termination handler for console events including
268 '''Register a termination handler for console events including
291 CTRL+C. python signal handlers do not work well with socket
269 CTRL+C. python signal handlers do not work well with socket
292 operations.
270 operations.
293 '''
271 '''
294 def handler(event):
272 def handler(event):
295 _kernel32.ExitProcess(1)
273 _kernel32.ExitProcess(1)
296
274
297 if _signalhandler:
275 if _signalhandler:
298 return # already registered
276 return # already registered
299 h = _SIGNAL_HANDLER(handler)
277 h = _SIGNAL_HANDLER(handler)
300 _signalhandler.append(h) # needed to prevent garbage collection
278 _signalhandler.append(h) # needed to prevent garbage collection
301 if not _kernel32.SetConsoleCtrlHandler(h, True):
279 if not _kernel32.SetConsoleCtrlHandler(h, True):
302 raise ctypes.WinError
280 raise ctypes.WinError
303
281
304 def hidewindow():
282 def hidewindow():
305
283
306 def callback(hwnd, pid):
284 def callback(hwnd, pid):
307 wpid = _DWORD()
285 wpid = _DWORD()
308 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
286 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
309 if pid == wpid.value:
287 if pid == wpid.value:
310 _user32.ShowWindow(hwnd, _SW_HIDE)
288 _user32.ShowWindow(hwnd, _SW_HIDE)
311 return False # stop enumerating windows
289 return False # stop enumerating windows
312 return True
290 return True
313
291
314 pid = _kernel32.GetCurrentProcessId()
292 pid = _kernel32.GetCurrentProcessId()
315 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
293 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
316
294
317 def termwidth():
295 def termwidth():
318 # cmd.exe does not handle CR like a unix console, the CR is
296 # cmd.exe does not handle CR like a unix console, the CR is
319 # counted in the line length. On 80 columns consoles, if 80
297 # counted in the line length. On 80 columns consoles, if 80
320 # characters are written, the following CR won't apply on the
298 # characters are written, the following CR won't apply on the
321 # current line but on the new one. Keep room for it.
299 # current line but on the new one. Keep room for it.
322 width = 79
300 width = 79
323 # Query stderr to avoid problems with redirections
301 # Query stderr to avoid problems with redirections
324 screenbuf = _kernel32.GetStdHandle(
302 screenbuf = _kernel32.GetStdHandle(
325 _STD_ERROR_HANDLE) # don't close the handle returned
303 _STD_ERROR_HANDLE) # don't close the handle returned
326 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
304 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
327 return width
305 return width
328 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
306 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
329 if not _kernel32.GetConsoleScreenBufferInfo(
307 if not _kernel32.GetConsoleScreenBufferInfo(
330 screenbuf, ctypes.byref(csbi)):
308 screenbuf, ctypes.byref(csbi)):
331 return width
309 return width
332 width = csbi.srWindow.Right - csbi.srWindow.Left
310 width = csbi.srWindow.Right - csbi.srWindow.Left
333 return width
311 return width
334
312
335 def spawndetached(args):
313 def spawndetached(args):
336 # No standard library function really spawns a fully detached
314 # No standard library function really spawns a fully detached
337 # process under win32 because they allocate pipes or other objects
315 # process under win32 because they allocate pipes or other objects
338 # to handle standard streams communications. Passing these objects
316 # to handle standard streams communications. Passing these objects
339 # to the child process requires handle inheritance to be enabled
317 # to the child process requires handle inheritance to be enabled
340 # which makes really detached processes impossible.
318 # which makes really detached processes impossible.
341 si = _STARTUPINFO()
319 si = _STARTUPINFO()
342 si.cb = ctypes.sizeof(_STARTUPINFO)
320 si.cb = ctypes.sizeof(_STARTUPINFO)
343 si.dwFlags = _STARTF_USESHOWWINDOW
321 si.dwFlags = _STARTF_USESHOWWINDOW
344 si.wShowWindow = _SW_HIDE
322 si.wShowWindow = _SW_HIDE
345
323
346 pi = _PROCESS_INFORMATION()
324 pi = _PROCESS_INFORMATION()
347
325
348 env = ''
326 env = ''
349 for k in os.environ:
327 for k in os.environ:
350 env += "%s=%s\0" % (k, os.environ[k])
328 env += "%s=%s\0" % (k, os.environ[k])
351 if not env:
329 if not env:
352 env = '\0'
330 env = '\0'
353 env += '\0'
331 env += '\0'
354
332
355 args = subprocess.list2cmdline(args)
333 args = subprocess.list2cmdline(args)
356 # Not running the command in shell mode makes python26 hang when
334 # Not running the command in shell mode makes python26 hang when
357 # writing to hgweb output socket.
335 # writing to hgweb output socket.
358 comspec = os.environ.get("COMSPEC", "cmd.exe")
336 comspec = os.environ.get("COMSPEC", "cmd.exe")
359 args = comspec + " /c " + args
337 args = comspec + " /c " + args
360
338
361 res = _kernel32.CreateProcessA(
339 res = _kernel32.CreateProcessA(
362 None, args, None, None, False, _DETACHED_PROCESS,
340 None, args, None, None, False, _DETACHED_PROCESS,
363 env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
341 env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
364 if not res:
342 if not res:
365 raise ctypes.WinError
343 raise ctypes.WinError
366
344
367 return pi.dwProcessId
345 return pi.dwProcessId
368
346
369 def unlink(f):
347 def unlink(f):
370 '''try to implement POSIX' unlink semantics on Windows'''
348 '''try to implement POSIX' unlink semantics on Windows'''
371
349
372 # POSIX allows to unlink and rename open files. Windows has serious
350 # POSIX allows to unlink and rename open files. Windows has serious
373 # problems with doing that:
351 # problems with doing that:
374 # - Calling os.unlink (or os.rename) on a file f fails if f or any
352 # - Calling os.unlink (or os.rename) on a file f fails if f or any
375 # hardlinked copy of f has been opened with Python's open(). There is no
353 # hardlinked copy of f has been opened with Python's open(). There is no
376 # way such a file can be deleted or renamed on Windows (other than
354 # way such a file can be deleted or renamed on Windows (other than
377 # scheduling the delete or rename for the next reboot).
355 # scheduling the delete or rename for the next reboot).
378 # - Calling os.unlink on a file that has been opened with Mercurial's
356 # - Calling os.unlink on a file that has been opened with Mercurial's
379 # posixfile (or comparable methods) will delay the actual deletion of
357 # posixfile (or comparable methods) will delay the actual deletion of
380 # the file for as long as the file is held open. The filename is blocked
358 # the file for as long as the file is held open. The filename is blocked
381 # during that time and cannot be used for recreating a new file under
359 # during that time and cannot be used for recreating a new file under
382 # that same name ("zombie file"). Directories containing such zombie files
360 # that same name ("zombie file"). Directories containing such zombie files
383 # cannot be removed or moved.
361 # cannot be removed or moved.
384 # A file that has been opened with posixfile can be renamed, so we rename
362 # A file that has been opened with posixfile can be renamed, so we rename
385 # f to a random temporary name before calling os.unlink on it. This allows
363 # f to a random temporary name before calling os.unlink on it. This allows
386 # callers to recreate f immediately while having other readers do their
364 # callers to recreate f immediately while having other readers do their
387 # implicit zombie filename blocking on a temporary name.
365 # implicit zombie filename blocking on a temporary name.
388
366
389 for tries in xrange(10):
367 for tries in xrange(10):
390 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
368 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
391 try:
369 try:
392 os.rename(f, temp) # raises OSError EEXIST if temp exists
370 os.rename(f, temp) # raises OSError EEXIST if temp exists
393 break
371 break
394 except OSError, e:
372 except OSError, e:
395 if e.errno != errno.EEXIST:
373 if e.errno != errno.EEXIST:
396 raise
374 raise
397 else:
375 else:
398 raise IOError, (errno.EEXIST, "No usable temporary filename found")
376 raise IOError, (errno.EEXIST, "No usable temporary filename found")
399
377
400 try:
378 try:
401 os.unlink(temp)
379 os.unlink(temp)
402 except OSError:
380 except OSError:
403 # The unlink might have failed because the READONLY attribute may heave
381 # The unlink might have failed because the READONLY attribute may heave
404 # been set on the original file. Rename works fine with READONLY set,
382 # been set on the original file. Rename works fine with READONLY set,
405 # but not os.unlink. Reset all attributes and try again.
383 # but not os.unlink. Reset all attributes and try again.
406 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
384 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
407 try:
385 try:
408 os.unlink(temp)
386 os.unlink(temp)
409 except OSError:
387 except OSError:
410 # The unlink might have failed due to some very rude AV-Scanners.
388 # The unlink might have failed due to some very rude AV-Scanners.
411 # Leaking a tempfile is the lesser evil than aborting here and
389 # Leaking a tempfile is the lesser evil than aborting here and
412 # leaving some potentially serious inconsistencies.
390 # leaving some potentially serious inconsistencies.
413 pass
391 pass
414
392
415 def makedir(path, notindexed):
393 def makedir(path, notindexed):
416 os.mkdir(path)
394 os.mkdir(path)
417 if notindexed:
395 if notindexed:
418 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
396 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
@@ -1,319 +1,339
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 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import osutil
9 import osutil, encoding
10 import errno, msvcrt, os, re, sys
10 import errno, msvcrt, os, re, sys, _winreg
11
11
12 import win32
12 import win32
13 executablepath = win32.executablepath
13 executablepath = win32.executablepath
14 getuser = win32.getuser
14 getuser = win32.getuser
15 hidewindow = win32.hidewindow
15 hidewindow = win32.hidewindow
16 lookupreg = win32.lookupreg
17 makedir = win32.makedir
16 makedir = win32.makedir
18 nlinks = win32.nlinks
17 nlinks = win32.nlinks
19 oslink = win32.oslink
18 oslink = win32.oslink
20 samedevice = win32.samedevice
19 samedevice = win32.samedevice
21 samefile = win32.samefile
20 samefile = win32.samefile
22 setsignalhandler = win32.setsignalhandler
21 setsignalhandler = win32.setsignalhandler
23 spawndetached = win32.spawndetached
22 spawndetached = win32.spawndetached
24 termwidth = win32.termwidth
23 termwidth = win32.termwidth
25 testpid = win32.testpid
24 testpid = win32.testpid
26 unlink = win32.unlink
25 unlink = win32.unlink
27
26
28 nulldev = 'NUL:'
27 nulldev = 'NUL:'
29 umask = 0022
28 umask = 0022
30
29
31 # wrap osutil.posixfile to provide friendlier exceptions
30 # wrap osutil.posixfile to provide friendlier exceptions
32 def posixfile(name, mode='r', buffering=-1):
31 def posixfile(name, mode='r', buffering=-1):
33 try:
32 try:
34 return osutil.posixfile(name, mode, buffering)
33 return osutil.posixfile(name, mode, buffering)
35 except WindowsError, err:
34 except WindowsError, err:
36 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
35 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
37 posixfile.__doc__ = osutil.posixfile.__doc__
36 posixfile.__doc__ = osutil.posixfile.__doc__
38
37
39 class winstdout(object):
38 class winstdout(object):
40 '''stdout on windows misbehaves if sent through a pipe'''
39 '''stdout on windows misbehaves if sent through a pipe'''
41
40
42 def __init__(self, fp):
41 def __init__(self, fp):
43 self.fp = fp
42 self.fp = fp
44
43
45 def __getattr__(self, key):
44 def __getattr__(self, key):
46 return getattr(self.fp, key)
45 return getattr(self.fp, key)
47
46
48 def close(self):
47 def close(self):
49 try:
48 try:
50 self.fp.close()
49 self.fp.close()
51 except IOError:
50 except IOError:
52 pass
51 pass
53
52
54 def write(self, s):
53 def write(self, s):
55 try:
54 try:
56 # This is workaround for "Not enough space" error on
55 # This is workaround for "Not enough space" error on
57 # writing large size of data to console.
56 # writing large size of data to console.
58 limit = 16000
57 limit = 16000
59 l = len(s)
58 l = len(s)
60 start = 0
59 start = 0
61 self.softspace = 0
60 self.softspace = 0
62 while start < l:
61 while start < l:
63 end = start + limit
62 end = start + limit
64 self.fp.write(s[start:end])
63 self.fp.write(s[start:end])
65 start = end
64 start = end
66 except IOError, inst:
65 except IOError, inst:
67 if inst.errno != 0:
66 if inst.errno != 0:
68 raise
67 raise
69 self.close()
68 self.close()
70 raise IOError(errno.EPIPE, 'Broken pipe')
69 raise IOError(errno.EPIPE, 'Broken pipe')
71
70
72 def flush(self):
71 def flush(self):
73 try:
72 try:
74 return self.fp.flush()
73 return self.fp.flush()
75 except IOError, inst:
74 except IOError, inst:
76 if inst.errno != errno.EINVAL:
75 if inst.errno != errno.EINVAL:
77 raise
76 raise
78 self.close()
77 self.close()
79 raise IOError(errno.EPIPE, 'Broken pipe')
78 raise IOError(errno.EPIPE, 'Broken pipe')
80
79
81 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
80 sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
82
81
83 def _is_win_9x():
82 def _is_win_9x():
84 '''return true if run on windows 95, 98 or me.'''
83 '''return true if run on windows 95, 98 or me.'''
85 try:
84 try:
86 return sys.getwindowsversion()[3] == 1
85 return sys.getwindowsversion()[3] == 1
87 except AttributeError:
86 except AttributeError:
88 return 'command' in os.environ.get('comspec', '')
87 return 'command' in os.environ.get('comspec', '')
89
88
90 def openhardlinks():
89 def openhardlinks():
91 return not _is_win_9x()
90 return not _is_win_9x()
92
91
93 def parsepatchoutput(output_line):
92 def parsepatchoutput(output_line):
94 """parses the output produced by patch and returns the filename"""
93 """parses the output produced by patch and returns the filename"""
95 pf = output_line[14:]
94 pf = output_line[14:]
96 if pf[0] == '`':
95 if pf[0] == '`':
97 pf = pf[1:-1] # Remove the quotes
96 pf = pf[1:-1] # Remove the quotes
98 return pf
97 return pf
99
98
100 def sshargs(sshcmd, host, user, port):
99 def sshargs(sshcmd, host, user, port):
101 '''Build argument list for ssh or Plink'''
100 '''Build argument list for ssh or Plink'''
102 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
101 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
103 args = user and ("%s@%s" % (user, host)) or host
102 args = user and ("%s@%s" % (user, host)) or host
104 return port and ("%s %s %s" % (args, pflag, port)) or args
103 return port and ("%s %s %s" % (args, pflag, port)) or args
105
104
106 def setflags(f, l, x):
105 def setflags(f, l, x):
107 pass
106 pass
108
107
109 def copymode(src, dst, mode=None):
108 def copymode(src, dst, mode=None):
110 pass
109 pass
111
110
112 def checkexec(path):
111 def checkexec(path):
113 return False
112 return False
114
113
115 def checklink(path):
114 def checklink(path):
116 return False
115 return False
117
116
118 def setbinary(fd):
117 def setbinary(fd):
119 # When run without console, pipes may expose invalid
118 # When run without console, pipes may expose invalid
120 # fileno(), usually set to -1.
119 # fileno(), usually set to -1.
121 fno = getattr(fd, 'fileno', None)
120 fno = getattr(fd, 'fileno', None)
122 if fno is not None and fno() >= 0:
121 if fno is not None and fno() >= 0:
123 msvcrt.setmode(fno(), os.O_BINARY)
122 msvcrt.setmode(fno(), os.O_BINARY)
124
123
125 def pconvert(path):
124 def pconvert(path):
126 return path.replace(os.sep, '/')
125 return path.replace(os.sep, '/')
127
126
128 def localpath(path):
127 def localpath(path):
129 return path.replace('/', '\\')
128 return path.replace('/', '\\')
130
129
131 def normpath(path):
130 def normpath(path):
132 return pconvert(os.path.normpath(path))
131 return pconvert(os.path.normpath(path))
133
132
134 encodinglower = None
133 encodinglower = None
135 encodingupper = None
134 encodingupper = None
136
135
137 def normcase(path):
136 def normcase(path):
138 return encodingupper(path)
137 return encodingupper(path)
139
138
140 def realpath(path):
139 def realpath(path):
141 '''
140 '''
142 Returns the true, canonical file system path equivalent to the given
141 Returns the true, canonical file system path equivalent to the given
143 path.
142 path.
144 '''
143 '''
145 # TODO: There may be a more clever way to do this that also handles other,
144 # TODO: There may be a more clever way to do this that also handles other,
146 # less common file systems.
145 # less common file systems.
147 return os.path.normpath(normcase(os.path.realpath(path)))
146 return os.path.normpath(normcase(os.path.realpath(path)))
148
147
149 def samestat(s1, s2):
148 def samestat(s1, s2):
150 return False
149 return False
151
150
152 # A sequence of backslashes is special iff it precedes a double quote:
151 # A sequence of backslashes is special iff it precedes a double quote:
153 # - if there's an even number of backslashes, the double quote is not
152 # - if there's an even number of backslashes, the double quote is not
154 # quoted (i.e. it ends the quoted region)
153 # quoted (i.e. it ends the quoted region)
155 # - if there's an odd number of backslashes, the double quote is quoted
154 # - if there's an odd number of backslashes, the double quote is quoted
156 # - in both cases, every pair of backslashes is unquoted into a single
155 # - in both cases, every pair of backslashes is unquoted into a single
157 # backslash
156 # backslash
158 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
157 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
159 # So, to quote a string, we must surround it in double quotes, double
158 # So, to quote a string, we must surround it in double quotes, double
160 # the number of backslashes that preceed double quotes and add another
159 # the number of backslashes that preceed double quotes and add another
161 # backslash before every double quote (being careful with the double
160 # backslash before every double quote (being careful with the double
162 # quote we've appended to the end)
161 # quote we've appended to the end)
163 _quotere = None
162 _quotere = None
164 def shellquote(s):
163 def shellquote(s):
165 global _quotere
164 global _quotere
166 if _quotere is None:
165 if _quotere is None:
167 _quotere = re.compile(r'(\\*)("|\\$)')
166 _quotere = re.compile(r'(\\*)("|\\$)')
168 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
167 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
169
168
170 def quotecommand(cmd):
169 def quotecommand(cmd):
171 """Build a command string suitable for os.popen* calls."""
170 """Build a command string suitable for os.popen* calls."""
172 if sys.version_info < (2, 7, 1):
171 if sys.version_info < (2, 7, 1):
173 # Python versions since 2.7.1 do this extra quoting themselves
172 # Python versions since 2.7.1 do this extra quoting themselves
174 return '"' + cmd + '"'
173 return '"' + cmd + '"'
175 return cmd
174 return cmd
176
175
177 def popen(command, mode='r'):
176 def popen(command, mode='r'):
178 # Work around "popen spawned process may not write to stdout
177 # Work around "popen spawned process may not write to stdout
179 # under windows"
178 # under windows"
180 # http://bugs.python.org/issue1366
179 # http://bugs.python.org/issue1366
181 command += " 2> %s" % nulldev
180 command += " 2> %s" % nulldev
182 return os.popen(quotecommand(command), mode)
181 return os.popen(quotecommand(command), mode)
183
182
184 def explainexit(code):
183 def explainexit(code):
185 return _("exited with status %d") % code, code
184 return _("exited with status %d") % code, code
186
185
187 # if you change this stub into a real check, please try to implement the
186 # if you change this stub into a real check, please try to implement the
188 # username and groupname functions above, too.
187 # username and groupname functions above, too.
189 def isowner(st):
188 def isowner(st):
190 return True
189 return True
191
190
192 def findexe(command):
191 def findexe(command):
193 '''Find executable for command searching like cmd.exe does.
192 '''Find executable for command searching like cmd.exe does.
194 If command is a basename then PATH is searched for command.
193 If command is a basename then PATH is searched for command.
195 PATH isn't searched if command is an absolute or relative path.
194 PATH isn't searched if command is an absolute or relative path.
196 An extension from PATHEXT is found and added if not present.
195 An extension from PATHEXT is found and added if not present.
197 If command isn't found None is returned.'''
196 If command isn't found None is returned.'''
198 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
197 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
199 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
198 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
200 if os.path.splitext(command)[1].lower() in pathexts:
199 if os.path.splitext(command)[1].lower() in pathexts:
201 pathexts = ['']
200 pathexts = ['']
202
201
203 def findexisting(pathcommand):
202 def findexisting(pathcommand):
204 'Will append extension (if needed) and return existing file'
203 'Will append extension (if needed) and return existing file'
205 for ext in pathexts:
204 for ext in pathexts:
206 executable = pathcommand + ext
205 executable = pathcommand + ext
207 if os.path.exists(executable):
206 if os.path.exists(executable):
208 return executable
207 return executable
209 return None
208 return None
210
209
211 if os.sep in command:
210 if os.sep in command:
212 return findexisting(command)
211 return findexisting(command)
213
212
214 for path in os.environ.get('PATH', '').split(os.pathsep):
213 for path in os.environ.get('PATH', '').split(os.pathsep):
215 executable = findexisting(os.path.join(path, command))
214 executable = findexisting(os.path.join(path, command))
216 if executable is not None:
215 if executable is not None:
217 return executable
216 return executable
218 return findexisting(os.path.expanduser(os.path.expandvars(command)))
217 return findexisting(os.path.expanduser(os.path.expandvars(command)))
219
218
220 def statfiles(files):
219 def statfiles(files):
221 '''Stat each file in files and yield stat or None if file does not exist.
220 '''Stat each file in files and yield stat or None if file does not exist.
222 Cluster and cache stat per directory to minimize number of OS stat calls.'''
221 Cluster and cache stat per directory to minimize number of OS stat calls.'''
223 dircache = {} # dirname -> filename -> status | None if file does not exist
222 dircache = {} # dirname -> filename -> status | None if file does not exist
224 for nf in files:
223 for nf in files:
225 nf = normcase(nf)
224 nf = normcase(nf)
226 dir, base = os.path.split(nf)
225 dir, base = os.path.split(nf)
227 if not dir:
226 if not dir:
228 dir = '.'
227 dir = '.'
229 cache = dircache.get(dir, None)
228 cache = dircache.get(dir, None)
230 if cache is None:
229 if cache is None:
231 try:
230 try:
232 dmap = dict([(normcase(n), s)
231 dmap = dict([(normcase(n), s)
233 for n, k, s in osutil.listdir(dir, True)])
232 for n, k, s in osutil.listdir(dir, True)])
234 except OSError, err:
233 except OSError, err:
235 # handle directory not found in Python version prior to 2.5
234 # handle directory not found in Python version prior to 2.5
236 # Python <= 2.4 returns native Windows code 3 in errno
235 # Python <= 2.4 returns native Windows code 3 in errno
237 # Python >= 2.5 returns ENOENT and adds winerror field
236 # Python >= 2.5 returns ENOENT and adds winerror field
238 # EINVAL is raised if dir is not a directory.
237 # EINVAL is raised if dir is not a directory.
239 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
238 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
240 errno.ENOTDIR):
239 errno.ENOTDIR):
241 raise
240 raise
242 dmap = {}
241 dmap = {}
243 cache = dircache.setdefault(dir, dmap)
242 cache = dircache.setdefault(dir, dmap)
244 yield cache.get(base, None)
243 yield cache.get(base, None)
245
244
246 def username(uid=None):
245 def username(uid=None):
247 """Return the name of the user with the given uid.
246 """Return the name of the user with the given uid.
248
247
249 If uid is None, return the name of the current user."""
248 If uid is None, return the name of the current user."""
250 return None
249 return None
251
250
252 def groupname(gid=None):
251 def groupname(gid=None):
253 """Return the name of the group with the given gid.
252 """Return the name of the group with the given gid.
254
253
255 If gid is None, return the name of the current group."""
254 If gid is None, return the name of the current group."""
256 return None
255 return None
257
256
258 def _removedirs(name):
257 def _removedirs(name):
259 """special version of os.removedirs that does not remove symlinked
258 """special version of os.removedirs that does not remove symlinked
260 directories or junction points if they actually contain files"""
259 directories or junction points if they actually contain files"""
261 if osutil.listdir(name):
260 if osutil.listdir(name):
262 return
261 return
263 os.rmdir(name)
262 os.rmdir(name)
264 head, tail = os.path.split(name)
263 head, tail = os.path.split(name)
265 if not tail:
264 if not tail:
266 head, tail = os.path.split(head)
265 head, tail = os.path.split(head)
267 while head and tail:
266 while head and tail:
268 try:
267 try:
269 if osutil.listdir(head):
268 if osutil.listdir(head):
270 return
269 return
271 os.rmdir(head)
270 os.rmdir(head)
272 except (ValueError, OSError):
271 except (ValueError, OSError):
273 break
272 break
274 head, tail = os.path.split(head)
273 head, tail = os.path.split(head)
275
274
276 def unlinkpath(f):
275 def unlinkpath(f):
277 """unlink and remove the directory if it is empty"""
276 """unlink and remove the directory if it is empty"""
278 unlink(f)
277 unlink(f)
279 # try removing directories that might now be empty
278 # try removing directories that might now be empty
280 try:
279 try:
281 _removedirs(os.path.dirname(f))
280 _removedirs(os.path.dirname(f))
282 except OSError:
281 except OSError:
283 pass
282 pass
284
283
285 def rename(src, dst):
284 def rename(src, dst):
286 '''atomically rename file src to dst, replacing dst if it exists'''
285 '''atomically rename file src to dst, replacing dst if it exists'''
287 try:
286 try:
288 os.rename(src, dst)
287 os.rename(src, dst)
289 except OSError, e:
288 except OSError, e:
290 if e.errno != errno.EEXIST:
289 if e.errno != errno.EEXIST:
291 raise
290 raise
292 unlink(dst)
291 unlink(dst)
293 os.rename(src, dst)
292 os.rename(src, dst)
294
293
295 def gethgcmd():
294 def gethgcmd():
296 return [sys.executable] + sys.argv[:1]
295 return [sys.executable] + sys.argv[:1]
297
296
298 def termwidth():
297 def termwidth():
299 # cmd.exe does not handle CR like a unix console, the CR is
298 # cmd.exe does not handle CR like a unix console, the CR is
300 # counted in the line length. On 80 columns consoles, if 80
299 # counted in the line length. On 80 columns consoles, if 80
301 # characters are written, the following CR won't apply on the
300 # characters are written, the following CR won't apply on the
302 # current line but on the new one. Keep room for it.
301 # current line but on the new one. Keep room for it.
303 return 79
302 return 79
304
303
305 def groupmembers(name):
304 def groupmembers(name):
306 # Don't support groups on Windows for now
305 # Don't support groups on Windows for now
307 raise KeyError
306 raise KeyError
308
307
309 def isexec(f):
308 def isexec(f):
310 return False
309 return False
311
310
312 class cachestat(object):
311 class cachestat(object):
313 def __init__(self, path):
312 def __init__(self, path):
314 pass
313 pass
315
314
316 def cacheable(self):
315 def cacheable(self):
317 return False
316 return False
318
317
318 def lookupreg(key, valname=None, scope=None):
319 ''' Look up a key/value name in the Windows registry.
320
321 valname: value name. If unspecified, the default value for the key
322 is used.
323 scope: optionally specify scope for registry lookup, this can be
324 a sequence of scopes to look up in order. Default (CURRENT_USER,
325 LOCAL_MACHINE).
326 '''
327 if scope is None:
328 scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
329 elif not isinstance(scope, (list, tuple)):
330 scope = (scope,)
331 for s in scope:
332 try:
333 val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
334 # never let a Unicode string escape into the wild
335 return encoding.tolocal(val.encode('UTF-8'))
336 except EnvironmentError:
337 pass
338
319 expandglobs = True
339 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now