blob: 4ad9f7fffe75216cd9c5f5911225a5cb9631e0c7 [file] [log] [blame]
#! /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())