##// END OF EJS Templates
windows: eliminate system_rcpath_win32()
Adrian Buehlmann -
r13378:244772f6 default
parent child Browse files
Show More
@@ -1,371 +1,365 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 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, error
9 import osutil, error
10 import errno, msvcrt, os, re, sys, random, subprocess
10 import errno, msvcrt, os, re, sys, random, subprocess
11
11
12 nulldev = 'NUL:'
12 nulldev = 'NUL:'
13 umask = 002
13 umask = 002
14
14
15 # wrap osutil.posixfile to provide friendlier exceptions
15 # wrap osutil.posixfile to provide friendlier exceptions
16 def posixfile(name, mode='r', buffering=-1):
16 def posixfile(name, mode='r', buffering=-1):
17 try:
17 try:
18 return osutil.posixfile(name, mode, buffering)
18 return osutil.posixfile(name, mode, buffering)
19 except WindowsError, err:
19 except WindowsError, err:
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 posixfile.__doc__ = osutil.posixfile.__doc__
21 posixfile.__doc__ = osutil.posixfile.__doc__
22
22
23 class winstdout(object):
23 class winstdout(object):
24 '''stdout on windows misbehaves if sent through a pipe'''
24 '''stdout on windows misbehaves if sent through a pipe'''
25
25
26 def __init__(self, fp):
26 def __init__(self, fp):
27 self.fp = fp
27 self.fp = fp
28
28
29 def __getattr__(self, key):
29 def __getattr__(self, key):
30 return getattr(self.fp, key)
30 return getattr(self.fp, key)
31
31
32 def close(self):
32 def close(self):
33 try:
33 try:
34 self.fp.close()
34 self.fp.close()
35 except: pass
35 except: pass
36
36
37 def write(self, s):
37 def write(self, s):
38 try:
38 try:
39 # This is workaround for "Not enough space" error on
39 # This is workaround for "Not enough space" error on
40 # writing large size of data to console.
40 # writing large size of data to console.
41 limit = 16000
41 limit = 16000
42 l = len(s)
42 l = len(s)
43 start = 0
43 start = 0
44 self.softspace = 0
44 self.softspace = 0
45 while start < l:
45 while start < l:
46 end = start + limit
46 end = start + limit
47 self.fp.write(s[start:end])
47 self.fp.write(s[start:end])
48 start = end
48 start = end
49 except IOError, inst:
49 except IOError, inst:
50 if inst.errno != 0:
50 if inst.errno != 0:
51 raise
51 raise
52 self.close()
52 self.close()
53 raise IOError(errno.EPIPE, 'Broken pipe')
53 raise IOError(errno.EPIPE, 'Broken pipe')
54
54
55 def flush(self):
55 def flush(self):
56 try:
56 try:
57 return self.fp.flush()
57 return self.fp.flush()
58 except IOError, inst:
58 except IOError, inst:
59 if inst.errno != errno.EINVAL:
59 if inst.errno != errno.EINVAL:
60 raise
60 raise
61 self.close()
61 self.close()
62 raise IOError(errno.EPIPE, 'Broken pipe')
62 raise IOError(errno.EPIPE, 'Broken pipe')
63
63
64 sys.stdout = winstdout(sys.stdout)
64 sys.stdout = winstdout(sys.stdout)
65
65
66 def _is_win_9x():
66 def _is_win_9x():
67 '''return true if run on windows 95, 98 or me.'''
67 '''return true if run on windows 95, 98 or me.'''
68 try:
68 try:
69 return sys.getwindowsversion()[3] == 1
69 return sys.getwindowsversion()[3] == 1
70 except AttributeError:
70 except AttributeError:
71 return 'command' in os.environ.get('comspec', '')
71 return 'command' in os.environ.get('comspec', '')
72
72
73 def openhardlinks():
73 def openhardlinks():
74 return not _is_win_9x()
74 return not _is_win_9x()
75
75
76 _HKEY_LOCAL_MACHINE = 0x80000002L
76 _HKEY_LOCAL_MACHINE = 0x80000002L
77
77
78 def system_rcpath_win32():
78 def system_rcpath():
79 '''return default os-specific hgrc search path'''
79 '''return default os-specific hgrc search path'''
80 rcpath = []
80 rcpath = []
81 filename = executable_path()
81 filename = executable_path()
82 # Use mercurial.ini found in directory with hg.exe
82 # Use mercurial.ini found in directory with hg.exe
83 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
83 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
84 if os.path.isfile(progrc):
84 if os.path.isfile(progrc):
85 rcpath.append(progrc)
85 rcpath.append(progrc)
86 return rcpath
86 return rcpath
87 # Use hgrc.d found in directory with hg.exe
87 # Use hgrc.d found in directory with hg.exe
88 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
88 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
89 if os.path.isdir(progrcd):
89 if os.path.isdir(progrcd):
90 for f, kind in osutil.listdir(progrcd):
90 for f, kind in osutil.listdir(progrcd):
91 if f.endswith('.rc'):
91 if f.endswith('.rc'):
92 rcpath.append(os.path.join(progrcd, f))
92 rcpath.append(os.path.join(progrcd, f))
93 return rcpath
93 return rcpath
94 # else look for a system rcpath in the registry
94 # else look for a system rcpath in the registry
95 value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
95 value = lookup_reg('SOFTWARE\\Mercurial', None, _HKEY_LOCAL_MACHINE)
96 if not isinstance(value, str) or not value:
96 if not isinstance(value, str) or not value:
97 return rcpath
97 return rcpath
98 value = value.replace('/', os.sep)
98 value = value.replace('/', os.sep)
99 for p in value.split(os.pathsep):
99 for p in value.split(os.pathsep):
100 if p.lower().endswith('mercurial.ini'):
100 if p.lower().endswith('mercurial.ini'):
101 rcpath.append(p)
101 rcpath.append(p)
102 elif os.path.isdir(p):
102 elif os.path.isdir(p):
103 for f, kind in osutil.listdir(p):
103 for f, kind in osutil.listdir(p):
104 if f.endswith('.rc'):
104 if f.endswith('.rc'):
105 rcpath.append(os.path.join(p, f))
105 rcpath.append(os.path.join(p, f))
106 return rcpath
106 return rcpath
107
107
108 def system_rcpath():
109 try:
110 return system_rcpath_win32()
111 except:
112 return [r'c:\mercurial\mercurial.ini']
113
114 def user_rcpath():
108 def user_rcpath():
115 '''return os-specific hgrc search path to the user dir'''
109 '''return os-specific hgrc search path to the user dir'''
116 try:
110 try:
117 path = user_rcpath_win32()
111 path = user_rcpath_win32()
118 except:
112 except:
119 home = os.path.expanduser('~')
113 home = os.path.expanduser('~')
120 path = [os.path.join(home, 'mercurial.ini'),
114 path = [os.path.join(home, 'mercurial.ini'),
121 os.path.join(home, '.hgrc')]
115 os.path.join(home, '.hgrc')]
122 userprofile = os.environ.get('USERPROFILE')
116 userprofile = os.environ.get('USERPROFILE')
123 if userprofile:
117 if userprofile:
124 path.append(os.path.join(userprofile, 'mercurial.ini'))
118 path.append(os.path.join(userprofile, 'mercurial.ini'))
125 path.append(os.path.join(userprofile, '.hgrc'))
119 path.append(os.path.join(userprofile, '.hgrc'))
126 return path
120 return path
127
121
128 def parse_patch_output(output_line):
122 def parse_patch_output(output_line):
129 """parses the output produced by patch and returns the filename"""
123 """parses the output produced by patch and returns the filename"""
130 pf = output_line[14:]
124 pf = output_line[14:]
131 if pf[0] == '`':
125 if pf[0] == '`':
132 pf = pf[1:-1] # Remove the quotes
126 pf = pf[1:-1] # Remove the quotes
133 return pf
127 return pf
134
128
135 def sshargs(sshcmd, host, user, port):
129 def sshargs(sshcmd, host, user, port):
136 '''Build argument list for ssh or Plink'''
130 '''Build argument list for ssh or Plink'''
137 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
131 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
138 args = user and ("%s@%s" % (user, host)) or host
132 args = user and ("%s@%s" % (user, host)) or host
139 return port and ("%s %s %s" % (args, pflag, port)) or args
133 return port and ("%s %s %s" % (args, pflag, port)) or args
140
134
141 def set_flags(f, l, x):
135 def set_flags(f, l, x):
142 pass
136 pass
143
137
144 def set_binary(fd):
138 def set_binary(fd):
145 # When run without console, pipes may expose invalid
139 # When run without console, pipes may expose invalid
146 # fileno(), usually set to -1.
140 # fileno(), usually set to -1.
147 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
141 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
148 msvcrt.setmode(fd.fileno(), os.O_BINARY)
142 msvcrt.setmode(fd.fileno(), os.O_BINARY)
149
143
150 def pconvert(path):
144 def pconvert(path):
151 return '/'.join(path.split(os.sep))
145 return '/'.join(path.split(os.sep))
152
146
153 def localpath(path):
147 def localpath(path):
154 return path.replace('/', '\\')
148 return path.replace('/', '\\')
155
149
156 def normpath(path):
150 def normpath(path):
157 return pconvert(os.path.normpath(path))
151 return pconvert(os.path.normpath(path))
158
152
159 def realpath(path):
153 def realpath(path):
160 '''
154 '''
161 Returns the true, canonical file system path equivalent to the given
155 Returns the true, canonical file system path equivalent to the given
162 path.
156 path.
163 '''
157 '''
164 # TODO: There may be a more clever way to do this that also handles other,
158 # TODO: There may be a more clever way to do this that also handles other,
165 # less common file systems.
159 # less common file systems.
166 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
160 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
167
161
168 def samestat(s1, s2):
162 def samestat(s1, s2):
169 return False
163 return False
170
164
171 # A sequence of backslashes is special iff it precedes a double quote:
165 # A sequence of backslashes is special iff it precedes a double quote:
172 # - if there's an even number of backslashes, the double quote is not
166 # - if there's an even number of backslashes, the double quote is not
173 # quoted (i.e. it ends the quoted region)
167 # quoted (i.e. it ends the quoted region)
174 # - if there's an odd number of backslashes, the double quote is quoted
168 # - if there's an odd number of backslashes, the double quote is quoted
175 # - in both cases, every pair of backslashes is unquoted into a single
169 # - in both cases, every pair of backslashes is unquoted into a single
176 # backslash
170 # backslash
177 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
171 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
178 # So, to quote a string, we must surround it in double quotes, double
172 # So, to quote a string, we must surround it in double quotes, double
179 # the number of backslashes that preceed double quotes and add another
173 # the number of backslashes that preceed double quotes and add another
180 # backslash before every double quote (being careful with the double
174 # backslash before every double quote (being careful with the double
181 # quote we've appended to the end)
175 # quote we've appended to the end)
182 _quotere = None
176 _quotere = None
183 def shellquote(s):
177 def shellquote(s):
184 global _quotere
178 global _quotere
185 if _quotere is None:
179 if _quotere is None:
186 _quotere = re.compile(r'(\\*)("|\\$)')
180 _quotere = re.compile(r'(\\*)("|\\$)')
187 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
181 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
188
182
189 def quotecommand(cmd):
183 def quotecommand(cmd):
190 """Build a command string suitable for os.popen* calls."""
184 """Build a command string suitable for os.popen* calls."""
191 if sys.version_info < (2, 7, 1):
185 if sys.version_info < (2, 7, 1):
192 # Python versions since 2.7.1 do this extra quoting themselves
186 # Python versions since 2.7.1 do this extra quoting themselves
193 return '"' + cmd + '"'
187 return '"' + cmd + '"'
194 return cmd
188 return cmd
195
189
196 def popen(command, mode='r'):
190 def popen(command, mode='r'):
197 # Work around "popen spawned process may not write to stdout
191 # Work around "popen spawned process may not write to stdout
198 # under windows"
192 # under windows"
199 # http://bugs.python.org/issue1366
193 # http://bugs.python.org/issue1366
200 command += " 2> %s" % nulldev
194 command += " 2> %s" % nulldev
201 return os.popen(quotecommand(command), mode)
195 return os.popen(quotecommand(command), mode)
202
196
203 def explain_exit(code):
197 def explain_exit(code):
204 return _("exited with status %d") % code, code
198 return _("exited with status %d") % code, code
205
199
206 # if you change this stub into a real check, please try to implement the
200 # if you change this stub into a real check, please try to implement the
207 # username and groupname functions above, too.
201 # username and groupname functions above, too.
208 def isowner(st):
202 def isowner(st):
209 return True
203 return True
210
204
211 def find_exe(command):
205 def find_exe(command):
212 '''Find executable for command searching like cmd.exe does.
206 '''Find executable for command searching like cmd.exe does.
213 If command is a basename then PATH is searched for command.
207 If command is a basename then PATH is searched for command.
214 PATH isn't searched if command is an absolute or relative path.
208 PATH isn't searched if command is an absolute or relative path.
215 An extension from PATHEXT is found and added if not present.
209 An extension from PATHEXT is found and added if not present.
216 If command isn't found None is returned.'''
210 If command isn't found None is returned.'''
217 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
211 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
218 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
212 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
219 if os.path.splitext(command)[1].lower() in pathexts:
213 if os.path.splitext(command)[1].lower() in pathexts:
220 pathexts = ['']
214 pathexts = ['']
221
215
222 def findexisting(pathcommand):
216 def findexisting(pathcommand):
223 'Will append extension (if needed) and return existing file'
217 'Will append extension (if needed) and return existing file'
224 for ext in pathexts:
218 for ext in pathexts:
225 executable = pathcommand + ext
219 executable = pathcommand + ext
226 if os.path.exists(executable):
220 if os.path.exists(executable):
227 return executable
221 return executable
228 return None
222 return None
229
223
230 if os.sep in command:
224 if os.sep in command:
231 return findexisting(command)
225 return findexisting(command)
232
226
233 for path in os.environ.get('PATH', '').split(os.pathsep):
227 for path in os.environ.get('PATH', '').split(os.pathsep):
234 executable = findexisting(os.path.join(path, command))
228 executable = findexisting(os.path.join(path, command))
235 if executable is not None:
229 if executable is not None:
236 return executable
230 return executable
237 return findexisting(os.path.expanduser(os.path.expandvars(command)))
231 return findexisting(os.path.expanduser(os.path.expandvars(command)))
238
232
239 def statfiles(files):
233 def statfiles(files):
240 '''Stat each file in files and yield stat or None if file does not exist.
234 '''Stat each file in files and yield stat or None if file does not exist.
241 Cluster and cache stat per directory to minimize number of OS stat calls.'''
235 Cluster and cache stat per directory to minimize number of OS stat calls.'''
242 ncase = os.path.normcase
236 ncase = os.path.normcase
243 dircache = {} # dirname -> filename -> status | None if file does not exist
237 dircache = {} # dirname -> filename -> status | None if file does not exist
244 for nf in files:
238 for nf in files:
245 nf = ncase(nf)
239 nf = ncase(nf)
246 dir, base = os.path.split(nf)
240 dir, base = os.path.split(nf)
247 if not dir:
241 if not dir:
248 dir = '.'
242 dir = '.'
249 cache = dircache.get(dir, None)
243 cache = dircache.get(dir, None)
250 if cache is None:
244 if cache is None:
251 try:
245 try:
252 dmap = dict([(ncase(n), s)
246 dmap = dict([(ncase(n), s)
253 for n, k, s in osutil.listdir(dir, True)])
247 for n, k, s in osutil.listdir(dir, True)])
254 except OSError, err:
248 except OSError, err:
255 # handle directory not found in Python version prior to 2.5
249 # handle directory not found in Python version prior to 2.5
256 # Python <= 2.4 returns native Windows code 3 in errno
250 # Python <= 2.4 returns native Windows code 3 in errno
257 # Python >= 2.5 returns ENOENT and adds winerror field
251 # Python >= 2.5 returns ENOENT and adds winerror field
258 # EINVAL is raised if dir is not a directory.
252 # EINVAL is raised if dir is not a directory.
259 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
253 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
260 errno.ENOTDIR):
254 errno.ENOTDIR):
261 raise
255 raise
262 dmap = {}
256 dmap = {}
263 cache = dircache.setdefault(dir, dmap)
257 cache = dircache.setdefault(dir, dmap)
264 yield cache.get(base, None)
258 yield cache.get(base, None)
265
259
266 def username(uid=None):
260 def username(uid=None):
267 """Return the name of the user with the given uid.
261 """Return the name of the user with the given uid.
268
262
269 If uid is None, return the name of the current user."""
263 If uid is None, return the name of the current user."""
270 return None
264 return None
271
265
272 def groupname(gid=None):
266 def groupname(gid=None):
273 """Return the name of the group with the given gid.
267 """Return the name of the group with the given gid.
274
268
275 If gid is None, return the name of the current group."""
269 If gid is None, return the name of the current group."""
276 return None
270 return None
277
271
278 def _removedirs(name):
272 def _removedirs(name):
279 """special version of os.removedirs that does not remove symlinked
273 """special version of os.removedirs that does not remove symlinked
280 directories or junction points if they actually contain files"""
274 directories or junction points if they actually contain files"""
281 if osutil.listdir(name):
275 if osutil.listdir(name):
282 return
276 return
283 os.rmdir(name)
277 os.rmdir(name)
284 head, tail = os.path.split(name)
278 head, tail = os.path.split(name)
285 if not tail:
279 if not tail:
286 head, tail = os.path.split(head)
280 head, tail = os.path.split(head)
287 while head and tail:
281 while head and tail:
288 try:
282 try:
289 if osutil.listdir(head):
283 if osutil.listdir(head):
290 return
284 return
291 os.rmdir(head)
285 os.rmdir(head)
292 except:
286 except:
293 break
287 break
294 head, tail = os.path.split(head)
288 head, tail = os.path.split(head)
295
289
296 def unlinkpath(f):
290 def unlinkpath(f):
297 """unlink and remove the directory if it is empty"""
291 """unlink and remove the directory if it is empty"""
298 os.unlink(f)
292 os.unlink(f)
299 # try removing directories that might now be empty
293 # try removing directories that might now be empty
300 try:
294 try:
301 _removedirs(os.path.dirname(f))
295 _removedirs(os.path.dirname(f))
302 except OSError:
296 except OSError:
303 pass
297 pass
304
298
305 def unlink(f):
299 def unlink(f):
306 '''try to implement POSIX' unlink semantics on Windows'''
300 '''try to implement POSIX' unlink semantics on Windows'''
307
301
308 # POSIX allows to unlink and rename open files. Windows has serious
302 # POSIX allows to unlink and rename open files. Windows has serious
309 # problems with doing that:
303 # problems with doing that:
310 # - Calling os.unlink (or os.rename) on a file f fails if f or any
304 # - Calling os.unlink (or os.rename) on a file f fails if f or any
311 # hardlinked copy of f has been opened with Python's open(). There is no
305 # hardlinked copy of f has been opened with Python's open(). There is no
312 # way such a file can be deleted or renamed on Windows (other than
306 # way such a file can be deleted or renamed on Windows (other than
313 # scheduling the delete or rename for the next reboot).
307 # scheduling the delete or rename for the next reboot).
314 # - Calling os.unlink on a file that has been opened with Mercurial's
308 # - Calling os.unlink on a file that has been opened with Mercurial's
315 # posixfile (or comparable methods) will delay the actual deletion of
309 # posixfile (or comparable methods) will delay the actual deletion of
316 # the file for as long as the file is held open. The filename is blocked
310 # the file for as long as the file is held open. The filename is blocked
317 # during that time and cannot be used for recreating a new file under
311 # during that time and cannot be used for recreating a new file under
318 # that same name ("zombie file"). Directories containing such zombie files
312 # that same name ("zombie file"). Directories containing such zombie files
319 # cannot be removed or moved.
313 # cannot be removed or moved.
320 # A file that has been opened with posixfile can be renamed, so we rename
314 # A file that has been opened with posixfile can be renamed, so we rename
321 # f to a random temporary name before calling os.unlink on it. This allows
315 # f to a random temporary name before calling os.unlink on it. This allows
322 # callers to recreate f immediately while having other readers do their
316 # callers to recreate f immediately while having other readers do their
323 # implicit zombie filename blocking on a temporary name.
317 # implicit zombie filename blocking on a temporary name.
324
318
325 for tries in xrange(10):
319 for tries in xrange(10):
326 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
320 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
327 try:
321 try:
328 os.rename(f, temp) # raises OSError EEXIST if temp exists
322 os.rename(f, temp) # raises OSError EEXIST if temp exists
329 break
323 break
330 except OSError, e:
324 except OSError, e:
331 if e.errno != errno.EEXIST:
325 if e.errno != errno.EEXIST:
332 raise
326 raise
333 else:
327 else:
334 raise IOError, (errno.EEXIST, "No usable temporary filename found")
328 raise IOError, (errno.EEXIST, "No usable temporary filename found")
335
329
336 try:
330 try:
337 os.unlink(temp)
331 os.unlink(temp)
338 except:
332 except:
339 # Some very rude AV-scanners on Windows may cause this unlink to fail.
333 # Some very rude AV-scanners on Windows may cause this unlink to fail.
340 # Not aborting here just leaks the temp file, whereas aborting at this
334 # Not aborting here just leaks the temp file, whereas aborting at this
341 # point may leave serious inconsistencies. Ideally, we would notify
335 # point may leave serious inconsistencies. Ideally, we would notify
342 # the user in this case here.
336 # the user in this case here.
343 pass
337 pass
344
338
345 def rename(src, dst):
339 def rename(src, dst):
346 '''atomically rename file src to dst, replacing dst if it exists'''
340 '''atomically rename file src to dst, replacing dst if it exists'''
347 try:
341 try:
348 os.rename(src, dst)
342 os.rename(src, dst)
349 except OSError, e:
343 except OSError, e:
350 if e.errno != errno.EEXIST:
344 if e.errno != errno.EEXIST:
351 raise
345 raise
352 unlink(dst)
346 unlink(dst)
353 os.rename(src, dst)
347 os.rename(src, dst)
354
348
355 def gethgcmd():
349 def gethgcmd():
356 return [sys.executable] + sys.argv[:1]
350 return [sys.executable] + sys.argv[:1]
357
351
358 def termwidth():
352 def termwidth():
359 # cmd.exe does not handle CR like a unix console, the CR is
353 # cmd.exe does not handle CR like a unix console, the CR is
360 # counted in the line length. On 80 columns consoles, if 80
354 # counted in the line length. On 80 columns consoles, if 80
361 # characters are written, the following CR won't apply on the
355 # characters are written, the following CR won't apply on the
362 # current line but on the new one. Keep room for it.
356 # current line but on the new one. Keep room for it.
363 return 79
357 return 79
364
358
365 def groupmembers(name):
359 def groupmembers(name):
366 # Don't support groups on Windows for now
360 # Don't support groups on Windows for now
367 raise KeyError()
361 raise KeyError()
368
362
369 from win32 import *
363 from win32 import *
370
364
371 expandglobs = True
365 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now