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 """Import generete werror.h/doserr.c files from WSPP HTML"""
27 from xml
.dom
import minidom
28 from optparse
import OptionParser
, OptionGroup
30 _wspp_werror_url
= 'http://msdn.microsoft.com/en-us/library/cc231199%28PROT.10%29.aspx'
32 class WerrorHtmlParser(object):
34 Parses HTML from WSPP documentation generating dictionary of
35 dictionaries with following keys:
36 - "err_hex" - hex number (as string)
37 - "err_name" - error name
38 - "err_desc" - error long description
39 For the key of returned dictionary err_hex is used,
40 i.e. "hex-error-code-str" => {error dictionary object}
43 ERROR_PREFIX
= ['ERROR_', 'NERR_', 'FRS_', 'RPC_', 'EPT_', 'OR_', 'WAIT_TIMEOUT']
44 ERROR_REPLACE
= ['ERROR_']
46 def __init__(self
, opt
):
48 self
._errors
_skipped
= []
51 def _is_error_code_name(self
, err_name
):
52 for pref
in self
.ERROR_PREFIX
:
53 if err_name
.startswith(pref
):
57 def _make_werr_name(self
, err_name
):
58 err_name
= err_name
.upper()
59 for pref
in self
.ERROR_REPLACE
:
60 if err_name
.startswith(pref
):
61 return err_name
.replace(pref
, 'WERR_', 1)
62 return 'WERR_' + err_name
64 def parse_url(self
, url
):
66 html
= self
._load
_url
(url
)
68 # let minidom to parse the tree, should be:
70 # p -> [hex code, br, error code]
72 table_node
= minidom
.parseString(html
)
73 for row_node
in table_node
.getElementsByTagName("tr"):
74 # verify we got right number of td elements
75 td_nodes
= row_node
.getElementsByTagName('td')
76 if len(td_nodes
) != 2:
78 # now get the real data
79 p_nodes
= row_node
.getElementsByTagName('p')
80 if len(p_nodes
) != 2: continue
81 if len(p_nodes
[0].childNodes
) != 3: continue
82 if len(p_nodes
[1].childNodes
) != 1: continue
83 err_hex
= str(p_nodes
[0].childNodes
[0].nodeValue
)
84 err_name
= str(p_nodes
[0].childNodes
[2].nodeValue
)
85 err_desc
= p_nodes
[1].childNodes
[0].nodeValue
.encode('utf-8')
86 err_desc
= err_desc
.replace('"', '\\"').replace("\'", "\\'")
88 if not err_hex
.startswith('0x'): continue
89 if not self
._is
_error
_code
_name
(err_name
):
90 self
._errors
_skipped
.append("%s - %s - %d" % (err_name
, err_hex
, int(err_hex
, 16)))
93 err_name
= self
._make
_werr
_name
(err_name
)
94 err_def
= {'err_hex': err_hex
,
97 'code': int(err_hex
, 16)}
98 errors
[err_def
['code']] = err_def
100 # print skipped errors
101 if self
.opt
.print_skipped
and len(self
._errors
_skipped
):
102 print "\nErrors skipped during HTML parsing:"
103 pprint
.pprint(self
._errors
_skipped
)
108 def _load_url(self
, url
):
111 fp
= urllib
.urlopen(url
)
113 html_str
+= line
.strip()
116 print "error loading url: " + e
.strerror
119 # currently ERROR codes are rendered as table
120 # locate table chunk with ERROR_SUCCESS
121 html
= [x
for x
in html_str
.split('<table ') if "ERROR_SUCCESS" in x
]
122 html
= '<table ' + html
[0]
123 pos
= html
.find('</table>')
126 html
= html
[:pos
] + '</table>'
129 html
= re
.sub(r
'<a[^>]*>(.*?)</a>', r
'\1', html
)
134 class WerrorGenerator(object):
136 provides methods to generate parts of werror.h and doserr.c files
139 FNAME_WERRORS
= 'w32errors.lst'
140 FNAME_WERROR_DEFS
= 'werror_defs.h'
141 FNAME_DOSERR_DEFS
= 'doserr_defs.c'
142 FNAME_DOSERR_DESC
= 'doserr_desc.c'
144 def __init__(self
, opt
):
146 self
._out
_dir
= opt
.out_dir
149 def _open_out_file(self
, fname
):
150 fname
= os
.path
.join(self
._out
_dir
, fname
)
151 return open(fname
, "w")
153 def _gen_werrors_list(self
, errors
):
154 """uses 'errors' dictionary to display list of Win32 Errors"""
156 fp
= self
._open
_out
_file
(self
.FNAME_WERRORS
)
157 for err_code
in sorted(errors
.keys()):
158 err_name
= errors
[err_code
]['err_name']
163 def _gen_werror_defs(self
, errors
):
164 """uses 'errors' dictionary to generate werror.h file"""
166 fp
= self
._open
_out
_file
(self
.FNAME_WERROR_DEFS
)
167 for err_code
in sorted(errors
.keys()):
168 err_name
= errors
[err_code
]['err_name']
169 err_hex
= errors
[err_code
]['err_hex']
170 fp
.write('#define %s\tW_ERROR(%s)' % (err_name
, err_hex
))
174 def _gen_doserr_defs(self
, errors
):
175 """uses 'errors' dictionary to generate defines in doserr.c file"""
177 fp
= self
._open
_out
_file
(self
.FNAME_DOSERR_DEFS
)
178 for err_code
in sorted(errors
.keys()):
179 err_name
= errors
[err_code
]['err_name']
180 fp
.write('\t{ "%s", %s },' % (err_name
, err_name
))
184 def _gen_doserr_descriptions(self
, errors
):
185 """uses 'errors' dictionary to generate descriptions in doserr.c file"""
187 fp
= self
._open
_out
_file
(self
.FNAME_DOSERR_DESC
)
188 for err_code
in sorted(errors
.keys()):
189 err_name
= errors
[err_code
]['err_name']
190 fp
.write('\t{ %s, "%s" },' % (err_name
, errors
[err_code
]['err_desc']))
194 def _lookup_error_by_name(self
, err_name
, defined_errors
):
195 for err
in defined_errors
.itervalues():
196 if err
['err_name'] == err_name
:
200 def _filter_errors(self
, errors
, defined_errors
):
202 returns tuple (new_erros, diff_code_errors, diff_name_errors)
203 new_errors - dictionary of errors not in defined_errors
204 diff_code_errors - list of errors found in defined_errors
205 but with different value
206 diff_name_errors - list of errors found with same code in
207 defined_errors, but with different name
208 Most critical is diff_code_errors list to be empty!
211 diff_code_errors
= []
212 diff_name_errors
= []
213 for err_def
in errors
.itervalues():
215 # try get defined error by code
216 if defined_errors
.has_key(err_def
['code']):
217 old_err
= defined_errors
[err_def
['code']]
218 if err_def
['err_name'] != old_err
['err_name']:
219 warning
= {'msg': 'New and Old errors has different error names',
222 diff_name_errors
.append(warning
)
224 # sanity check for errors with same name but different values
225 old_err
= self
._lookup
_error
_by
_name
(err_def
['err_name'], defined_errors
)
227 if err_def
['code'] != old_err
['code']:
228 warning
= {'msg': 'New and Old error defs has different error value',
231 diff_code_errors
.append(warning
)
232 # exclude error already defined with same name
234 # do add the error in new_errors if everything is fine
236 new_errors
[err_def
['code']] = err_def
238 return (new_errors
, diff_code_errors
, diff_name_errors
)
240 def generate(self
, errors
):
241 # load already defined error codes
242 werr_parser
= WerrorParser(self
.opt
)
244 no_value_errors
) = werr_parser
.load_err_codes(self
.opt
.werror_file
)
245 if not defined_errors
:
246 print "\nUnable to load existing errors file: %s" % self
.opt
.werror_file
248 if self
.opt
.verbose
and len(no_value_errors
):
249 print "\nWarning: there are errors defines using macro value:"
250 pprint
.pprint(no_value_errors
)
252 # filter generated error codes
255 diff_name_errors
) = self
._filter
_errors
(errors
, defined_errors
)
257 print("\nFound %d errors with same names but different error values! Aborting."
258 % len(diff_code_errors
))
259 pprint
.pprint(diff_code_errors
)
263 print("\nFound %d errors with same values but different names (should be normal)"
264 % len(diff_name_errors
))
265 pprint
.pprint(diff_name_errors
)
267 # finally generate output files
268 self
._gen
_werror
_defs
(new_errors
)
269 self
._gen
_doserr
_defs
(new_errors
)
270 self
._gen
_werrors
_list
(errors
)
271 self
._gen
_doserr
_descriptions
(errors
)
274 class WerrorParser(object):
276 Parses errors defined in werror.h file
279 def __init__(self
, opt
):
283 def _parse_werror_line(self
, line
):
284 m
= re
.match('#define[ \t]*(.*?)[ \t]*W_ERROR\((.*?)\)', line
)
285 if not m
or (len(m
.groups()) != 2):
287 if len(m
.group(1)) == 0:
289 if str(m
.group(2)).startswith('0x'):
290 err_code
= int(m
.group(2), 16)
291 elif m
.group(2).isdigit():
292 err_code
= int(m
.group(2))
294 self
.err_no_values
.append(line
)
296 return {'err_name': str(m
.group(1)),
297 'err_hex': "0x%08X" % err_code
,
301 def load_err_codes(self
, fname
):
304 dictionary of "hex_err_code" => {code, name}
305 "hex_err_code" is string
306 "code" is int value for the error
307 list of errors that was ignored for some reason
309 # reset internal variables
310 self
.err_no_values
= []
313 for line
in fp
.readlines():
314 err_def
= self
._parse
_werror
_line
(line
)
316 err_codes
[err_def
['code']] = err_def
318 return (err_codes
, self
.err_no_values
)
322 def _generate_files(opt
):
323 parser
= WerrorHtmlParser(opt
)
324 errors
= parser
.parse_url(opt
.url
)
326 out
= WerrorGenerator(opt
)
331 if __name__
== '__main__':
332 _cur_dir
= os
.path
.abspath(os
.path
.dirname(__file__
))
333 opt_parser
= OptionParser(usage
="usage: %prog [options]", version
="%prog 0.3")
334 opt_group
= OptionGroup(opt_parser
, "Main options")
335 opt_group
.add_option("--url", dest
="url",
336 default
=_wspp_werror_url
,
337 help="url for w32 error codes html - may be local file")
338 opt_group
.add_option("--out", dest
="out_dir",
340 help="output dir for generated files")
341 opt_group
.add_option("--werror", dest
="werror_file",
342 default
=os
.path
.join(_cur_dir
, 'werror.h'),
343 help="path to werror.h file")
344 opt_group
.add_option("--print_skipped",
345 action
="store_true", dest
="print_skipped", default
=False,
346 help="print errors skipped during HTML parsing")
347 opt_group
.add_option("-q", "--quiet",
348 action
="store_false", dest
="verbose", default
=True,
349 help="don't print warnings to stdout")
351 opt_parser
.add_option_group(opt_group
)
353 (options
, args
) = opt_parser
.parse_args()
355 # add some options to be used internally
356 options
.err_defs_file
= os
.path
.join(options
.out_dir
, WerrorGenerator
.FNAME_WERROR_DEFS
)
357 options
.dos_defs_file
= os
.path
.join(options
.out_dir
, WerrorGenerator
.FNAME_DOSERR_DEFS
)
358 options
.dos_desc_file
= os
.path
.join(options
.out_dir
, WerrorGenerator
.FNAME_DOSERR_DESC
)
361 _generate_files(options
)