2010-03-24 16:50:50 +11:00
#!/usr/bin/env python
2009-09-30 12:28:10 +03:00
# Unix SMB/CIFS implementation.
# Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2009
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
""" Import generete werror.h/doserr.c files from WSPP HTML """
import re
import os
import sys
import urllib
import pprint
from xml . dom import minidom
from optparse import OptionParser , OptionGroup
_wspp_werror_url = ' http://msdn.microsoft.com/en-us/library/cc231199 % 28PROT.10 % 29.aspx '
class WerrorHtmlParser ( object ) :
"""
Parses HTML from WSPP documentation generating dictionary of
dictionaries with following keys :
- " err_hex " - hex number ( as string )
- " err_name " - error name
- " err_desc " - error long description
For the key of returned dictionary err_hex is used ,
i . e . " hex-error-code-str " = > { error dictionary object }
"""
ERROR_PREFIX = [ ' ERROR_ ' , ' NERR_ ' , ' FRS_ ' , ' RPC_ ' , ' EPT_ ' , ' OR_ ' , ' WAIT_TIMEOUT ' ]
ERROR_REPLACE = [ ' ERROR_ ' ]
def __init__ ( self , opt ) :
self . opt = opt
self . _errors_skipped = [ ]
pass
def _is_error_code_name ( self , err_name ) :
for pref in self . ERROR_PREFIX :
if err_name . startswith ( pref ) :
return True
return False
def _make_werr_name ( self , err_name ) :
err_name = err_name . upper ( )
for pref in self . ERROR_REPLACE :
if err_name . startswith ( pref ) :
return err_name . replace ( pref , ' WERR_ ' , 1 )
return ' WERR_ ' + err_name
def parse_url ( self , url ) :
errors = { }
html = self . _load_url ( url )
# let minidom to parse the tree, should be:
# table -> tr -> td
# p -> [hex code, br, error code]
# p -> [description]
table_node = minidom . parseString ( html )
for row_node in table_node . getElementsByTagName ( " tr " ) :
# verify we got right number of td elements
td_nodes = row_node . getElementsByTagName ( ' td ' )
if len ( td_nodes ) != 2 :
continue
# now get the real data
p_nodes = row_node . getElementsByTagName ( ' p ' )
if len ( p_nodes ) != 2 : continue
if len ( p_nodes [ 0 ] . childNodes ) != 3 : continue
if len ( p_nodes [ 1 ] . childNodes ) != 1 : continue
err_hex = str ( p_nodes [ 0 ] . childNodes [ 0 ] . nodeValue )
err_name = str ( p_nodes [ 0 ] . childNodes [ 2 ] . nodeValue )
err_desc = p_nodes [ 1 ] . childNodes [ 0 ] . nodeValue . encode ( ' utf-8 ' )
err_desc = err_desc . replace ( ' " ' , ' \\ " ' ) . replace ( " \' " , " \\ ' " )
# do some checking
if not err_hex . startswith ( ' 0x ' ) : continue
if not self . _is_error_code_name ( err_name ) :
self . _errors_skipped . append ( " %s - %s - %d " % ( err_name , err_hex , int ( err_hex , 16 ) ) )
continue
# create entry
err_name = self . _make_werr_name ( err_name )
err_def = { ' err_hex ' : err_hex ,
' err_name ' : err_name ,
' err_desc ' : err_desc ,
' code ' : int ( err_hex , 16 ) }
errors [ err_def [ ' code ' ] ] = err_def
# print skipped errors
if self . opt . print_skipped and len ( self . _errors_skipped ) :
print " \n Errors skipped during HTML parsing: "
pprint . pprint ( self . _errors_skipped )
print " \n "
return errors
def _load_url ( self , url ) :
html_str = " "
try :
fp = urllib . urlopen ( url )
for line in fp :
html_str + = line . strip ( )
fp . close ( )
except IOError , e :
print " error loading url: " + e . strerror
pass
# currently ERROR codes are rendered as table
# locate table chunk with ERROR_SUCCESS
html = [ x for x in html_str . split ( ' <table ' ) if " ERROR_SUCCESS " in x ]
html = ' <table ' + html [ 0 ]
pos = html . find ( ' </table> ' )
if pos == - 1 :
return ' ' ;
html = html [ : pos ] + ' </table> '
# html clean up
html = re . sub ( r ' <a[^>]*>(.*?)</a> ' , r ' \ 1 ' , html )
return html
class WerrorGenerator ( object ) :
"""
provides methods to generate parts of werror . h and doserr . c files
"""
FNAME_WERRORS = ' w32errors.lst '
FNAME_WERROR_DEFS = ' werror_defs.h '
FNAME_DOSERR_DEFS = ' doserr_defs.c '
FNAME_DOSERR_DESC = ' doserr_desc.c '
def __init__ ( self , opt ) :
self . opt = opt
self . _out_dir = opt . out_dir
pass
def _open_out_file ( self , fname ) :
fname = os . path . join ( self . _out_dir , fname )
return open ( fname , " w " )
def _gen_werrors_list ( self , errors ) :
""" uses ' errors ' dictionary to display list of Win32 Errors """
fp = self . _open_out_file ( self . FNAME_WERRORS )
for err_code in sorted ( errors . keys ( ) ) :
err_name = errors [ err_code ] [ ' err_name ' ]
fp . write ( err_name )
fp . write ( " \n " )
fp . close ( )
def _gen_werror_defs ( self , errors ) :
""" uses ' errors ' dictionary to generate werror.h file """
fp = self . _open_out_file ( self . FNAME_WERROR_DEFS )
for err_code in sorted ( errors . keys ( ) ) :
err_name = errors [ err_code ] [ ' err_name ' ]
err_hex = errors [ err_code ] [ ' err_hex ' ]
fp . write ( ' #define %s \t W_ERROR( %s ) ' % ( err_name , err_hex ) )
fp . write ( " \n " )
fp . close ( )
def _gen_doserr_defs ( self , errors ) :
""" uses ' errors ' dictionary to generate defines in doserr.c file """
fp = self . _open_out_file ( self . FNAME_DOSERR_DEFS )
for err_code in sorted ( errors . keys ( ) ) :
err_name = errors [ err_code ] [ ' err_name ' ]
fp . write ( ' \t { " %s " , %s }, ' % ( err_name , err_name ) )
fp . write ( " \n " )
fp . close ( )
def _gen_doserr_descriptions ( self , errors ) :
""" uses ' errors ' dictionary to generate descriptions in doserr.c file """
fp = self . _open_out_file ( self . FNAME_DOSERR_DESC )
for err_code in sorted ( errors . keys ( ) ) :
err_name = errors [ err_code ] [ ' err_name ' ]
fp . write ( ' \t { %s , " %s " }, ' % ( err_name , errors [ err_code ] [ ' err_desc ' ] ) )
fp . write ( " \n " )
fp . close ( )
def _lookup_error_by_name ( self , err_name , defined_errors ) :
for err in defined_errors . itervalues ( ) :
if err [ ' err_name ' ] == err_name :
return err
return None
def _filter_errors ( self , errors , defined_errors ) :
"""
returns tuple ( new_erros , diff_code_errors , diff_name_errors )
new_errors - dictionary of errors not in defined_errors
diff_code_errors - list of errors found in defined_errors
but with different value
diff_name_errors - list of errors found with same code in
defined_errors , but with different name
Most critical is diff_code_errors list to be empty !
"""
new_errors = { }
diff_code_errors = [ ]
diff_name_errors = [ ]
for err_def in errors . itervalues ( ) :
add_error = True
# try get defined error by code
if defined_errors . has_key ( err_def [ ' code ' ] ) :
old_err = defined_errors [ err_def [ ' code ' ] ]
if err_def [ ' err_name ' ] != old_err [ ' err_name ' ] :
warning = { ' msg ' : ' New and Old errors has different error names ' ,
' err_new ' : err_def ,
' err_old ' : old_err }
diff_name_errors . append ( warning )
# sanity check for errors with same name but different values
old_err = self . _lookup_error_by_name ( err_def [ ' err_name ' ] , defined_errors )
if old_err :
if err_def [ ' code ' ] != old_err [ ' code ' ] :
warning = { ' msg ' : ' New and Old error defs has different error value ' ,
' err_new ' : err_def ,
' err_old ' : old_err }
diff_code_errors . append ( warning )
# exclude error already defined with same name
add_error = False
# do add the error in new_errors if everything is fine
if add_error :
new_errors [ err_def [ ' code ' ] ] = err_def
pass
return ( new_errors , diff_code_errors , diff_name_errors )
def generate ( self , errors ) :
# load already defined error codes
werr_parser = WerrorParser ( self . opt )
( defined_errors ,
no_value_errors ) = werr_parser . load_err_codes ( self . opt . werror_file )
if not defined_errors :
print " \n Unable to load existing errors file: %s " % self . opt . werror_file
sys . exit ( 1 )
if self . opt . verbose and len ( no_value_errors ) :
print " \n Warning: there are errors defines using macro value: "
pprint . pprint ( no_value_errors )
print " "
# filter generated error codes
( new_errors ,
diff_code_errors ,
diff_name_errors ) = self . _filter_errors ( errors , defined_errors )
if diff_code_errors :
print ( " \n Found %d errors with same names but different error values! Aborting. "
% len ( diff_code_errors ) )
pprint . pprint ( diff_code_errors )
sys . exit ( 2 )
if diff_name_errors :
print ( " \n Found %d errors with same values but different names (should be normal) "
% len ( diff_name_errors ) )
pprint . pprint ( diff_name_errors )
# finally generate output files
self . _gen_werror_defs ( new_errors )
self . _gen_doserr_defs ( new_errors )
self . _gen_werrors_list ( errors )
self . _gen_doserr_descriptions ( errors )
pass
class WerrorParser ( object ) :
"""
Parses errors defined in werror . h file
"""
def __init__ ( self , opt ) :
self . opt = opt
pass
def _parse_werror_line ( self , line ) :
m = re . match ( ' #define[ \t ]*(.*?)[ \t ]*W_ERROR \ ((.*?) \ ) ' , line )
if not m or ( len ( m . groups ( ) ) != 2 ) :
return None
if len ( m . group ( 1 ) ) == 0 :
return None
if str ( m . group ( 2 ) ) . startswith ( ' 0x ' ) :
err_code = int ( m . group ( 2 ) , 16 )
elif m . group ( 2 ) . isdigit ( ) :
err_code = int ( m . group ( 2 ) )
else :
self . err_no_values . append ( line )
return None
return { ' err_name ' : str ( m . group ( 1 ) ) ,
' err_hex ' : " 0x %08X " % err_code ,
' code ' : err_code }
pass
def load_err_codes ( self , fname ) :
"""
Returns tuple of :
dictionary of " hex_err_code " = > { code , name }
" hex_err_code " is string
" code " is int value for the error
list of errors that was ignored for some reason
"""
# reset internal variables
self . err_no_values = [ ]
err_codes = { }
fp = open ( fname )
for line in fp . readlines ( ) :
err_def = self . _parse_werror_line ( line )
if err_def :
err_codes [ err_def [ ' code ' ] ] = err_def
fp . close ( ) ;
return ( err_codes , self . err_no_values )
def _generate_files ( opt ) :
parser = WerrorHtmlParser ( opt )
errors = parser . parse_url ( opt . url )
out = WerrorGenerator ( opt )
out . generate ( errors )
pass
if __name__ == ' __main__ ' :
_cur_dir = os . path . abspath ( os . path . dirname ( __file__ ) )
opt_parser = OptionParser ( usage = " usage: % prog [options] " , version = " % prog 0.3 " )
opt_group = OptionGroup ( opt_parser , " Main options " )
opt_group . add_option ( " --url " , dest = " url " ,
default = _wspp_werror_url ,
help = " url for w32 error codes html - may be local file " )
opt_group . add_option ( " --out " , dest = " out_dir " ,
default = _cur_dir ,
help = " output dir for generated files " )
opt_group . add_option ( " --werror " , dest = " werror_file " ,
default = os . path . join ( _cur_dir , ' werror.h ' ) ,
help = " path to werror.h file " )
opt_group . add_option ( " --print_skipped " ,
action = " store_true " , dest = " print_skipped " , default = False ,
help = " print errors skipped during HTML parsing " )
opt_group . add_option ( " -q " , " --quiet " ,
action = " store_false " , dest = " verbose " , default = True ,
help = " don ' t print warnings to stdout " )
opt_parser . add_option_group ( opt_group )
( options , args ) = opt_parser . parse_args ( )
# add some options to be used internally
options . err_defs_file = os . path . join ( options . out_dir , WerrorGenerator . FNAME_WERROR_DEFS )
options . dos_defs_file = os . path . join ( options . out_dir , WerrorGenerator . FNAME_DOSERR_DEFS )
options . dos_desc_file = os . path . join ( options . out_dir , WerrorGenerator . FNAME_DOSERR_DESC )
# check options
_generate_files ( options )