##// END OF EJS Templates
use repr() instead of backticks
Benoit Boissinot -
r7470:1d58c049 default
parent child Browse files
Show More
@@ -1,196 +1,196 b''
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog class 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import bin, hex, nullid
8 from node import bin, hex, nullid
9 from revlog import revlog, RevlogError
9 from revlog import revlog, RevlogError
10 from i18n import _
10 from i18n import _
11 import util
11 import util
12
12
13 def _string_escape(text):
13 def _string_escape(text):
14 """
14 """
15 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
15 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
16 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
16 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
17 >>> s
17 >>> s
18 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
18 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
19 >>> res = _string_escape(s)
19 >>> res = _string_escape(s)
20 >>> s == res.decode('string_escape')
20 >>> s == res.decode('string_escape')
21 True
21 True
22 """
22 """
23 # subset of the string_escape codec
23 # subset of the string_escape codec
24 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
24 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
25 return text.replace('\0', '\\0')
25 return text.replace('\0', '\\0')
26
26
27 class appender:
27 class appender:
28 '''the changelog index must be update last on disk, so we use this class
28 '''the changelog index must be update last on disk, so we use this class
29 to delay writes to it'''
29 to delay writes to it'''
30 def __init__(self, fp, buf):
30 def __init__(self, fp, buf):
31 self.data = buf
31 self.data = buf
32 self.fp = fp
32 self.fp = fp
33 self.offset = fp.tell()
33 self.offset = fp.tell()
34 self.size = util.fstat(fp).st_size
34 self.size = util.fstat(fp).st_size
35
35
36 def end(self):
36 def end(self):
37 return self.size + len("".join(self.data))
37 return self.size + len("".join(self.data))
38 def tell(self):
38 def tell(self):
39 return self.offset
39 return self.offset
40 def flush(self):
40 def flush(self):
41 pass
41 pass
42 def close(self):
42 def close(self):
43 self.fp.close()
43 self.fp.close()
44
44
45 def seek(self, offset, whence=0):
45 def seek(self, offset, whence=0):
46 '''virtual file offset spans real file and data'''
46 '''virtual file offset spans real file and data'''
47 if whence == 0:
47 if whence == 0:
48 self.offset = offset
48 self.offset = offset
49 elif whence == 1:
49 elif whence == 1:
50 self.offset += offset
50 self.offset += offset
51 elif whence == 2:
51 elif whence == 2:
52 self.offset = self.end() + offset
52 self.offset = self.end() + offset
53 if self.offset < self.size:
53 if self.offset < self.size:
54 self.fp.seek(self.offset)
54 self.fp.seek(self.offset)
55
55
56 def read(self, count=-1):
56 def read(self, count=-1):
57 '''only trick here is reads that span real file and data'''
57 '''only trick here is reads that span real file and data'''
58 ret = ""
58 ret = ""
59 if self.offset < self.size:
59 if self.offset < self.size:
60 s = self.fp.read(count)
60 s = self.fp.read(count)
61 ret = s
61 ret = s
62 self.offset += len(s)
62 self.offset += len(s)
63 if count > 0:
63 if count > 0:
64 count -= len(s)
64 count -= len(s)
65 if count != 0:
65 if count != 0:
66 doff = self.offset - self.size
66 doff = self.offset - self.size
67 self.data.insert(0, "".join(self.data))
67 self.data.insert(0, "".join(self.data))
68 del self.data[1:]
68 del self.data[1:]
69 s = self.data[0][doff:doff+count]
69 s = self.data[0][doff:doff+count]
70 self.offset += len(s)
70 self.offset += len(s)
71 ret += s
71 ret += s
72 return ret
72 return ret
73
73
74 def write(self, s):
74 def write(self, s):
75 self.data.append(str(s))
75 self.data.append(str(s))
76 self.offset += len(s)
76 self.offset += len(s)
77
77
78 class changelog(revlog):
78 class changelog(revlog):
79 def __init__(self, opener):
79 def __init__(self, opener):
80 revlog.__init__(self, opener, "00changelog.i")
80 revlog.__init__(self, opener, "00changelog.i")
81
81
82 def delayupdate(self):
82 def delayupdate(self):
83 "delay visibility of index updates to other readers"
83 "delay visibility of index updates to other readers"
84 self._realopener = self.opener
84 self._realopener = self.opener
85 self.opener = self._delayopener
85 self.opener = self._delayopener
86 self._delaycount = len(self)
86 self._delaycount = len(self)
87 self._delaybuf = []
87 self._delaybuf = []
88 self._delayname = None
88 self._delayname = None
89
89
90 def finalize(self, tr):
90 def finalize(self, tr):
91 "finalize index updates"
91 "finalize index updates"
92 self.opener = self._realopener
92 self.opener = self._realopener
93 # move redirected index data back into place
93 # move redirected index data back into place
94 if self._delayname:
94 if self._delayname:
95 util.rename(self._delayname + ".a", self._delayname)
95 util.rename(self._delayname + ".a", self._delayname)
96 elif self._delaybuf:
96 elif self._delaybuf:
97 fp = self.opener(self.indexfile, 'a')
97 fp = self.opener(self.indexfile, 'a')
98 fp.write("".join(self._delaybuf))
98 fp.write("".join(self._delaybuf))
99 fp.close()
99 fp.close()
100 del self._delaybuf
100 del self._delaybuf
101 # split when we're done
101 # split when we're done
102 self.checkinlinesize(tr)
102 self.checkinlinesize(tr)
103
103
104 def _delayopener(self, name, mode='r'):
104 def _delayopener(self, name, mode='r'):
105 fp = self._realopener(name, mode)
105 fp = self._realopener(name, mode)
106 # only divert the index
106 # only divert the index
107 if not name == self.indexfile:
107 if not name == self.indexfile:
108 return fp
108 return fp
109 # if we're doing an initial clone, divert to another file
109 # if we're doing an initial clone, divert to another file
110 if self._delaycount == 0:
110 if self._delaycount == 0:
111 self._delayname = fp.name
111 self._delayname = fp.name
112 if not len(self):
112 if not len(self):
113 # make sure to truncate the file
113 # make sure to truncate the file
114 mode = mode.replace('a', 'w')
114 mode = mode.replace('a', 'w')
115 return self._realopener(name + ".a", mode)
115 return self._realopener(name + ".a", mode)
116 # otherwise, divert to memory
116 # otherwise, divert to memory
117 return appender(fp, self._delaybuf)
117 return appender(fp, self._delaybuf)
118
118
119 def checkinlinesize(self, tr, fp=None):
119 def checkinlinesize(self, tr, fp=None):
120 if self.opener == self._delayopener:
120 if self.opener == self._delayopener:
121 return
121 return
122 return revlog.checkinlinesize(self, tr, fp)
122 return revlog.checkinlinesize(self, tr, fp)
123
123
124 def decode_extra(self, text):
124 def decode_extra(self, text):
125 extra = {}
125 extra = {}
126 for l in text.split('\0'):
126 for l in text.split('\0'):
127 if l:
127 if l:
128 k, v = l.decode('string_escape').split(':', 1)
128 k, v = l.decode('string_escape').split(':', 1)
129 extra[k] = v
129 extra[k] = v
130 return extra
130 return extra
131
131
132 def encode_extra(self, d):
132 def encode_extra(self, d):
133 # keys must be sorted to produce a deterministic changelog entry
133 # keys must be sorted to produce a deterministic changelog entry
134 items = [_string_escape('%s:%s' % (k, d[k])) for k in util.sort(d)]
134 items = [_string_escape('%s:%s' % (k, d[k])) for k in util.sort(d)]
135 return "\0".join(items)
135 return "\0".join(items)
136
136
137 def read(self, node):
137 def read(self, node):
138 """
138 """
139 format used:
139 format used:
140 nodeid\n : manifest node in ascii
140 nodeid\n : manifest node in ascii
141 user\n : user, no \n or \r allowed
141 user\n : user, no \n or \r allowed
142 time tz extra\n : date (time is int or float, timezone is int)
142 time tz extra\n : date (time is int or float, timezone is int)
143 : extra is metadatas, encoded and separated by '\0'
143 : extra is metadatas, encoded and separated by '\0'
144 : older versions ignore it
144 : older versions ignore it
145 files\n\n : files modified by the cset, no \n or \r allowed
145 files\n\n : files modified by the cset, no \n or \r allowed
146 (.*) : comment (free text, ideally utf-8)
146 (.*) : comment (free text, ideally utf-8)
147
147
148 changelog v0 doesn't use extra
148 changelog v0 doesn't use extra
149 """
149 """
150 text = self.revision(node)
150 text = self.revision(node)
151 if not text:
151 if not text:
152 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
152 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
153 last = text.index("\n\n")
153 last = text.index("\n\n")
154 desc = util.tolocal(text[last + 2:])
154 desc = util.tolocal(text[last + 2:])
155 l = text[:last].split('\n')
155 l = text[:last].split('\n')
156 manifest = bin(l[0])
156 manifest = bin(l[0])
157 user = util.tolocal(l[1])
157 user = util.tolocal(l[1])
158
158
159 extra_data = l[2].split(' ', 2)
159 extra_data = l[2].split(' ', 2)
160 if len(extra_data) != 3:
160 if len(extra_data) != 3:
161 time = float(extra_data.pop(0))
161 time = float(extra_data.pop(0))
162 try:
162 try:
163 # various tools did silly things with the time zone field.
163 # various tools did silly things with the time zone field.
164 timezone = int(extra_data[0])
164 timezone = int(extra_data[0])
165 except:
165 except:
166 timezone = 0
166 timezone = 0
167 extra = {}
167 extra = {}
168 else:
168 else:
169 time, timezone, extra = extra_data
169 time, timezone, extra = extra_data
170 time, timezone = float(time), int(timezone)
170 time, timezone = float(time), int(timezone)
171 extra = self.decode_extra(extra)
171 extra = self.decode_extra(extra)
172 if not extra.get('branch'):
172 if not extra.get('branch'):
173 extra['branch'] = 'default'
173 extra['branch'] = 'default'
174 files = l[3:]
174 files = l[3:]
175 return (manifest, user, (time, timezone), files, desc, extra)
175 return (manifest, user, (time, timezone), files, desc, extra)
176
176
177 def add(self, manifest, files, desc, transaction, p1=None, p2=None,
177 def add(self, manifest, files, desc, transaction, p1=None, p2=None,
178 user=None, date=None, extra={}):
178 user=None, date=None, extra={}):
179
179
180 user = user.strip()
180 user = user.strip()
181 if "\n" in user:
181 if "\n" in user:
182 raise RevlogError(_("username %s contains a newline") % `user`)
182 raise RevlogError(_("username %s contains a newline") % repr(user))
183 user, desc = util.fromlocal(user), util.fromlocal(desc)
183 user, desc = util.fromlocal(user), util.fromlocal(desc)
184
184
185 if date:
185 if date:
186 parseddate = "%d %d" % util.parsedate(date)
186 parseddate = "%d %d" % util.parsedate(date)
187 else:
187 else:
188 parseddate = "%d %d" % util.makedate()
188 parseddate = "%d %d" % util.makedate()
189 if extra and extra.get("branch") in ("default", ""):
189 if extra and extra.get("branch") in ("default", ""):
190 del extra["branch"]
190 del extra["branch"]
191 if extra:
191 if extra:
192 extra = self.encode_extra(extra)
192 extra = self.encode_extra(extra)
193 parseddate = "%s %s" % (parseddate, extra)
193 parseddate = "%s %s" % (parseddate, extra)
194 l = [hex(manifest), user, parseddate] + util.sort(files) + ["", desc]
194 l = [hex(manifest), user, parseddate] + util.sort(files) + ["", desc]
195 text = "\n".join(l)
195 text = "\n".join(l)
196 return self.addrevision(text, transaction, len(self), p1, p2)
196 return self.addrevision(text, transaction, len(self), p1, p2)
@@ -1,486 +1,486 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, re, socket, sys, tempfile
9 import errno, getpass, os, re, socket, sys, tempfile
10 import ConfigParser, traceback, util
10 import ConfigParser, traceback, util
11
11
12 def dupconfig(orig):
12 def dupconfig(orig):
13 new = util.configparser(orig.defaults())
13 new = util.configparser(orig.defaults())
14 updateconfig(orig, new)
14 updateconfig(orig, new)
15 return new
15 return new
16
16
17 def updateconfig(source, dest, sections=None):
17 def updateconfig(source, dest, sections=None):
18 if not sections:
18 if not sections:
19 sections = source.sections()
19 sections = source.sections()
20 for section in sections:
20 for section in sections:
21 if not dest.has_section(section):
21 if not dest.has_section(section):
22 dest.add_section(section)
22 dest.add_section(section)
23 for name, value in source.items(section, raw=True):
23 for name, value in source.items(section, raw=True):
24 dest.set(section, name, value)
24 dest.set(section, name, value)
25
25
26 class ui(object):
26 class ui(object):
27 _isatty = None
27 _isatty = None
28
28
29 def __init__(self, verbose=False, debug=False, quiet=False,
29 def __init__(self, verbose=False, debug=False, quiet=False,
30 interactive=True, traceback=False, report_untrusted=True,
30 interactive=True, traceback=False, report_untrusted=True,
31 parentui=None):
31 parentui=None):
32 self.overlay = None
32 self.overlay = None
33 self.buffers = []
33 self.buffers = []
34 if parentui is None:
34 if parentui is None:
35 # this is the parent of all ui children
35 # this is the parent of all ui children
36 self.parentui = None
36 self.parentui = None
37 self.quiet = quiet
37 self.quiet = quiet
38 self.verbose = verbose
38 self.verbose = verbose
39 self.debugflag = debug
39 self.debugflag = debug
40 self.interactive = interactive
40 self.interactive = interactive
41 self.traceback = traceback
41 self.traceback = traceback
42 self.report_untrusted = report_untrusted
42 self.report_untrusted = report_untrusted
43 self.trusted_users = {}
43 self.trusted_users = {}
44 self.trusted_groups = {}
44 self.trusted_groups = {}
45 # if ucdata is not None, its keys must be a superset of cdata's
45 # if ucdata is not None, its keys must be a superset of cdata's
46 self.cdata = util.configparser()
46 self.cdata = util.configparser()
47 self.ucdata = None
47 self.ucdata = None
48 # we always trust global config files
48 # we always trust global config files
49 self.check_trusted = False
49 self.check_trusted = False
50 self.readconfig(util.rcpath())
50 self.readconfig(util.rcpath())
51 self.check_trusted = True
51 self.check_trusted = True
52 self.updateopts(verbose, debug, quiet, interactive)
52 self.updateopts(verbose, debug, quiet, interactive)
53 else:
53 else:
54 # parentui may point to an ui object which is already a child
54 # parentui may point to an ui object which is already a child
55 self.parentui = parentui.parentui or parentui
55 self.parentui = parentui.parentui or parentui
56 self.trusted_users = parentui.trusted_users.copy()
56 self.trusted_users = parentui.trusted_users.copy()
57 self.trusted_groups = parentui.trusted_groups.copy()
57 self.trusted_groups = parentui.trusted_groups.copy()
58 self.cdata = dupconfig(self.parentui.cdata)
58 self.cdata = dupconfig(self.parentui.cdata)
59 if self.parentui.ucdata:
59 if self.parentui.ucdata:
60 self.ucdata = dupconfig(self.parentui.ucdata)
60 self.ucdata = dupconfig(self.parentui.ucdata)
61 if self.parentui.overlay:
61 if self.parentui.overlay:
62 self.overlay = dupconfig(self.parentui.overlay)
62 self.overlay = dupconfig(self.parentui.overlay)
63 if self.parentui is not parentui and parentui.overlay is not None:
63 if self.parentui is not parentui and parentui.overlay is not None:
64 if self.overlay is None:
64 if self.overlay is None:
65 self.overlay = util.configparser()
65 self.overlay = util.configparser()
66 updateconfig(parentui.overlay, self.overlay)
66 updateconfig(parentui.overlay, self.overlay)
67 self.buffers = parentui.buffers
67 self.buffers = parentui.buffers
68
68
69 def __getattr__(self, key):
69 def __getattr__(self, key):
70 return getattr(self.parentui, key)
70 return getattr(self.parentui, key)
71
71
72 def isatty(self):
72 def isatty(self):
73 if ui._isatty is None:
73 if ui._isatty is None:
74 ui._isatty = sys.stdin.isatty()
74 ui._isatty = sys.stdin.isatty()
75 return ui._isatty
75 return ui._isatty
76
76
77 def updateopts(self, verbose=False, debug=False, quiet=False,
77 def updateopts(self, verbose=False, debug=False, quiet=False,
78 interactive=True, traceback=False, config=[]):
78 interactive=True, traceback=False, config=[]):
79 for section, name, value in config:
79 for section, name, value in config:
80 self.setconfig(section, name, value)
80 self.setconfig(section, name, value)
81
81
82 if quiet or verbose or debug:
82 if quiet or verbose or debug:
83 self.setconfig('ui', 'quiet', str(bool(quiet)))
83 self.setconfig('ui', 'quiet', str(bool(quiet)))
84 self.setconfig('ui', 'verbose', str(bool(verbose)))
84 self.setconfig('ui', 'verbose', str(bool(verbose)))
85 self.setconfig('ui', 'debug', str(bool(debug)))
85 self.setconfig('ui', 'debug', str(bool(debug)))
86
86
87 self.verbosity_constraints()
87 self.verbosity_constraints()
88
88
89 if not interactive:
89 if not interactive:
90 self.setconfig('ui', 'interactive', 'False')
90 self.setconfig('ui', 'interactive', 'False')
91 self.interactive = False
91 self.interactive = False
92
92
93 self.traceback = self.traceback or traceback
93 self.traceback = self.traceback or traceback
94
94
95 def verbosity_constraints(self):
95 def verbosity_constraints(self):
96 self.quiet = self.configbool('ui', 'quiet')
96 self.quiet = self.configbool('ui', 'quiet')
97 self.verbose = self.configbool('ui', 'verbose')
97 self.verbose = self.configbool('ui', 'verbose')
98 self.debugflag = self.configbool('ui', 'debug')
98 self.debugflag = self.configbool('ui', 'debug')
99
99
100 if self.debugflag:
100 if self.debugflag:
101 self.verbose = True
101 self.verbose = True
102 self.quiet = False
102 self.quiet = False
103 elif self.verbose and self.quiet:
103 elif self.verbose and self.quiet:
104 self.quiet = self.verbose = False
104 self.quiet = self.verbose = False
105
105
106 def _is_trusted(self, fp, f, warn=True):
106 def _is_trusted(self, fp, f, warn=True):
107 if not self.check_trusted:
107 if not self.check_trusted:
108 return True
108 return True
109 st = util.fstat(fp)
109 st = util.fstat(fp)
110 if util.isowner(fp, st):
110 if util.isowner(fp, st):
111 return True
111 return True
112 tusers = self.trusted_users
112 tusers = self.trusted_users
113 tgroups = self.trusted_groups
113 tgroups = self.trusted_groups
114 if not tusers:
114 if not tusers:
115 user = util.username()
115 user = util.username()
116 if user is not None:
116 if user is not None:
117 self.trusted_users[user] = 1
117 self.trusted_users[user] = 1
118 self.fixconfig(section='trusted')
118 self.fixconfig(section='trusted')
119 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
119 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
120 user = util.username(st.st_uid)
120 user = util.username(st.st_uid)
121 group = util.groupname(st.st_gid)
121 group = util.groupname(st.st_gid)
122 if user not in tusers and group not in tgroups:
122 if user not in tusers and group not in tgroups:
123 if warn and self.report_untrusted:
123 if warn and self.report_untrusted:
124 self.warn(_('Not trusting file %s from untrusted '
124 self.warn(_('Not trusting file %s from untrusted '
125 'user %s, group %s\n') % (f, user, group))
125 'user %s, group %s\n') % (f, user, group))
126 return False
126 return False
127 return True
127 return True
128
128
129 def readconfig(self, fn, root=None):
129 def readconfig(self, fn, root=None):
130 if isinstance(fn, basestring):
130 if isinstance(fn, basestring):
131 fn = [fn]
131 fn = [fn]
132 for f in fn:
132 for f in fn:
133 try:
133 try:
134 fp = open(f)
134 fp = open(f)
135 except IOError:
135 except IOError:
136 continue
136 continue
137 cdata = self.cdata
137 cdata = self.cdata
138 trusted = self._is_trusted(fp, f)
138 trusted = self._is_trusted(fp, f)
139 if not trusted:
139 if not trusted:
140 if self.ucdata is None:
140 if self.ucdata is None:
141 self.ucdata = dupconfig(self.cdata)
141 self.ucdata = dupconfig(self.cdata)
142 cdata = self.ucdata
142 cdata = self.ucdata
143 elif self.ucdata is not None:
143 elif self.ucdata is not None:
144 # use a separate configparser, so that we don't accidentally
144 # use a separate configparser, so that we don't accidentally
145 # override ucdata settings later on.
145 # override ucdata settings later on.
146 cdata = util.configparser()
146 cdata = util.configparser()
147
147
148 try:
148 try:
149 cdata.readfp(fp, f)
149 cdata.readfp(fp, f)
150 except ConfigParser.ParsingError, inst:
150 except ConfigParser.ParsingError, inst:
151 msg = _("Failed to parse %s\n%s") % (f, inst)
151 msg = _("Failed to parse %s\n%s") % (f, inst)
152 if trusted:
152 if trusted:
153 raise util.Abort(msg)
153 raise util.Abort(msg)
154 self.warn(_("Ignored: %s\n") % msg)
154 self.warn(_("Ignored: %s\n") % msg)
155
155
156 if trusted:
156 if trusted:
157 if cdata != self.cdata:
157 if cdata != self.cdata:
158 updateconfig(cdata, self.cdata)
158 updateconfig(cdata, self.cdata)
159 if self.ucdata is not None:
159 if self.ucdata is not None:
160 updateconfig(cdata, self.ucdata)
160 updateconfig(cdata, self.ucdata)
161 # override data from config files with data set with ui.setconfig
161 # override data from config files with data set with ui.setconfig
162 if self.overlay:
162 if self.overlay:
163 updateconfig(self.overlay, self.cdata)
163 updateconfig(self.overlay, self.cdata)
164 if root is None:
164 if root is None:
165 root = os.path.expanduser('~')
165 root = os.path.expanduser('~')
166 self.fixconfig(root=root)
166 self.fixconfig(root=root)
167
167
168 def readsections(self, filename, *sections):
168 def readsections(self, filename, *sections):
169 """Read filename and add only the specified sections to the config data
169 """Read filename and add only the specified sections to the config data
170
170
171 The settings are added to the trusted config data.
171 The settings are added to the trusted config data.
172 """
172 """
173 if not sections:
173 if not sections:
174 return
174 return
175
175
176 cdata = util.configparser()
176 cdata = util.configparser()
177 try:
177 try:
178 try:
178 try:
179 fp = open(filename)
179 fp = open(filename)
180 except IOError, inst:
180 except IOError, inst:
181 raise util.Abort(_("unable to open %s: %s") %
181 raise util.Abort(_("unable to open %s: %s") %
182 (filename, getattr(inst, "strerror", inst)))
182 (filename, getattr(inst, "strerror", inst)))
183 try:
183 try:
184 cdata.readfp(fp, filename)
184 cdata.readfp(fp, filename)
185 finally:
185 finally:
186 fp.close()
186 fp.close()
187 except ConfigParser.ParsingError, inst:
187 except ConfigParser.ParsingError, inst:
188 raise util.Abort(_("failed to parse %s\n%s") % (filename, inst))
188 raise util.Abort(_("failed to parse %s\n%s") % (filename, inst))
189
189
190 for section in sections:
190 for section in sections:
191 if not cdata.has_section(section):
191 if not cdata.has_section(section):
192 cdata.add_section(section)
192 cdata.add_section(section)
193
193
194 updateconfig(cdata, self.cdata, sections)
194 updateconfig(cdata, self.cdata, sections)
195 if self.ucdata:
195 if self.ucdata:
196 updateconfig(cdata, self.ucdata, sections)
196 updateconfig(cdata, self.ucdata, sections)
197
197
198 def fixconfig(self, section=None, name=None, value=None, root=None):
198 def fixconfig(self, section=None, name=None, value=None, root=None):
199 # translate paths relative to root (or home) into absolute paths
199 # translate paths relative to root (or home) into absolute paths
200 if section is None or section == 'paths':
200 if section is None or section == 'paths':
201 if root is None:
201 if root is None:
202 root = os.getcwd()
202 root = os.getcwd()
203 items = section and [(name, value)] or []
203 items = section and [(name, value)] or []
204 for cdata in self.cdata, self.ucdata, self.overlay:
204 for cdata in self.cdata, self.ucdata, self.overlay:
205 if not cdata: continue
205 if not cdata: continue
206 if not items and cdata.has_section('paths'):
206 if not items and cdata.has_section('paths'):
207 pathsitems = cdata.items('paths')
207 pathsitems = cdata.items('paths')
208 else:
208 else:
209 pathsitems = items
209 pathsitems = items
210 for n, path in pathsitems:
210 for n, path in pathsitems:
211 if path and "://" not in path and not os.path.isabs(path):
211 if path and "://" not in path and not os.path.isabs(path):
212 cdata.set("paths", n,
212 cdata.set("paths", n,
213 os.path.normpath(os.path.join(root, path)))
213 os.path.normpath(os.path.join(root, path)))
214
214
215 # update verbosity/interactive/report_untrusted settings
215 # update verbosity/interactive/report_untrusted settings
216 if section is None or section == 'ui':
216 if section is None or section == 'ui':
217 if name is None or name in ('quiet', 'verbose', 'debug'):
217 if name is None or name in ('quiet', 'verbose', 'debug'):
218 self.verbosity_constraints()
218 self.verbosity_constraints()
219 if name is None or name == 'interactive':
219 if name is None or name == 'interactive':
220 interactive = self.configbool("ui", "interactive", None)
220 interactive = self.configbool("ui", "interactive", None)
221 if interactive is None and self.interactive:
221 if interactive is None and self.interactive:
222 self.interactive = self.isatty()
222 self.interactive = self.isatty()
223 else:
223 else:
224 self.interactive = interactive
224 self.interactive = interactive
225 if name is None or name == 'report_untrusted':
225 if name is None or name == 'report_untrusted':
226 self.report_untrusted = (
226 self.report_untrusted = (
227 self.configbool("ui", "report_untrusted", True))
227 self.configbool("ui", "report_untrusted", True))
228
228
229 # update trust information
229 # update trust information
230 if (section is None or section == 'trusted') and self.trusted_users:
230 if (section is None or section == 'trusted') and self.trusted_users:
231 for user in self.configlist('trusted', 'users'):
231 for user in self.configlist('trusted', 'users'):
232 self.trusted_users[user] = 1
232 self.trusted_users[user] = 1
233 for group in self.configlist('trusted', 'groups'):
233 for group in self.configlist('trusted', 'groups'):
234 self.trusted_groups[group] = 1
234 self.trusted_groups[group] = 1
235
235
236 def setconfig(self, section, name, value):
236 def setconfig(self, section, name, value):
237 if not self.overlay:
237 if not self.overlay:
238 self.overlay = util.configparser()
238 self.overlay = util.configparser()
239 for cdata in (self.overlay, self.cdata, self.ucdata):
239 for cdata in (self.overlay, self.cdata, self.ucdata):
240 if not cdata: continue
240 if not cdata: continue
241 if not cdata.has_section(section):
241 if not cdata.has_section(section):
242 cdata.add_section(section)
242 cdata.add_section(section)
243 cdata.set(section, name, value)
243 cdata.set(section, name, value)
244 self.fixconfig(section, name, value)
244 self.fixconfig(section, name, value)
245
245
246 def _get_cdata(self, untrusted):
246 def _get_cdata(self, untrusted):
247 if untrusted and self.ucdata:
247 if untrusted and self.ucdata:
248 return self.ucdata
248 return self.ucdata
249 return self.cdata
249 return self.cdata
250
250
251 def _config(self, section, name, default, funcname, untrusted, abort):
251 def _config(self, section, name, default, funcname, untrusted, abort):
252 cdata = self._get_cdata(untrusted)
252 cdata = self._get_cdata(untrusted)
253 if cdata.has_option(section, name):
253 if cdata.has_option(section, name):
254 try:
254 try:
255 func = getattr(cdata, funcname)
255 func = getattr(cdata, funcname)
256 return func(section, name)
256 return func(section, name)
257 except (ConfigParser.InterpolationError, ValueError), inst:
257 except (ConfigParser.InterpolationError, ValueError), inst:
258 msg = _("Error in configuration section [%s] "
258 msg = _("Error in configuration section [%s] "
259 "parameter '%s':\n%s") % (section, name, inst)
259 "parameter '%s':\n%s") % (section, name, inst)
260 if abort:
260 if abort:
261 raise util.Abort(msg)
261 raise util.Abort(msg)
262 self.warn(_("Ignored: %s\n") % msg)
262 self.warn(_("Ignored: %s\n") % msg)
263 return default
263 return default
264
264
265 def _configcommon(self, section, name, default, funcname, untrusted):
265 def _configcommon(self, section, name, default, funcname, untrusted):
266 value = self._config(section, name, default, funcname,
266 value = self._config(section, name, default, funcname,
267 untrusted, abort=True)
267 untrusted, abort=True)
268 if self.debugflag and not untrusted and self.ucdata:
268 if self.debugflag and not untrusted and self.ucdata:
269 uvalue = self._config(section, name, None, funcname,
269 uvalue = self._config(section, name, None, funcname,
270 untrusted=True, abort=False)
270 untrusted=True, abort=False)
271 if uvalue is not None and uvalue != value:
271 if uvalue is not None and uvalue != value:
272 self.warn(_("Ignoring untrusted configuration option "
272 self.warn(_("Ignoring untrusted configuration option "
273 "%s.%s = %s\n") % (section, name, uvalue))
273 "%s.%s = %s\n") % (section, name, uvalue))
274 return value
274 return value
275
275
276 def config(self, section, name, default=None, untrusted=False):
276 def config(self, section, name, default=None, untrusted=False):
277 return self._configcommon(section, name, default, 'get', untrusted)
277 return self._configcommon(section, name, default, 'get', untrusted)
278
278
279 def configbool(self, section, name, default=False, untrusted=False):
279 def configbool(self, section, name, default=False, untrusted=False):
280 return self._configcommon(section, name, default, 'getboolean',
280 return self._configcommon(section, name, default, 'getboolean',
281 untrusted)
281 untrusted)
282
282
283 def configlist(self, section, name, default=None, untrusted=False):
283 def configlist(self, section, name, default=None, untrusted=False):
284 """Return a list of comma/space separated strings"""
284 """Return a list of comma/space separated strings"""
285 result = self.config(section, name, untrusted=untrusted)
285 result = self.config(section, name, untrusted=untrusted)
286 if result is None:
286 if result is None:
287 result = default or []
287 result = default or []
288 if isinstance(result, basestring):
288 if isinstance(result, basestring):
289 result = result.replace(",", " ").split()
289 result = result.replace(",", " ").split()
290 return result
290 return result
291
291
292 def has_section(self, section, untrusted=False):
292 def has_section(self, section, untrusted=False):
293 '''tell whether section exists in config.'''
293 '''tell whether section exists in config.'''
294 cdata = self._get_cdata(untrusted)
294 cdata = self._get_cdata(untrusted)
295 return cdata.has_section(section)
295 return cdata.has_section(section)
296
296
297 def _configitems(self, section, untrusted, abort):
297 def _configitems(self, section, untrusted, abort):
298 items = {}
298 items = {}
299 cdata = self._get_cdata(untrusted)
299 cdata = self._get_cdata(untrusted)
300 if cdata.has_section(section):
300 if cdata.has_section(section):
301 try:
301 try:
302 items.update(dict(cdata.items(section)))
302 items.update(dict(cdata.items(section)))
303 except ConfigParser.InterpolationError, inst:
303 except ConfigParser.InterpolationError, inst:
304 msg = _("Error in configuration section [%s]:\n"
304 msg = _("Error in configuration section [%s]:\n"
305 "%s") % (section, inst)
305 "%s") % (section, inst)
306 if abort:
306 if abort:
307 raise util.Abort(msg)
307 raise util.Abort(msg)
308 self.warn(_("Ignored: %s\n") % msg)
308 self.warn(_("Ignored: %s\n") % msg)
309 return items
309 return items
310
310
311 def configitems(self, section, untrusted=False):
311 def configitems(self, section, untrusted=False):
312 items = self._configitems(section, untrusted=untrusted, abort=True)
312 items = self._configitems(section, untrusted=untrusted, abort=True)
313 if self.debugflag and not untrusted and self.ucdata:
313 if self.debugflag and not untrusted and self.ucdata:
314 uitems = self._configitems(section, untrusted=True, abort=False)
314 uitems = self._configitems(section, untrusted=True, abort=False)
315 for k in util.sort(uitems):
315 for k in util.sort(uitems):
316 if uitems[k] != items.get(k):
316 if uitems[k] != items.get(k):
317 self.warn(_("Ignoring untrusted configuration option "
317 self.warn(_("Ignoring untrusted configuration option "
318 "%s.%s = %s\n") % (section, k, uitems[k]))
318 "%s.%s = %s\n") % (section, k, uitems[k]))
319 return util.sort(items.items())
319 return util.sort(items.items())
320
320
321 def walkconfig(self, untrusted=False):
321 def walkconfig(self, untrusted=False):
322 cdata = self._get_cdata(untrusted)
322 cdata = self._get_cdata(untrusted)
323 sections = cdata.sections()
323 sections = cdata.sections()
324 sections.sort()
324 sections.sort()
325 for section in sections:
325 for section in sections:
326 for name, value in self.configitems(section, untrusted):
326 for name, value in self.configitems(section, untrusted):
327 yield section, name, str(value).replace('\n', '\\n')
327 yield section, name, str(value).replace('\n', '\\n')
328
328
329 def username(self):
329 def username(self):
330 """Return default username to be used in commits.
330 """Return default username to be used in commits.
331
331
332 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
332 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
333 and stop searching if one of these is set.
333 and stop searching if one of these is set.
334 If not found and ui.askusername is True, ask the user, else use
334 If not found and ui.askusername is True, ask the user, else use
335 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
335 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
336 """
336 """
337 user = os.environ.get("HGUSER")
337 user = os.environ.get("HGUSER")
338 if user is None:
338 if user is None:
339 user = self.config("ui", "username")
339 user = self.config("ui", "username")
340 if user is None:
340 if user is None:
341 user = os.environ.get("EMAIL")
341 user = os.environ.get("EMAIL")
342 if user is None and self.configbool("ui", "askusername"):
342 if user is None and self.configbool("ui", "askusername"):
343 user = self.prompt(_("Enter a commit username:"), default=None)
343 user = self.prompt(_("Enter a commit username:"), default=None)
344 if user is None:
344 if user is None:
345 try:
345 try:
346 user = '%s@%s' % (util.getuser(), socket.getfqdn())
346 user = '%s@%s' % (util.getuser(), socket.getfqdn())
347 self.warn(_("No username found, using '%s' instead\n") % user)
347 self.warn(_("No username found, using '%s' instead\n") % user)
348 except KeyError:
348 except KeyError:
349 pass
349 pass
350 if not user:
350 if not user:
351 raise util.Abort(_("Please specify a username."))
351 raise util.Abort(_("Please specify a username."))
352 if "\n" in user:
352 if "\n" in user:
353 raise util.Abort(_("username %s contains a newline\n") % `user`)
353 raise util.Abort(_("username %s contains a newline\n") % repr(user))
354 return user
354 return user
355
355
356 def shortuser(self, user):
356 def shortuser(self, user):
357 """Return a short representation of a user name or email address."""
357 """Return a short representation of a user name or email address."""
358 if not self.verbose: user = util.shortuser(user)
358 if not self.verbose: user = util.shortuser(user)
359 return user
359 return user
360
360
361 def expandpath(self, loc, default=None):
361 def expandpath(self, loc, default=None):
362 """Return repository location relative to cwd or from [paths]"""
362 """Return repository location relative to cwd or from [paths]"""
363 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
363 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
364 return loc
364 return loc
365
365
366 path = self.config("paths", loc)
366 path = self.config("paths", loc)
367 if not path and default is not None:
367 if not path and default is not None:
368 path = self.config("paths", default)
368 path = self.config("paths", default)
369 return path or loc
369 return path or loc
370
370
371 def pushbuffer(self):
371 def pushbuffer(self):
372 self.buffers.append([])
372 self.buffers.append([])
373
373
374 def popbuffer(self):
374 def popbuffer(self):
375 return "".join(self.buffers.pop())
375 return "".join(self.buffers.pop())
376
376
377 def write(self, *args):
377 def write(self, *args):
378 if self.buffers:
378 if self.buffers:
379 self.buffers[-1].extend([str(a) for a in args])
379 self.buffers[-1].extend([str(a) for a in args])
380 else:
380 else:
381 for a in args:
381 for a in args:
382 sys.stdout.write(str(a))
382 sys.stdout.write(str(a))
383
383
384 def write_err(self, *args):
384 def write_err(self, *args):
385 try:
385 try:
386 if not sys.stdout.closed: sys.stdout.flush()
386 if not sys.stdout.closed: sys.stdout.flush()
387 for a in args:
387 for a in args:
388 sys.stderr.write(str(a))
388 sys.stderr.write(str(a))
389 # stderr may be buffered under win32 when redirected to files,
389 # stderr may be buffered under win32 when redirected to files,
390 # including stdout.
390 # including stdout.
391 if not sys.stderr.closed: sys.stderr.flush()
391 if not sys.stderr.closed: sys.stderr.flush()
392 except IOError, inst:
392 except IOError, inst:
393 if inst.errno != errno.EPIPE:
393 if inst.errno != errno.EPIPE:
394 raise
394 raise
395
395
396 def flush(self):
396 def flush(self):
397 try: sys.stdout.flush()
397 try: sys.stdout.flush()
398 except: pass
398 except: pass
399 try: sys.stderr.flush()
399 try: sys.stderr.flush()
400 except: pass
400 except: pass
401
401
402 def _readline(self, prompt=''):
402 def _readline(self, prompt=''):
403 if self.isatty():
403 if self.isatty():
404 try:
404 try:
405 # magically add command line editing support, where
405 # magically add command line editing support, where
406 # available
406 # available
407 import readline
407 import readline
408 # force demandimport to really load the module
408 # force demandimport to really load the module
409 readline.read_history_file
409 readline.read_history_file
410 except ImportError:
410 except ImportError:
411 pass
411 pass
412 line = raw_input(prompt)
412 line = raw_input(prompt)
413 # When stdin is in binary mode on Windows, it can cause
413 # When stdin is in binary mode on Windows, it can cause
414 # raw_input() to emit an extra trailing carriage return
414 # raw_input() to emit an extra trailing carriage return
415 if os.linesep == '\r\n' and line and line[-1] == '\r':
415 if os.linesep == '\r\n' and line and line[-1] == '\r':
416 line = line[:-1]
416 line = line[:-1]
417 return line
417 return line
418
418
419 def prompt(self, msg, pat=None, default="y"):
419 def prompt(self, msg, pat=None, default="y"):
420 """Prompt user with msg, read response, and ensure it matches pat
420 """Prompt user with msg, read response, and ensure it matches pat
421
421
422 If not interactive -- the default is returned
422 If not interactive -- the default is returned
423 """
423 """
424 if not self.interactive:
424 if not self.interactive:
425 self.note(msg, ' ', default, "\n")
425 self.note(msg, ' ', default, "\n")
426 return default
426 return default
427 while True:
427 while True:
428 try:
428 try:
429 r = self._readline(msg + ' ')
429 r = self._readline(msg + ' ')
430 if not r:
430 if not r:
431 return default
431 return default
432 if not pat or re.match(pat, r):
432 if not pat or re.match(pat, r):
433 return r
433 return r
434 else:
434 else:
435 self.write(_("unrecognized response\n"))
435 self.write(_("unrecognized response\n"))
436 except EOFError:
436 except EOFError:
437 raise util.Abort(_('response expected'))
437 raise util.Abort(_('response expected'))
438
438
439 def getpass(self, prompt=None, default=None):
439 def getpass(self, prompt=None, default=None):
440 if not self.interactive: return default
440 if not self.interactive: return default
441 return getpass.getpass(prompt or _('password: '))
441 return getpass.getpass(prompt or _('password: '))
442 def status(self, *msg):
442 def status(self, *msg):
443 if not self.quiet: self.write(*msg)
443 if not self.quiet: self.write(*msg)
444 def warn(self, *msg):
444 def warn(self, *msg):
445 self.write_err(*msg)
445 self.write_err(*msg)
446 def note(self, *msg):
446 def note(self, *msg):
447 if self.verbose: self.write(*msg)
447 if self.verbose: self.write(*msg)
448 def debug(self, *msg):
448 def debug(self, *msg):
449 if self.debugflag: self.write(*msg)
449 if self.debugflag: self.write(*msg)
450 def edit(self, text, user):
450 def edit(self, text, user):
451 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
451 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
452 text=True)
452 text=True)
453 try:
453 try:
454 f = os.fdopen(fd, "w")
454 f = os.fdopen(fd, "w")
455 f.write(text)
455 f.write(text)
456 f.close()
456 f.close()
457
457
458 editor = self.geteditor()
458 editor = self.geteditor()
459
459
460 util.system("%s \"%s\"" % (editor, name),
460 util.system("%s \"%s\"" % (editor, name),
461 environ={'HGUSER': user},
461 environ={'HGUSER': user},
462 onerr=util.Abort, errprefix=_("edit failed"))
462 onerr=util.Abort, errprefix=_("edit failed"))
463
463
464 f = open(name)
464 f = open(name)
465 t = f.read()
465 t = f.read()
466 f.close()
466 f.close()
467 t = re.sub("(?m)^HG:.*\n", "", t)
467 t = re.sub("(?m)^HG:.*\n", "", t)
468 finally:
468 finally:
469 os.unlink(name)
469 os.unlink(name)
470
470
471 return t
471 return t
472
472
473 def print_exc(self):
473 def print_exc(self):
474 '''print exception traceback if traceback printing enabled.
474 '''print exception traceback if traceback printing enabled.
475 only to call in exception handler. returns true if traceback
475 only to call in exception handler. returns true if traceback
476 printed.'''
476 printed.'''
477 if self.traceback:
477 if self.traceback:
478 traceback.print_exc()
478 traceback.print_exc()
479 return self.traceback
479 return self.traceback
480
480
481 def geteditor(self):
481 def geteditor(self):
482 '''return editor to use'''
482 '''return editor to use'''
483 return (os.environ.get("HGEDITOR") or
483 return (os.environ.get("HGEDITOR") or
484 self.config("ui", "editor") or
484 self.config("ui", "editor") or
485 os.environ.get("VISUAL") or
485 os.environ.get("VISUAL") or
486 os.environ.get("EDITOR", "vi"))
486 os.environ.get("EDITOR", "vi"))
General Comments 0
You need to be logged in to leave comments. Login now