Mercurial > hg
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 |