| #! /usr/bin/python3 |
| |
| # Copyright (c) Symas Corporation |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of the Symas Corporation nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| import sys, os, getopt, re, copy |
| from pycparser import c_parser, c_generator, c_ast, parse_file |
| |
| def starify(param): |
| stars = "" |
| while( isinstance(param, c_ast.PtrDecl) ): |
| q = ' '.join(param.quals) |
| stars = '*' + ' '.join((stars, q)) |
| param = param.type |
| if( isinstance(param.type, c_ast.PtrDecl) ): |
| (stars, param) = starify(param.type) |
| if( isinstance(param, c_ast.TypeDecl) ): |
| return (stars, param) |
| return (stars, param.type) |
| |
| def linkage_str( i, name, param ) -> str: |
| if name == 'execve': |
| param.show() |
| if( isinstance(param, c_ast.EllipsisParam) ): |
| return (None, None, '...') # COBOL syntax error: no variadic UDF |
| |
| is_array = False; |
| node = param |
| |
| if( isinstance(node, c_ast.Decl) ): |
| node = node.type |
| |
| if( isinstance(node, c_ast.ArrayDecl) ): |
| is_array = True; |
| node = node.type |
| |
| (stars, node) = starify(node) |
| |
| if( isinstance(node, c_ast.TypeDecl) ): |
| level = 1 |
| item_name = '' |
| picture = '' |
| usage = '' |
| if node.declname: |
| item_name = 'Lk-' + node.declname |
| |
| if is_array: # ignore level |
| if stars: |
| usage = 'Usage POINTER' |
| output = '01 FILLER.\n 02 %s %s %s OCCURS 100' \ |
| % (item_name, picture, usage) |
| return (None, None, output) |
| |
| if( isinstance(node.type, c_ast.Struct) ): |
| stars = None |
| |
| if isinstance(node.type, c_ast.IdentifierType): |
| ctype = node.type.names[-1] |
| if ctype == 'void': |
| if not stars and not item_name: |
| return (None, None, None) |
| if ctype == 'char': |
| picture = 'X' |
| if stars[0] == '*': |
| picture = 'X ANY LENGTH' |
| if ctype == 'int' or \ |
| ctype == 'long' or \ |
| ctype == 'mode_t' or \ |
| ctype == 'off_t' or \ |
| ctype == 'size_t': |
| picture = '9(8)' |
| usage = 'Usage COMP' |
| stars = None |
| |
| output = "%02d %s" % (level, ' '.join((item_name, 'PIC ' + picture, usage))) |
| return (stars, item_name, output) |
| |
| node.show() |
| return (None, None, '???') |
| |
| def using_str( i, name, param ) -> str: |
| item_name = '' |
| if( isinstance(param, c_ast.EllipsisParam) ): |
| return '...' # COBOL syntax error: no variadic UDF |
| node = param |
| |
| if( isinstance(node, c_ast.Decl) ): |
| node = node.type |
| |
| if( isinstance(node, c_ast.ArrayDecl) ): |
| node = node.type |
| |
| (stars, node) = starify(node) |
| |
| if( isinstance(node, c_ast.TypeDecl) ): |
| item_name = '' |
| |
| if isinstance(node.type, c_ast.IdentifierType): |
| ctype = node.type.names[-1] |
| how = 'By Reference' |
| if ctype == 'int' or \ |
| ctype == 'long' or \ |
| ctype == 'mode_t' or \ |
| ctype == 'off_t' or \ |
| ctype == 'size_t': |
| how = 'By Value' |
| if node.declname: |
| item_name = '%s Lk-%s' % (how, node.declname) |
| |
| return item_name |
| |
| def parameter_str( i, name, param ) -> str: |
| if( isinstance(param, c_ast.EllipsisParam) ): |
| return '...' |
| |
| t = [0, 1, 2] # qual, type, name |
| is_array = False; |
| node = param |
| |
| if( isinstance(node, c_ast.Decl) ): |
| node = node.type |
| |
| if( isinstance(node, c_ast.ArrayDecl) ): |
| is_array = True; |
| node = node.type |
| |
| (stars, node) = starify(node) |
| |
| if( isinstance(node, c_ast.TypeDecl) ): |
| t[0] = ' '.join(node.quals) |
| item_name = '' |
| if node.declname: |
| item_name = 'Lk-' + node.declname |
| t[2] = ' '.join((stars, item_name)) |
| if( node.declname == None ): |
| t[2] = '' |
| if( isinstance(node.type, c_ast.IdentifierType) ): |
| try: |
| t[1] = ' '.join(node.type.names) |
| except: |
| print("oops: node.type of %s is %s" % (name, str(node.type))) |
| return "could not parse %s arg[%d]" % (name, i) |
| if( isinstance(node.type, c_ast.Struct) ): |
| t[0] = ' '.join(node.quals) |
| t[1] = "struct " + node.type.name |
| if( isinstance(node, c_ast.ArrayDecl) ): |
| return parameter_str(i, name, node.type) + '[]' |
| |
| try: |
| return ' '.join(t) |
| except: |
| print("oops: %s[%d]: {%s}" % (name, i, str(t)) ) |
| param.show() |
| |
| class VisitPrototypes(c_ast.NodeVisitor): |
| def __init__(self): |
| self.done = set() |
| |
| def type_of(self, node): |
| while( not isinstance(node.type, c_ast.TypeDecl) ): |
| node = node.type |
| return node.type.type.name |
| |
| def visit_Decl(self, node): |
| name = node.name |
| if name in self.done: |
| return |
| self.done.add(name) |
| |
| params = [] |
| cbl_args = [] |
| linkage_items = [] |
| string_items = [] |
| returns = '???' |
| |
| if False and isinstance(node.type, c_ast.FuncDecl): |
| function_decl = node.type |
| print('Function: %s' % node.name) |
| if( node.type.args == None ): |
| print(' (no arguments)') |
| else: |
| for param_decl in node.type.args.params: |
| if( isinstance(param_decl, c_ast.EllipsisParam) ): |
| param_decl.show(offset=6) |
| continue |
| print(' Arg name: %s' % param_decl.name) |
| print(' Type:') |
| param_decl.type.show(offset=6) |
| |
| if isinstance(node.type, c_ast.FuncDecl): |
| args = node.type.args |
| if isinstance(args, c_ast.ParamList): |
| #rint("params are %s (type %s)" % (str(args.params), type(args.params))) |
| if( args == None ): |
| params.append('') |
| else: |
| for (i, param) in enumerate(args.params): |
| params.append(parameter_str(i, name, param)) |
| cbl_args.append(using_str(i, name, param)) |
| (stars, item, definition) = linkage_str(i, name, param) |
| if definition: |
| if stars: |
| string_items.append(item) |
| linkage_items.append(definition) |
| |
| (stars, rets) = starify(node.type) |
| |
| if isinstance(rets, c_ast.TypeDecl): |
| q = ' '.join(rets.quals) |
| if( isinstance(rets.type, c_ast.Struct) ): |
| t = "struct " + rets.type.name |
| else: |
| t = ' '.join(rets.type.names) |
| returns = ' '.join((q, t, stars)) |
| |
| if name == None: |
| return |
| |
| # print the C version as a comment |
| cparams = [ x.replace('Lk-', '') for x in params ] |
| print( " * %s %s(%s)" |
| % (returns, name, ', '.join(cparams)) ) |
| |
| # print the UDF |
| print( ' Identification Division.') |
| sname = name |
| if( sname[0] == '_' ): |
| sname = sname[1:] |
| print( ' Function-ID. posix-%s.' % sname) |
| |
| print( ' Data Division.') |
| print( ' Linkage Section.') |
| print( ' 77 Return-Value Binary-Long.') |
| for item in linkage_items: |
| print( ' %s.' % item.strip()) |
| args = ',\n '.join(cbl_args) |
| args = 'using\n %s\n ' % args |
| print( ' Procedure Division %s Returning Return-Value.' |
| % args ) |
| for item in string_items: |
| print( ' Inspect Backward %s ' % item + |
| 'Replacing Leading Space By Low-Value' ) |
| using_args = '' |
| if args: |
| using_args = '%s' % args |
| print( ' Call "%s" %s Returning Return-Value.' |
| % (name, using_args) ) |
| print( ' Goback.') |
| print( ' End Function posix-%s.' % sname) |
| |
| # Hard code a path to the fake includes |
| # if not using cpp(1) environment variables. |
| cpp_args = ['-I/home/jklowden/projects/3rd/pycparser/utils/fake_libc_include'] |
| |
| for var in ('CPATH', 'C_INCLUDE_PATH'): |
| dir = os.getenv(var) |
| if dir: |
| cpp_args = '' |
| |
| def process(srcfile): |
| ast = parse_file(srcfile, use_cpp=True, cpp_args=cpp_args) |
| # print(c_generator.CGenerator().visit(ast)) |
| v = VisitPrototypes() |
| v.visit(ast) |
| |
| __doc__ = """ |
| SYNOPSIS |
| udf-gen [-I include-path] [header-file ...] |
| |
| DESCRIPTION |
| For each C function declared in header-file, |
| produce an ISO COBOL user-defined function definition to call it. |
| If no filename is supplied, declarations are read from standard input. |
| All output is written to standard output. |
| |
| This Python script uses the PLY pycparser module, |
| (http://www.dabeaz.com/ply/), which supplies a set of simplified "fake |
| header files" to avoid parsing the (very complex) standard C header |
| files. These alost suffice for parsing the Posix function |
| declarations in Section 2 of the manual. |
| |
| Use the -I option or the cpp(1) environment variables to direct |
| the preprocessor to use the fake header files instead of the system |
| header files. |
| |
| LIMITATIONS |
| udf-gen does not recognize C struct parameters, such as used by stat(2). |
| |
| No attempt has been made to define "magic" values, such as would |
| be needed for example by chmod(2). |
| """ |
| |
| def main( argv=None ): |
| global cpp_args |
| if argv is None: |
| argv = sys.argv |
| # parse command line options |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], "D:hI:m:", ["help"]) |
| except getopt.error as msg: |
| print(msg) |
| print("for help use --help") |
| sys.exit(2) |
| |
| # process options |
| astfile = None |
| |
| for opt, arg in opts: |
| if opt in ("-h", "--help"): |
| print(__doc__) |
| sys.exit(0) |
| if opt == '-D': |
| cpp_args.append('-D%s ' % arg) |
| if opt == '-I': |
| cpp_args[0] = '-I' + arg |
| |
| # process arguments |
| if not args: |
| args = ('/dev/stdin',) |
| |
| for arg in args: |
| process(arg) |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |