comparison mercurial/windows.py @ 7890:e710f0f592b2

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