##// END OF EJS Templates
reintroduces util.unlink, for POSIX and Windows....
Adrian Buehlmann -
r13280:6052bbc7 default
parent child Browse files
Show More
@@ -1,297 +1,298 b''
1 1 # posix.py - Posix utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import osutil
10 10 import os, sys, errno, stat, getpass, pwd, grp
11 11
12 12 posixfile = open
13 13 nulldev = '/dev/null'
14 14 normpath = os.path.normpath
15 15 samestat = os.path.samestat
16 unlink = os.unlink
16 17 rename = os.rename
17 18 expandglobs = False
18 19
19 20 umask = os.umask(0)
20 21 os.umask(umask)
21 22
22 23 def openhardlinks():
23 24 '''return true if it is safe to hold open file handles to hardlinks'''
24 25 return True
25 26
26 27 def rcfiles(path):
27 28 rcs = [os.path.join(path, 'hgrc')]
28 29 rcdir = os.path.join(path, 'hgrc.d')
29 30 try:
30 31 rcs.extend([os.path.join(rcdir, f)
31 32 for f, kind in osutil.listdir(rcdir)
32 33 if f.endswith(".rc")])
33 34 except OSError:
34 35 pass
35 36 return rcs
36 37
37 38 def system_rcpath():
38 39 path = []
39 40 # old mod_python does not set sys.argv
40 41 if len(getattr(sys, 'argv', [])) > 0:
41 42 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
42 43 '/../etc/mercurial'))
43 44 path.extend(rcfiles('/etc/mercurial'))
44 45 return path
45 46
46 47 def user_rcpath():
47 48 return [os.path.expanduser('~/.hgrc')]
48 49
49 50 def parse_patch_output(output_line):
50 51 """parses the output produced by patch and returns the filename"""
51 52 pf = output_line[14:]
52 53 if os.sys.platform == 'OpenVMS':
53 54 if pf[0] == '`':
54 55 pf = pf[1:-1] # Remove the quotes
55 56 else:
56 57 if pf.startswith("'") and pf.endswith("'") and " " in pf:
57 58 pf = pf[1:-1] # Remove the quotes
58 59 return pf
59 60
60 61 def sshargs(sshcmd, host, user, port):
61 62 '''Build argument list for ssh'''
62 63 args = user and ("%s@%s" % (user, host)) or host
63 64 return port and ("%s -p %s" % (args, port)) or args
64 65
65 66 def is_exec(f):
66 67 """check whether a file is executable"""
67 68 return (os.lstat(f).st_mode & 0100 != 0)
68 69
69 70 def set_flags(f, l, x):
70 71 s = os.lstat(f).st_mode
71 72 if l:
72 73 if not stat.S_ISLNK(s):
73 74 # switch file to link
74 75 data = open(f).read()
75 76 os.unlink(f)
76 77 try:
77 78 os.symlink(data, f)
78 79 except:
79 80 # failed to make a link, rewrite file
80 81 open(f, "w").write(data)
81 82 # no chmod needed at this point
82 83 return
83 84 if stat.S_ISLNK(s):
84 85 # switch link to file
85 86 data = os.readlink(f)
86 87 os.unlink(f)
87 88 open(f, "w").write(data)
88 89 s = 0666 & ~umask # avoid restatting for chmod
89 90
90 91 sx = s & 0100
91 92 if x and not sx:
92 93 # Turn on +x for every +r bit when making a file executable
93 94 # and obey umask.
94 95 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
95 96 elif not x and sx:
96 97 # Turn off all +x bits
97 98 os.chmod(f, s & 0666)
98 99
99 100 def set_binary(fd):
100 101 pass
101 102
102 103 def pconvert(path):
103 104 return path
104 105
105 106 def localpath(path):
106 107 return path
107 108
108 109 def samefile(fpath1, fpath2):
109 110 """Returns whether path1 and path2 refer to the same file. This is only
110 111 guaranteed to work for files, not directories."""
111 112 return os.path.samefile(fpath1, fpath2)
112 113
113 114 def samedevice(fpath1, fpath2):
114 115 """Returns whether fpath1 and fpath2 are on the same device. This is only
115 116 guaranteed to work for files, not directories."""
116 117 st1 = os.lstat(fpath1)
117 118 st2 = os.lstat(fpath2)
118 119 return st1.st_dev == st2.st_dev
119 120
120 121 if sys.platform == 'darwin':
121 122 import fcntl # only needed on darwin, missing on jython
122 123 def realpath(path):
123 124 '''
124 125 Returns the true, canonical file system path equivalent to the given
125 126 path.
126 127
127 128 Equivalent means, in this case, resulting in the same, unique
128 129 file system link to the path. Every file system entry, whether a file,
129 130 directory, hard link or symbolic link or special, will have a single
130 131 path preferred by the system, but may allow multiple, differing path
131 132 lookups to point to it.
132 133
133 134 Most regular UNIX file systems only allow a file system entry to be
134 135 looked up by its distinct path. Obviously, this does not apply to case
135 136 insensitive file systems, whether case preserving or not. The most
136 137 complex issue to deal with is file systems transparently reencoding the
137 138 path, such as the non-standard Unicode normalisation required for HFS+
138 139 and HFSX.
139 140 '''
140 141 # Constants copied from /usr/include/sys/fcntl.h
141 142 F_GETPATH = 50
142 143 O_SYMLINK = 0x200000
143 144
144 145 try:
145 146 fd = os.open(path, O_SYMLINK)
146 147 except OSError, err:
147 148 if err.errno == errno.ENOENT:
148 149 return path
149 150 raise
150 151
151 152 try:
152 153 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
153 154 finally:
154 155 os.close(fd)
155 156 else:
156 157 # Fallback to the likely inadequate Python builtin function.
157 158 realpath = os.path.realpath
158 159
159 160 def shellquote(s):
160 161 if os.sys.platform == 'OpenVMS':
161 162 return '"%s"' % s
162 163 else:
163 164 return "'%s'" % s.replace("'", "'\\''")
164 165
165 166 def quotecommand(cmd):
166 167 return cmd
167 168
168 169 def popen(command, mode='r'):
169 170 return os.popen(command, mode)
170 171
171 172 def testpid(pid):
172 173 '''return False if pid dead, True if running or not sure'''
173 174 if os.sys.platform == 'OpenVMS':
174 175 return True
175 176 try:
176 177 os.kill(pid, 0)
177 178 return True
178 179 except OSError, inst:
179 180 return inst.errno != errno.ESRCH
180 181
181 182 def explain_exit(code):
182 183 """return a 2-tuple (desc, code) describing a subprocess status
183 184 (codes from kill are negative - not os.system/wait encoding)"""
184 185 if code >= 0:
185 186 return _("exited with status %d") % code, code
186 187 return _("killed by signal %d") % -code, -code
187 188
188 189 def isowner(st):
189 190 """Return True if the stat object st is from the current user."""
190 191 return st.st_uid == os.getuid()
191 192
192 193 def find_exe(command):
193 194 '''Find executable for command searching like which does.
194 195 If command is a basename then PATH is searched for command.
195 196 PATH isn't searched if command is an absolute or relative path.
196 197 If command isn't found None is returned.'''
197 198 if sys.platform == 'OpenVMS':
198 199 return command
199 200
200 201 def findexisting(executable):
201 202 'Will return executable if existing file'
202 203 if os.path.exists(executable):
203 204 return executable
204 205 return None
205 206
206 207 if os.sep in command:
207 208 return findexisting(command)
208 209
209 210 for path in os.environ.get('PATH', '').split(os.pathsep):
210 211 executable = findexisting(os.path.join(path, command))
211 212 if executable is not None:
212 213 return executable
213 214 return None
214 215
215 216 def set_signal_handler():
216 217 pass
217 218
218 219 def statfiles(files):
219 220 'Stat each file in files and yield stat or None if file does not exist.'
220 221 lstat = os.lstat
221 222 for nf in files:
222 223 try:
223 224 st = lstat(nf)
224 225 except OSError, err:
225 226 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
226 227 raise
227 228 st = None
228 229 yield st
229 230
230 231 def getuser():
231 232 '''return name of current user'''
232 233 return getpass.getuser()
233 234
234 235 def expand_glob(pats):
235 236 '''On Windows, expand the implicit globs in a list of patterns'''
236 237 return list(pats)
237 238
238 239 def username(uid=None):
239 240 """Return the name of the user with the given uid.
240 241
241 242 If uid is None, return the name of the current user."""
242 243
243 244 if uid is None:
244 245 uid = os.getuid()
245 246 try:
246 247 return pwd.getpwuid(uid)[0]
247 248 except KeyError:
248 249 return str(uid)
249 250
250 251 def groupname(gid=None):
251 252 """Return the name of the group with the given gid.
252 253
253 254 If gid is None, return the name of the current group."""
254 255
255 256 if gid is None:
256 257 gid = os.getgid()
257 258 try:
258 259 return grp.getgrgid(gid)[0]
259 260 except KeyError:
260 261 return str(gid)
261 262
262 263 def groupmembers(name):
263 264 """Return the list of members of the group with the given
264 265 name, KeyError if the group does not exist.
265 266 """
266 267 return list(grp.getgrnam(name).gr_mem)
267 268
268 269 def spawndetached(args):
269 270 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
270 271 args[0], args)
271 272
272 273 def gethgcmd():
273 274 return sys.argv[:1]
274 275
275 276 def termwidth():
276 277 try:
277 278 import termios, array, fcntl
278 279 for dev in (sys.stderr, sys.stdout, sys.stdin):
279 280 try:
280 281 try:
281 282 fd = dev.fileno()
282 283 except AttributeError:
283 284 continue
284 285 if not os.isatty(fd):
285 286 continue
286 287 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
287 288 return array.array('h', arri)[1]
288 289 except ValueError:
289 290 pass
290 291 except IOError, e:
291 292 if e[0] == errno.EINVAL:
292 293 pass
293 294 else:
294 295 raise
295 296 except ImportError:
296 297 pass
297 298 return 80
@@ -1,377 +1,389 b''
1 1 # windows.py - Windows utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import osutil, error
10 10 import errno, msvcrt, os, re, sys, random, subprocess
11 11
12 12 nulldev = 'NUL:'
13 13 umask = 002
14 14
15 15 # wrap osutil.posixfile to provide friendlier exceptions
16 16 def posixfile(name, mode='r', buffering=-1):
17 17 try:
18 18 return osutil.posixfile(name, mode, buffering)
19 19 except WindowsError, err:
20 20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 21 posixfile.__doc__ = osutil.posixfile.__doc__
22 22
23 23 class winstdout(object):
24 24 '''stdout on windows misbehaves if sent through a pipe'''
25 25
26 26 def __init__(self, fp):
27 27 self.fp = fp
28 28
29 29 def __getattr__(self, key):
30 30 return getattr(self.fp, key)
31 31
32 32 def close(self):
33 33 try:
34 34 self.fp.close()
35 35 except: pass
36 36
37 37 def write(self, s):
38 38 try:
39 39 # This is workaround for "Not enough space" error on
40 40 # writing large size of data to console.
41 41 limit = 16000
42 42 l = len(s)
43 43 start = 0
44 44 self.softspace = 0
45 45 while start < l:
46 46 end = start + limit
47 47 self.fp.write(s[start:end])
48 48 start = end
49 49 except IOError, inst:
50 50 if inst.errno != 0:
51 51 raise
52 52 self.close()
53 53 raise IOError(errno.EPIPE, 'Broken pipe')
54 54
55 55 def flush(self):
56 56 try:
57 57 return self.fp.flush()
58 58 except IOError, inst:
59 59 if inst.errno != errno.EINVAL:
60 60 raise
61 61 self.close()
62 62 raise IOError(errno.EPIPE, 'Broken pipe')
63 63
64 64 sys.stdout = winstdout(sys.stdout)
65 65
66 66 def _is_win_9x():
67 67 '''return true if run on windows 95, 98 or me.'''
68 68 try:
69 69 return sys.getwindowsversion()[3] == 1
70 70 except AttributeError:
71 71 return 'command' in os.environ.get('comspec', '')
72 72
73 73 def openhardlinks():
74 74 return not _is_win_9x() and "win32api" in globals()
75 75
76 76 def system_rcpath():
77 77 try:
78 78 return system_rcpath_win32()
79 79 except:
80 80 return [r'c:\mercurial\mercurial.ini']
81 81
82 82 def user_rcpath():
83 83 '''return os-specific hgrc search path to the user dir'''
84 84 try:
85 85 path = user_rcpath_win32()
86 86 except:
87 87 home = os.path.expanduser('~')
88 88 path = [os.path.join(home, 'mercurial.ini'),
89 89 os.path.join(home, '.hgrc')]
90 90 userprofile = os.environ.get('USERPROFILE')
91 91 if userprofile:
92 92 path.append(os.path.join(userprofile, 'mercurial.ini'))
93 93 path.append(os.path.join(userprofile, '.hgrc'))
94 94 return path
95 95
96 96 def parse_patch_output(output_line):
97 97 """parses the output produced by patch and returns the filename"""
98 98 pf = output_line[14:]
99 99 if pf[0] == '`':
100 100 pf = pf[1:-1] # Remove the quotes
101 101 return pf
102 102
103 103 def sshargs(sshcmd, host, user, port):
104 104 '''Build argument list for ssh or Plink'''
105 105 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
106 106 args = user and ("%s@%s" % (user, host)) or host
107 107 return port and ("%s %s %s" % (args, pflag, port)) or args
108 108
109 109 def testpid(pid):
110 110 '''return False if pid dead, True if running or not known'''
111 111 return True
112 112
113 113 def set_flags(f, l, x):
114 114 pass
115 115
116 116 def set_binary(fd):
117 117 # When run without console, pipes may expose invalid
118 118 # fileno(), usually set to -1.
119 119 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
120 120 msvcrt.setmode(fd.fileno(), os.O_BINARY)
121 121
122 122 def pconvert(path):
123 123 return '/'.join(path.split(os.sep))
124 124
125 125 def localpath(path):
126 126 return path.replace('/', '\\')
127 127
128 128 def normpath(path):
129 129 return pconvert(os.path.normpath(path))
130 130
131 131 def realpath(path):
132 132 '''
133 133 Returns the true, canonical file system path equivalent to the given
134 134 path.
135 135 '''
136 136 # TODO: There may be a more clever way to do this that also handles other,
137 137 # less common file systems.
138 138 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
139 139
140 140 def samestat(s1, s2):
141 141 return False
142 142
143 143 # A sequence of backslashes is special iff it precedes a double quote:
144 144 # - if there's an even number of backslashes, the double quote is not
145 145 # quoted (i.e. it ends the quoted region)
146 146 # - if there's an odd number of backslashes, the double quote is quoted
147 147 # - in both cases, every pair of backslashes is unquoted into a single
148 148 # backslash
149 149 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
150 150 # So, to quote a string, we must surround it in double quotes, double
151 151 # the number of backslashes that preceed double quotes and add another
152 152 # backslash before every double quote (being careful with the double
153 153 # quote we've appended to the end)
154 154 _quotere = None
155 155 def shellquote(s):
156 156 global _quotere
157 157 if _quotere is None:
158 158 _quotere = re.compile(r'(\\*)("|\\$)')
159 159 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
160 160
161 161 def quotecommand(cmd):
162 162 """Build a command string suitable for os.popen* calls."""
163 163 if sys.version_info < (2, 7, 1):
164 164 # Python versions since 2.7.1 do this extra quoting themselves
165 165 return '"' + cmd + '"'
166 166 return cmd
167 167
168 168 def popen(command, mode='r'):
169 169 # Work around "popen spawned process may not write to stdout
170 170 # under windows"
171 171 # http://bugs.python.org/issue1366
172 172 command += " 2> %s" % nulldev
173 173 return os.popen(quotecommand(command), mode)
174 174
175 175 def explain_exit(code):
176 176 return _("exited with status %d") % code, code
177 177
178 178 # if you change this stub into a real check, please try to implement the
179 179 # username and groupname functions above, too.
180 180 def isowner(st):
181 181 return True
182 182
183 183 def find_exe(command):
184 184 '''Find executable for command searching like cmd.exe does.
185 185 If command is a basename then PATH is searched for command.
186 186 PATH isn't searched if command is an absolute or relative path.
187 187 An extension from PATHEXT is found and added if not present.
188 188 If command isn't found None is returned.'''
189 189 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
190 190 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
191 191 if os.path.splitext(command)[1].lower() in pathexts:
192 192 pathexts = ['']
193 193
194 194 def findexisting(pathcommand):
195 195 'Will append extension (if needed) and return existing file'
196 196 for ext in pathexts:
197 197 executable = pathcommand + ext
198 198 if os.path.exists(executable):
199 199 return executable
200 200 return None
201 201
202 202 if os.sep in command:
203 203 return findexisting(command)
204 204
205 205 for path in os.environ.get('PATH', '').split(os.pathsep):
206 206 executable = findexisting(os.path.join(path, command))
207 207 if executable is not None:
208 208 return executable
209 209 return findexisting(os.path.expanduser(os.path.expandvars(command)))
210 210
211 211 def set_signal_handler():
212 212 try:
213 213 set_signal_handler_win32()
214 214 except NameError:
215 215 pass
216 216
217 217 def statfiles(files):
218 218 '''Stat each file in files and yield stat or None if file does not exist.
219 219 Cluster and cache stat per directory to minimize number of OS stat calls.'''
220 220 ncase = os.path.normcase
221 221 dircache = {} # dirname -> filename -> status | None if file does not exist
222 222 for nf in files:
223 223 nf = ncase(nf)
224 224 dir, base = os.path.split(nf)
225 225 if not dir:
226 226 dir = '.'
227 227 cache = dircache.get(dir, None)
228 228 if cache is None:
229 229 try:
230 230 dmap = dict([(ncase(n), s)
231 231 for n, k, s in osutil.listdir(dir, True)])
232 232 except OSError, err:
233 233 # handle directory not found in Python version prior to 2.5
234 234 # Python <= 2.4 returns native Windows code 3 in errno
235 235 # Python >= 2.5 returns ENOENT and adds winerror field
236 236 # EINVAL is raised if dir is not a directory.
237 237 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
238 238 errno.ENOTDIR):
239 239 raise
240 240 dmap = {}
241 241 cache = dircache.setdefault(dir, dmap)
242 242 yield cache.get(base, None)
243 243
244 244 def getuser():
245 245 '''return name of current user'''
246 246 raise error.Abort(_('user name not available - set USERNAME '
247 247 'environment variable'))
248 248
249 249 def username(uid=None):
250 250 """Return the name of the user with the given uid.
251 251
252 252 If uid is None, return the name of the current user."""
253 253 return None
254 254
255 255 def groupname(gid=None):
256 256 """Return the name of the group with the given gid.
257 257
258 258 If gid is None, return the name of the current group."""
259 259 return None
260 260
261 261 def _removedirs(name):
262 262 """special version of os.removedirs that does not remove symlinked
263 263 directories or junction points if they actually contain files"""
264 264 if osutil.listdir(name):
265 265 return
266 266 os.rmdir(name)
267 267 head, tail = os.path.split(name)
268 268 if not tail:
269 269 head, tail = os.path.split(head)
270 270 while head and tail:
271 271 try:
272 272 if osutil.listdir(head):
273 273 return
274 274 os.rmdir(head)
275 275 except:
276 276 break
277 277 head, tail = os.path.split(head)
278 278
279 279 def unlinkpath(f):
280 280 """unlink and remove the directory if it is empty"""
281 281 os.unlink(f)
282 282 # try removing directories that might now be empty
283 283 try:
284 284 _removedirs(os.path.dirname(f))
285 285 except OSError:
286 286 pass
287 287
288 def unlink(f):
289 '''try to implement POSIX' unlink semantics on Windows'''
290
291 # POSIX allows to unlink and rename open files. Windows has serious
292 # problems with doing that:
293 # - Calling os.unlink (or os.rename) on a file f fails if f or any
294 # hardlinked copy of f has been opened with Python's open(). There is no
295 # way such a file can be deleted or renamed on Windows (other than
296 # scheduling the delete or rename for the next reboot).
297 # - Calling os.unlink on a file that has been opened with Mercurial's
298 # posixfile (or comparable methods) will delay the actual deletion of
299 # the file for as long as the file is held open. The filename is blocked
300 # during that time and cannot be used for recreating a new file under
301 # that same name ("zombie file"). Directories containing such zombie files
302 # cannot be removed or moved.
303 # A file that has been opened with posixfile can be renamed, so we rename
304 # f to a random temporary name before calling os.unlink on it. This allows
305 # callers to recreate f immediately while having other readers do their
306 # implicit zombie filename blocking on a temporary name.
307
308 for tries in xrange(10):
309 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
310 try:
311 os.rename(f, temp) # raises OSError EEXIST if temp exists
312 break
313 except OSError, e:
314 if e.errno != errno.EEXIST:
315 raise
316 else:
317 raise IOError, (errno.EEXIST, "No usable temporary filename found")
318
319 try:
320 os.unlink(temp)
321 except:
322 # Some very rude AV-scanners on Windows may cause this unlink to fail.
323 # Not aborting here just leaks the temp file, whereas aborting at this
324 # point may leave serious inconsistencies. Ideally, we would notify
325 # the user in this case here.
326 pass
327
288 328 def rename(src, dst):
289 329 '''atomically rename file src to dst, replacing dst if it exists'''
290 330 try:
291 331 os.rename(src, dst)
292 332 except OSError, e:
293 333 if e.errno != errno.EEXIST:
294 334 raise
295
296 # On windows, rename to existing file is not allowed, so we
297 # must delete destination first. But if a file is open, unlink
298 # schedules it for delete but does not delete it. Rename
299 # happens immediately even for open files, so we rename
300 # destination to a temporary name, then delete that. Then
301 # rename is safe to do.
302 # The temporary name is chosen at random to avoid the situation
303 # where a file is left lying around from a previous aborted run.
304
305 for tries in xrange(10):
306 temp = '%s-%08x' % (dst, random.randint(0, 0xffffffff))
307 try:
308 os.rename(dst, temp) # raises OSError EEXIST if temp exists
309 break
310 except OSError, e:
311 if e.errno != errno.EEXIST:
312 raise
313 else:
314 raise IOError, (errno.EEXIST, "No usable temporary filename found")
315
316 try:
317 os.unlink(temp)
318 except:
319 # Some rude AV-scanners on Windows may cause the unlink to
320 # fail. Not aborting here just leaks the temp file, whereas
321 # aborting at this point may leave serious inconsistencies.
322 # Ideally, we would notify the user here.
323 pass
335 unlink(dst)
324 336 os.rename(src, dst)
325 337
326 338 def spawndetached(args):
327 339 # No standard library function really spawns a fully detached
328 340 # process under win32 because they allocate pipes or other objects
329 341 # to handle standard streams communications. Passing these objects
330 342 # to the child process requires handle inheritance to be enabled
331 343 # which makes really detached processes impossible.
332 344 class STARTUPINFO:
333 345 dwFlags = subprocess.STARTF_USESHOWWINDOW
334 346 hStdInput = None
335 347 hStdOutput = None
336 348 hStdError = None
337 349 wShowWindow = subprocess.SW_HIDE
338 350
339 351 args = subprocess.list2cmdline(args)
340 352 # Not running the command in shell mode makes python26 hang when
341 353 # writing to hgweb output socket.
342 354 comspec = os.environ.get("COMSPEC", "cmd.exe")
343 355 args = comspec + " /c " + args
344 356 hp, ht, pid, tid = subprocess.CreateProcess(
345 357 None, args,
346 358 # no special security
347 359 None, None,
348 360 # Do not inherit handles
349 361 0,
350 362 # DETACHED_PROCESS
351 363 0x00000008,
352 364 os.environ,
353 365 os.getcwd(),
354 366 STARTUPINFO())
355 367 return pid
356 368
357 369 def gethgcmd():
358 370 return [sys.executable] + sys.argv[:1]
359 371
360 372 def termwidth():
361 373 # cmd.exe does not handle CR like a unix console, the CR is
362 374 # counted in the line length. On 80 columns consoles, if 80
363 375 # characters are written, the following CR won't apply on the
364 376 # current line but on the new one. Keep room for it.
365 377 return 79
366 378
367 379 def groupmembers(name):
368 380 # Don't support groups on Windows for now
369 381 raise KeyError()
370 382
371 383 try:
372 384 # override functions with win32 versions if possible
373 385 from win32 import *
374 386 except ImportError:
375 387 pass
376 388
377 389 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now