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