##// END OF EJS Templates
posix: do not use fstat in isowner...
Martin Geisler -
r8657:3fa92c61 default
parent child Browse files
Show More
@@ -1,219 +1,214 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
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 = file
12 posixfile = file
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 expandglobs = False
16 expandglobs = False
17
17
18 umask = os.umask(0)
18 umask = os.umask(0)
19 os.umask(umask)
19 os.umask(umask)
20
20
21 def openhardlinks():
21 def openhardlinks():
22 '''return true if it is safe to hold open file handles to hardlinks'''
22 '''return true if it is safe to hold open file handles to hardlinks'''
23 return True
23 return True
24
24
25 def rcfiles(path):
25 def rcfiles(path):
26 rcs = [os.path.join(path, 'hgrc')]
26 rcs = [os.path.join(path, 'hgrc')]
27 rcdir = os.path.join(path, 'hgrc.d')
27 rcdir = os.path.join(path, 'hgrc.d')
28 try:
28 try:
29 rcs.extend([os.path.join(rcdir, f)
29 rcs.extend([os.path.join(rcdir, f)
30 for f, kind in osutil.listdir(rcdir)
30 for f, kind in osutil.listdir(rcdir)
31 if f.endswith(".rc")])
31 if f.endswith(".rc")])
32 except OSError:
32 except OSError:
33 pass
33 pass
34 return rcs
34 return rcs
35
35
36 def system_rcpath():
36 def system_rcpath():
37 path = []
37 path = []
38 # old mod_python does not set sys.argv
38 # old mod_python does not set sys.argv
39 if len(getattr(sys, 'argv', [])) > 0:
39 if len(getattr(sys, 'argv', [])) > 0:
40 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
40 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
41 '/../etc/mercurial'))
41 '/../etc/mercurial'))
42 path.extend(rcfiles('/etc/mercurial'))
42 path.extend(rcfiles('/etc/mercurial'))
43 return path
43 return path
44
44
45 def user_rcpath():
45 def user_rcpath():
46 return [os.path.expanduser('~/.hgrc')]
46 return [os.path.expanduser('~/.hgrc')]
47
47
48 def parse_patch_output(output_line):
48 def parse_patch_output(output_line):
49 """parses the output produced by patch and returns the file name"""
49 """parses the output produced by patch and returns the file name"""
50 pf = output_line[14:]
50 pf = output_line[14:]
51 if os.sys.platform == 'OpenVMS':
51 if os.sys.platform == 'OpenVMS':
52 if pf[0] == '`':
52 if pf[0] == '`':
53 pf = pf[1:-1] # Remove the quotes
53 pf = pf[1:-1] # Remove the quotes
54 else:
54 else:
55 if pf.startswith("'") and pf.endswith("'") and " " in pf:
55 if pf.startswith("'") and pf.endswith("'") and " " in pf:
56 pf = pf[1:-1] # Remove the quotes
56 pf = pf[1:-1] # Remove the quotes
57 return pf
57 return pf
58
58
59 def sshargs(sshcmd, host, user, port):
59 def sshargs(sshcmd, host, user, port):
60 '''Build argument list for ssh'''
60 '''Build argument list for ssh'''
61 args = user and ("%s@%s" % (user, host)) or host
61 args = user and ("%s@%s" % (user, host)) or host
62 return port and ("%s -p %s" % (args, port)) or args
62 return port and ("%s -p %s" % (args, port)) or args
63
63
64 def is_exec(f):
64 def is_exec(f):
65 """check whether a file is executable"""
65 """check whether a file is executable"""
66 return (os.lstat(f).st_mode & 0100 != 0)
66 return (os.lstat(f).st_mode & 0100 != 0)
67
67
68 def set_flags(f, l, x):
68 def set_flags(f, l, x):
69 s = os.lstat(f).st_mode
69 s = os.lstat(f).st_mode
70 if l:
70 if l:
71 if not stat.S_ISLNK(s):
71 if not stat.S_ISLNK(s):
72 # switch file to link
72 # switch file to link
73 data = file(f).read()
73 data = file(f).read()
74 os.unlink(f)
74 os.unlink(f)
75 try:
75 try:
76 os.symlink(data, f)
76 os.symlink(data, f)
77 except:
77 except:
78 # failed to make a link, rewrite file
78 # failed to make a link, rewrite file
79 file(f, "w").write(data)
79 file(f, "w").write(data)
80 # no chmod needed at this point
80 # no chmod needed at this point
81 return
81 return
82 if stat.S_ISLNK(s):
82 if stat.S_ISLNK(s):
83 # switch link to file
83 # switch link to file
84 data = os.readlink(f)
84 data = os.readlink(f)
85 os.unlink(f)
85 os.unlink(f)
86 file(f, "w").write(data)
86 file(f, "w").write(data)
87 s = 0666 & ~umask # avoid restatting for chmod
87 s = 0666 & ~umask # avoid restatting for chmod
88
88
89 sx = s & 0100
89 sx = s & 0100
90 if x and not sx:
90 if x and not sx:
91 # Turn on +x for every +r bit when making a file executable
91 # Turn on +x for every +r bit when making a file executable
92 # and obey umask.
92 # and obey umask.
93 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
93 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
94 elif not x and sx:
94 elif not x and sx:
95 # Turn off all +x bits
95 # Turn off all +x bits
96 os.chmod(f, s & 0666)
96 os.chmod(f, s & 0666)
97
97
98 def set_binary(fd):
98 def set_binary(fd):
99 pass
99 pass
100
100
101 def pconvert(path):
101 def pconvert(path):
102 return path
102 return path
103
103
104 def localpath(path):
104 def localpath(path):
105 return path
105 return path
106
106
107 def shellquote(s):
107 def shellquote(s):
108 if os.sys.platform == 'OpenVMS':
108 if os.sys.platform == 'OpenVMS':
109 return '"%s"' % s
109 return '"%s"' % s
110 else:
110 else:
111 return "'%s'" % s.replace("'", "'\\''")
111 return "'%s'" % s.replace("'", "'\\''")
112
112
113 def quotecommand(cmd):
113 def quotecommand(cmd):
114 return cmd
114 return cmd
115
115
116 def popen(command, mode='r'):
116 def popen(command, mode='r'):
117 return os.popen(command, mode)
117 return os.popen(command, mode)
118
118
119 def testpid(pid):
119 def testpid(pid):
120 '''return False if pid dead, True if running or not sure'''
120 '''return False if pid dead, True if running or not sure'''
121 if os.sys.platform == 'OpenVMS':
121 if os.sys.platform == 'OpenVMS':
122 return True
122 return True
123 try:
123 try:
124 os.kill(pid, 0)
124 os.kill(pid, 0)
125 return True
125 return True
126 except OSError, inst:
126 except OSError, inst:
127 return inst.errno != errno.ESRCH
127 return inst.errno != errno.ESRCH
128
128
129 def explain_exit(code):
129 def explain_exit(code):
130 """return a 2-tuple (desc, code) describing a process's status"""
130 """return a 2-tuple (desc, code) describing a process's status"""
131 if os.WIFEXITED(code):
131 if os.WIFEXITED(code):
132 val = os.WEXITSTATUS(code)
132 val = os.WEXITSTATUS(code)
133 return _("exited with status %d") % val, val
133 return _("exited with status %d") % val, val
134 elif os.WIFSIGNALED(code):
134 elif os.WIFSIGNALED(code):
135 val = os.WTERMSIG(code)
135 val = os.WTERMSIG(code)
136 return _("killed by signal %d") % val, val
136 return _("killed by signal %d") % val, val
137 elif os.WIFSTOPPED(code):
137 elif os.WIFSTOPPED(code):
138 val = os.WSTOPSIG(code)
138 val = os.WSTOPSIG(code)
139 return _("stopped by signal %d") % val, val
139 return _("stopped by signal %d") % val, val
140 raise ValueError(_("invalid exit code"))
140 raise ValueError(_("invalid exit code"))
141
141
142 def isowner(fp, st=None):
142 def isowner(st):
143 """Return True if the file object f belongs to the current user.
143 """Return True if the stat object st is from the current user."""
144
145 The return value of a util.fstat(f) may be passed as the st argument.
146 """
147 if st is None:
148 st = fstat(fp)
149 return st.st_uid == os.getuid()
144 return st.st_uid == os.getuid()
150
145
151 def find_exe(command):
146 def find_exe(command):
152 '''Find executable for command searching like which does.
147 '''Find executable for command searching like which does.
153 If command is a basename then PATH is searched for command.
148 If command is a basename then PATH is searched for command.
154 PATH isn't searched if command is an absolute or relative path.
149 PATH isn't searched if command is an absolute or relative path.
155 If command isn't found None is returned.'''
150 If command isn't found None is returned.'''
156 if sys.platform == 'OpenVMS':
151 if sys.platform == 'OpenVMS':
157 return command
152 return command
158
153
159 def findexisting(executable):
154 def findexisting(executable):
160 'Will return executable if existing file'
155 'Will return executable if existing file'
161 if os.path.exists(executable):
156 if os.path.exists(executable):
162 return executable
157 return executable
163 return None
158 return None
164
159
165 if os.sep in command:
160 if os.sep in command:
166 return findexisting(command)
161 return findexisting(command)
167
162
168 for path in os.environ.get('PATH', '').split(os.pathsep):
163 for path in os.environ.get('PATH', '').split(os.pathsep):
169 executable = findexisting(os.path.join(path, command))
164 executable = findexisting(os.path.join(path, command))
170 if executable is not None:
165 if executable is not None:
171 return executable
166 return executable
172 return None
167 return None
173
168
174 def set_signal_handler():
169 def set_signal_handler():
175 pass
170 pass
176
171
177 def statfiles(files):
172 def statfiles(files):
178 'Stat each file in files and yield stat or None if file does not exist.'
173 'Stat each file in files and yield stat or None if file does not exist.'
179 lstat = os.lstat
174 lstat = os.lstat
180 for nf in files:
175 for nf in files:
181 try:
176 try:
182 st = lstat(nf)
177 st = lstat(nf)
183 except OSError, err:
178 except OSError, err:
184 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
179 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
185 raise
180 raise
186 st = None
181 st = None
187 yield st
182 yield st
188
183
189 def getuser():
184 def getuser():
190 '''return name of current user'''
185 '''return name of current user'''
191 return getpass.getuser()
186 return getpass.getuser()
192
187
193 def expand_glob(pats):
188 def expand_glob(pats):
194 '''On Windows, expand the implicit globs in a list of patterns'''
189 '''On Windows, expand the implicit globs in a list of patterns'''
195 return list(pats)
190 return list(pats)
196
191
197 def username(uid=None):
192 def username(uid=None):
198 """Return the name of the user with the given uid.
193 """Return the name of the user with the given uid.
199
194
200 If uid is None, return the name of the current user."""
195 If uid is None, return the name of the current user."""
201
196
202 if uid is None:
197 if uid is None:
203 uid = os.getuid()
198 uid = os.getuid()
204 try:
199 try:
205 return pwd.getpwuid(uid)[0]
200 return pwd.getpwuid(uid)[0]
206 except KeyError:
201 except KeyError:
207 return str(uid)
202 return str(uid)
208
203
209 def groupname(gid=None):
204 def groupname(gid=None):
210 """Return the name of the group with the given gid.
205 """Return the name of the group with the given gid.
211
206
212 If gid is None, return the name of the current group."""
207 If gid is None, return the name of the current group."""
213
208
214 if gid is None:
209 if gid is None:
215 gid = os.getgid()
210 gid = os.getgid()
216 try:
211 try:
217 return grp.getgrgid(gid)[0]
212 return grp.getgrgid(gid)[0]
218 except KeyError:
213 except KeyError:
219 return str(gid)
214 return str(gid)
@@ -1,346 +1,346 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, util, error
10 import config, util, error
11
11
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
13 '0': False, 'no': False, 'false': False, 'off': False}
13 '0': False, 'no': False, 'false': False, 'off': False}
14
14
15 class ui(object):
15 class ui(object):
16 def __init__(self, src=None):
16 def __init__(self, src=None):
17 self._buffers = []
17 self._buffers = []
18 self.quiet = self.verbose = self.debugflag = self._traceback = False
18 self.quiet = self.verbose = self.debugflag = self._traceback = False
19 self._reportuntrusted = True
19 self._reportuntrusted = True
20 self._ocfg = config.config() # overlay
20 self._ocfg = config.config() # overlay
21 self._tcfg = config.config() # trusted
21 self._tcfg = config.config() # trusted
22 self._ucfg = config.config() # untrusted
22 self._ucfg = config.config() # untrusted
23 self._trustusers = set()
23 self._trustusers = set()
24 self._trustgroups = set()
24 self._trustgroups = set()
25
25
26 if src:
26 if src:
27 self._tcfg = src._tcfg.copy()
27 self._tcfg = src._tcfg.copy()
28 self._ucfg = src._ucfg.copy()
28 self._ucfg = src._ucfg.copy()
29 self._ocfg = src._ocfg.copy()
29 self._ocfg = src._ocfg.copy()
30 self._trustusers = src._trustusers.copy()
30 self._trustusers = src._trustusers.copy()
31 self._trustgroups = src._trustgroups.copy()
31 self._trustgroups = src._trustgroups.copy()
32 self.fixconfig()
32 self.fixconfig()
33 else:
33 else:
34 # we always trust global config files
34 # we always trust global config files
35 for f in util.rcpath():
35 for f in util.rcpath():
36 self.readconfig(f, trust=True)
36 self.readconfig(f, trust=True)
37
37
38 def copy(self):
38 def copy(self):
39 return self.__class__(self)
39 return self.__class__(self)
40
40
41 def _is_trusted(self, fp, f):
41 def _is_trusted(self, fp, f):
42 st = util.fstat(fp)
42 st = util.fstat(fp)
43 if util.isowner(fp, st):
43 if util.isowner(st):
44 return True
44 return True
45
45
46 tusers, tgroups = self._trustusers, self._trustgroups
46 tusers, tgroups = self._trustusers, self._trustgroups
47 if '*' in tusers or '*' in tgroups:
47 if '*' in tusers or '*' in tgroups:
48 return True
48 return True
49
49
50 user = util.username(st.st_uid)
50 user = util.username(st.st_uid)
51 group = util.groupname(st.st_gid)
51 group = util.groupname(st.st_gid)
52 if user in tusers or group in tgroups or user == util.username():
52 if user in tusers or group in tgroups or user == util.username():
53 return True
53 return True
54
54
55 if self._reportuntrusted:
55 if self._reportuntrusted:
56 self.warn(_('Not trusting file %s from untrusted '
56 self.warn(_('Not trusting file %s from untrusted '
57 'user %s, group %s\n') % (f, user, group))
57 'user %s, group %s\n') % (f, user, group))
58 return False
58 return False
59
59
60 def readconfig(self, filename, root=None, trust=False,
60 def readconfig(self, filename, root=None, trust=False,
61 sections=None, remap=None):
61 sections=None, remap=None):
62 try:
62 try:
63 fp = open(filename)
63 fp = open(filename)
64 except IOError:
64 except IOError:
65 if not sections: # ignore unless we were looking for something
65 if not sections: # ignore unless we were looking for something
66 return
66 return
67 raise
67 raise
68
68
69 cfg = config.config()
69 cfg = config.config()
70 trusted = sections or trust or self._is_trusted(fp, filename)
70 trusted = sections or trust or self._is_trusted(fp, filename)
71
71
72 try:
72 try:
73 cfg.read(filename, fp, sections=sections, remap=remap)
73 cfg.read(filename, fp, sections=sections, remap=remap)
74 except error.ConfigError, inst:
74 except error.ConfigError, inst:
75 if trusted:
75 if trusted:
76 raise
76 raise
77 self.warn(_("Ignored: %s\n") % str(inst))
77 self.warn(_("Ignored: %s\n") % str(inst))
78
78
79 if trusted:
79 if trusted:
80 self._tcfg.update(cfg)
80 self._tcfg.update(cfg)
81 self._tcfg.update(self._ocfg)
81 self._tcfg.update(self._ocfg)
82 self._ucfg.update(cfg)
82 self._ucfg.update(cfg)
83 self._ucfg.update(self._ocfg)
83 self._ucfg.update(self._ocfg)
84
84
85 if root is None:
85 if root is None:
86 root = os.path.expanduser('~')
86 root = os.path.expanduser('~')
87 self.fixconfig(root=root)
87 self.fixconfig(root=root)
88
88
89 def fixconfig(self, root=None):
89 def fixconfig(self, root=None):
90 # translate paths relative to root (or home) into absolute paths
90 # translate paths relative to root (or home) into absolute paths
91 root = root or os.getcwd()
91 root = root or os.getcwd()
92 for c in self._tcfg, self._ucfg, self._ocfg:
92 for c in self._tcfg, self._ucfg, self._ocfg:
93 for n, p in c.items('paths'):
93 for n, p in c.items('paths'):
94 if p and "://" not in p and not os.path.isabs(p):
94 if p and "://" not in p and not os.path.isabs(p):
95 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
95 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
96
96
97 # update ui options
97 # update ui options
98 self.debugflag = self.configbool('ui', 'debug')
98 self.debugflag = self.configbool('ui', 'debug')
99 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
99 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
101 if self.verbose and self.quiet:
101 if self.verbose and self.quiet:
102 self.quiet = self.verbose = False
102 self.quiet = self.verbose = False
103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
104 self._traceback = self.configbool('ui', 'traceback', False)
104 self._traceback = self.configbool('ui', 'traceback', False)
105
105
106 # update trust information
106 # update trust information
107 self._trustusers.update(self.configlist('trusted', 'users'))
107 self._trustusers.update(self.configlist('trusted', 'users'))
108 self._trustgroups.update(self.configlist('trusted', 'groups'))
108 self._trustgroups.update(self.configlist('trusted', 'groups'))
109
109
110 def setconfig(self, section, name, value):
110 def setconfig(self, section, name, value):
111 for cfg in (self._ocfg, self._tcfg, self._ucfg):
111 for cfg in (self._ocfg, self._tcfg, self._ucfg):
112 cfg.set(section, name, value)
112 cfg.set(section, name, value)
113 self.fixconfig()
113 self.fixconfig()
114
114
115 def _data(self, untrusted):
115 def _data(self, untrusted):
116 return untrusted and self._ucfg or self._tcfg
116 return untrusted and self._ucfg or self._tcfg
117
117
118 def configsource(self, section, name, untrusted=False):
118 def configsource(self, section, name, untrusted=False):
119 return self._data(untrusted).source(section, name) or 'none'
119 return self._data(untrusted).source(section, name) or 'none'
120
120
121 def config(self, section, name, default=None, untrusted=False):
121 def config(self, section, name, default=None, untrusted=False):
122 value = self._data(untrusted).get(section, name, default)
122 value = self._data(untrusted).get(section, name, default)
123 if self.debugflag and not untrusted and self._reportuntrusted:
123 if self.debugflag and not untrusted and self._reportuntrusted:
124 uvalue = self._ucfg.get(section, name)
124 uvalue = self._ucfg.get(section, name)
125 if uvalue is not None and uvalue != value:
125 if uvalue is not None and uvalue != value:
126 self.debug(_("ignoring untrusted configuration option "
126 self.debug(_("ignoring untrusted configuration option "
127 "%s.%s = %s\n") % (section, name, uvalue))
127 "%s.%s = %s\n") % (section, name, uvalue))
128 return value
128 return value
129
129
130 def configbool(self, section, name, default=False, untrusted=False):
130 def configbool(self, section, name, default=False, untrusted=False):
131 v = self.config(section, name, None, untrusted)
131 v = self.config(section, name, None, untrusted)
132 if v is None:
132 if v is None:
133 return default
133 return default
134 if v.lower() not in _booleans:
134 if v.lower() not in _booleans:
135 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
135 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
136 % (section, name, v))
136 % (section, name, v))
137 return _booleans[v.lower()]
137 return _booleans[v.lower()]
138
138
139 def configlist(self, section, name, default=None, untrusted=False):
139 def configlist(self, section, name, default=None, untrusted=False):
140 """Return a list of comma/space separated strings"""
140 """Return a list of comma/space separated strings"""
141 result = self.config(section, name, untrusted=untrusted)
141 result = self.config(section, name, untrusted=untrusted)
142 if result is None:
142 if result is None:
143 result = default or []
143 result = default or []
144 if isinstance(result, basestring):
144 if isinstance(result, basestring):
145 result = result.replace(",", " ").split()
145 result = result.replace(",", " ").split()
146 return result
146 return result
147
147
148 def has_section(self, section, untrusted=False):
148 def has_section(self, section, untrusted=False):
149 '''tell whether section exists in config.'''
149 '''tell whether section exists in config.'''
150 return section in self._data(untrusted)
150 return section in self._data(untrusted)
151
151
152 def configitems(self, section, untrusted=False):
152 def configitems(self, section, untrusted=False):
153 items = self._data(untrusted).items(section)
153 items = self._data(untrusted).items(section)
154 if self.debugflag and not untrusted and self._reportuntrusted:
154 if self.debugflag and not untrusted and self._reportuntrusted:
155 for k, v in self._ucfg.items(section):
155 for k, v in self._ucfg.items(section):
156 if self._tcfg.get(section, k) != v:
156 if self._tcfg.get(section, k) != v:
157 self.debug(_("ignoring untrusted configuration option "
157 self.debug(_("ignoring untrusted configuration option "
158 "%s.%s = %s\n") % (section, k, v))
158 "%s.%s = %s\n") % (section, k, v))
159 return items
159 return items
160
160
161 def walkconfig(self, untrusted=False):
161 def walkconfig(self, untrusted=False):
162 cfg = self._data(untrusted)
162 cfg = self._data(untrusted)
163 for section in cfg.sections():
163 for section in cfg.sections():
164 for name, value in self.configitems(section, untrusted):
164 for name, value in self.configitems(section, untrusted):
165 yield section, name, str(value).replace('\n', '\\n')
165 yield section, name, str(value).replace('\n', '\\n')
166
166
167 def username(self):
167 def username(self):
168 """Return default username to be used in commits.
168 """Return default username to be used in commits.
169
169
170 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
170 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
171 and stop searching if one of these is set.
171 and stop searching if one of these is set.
172 If not found and ui.askusername is True, ask the user, else use
172 If not found and ui.askusername is True, ask the user, else use
173 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
173 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
174 """
174 """
175 user = os.environ.get("HGUSER")
175 user = os.environ.get("HGUSER")
176 if user is None:
176 if user is None:
177 user = self.config("ui", "username")
177 user = self.config("ui", "username")
178 if user is None:
178 if user is None:
179 user = os.environ.get("EMAIL")
179 user = os.environ.get("EMAIL")
180 if user is None and self.configbool("ui", "askusername"):
180 if user is None and self.configbool("ui", "askusername"):
181 user = self.prompt(_("enter a commit username:"), default=None)
181 user = self.prompt(_("enter a commit username:"), default=None)
182 if user is None:
182 if user is None:
183 try:
183 try:
184 user = '%s@%s' % (util.getuser(), socket.getfqdn())
184 user = '%s@%s' % (util.getuser(), socket.getfqdn())
185 self.warn(_("No username found, using '%s' instead\n") % user)
185 self.warn(_("No username found, using '%s' instead\n") % user)
186 except KeyError:
186 except KeyError:
187 pass
187 pass
188 if not user:
188 if not user:
189 raise util.Abort(_("Please specify a username."))
189 raise util.Abort(_("Please specify a username."))
190 if "\n" in user:
190 if "\n" in user:
191 raise util.Abort(_("username %s contains a newline\n") % repr(user))
191 raise util.Abort(_("username %s contains a newline\n") % repr(user))
192 return user
192 return user
193
193
194 def shortuser(self, user):
194 def shortuser(self, user):
195 """Return a short representation of a user name or email address."""
195 """Return a short representation of a user name or email address."""
196 if not self.verbose: user = util.shortuser(user)
196 if not self.verbose: user = util.shortuser(user)
197 return user
197 return user
198
198
199 def _path(self, loc):
199 def _path(self, loc):
200 p = self.config('paths', loc)
200 p = self.config('paths', loc)
201 if p and '%%' in p:
201 if p and '%%' in p:
202 self.warn('(deprecated \'%%\' in path %s=%s from %s)\n' %
202 self.warn('(deprecated \'%%\' in path %s=%s from %s)\n' %
203 (loc, p, self.configsource('paths', loc)))
203 (loc, p, self.configsource('paths', loc)))
204 p = p.replace('%%', '%')
204 p = p.replace('%%', '%')
205 return p
205 return p
206
206
207 def expandpath(self, loc, default=None):
207 def expandpath(self, loc, default=None):
208 """Return repository location relative to cwd or from [paths]"""
208 """Return repository location relative to cwd or from [paths]"""
209 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
209 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
210 return loc
210 return loc
211
211
212 path = self._path(loc)
212 path = self._path(loc)
213 if not path and default is not None:
213 if not path and default is not None:
214 path = self._path(default)
214 path = self._path(default)
215 return path or loc
215 return path or loc
216
216
217 def pushbuffer(self):
217 def pushbuffer(self):
218 self._buffers.append([])
218 self._buffers.append([])
219
219
220 def popbuffer(self):
220 def popbuffer(self):
221 return "".join(self._buffers.pop())
221 return "".join(self._buffers.pop())
222
222
223 def write(self, *args):
223 def write(self, *args):
224 if self._buffers:
224 if self._buffers:
225 self._buffers[-1].extend([str(a) for a in args])
225 self._buffers[-1].extend([str(a) for a in args])
226 else:
226 else:
227 for a in args:
227 for a in args:
228 sys.stdout.write(str(a))
228 sys.stdout.write(str(a))
229
229
230 def write_err(self, *args):
230 def write_err(self, *args):
231 try:
231 try:
232 if not sys.stdout.closed: sys.stdout.flush()
232 if not sys.stdout.closed: sys.stdout.flush()
233 for a in args:
233 for a in args:
234 sys.stderr.write(str(a))
234 sys.stderr.write(str(a))
235 # stderr may be buffered under win32 when redirected to files,
235 # stderr may be buffered under win32 when redirected to files,
236 # including stdout.
236 # including stdout.
237 if not sys.stderr.closed: sys.stderr.flush()
237 if not sys.stderr.closed: sys.stderr.flush()
238 except IOError, inst:
238 except IOError, inst:
239 if inst.errno != errno.EPIPE:
239 if inst.errno != errno.EPIPE:
240 raise
240 raise
241
241
242 def flush(self):
242 def flush(self):
243 try: sys.stdout.flush()
243 try: sys.stdout.flush()
244 except: pass
244 except: pass
245 try: sys.stderr.flush()
245 try: sys.stderr.flush()
246 except: pass
246 except: pass
247
247
248 def interactive(self):
248 def interactive(self):
249 i = self.configbool("ui", "interactive", None)
249 i = self.configbool("ui", "interactive", None)
250 if i is None:
250 if i is None:
251 return sys.stdin.isatty()
251 return sys.stdin.isatty()
252 return i
252 return i
253
253
254 def _readline(self, prompt=''):
254 def _readline(self, prompt=''):
255 if sys.stdin.isatty():
255 if sys.stdin.isatty():
256 try:
256 try:
257 # magically add command line editing support, where
257 # magically add command line editing support, where
258 # available
258 # available
259 import readline
259 import readline
260 # force demandimport to really load the module
260 # force demandimport to really load the module
261 readline.read_history_file
261 readline.read_history_file
262 # windows sometimes raises something other than ImportError
262 # windows sometimes raises something other than ImportError
263 except Exception:
263 except Exception:
264 pass
264 pass
265 line = raw_input(prompt)
265 line = raw_input(prompt)
266 # When stdin is in binary mode on Windows, it can cause
266 # When stdin is in binary mode on Windows, it can cause
267 # raw_input() to emit an extra trailing carriage return
267 # raw_input() to emit an extra trailing carriage return
268 if os.linesep == '\r\n' and line and line[-1] == '\r':
268 if os.linesep == '\r\n' and line and line[-1] == '\r':
269 line = line[:-1]
269 line = line[:-1]
270 return line
270 return line
271
271
272 def prompt(self, msg, choices=None, default="y"):
272 def prompt(self, msg, choices=None, default="y"):
273 """Prompt user with msg, read response, and ensure it matches
273 """Prompt user with msg, read response, and ensure it matches
274 one of the provided choices. choices is a sequence of acceptable
274 one of the provided choices. choices is a sequence of acceptable
275 responses with the format: ('&None', 'E&xec', 'Sym&link')
275 responses with the format: ('&None', 'E&xec', 'Sym&link')
276 No sequence implies no response checking. Responses are case
276 No sequence implies no response checking. Responses are case
277 insensitive. If ui is not interactive, the default is returned.
277 insensitive. If ui is not interactive, the default is returned.
278 """
278 """
279 if not self.interactive():
279 if not self.interactive():
280 self.note(msg, ' ', default, "\n")
280 self.note(msg, ' ', default, "\n")
281 return default
281 return default
282 while True:
282 while True:
283 try:
283 try:
284 r = self._readline(msg + ' ')
284 r = self._readline(msg + ' ')
285 if not r:
285 if not r:
286 return default
286 return default
287 if not choices:
287 if not choices:
288 return r
288 return r
289 resps = [s[s.index('&')+1].lower() for s in choices]
289 resps = [s[s.index('&')+1].lower() for s in choices]
290 if r.lower() in resps:
290 if r.lower() in resps:
291 return r.lower()
291 return r.lower()
292 else:
292 else:
293 self.write(_("unrecognized response\n"))
293 self.write(_("unrecognized response\n"))
294 except EOFError:
294 except EOFError:
295 raise util.Abort(_('response expected'))
295 raise util.Abort(_('response expected'))
296
296
297 def getpass(self, prompt=None, default=None):
297 def getpass(self, prompt=None, default=None):
298 if not self.interactive(): return default
298 if not self.interactive(): return default
299 try:
299 try:
300 return getpass.getpass(prompt or _('password: '))
300 return getpass.getpass(prompt or _('password: '))
301 except EOFError:
301 except EOFError:
302 raise util.Abort(_('response expected'))
302 raise util.Abort(_('response expected'))
303 def status(self, *msg):
303 def status(self, *msg):
304 if not self.quiet: self.write(*msg)
304 if not self.quiet: self.write(*msg)
305 def warn(self, *msg):
305 def warn(self, *msg):
306 self.write_err(*msg)
306 self.write_err(*msg)
307 def note(self, *msg):
307 def note(self, *msg):
308 if self.verbose: self.write(*msg)
308 if self.verbose: self.write(*msg)
309 def debug(self, *msg):
309 def debug(self, *msg):
310 if self.debugflag: self.write(*msg)
310 if self.debugflag: self.write(*msg)
311 def edit(self, text, user):
311 def edit(self, text, user):
312 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
312 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
313 text=True)
313 text=True)
314 try:
314 try:
315 f = os.fdopen(fd, "w")
315 f = os.fdopen(fd, "w")
316 f.write(text)
316 f.write(text)
317 f.close()
317 f.close()
318
318
319 editor = self.geteditor()
319 editor = self.geteditor()
320
320
321 util.system("%s \"%s\"" % (editor, name),
321 util.system("%s \"%s\"" % (editor, name),
322 environ={'HGUSER': user},
322 environ={'HGUSER': user},
323 onerr=util.Abort, errprefix=_("edit failed"))
323 onerr=util.Abort, errprefix=_("edit failed"))
324
324
325 f = open(name)
325 f = open(name)
326 t = f.read()
326 t = f.read()
327 f.close()
327 f.close()
328 finally:
328 finally:
329 os.unlink(name)
329 os.unlink(name)
330
330
331 return t
331 return t
332
332
333 def traceback(self):
333 def traceback(self):
334 '''print exception traceback if traceback printing enabled.
334 '''print exception traceback if traceback printing enabled.
335 only to call in exception handler. returns true if traceback
335 only to call in exception handler. returns true if traceback
336 printed.'''
336 printed.'''
337 if self._traceback:
337 if self._traceback:
338 traceback.print_exc()
338 traceback.print_exc()
339 return self._traceback
339 return self._traceback
340
340
341 def geteditor(self):
341 def geteditor(self):
342 '''return editor to use'''
342 '''return editor to use'''
343 return (os.environ.get("HGEDITOR") or
343 return (os.environ.get("HGEDITOR") or
344 self.config("ui", "editor") or
344 self.config("ui", "editor") or
345 os.environ.get("VISUAL") or
345 os.environ.get("VISUAL") or
346 os.environ.get("EDITOR", "vi"))
346 os.environ.get("EDITOR", "vi"))
@@ -1,284 +1,284 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, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
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
10 import errno, msvcrt, os, re, sys
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 WinIOError(err)
20 raise WinIOError(err)
21 posixfile.__doc__ = osutil.posixfile.__doc__
21 posixfile.__doc__ = osutil.posixfile.__doc__
22
22
23 class winstdout:
23 class winstdout:
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 while start < l:
44 while start < l:
45 end = start + limit
45 end = start + limit
46 self.fp.write(s[start:end])
46 self.fp.write(s[start:end])
47 start = end
47 start = end
48 except IOError, inst:
48 except IOError, inst:
49 if inst.errno != 0: raise
49 if inst.errno != 0: raise
50 self.close()
50 self.close()
51 raise IOError(errno.EPIPE, 'Broken pipe')
51 raise IOError(errno.EPIPE, 'Broken pipe')
52
52
53 def flush(self):
53 def flush(self):
54 try:
54 try:
55 return self.fp.flush()
55 return self.fp.flush()
56 except IOError, inst:
56 except IOError, inst:
57 if inst.errno != errno.EINVAL: raise
57 if inst.errno != errno.EINVAL: raise
58 self.close()
58 self.close()
59 raise IOError(errno.EPIPE, 'Broken pipe')
59 raise IOError(errno.EPIPE, 'Broken pipe')
60
60
61 sys.stdout = winstdout(sys.stdout)
61 sys.stdout = winstdout(sys.stdout)
62
62
63 def _is_win_9x():
63 def _is_win_9x():
64 '''return true if run on windows 95, 98 or me.'''
64 '''return true if run on windows 95, 98 or me.'''
65 try:
65 try:
66 return sys.getwindowsversion()[3] == 1
66 return sys.getwindowsversion()[3] == 1
67 except AttributeError:
67 except AttributeError:
68 return 'command' in os.environ.get('comspec', '')
68 return 'command' in os.environ.get('comspec', '')
69
69
70 def openhardlinks():
70 def openhardlinks():
71 return not _is_win_9x() and "win32api" in globals()
71 return not _is_win_9x() and "win32api" in globals()
72
72
73 def system_rcpath():
73 def system_rcpath():
74 try:
74 try:
75 return system_rcpath_win32()
75 return system_rcpath_win32()
76 except:
76 except:
77 return [r'c:\mercurial\mercurial.ini']
77 return [r'c:\mercurial\mercurial.ini']
78
78
79 def user_rcpath():
79 def user_rcpath():
80 '''return os-specific hgrc search path to the user dir'''
80 '''return os-specific hgrc search path to the user dir'''
81 try:
81 try:
82 path = user_rcpath_win32()
82 path = user_rcpath_win32()
83 except:
83 except:
84 home = os.path.expanduser('~')
84 home = os.path.expanduser('~')
85 path = [os.path.join(home, 'mercurial.ini'),
85 path = [os.path.join(home, 'mercurial.ini'),
86 os.path.join(home, '.hgrc')]
86 os.path.join(home, '.hgrc')]
87 userprofile = os.environ.get('USERPROFILE')
87 userprofile = os.environ.get('USERPROFILE')
88 if userprofile:
88 if userprofile:
89 path.append(os.path.join(userprofile, 'mercurial.ini'))
89 path.append(os.path.join(userprofile, 'mercurial.ini'))
90 path.append(os.path.join(userprofile, '.hgrc'))
90 path.append(os.path.join(userprofile, '.hgrc'))
91 return path
91 return path
92
92
93 def parse_patch_output(output_line):
93 def parse_patch_output(output_line):
94 """parses the output produced by patch and returns the file name"""
94 """parses the output produced by patch and returns the file name"""
95 pf = output_line[14:]
95 pf = output_line[14:]
96 if pf[0] == '`':
96 if pf[0] == '`':
97 pf = pf[1:-1] # Remove the quotes
97 pf = pf[1:-1] # Remove the quotes
98 return pf
98 return pf
99
99
100 def sshargs(sshcmd, host, user, port):
100 def sshargs(sshcmd, host, user, port):
101 '''Build argument list for ssh or Plink'''
101 '''Build argument list for ssh or Plink'''
102 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
102 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
103 args = user and ("%s@%s" % (user, host)) or host
103 args = user and ("%s@%s" % (user, host)) or host
104 return port and ("%s %s %s" % (args, pflag, port)) or args
104 return port and ("%s %s %s" % (args, pflag, port)) or args
105
105
106 def testpid(pid):
106 def testpid(pid):
107 '''return False if pid dead, True if running or not known'''
107 '''return False if pid dead, True if running or not known'''
108 return True
108 return True
109
109
110 def set_flags(f, l, x):
110 def set_flags(f, l, x):
111 pass
111 pass
112
112
113 def set_binary(fd):
113 def set_binary(fd):
114 # When run without console, pipes may expose invalid
114 # When run without console, pipes may expose invalid
115 # fileno(), usually set to -1.
115 # fileno(), usually set to -1.
116 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
116 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
117 msvcrt.setmode(fd.fileno(), os.O_BINARY)
117 msvcrt.setmode(fd.fileno(), os.O_BINARY)
118
118
119 def pconvert(path):
119 def pconvert(path):
120 return '/'.join(path.split(os.sep))
120 return '/'.join(path.split(os.sep))
121
121
122 def localpath(path):
122 def localpath(path):
123 return path.replace('/', '\\')
123 return path.replace('/', '\\')
124
124
125 def normpath(path):
125 def normpath(path):
126 return pconvert(os.path.normpath(path))
126 return pconvert(os.path.normpath(path))
127
127
128 def samestat(s1, s2):
128 def samestat(s1, s2):
129 return False
129 return False
130
130
131 # A sequence of backslashes is special iff it precedes a double quote:
131 # A sequence of backslashes is special iff it precedes a double quote:
132 # - if there's an even number of backslashes, the double quote is not
132 # - if there's an even number of backslashes, the double quote is not
133 # quoted (i.e. it ends the quoted region)
133 # quoted (i.e. it ends the quoted region)
134 # - if there's an odd number of backslashes, the double quote is quoted
134 # - if there's an odd number of backslashes, the double quote is quoted
135 # - in both cases, every pair of backslashes is unquoted into a single
135 # - in both cases, every pair of backslashes is unquoted into a single
136 # backslash
136 # backslash
137 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
137 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
138 # So, to quote a string, we must surround it in double quotes, double
138 # So, to quote a string, we must surround it in double quotes, double
139 # the number of backslashes that preceed double quotes and add another
139 # the number of backslashes that preceed double quotes and add another
140 # backslash before every double quote (being careful with the double
140 # backslash before every double quote (being careful with the double
141 # quote we've appended to the end)
141 # quote we've appended to the end)
142 _quotere = None
142 _quotere = None
143 def shellquote(s):
143 def shellquote(s):
144 global _quotere
144 global _quotere
145 if _quotere is None:
145 if _quotere is None:
146 _quotere = re.compile(r'(\\*)("|\\$)')
146 _quotere = re.compile(r'(\\*)("|\\$)')
147 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
147 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
148
148
149 def quotecommand(cmd):
149 def quotecommand(cmd):
150 """Build a command string suitable for os.popen* calls."""
150 """Build a command string suitable for os.popen* calls."""
151 # The extra quotes are needed because popen* runs the command
151 # The extra quotes are needed because popen* runs the command
152 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
152 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
153 return '"' + cmd + '"'
153 return '"' + cmd + '"'
154
154
155 def popen(command, mode='r'):
155 def popen(command, mode='r'):
156 # Work around "popen spawned process may not write to stdout
156 # Work around "popen spawned process may not write to stdout
157 # under windows"
157 # under windows"
158 # http://bugs.python.org/issue1366
158 # http://bugs.python.org/issue1366
159 command += " 2> %s" % nulldev
159 command += " 2> %s" % nulldev
160 return os.popen(quotecommand(command), mode)
160 return os.popen(quotecommand(command), mode)
161
161
162 def explain_exit(code):
162 def explain_exit(code):
163 return _("exited with status %d") % code, code
163 return _("exited with status %d") % code, code
164
164
165 # if you change this stub into a real check, please try to implement the
165 # if you change this stub into a real check, please try to implement the
166 # username and groupname functions above, too.
166 # username and groupname functions above, too.
167 def isowner(fp, st=None):
167 def isowner(st):
168 return True
168 return True
169
169
170 def find_exe(command):
170 def find_exe(command):
171 '''Find executable for command searching like cmd.exe does.
171 '''Find executable for command searching like cmd.exe does.
172 If command is a basename then PATH is searched for command.
172 If command is a basename then PATH is searched for command.
173 PATH isn't searched if command is an absolute or relative path.
173 PATH isn't searched if command is an absolute or relative path.
174 An extension from PATHEXT is found and added if not present.
174 An extension from PATHEXT is found and added if not present.
175 If command isn't found None is returned.'''
175 If command isn't found None is returned.'''
176 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
176 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
177 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
177 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
178 if os.path.splitext(command)[1].lower() in pathexts:
178 if os.path.splitext(command)[1].lower() in pathexts:
179 pathexts = ['']
179 pathexts = ['']
180
180
181 def findexisting(pathcommand):
181 def findexisting(pathcommand):
182 'Will append extension (if needed) and return existing file'
182 'Will append extension (if needed) and return existing file'
183 for ext in pathexts:
183 for ext in pathexts:
184 executable = pathcommand + ext
184 executable = pathcommand + ext
185 if os.path.exists(executable):
185 if os.path.exists(executable):
186 return executable
186 return executable
187 return None
187 return None
188
188
189 if os.sep in command:
189 if os.sep in command:
190 return findexisting(command)
190 return findexisting(command)
191
191
192 for path in os.environ.get('PATH', '').split(os.pathsep):
192 for path in os.environ.get('PATH', '').split(os.pathsep):
193 executable = findexisting(os.path.join(path, command))
193 executable = findexisting(os.path.join(path, command))
194 if executable is not None:
194 if executable is not None:
195 return executable
195 return executable
196 return None
196 return None
197
197
198 def set_signal_handler():
198 def set_signal_handler():
199 try:
199 try:
200 set_signal_handler_win32()
200 set_signal_handler_win32()
201 except NameError:
201 except NameError:
202 pass
202 pass
203
203
204 def statfiles(files):
204 def statfiles(files):
205 '''Stat each file in files and yield stat or None if file does not exist.
205 '''Stat each file in files and yield stat or None if file does not exist.
206 Cluster and cache stat per directory to minimize number of OS stat calls.'''
206 Cluster and cache stat per directory to minimize number of OS stat calls.'''
207 ncase = os.path.normcase
207 ncase = os.path.normcase
208 sep = os.sep
208 sep = os.sep
209 dircache = {} # dirname -> filename -> status | None if file does not exist
209 dircache = {} # dirname -> filename -> status | None if file does not exist
210 for nf in files:
210 for nf in files:
211 nf = ncase(nf)
211 nf = ncase(nf)
212 pos = nf.rfind(sep)
212 pos = nf.rfind(sep)
213 if pos == -1:
213 if pos == -1:
214 dir, base = '.', nf
214 dir, base = '.', nf
215 else:
215 else:
216 dir, base = nf[:pos+1], nf[pos+1:]
216 dir, base = nf[:pos+1], nf[pos+1:]
217 cache = dircache.get(dir, None)
217 cache = dircache.get(dir, None)
218 if cache is None:
218 if cache is None:
219 try:
219 try:
220 dmap = dict([(ncase(n), s)
220 dmap = dict([(ncase(n), s)
221 for n, k, s in osutil.listdir(dir, True)])
221 for n, k, s in osutil.listdir(dir, True)])
222 except OSError, err:
222 except OSError, err:
223 # handle directory not found in Python version prior to 2.5
223 # handle directory not found in Python version prior to 2.5
224 # Python <= 2.4 returns native Windows code 3 in errno
224 # Python <= 2.4 returns native Windows code 3 in errno
225 # Python >= 2.5 returns ENOENT and adds winerror field
225 # Python >= 2.5 returns ENOENT and adds winerror field
226 # EINVAL is raised if dir is not a directory.
226 # EINVAL is raised if dir is not a directory.
227 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
227 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
228 errno.ENOTDIR):
228 errno.ENOTDIR):
229 raise
229 raise
230 dmap = {}
230 dmap = {}
231 cache = dircache.setdefault(dir, dmap)
231 cache = dircache.setdefault(dir, dmap)
232 yield cache.get(base, None)
232 yield cache.get(base, None)
233
233
234 def getuser():
234 def getuser():
235 '''return name of current user'''
235 '''return name of current user'''
236 raise error.Abort(_('user name not available - set USERNAME '
236 raise error.Abort(_('user name not available - set USERNAME '
237 'environment variable'))
237 'environment variable'))
238
238
239 def username(uid=None):
239 def username(uid=None):
240 """Return the name of the user with the given uid.
240 """Return the name of the user with the given uid.
241
241
242 If uid is None, return the name of the current user."""
242 If uid is None, return the name of the current user."""
243 return None
243 return None
244
244
245 def groupname(gid=None):
245 def groupname(gid=None):
246 """Return the name of the group with the given gid.
246 """Return the name of the group with the given gid.
247
247
248 If gid is None, return the name of the current group."""
248 If gid is None, return the name of the current group."""
249 return None
249 return None
250
250
251 def _removedirs(name):
251 def _removedirs(name):
252 """special version of os.removedirs that does not remove symlinked
252 """special version of os.removedirs that does not remove symlinked
253 directories or junction points if they actually contain files"""
253 directories or junction points if they actually contain files"""
254 if osutil.listdir(name):
254 if osutil.listdir(name):
255 return
255 return
256 os.rmdir(name)
256 os.rmdir(name)
257 head, tail = os.path.split(name)
257 head, tail = os.path.split(name)
258 if not tail:
258 if not tail:
259 head, tail = os.path.split(head)
259 head, tail = os.path.split(head)
260 while head and tail:
260 while head and tail:
261 try:
261 try:
262 if osutil.listdir(name):
262 if osutil.listdir(name):
263 return
263 return
264 os.rmdir(head)
264 os.rmdir(head)
265 except:
265 except:
266 break
266 break
267 head, tail = os.path.split(head)
267 head, tail = os.path.split(head)
268
268
269 def unlink(f):
269 def unlink(f):
270 """unlink and remove the directory if it is empty"""
270 """unlink and remove the directory if it is empty"""
271 os.unlink(f)
271 os.unlink(f)
272 # try removing directories that might now be empty
272 # try removing directories that might now be empty
273 try:
273 try:
274 _removedirs(os.path.dirname(f))
274 _removedirs(os.path.dirname(f))
275 except OSError:
275 except OSError:
276 pass
276 pass
277
277
278 try:
278 try:
279 # override functions with win32 versions if possible
279 # override functions with win32 versions if possible
280 from win32 import *
280 from win32 import *
281 except ImportError:
281 except ImportError:
282 pass
282 pass
283
283
284 expandglobs = True
284 expandglobs = True
@@ -1,189 +1,189 b''
1 # Since it's not easy to write a test that portably deals
1 # Since it's not easy to write a test that portably deals
2 # with files from different users/groups, we cheat a bit by
2 # with files from different users/groups, we cheat a bit by
3 # monkey-patching some functions in the util module
3 # monkey-patching some functions in the util module
4
4
5 import os
5 import os
6 from mercurial import ui, util, error
6 from mercurial import ui, util, error
7
7
8 hgrc = os.environ['HGRCPATH']
8 hgrc = os.environ['HGRCPATH']
9 f = open(hgrc)
9 f = open(hgrc)
10 basehgrc = f.read()
10 basehgrc = f.read()
11 f.close()
11 f.close()
12
12
13 def testui(user='foo', group='bar', tusers=(), tgroups=(),
13 def testui(user='foo', group='bar', tusers=(), tgroups=(),
14 cuser='foo', cgroup='bar', debug=False, silent=False):
14 cuser='foo', cgroup='bar', debug=False, silent=False):
15 # user, group => owners of the file
15 # user, group => owners of the file
16 # tusers, tgroups => trusted users/groups
16 # tusers, tgroups => trusted users/groups
17 # cuser, cgroup => user/group of the current process
17 # cuser, cgroup => user/group of the current process
18
18
19 # write a global hgrc with the list of trusted users/groups and
19 # write a global hgrc with the list of trusted users/groups and
20 # some setting so that we can be sure it was read
20 # some setting so that we can be sure it was read
21 f = open(hgrc, 'w')
21 f = open(hgrc, 'w')
22 f.write(basehgrc)
22 f.write(basehgrc)
23 f.write('\n[paths]\n')
23 f.write('\n[paths]\n')
24 f.write('global = /some/path\n\n')
24 f.write('global = /some/path\n\n')
25
25
26 if tusers or tgroups:
26 if tusers or tgroups:
27 f.write('[trusted]\n')
27 f.write('[trusted]\n')
28 if tusers:
28 if tusers:
29 f.write('users = %s\n' % ', '.join(tusers))
29 f.write('users = %s\n' % ', '.join(tusers))
30 if tgroups:
30 if tgroups:
31 f.write('groups = %s\n' % ', '.join(tgroups))
31 f.write('groups = %s\n' % ', '.join(tgroups))
32 f.close()
32 f.close()
33
33
34 # override the functions that give names to uids and gids
34 # override the functions that give names to uids and gids
35 def username(uid=None):
35 def username(uid=None):
36 if uid is None:
36 if uid is None:
37 return cuser
37 return cuser
38 return user
38 return user
39 util.username = username
39 util.username = username
40
40
41 def groupname(gid=None):
41 def groupname(gid=None):
42 if gid is None:
42 if gid is None:
43 return 'bar'
43 return 'bar'
44 return group
44 return group
45 util.groupname = groupname
45 util.groupname = groupname
46
46
47 def isowner(fp, st=None):
47 def isowner(st):
48 return user == cuser
48 return user == cuser
49 util.isowner = isowner
49 util.isowner = isowner
50
50
51 # try to read everything
51 # try to read everything
52 #print '# File belongs to user %s, group %s' % (user, group)
52 #print '# File belongs to user %s, group %s' % (user, group)
53 #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
53 #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
54 kind = ('different', 'same')
54 kind = ('different', 'same')
55 who = ('', 'user', 'group', 'user and the group')
55 who = ('', 'user', 'group', 'user and the group')
56 trusted = who[(user in tusers) + 2*(group in tgroups)]
56 trusted = who[(user in tusers) + 2*(group in tgroups)]
57 if trusted:
57 if trusted:
58 trusted = ', but we trust the ' + trusted
58 trusted = ', but we trust the ' + trusted
59 print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
59 print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
60 trusted)
60 trusted)
61
61
62 u = ui.ui()
62 u = ui.ui()
63 u.setconfig('ui', 'debug', str(bool(debug)))
63 u.setconfig('ui', 'debug', str(bool(debug)))
64 u.readconfig('.hg/hgrc')
64 u.readconfig('.hg/hgrc')
65 if silent:
65 if silent:
66 return u
66 return u
67 print 'trusted'
67 print 'trusted'
68 for name, path in u.configitems('paths'):
68 for name, path in u.configitems('paths'):
69 print ' ', name, '=', path
69 print ' ', name, '=', path
70 print 'untrusted'
70 print 'untrusted'
71 for name, path in u.configitems('paths', untrusted=True):
71 for name, path in u.configitems('paths', untrusted=True):
72 print '.',
72 print '.',
73 u.config('paths', name) # warning with debug=True
73 u.config('paths', name) # warning with debug=True
74 print '.',
74 print '.',
75 u.config('paths', name, untrusted=True) # no warnings
75 u.config('paths', name, untrusted=True) # no warnings
76 print name, '=', path
76 print name, '=', path
77 print
77 print
78
78
79 return u
79 return u
80
80
81 os.mkdir('repo')
81 os.mkdir('repo')
82 os.chdir('repo')
82 os.chdir('repo')
83 os.mkdir('.hg')
83 os.mkdir('.hg')
84 f = open('.hg/hgrc', 'w')
84 f = open('.hg/hgrc', 'w')
85 f.write('[paths]\n')
85 f.write('[paths]\n')
86 f.write('local = /another/path\n\n')
86 f.write('local = /another/path\n\n')
87 f.close()
87 f.close()
88
88
89 #print '# Everything is run by user foo, group bar\n'
89 #print '# Everything is run by user foo, group bar\n'
90
90
91 # same user, same group
91 # same user, same group
92 testui()
92 testui()
93 # same user, different group
93 # same user, different group
94 testui(group='def')
94 testui(group='def')
95 # different user, same group
95 # different user, same group
96 testui(user='abc')
96 testui(user='abc')
97 # ... but we trust the group
97 # ... but we trust the group
98 testui(user='abc', tgroups=['bar'])
98 testui(user='abc', tgroups=['bar'])
99 # different user, different group
99 # different user, different group
100 testui(user='abc', group='def')
100 testui(user='abc', group='def')
101 # ... but we trust the user
101 # ... but we trust the user
102 testui(user='abc', group='def', tusers=['abc'])
102 testui(user='abc', group='def', tusers=['abc'])
103 # ... but we trust the group
103 # ... but we trust the group
104 testui(user='abc', group='def', tgroups=['def'])
104 testui(user='abc', group='def', tgroups=['def'])
105 # ... but we trust the user and the group
105 # ... but we trust the user and the group
106 testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
106 testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
107 # ... but we trust all users
107 # ... but we trust all users
108 print '# we trust all users'
108 print '# we trust all users'
109 testui(user='abc', group='def', tusers=['*'])
109 testui(user='abc', group='def', tusers=['*'])
110 # ... but we trust all groups
110 # ... but we trust all groups
111 print '# we trust all groups'
111 print '# we trust all groups'
112 testui(user='abc', group='def', tgroups=['*'])
112 testui(user='abc', group='def', tgroups=['*'])
113 # ... but we trust the whole universe
113 # ... but we trust the whole universe
114 print '# we trust all users and groups'
114 print '# we trust all users and groups'
115 testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
115 testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
116 # ... check that users and groups are in different namespaces
116 # ... check that users and groups are in different namespaces
117 print "# we don't get confused by users and groups with the same name"
117 print "# we don't get confused by users and groups with the same name"
118 testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
118 testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
119 # ... lists of user names work
119 # ... lists of user names work
120 print "# list of user names"
120 print "# list of user names"
121 testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
121 testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
122 tgroups=['bar', 'baz', 'qux'])
122 tgroups=['bar', 'baz', 'qux'])
123 # ... lists of group names work
123 # ... lists of group names work
124 print "# list of group names"
124 print "# list of group names"
125 testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
125 testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
126 tgroups=['bar', 'def', 'baz', 'qux'])
126 tgroups=['bar', 'def', 'baz', 'qux'])
127
127
128 print "# Can't figure out the name of the user running this process"
128 print "# Can't figure out the name of the user running this process"
129 testui(user='abc', group='def', cuser=None)
129 testui(user='abc', group='def', cuser=None)
130
130
131 print "# prints debug warnings"
131 print "# prints debug warnings"
132 u = testui(user='abc', group='def', cuser='foo', debug=True)
132 u = testui(user='abc', group='def', cuser='foo', debug=True)
133
133
134 print "# ui.readconfig sections"
134 print "# ui.readconfig sections"
135 filename = 'foobar'
135 filename = 'foobar'
136 f = open(filename, 'w')
136 f = open(filename, 'w')
137 f.write('[foobar]\n')
137 f.write('[foobar]\n')
138 f.write('baz = quux\n')
138 f.write('baz = quux\n')
139 f.close()
139 f.close()
140 u.readconfig(filename, sections = ['foobar'])
140 u.readconfig(filename, sections = ['foobar'])
141 print u.config('foobar', 'baz')
141 print u.config('foobar', 'baz')
142
142
143 print
143 print
144 print "# read trusted, untrusted, new ui, trusted"
144 print "# read trusted, untrusted, new ui, trusted"
145 u = ui.ui()
145 u = ui.ui()
146 u.setconfig('ui', 'debug', 'on')
146 u.setconfig('ui', 'debug', 'on')
147 u.readconfig(filename)
147 u.readconfig(filename)
148 u2 = u.copy()
148 u2 = u.copy()
149 def username(uid=None):
149 def username(uid=None):
150 return 'foo'
150 return 'foo'
151 util.username = username
151 util.username = username
152 u2.readconfig('.hg/hgrc')
152 u2.readconfig('.hg/hgrc')
153 print 'trusted:'
153 print 'trusted:'
154 print u2.config('foobar', 'baz')
154 print u2.config('foobar', 'baz')
155 print 'untrusted:'
155 print 'untrusted:'
156 print u2.config('foobar', 'baz', untrusted=True)
156 print u2.config('foobar', 'baz', untrusted=True)
157
157
158 print
158 print
159 print "# error handling"
159 print "# error handling"
160
160
161 def assertraises(f, exc=util.Abort):
161 def assertraises(f, exc=util.Abort):
162 try:
162 try:
163 f()
163 f()
164 except exc, inst:
164 except exc, inst:
165 print 'raised', inst.__class__.__name__
165 print 'raised', inst.__class__.__name__
166 else:
166 else:
167 print 'no exception?!'
167 print 'no exception?!'
168
168
169 print "# file doesn't exist"
169 print "# file doesn't exist"
170 os.unlink('.hg/hgrc')
170 os.unlink('.hg/hgrc')
171 assert not os.path.exists('.hg/hgrc')
171 assert not os.path.exists('.hg/hgrc')
172 testui(debug=True, silent=True)
172 testui(debug=True, silent=True)
173 testui(user='abc', group='def', debug=True, silent=True)
173 testui(user='abc', group='def', debug=True, silent=True)
174
174
175 print
175 print
176 print "# parse error"
176 print "# parse error"
177 f = open('.hg/hgrc', 'w')
177 f = open('.hg/hgrc', 'w')
178 f.write('foo')
178 f.write('foo')
179 f.close()
179 f.close()
180
180
181 try:
181 try:
182 testui(user='abc', group='def', silent=True)
182 testui(user='abc', group='def', silent=True)
183 except error.ConfigError, inst:
183 except error.ConfigError, inst:
184 print inst
184 print inst
185
185
186 try:
186 try:
187 testui(debug=True, silent=True)
187 testui(debug=True, silent=True)
188 except error.ConfigError, inst:
188 except error.ConfigError, inst:
189 print inst
189 print inst
General Comments 0
You need to be logged in to leave comments. Login now