3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from __future__ import print_function
21 """Import generete werror.h/doserr.c files from WSPP HTML"""
28 from xml.dom import minidom
29 from optparse import OptionParser, OptionGroup
31 _wspp_werror_url = 'http://msdn.microsoft.com/en-us/library/cc231199%28PROT.10%29.aspx'
33 class WerrorHtmlParser(object):
35 Parses HTML from WSPP documentation generating dictionary of
36 dictionaries with following keys:
37 - "err_hex" - hex number (as string)
38 - "err_name" - error name
39 - "err_desc" - error long description
40 For the key of returned dictionary err_hex is used,
41 i.e. "hex-error-code-str" => {error dictionary object}
44 ERROR_PREFIX = ['ERROR_', 'NERR_', 'FRS_', 'RPC_', 'EPT_', 'OR_', 'WAIT_TIMEOUT']
45 ERROR_REPLACE = ['ERROR_']
47 def __init__(self, opt):
49 self._errors_skipped = []
52 def _is_error_code_name(self, err_name):
53 for pref in self.ERROR_PREFIX:
54 if err_name.startswith(pref):
58 def _make_werr_name(self, err_name):
59 err_name = err_name.upper()
60 for pref in self.ERROR_REPLACE:
61 if err_name.startswith(pref):
62 return err_name.replace(pref, 'WERR_', 1)
63 return 'WERR_' + err_name
65 def parse_url(self, url):
67 html = self._load_url(url)
69 # let minidom to parse the tree, should be:
71 # p -> [hex code, br, error code]
73 table_node = minidom.parseString(html)
74 for row_node in table_node.getElementsByTagName("tr"):
75 # verify we got right number of td elements
76 td_nodes = row_node.getElementsByTagName('td')
77 if len(td_nodes) != 2:
79 # now get the real data
80 p_nodes = row_node.getElementsByTagName('p')
81 if len(p_nodes) != 3: continue
82 if len(p_nodes[0].childNodes) != 1: continue
83 if len(p_nodes[1].childNodes) != 1: continue
84 if len(p_nodes[2].childNodes) != 1: continue
85 err_hex = str(p_nodes[0].childNodes[0].nodeValue)
86 err_name = str(p_nodes[1].childNodes[0].nodeValue)
87 err_desc = p_nodes[2].childNodes[0].nodeValue.encode('utf-8')
88 err_desc = err_desc.replace('"', '\\"').replace("\'", "\\'")
90 if not err_hex.startswith('0x'): continue
91 if not self._is_error_code_name(err_name):
92 self._errors_skipped.append("%s - %s - %d" % (err_name, err_hex, int(err_hex, 16)))
95 err_name = self._make_werr_name(err_name)
96 err_def = {'err_hex': err_hex,
99 'code': int(err_hex, 16)}
100 errors[err_def['code']] = err_def
102 # print skipped errors
103 if self.opt.print_skipped and len(self._errors_skipped):
104 print("\nErrors skipped during HTML parsing:")
105 pprint.pprint(self._errors_skipped)
110 def _load_url(self, url):
113 fp = urllib.urlopen(url)
115 html_str += line.strip()
118 print("error loading url: " + e.strerror)
121 # currently ERROR codes are rendered as table
122 # locate table chunk with ERROR_SUCCESS
123 html = [x for x in html_str.split('<table ') if "ERROR_SUCCESS" in x]
124 html = '<table ' + html[0]
125 pos = html.find('</table>')
128 html = html[:pos] + '</table>'
131 html = re.sub(r'<a[^>]*>(.*?)</a>', r'\1', html)
136 class WerrorGenerator(object):
138 provides methods to generate parts of werror.h and doserr.c files
141 FNAME_WERRORS = 'w32errors.lst'
142 FNAME_WERROR_DEFS = 'werror_defs.h'
143 FNAME_DOSERR_DEFS = 'doserr_defs.c'
144 FNAME_DOSERR_DESC = 'doserr_desc.c'
146 def __init__(self, opt):
148 self._out_dir = opt.out_dir
151 def _open_out_file(self, fname):
152 fname = os.path.join(self._out_dir, fname)
153 return open(fname, "w")
155 def _gen_werrors_list(self, errors):
156 """uses 'errors' dictionary to display list of Win32 Errors"""
158 fp = self._open_out_file(self.FNAME_WERRORS)
159 for err_code in sorted(errors.keys()):
160 err_name = errors[err_code]['err_name']
165 def _gen_werror_defs(self, errors):
166 """uses 'errors' dictionary to generate werror.h file"""
168 fp = self._open_out_file(self.FNAME_WERROR_DEFS)
169 for err_code in sorted(errors.keys()):
170 err_name = errors[err_code]['err_name']
171 err_hex = errors[err_code]['err_hex']
172 fp.write('#define %s\tW_ERROR(%s)' % (err_name, err_hex))
176 def _gen_doserr_defs(self, errors):
177 """uses 'errors' dictionary to generate defines in doserr.c file"""
179 fp = self._open_out_file(self.FNAME_DOSERR_DEFS)
180 for err_code in sorted(errors.keys()):
181 err_name = errors[err_code]['err_name']
182 fp.write('\t{ "%s", %s },' % (err_name, err_name))
186 def _gen_doserr_descriptions(self, errors):
187 """uses 'errors' dictionary to generate descriptions in doserr.c file"""
189 fp = self._open_out_file(self.FNAME_DOSERR_DESC)
190 for err_code in sorted(errors.keys()):
191 err_name = errors[err_code]['err_name']
192 fp.write('\t{ %s, "%s" },' % (err_name, errors[err_code]['err_desc']))
196 def _lookup_error_by_name(self, err_name, defined_errors):
197 for err in defined_errors.values():
198 if err['err_name'] == err_name:
202 def _filter_errors(self, errors, defined_errors):
204 returns tuple (new_erros, diff_code_errors, diff_name_errors)
205 new_errors - dictionary of errors not in defined_errors
206 diff_code_errors - list of errors found in defined_errors
207 but with different value
208 diff_name_errors - list of errors found with same code in
209 defined_errors, but with different name
210 Most critical is diff_code_errors list to be empty!
213 diff_code_errors = []
214 diff_name_errors = []
215 for err_def in errors.values():
217 # try get defined error by code
218 if defined_errors.has_key(err_def['code']):
219 old_err = defined_errors[err_def['code']]
220 if err_def['err_name'] != old_err['err_name']:
221 warning = {'msg': 'New and Old errors has different error names',
224 diff_name_errors.append(warning)
226 # sanity check for errors with same name but different values
227 old_err = self._lookup_error_by_name(err_def['err_name'], defined_errors)
229 if err_def['code'] != old_err['code']:
230 warning = {'msg': 'New and Old error defs has different error value',
233 diff_code_errors.append(warning)
234 # exclude error already defined with same name
236 # do add the error in new_errors if everything is fine
238 new_errors[err_def['code']] = err_def
240 return (new_errors, diff_code_errors, diff_name_errors)
242 def generate(self, errors):
243 # load already defined error codes
244 werr_parser = WerrorParser(self.opt)
246 no_value_errors) = werr_parser.load_err_codes(self.opt.werror_file)
247 if not defined_errors:
248 print("\nUnable to load existing errors file: %s" % self.opt.werror_file)
250 if self.opt.verbose and len(no_value_errors):
251 print("\nWarning: there are errors defines using macro value:")
252 pprint.pprint(no_value_errors)
254 # filter generated error codes
257 diff_name_errors) = self._filter_errors(errors, defined_errors)
259 print("\nFound %d errors with same names but different error values! Aborting."
260 % len(diff_code_errors))
261 pprint.pprint(diff_code_errors)
265 print("\nFound %d errors with same values but different names (should be normal)"
266 % len(diff_name_errors))
267 pprint.pprint(diff_name_errors)
269 # finally generate output files
270 self._gen_werror_defs(new_errors)
271 self._gen_doserr_defs(new_errors)
272 self._gen_werrors_list(errors)
273 self._gen_doserr_descriptions(errors)
276 class WerrorParser(object):
278 Parses errors defined in werror.h file
281 def __init__(self, opt):
285 def _parse_werror_line(self, line):
286 m = re.match('#define[ \t]*(.*?)[ \t]*W_ERROR\((.*?)\)', line)
287 if not m or (len(m.groups()) != 2):
289 if len(m.group(1)) == 0:
291 if str(m.group(2)).startswith('0x'):
292 err_code = int(m.group(2), 16)
293 elif m.group(2).isdigit():
294 err_code = int(m.group(2))
296 self.err_no_values.append(line)
298 return {'err_name': str(m.group(1)),
299 'err_hex': "0x%08X" % err_code,
303 def load_err_codes(self, fname):
306 dictionary of "hex_err_code" => {code, name}
307 "hex_err_code" is string
308 "code" is int value for the error
309 list of errors that was ignored for some reason
311 # reset internal variables
312 self.err_no_values = []
315 for line in fp.readlines():
316 err_def = self._parse_werror_line(line)
318 err_codes[err_def['code']] = err_def
320 return (err_codes, self.err_no_values)
324 def _generate_files(opt):
325 parser = WerrorHtmlParser(opt)
326 errors = parser.parse_url(opt.url)
328 out = WerrorGenerator(opt)
333 if __name__ == '__main__':
334 _cur_dir = os.path.abspath(os.path.dirname(__file__))
335 opt_parser = OptionParser(usage="usage: %prog [options]", version="%prog 0.3")
336 opt_group = OptionGroup(opt_parser, "Main options")
337 opt_group.add_option("--url", dest="url",
338 default=_wspp_werror_url,
339 help="url for w32 error codes html - may be local file")
340 opt_group.add_option("--out", dest="out_dir",
342 help="output dir for generated files")
343 opt_group.add_option("--werror", dest="werror_file",
344 default=os.path.join(_cur_dir, 'werror.h'),
345 help="path to werror.h file")
346 opt_group.add_option("--print_skipped",
347 action="store_true", dest="print_skipped", default=False,
348 help="print errors skipped during HTML parsing")
349 opt_group.add_option("-q", "--quiet",
350 action="store_false", dest="verbose", default=False,
351 help="don't print warnings to stdout")
353 opt_parser.add_option_group(opt_group)
355 (options, args) = opt_parser.parse_args()
357 # add some options to be used internally
358 options.err_defs_file = os.path.join(options.out_dir, WerrorGenerator.FNAME_WERROR_DEFS)
359 options.dos_defs_file = os.path.join(options.out_dir, WerrorGenerator.FNAME_DOSERR_DEFS)
360 options.dos_desc_file = os.path.join(options.out_dir, WerrorGenerator.FNAME_DOSERR_DESC)
363 _generate_files(options)