##// END OF EJS Templates
config: discard UTF-8 BOM if found
Matt Mackall -
r16348:f350021e default
parent child Browse files
Show More
@@ -1,176 +1,179 b''
1 # config.py - configuration parsing for Mercurial
1 # config.py - configuration parsing for Mercurial
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import error, util
9 import error, util
10 import re, os, errno
10 import re, os, errno
11
11
12 class sortdict(dict):
12 class sortdict(dict):
13 'a simple sorted dictionary'
13 'a simple sorted dictionary'
14 def __init__(self, data=None):
14 def __init__(self, data=None):
15 self._list = []
15 self._list = []
16 if data:
16 if data:
17 self.update(data)
17 self.update(data)
18 def copy(self):
18 def copy(self):
19 return sortdict(self)
19 return sortdict(self)
20 def __setitem__(self, key, val):
20 def __setitem__(self, key, val):
21 if key in self:
21 if key in self:
22 self._list.remove(key)
22 self._list.remove(key)
23 self._list.append(key)
23 self._list.append(key)
24 dict.__setitem__(self, key, val)
24 dict.__setitem__(self, key, val)
25 def __iter__(self):
25 def __iter__(self):
26 return self._list.__iter__()
26 return self._list.__iter__()
27 def update(self, src):
27 def update(self, src):
28 for k in src:
28 for k in src:
29 self[k] = src[k]
29 self[k] = src[k]
30 def clear(self):
30 def clear(self):
31 dict.clear(self)
31 dict.clear(self)
32 self._list = []
32 self._list = []
33 def items(self):
33 def items(self):
34 return [(k, self[k]) for k in self._list]
34 return [(k, self[k]) for k in self._list]
35 def __delitem__(self, key):
35 def __delitem__(self, key):
36 dict.__delitem__(self, key)
36 dict.__delitem__(self, key)
37 self._list.remove(key)
37 self._list.remove(key)
38
38
39 class config(object):
39 class config(object):
40 def __init__(self, data=None):
40 def __init__(self, data=None):
41 self._data = {}
41 self._data = {}
42 self._source = {}
42 self._source = {}
43 if data:
43 if data:
44 for k in data._data:
44 for k in data._data:
45 self._data[k] = data[k].copy()
45 self._data[k] = data[k].copy()
46 self._source = data._source.copy()
46 self._source = data._source.copy()
47 def copy(self):
47 def copy(self):
48 return config(self)
48 return config(self)
49 def __contains__(self, section):
49 def __contains__(self, section):
50 return section in self._data
50 return section in self._data
51 def __getitem__(self, section):
51 def __getitem__(self, section):
52 return self._data.get(section, {})
52 return self._data.get(section, {})
53 def __iter__(self):
53 def __iter__(self):
54 for d in self.sections():
54 for d in self.sections():
55 yield d
55 yield d
56 def update(self, src):
56 def update(self, src):
57 for s in src:
57 for s in src:
58 if s not in self:
58 if s not in self:
59 self._data[s] = sortdict()
59 self._data[s] = sortdict()
60 self._data[s].update(src._data[s])
60 self._data[s].update(src._data[s])
61 self._source.update(src._source)
61 self._source.update(src._source)
62 def get(self, section, item, default=None):
62 def get(self, section, item, default=None):
63 return self._data.get(section, {}).get(item, default)
63 return self._data.get(section, {}).get(item, default)
64
64
65 def backup(self, section, item):
65 def backup(self, section, item):
66 """return a tuple allowing restore to reinstall a previous valuesi
66 """return a tuple allowing restore to reinstall a previous valuesi
67
67
68 The main reason we need it is because it handle the "no data" case.
68 The main reason we need it is because it handle the "no data" case.
69 """
69 """
70 try:
70 try:
71 value = self._data[section][item]
71 value = self._data[section][item]
72 source = self.source(section, item)
72 source = self.source(section, item)
73 return (section, item, value, source)
73 return (section, item, value, source)
74 except KeyError:
74 except KeyError:
75 return (section, item)
75 return (section, item)
76
76
77 def source(self, section, item):
77 def source(self, section, item):
78 return self._source.get((section, item), "")
78 return self._source.get((section, item), "")
79 def sections(self):
79 def sections(self):
80 return sorted(self._data.keys())
80 return sorted(self._data.keys())
81 def items(self, section):
81 def items(self, section):
82 return self._data.get(section, {}).items()
82 return self._data.get(section, {}).items()
83 def set(self, section, item, value, source=""):
83 def set(self, section, item, value, source=""):
84 if section not in self:
84 if section not in self:
85 self._data[section] = sortdict()
85 self._data[section] = sortdict()
86 self._data[section][item] = value
86 self._data[section][item] = value
87 self._source[(section, item)] = source
87 self._source[(section, item)] = source
88
88
89 def restore(self, data):
89 def restore(self, data):
90 """restore data returned by self.backup"""
90 """restore data returned by self.backup"""
91 if len(data) == 4:
91 if len(data) == 4:
92 # restore old data
92 # restore old data
93 section, item, value, source = data
93 section, item, value, source = data
94 self._data[section][item] = value
94 self._data[section][item] = value
95 self._source[(section, item)] = source
95 self._source[(section, item)] = source
96 else:
96 else:
97 # no data before, remove everything
97 # no data before, remove everything
98 section, item = data
98 section, item = data
99 if section in self._data:
99 if section in self._data:
100 del self._data[section][item]
100 del self._data[section][item]
101 self._source.pop((section, item), None)
101 self._source.pop((section, item), None)
102
102
103 def parse(self, src, data, sections=None, remap=None, include=None):
103 def parse(self, src, data, sections=None, remap=None, include=None):
104 sectionre = re.compile(r'\[([^\[]+)\]')
104 sectionre = re.compile(r'\[([^\[]+)\]')
105 itemre = re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
105 itemre = re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
106 contre = re.compile(r'\s+(\S|\S.*\S)\s*$')
106 contre = re.compile(r'\s+(\S|\S.*\S)\s*$')
107 emptyre = re.compile(r'(;|#|\s*$)')
107 emptyre = re.compile(r'(;|#|\s*$)')
108 commentre = re.compile(r'(;|#)')
108 commentre = re.compile(r'(;|#)')
109 unsetre = re.compile(r'%unset\s+(\S+)')
109 unsetre = re.compile(r'%unset\s+(\S+)')
110 includere = re.compile(r'%include\s+(\S|\S.*\S)\s*$')
110 includere = re.compile(r'%include\s+(\S|\S.*\S)\s*$')
111 section = ""
111 section = ""
112 item = None
112 item = None
113 line = 0
113 line = 0
114 cont = False
114 cont = False
115
115
116 for l in data.splitlines(True):
116 for l in data.splitlines(True):
117 line += 1
117 line += 1
118 if line == 1 and l.startswith('\xef\xbb\xbf'):
119 # Someone set us up the BOM
120 l = l[3:]
118 if cont:
121 if cont:
119 if commentre.match(l):
122 if commentre.match(l):
120 continue
123 continue
121 m = contre.match(l)
124 m = contre.match(l)
122 if m:
125 if m:
123 if sections and section not in sections:
126 if sections and section not in sections:
124 continue
127 continue
125 v = self.get(section, item) + "\n" + m.group(1)
128 v = self.get(section, item) + "\n" + m.group(1)
126 self.set(section, item, v, "%s:%d" % (src, line))
129 self.set(section, item, v, "%s:%d" % (src, line))
127 continue
130 continue
128 item = None
131 item = None
129 cont = False
132 cont = False
130 m = includere.match(l)
133 m = includere.match(l)
131 if m:
134 if m:
132 inc = util.expandpath(m.group(1))
135 inc = util.expandpath(m.group(1))
133 base = os.path.dirname(src)
136 base = os.path.dirname(src)
134 inc = os.path.normpath(os.path.join(base, inc))
137 inc = os.path.normpath(os.path.join(base, inc))
135 if include:
138 if include:
136 try:
139 try:
137 include(inc, remap=remap, sections=sections)
140 include(inc, remap=remap, sections=sections)
138 except IOError, inst:
141 except IOError, inst:
139 if inst.errno != errno.ENOENT:
142 if inst.errno != errno.ENOENT:
140 raise error.ParseError(_("cannot include %s (%s)")
143 raise error.ParseError(_("cannot include %s (%s)")
141 % (inc, inst.strerror),
144 % (inc, inst.strerror),
142 "%s:%s" % (src, line))
145 "%s:%s" % (src, line))
143 continue
146 continue
144 if emptyre.match(l):
147 if emptyre.match(l):
145 continue
148 continue
146 m = sectionre.match(l)
149 m = sectionre.match(l)
147 if m:
150 if m:
148 section = m.group(1)
151 section = m.group(1)
149 if remap:
152 if remap:
150 section = remap.get(section, section)
153 section = remap.get(section, section)
151 if section not in self:
154 if section not in self:
152 self._data[section] = sortdict()
155 self._data[section] = sortdict()
153 continue
156 continue
154 m = itemre.match(l)
157 m = itemre.match(l)
155 if m:
158 if m:
156 item = m.group(1)
159 item = m.group(1)
157 cont = True
160 cont = True
158 if sections and section not in sections:
161 if sections and section not in sections:
159 continue
162 continue
160 self.set(section, item, m.group(2), "%s:%d" % (src, line))
163 self.set(section, item, m.group(2), "%s:%d" % (src, line))
161 continue
164 continue
162 m = unsetre.match(l)
165 m = unsetre.match(l)
163 if m:
166 if m:
164 name = m.group(1)
167 name = m.group(1)
165 if sections and section not in sections:
168 if sections and section not in sections:
166 continue
169 continue
167 if self.get(section, name) is not None:
170 if self.get(section, name) is not None:
168 del self._data[section][name]
171 del self._data[section][name]
169 continue
172 continue
170
173
171 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
174 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
172
175
173 def read(self, path, fp=None, sections=None, remap=None):
176 def read(self, path, fp=None, sections=None, remap=None):
174 if not fp:
177 if not fp:
175 fp = util.posixfile(path)
178 fp = util.posixfile(path)
176 self.parse(path, fp.read(), sections, remap, self.read)
179 self.parse(path, fp.read(), sections, remap, self.read)
General Comments 0
You need to be logged in to leave comments. Login now