ICOBOL LINK KIT 3.66 17-Jul-2008 A. Introduction The ICOBOL Link Kit is for users who wish to extend their use of ICOBOL by installing user-defined (written) subroutines into the ICOBOL runtime. Under UNIX, the routines are included in a shared object (icbltn.so or (HP is .sl)) which is dynamically loaded by the runtime system executable at startup. Under Windows, the routines are included in a .DLL module (icbltn.dll) which is dynamically loaded by the runtine system executable at startup. NOTE: When starting the runtime with an audit file and the info switch, an information message will be given when an icbltn module has been loaded. In this section, the word scripts will be used to refer to Windows .BAT files and UNIX shell scripts. The ICOBOL Link Kit provides the necessary scripts, objects, libraries, and sample C code to build an the needed shared object/dll. These user-defined subroutines can then be called from COBOL programs with the CALL statement. In most cases all the files necessary for using the ICOBOL Link Kit are provided in the link_kit subdirectory. The possible exceptions: 1. In certain cases, an operating system development kit or C compiler is required to provide the needed C libraries that ICOBOL requires but which cannot be distributed with ICOBOL. 2. In certain cases, an operating system development kit or C compiler is required which will include the needed linker (ld) to build the needed shared library. Updates ------- 3.50 ---- 1. ld script was not up-to-date. 2. DG/UX 88K and AIX now support icbltn.so. B. Interface The following is the interface that the user-defined subroutine will be passed when it is called. status_t routine ( argc_t argc, argv_t argv[] ) { . . . } Where status_t Is a defined typedef argc_t Is a defined typedef argv_t Is a defined typedef structure defined with the following elements: arg_info, pic_ptr, arg_len, and arg_ptr. The header files status.h and calldefs.h, should be included in all subroutines to provide the appropriate typedef's and definitions to be used. The status_t type is used for the return value of the subroutine. This return code will be moved into the Exception Status. The return code is defined as such: A zero return value means no error, A positive return value means an error occurred, and A negative return value means the operation was ok, but a status code is being returned to provide additional information. If an error is returned (positive code), the ON EXCEPTION clause, if provided, will be executed. The status_t is reserved for the builtin to return system related Exception Status values and/or information. If you desire to return application related status values, such information should be passed via one of the arguments to the builtin subroutine. Complete definitions of status_t, macros for manipulating it, and error definitions are contained in the header file status.h. Argc holds the number of parameters passed to the subroutine. Since the ICOBOL compiler limits the number of arguments in a CALL to 32, argc, will always be between 0 and 32. Argv is a structure array that describes each argument passed. The type field, arg_info.num.type, will have a data type code (defined in CALLDEFS.H). The length field, arg_len, should always be used to prohibit accessing or destroying data outside of the area to be used. Finally arg_ptr, is a pointer into the COBOL program's space for the passed data. It is the subroutine's responsibility to be aware of the expected data type and how that data type is stored in ICOBOL. Generally we recommend that only DISPLAY type storage be passed. Remember that strings passed from COBOL to C are not null (LOW-VALUE) terminated. To get a null terminated string in your subroutine, you must allocate a character temporary, move the string into it using the length passed in and then place a null at the end of the string. Be aware that COBOL strings can have embedded LOW-VALUES (nulls) that may behave unexpectedly when accessed by C. NOTE: Immediately prior to the argv vector is a hidden entry of type argv_t which contains the name of the called routine. It is up to the subroutine to check the expected length and type of each parameter, and if they are not of the expected value the error SE_PARM_MISMATCH should be returned. If the subroutine executes correctly, it should return the value SE_NO_ERR or SUCCESS. The simplest way to add a user-defined subroutine is to start with the file bltnstub.c and add your code as needed. C. Initialization To enable a user-defined subroutine to be accessed from within the ICOBOL runtime, the user-defined subroutine must be included in a call table. The call table is an array of entries of type call_tbl_t. Each entry has 6 fields which must be initialized: 1. link field - should always be initialized to NULL. 2. name length - should always be initialized to 0. 3. minimum number of arguments to the routine 4. maximum number of arguments to the routine 5. function - the entry point for the routine 6. function name - the name by which to call the subroutine from COBOL For example: #define USER_CALL_CNT 2 call_tbl_t user_call_tbl [USER_CALL_CNT] = { { NULL, 0, 1, 1, ic_upper_bltn, "ic_upper" }, { NULL, 0, 1, 1, ic_lower_bltn, "ic_lower" }. To actually enable the routines, a call must be made to the routine named load_user_calls_. This call will inform the runtime system as to the presence of the routines. To facilitate adding user-defined subroutines the ICOBOL runtime will call the function init_user_bltn at initialization. The stub for this function is in the file bltnstub.c and a functional example is in the file bltnsamp.c. Under UNIX, the init_user_bltn function has no arguments and returns a status code. It should be coded as follows: status_t init_user_bltn (void) { /* load the call table by directly calling load_user_calls */ return (load_user_calls_ (USER_CALL_CNT, &user_call_tbl [0])); } /* init_user_bltn */ Under Windows, the init_user_bltn function has 2 arguments and returns a status code. The first argument is a pointer to the load_user_calls_ function, and the second is a pointer to the register cleanup function (to be discussed later). It should be coded as follows: /* location to preserve the pointer to the cleanup registration function provided in the init_user_bltn function */ static status_t (*user_cleanup_ptr) (void (*fcn) (void)) = NULL; status_t init_user_bltn ( status_t (*load_calls_ptr) (unsigned int call_cnt, call_tbl_t *call_tbl_ptr), status_t (*register_cleanup_ptr) (void (*fcn) (void)) ) { /* protect against invalid function pointers */ if (load_calls_ptr == NULL || register_cleanup_ptr == NULL) { return (SE_INV_OPER); } /* save the register cleanup function pointer in case any of the builtin functions want to register a cleanup function via the register_user_cleanup_ routine found below */ user_cleanup_ptr = register_cleanup_ptr; /* load the call table using the provided function pointer */ return ((*load_calls_ptr) (USER_CALL_CNT, &user_call_tbl[0])); } /* init_user_bltn */ When a COBOL program executes a CALL statement, user-defined subroutines will always be sought before looking for ICOBOL's builtin functions or a COBOL subroutines. The subroutine name is always sought by the runtime system in a case-insensitive manner. D. Termination If a user-defined routine needs to execute any cleanup procedures prior to the shutdown of the runtime system, these procedures must be registered. Registered procedures will be executed during runtime termination in the reverse order from which they are registered. Cleanup routines may nether expect arguments nor return a value. For example, void my_cleanup_routine (void) { /* perform the cleanup tasks required by the user builtin */ } /* my_cleanup_routine */ To register a cleanup function the routine register_user_cleanup_ must be called. Normally, cleanup routines should be registered after the call to load_user_calls_ which makes them known. This can be done in init_user_bltn or the first time into a function that needs subsequent cleanup. Under UNIX, the call to register_user_cleanup_ can be made directly: register_user_cleanup_ (my_cleanup_routine); Under Windows, the call must be made through the pointer provided to the init_user_bltn function: (user_cleanup_ptr) (my_cleanup_routine); E. Restrictions Within the user-defined subroutine there are certain system calls that should be avoided to prevent conflicts with ICOBOL. Some examples of these include: exit() abort() chdir() exec functions brk() and sbrk() sigset() and signal() any I/O to the current console. If you do use any signals, save the state of the applicable signal handler(s) and restore them before returning. If you do I/O to the current console you must be aware of the state of the line. Input will always be in raw mode and output will be in raw mode in most cases. When reporting problems with ICOBOL, the problem must be reproduced with no user-defined subroutines. F. Building a new runtime Under Unix, the user defined builtins are in their own shared library (icbltn.so or .sl), thus you will be rebuilding the shared library. The runtime itself does not need to be re-linked. The new icbltn.so (or .sl) must be installed in the appropriate directory to allow the runtimes to find it. The installic script can be used to install the shared object. The runtime can then be started to use the new routines. NOTE: Under DG/UX Intel, LD_LIBRARY_PATH must be set to some value to use icbltn.so. Under Windows, the user defined builtins are in their own .dll module, thus you will be rebuilding the .dll. The runtime itself does not need to be re-linked. The new icbltn.dll must be copied to the release directory and the runtime must be started to use the new one. Note that the init_user_bltn is defined to use the __stdcall interface. See bltnstub.c or bltnsamp.c for the proper declarations Microsoft Visual C was used to build the current version of ICOBOL. The included CC script should be read to see how to build the objects needed when using C. In many cases it can be used unchanged although on certain machines you may need to edit the file. The included scripts should be read to see how to link the user built object(s) with the appropriate modules to build new ICOBOL executables. In particular "ld_bltn" will build a new icbltn shared object using the sample bltnsamp. The "bltn_dll" and "bltn_so " scripts built the WIndows .dll and the UNIX shared object respectively. These scripts can be altered to build with symbols for debugging purposes. G. Sample (bltnsamp.c and bltncall.sr) The provided file, bltnsamp.c, is a simple example of how to add C subroutines into ICOBOL. The source file bltnsamp.c adds several calls into the runtime system. These are: convert_upper, convert_lower, and, under UNIX, get_error_message. The routines convert_upper and convert_lower take a string to be converted to the appropriate case, while get_error_message takes a UNIX error number as PIC 9(4) respectively and returns a string holding the message for that value. The COBOL program bltncall.sr shows the COBOL CALLs that are needed to actually use these new routines added by bltnsamp.c. Under UNIX, the script files cc.sh and ld_bltn.sh allow bltnsamp.c to be compiled and linked into a shared library (icbltn) that the runtime can load. Under Windows, cc.bat can be used for compiling bltnsamp.c and ld_bltn.bat calls bltn_dll.bat to relink icbltn.dll. End of Readme