home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 18 REXX
/
18-REXX.zip
/
123rexx.zip
/
REXXLINK.C
< prev
next >
Wrap
Text File
|
1993-06-16
|
74KB
|
1,285 lines
/*****************************************************************************/
/*****************************************************************************/
/* */
/* Copyright (c) 1991, 1992 Lotus Development Corporation */
/* */
/* 1-2-3 for OS/2 REXX @function and macro command support. Part of the */
/* Lotus Techpack. */
/* */
/* Module: rexxlink.c */
/* Description: C source file for rexxlink.dll */
/* For usage examles, see REXXLINK.WG2. */
/* */
/* */
/* For @functions the syntax is: */
/* */
/* @rexx("rexx_procedure [log_file]",[arg...]) */
/* @rexxd("rexx_procedure [log_file]",[arg...]) */
/* */
/* For macro commands the syntax is: */
/* */
/* {rexxv "rexx_procedure [log_file]", value [, value]} */
/* {rexxr "rexx_procedure [log_file]", range [, range]} */
/* {rexxrv "rexx_procedure [log_file]", range [, value]} */
/* */
/* where: */
/* */
/* rexx_procedure is the name and extension of the REXX procedure to be */
/* be invoked. The name must be in the search PATH or be */
/* preceded by a fully-qualified path. Any extension (not */
/* just CMD) is valid. */
/* */
/* log_file is the (optional path), name and extension of a file to */
/* which the output of REXX SAY and TRACE instructions */
/* will be written, to facilitate debugging. */
/* */
/* arg is an argument to be passed to the REXX procedure. Up */
/* to 20 arguments may be supplied, separated by commas. */
/* Standard 1-2-3 rules for @function and macro command */
/* arguments apply. */
/* */
/* If the argument is a range, the cells of the range are */
/* available to the REXX procedure as shared compound */
/* variables. The stem names used to hold the values of */
/* range cells are always RANGE1., RANGE2., and so on. */
/* 'stem.0' has as its value the number of cells in the */
/* range. */
/* */
/* value is a value argument to be passed to the REXX procedure. */
/* It may be a string, number or special value (NA, ERR). */
/* */
/* range is a range (not a collection) to be passed to the REXX */
/* procedure. The cells of the range are available to the */
/* REXX procedure as shared compound variables. The stem */
/* names used to hold the values of range cells are always */
/* RANGE1., RANGE2., and so on. 'stem.0' has as its value */
/* the number of cells in the range. */
/* */
/* */
/* Notes for @functions: */
/* */
/* The '@rexx' @function is called when this DLL is attached via the */
/* {LIBRARY-ATTACH} macro command, and whenever a cell containing it is */
/* recalculated. The '@rexxd' @function is always "dirty", that is, it will */
/* be called whenever a recalc is done. (@NOW is an example of an "always */
/* dirty" @function.) 1-2-3 @functions cannot modify their arguments; they */
/* can only return a value. */
/* */
/* If the return value from the REXX procedure can be interpreted as a */
/* number, it will be returned to 1-2-3 as a number, otherwise it will be */
/* returned as a string. The standard 1-2-3 string prefixes (', ", ^) can */
/* be used to control justification of the returned string. Note that if */
/* the numeric value returned does not fit into a DOUBLE (for floating point */
/* numbers) or a LONG (for integer numbers), truncation will occur without */
/* warning, possibly with significant data loss. */
/* */
/* */
/* Notes for macro commands: */
/* */
/* Macro commands must have their operands defined at the time the external */
/* library is attached. We have defined three macro commands that take a */
/* variety of operands. You could modify this code to add more to suit your */
/* needs. */
/* */
/* REXX procedures called as 1-2-3 macro commands can freely modify the */
/* values of cells in ranges that are passed as arguments. In fact, they */
/* can modify the value of any cell that is below, to the right of, or */
/* behind the top lefthand front of the range. Thus passing the range */
/* A:A1..A:A1 as a range gives the REXX procedure access to every cell in */
/* the spreadsheet. */
/* */
/* The following external functions are available to REXX procedures called */
/* from 1-2-3 as macro commands: */
/* */
/* Display123Error(string) */
/* */
/* Displays 'string' as an error message in a popup window and returns 0. */
/* */
/* Get123Cell(range, sheet, column, row) */
/* */
/* Returns the value of the cell at address 'sheet', 'column, 'row'. The */
/* address is relative to the upper lefthand front corner of 'range', */
/* which must be the name of a range that was passed to the REXX */
/* procedure as an argument (e.g. RANGE1, RANGE2, etc.). */
/* */
/* Set123Cell(range, sheet, column, row, type, value) */
/* */
/* Sets the value of the cell at address 'sheet', 'column, 'row' to */
/* 'value'. The address is relative to the upper lefthand front corner */
/* of 'range', which must be the name of a range that was passed to the */
/* REXX procedure as an argument (e.g. RANGE1, RANGE2, etc.). 'type' */
/* must be: */
/* */
/* EMPTY an empty cell. 'value' is ignored and may be omitted. */
/* ERR the special value ERR. 'value' is ignored and may be */
/* omitted. */
/* NA the special value NA. 'value' is ignored and may be */
/* omitted. */
/* NUMBER a numeric value. If the value contains a decimal point, */
/* the cell will be set as a floating point number; if the */
/* value does not contain a decimal point, the cell will be */
/* set as an integer number. Note that if the numeric value */
/* passed by the REXX procedure does not fit into a DOUBLE */
/* (for floating point numbers) or a LONG (for integer */
/* numbers), truncation will occur without warning, possibly */
/* with significant data loss. */
/* */
/* STRING a string. */
/* */
/* Returns 0 if the cell's value was set, 1 otherwise. */
/* */
/* */
/* General notes: */
/* */
/* The REXX procedure can do no console I/O (no SAY or TRACE instructions), */
/* except to the log file, described above. Thus you can freely use the */
/* REXX TRACE instruction in conjunction with the log_file argument */
/* described above, and then view the resultant log file from a text editor */
/* while 1-2-3 is still active, as the file is closed when the REXX */
/* procedure completes execution. File I/O works fine. It is useful to */
/* invoke the REXX procedure from the OS/2 prompt to have REXX check it for */
/* gross syntax errors. */
/* */
/* Disclaimers and excuses: */
/* */
/* This is EXAMPLE code. It is NOT industrial-strength, production-quality */
/* code. The code has been lightly tested. Note that this code does not: */
/* */
/* 1. Handle multi-byte character sets. */
/* */
/* 2. Handle with REXX numeric precision or string length issues. In */
/* particular the main buffer used to accumulate values to pass to */
/* REXX (rxBuffer) is never checked for overrun. In fact no checking */
/* for buffer overrun is done anywhere in the program. AND WHEN THE */
/* BUFFER IS OVERRUN WE CRASH, POSSIBLY TAKING 1-2-3 DOWN WITH US. */
/* THIS CAN LEAD TO DATA LOSS. PEOPLE MIGHT GET MAD AT YOU. OR US. */
/* */
/* 3. Most return codes from calling other functions are not checked. */
/* Many (if not most) error paths have not been tested, or in some */
/* cases, coded. */
/* */
/* For usage examples, see REXXLINK.WG2. */
/* */
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/* */
/* 1. Lotus grants you a non-exclusive royalty free right to use and modify */
/* the source code version and to reproduce and distribute the object */
/* code version of the Example Programs provided in the Techpack, */
/* provided that you: */
/* a) distribute the Example Programs only in conjunction with and as a */
/* part of your software application product which is designed as an */
/* add-in to Lotus 1-2-3, and */
/* b) do not represent your product to be a product of Lotus */
/* Development Corpration. */
/* */
/* 2. The Example Programs are provided as is, without warranty of any */
/* kind, either express or implied, including, without limitation, the */
/* implied warranties of merchantability of fitness for a particular */
/* purpose. Remember, your mileage may vary. */
/* */
/*****************************************************************************/
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/* Defines and includes. */
/*---------------------------------------------------------------------------*/
#define AND && // I find these defines make the code more readable
#define ELEMENTS(a) (sizeof(a) / sizeof(a[0]))
#define EOS '\0'
#define IS ==
#define ISNT !=
#define NOT !
#define OR ||
#define INCL_WIN
#define INCL_DOS
#include <os2.h> // this is OS/2, a REAL operating system
#define INCL_RXFUNC
#define INCL_RXSYSEXIT
#define INCL_RXSHV
#include <rexxsaa.h> // REXX programming support, in OS/2 toolkit or SDK
#include <stdio.h> // sprintf
#include <stdlib.h> // sprintf
#include <string.h> // strcpy
#include "lep.h" // 1-2-3 external macro & @function programming support
#define BLANK " "
#define LOTUS_ENV "RexxHandler"
#define MAX_RANGES 2 // maximum number of range arguments we can handle
#define REXX_ARGS 20 // maximum arguments allowed by REXX, I think
#define RXBUFFER_SIZE 20480 // size of main working buffer
#define REXX_ENTRY_POINT "RexxFunction" // external function entry point
#define REXX_FN_DISPLAY "Display123Error"
#define REXX_FN_GETCELL "Get123Cell"
#define REXX_FN_SETCELL "Set123Cell"
#define REXX_FN_SETRANGE "Set123Range"
#define REXX_AT "rexx"
#define REXX_AT_DIRTY "rexxd"
#define REXX_MAC_V "rexxv"
#define REXX_MAC_R "rexxr"
#define REXX_MAC_RV "rexxrv"
#define REXX_OOPS_1 "Error"
#define REXX_OOPS_2 "executing REXX procedure"
#define OUR_DLL "REXXLINK" // our name
#define VALUE_ERR "ERR"
#define VALUE_NA "NA"
typedef struct { // the common variables in each main entry point
// Here is the buffer that we use to store all data moving between 1-2-3 and
// REXX. Never do we check before placing data in it. If we do overrun it
// we crash 1-2-3, and the user loses whatever work was unsaved.
char rxBuffer[RXBUFFER_SIZE]; // main working buffer
PSZ rxBufPtr; // next spot in the buffer
PSZ rxProcName; // name of REXX procedure to invoke
PSZ logFname; // filename of log file
HFILE logFile; // file handle to log file
PRXSYSEXIT rxExitPtr; // pointer to REXX system exit
RXSYSEXIT rxExit[4]; // REXX system exit list
PSHVBLOCK rxVars; // ptr to SHVBLOCK chain
PSHVBLOCK aVar; // ptr to current SHVBLOCK
RXSTRING rxArg[REXX_ARGS]; // REXX arguments
LONG rxArgs; // number of arguments to the REXX procedure
RXSTRING rxRetVal; // return string from REXX
PVOID hRange[MAX_RANGES]; // range handles
CHAR rxRange[MAX_RANGES][8]; // REXX var names matching the range handles
ULONG numRange; // number of ranges in hRange
} THEWORLD, * PTHEWORLD; // end of theWorld
/*---------------------------------------------------------------------------*/
/* Subroutine forward declarations. */
/*---------------------------------------------------------------------------*/
VOID LEPC_API RexxAt(PVOID, USHORT);
LONG EXPENTRY RexxHandler(LONG, LONG, PRXSTRING);
LONG EXPENTRY RexxFunction(char *, USHORT, RXSTRING *, char *,
RXSTRING *);
VOID LEPC_API RexxMacRange(PVOID, USHORT);
VOID LEPC_API RexxMacRangeValue(PVOID, USHORT);
VOID LEPC_API RexxMacValue(PVOID, USHORT);
APIRET CallREXX(PTHEWORLD);
SHORT EnumerateRange(PTHEWORLD, PVOID, char *);
VOID GetCell(PVOID, PUSHORT, PRXSTRING);
VOID GetRangeArg(PTHEWORLD, USHORT, USHORT);
VOID GetValueArg(PTHEWORLD, USHORT, USHORT);
VOID ProcessRange(PTHEWORLD, PVOID, char *);
VOID RexxSysExit(PTHEWORLD);
VOID RxstringToCstring(char *, RXSTRING);
APIRET SetCell(PVOID, PUSHORT, RXSTRING, RXSTRING, char *);
/*---------------------------------------------------------------------------*/
/* Global variable declarations. */
/*---------------------------------------------------------------------------*/
USHORT oldBP, oldSS, oldSP; // saved registers for stack swap
PTHEWORLD globalWorld; // pointer to our "global" data
/*****************************************************************************/
/*****************************************************************************/
/* */
/* Here are our externally known entry points. */
/* */
/*****************************************************************************/
/*****************************************************************************/
#pragma handler(RexxAt)
VOID LEPC_API RexxAt
/*****************************************************************************/
/* */
/* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 @function. */
/* */
/*****************************************************************************/
(
PVOID pData, // our instance data
USHORT nArgs // number of arguments
)
{
char * p;
double number; // holds numeric args from 1-2-3 should be long???
ULONG len;
PVOID pvErr; // return ERR to 1-2-3
PVOID aRange; // range argument handle
USHORT LepRC; // return code
APIRET rc;
THEWORLD r; // common data structure
USHORT argSize; // size of an argument from 1-2-3
USHORT argType; // type of an argument from 1-2-3
USHORT nextArg; // index to r.rxArg array
USHORT i; // because FORTRAN was my first language
number = 0;
if (nArgs IS 0) { // we are expecting at least one argument, the proc name
pvErr = NULL;
LepAfReturnValue(LEPC_TYPE_ERR, 0, pvErr); // missing procedure name
return;
}
LepAfGetArgType(1, &argType, &argSize); // it better be a string
if (argType ISNT LEPC_TYPE_STRING) {
LepAfReturnValue(LEPC_TYPE_ERR, 0, pvErr); // proc name was not a string
return;
}
r.numRange = 0; // initialize count of ranges
r.rxVars = NULL;
r.rxBufPtr = r.rxBuffer; // initialize buffer pointer
LepRC = LepAfGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
r.rxProcName = strtok(r.rxBufPtr, BLANK); // get the procedure name
r.logFname = strtok(NULL, BLANK); // get the log file name
r.rxBufPtr = r.rxBufPtr + argSize; // bump buffer pointer
/*---------------------------------------------------------------------------*/
/* Commence processing in earnest. Loop to process the arguments for the */
/* REXX procedure from 1-2-3, building the REXX-style argument list. The */
/* three types of arguments are handled thusly: */
/* STRING Copied as a REXX argument. */
/* RANGE A compound variable is created to pass the values of the range */
/* cells to REXX, in the EnumerateRange routine. */
/* REAL Converted to a string for REXX. */
/*---------------------------------------------------------------------------*/
for (nextArg = 2, i = 0; nextArg <= nArgs AND i < REXX_ARGS; nextArg++, i++) {
LepAfGetArgType(nextArg, &argType, &argSize);
switch (argType) {
case LEPC_TYPE_STRING: // the argument is a string
LepRC = LepAfGetArgValue(nextArg, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
r.rxArg[i].strptr = r.rxBufPtr;
len = (ULONG)argSize-1; // don't count NULL
r.rxArg[i].strlength = len;
r.rxBufPtr = r.rxBufPtr + argSize; // bump buffer pointer
break;
case LEPC_TYPE_RANGE: // the argument is a range
LepRC = LepAfGetArgValue(nextArg, LEPC_TYPE_RANGE, argSize, &aRange);
len = (ULONG)sprintf(r.rxBufPtr, "RANGE%d", ++r.numRange); // stem variable name
r.rxArg[i].strptr = r.rxBufPtr;
r.rxArg[i].strlength = (ULONG)len;
r.rxBufPtr = r.rxBufPtr + len + 1; // bump pointer
EnumerateRange(&r, aRange, r.rxArg[i].strptr);
break;
case LEPC_TYPE_TREAL: // the argument is a number
LepRC = LepAfGetArgValue(nextArg, LEPC_TYPE_DOUBLE, argSize, &number);
len = strlen(_gcvt(number, 15, r.rxBufPtr));
r.rxArg[i].strptr = r.rxBufPtr;
r.rxArg[i].strlength = len;
r.rxBufPtr = r.rxBufPtr + len + 1; // bump pointer
break;
default: // some other type
len = 0;
} // of switch
if (LepRC ISNT 0 || len IS 0)
{
LepAfReturnValue(LEPC_TYPE_ERR, 0, pvErr); //Bail out
nextArg = nArgs;
LepRC = 1; //Flag error
}
} // of loop
/*---------------------------------------------------------------------------*/
/* At last we invoke the REXX interpreter, and return the result to 1-2-3. */
/* If the result can be interpreted as a number, it is returned as such. */
/*---------------------------------------------------------------------------*/
if (LepRC == 0) {
r.rxArgs = (LONG) i;
RexxSysExit(&r); // set up a REXX system exit
rc = CallREXX(&r); // invoke the REXX interpreter
}
if (rc == 0 && r.rxRetVal.strptr != NULL) { // return the result from the REXX procedure to 1-2-3
memcpy(r.rxBufPtr, r.rxRetVal.strptr, (size_t) r.rxRetVal.strlength);
r.rxBufPtr[r.rxRetVal.strlength] = EOS; // append a NULL for sscanf
// p = r.rxBufPtr + r.rxRetVal.strlength + 1;
if (number = atof(r.rxBufPtr)) // is it a number ?
LepAfReturnValue(LEPC_TYPE_DOUBLE, sizeof(number), &number); // yes
else // it's not a number, return it as a string
LepAfReturnValue(LEPC_TYPE_STRING,
(USHORT)(1 + r.rxRetVal.strlength), r.rxBufPtr); // return it to 1-2-3
DosFreeMem(r.rxRetVal.strptr);
r.rxRetVal.strlength = 0;
}
else LepAfReturnValue(LEPC_TYPE_ERR, 0, NULL); // error executing REXX proc
return; // th-that's all, folks
} // of RexxAt
#pragma handler(RexxHandler)
LONG EXPENTRY RexxHandler
/*****************************************************************************/
/* */
/* REXX system exit handler. We are entered here just before the REXX */
/* procedure we invoked is about to be interpreted. We share the variables */
/* that contain the values of the cells in the range(s) passed as arguments */
/* to the REXX procedure. */
/* */
/* We are also entered here whenever the REXX procedure performs any I/O, */
/* and we trap all console output, redirecting it to the log file, if there */
/* is one, or to the bit bucket if there is not. */
/* */
/*****************************************************************************/
(
LONG func, // exit function
LONG subFunc, // exit sub-function
PRXSTRING parm // control block
)
{
PTHEWORLD r; // "global" data
LONG rc;
ULONG w;
r = globalWorld; // get our global data
switch (func) {
case RXINI: // initialization processing
rc = (LONG)RexxVariablePool(r->rxVars);// share the vars that have the range cells values
rc = 0;
return 0;
case RXSIO: // handle I/O from REXX
switch (subFunc) {
case RXSIOSAY: // output from the SAY instruction
case RXSIOTRC: // output from the TRACE instruction
if (r->logFile ISNT 0) { // are we writing a log file?
rc = (LONG)DosWrite(r->logFile, parm->strptr, parm->strlength, &w);
rc = (LONG)DosWrite(r->logFile, "\r\n", 2, &w); // add a cr and lf
}
rc = 0; // tell REXX we handled it even if no log file active
break;
default:
rc = 1; // we aren't handling these functions
break;
} // of sub function code switch
break;
default:
rc = 1; // we aren't handling these functions
} // of function code switch
return rc;
} // of RexxHandler
#pragma handler(RexxFunction)
LONG EXPENTRY RexxFunction
/*****************************************************************************/
/* */
/* Handle the external REXX functions we provide: */
/* */
/* Display123Error(string) */
/* Get123Cell(range, sheet, column, row) */
/* Set123Cell(range, sheet, column, row, type, value) */
/* Set123Range(range, sheet, column, row, type, value [, type, value]...) */
/* */
/*****************************************************************************/
(
char * functionName, // name of the function
USHORT argc, // number of arguments
RXSTRING arg[], // argument array
char * queue, // name of current queue
RXSTRING * result // return string here
)
{
PTHEWORLD r; // "global" data
PVOID range;
APIRET rc;
int i;
USHORT cellCoord[LEPC_COORD_DIMEN];
r = globalWorld; // get our global data
result->strlength = 0;
/*---------------------------------------------------------------------------*/
/* Display123Error(string) */
/*---------------------------------------------------------------------------*/
if (strcmpi(functionName, REXX_FN_DISPLAY) IS 0) { // display an error msg
if (argc ISNT 1) return 40; // syntax error
RxstringToCstring(result->strptr, arg[0]); // RXSTRING to C string
rc = LepMcDisplayError(result->strptr);
return 0;
}
/*---------------------------------------------------------------------------*/
/* Common parsing for the Get and Set functions. */
/*---------------------------------------------------------------------------*/
if (argc < 4) return 40; // syntax error
range = NULL;
RxstringToCstring(result->strptr, arg[0]); // get range name
for (i = 0; i < r->numRange; i++) { // validate range name argument
if (strcmpi(result->strptr, r->rxRange[i]) IS 0) { // found it?
range = r->hRange[i]; // get the handle
break;
}
}
if (range IS NULL) return 40; // invalid range name
RxstringToCstring(result->strptr, arg[1]); // get sheet number
if (NOT sscanf(result->strptr, "%u", &cellCoord[LEPC_COORD_SHEET]))
return 40; // invalid sheet number
RxstringToCstring(result->strptr, arg[2]); // get column number
if (NOT sscanf(result->strptr, "%u", &cellCoord[LEPC_COORD_COLUMN]))
return 40; // invalid column number
RxstringToCstring(result->strptr, arg[3]); // get row number
if (NOT sscanf(result->strptr, "%u", &cellCoord[LEPC_COORD_ROW]))
return 40; // invalid row number
/*---------------------------------------------------------------------------*/
/* Get123Cell(range, sheet, column, row) */
/*---------------------------------------------------------------------------*/
if (strcmpi(functionName, REXX_FN_GETCELL) IS 0) { // get a cell's value
GetCell(range, cellCoord, result); // at last we get the cell's value
return 0;
}
/*---------------------------------------------------------------------------*/
/* Set123Cell(range, sheet, column, row, type, value) */
/*---------------------------------------------------------------------------*/
if (strcmpi(functionName, REXX_FN_SETCELL) IS 0) { // set a cell's value
if (argc ISNT 6) return 40; // syntax error
return SetCell(range, cellCoord, arg[4], arg[5], result->strptr);
}
/*---------------------------------------------------------------------------*/
/* Set123Range(range, sheet, column, row, type, value [, type, value]...) */
/*---------------------------------------------------------------------------*/
if (strcmpi(functionName, REXX_FN_SETRANGE) IS 0) { // set range values
if (argc < 6) return 40; // syntax error
for (i = 5; i < argc; i = i + 2) { // loop to set the cells of the range
rc = SetCell(range, cellCoord, arg[i - 1], arg[i], result->strptr);
cellCoord[LEPC_COORD_COLUMN]++; // bump the column coordinate
}
return (LONG)rc;
}
return 40; // should never get here
} // of RexxFunction
PVOID LEPC_API RexxLibraryManager(PVOID pData, USHORT event);
HAB hab;
HMQ hmq;
#pragma handler(RexxLibraryManager)
PVOID LEPC_API RexxLibraryManager
/*****************************************************************************/
/* */
/* This routine is called by 1-2-3 in response to the {library-attach} and */
/* {library-detach} macro commands. */
/* */
/* THIS IS WHERE WE ARE FIRST ENTERED (NORMALLY). */
/* */
/*****************************************************************************/
(
PVOID pData, // our instance data
USHORT event // the event that has occurred
)
{
APIRET rc;
USHORT rexxPrototype[3];
ERRORID LEr;
switch (event) { // dispatch this message
case LEPC_MSG_ATTACH: // we are being attached: register our stuff
hab = WinInitialize(0);
hmq = WinCreateMsgQueue(hab, 0);
LEr = WinGetLastError(hab);
rc = LepAfRegisterFunction(
REXX_AT, // name of the @function
RexxAt, // routine to implement it
0, NULL, NULL); // no flags, no instance data, no help string
rc = LepAfRegisterFunction(
REXX_AT_DIRTY, // name of the @function
RexxAt, // routine to implement it
LEPC_OPT_REG_ALWAYS_DIRTY, // @function is always dirty
NULL, NULL); // no instance data, no help string
rexxPrototype[0] = LEPC_PROT_TYPE_STRING;
rexxPrototype[1] = LEPC_PROT_TYPE_VALUE;
rexxPrototype[2] = LEPC_PROT_TYPE_VALUE | LEPC_PROT_OPT_OPTIONAL;
rc = LepMcRegisterCommand(
REXX_MAC_V, // name of the macro command
RexxMacValue, // routine to implement it
0, 3, // no options, we take three arguments
rexxPrototype, // argument prototypes
NULL, NULL); // no instance data, no help string
rexxPrototype[0] = LEPC_PROT_TYPE_STRING;
rexxPrototype[1] = LEPC_PROT_TYPE_RANGE;
rexxPrototype[2] = LEPC_PROT_TYPE_RANGE | LEPC_PROT_OPT_OPTIONAL;
rc = LepMcRegisterCommand(
REXX_MAC_R, // name of the macro command
RexxMacRange, // routine to implement it
0, 3, // no options, we take three arguments
rexxPrototype, // argument prototypes
NULL, NULL); // no instance data, no help string
rexxPrototype[0] = LEPC_PROT_TYPE_STRING;
rexxPrototype[1] = LEPC_PROT_TYPE_RANGE;
rexxPrototype[2] = LEPC_PROT_TYPE_VALUE | LEPC_PROT_OPT_OPTIONAL;
rc = LepMcRegisterCommand(
REXX_MAC_RV, // name of the macro command
RexxMacRangeValue, // routine to implement it
0, 3, // no options, we take three arguments
rexxPrototype, // argument prototypes
NULL, NULL); // no instance data, no help string
rc = RexxRegisterFunctionDll(REXX_FN_DISPLAY, OUR_DLL, REXX_ENTRY_POINT);
rc = RexxRegisterFunctionDll(REXX_FN_GETCELL, OUR_DLL, REXX_ENTRY_POINT);
rc = RexxRegisterFunctionDll(REXX_FN_SETCELL, OUR_DLL, REXX_ENTRY_POINT);
rc = RexxRegisterFunctionDll(REXX_FN_SETRANGE, OUR_DLL, REXX_ENTRY_POINT);
break;
case LEPC_MSG_DETACH: // we are being detached; clean up
rc = RexxDeregisterFunction(REXX_FN_DISPLAY); // deregister functions
rc = RexxDeregisterFunction(REXX_FN_GETCELL);
rc = RexxDeregisterFunction(REXX_FN_SETCELL);
rc = RexxDeregisterFunction(REXX_FN_SETRANGE);
WinDestroyMsgQueue(hmq);
break;
default: break; // should never happen
} // of switch
return (NULL);
} // of RexxLibraryManager
#pragma handler(RexxMacRange)
VOID LEPC_API RexxMacRange
/*****************************************************************************/
/* */
/* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 macro command. */
/* */
/* {rexxr "rexx_procedure [log_file]", range [, range]} */
/* */
/*****************************************************************************/
(
PVOID pData, // our instance data
USHORT nArgs // number of arguments
)
{
THEWORLD r; // common data structure
LONG rc; // return code
LONG rexxRc; // return code from REXX procedure
USHORT argSize; // size of an argument from 1-2-3
USHORT argType; // type of an argument from 1-2-3
if (nArgs < 2) return; // we are expecting at least two arguments
LepMcGetArgType(1, &argType, &argSize); // it better be a string
if (argType ISNT LEPC_TYPE_STRING) return;
rexxRc = 0;
r.rxVars = NULL;
r.rxBufPtr = r.rxBuffer; // initialize buffer pointer
rc = LepMcGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
r.rxProcName = strtok(r.rxBufPtr, BLANK); // get the procedure name
r.logFname = strtok(NULL, BLANK); // get the log file name
r.rxBufPtr = r.rxBufPtr + argSize; // bump buffer pointer
/*---------------------------------------------------------------------------*/
/* Commence processing in earnest. Process the arguments for the REXX */
/* procedure from 1-2-3, building the REXX-style argument list. The second */
/* and optional third arguments must be ranges, and a compound variable is */
/* created for each to pass the values of the range cells to REXX, in the */
/* ProcessRange routine. */
/*---------------------------------------------------------------------------*/
r.numRange = 0;
GetRangeArg(&r, 2, 0); // process range arg as 1st arg to REXX
if (nArgs > 2) { // do we have a value argument?
GetRangeArg(&r, 3, 1); // process range arg as 2nd arg to REXX
r.rxArgs = 2;
}
else
r.rxArgs = 1;
RexxSysExit(&r); // set up a REXX system exit
/*---------------------------------------------------------------------------*/
/* At last we invoke the REXX interpreter. When control returns to us, we */
/* clean up and return to Lotus 1-2-3. Of course, we may have been */
/* re-entered through calls to our REXX external function routine. */
/*---------------------------------------------------------------------------*/
rc = CallREXX(&r); // invoke the REXX interpreter
return; // th-that's all, folks
} // of RexxMacRange
#pragma handler(RexxMacRangeValue)
VOID LEPC_API RexxMacRangeValue
/*****************************************************************************/
/* */
/* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 macro command. */
/* */
/* {rexxrv "rexx_procedure [log_file]", range [, value]} */
/* */
/*****************************************************************************/
(
PVOID pData, // our instance data
USHORT nArgs // number of arguments
)
{
THEWORLD r; // common data structure
APIRET rc; // return code
LONG rexxRc; // return code from REXX procedure
USHORT argSize; // size of an argument from 1-2-3
USHORT argType; // type of an argument from 1-2-3
if (nArgs < 2) return; // we are expecting at least two arguments
LepMcGetArgType(1, &argType, &argSize); // it better be a string
if (argType ISNT LEPC_TYPE_STRING) return;
rexxRc = 0;
r.rxVars = NULL;
r.rxBufPtr = r.rxBuffer; // initialize buffer pointer
rc = LepMcGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
r.rxProcName = strtok(r.rxBufPtr, BLANK); // get the procedure name
r.logFname = strtok(NULL, BLANK); // get the log file name
r.rxBufPtr = r.rxBufPtr + argSize; // bump buffer pointer
/*---------------------------------------------------------------------------*/
/* Commence processing in earnest. Process the arguments for the REXX */
/* procedure from 1-2-3, building the REXX-style argument list. The second */
/* argument must be a range, and a compound variable is created to pass the */
/* values of the range cells to REXX, in the ProcessRange routine. The */
/* value of the optional third argument is passed to REXX as an argument. */
/*---------------------------------------------------------------------------*/
r.numRange = 0;
GetRangeArg(&r, 2, 0); // process range arg as 1st arg to REXX
if (nArgs > 2) { // do we have a value argument?
GetValueArg(&r, 3, 1); // process value arg as 2nd arg to REXX
r.rxArgs = 2;
}
else
r.rxArgs = 1;
RexxSysExit(&r); // set up a REXX system exit
/*---------------------------------------------------------------------------*/
/* At last we invoke the REXX interpreter. When control returns to us, we */
/* clean up and return to Lotus 1-2-3. Of course, we may have been */
/* re-entered through calls to our REXX external function routine. */
/*---------------------------------------------------------------------------*/
rc = CallREXX(&r); // invoke the REXX interpreter
return; // th-that's all, folks
} // of RexxMacRangeValue
#pragma handler(RexxMacValue)
VOID LEPC_API RexxMacValue
/*****************************************************************************/
/* */
/* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 macro command. */
/* */
/* {rexxv "rexx_procedure [log_file]", value [, value]} */
/* */
/*****************************************************************************/
(
PVOID pData, // our instance data
USHORT nArgs // number of arguments
)
{
THEWORLD r; // common data structure
LONG rc; // return code
LONG rexxRc; // return code from REXX procedure
USHORT argSize; // size of an argument from 1-2-3
USHORT argType; // type of an argument from 1-2-3
if (nArgs < 2) return; // we are expecting at least two arguments
LepMcGetArgType(1, &argType, &argSize); // it better be a string
if (argType ISNT LEPC_TYPE_STRING) return;
rexxRc = 0;
r.rxVars = NULL;
r.rxBufPtr = r.rxBuffer; // initialize buffer pointer
rc = LepMcGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
r.rxProcName = strtok(r.rxBufPtr, BLANK); // get the procedure name
r.logFname = strtok(NULL, BLANK); // get the log file name
r.rxBufPtr = r.rxBufPtr + argSize; // bump buffer pointer
/*---------------------------------------------------------------------------*/
/* Commence processing in earnest. Process the arguments for the REXX */
/* procedure from 1-2-3, building the REXX-style argument list. The values */
/* of the second and optional third argument from 1-2-3 are passed to REXX */
/* as arguments. */
/*---------------------------------------------------------------------------*/
GetValueArg(&r, 2, 0); // process value arg as 1st arg to REXX
if (nArgs > 2) { // do we have a value argument?
GetValueArg(&r, 3, 1); // process value arg as 2nd arg to REXX
r.rxArgs = 2;
}
else
r.rxArgs = 1;
RexxSysExit(&r); // set up a REXX system exit
/*---------------------------------------------------------------------------*/
/* At last we invoke the REXX interpreter. When control returns to us, we */
/* clean up and return to Lotus 1-2-3. Of course, we may have been */
/* re-entered through calls to our REXX external function routine. */
/*---------------------------------------------------------------------------*/
rc = CallREXX(&r); // invoke the REXX interpreter
return; // th-that's all, folks
} // of RexxMacValue
/*****************************************************************************/
/*****************************************************************************/
/* */
/* Here are our internal subroutines. */
/* */
/*****************************************************************************/
/*****************************************************************************/
APIRET CallREXX
/*****************************************************************************/
/* */
/* Call the REXX interpreter. */
/* */
/*****************************************************************************/
(
PTHEWORLD r // "global" data
)
{
APIRET rc;
SHORT rxRc;
globalWorld = r; // save our global data
r->rxRetVal.strlength = 0;
r->rxRetVal.strptr = NULL;
rc = RexxStart( // invoke the REXX interpreter
r->rxArgs, // number of arguments
r->rxArg, // here they are
r->rxProcName, // name of the REXX procedure
0, // it's not in memory
0, // we do not set up an environment
RXCOMMAND, // call it as a command
r->rxExitPtr, // system exits we are setting up
&rxRc, // return code from REXX
&r->rxRetVal); // return value from REXX
if (rc ISNT 0) { // error trying to execute the REXX program
sprintf(r->rxBufPtr, "%s %d %s\n'%s'.", REXX_OOPS_1, rc, REXX_OOPS_2,
r->rxProcName);
rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, r->rxBufPtr, OUR_DLL, 0,
MB_OK | MB_ICONEXCLAMATION); // tell the user
}
if (r->logFile ISNT 0) rc = DosClose(r->logFile);
if (r->rxVars ISNT NULL) { // free the shvblock chain
r->aVar = r->rxVars->shvnext;
free(r->rxVars);
while (r->aVar ISNT NULL) {
r->rxVars = r->aVar;
r->aVar = r->rxVars->shvnext;
free(r->rxVars);
}
}
rc = RexxDeregisterExit(LOTUS_ENV, OUR_DLL); // we are gone
return (SHORT)rc;
} // of CallREXX
SHORT EnumerateRange
/*****************************************************************************/
/* */
/* Enumerate a range passed as an @function argument by creating a linked */
/* list of SHVBLOCK requests to share the value of the cells in the range. */
/* To do this we enumerate each of the cells of the range that was passed, */
/* and then create a SHVBLOCK to create a REXX variable for that cell. */
/* */
/*****************************************************************************/
(
PTHEWORLD r, // "global" data
PVOID thisRange, // range argument handle
char * stem
)
{
double num; // holds numeric args from 1-2-3
PVOID hrLoop; // range loop handle
RXSTRING value;
USHORT LepRC;
SHORT rc;
int len;
USHORT cell;
USHORT cellSize; // size of a range cell
USHORT cellType; // type of a range cell
cell = 0;
hrLoop = NULL;
rc = (SHORT)LepAfBeginRangeLoop(thisRange, LEPC_OPT_LOOP_EMPTY_CELLS, &hrLoop);
while (rc = (SHORT)LepAfNextLoopCell(hrLoop) IS LEPC_RET_SUCCESS) // loop thru range
{
rc = (SHORT)LepAfGetLoopType(hrLoop, &cellType, &cellSize);
if (r->rxBufPtr + cellSize - r->rxBufPtr > RXBUFFER_SIZE) { // enough room?
rc = -1; // not enough room in the buffer
break; // give up
}
switch (cellType)
{
case LEPC_TYPE_STRING: // the argument is a string
rc = (SHORT)LepAfGetLoopValue(hrLoop, LEPC_TYPE_STRING, cellSize, r->rxBufPtr);
value.strptr = r->rxBufPtr;
value.strlength = cellSize - 1; // don't count NULL
r->rxBufPtr = r->rxBufPtr + cellSize; // bump buffer pointer
break;
case LEPC_TYPE_TREAL: // the argument is a number
rc = (USHORT)LepAfGetLoopValue(hrLoop, LEPC_TYPE_DOUBLE, cellSize, &num);
len = strlen(_gcvt(num, 15, r->rxBufPtr));
value.strptr = r->rxBufPtr;
value.strlength = len;
r->rxBufPtr = r->rxBufPtr + len + 1; // bump pointer
if (r->rxBufPtr - r->rxBufPtr > RXBUFFER_SIZE) // enough room?
rc = -1; // not enough room in the buffer
break;
default: // some other type
value.strptr = NULL;
value.strlength = 0;
break;
} // of switch
cell++; // now we build a request to REXX to share this cell as a variable
if (r->rxVars IS NULL) // anchor the list
r->rxVars = r->aVar = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
else { // allocate another element in the chain
r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
r->aVar = r->aVar->shvnext;
}
memset(r->aVar, 0, sizeof(SHVBLOCK)); // zero it out
r->aVar->shvcode = RXSHV_SET;
len = sprintf(r->rxBufPtr, "%s.%d", stem, cell); // build REXX variable name
r->aVar->shvname.strptr = r->rxBufPtr;
r->aVar->shvname.strlength = len;
r->rxBufPtr = r->rxBufPtr + len + 1; // bump pointer
r->aVar->shvvalue.strptr = value.strptr;
r->aVar->shvvalue.strlength = value.strlength;
} // of loop through the cells of the range
/*---------------------------------------------------------------------------*/
/* Finally, we create 'stem.0' to hold the number of cells in this range. */
/*---------------------------------------------------------------------------*/
LepAfEndRangeLoop(hrLoop);
r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK)); // create stem.0
r->aVar = r->aVar->shvnext;
memset(r->aVar, 0, sizeof(SHVBLOCK)); // zero it out
r->aVar->shvcode = RXSHV_SET;
strcpy(r->rxBufPtr, stem);
strcat(r->rxBufPtr, ".0");
r->aVar->shvname.strptr = r->rxBufPtr;
r->aVar->shvname.strlength = strlen(r->rxBufPtr);
r->rxBufPtr = r->rxBufPtr + r->aVar->shvname.strlength + 1; // bump pointer
len = sprintf(r->rxBufPtr, "%d", cell); // number of cells
r->aVar->shvvalue.strptr = r->rxBufPtr;
r->aVar->shvvalue.strlength = len;
r->rxBufPtr = r->rxBufPtr + r->aVar->shvvalue.strlength + 1; // bump pointer
return (SHORT)len;
} // of EnumerateRange
VOID GetCell
/*****************************************************************************/
/* */
/* Get a 1-2-3 Cell and return its value as an RXSTRING. */
/* */
/*****************************************************************************/
(
PVOID range,
PUSHORT cellCoord,
PRXSTRING result
)
{
double num; // holds numeric values from 1-2-3
int len;
USHORT LepRC; // return code
USHORT cellSize; // size of a range cell
USHORT cellType; // type of a range cell
LepRC = LepMcGetCellType(range, cellCoord, &cellType, &cellSize);
switch (cellType) {
case LEPC_TYPE_STRING: // the cell is a string
LepRC = LepMcGetCellValue(range, cellCoord, LEPC_TYPE_STRING, cellSize,
result->strptr);
result->strlength = cellSize - 1; // don't count NULL
break;
case LEPC_TYPE_TREAL: // the cell is a number
LepRC = LepMcGetCellValue(range, cellCoord, LEPC_TYPE_DOUBLE, cellSize, &num);
len = strlen(_gcvt(num, 15, result->strptr));
result->strlength = len;
break;
default: // some other type
result->strlength = 0; // indicate we are returning a value of zero length
break;
} // of switch
return;
} // of GetCell
VOID GetRangeArg
/*****************************************************************************/
/* */
/* Process a range argument from 1-2-3, and create the corresponding */
/* argument in the REXX argument list. We create a compound variable to */
/* pass the values of the range cells to REXX, in the ProcessRange function. */
/* */
/*****************************************************************************/
(
PTHEWORLD r, // "global" data
USHORT argNum, // index of this arg in the arg list from 1-2-3
USHORT rxArgIndex // index for the REXX arg we will create
)
{
int len;
USHORT LepRC;
LepRC = LepMcGetArgValue(argNum, LEPC_TYPE_RANGE, sizeof(PVOID),
&r->hRange[r->numRange]);
len = sprintf(r->rxRange[r->numRange], "RANGE%d", r->numRange + 1); // stem name
r->rxArg[rxArgIndex].strptr = r->rxRange[r->numRange];
r->rxArg[rxArgIndex].strlength = (ULONG)len;
ProcessRange(r, r->hRange[r->numRange], r->rxRange[r->numRange]);
r->numRange++; // bump the count of ranges
return;
} // of GetRangeArg
VOID GetValueArg
/*****************************************************************************/
/* */
/* Process a value argument from 1-2-3, and create the corresponding */
/* argument in the REXX argument list. The two possible data types are */
/* handled thusly: */
/* STRING Copied as a REXX argument. */
/* REAL Converted to a string for REXX. */
/* */
/*****************************************************************************/
(
PTHEWORLD r, // "global" data
USHORT argNum, // index of this arg in the arg list from 1-2-3
USHORT rxArgIndex // index for the REXX arg we will create
)
{
double number; // holds numeric args from 1-2-3
int len;
USHORT argSize; // size of an argument from 1-2-3
USHORT argType; // type of an argument from 1-2-3
USHORT LepRC;
LepMcGetArgType(argNum, &argType, &argSize);
switch (argType) {
default: // some other type
case LEPC_TYPE_ERR: // the argument is the ERR special value
strcpy(r->rxBufPtr, VALUE_ERR);
goto doString;
case LEPC_TYPE_EMPTY: // the argument is the EMPTY special value
r->rxBufPtr[0] = EOS;
goto doString;
case LEPC_TYPE_NA: // the argument is the NA special value
strcpy(r->rxBufPtr, VALUE_NA);
goto doString;
case LEPC_TYPE_STRING: // the argument is a string
LepRC = LepMcGetArgValue(argNum, LEPC_TYPE_STRING, argSize, r->rxBufPtr);
doString:
r->rxArg[rxArgIndex].strptr = r->rxBufPtr;
r->rxArg[rxArgIndex].strlength = strlen(r->rxBufPtr);
r->rxBufPtr = r->rxBufPtr + strlen(r->rxBufPtr); // bump buffer pointer
break;
case LEPC_TYPE_TREAL: // the argument is a number
LepRC = LepMcGetArgValue(argNum, LEPC_TYPE_DOUBLE, argSize, &number);
len = strlen(_gcvt(number, 15, r->rxBufPtr));
r->rxArg[rxArgIndex].strptr = r->rxBufPtr;
r->rxArg[rxArgIndex].strlength = (ULONG)len;
r->rxBufPtr = r->rxBufPtr + len + 1; // bump pointer
break;
} // of switch
return;
} // of GetValueArg
VOID ProcessRange
/*****************************************************************************/
/* */
/* Process a range by creating a linked list of SHVBLOCK requests to share */
/* the value of the cells in the range. To do this we enumerate each of the */
/* cells of the range that was passed, and then create a SHVBLOCK to create */
/* a REXX variable for that cell. */
/* */
/*****************************************************************************/
(
PTHEWORLD r, // "global" data
PVOID thisRange, // range argument handle
char * stem
)
{
int len;
RXSTRING value;
USHORT LepRC;
USHORT cell;
USHORT rangeDimen[LEPC_COORD_DIMEN], cellCoord[LEPC_COORD_DIMEN];
cell = 0;
LepMcGetRangeDimen(thisRange, rangeDimen); // get the range dimensions
for (cellCoord[LEPC_COORD_SHEET] = 1; // loop through the sheets of the range
cellCoord[LEPC_COORD_SHEET] <= rangeDimen[LEPC_COORD_SHEET];
cellCoord[LEPC_COORD_SHEET]++)
for (cellCoord[LEPC_COORD_COLUMN] = 1; // loop through the cols of the range
cellCoord[LEPC_COORD_COLUMN] <= rangeDimen[LEPC_COORD_COLUMN];
cellCoord[LEPC_COORD_COLUMN]++)
for (cellCoord[LEPC_COORD_ROW] = 1; // loop through the rows of the range
cellCoord[LEPC_COORD_ROW] <= rangeDimen[LEPC_COORD_ROW];
cellCoord[LEPC_COORD_ROW]++) {
value.strptr = r->rxBufPtr;
GetCell(thisRange, cellCoord, &value);
r->rxBufPtr = r->rxBufPtr + value.strlength + 1; // bump pointer
cell++; // now we build a request to REXX to share this cell as a variable
if (r->rxVars IS NULL) // anchor the list
r->rxVars = r->aVar = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
else { // allocate another element in the chain
r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
r->aVar = r->aVar->shvnext;
}
memset(r->aVar, 0, sizeof(SHVBLOCK)); // zero it out
r->aVar->shvcode = RXSHV_SET;
len = sprintf(r->rxBufPtr, "%s.%d", stem, cell); // build REXX variable name
r->aVar->shvname.strptr = r->rxBufPtr;
r->aVar->shvname.strlength = (ULONG)len;
r->rxBufPtr = r->rxBufPtr + len + 1; // bump pointer
r->aVar->shvvalue.strptr = value.strptr;
r->aVar->shvvalue.strlength = value.strlength;
} // of loop through the cells of the range
/*---------------------------------------------------------------------------*/
/* Finally, we create 'stem.0' to hold the number of cells in and the */
/* dimensions of this range. */
/*---------------------------------------------------------------------------*/
r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK)); // create stem.0
r->aVar = r->aVar->shvnext;
memset(r->aVar, 0, sizeof(SHVBLOCK)); // zero it out
r->aVar->shvcode = RXSHV_SET;
strcpy(r->rxBufPtr, stem);
strcat(r->rxBufPtr, ".0");
r->aVar->shvname.strptr = r->rxBufPtr;
r->aVar->shvname.strlength = strlen(r->rxBufPtr);
r->rxBufPtr = r->rxBufPtr + r->aVar->shvname.strlength + 1; // bump pointer
len = sprintf(r->rxBufPtr, "%d %d %d %d", cell, rangeDimen[LEPC_COORD_SHEET],
rangeDimen[LEPC_COORD_COLUMN], rangeDimen[LEPC_COORD_ROW]);
r->aVar->shvvalue.strptr = r->rxBufPtr;
r->aVar->shvvalue.strlength = (ULONG)len;
r->rxBufPtr = r->rxBufPtr + len + 1; // bump pointer
return;
} // of ProcessRange
#define LOG_FLAG (OPEN_ACTION_CREATE_IF_NEW\
| OPEN_ACTION_REPLACE_IF_EXISTS)
#define LOG_MODE (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_NO_CACHE\
| OPEN_FLAGS_FAIL_ON_ERROR\
| OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYWRITE\
| OPEN_ACCESS_WRITEONLY)
VOID RexxSysExit
/*****************************************************************************/
/* */
/* Create a REXX system exit. We always handle REXX I/O so that the TRACE */
/* or SAY instructions do not halt the REXX procedure (which will happen if */
/* we do not catch them. If we are passing a range to REXX we set an exit */
/* for REXX initialization at which time we set up the shared variables. */
/* */
/*****************************************************************************/
(
PTHEWORLD r // "global" data
)
{
APIRET rc; // return code
ULONG a; // DosOpen info
USHORT i; // because FORTRAN was my first language
if (r->logFname ISNT NULL) // log file specified?
rc = DosOpen(r->logFname, &r->logFile, &a, 0, 0, LOG_FLAG, LOG_MODE, 0L);
else r->logFile = 0; // no, remember this
rc = RexxRegisterExitDll(LOTUS_ENV, OUR_DLL, "RexxHandler", NULL, RXEXIT_NONDROP);
i = 0;
r->rxExit[i].sysexit_name = LOTUS_ENV; // always trap I/O
r->rxExit[i++].sysexit_code = RXSIO; // handle I/O from the REXX procedure
if (r->numRange > 0) { // yes, we have some ranges
r->rxExit[i].sysexit_name = LOTUS_ENV;
r->rxExit[i++].sysexit_code = RXINI;
}
r->rxExit[i].sysexit_name = NULL;
r->rxExit[i].sysexit_code = RXENDLST;
r->rxExitPtr = r->rxExit;
return;
} // of RexxSysExit
VOID RxstringToCstring
/*****************************************************************************/
/* */
/* Copy an RXSTRING to a C (null terminated) string. */
/* */
/*****************************************************************************/
(
char * to,
RXSTRING from
)
{
USHORT temp;
temp = (USHORT) from.strlength;
memmove(to, from.strptr, temp); // should check buffer size!
to[temp] = EOS;
return;
} // of RxstringToCstring
APIRET SetCell
/*****************************************************************************/
/* */
/* Set a 1-2-3 cell to the passed type and value. */
/* */
/*****************************************************************************/
(
PVOID range, // 1-2-3 range handle
USHORT coord[], // cell coordinates
RXSTRING theType, // type for the cell
RXSTRING theValue, // value for the cell
char * workBuffer
)
{
#define CELLTYPE_EMPTY 0
#define CELLTYPE_ERR 1
#define CELLTYPE_NA 2
#define CELLTYPE_NUMBER 3
#define CELLTYPE_STRING 4
#define CELLTYPES 5
static char cType[CELLTYPES][7] = {"EMPTY", "ERR", "NA", "NUMBER", "STRING"};
double realNumber;
long integerNumber;
PVOID value;
LONG rc;
USHORT i;
USHORT cellType, cellSize;
RxstringToCstring(workBuffer, theType); // get cell type
for (i = 0; i < CELLTYPES; i++) { // validate type argument
if (strcmpi(workBuffer, cType[i]) IS 0) break; // found it?
}
switch (i) { // set up cell type
case CELLTYPE_EMPTY: // set the cell to be empty
cellType = LEPC_TYPE_EMPTY;
cellSize = 0;
break;
case CELLTYPE_ERR: // set the cell to the special value ERR
cellType = LEPC_TYPE_ERR;
cellSize = 0;
break;
case CELLTYPE_NA: // set the cell to the special value NA
cellType = LEPC_TYPE_NA;
cellSize = 0;
break;
case CELLTYPE_NUMBER: // set the cell to an integer or floating point number
RxstringToCstring(workBuffer, theValue); // get value
if (strchr(workBuffer, '.') ISNT NULL) { // there is a decimal point
cellType = LEPC_TYPE_DOUBLE; // set it as a floating point number
cellSize = sizeof(double);
if (NOT sscanf(workBuffer, "%lf", &realNumber)) return 40;
value = &realNumber;
}
else { // no decimal point
cellType = LEPC_TYPE_LONG; // set it as an integer number
cellSize = sizeof(long);
if (NOT sscanf(workBuffer, "%d", &integerNumber)) return 40;
value = &integerNumber;
}
rc = LepMcSetCellValue(range, coord, cellType, cellSize, value);
break;
case CELLTYPE_STRING:
cellType = LEPC_TYPE_STRING;
RxstringToCstring(workBuffer, theValue); // get value
cellSize = (USHORT)( theValue.strlength + 1);
value = workBuffer;
break;
default: return 40; // invalid cell type
} // of switch
rc = LepMcSetCellValue(range, coord, cellType, cellSize, value);
return 0;
} // of RxstringToCstring
/*****************************************************************************/
/*****************************************************************************/
/* */
/* Th-th-that's all, folks! */
/* */
/*****************************************************************************/
/*****************************************************************************/