home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / 123rexx.zip / REXXLINK.C < prev    next >
Text File  |  1993-06-16  |  74KB  |  1,285 lines

  1. /*****************************************************************************/
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /* Copyright (c) 1991, 1992 Lotus Development Corporation                    */
  5. /*                                                                           */
  6. /* 1-2-3 for OS/2 REXX @function and macro command support. Part of the      */
  7. /* Lotus Techpack.                                                           */
  8. /*                                                                           */
  9. /* Module:      rexxlink.c                                                   */
  10. /* Description: C source file for rexxlink.dll                               */
  11. /*              For usage examles, see REXXLINK.WG2.                         */
  12. /*                                                                           */
  13. /*                                                                           */
  14. /* For @functions the syntax is:                                             */
  15. /*                                                                           */
  16. /*  @rexx("rexx_procedure [log_file]",[arg...])                              */
  17. /*  @rexxd("rexx_procedure [log_file]",[arg...])                             */
  18. /*                                                                           */
  19. /* For macro commands the syntax is:                                         */
  20. /*                                                                           */
  21. /*  {rexxv "rexx_procedure [log_file]", value [, value]}                     */
  22. /*  {rexxr "rexx_procedure [log_file]", range [, range]}                     */
  23. /*  {rexxrv "rexx_procedure [log_file]", range [, value]}                    */
  24. /*                                                                           */
  25. /* where:                                                                    */
  26. /*                                                                           */
  27. /*  rexx_procedure is the name and extension of the REXX procedure to be     */
  28. /*                 be invoked.  The name must be in the search PATH or be    */
  29. /*                 preceded by a fully-qualified path.  Any extension (not   */
  30. /*                 just CMD) is valid.                                       */
  31. /*                                                                           */
  32. /*  log_file       is the (optional path), name and extension of a file to   */
  33. /*                 which the output of REXX SAY and TRACE instructions       */
  34. /*                 will be written, to facilitate debugging.                 */
  35. /*                                                                           */
  36. /*  arg            is an argument to be passed to the REXX procedure.  Up    */
  37. /*                 to 20 arguments may be supplied, separated by commas.     */
  38. /*                 Standard 1-2-3 rules for @function and macro command      */
  39. /*                 arguments apply.                                          */
  40. /*                                                                           */
  41. /*                 If the argument is a range, the cells of the range are    */
  42. /*                 available to the REXX procedure as shared compound        */
  43. /*                 variables.  The stem names used to hold the values of     */
  44. /*                 range cells are always RANGE1., RANGE2., and so on.       */
  45. /*                 'stem.0' has as its value the number of cells in the      */
  46. /*                 range.                                                    */
  47. /*                                                                           */
  48. /*  value          is a value argument to be passed to the REXX procedure.   */
  49. /*                 It may be a string, number or special value (NA, ERR).    */
  50. /*                                                                           */
  51. /*  range          is a range (not a collection) to be passed to the REXX    */
  52. /*                 procedure.  The cells of the range are available to the   */
  53. /*                 REXX procedure as shared compound variables.  The stem    */
  54. /*                 names used to hold the values of range cells are always   */
  55. /*                 RANGE1., RANGE2., and so on.    'stem.0' has as its value */
  56. /*                 the number of cells in the range.                         */
  57. /*                                                                           */
  58. /*                                                                           */
  59. /* Notes for @functions:                                                     */
  60. /*                                                                           */
  61. /* The '@rexx' @function is called when this DLL is attached via the         */
  62. /* {LIBRARY-ATTACH} macro command, and whenever a cell containing it is      */
  63. /* recalculated.  The '@rexxd' @function is always "dirty", that is, it will */
  64. /* be called whenever a recalc is done.  (@NOW is an example of an "always   */
  65. /* dirty" @function.)  1-2-3 @functions cannot modify their arguments; they  */
  66. /* can only return a value.                                                  */
  67. /*                                                                           */
  68. /* If the return value from the REXX procedure can be interpreted as a       */
  69. /* number, it will be returned to 1-2-3 as a number, otherwise it will be    */
  70. /* returned as a string.  The standard 1-2-3 string prefixes (', ", ^) can   */
  71. /* be used to control justification of the returned string.  Note that if    */
  72. /* the numeric value returned does not fit into a DOUBLE (for floating point */
  73. /* numbers) or a LONG (for integer numbers), truncation will occur without   */
  74. /* warning, possibly with significant data loss.                             */
  75. /*                                                                           */
  76. /*                                                                           */
  77. /* Notes for macro commands:                                                 */
  78. /*                                                                           */
  79. /* Macro commands must have their operands defined at the time the external  */
  80. /* library is attached.  We have defined three macro commands that take a    */
  81. /* variety of operands.  You could modify this code to add more to suit your */
  82. /* needs.                                                                    */
  83. /*                                                                           */
  84. /* REXX procedures called as 1-2-3 macro commands can freely modify the      */
  85. /* values of cells in ranges that are passed as arguments.  In fact, they    */
  86. /* can modify the value of any cell that is below, to the right of, or       */
  87. /* behind the top lefthand front of the range.  Thus passing the range       */
  88. /* A:A1..A:A1 as a range gives the REXX procedure access to every cell in    */
  89. /* the spreadsheet.                                                          */
  90. /*                                                                           */
  91. /* The following external functions are available to REXX procedures called  */
  92. /* from 1-2-3 as macro commands:                                             */
  93. /*                                                                           */
  94. /* Display123Error(string)                                                   */
  95. /*                                                                           */
  96. /*  Displays 'string' as an error message in a popup window and returns 0.   */
  97. /*                                                                           */
  98. /* Get123Cell(range, sheet, column, row)                                     */
  99. /*                                                                           */
  100. /*  Returns the value of the cell at address 'sheet', 'column, 'row'.  The   */
  101. /*  address is relative to the upper lefthand front corner of 'range',       */
  102. /*  which must be the name of a range that was passed to the REXX            */
  103. /*  procedure as an argument (e.g. RANGE1, RANGE2, etc.).                    */
  104. /*                                                                           */
  105. /* Set123Cell(range, sheet, column, row, type, value)                        */
  106. /*                                                                           */
  107. /*  Sets the value of the cell at address 'sheet', 'column, 'row' to         */
  108. /*  'value'.  The address is relative to the upper lefthand front corner     */
  109. /*  of 'range', which must be the name of a range that was passed to the     */
  110. /*  REXX procedure as an argument (e.g. RANGE1, RANGE2, etc.).  'type'       */
  111. /*  must be:                                                                 */
  112. /*                                                                           */
  113. /*      EMPTY   an empty cell.  'value' is ignored and may be omitted.       */
  114. /*      ERR     the special value ERR.  'value' is ignored and may be        */
  115. /*              omitted.                                                     */
  116. /*      NA      the special value NA.  'value' is ignored and may be         */
  117. /*              omitted.                                                     */
  118. /*      NUMBER  a numeric value.    If the value contains a decimal point,   */
  119. /*              the cell will be set as a floating point number; if the      */
  120. /*              value does not contain a decimal point, the cell will be     */
  121. /*              set as an integer number.   Note that if the numeric value   */
  122. /*              passed by the REXX procedure does not fit into a DOUBLE      */
  123. /*              (for floating point numbers) or a LONG (for integer          */
  124. /*              numbers), truncation will occur without warning, possibly    */
  125. /*              with significant data loss.                                  */
  126. /*                                                                           */
  127. /*      STRING  a string.                                                    */
  128. /*                                                                           */
  129. /*  Returns 0 if the cell's value was set, 1 otherwise.                      */
  130. /*                                                                           */
  131. /*                                                                           */
  132. /* General notes:                                                            */
  133. /*                                                                           */
  134. /* The REXX procedure can do no console I/O (no SAY or TRACE instructions),  */
  135. /* except to the log file, described above.  Thus you can freely use the     */
  136. /* REXX TRACE instruction in conjunction with the log_file argument          */
  137. /* described above, and then view the resultant log file from a text editor  */
  138. /* while 1-2-3 is still active, as the file is closed when the REXX          */
  139. /* procedure completes execution.  File I/O works fine. It is useful to      */
  140. /* invoke the REXX procedure from the OS/2 prompt to have REXX check it for  */
  141. /* gross syntax errors.                                                      */
  142. /*                                                                           */
  143. /* Disclaimers and excuses:                                                  */
  144. /*                                                                           */
  145. /* This is EXAMPLE code.  It is NOT industrial-strength, production-quality  */
  146. /* code.  The code has been lightly tested. Note that this code does not:    */
  147. /*                                                                           */
  148. /*  1. Handle multi-byte character sets.                                     */
  149. /*                                                                           */
  150. /*  2. Handle with REXX numeric precision or string length issues.  In       */
  151. /*     particular the main buffer used to accumulate values to pass to       */
  152. /*     REXX (rxBuffer) is never checked for overrun.  In fact no checking    */
  153. /*     for buffer overrun is done anywhere in the program.  AND WHEN THE     */
  154. /*     BUFFER IS OVERRUN WE CRASH, POSSIBLY TAKING 1-2-3 DOWN WITH US.       */
  155. /*     THIS CAN LEAD TO DATA LOSS.  PEOPLE MIGHT GET MAD AT YOU.  OR US.     */
  156. /*                                                                           */
  157. /*  3. Most return codes from calling other functions are not checked.       */
  158. /*     Many (if not most) error paths have not been tested, or in some       */
  159. /*     cases, coded.                                                         */
  160. /*                                                                           */
  161. /* For usage examples, see REXXLINK.WG2.                                     */
  162. /*                                                                           */
  163. /*****************************************************************************/
  164. /*****************************************************************************/
  165.  
  166. /*****************************************************************************/
  167. /*****************************************************************************/
  168. /*                                                                           */
  169. /* 1.  Lotus grants you a non-exclusive royalty free right to use and modify */
  170. /*     the source code version and to reproduce and distribute the object    */
  171. /*     code version of the Example Programs provided in the Techpack,        */
  172. /*     provided that you:                                                    */
  173. /*     a) distribute the Example Programs only in conjunction with and as a  */
  174. /*        part of your software application product which is designed as an  */
  175. /*        add-in to Lotus 1-2-3, and                                         */
  176. /*     b) do not represent your product to be a product of Lotus             */
  177. /*        Development Corpration.                                            */
  178. /*                                                                           */
  179. /* 2.  The Example Programs are provided as is, without warranty of any      */
  180. /*     kind, either express or implied, including, without limitation, the   */
  181. /*     implied warranties of merchantability of fitness for a particular     */
  182. /*     purpose.  Remember, your mileage may vary.                            */
  183. /*                                                                           */
  184. /*****************************************************************************/
  185. /*****************************************************************************/
  186.  
  187. /*---------------------------------------------------------------------------*/
  188. /* Defines and includes.                                                     */
  189. /*---------------------------------------------------------------------------*/
  190. #define AND  &&             // I find these defines make the code more readable
  191. #define ELEMENTS(a) (sizeof(a) / sizeof(a[0]))
  192. #define EOS  '\0'
  193. #define IS   ==
  194. #define ISNT !=
  195. #define NOT  !
  196. #define OR   ||
  197. #define INCL_WIN
  198. #define INCL_DOS
  199. #include <os2.h>                       // this is OS/2, a REAL operating system
  200. #define INCL_RXFUNC
  201. #define INCL_RXSYSEXIT
  202. #define INCL_RXSHV
  203. #include <rexxsaa.h>        // REXX programming support, in OS/2 toolkit or SDK
  204. #include <stdio.h>                                                   // sprintf
  205. #include <stdlib.h>                                                   // sprintf
  206. #include <string.h>                                                   // strcpy
  207. #include "lep.h"        // 1-2-3 external macro & @function programming support
  208.  
  209.  
  210. #define BLANK " "
  211. #define LOTUS_ENV "RexxHandler"
  212. #define MAX_RANGES 2         // maximum number of range arguments we can handle
  213. #define REXX_ARGS 20              // maximum arguments allowed by REXX, I think
  214. #define RXBUFFER_SIZE 20480                      // size of main working buffer
  215. #define REXX_ENTRY_POINT "RexxFunction"      // external function entry point
  216. #define REXX_FN_DISPLAY "Display123Error"
  217. #define REXX_FN_GETCELL "Get123Cell"
  218. #define REXX_FN_SETCELL "Set123Cell"
  219. #define REXX_FN_SETRANGE "Set123Range"
  220. #define REXX_AT "rexx"
  221. #define REXX_AT_DIRTY "rexxd"
  222. #define REXX_MAC_V "rexxv"
  223. #define REXX_MAC_R "rexxr"
  224. #define REXX_MAC_RV "rexxrv"
  225. #define REXX_OOPS_1 "Error"
  226. #define REXX_OOPS_2 "executing REXX procedure"
  227. #define OUR_DLL "REXXLINK"                                          // our name
  228. #define VALUE_ERR "ERR"
  229. #define VALUE_NA "NA"
  230.  
  231. typedef struct {               // the common variables in each main entry point
  232.    // Here is the buffer that we use to store all data moving between 1-2-3 and
  233.    // REXX. Never do we check before placing data in it.  If we do overrun it
  234.    // we crash 1-2-3, and the user loses whatever work was unsaved.
  235.    char rxBuffer[RXBUFFER_SIZE];                         // main working buffer
  236.    PSZ rxBufPtr;                                     // next spot in the buffer
  237.    PSZ rxProcName;                          // name of REXX procedure to invoke
  238.    PSZ logFname;                                        // filename of log file
  239.    HFILE logFile;                                    // file handle to log file
  240.    PRXSYSEXIT rxExitPtr;                         // pointer to REXX system exit
  241.    RXSYSEXIT rxExit[4];                                // REXX system exit list
  242.    PSHVBLOCK rxVars;                                   // ptr to SHVBLOCK chain
  243.    PSHVBLOCK aVar;                                   // ptr to current SHVBLOCK
  244.    RXSTRING rxArg[REXX_ARGS];                                 // REXX arguments
  245.    LONG rxArgs;                    // number of arguments to the REXX procedure
  246.    RXSTRING rxRetVal;                                // return string from REXX
  247.    PVOID hRange[MAX_RANGES];                                   // range handles
  248.    CHAR rxRange[MAX_RANGES][8];    // REXX var names matching the range handles
  249.    ULONG numRange;                               // number of ranges in hRange
  250.    } THEWORLD, * PTHEWORLD;                                  // end of theWorld
  251.  
  252. /*---------------------------------------------------------------------------*/
  253. /* Subroutine forward declarations.                                          */
  254. /*---------------------------------------------------------------------------*/
  255. VOID LEPC_API RexxAt(PVOID, USHORT);
  256. LONG   EXPENTRY      RexxHandler(LONG, LONG, PRXSTRING);
  257. LONG   EXPENTRY      RexxFunction(char *, USHORT, RXSTRING *, char *,
  258.                         RXSTRING *);
  259. VOID LEPC_API RexxMacRange(PVOID, USHORT);
  260. VOID LEPC_API RexxMacRangeValue(PVOID, USHORT);
  261. VOID LEPC_API RexxMacValue(PVOID, USHORT);
  262.  
  263. APIRET CallREXX(PTHEWORLD);
  264. SHORT EnumerateRange(PTHEWORLD, PVOID, char *);
  265. VOID   GetCell(PVOID, PUSHORT, PRXSTRING);
  266. VOID   GetRangeArg(PTHEWORLD, USHORT, USHORT);
  267. VOID   GetValueArg(PTHEWORLD, USHORT, USHORT);
  268. VOID   ProcessRange(PTHEWORLD, PVOID, char *);
  269. VOID   RexxSysExit(PTHEWORLD);
  270. VOID   RxstringToCstring(char *, RXSTRING);
  271. APIRET SetCell(PVOID, PUSHORT, RXSTRING, RXSTRING, char *);
  272.  
  273. /*---------------------------------------------------------------------------*/
  274. /* Global variable declarations.                                             */
  275. /*---------------------------------------------------------------------------*/
  276. USHORT oldBP, oldSS, oldSP;                   // saved registers for stack swap
  277. PTHEWORLD globalWorld;                          // pointer to our "global" data
  278.  
  279. /*****************************************************************************/
  280. /*****************************************************************************/
  281. /*                                                                           */
  282. /* Here are our externally known entry points.                               */
  283. /*                                                                           */
  284. /*****************************************************************************/
  285. /*****************************************************************************/
  286.  
  287. #pragma handler(RexxAt)
  288. VOID LEPC_API RexxAt
  289. /*****************************************************************************/
  290. /*                                                                           */
  291. /* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 @function.              */
  292. /*                                                                           */
  293. /*****************************************************************************/
  294. (
  295. PVOID pData,                                               // our instance data
  296. USHORT nArgs                                             // number of arguments
  297. )
  298. {
  299. char    * p;
  300. double number;               // holds numeric args from 1-2-3 should be long???
  301. ULONG  len;
  302. PVOID pvErr;                                             // return ERR to 1-2-3
  303. PVOID aRange;                                          // range argument handle
  304. USHORT LepRC;                                                         // return code
  305. APIRET rc;
  306. THEWORLD r;                                            // common data structure
  307. USHORT argSize;                               // size of an argument from 1-2-3
  308. USHORT argType;                               // type of an argument from 1-2-3
  309. USHORT nextArg;                                       // index to r.rxArg array
  310. USHORT i;                              // because FORTRAN was my first language
  311.  
  312. number = 0;
  313. if (nArgs IS 0) {      // we are expecting at least one argument, the proc name
  314.    pvErr = NULL;
  315.    LepAfReturnValue(LEPC_TYPE_ERR, 0, pvErr);         // missing procedure name
  316.    return;
  317.    }
  318. LepAfGetArgType(1, &argType, &argSize);                // it better be a string
  319. if (argType ISNT LEPC_TYPE_STRING) {
  320.    LepAfReturnValue(LEPC_TYPE_ERR, 0, pvErr);     // proc name was not a string
  321.    return;
  322.    }
  323. r.numRange = 0;                                   // initialize count of ranges
  324. r.rxVars = NULL;
  325. r.rxBufPtr = r.rxBuffer;                           // initialize buffer pointer
  326. LepRC = LepAfGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
  327. r.rxProcName = strtok(r.rxBufPtr, BLANK);             // get the procedure name
  328. r.logFname = strtok(NULL, BLANK);                      // get the log file name
  329. r.rxBufPtr = r.rxBufPtr + argSize;                       // bump buffer pointer
  330.  
  331. /*---------------------------------------------------------------------------*/
  332. /* Commence processing in earnest. Loop to process the arguments for the     */
  333. /* REXX procedure from 1-2-3, building the REXX-style argument list.  The    */
  334. /* three types of arguments are handled thusly:                              */
  335. /*  STRING  Copied as a REXX argument.                                       */
  336. /*  RANGE   A compound variable is created to pass the values of the range   */
  337. /*          cells to REXX, in the EnumerateRange routine.                    */
  338. /*  REAL    Converted to a string for REXX.                                  */
  339. /*---------------------------------------------------------------------------*/
  340. for (nextArg = 2, i = 0; nextArg <= nArgs AND i < REXX_ARGS; nextArg++, i++) {
  341.    LepAfGetArgType(nextArg, &argType, &argSize);
  342.    switch (argType) {
  343.    case LEPC_TYPE_STRING:                           // the argument is a string
  344.       LepRC = LepAfGetArgValue(nextArg, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
  345.       r.rxArg[i].strptr = r.rxBufPtr;
  346.       len = (ULONG)argSize-1;                                   // don't count NULL 
  347.       r.rxArg[i].strlength = len;
  348.       r.rxBufPtr = r.rxBufPtr + argSize;                 // bump buffer pointer
  349.       break;
  350.    case LEPC_TYPE_RANGE:                             // the argument is a range
  351.       LepRC = LepAfGetArgValue(nextArg, LEPC_TYPE_RANGE, argSize, &aRange);
  352.       len = (ULONG)sprintf(r.rxBufPtr, "RANGE%d", ++r.numRange); // stem variable name
  353.       r.rxArg[i].strptr = r.rxBufPtr;
  354.       r.rxArg[i].strlength = (ULONG)len;
  355.       r.rxBufPtr = r.rxBufPtr + len + 1;                        // bump pointer
  356.       EnumerateRange(&r, aRange, r.rxArg[i].strptr);
  357.       break;
  358.    case LEPC_TYPE_TREAL:                            // the argument is a number
  359.       LepRC = LepAfGetArgValue(nextArg, LEPC_TYPE_DOUBLE, argSize, &number);
  360.       len = strlen(_gcvt(number, 15, r.rxBufPtr));
  361.       r.rxArg[i].strptr = r.rxBufPtr;
  362.       r.rxArg[i].strlength = len;
  363.       r.rxBufPtr = r.rxBufPtr + len + 1;                        // bump pointer
  364.       break;
  365.    default:                                                  // some other type
  366.       len = 0;
  367.       }                                                            // of switch
  368.    if (LepRC ISNT 0 || len IS 0)
  369.    {
  370.       LepAfReturnValue(LEPC_TYPE_ERR, 0, pvErr);                    //Bail out
  371.       nextArg = nArgs;
  372.       LepRC = 1;                                                //Flag error
  373.    }
  374. }                                                                   // of loop
  375.  
  376. /*---------------------------------------------------------------------------*/
  377. /* At last we invoke the REXX interpreter, and return the result to 1-2-3.   */
  378. /* If the result can be interpreted as a number, it is returned as such.     */
  379. /*---------------------------------------------------------------------------*/
  380. if (LepRC == 0) {
  381.    r.rxArgs = (LONG) i;
  382.    RexxSysExit(&r);                                // set up a REXX system exit
  383.    rc = CallREXX(&r);                            // invoke the REXX interpreter
  384.    }
  385. if (rc == 0 && r.rxRetVal.strptr != NULL) { // return the result from the REXX procedure to 1-2-3
  386.    memcpy(r.rxBufPtr, r.rxRetVal.strptr, (size_t) r.rxRetVal.strlength);
  387.    r.rxBufPtr[r.rxRetVal.strlength] = EOS;          // append a NULL for sscanf
  388. // p = r.rxBufPtr + r.rxRetVal.strlength + 1;
  389.    if (number = atof(r.rxBufPtr))                           // is it a number ?
  390.       LepAfReturnValue(LEPC_TYPE_DOUBLE, sizeof(number), &number);       // yes
  391.    else                             // it's not a number, return it as a string
  392.       LepAfReturnValue(LEPC_TYPE_STRING,
  393.                        (USHORT)(1 +  r.rxRetVal.strlength), r.rxBufPtr);                                     // return it to 1-2-3
  394.    DosFreeMem(r.rxRetVal.strptr);
  395.    r.rxRetVal.strlength = 0;
  396.    }
  397. else LepAfReturnValue(LEPC_TYPE_ERR, 0, NULL);     // error executing REXX proc
  398. return;                                                 // th-that's all, folks
  399.  
  400. }                                                                  // of RexxAt
  401.  
  402. #pragma handler(RexxHandler)
  403. LONG EXPENTRY RexxHandler
  404. /*****************************************************************************/
  405. /*                                                                           */
  406. /* REXX system exit handler.  We are entered here just before the REXX       */
  407. /* procedure we invoked is about to be interpreted.  We share the variables  */
  408. /* that contain the values of the cells in the range(s) passed as arguments  */
  409. /* to the REXX procedure.                                                    */
  410. /*                                                                           */
  411. /* We are also entered here whenever the REXX procedure performs any I/O,    */
  412. /* and we trap all console output, redirecting it to the log file, if there  */
  413. /* is one, or to the bit bucket if there is not.                             */
  414. /*                                                                           */
  415. /*****************************************************************************/
  416. (
  417. LONG func,                                                     // exit function
  418. LONG subFunc,                                              // exit sub-function
  419. PRXSTRING parm                                                 // control block
  420. )
  421. {
  422. PTHEWORLD r;                                                   // "global" data
  423. LONG rc;
  424. ULONG w;
  425.  
  426. r = globalWorld;                                         // get our global data
  427. switch (func) {
  428. case RXINI:                                        // initialization processing
  429.    rc = (LONG)RexxVariablePool(r->rxVars);// share the vars that have the range cells values
  430.    rc = 0;
  431.    return 0;
  432. case RXSIO:                                             // handle I/O from REXX
  433.    switch (subFunc) {
  434.    case RXSIOSAY:                            // output from the SAY instruction
  435.    case RXSIOTRC:                          // output from the TRACE instruction
  436.       if (r->logFile ISNT 0) {                    // are we writing a log file?
  437.          rc = (LONG)DosWrite(r->logFile, parm->strptr, parm->strlength, &w);
  438.          rc = (LONG)DosWrite(r->logFile, "\r\n", 2, &w);           // add a cr and lf
  439.          }
  440.       rc = 0;             // tell REXX we handled it even if no log file active
  441.       break;
  442.    default:
  443.       rc = 1;                             // we aren't handling these functions
  444.       break;
  445.       }                                          // of sub function code switch
  446.    break;
  447. default:
  448.    rc = 1;                                // we aren't handling these functions
  449. }                                                    // of function code switch
  450. return rc;
  451. }                                                             // of RexxHandler
  452.  
  453. #pragma handler(RexxFunction)
  454. LONG EXPENTRY RexxFunction
  455. /*****************************************************************************/
  456. /*                                                                           */
  457. /* Handle the external REXX functions we provide:                            */
  458. /*                                                                           */
  459. /* Display123Error(string)                                                   */
  460. /* Get123Cell(range, sheet, column, row)                                     */
  461. /* Set123Cell(range, sheet, column, row, type, value)                        */
  462. /* Set123Range(range, sheet, column, row, type, value [, type, value]...)    */
  463. /*                                                                           */
  464. /*****************************************************************************/
  465. (
  466. char * functionName,                                    // name of the function
  467. USHORT argc,                                             // number of arguments
  468. RXSTRING arg[],                                               // argument array
  469. char * queue,                                          // name of current queue
  470. RXSTRING * result                                         // return string here
  471. )
  472. {
  473. PTHEWORLD r;                                                   // "global" data
  474. PVOID  range;
  475. APIRET rc;
  476. int     i;
  477. USHORT cellCoord[LEPC_COORD_DIMEN];
  478.  
  479. r = globalWorld;                                         // get our global data
  480. result->strlength = 0;
  481.  
  482. /*---------------------------------------------------------------------------*/
  483. /* Display123Error(string)                                                   */
  484. /*---------------------------------------------------------------------------*/
  485. if (strcmpi(functionName, REXX_FN_DISPLAY) IS 0) {    // display an error msg
  486.    if (argc ISNT 1) return 40;                                  // syntax error
  487.    RxstringToCstring(result->strptr, arg[0]);           // RXSTRING to C string
  488.    rc = LepMcDisplayError(result->strptr);
  489.    return 0;
  490.    }
  491.  
  492. /*---------------------------------------------------------------------------*/
  493. /* Common parsing for the Get and Set functions.                             */
  494. /*---------------------------------------------------------------------------*/
  495. if (argc < 4) return 40;                                        // syntax error
  496. range = NULL;
  497. RxstringToCstring(result->strptr, arg[0]);                    // get range name
  498. for (i = 0; i < r->numRange; i++) {             // validate range name argument
  499.    if (strcmpi(result->strptr, r->rxRange[i]) IS 0) {              // found it?
  500.       range = r->hRange[i];                                   // get the handle
  501.       break;
  502.       }
  503.    }
  504. if (range IS NULL) return 40;                             // invalid range name
  505. RxstringToCstring(result->strptr, arg[1]);                  // get sheet number
  506. if (NOT sscanf(result->strptr, "%u", &cellCoord[LEPC_COORD_SHEET]))
  507.    return 40;                                           // invalid sheet number
  508. RxstringToCstring(result->strptr, arg[2]);                 // get column number
  509. if (NOT sscanf(result->strptr, "%u", &cellCoord[LEPC_COORD_COLUMN]))
  510.    return 40;                                          // invalid column number
  511. RxstringToCstring(result->strptr, arg[3]);                    // get row number
  512. if (NOT sscanf(result->strptr, "%u", &cellCoord[LEPC_COORD_ROW]))
  513.    return 40;                                             // invalid row number
  514.  
  515. /*---------------------------------------------------------------------------*/
  516. /* Get123Cell(range, sheet, column, row)                                     */
  517. /*---------------------------------------------------------------------------*/
  518. if (strcmpi(functionName, REXX_FN_GETCELL) IS 0) {      // get a cell's value
  519.    GetCell(range, cellCoord, result);        // at last we get the cell's value
  520.    return 0;
  521.    }
  522.  
  523. /*---------------------------------------------------------------------------*/
  524. /* Set123Cell(range, sheet, column, row, type, value)                        */
  525. /*---------------------------------------------------------------------------*/
  526. if (strcmpi(functionName, REXX_FN_SETCELL) IS 0) {      // set a cell's value
  527.    if (argc ISNT 6) return 40;                                  // syntax error
  528.    return SetCell(range, cellCoord, arg[4], arg[5], result->strptr);
  529.    }
  530.  
  531. /*---------------------------------------------------------------------------*/
  532. /* Set123Range(range, sheet, column, row, type, value [, type, value]...)    */
  533. /*---------------------------------------------------------------------------*/
  534. if (strcmpi(functionName, REXX_FN_SETRANGE) IS 0) {       // set range values
  535.    if (argc < 6) return 40;                                     // syntax error
  536.    for (i = 5; i < argc; i = i + 2) {     // loop to set the cells of the range
  537.       rc = SetCell(range, cellCoord, arg[i - 1], arg[i], result->strptr);
  538.       cellCoord[LEPC_COORD_COLUMN]++;             // bump the column coordinate
  539.       }
  540.    return (LONG)rc;
  541.    }
  542. return 40;                                             // should never get here
  543. }                                                            // of RexxFunction
  544.  
  545. PVOID LEPC_API RexxLibraryManager(PVOID pData, USHORT event);
  546. HAB hab;
  547. HMQ hmq;
  548.  
  549.  
  550. #pragma handler(RexxLibraryManager)
  551. PVOID LEPC_API RexxLibraryManager
  552. /*****************************************************************************/
  553. /*                                                                           */
  554. /* This routine is called by 1-2-3 in response to the {library-attach} and   */
  555. /* {library-detach} macro commands.                                          */
  556. /*                                                                           */
  557. /* THIS IS WHERE WE ARE FIRST ENTERED (NORMALLY).                            */
  558. /*                                                                           */
  559. /*****************************************************************************/
  560. (
  561. PVOID pData,                                               // our instance data
  562. USHORT event                                     // the event that has occurred
  563. )
  564. {
  565. APIRET rc;
  566. USHORT rexxPrototype[3];
  567. ERRORID LEr;
  568. switch (event) {                                       // dispatch this message
  569. case LEPC_MSG_ATTACH:           // we are being attached: register our stuff
  570.     hab = WinInitialize(0);
  571.     hmq = WinCreateMsgQueue(hab, 0);
  572.     LEr = WinGetLastError(hab);
  573.     rc = LepAfRegisterFunction(
  574.        REXX_AT,                                        // name of the @function
  575.        RexxAt,                                       // routine to implement it
  576.        0, NULL, NULL);            // no flags, no instance data, no help string
  577.     rc = LepAfRegisterFunction(
  578.        REXX_AT_DIRTY,                                  // name of the @function
  579.        RexxAt,                                       // routine to implement it
  580.        LEPC_OPT_REG_ALWAYS_DIRTY,                  // @function is always dirty
  581.        NULL, NULL);                         // no instance data, no help string
  582.     rexxPrototype[0] = LEPC_PROT_TYPE_STRING;
  583.     rexxPrototype[1] = LEPC_PROT_TYPE_VALUE;
  584.     rexxPrototype[2] = LEPC_PROT_TYPE_VALUE | LEPC_PROT_OPT_OPTIONAL;
  585.     rc = LepMcRegisterCommand(
  586.        REXX_MAC_V,                                 // name of the macro command
  587.        RexxMacValue,                                 // routine to implement it
  588.        0, 3,                             // no options, we take three arguments
  589.        rexxPrototype,                                    // argument prototypes
  590.        NULL, NULL);                         // no instance data, no help string
  591.     rexxPrototype[0] = LEPC_PROT_TYPE_STRING;
  592.     rexxPrototype[1] = LEPC_PROT_TYPE_RANGE;
  593.     rexxPrototype[2] = LEPC_PROT_TYPE_RANGE | LEPC_PROT_OPT_OPTIONAL;
  594.     rc = LepMcRegisterCommand(
  595.        REXX_MAC_R,                                 // name of the macro command
  596.        RexxMacRange,                                 // routine to implement it
  597.        0, 3,                             // no options, we take three arguments
  598.        rexxPrototype,                                    // argument prototypes
  599.        NULL, NULL);                         // no instance data, no help string
  600.     rexxPrototype[0] = LEPC_PROT_TYPE_STRING;
  601.     rexxPrototype[1] = LEPC_PROT_TYPE_RANGE;
  602.     rexxPrototype[2] = LEPC_PROT_TYPE_VALUE | LEPC_PROT_OPT_OPTIONAL;
  603.     rc = LepMcRegisterCommand(
  604.        REXX_MAC_RV,                                // name of the macro command
  605.        RexxMacRangeValue,                            // routine to implement it
  606.        0, 3,                             // no options, we take three arguments
  607.        rexxPrototype,                                    // argument prototypes
  608.        NULL, NULL);                         // no instance data, no help string
  609.     rc = RexxRegisterFunctionDll(REXX_FN_DISPLAY, OUR_DLL, REXX_ENTRY_POINT);
  610.     rc = RexxRegisterFunctionDll(REXX_FN_GETCELL, OUR_DLL, REXX_ENTRY_POINT);
  611.     rc = RexxRegisterFunctionDll(REXX_FN_SETCELL, OUR_DLL, REXX_ENTRY_POINT);
  612.     rc = RexxRegisterFunctionDll(REXX_FN_SETRANGE, OUR_DLL, REXX_ENTRY_POINT);
  613.     break;
  614. case LEPC_MSG_DETACH:                        // we are being detached; clean up
  615.     rc = RexxDeregisterFunction(REXX_FN_DISPLAY);       // deregister functions
  616.     rc = RexxDeregisterFunction(REXX_FN_GETCELL);
  617.     rc = RexxDeregisterFunction(REXX_FN_SETCELL);
  618.     rc = RexxDeregisterFunction(REXX_FN_SETRANGE);
  619.     WinDestroyMsgQueue(hmq);
  620.     break;
  621. default: break;                                          // should never happen
  622. }                                                                  // of switch
  623. return (NULL);
  624.  
  625. }                                                      // of RexxLibraryManager
  626.  
  627. #pragma handler(RexxMacRange)
  628. VOID LEPC_API RexxMacRange
  629. /*****************************************************************************/
  630. /*                                                                           */
  631. /* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 macro command.          */
  632. /*                                                                           */
  633. /* {rexxr "rexx_procedure [log_file]", range [, range]}                      */
  634. /*                                                                           */
  635. /*****************************************************************************/
  636. (
  637. PVOID pData,                                               // our instance data
  638. USHORT nArgs                                             // number of arguments
  639. )
  640. {
  641. THEWORLD r;                                            // common data structure
  642. LONG rc;                                                         // return code
  643. LONG rexxRc;                                 // return code from REXX procedure
  644. USHORT argSize;                               // size of an argument from 1-2-3
  645. USHORT argType;                               // type of an argument from 1-2-3
  646.  
  647. if (nArgs < 2) return;               // we are expecting at least two arguments
  648. LepMcGetArgType(1, &argType, &argSize);                // it better be a string
  649. if (argType ISNT LEPC_TYPE_STRING) return;
  650. rexxRc = 0;
  651. r.rxVars = NULL;
  652. r.rxBufPtr = r.rxBuffer;                           // initialize buffer pointer
  653. rc = LepMcGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
  654. r.rxProcName = strtok(r.rxBufPtr, BLANK);             // get the procedure name
  655. r.logFname = strtok(NULL, BLANK);                      // get the log file name
  656. r.rxBufPtr = r.rxBufPtr + argSize;                       // bump buffer pointer
  657.  
  658. /*---------------------------------------------------------------------------*/
  659. /* Commence processing in earnest.  Process the arguments for the REXX       */
  660. /* procedure from 1-2-3, building the REXX-style argument list.  The second  */
  661. /* and optional third arguments must be ranges, and a compound variable is   */
  662. /* created for each to pass the values of the range cells to REXX, in the    */
  663. /* ProcessRange routine.                                                     */
  664. /*---------------------------------------------------------------------------*/
  665. r.numRange = 0;
  666. GetRangeArg(&r, 2, 0);                  // process range arg as 1st arg to REXX
  667. if (nArgs > 2) {                                // do we have a value argument?
  668.    GetRangeArg(&r, 3, 1);               // process range arg as 2nd arg to REXX
  669.    r.rxArgs = 2;
  670.    }
  671. else
  672.    r.rxArgs = 1;
  673. RexxSysExit(&r);                                   // set up a REXX system exit
  674.  
  675. /*---------------------------------------------------------------------------*/
  676. /* At last we invoke the REXX interpreter.  When control returns to us, we   */
  677. /* clean up and return to Lotus 1-2-3.  Of course, we may have been          */
  678. /* re-entered through calls to our REXX external function routine.           */
  679. /*---------------------------------------------------------------------------*/
  680. rc = CallREXX(&r);                               // invoke the REXX interpreter
  681. return;                                                 // th-that's all, folks
  682.  
  683. }                                                            // of RexxMacRange
  684.  
  685. #pragma handler(RexxMacRangeValue)
  686. VOID LEPC_API RexxMacRangeValue
  687. /*****************************************************************************/
  688. /*                                                                           */
  689. /* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 macro command.          */
  690. /*                                                                           */
  691. /* {rexxrv "rexx_procedure [log_file]", range [, value]}                     */
  692. /*                                                                           */
  693. /*****************************************************************************/
  694. (
  695. PVOID pData,                                               // our instance data
  696. USHORT nArgs                                             // number of arguments
  697. )
  698. {
  699. THEWORLD r;                                            // common data structure
  700. APIRET  rc;                                                         // return code
  701. LONG rexxRc;                                 // return code from REXX procedure
  702. USHORT argSize;                               // size of an argument from 1-2-3
  703. USHORT argType;                               // type of an argument from 1-2-3
  704.  
  705. if (nArgs < 2) return;               // we are expecting at least two arguments
  706. LepMcGetArgType(1, &argType, &argSize);                // it better be a string
  707. if (argType ISNT LEPC_TYPE_STRING) return;
  708. rexxRc = 0;
  709. r.rxVars = NULL;
  710. r.rxBufPtr = r.rxBuffer;                           // initialize buffer pointer
  711. rc = LepMcGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
  712. r.rxProcName = strtok(r.rxBufPtr, BLANK);             // get the procedure name
  713. r.logFname = strtok(NULL, BLANK);                      // get the log file name
  714. r.rxBufPtr = r.rxBufPtr + argSize;                       // bump buffer pointer
  715.  
  716. /*---------------------------------------------------------------------------*/
  717. /* Commence processing in earnest.  Process the arguments for the REXX       */
  718. /* procedure from 1-2-3, building the REXX-style argument list.  The second  */
  719. /* argument must be a range, and a compound variable is created to pass the  */
  720. /* values of the range cells to REXX, in the ProcessRange routine.  The      */
  721. /* value of the optional third argument is passed to REXX as an argument.    */
  722. /*---------------------------------------------------------------------------*/
  723. r.numRange = 0;
  724. GetRangeArg(&r, 2, 0);                  // process range arg as 1st arg to REXX
  725. if (nArgs > 2) {                                // do we have a value argument?
  726.    GetValueArg(&r, 3, 1);               // process value arg as 2nd arg to REXX
  727.    r.rxArgs = 2;
  728.    }
  729. else
  730.    r.rxArgs = 1;
  731. RexxSysExit(&r);                                   // set up a REXX system exit
  732.  
  733. /*---------------------------------------------------------------------------*/
  734. /* At last we invoke the REXX interpreter.  When control returns to us, we   */
  735. /* clean up and return to Lotus 1-2-3.  Of course, we may have been          */
  736. /* re-entered through calls to our REXX external function routine.           */
  737. /*---------------------------------------------------------------------------*/
  738. rc = CallREXX(&r);                               // invoke the REXX interpreter
  739. return;                                                 // th-that's all, folks
  740.  
  741. }                                                       // of RexxMacRangeValue
  742.  
  743. #pragma handler(RexxMacValue)
  744. VOID LEPC_API RexxMacValue
  745.  
  746. /*****************************************************************************/
  747. /*                                                                           */
  748. /* Invoke a REXX procedure as a Lotus 1-2-3 For OS/2 macro command.          */
  749. /*                                                                           */
  750. /* {rexxv "rexx_procedure [log_file]", value [, value]}                      */
  751. /*                                                                           */
  752. /*****************************************************************************/
  753. (
  754. PVOID pData,                                               // our instance data
  755. USHORT nArgs                                             // number of arguments
  756. )
  757. {
  758. THEWORLD r;                                            // common data structure
  759. LONG rc;                                                         // return code
  760. LONG rexxRc;                                 // return code from REXX procedure
  761. USHORT argSize;                               // size of an argument from 1-2-3
  762. USHORT argType;                               // type of an argument from 1-2-3
  763.  
  764. if (nArgs < 2) return;               // we are expecting at least two arguments
  765. LepMcGetArgType(1, &argType, &argSize);                // it better be a string
  766. if (argType ISNT LEPC_TYPE_STRING) return;
  767. rexxRc = 0;
  768. r.rxVars = NULL;
  769. r.rxBufPtr = r.rxBuffer;                           // initialize buffer pointer
  770. rc = LepMcGetArgValue(1, LEPC_TYPE_STRING, argSize, r.rxBufPtr);
  771. r.rxProcName = strtok(r.rxBufPtr, BLANK);             // get the procedure name
  772. r.logFname = strtok(NULL, BLANK);                      // get the log file name
  773. r.rxBufPtr = r.rxBufPtr + argSize;                       // bump buffer pointer
  774.  
  775. /*---------------------------------------------------------------------------*/
  776. /* Commence processing in earnest.  Process the arguments for the REXX       */
  777. /* procedure from 1-2-3, building the REXX-style argument list.  The values  */
  778. /* of the second and optional third argument from 1-2-3 are passed to REXX   */
  779. /* as arguments.                                                             */
  780. /*---------------------------------------------------------------------------*/
  781. GetValueArg(&r, 2, 0);                  // process value arg as 1st arg to REXX
  782. if (nArgs > 2) {                                // do we have a value argument?
  783.    GetValueArg(&r, 3, 1);               // process value arg as 2nd arg to REXX
  784.    r.rxArgs = 2;
  785.    }
  786. else
  787.    r.rxArgs = 1;
  788. RexxSysExit(&r);                                   // set up a REXX system exit
  789.  
  790. /*---------------------------------------------------------------------------*/
  791. /* At last we invoke the REXX interpreter.  When control returns to us, we   */
  792. /* clean up and return to Lotus 1-2-3.  Of course, we may have been          */
  793. /* re-entered through calls to our REXX external function routine.           */
  794. /*---------------------------------------------------------------------------*/
  795. rc = CallREXX(&r);                               // invoke the REXX interpreter
  796. return;                                                 // th-that's all, folks
  797.  
  798. }                                                            // of RexxMacValue
  799.  
  800. /*****************************************************************************/
  801. /*****************************************************************************/
  802. /*                                                                           */
  803. /* Here are our internal subroutines.                                        */
  804. /*                                                                           */
  805. /*****************************************************************************/
  806. /*****************************************************************************/
  807.  
  808. APIRET CallREXX
  809. /*****************************************************************************/
  810. /*                                                                           */
  811. /* Call the REXX interpreter.                                                */
  812. /*                                                                           */
  813. /*****************************************************************************/
  814. (
  815. PTHEWORLD r                                                    // "global" data
  816. )
  817. {
  818. APIRET rc;
  819. SHORT rxRc;
  820.  
  821. globalWorld = r;                                        // save our global data
  822.  
  823. r->rxRetVal.strlength = 0;
  824. r->rxRetVal.strptr    = NULL;
  825.  
  826. rc = RexxStart(                              // invoke the REXX interpreter
  827.    r->rxArgs,                                            // number of arguments
  828.    r->rxArg,                                                   // here they are
  829.    r->rxProcName,                                 // name of the REXX procedure
  830.    0,                                                     // it's not in memory
  831.    0,                                        // we do not set up an environment
  832.    RXCOMMAND,                                           // call it as a command
  833.    r->rxExitPtr,                              // system exits we are setting up
  834.    &rxRc,                                              // return code from REXX
  835.    &r->rxRetVal);                                     // return value from REXX
  836. if (rc ISNT 0) {                    // error trying to execute the REXX program
  837.    sprintf(r->rxBufPtr, "%s %d %s\n'%s'.", REXX_OOPS_1, rc, REXX_OOPS_2,
  838.       r->rxProcName);
  839.    rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, r->rxBufPtr, OUR_DLL, 0,
  840.       MB_OK | MB_ICONEXCLAMATION);                             // tell the user
  841.     }
  842. if (r->logFile ISNT 0) rc = DosClose(r->logFile);
  843. if (r->rxVars ISNT NULL) {                           // free the shvblock chain
  844.    r->aVar = r->rxVars->shvnext;
  845.    free(r->rxVars);
  846.    while (r->aVar ISNT NULL) {
  847.       r->rxVars = r->aVar;
  848.       r->aVar = r->rxVars->shvnext;
  849.       free(r->rxVars);
  850.       }
  851.    }
  852. rc = RexxDeregisterExit(LOTUS_ENV, OUR_DLL);                     // we are gone
  853. return (SHORT)rc;
  854. }                                                                // of CallREXX
  855.  
  856. SHORT EnumerateRange
  857. /*****************************************************************************/
  858. /*                                                                           */
  859. /* Enumerate a range passed as an @function argument by creating a linked    */
  860. /* list of SHVBLOCK requests to share the value of the cells in the range.   */
  861. /* To do this we enumerate each of the cells of the range that was passed,   */
  862. /* and then create a SHVBLOCK to create a REXX variable for that cell.       */
  863. /*                                                                           */
  864. /*****************************************************************************/
  865. (
  866. PTHEWORLD r,                                                   // "global" data
  867. PVOID thisRange,                                       // range argument handle
  868. char * stem
  869. )
  870. {
  871. double num;                                    // holds numeric args from 1-2-3
  872. PVOID  hrLoop;                                           // range loop handle
  873. RXSTRING value;
  874. USHORT LepRC;
  875. SHORT  rc;
  876. int    len;
  877. USHORT cell;
  878. USHORT cellSize;                                        // size of a range cell
  879. USHORT cellType;                                        // type of a range cell
  880.  
  881. cell = 0;
  882. hrLoop = NULL;
  883. rc = (SHORT)LepAfBeginRangeLoop(thisRange, LEPC_OPT_LOOP_EMPTY_CELLS, &hrLoop);
  884. while (rc = (SHORT)LepAfNextLoopCell(hrLoop) IS LEPC_RET_SUCCESS)  // loop thru range
  885. {
  886.    rc = (SHORT)LepAfGetLoopType(hrLoop, &cellType, &cellSize);
  887.    if (r->rxBufPtr + cellSize - r->rxBufPtr > RXBUFFER_SIZE) {  // enough room?
  888.       rc = -1;                                 // not enough room in the buffer
  889.       break;                                                         // give up
  890.       }
  891.    switch (cellType)
  892.    {
  893.       case LEPC_TYPE_STRING:                        // the argument is a string
  894.          rc = (SHORT)LepAfGetLoopValue(hrLoop, LEPC_TYPE_STRING, cellSize, r->rxBufPtr);
  895.          value.strptr = r->rxBufPtr;
  896.          value.strlength = cellSize - 1;                    // don't count NULL
  897.          r->rxBufPtr = r->rxBufPtr + cellSize;           // bump buffer pointer
  898.          break;
  899.       case LEPC_TYPE_TREAL:                         // the argument is a number
  900.          rc = (USHORT)LepAfGetLoopValue(hrLoop, LEPC_TYPE_DOUBLE, cellSize, &num);
  901.          len = strlen(_gcvt(num, 15, r->rxBufPtr));
  902.          value.strptr = r->rxBufPtr;
  903.          value.strlength = len;
  904.          r->rxBufPtr = r->rxBufPtr + len + 1;                   // bump pointer
  905.          if (r->rxBufPtr - r->rxBufPtr > RXBUFFER_SIZE)         // enough room?
  906.             rc = -1;                           // not enough room in the buffer
  907.          break;
  908.       default:                                               // some other type
  909.          value.strptr = NULL;
  910.          value.strlength = 0;
  911.          break;
  912.    }                                                               // of switch
  913.    cell++;   // now we build a request to REXX to share this cell as a variable
  914.    if (r->rxVars IS NULL)                                    // anchor the list
  915.       r->rxVars = r->aVar = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
  916.    else {                              // allocate another element in the chain
  917.       r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
  918.       r->aVar = r->aVar->shvnext;
  919.       }
  920.    memset(r->aVar, 0, sizeof(SHVBLOCK));                         // zero it out
  921.    r->aVar->shvcode = RXSHV_SET;
  922.    len = sprintf(r->rxBufPtr, "%s.%d", stem, cell);  // build REXX variable name
  923.    r->aVar->shvname.strptr = r->rxBufPtr;
  924.    r->aVar->shvname.strlength = len;
  925.    r->rxBufPtr = r->rxBufPtr + len + 1;                          // bump pointer
  926.    r->aVar->shvvalue.strptr = value.strptr;
  927.    r->aVar->shvvalue.strlength = value.strlength;
  928.    }                                  // of loop through the cells of the range
  929.  
  930. /*---------------------------------------------------------------------------*/
  931. /* Finally, we create 'stem.0' to hold the number of cells in this range.    */
  932. /*---------------------------------------------------------------------------*/
  933. LepAfEndRangeLoop(hrLoop);
  934. r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));       // create stem.0
  935. r->aVar = r->aVar->shvnext;
  936. memset(r->aVar, 0, sizeof(SHVBLOCK));                            // zero it out
  937. r->aVar->shvcode = RXSHV_SET;
  938. strcpy(r->rxBufPtr, stem);
  939. strcat(r->rxBufPtr, ".0");
  940. r->aVar->shvname.strptr = r->rxBufPtr;
  941. r->aVar->shvname.strlength = strlen(r->rxBufPtr);
  942. r->rxBufPtr = r->rxBufPtr + r->aVar->shvname.strlength + 1;     // bump pointer
  943. len = sprintf(r->rxBufPtr, "%d", cell);                      // number of cells
  944. r->aVar->shvvalue.strptr = r->rxBufPtr;
  945. r->aVar->shvvalue.strlength = len;
  946. r->rxBufPtr = r->rxBufPtr + r->aVar->shvvalue.strlength + 1;    // bump pointer
  947. return (SHORT)len;
  948. }                                                          // of EnumerateRange
  949.  
  950. VOID GetCell
  951. /*****************************************************************************/
  952. /*                                                                           */
  953. /* Get a 1-2-3 Cell and return its value as an RXSTRING.                     */
  954. /*                                                                           */
  955. /*****************************************************************************/
  956. (
  957. PVOID range,
  958. PUSHORT cellCoord,
  959. PRXSTRING result
  960. )
  961. {
  962. double num;                                  // holds numeric values from 1-2-3
  963. int    len;
  964. USHORT LepRC;                                                         // return code
  965. USHORT cellSize;                                        // size of a range cell
  966. USHORT cellType;                                        // type of a range cell
  967.  
  968. LepRC = LepMcGetCellType(range, cellCoord, &cellType, &cellSize);
  969. switch (cellType) {
  970. case LEPC_TYPE_STRING:                                  // the cell is a string
  971.    LepRC = LepMcGetCellValue(range, cellCoord, LEPC_TYPE_STRING, cellSize,
  972.       result->strptr);
  973.    result->strlength = cellSize - 1;                        // don't count NULL
  974.    break;
  975. case LEPC_TYPE_TREAL:                                   // the cell is a number
  976.    LepRC = LepMcGetCellValue(range, cellCoord, LEPC_TYPE_DOUBLE, cellSize, &num);
  977.  
  978.    len = strlen(_gcvt(num, 15, result->strptr));
  979.    result->strlength = len;
  980.    break;
  981. default:                                                     // some other type
  982.    result->strlength = 0;   // indicate we are returning a value of zero length
  983.    break;
  984. }                                                                  // of switch
  985. return;
  986. }                                                                 // of GetCell
  987.  
  988.  
  989. VOID GetRangeArg
  990. /*****************************************************************************/
  991. /*                                                                           */
  992. /* Process a range argument from 1-2-3, and create the corresponding         */
  993. /* argument in the REXX argument list.  We create a compound variable to     */
  994. /* pass the values of the range cells to REXX, in the ProcessRange function. */
  995. /*                                                                           */
  996. /*****************************************************************************/
  997. (
  998. PTHEWORLD r,                                                   // "global" data
  999. USHORT argNum,                  // index of this arg in the arg list from 1-2-3
  1000. USHORT rxArgIndex                      // index for the REXX arg we will create
  1001. )
  1002. {
  1003. int    len;
  1004. USHORT LepRC;
  1005.  
  1006. LepRC = LepMcGetArgValue(argNum, LEPC_TYPE_RANGE, sizeof(PVOID),
  1007.    &r->hRange[r->numRange]);
  1008. len = sprintf(r->rxRange[r->numRange], "RANGE%d", r->numRange + 1); // stem name
  1009. r->rxArg[rxArgIndex].strptr = r->rxRange[r->numRange];
  1010. r->rxArg[rxArgIndex].strlength = (ULONG)len;
  1011. ProcessRange(r, r->hRange[r->numRange], r->rxRange[r->numRange]);
  1012. r->numRange++;                                      // bump the count of ranges
  1013. return;
  1014. }                                                             // of GetRangeArg
  1015.  
  1016. VOID GetValueArg
  1017. /*****************************************************************************/
  1018. /*                                                                           */
  1019. /* Process a value argument from 1-2-3, and create the corresponding         */
  1020. /* argument in the REXX argument list.  The two possible data types are      */
  1021. /* handled thusly:                                                           */
  1022. /*  STRING  Copied as a REXX argument.                                       */
  1023. /*  REAL    Converted to a string for REXX.                                  */
  1024. /*                                                                           */
  1025. /*****************************************************************************/
  1026. (
  1027. PTHEWORLD r,                                                   // "global" data
  1028. USHORT argNum,                  // index of this arg in the arg list from 1-2-3
  1029. USHORT rxArgIndex                      // index for the REXX arg we will create
  1030. )
  1031. {
  1032. double number;                                 // holds numeric args from 1-2-3
  1033. int    len;
  1034. USHORT argSize;                               // size of an argument from 1-2-3
  1035. USHORT argType;                               // type of an argument from 1-2-3
  1036. USHORT LepRC;
  1037.  
  1038. LepMcGetArgType(argNum, &argType, &argSize);
  1039. switch (argType) {
  1040. default:                                                     // some other type
  1041. case LEPC_TYPE_ERR:                    // the argument is the ERR special value
  1042.    strcpy(r->rxBufPtr, VALUE_ERR);
  1043.    goto doString;
  1044. case LEPC_TYPE_EMPTY:                // the argument is the EMPTY special value
  1045.    r->rxBufPtr[0] = EOS;
  1046.    goto doString;
  1047. case LEPC_TYPE_NA:                      // the argument is the NA special value
  1048.    strcpy(r->rxBufPtr, VALUE_NA);
  1049.    goto doString;
  1050. case LEPC_TYPE_STRING:                              // the argument is a string
  1051.    LepRC = LepMcGetArgValue(argNum, LEPC_TYPE_STRING, argSize, r->rxBufPtr);
  1052. doString:
  1053.    r->rxArg[rxArgIndex].strptr = r->rxBufPtr;
  1054.    r->rxArg[rxArgIndex].strlength = strlen(r->rxBufPtr);
  1055.    r->rxBufPtr = r->rxBufPtr + strlen(r->rxBufPtr);      // bump buffer pointer
  1056.    break;
  1057. case LEPC_TYPE_TREAL:                               // the argument is a number
  1058.    LepRC = LepMcGetArgValue(argNum, LEPC_TYPE_DOUBLE, argSize, &number);
  1059.    len = strlen(_gcvt(number, 15, r->rxBufPtr));
  1060.    r->rxArg[rxArgIndex].strptr = r->rxBufPtr;
  1061.    r->rxArg[rxArgIndex].strlength = (ULONG)len;
  1062.    r->rxBufPtr = r->rxBufPtr + len + 1;                          // bump pointer
  1063.    break;
  1064. }                                                                  // of switch
  1065. return;
  1066. }                                                             // of GetValueArg
  1067.  
  1068. VOID ProcessRange
  1069. /*****************************************************************************/
  1070. /*                                                                           */
  1071. /* Process a range by creating a linked list of SHVBLOCK requests to share   */
  1072. /* the value of the cells in the range.  To do this we enumerate each of the */
  1073. /* cells of the range that was passed, and then create a SHVBLOCK to create  */
  1074. /* a REXX variable for that cell.                                            */
  1075. /*                                                                           */
  1076. /*****************************************************************************/
  1077. (
  1078. PTHEWORLD r,                                                   // "global" data
  1079. PVOID thisRange,                                       // range argument handle
  1080. char * stem
  1081. )
  1082. {
  1083. int   len;
  1084. RXSTRING value;
  1085. USHORT LepRC;
  1086. USHORT cell;
  1087. USHORT rangeDimen[LEPC_COORD_DIMEN], cellCoord[LEPC_COORD_DIMEN];
  1088.  
  1089. cell = 0;
  1090. LepMcGetRangeDimen(thisRange, rangeDimen);          // get the range dimensions
  1091. for (cellCoord[LEPC_COORD_SHEET] = 1;   // loop through the sheets of the range
  1092.    cellCoord[LEPC_COORD_SHEET] <= rangeDimen[LEPC_COORD_SHEET];
  1093.    cellCoord[LEPC_COORD_SHEET]++)
  1094.    for (cellCoord[LEPC_COORD_COLUMN] = 1; // loop through the cols of the range
  1095.       cellCoord[LEPC_COORD_COLUMN] <= rangeDimen[LEPC_COORD_COLUMN];
  1096.       cellCoord[LEPC_COORD_COLUMN]++)
  1097.       for (cellCoord[LEPC_COORD_ROW] = 1; // loop through the rows of the range
  1098.          cellCoord[LEPC_COORD_ROW] <= rangeDimen[LEPC_COORD_ROW];
  1099.          cellCoord[LEPC_COORD_ROW]++) {
  1100.    value.strptr = r->rxBufPtr;
  1101.    GetCell(thisRange, cellCoord, &value);
  1102.    r->rxBufPtr = r->rxBufPtr + value.strlength + 1;             // bump pointer
  1103.    cell++;   // now we build a request to REXX to share this cell as a variable
  1104.    if (r->rxVars IS NULL)                                    // anchor the list
  1105.       r->rxVars = r->aVar = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
  1106.    else {                              // allocate another element in the chain
  1107.       r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));
  1108.       r->aVar = r->aVar->shvnext;
  1109.       }
  1110.    memset(r->aVar, 0, sizeof(SHVBLOCK));                         // zero it out
  1111.    r->aVar->shvcode = RXSHV_SET;
  1112.    len = sprintf(r->rxBufPtr, "%s.%d", stem, cell);  // build REXX variable name
  1113.    r->aVar->shvname.strptr = r->rxBufPtr;
  1114.    r->aVar->shvname.strlength = (ULONG)len;
  1115.    r->rxBufPtr = r->rxBufPtr + len + 1;                          // bump pointer
  1116.    r->aVar->shvvalue.strptr = value.strptr;
  1117.    r->aVar->shvvalue.strlength = value.strlength;
  1118.    }                                  // of loop through the cells of the range
  1119.  
  1120. /*---------------------------------------------------------------------------*/
  1121. /* Finally, we create 'stem.0' to hold the number of cells in and the        */
  1122. /* dimensions of this range.                                                 */
  1123. /*---------------------------------------------------------------------------*/
  1124. r->aVar->shvnext = (PSHVBLOCK) malloc(sizeof(SHVBLOCK));       // create stem.0
  1125. r->aVar = r->aVar->shvnext;
  1126. memset(r->aVar, 0, sizeof(SHVBLOCK));                            // zero it out
  1127. r->aVar->shvcode = RXSHV_SET;
  1128. strcpy(r->rxBufPtr, stem);
  1129. strcat(r->rxBufPtr, ".0");
  1130. r->aVar->shvname.strptr = r->rxBufPtr;
  1131. r->aVar->shvname.strlength = strlen(r->rxBufPtr);
  1132. r->rxBufPtr = r->rxBufPtr + r->aVar->shvname.strlength + 1;     // bump pointer
  1133. len = sprintf(r->rxBufPtr, "%d %d %d %d", cell, rangeDimen[LEPC_COORD_SHEET],
  1134.    rangeDimen[LEPC_COORD_COLUMN], rangeDimen[LEPC_COORD_ROW]);
  1135. r->aVar->shvvalue.strptr = r->rxBufPtr;
  1136. r->aVar->shvvalue.strlength = (ULONG)len;
  1137. r->rxBufPtr = r->rxBufPtr + len + 1;                             // bump pointer
  1138. return;
  1139. }                                                            // of ProcessRange
  1140.  
  1141. #define LOG_FLAG (OPEN_ACTION_CREATE_IF_NEW\
  1142.                   | OPEN_ACTION_REPLACE_IF_EXISTS)
  1143.  
  1144. #define LOG_MODE (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_NO_CACHE\
  1145.                   | OPEN_FLAGS_FAIL_ON_ERROR\
  1146.                   | OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYWRITE\
  1147.                   | OPEN_ACCESS_WRITEONLY)
  1148. VOID RexxSysExit
  1149. /*****************************************************************************/
  1150. /*                                                                           */
  1151. /* Create a REXX system exit.  We always handle REXX I/O so that the TRACE   */
  1152. /* or SAY instructions do not halt the REXX procedure (which will happen if  */
  1153. /* we do not catch them.  If we are passing a range to REXX we set an exit   */
  1154. /* for REXX initialization at which time we set up the shared variables.     */
  1155. /*                                                                           */
  1156. /*****************************************************************************/
  1157. (
  1158. PTHEWORLD r                                                    // "global" data
  1159. )
  1160. {
  1161. APIRET rc;                                                         // return code
  1162. ULONG a;                                                        // DosOpen info
  1163. USHORT i;                              // because FORTRAN was my first language
  1164.  
  1165.  
  1166. if (r->logFname ISNT NULL)                               // log file specified?
  1167.    rc = DosOpen(r->logFname, &r->logFile, &a, 0, 0, LOG_FLAG, LOG_MODE, 0L);
  1168. else r->logFile = 0;                                       // no, remember this
  1169. rc = RexxRegisterExitDll(LOTUS_ENV, OUR_DLL, "RexxHandler", NULL, RXEXIT_NONDROP);
  1170. i = 0;
  1171. r->rxExit[i].sysexit_name = LOTUS_ENV;                       // always trap I/O
  1172. r->rxExit[i++].sysexit_code = RXSIO;      // handle I/O from the REXX procedure
  1173. if (r->numRange > 0) {                              // yes, we have some ranges
  1174.    r->rxExit[i].sysexit_name = LOTUS_ENV;
  1175.    r->rxExit[i++].sysexit_code = RXINI;
  1176.    }
  1177. r->rxExit[i].sysexit_name = NULL;
  1178. r->rxExit[i].sysexit_code = RXENDLST;
  1179. r->rxExitPtr = r->rxExit;
  1180. return;
  1181. }                                                             // of RexxSysExit
  1182.  
  1183.  
  1184. VOID RxstringToCstring
  1185. /*****************************************************************************/
  1186. /*                                                                           */
  1187. /* Copy an RXSTRING to a C (null terminated) string.                         */
  1188. /*                                                                           */
  1189. /*****************************************************************************/
  1190. (
  1191. char * to,
  1192. RXSTRING from
  1193. )
  1194. {
  1195. USHORT temp;
  1196.  
  1197. temp = (USHORT) from.strlength;
  1198. memmove(to, from.strptr, temp);                    // should check buffer size!
  1199. to[temp] = EOS;
  1200. return;
  1201. }                                                       // of RxstringToCstring
  1202.  
  1203. APIRET SetCell
  1204. /*****************************************************************************/
  1205. /*                                                                           */
  1206. /* Set a 1-2-3 cell to the passed type and value.                            */
  1207. /*                                                                           */
  1208. /*****************************************************************************/
  1209. (
  1210. PVOID range,                                              // 1-2-3 range handle
  1211. USHORT coord[],                                             // cell coordinates
  1212. RXSTRING theType,                                          // type for the cell
  1213. RXSTRING theValue,                                        // value for the cell
  1214. char * workBuffer
  1215. )
  1216. {
  1217. #define CELLTYPE_EMPTY  0
  1218. #define CELLTYPE_ERR    1
  1219. #define CELLTYPE_NA     2
  1220. #define CELLTYPE_NUMBER 3
  1221. #define CELLTYPE_STRING 4
  1222. #define CELLTYPES 5
  1223. static char cType[CELLTYPES][7] = {"EMPTY", "ERR", "NA", "NUMBER", "STRING"};
  1224. double realNumber;
  1225. long integerNumber;
  1226. PVOID value;
  1227. LONG rc;
  1228. USHORT i;
  1229. USHORT cellType, cellSize;
  1230.  
  1231. RxstringToCstring(workBuffer, theType);                        // get cell type
  1232. for (i = 0; i < CELLTYPES; i++) {                     // validate type argument
  1233.    if (strcmpi(workBuffer, cType[i]) IS 0) break;                  // found it?
  1234.    }
  1235. switch (i) {                                                // set up cell type
  1236. case CELLTYPE_EMPTY:                                // set the cell to be empty
  1237.    cellType = LEPC_TYPE_EMPTY;
  1238.    cellSize = 0;
  1239.    break;
  1240. case CELLTYPE_ERR:                     // set the cell to the special value ERR
  1241.    cellType = LEPC_TYPE_ERR;
  1242.    cellSize = 0;
  1243.    break;
  1244. case CELLTYPE_NA:                       // set the cell to the special value NA
  1245.    cellType = LEPC_TYPE_NA;
  1246.    cellSize = 0;
  1247.    break;
  1248. case CELLTYPE_NUMBER:    // set the cell to an integer or floating point number
  1249.    RxstringToCstring(workBuffer, theValue);                        // get value
  1250.    if (strchr(workBuffer, '.') ISNT NULL) {         // there is a decimal point
  1251.       cellType = LEPC_TYPE_DOUBLE;         // set it as a floating point number
  1252.       cellSize = sizeof(double);
  1253.       if (NOT sscanf(workBuffer, "%lf", &realNumber)) return 40;
  1254.       value = &realNumber;
  1255.       }
  1256.    else {                                                   // no decimal point
  1257.       cellType = LEPC_TYPE_LONG;                 // set it as an integer number
  1258.       cellSize = sizeof(long);
  1259.       if (NOT sscanf(workBuffer, "%d", &integerNumber)) return 40;
  1260.       value = &integerNumber;
  1261.       }
  1262.    rc = LepMcSetCellValue(range, coord, cellType, cellSize, value);
  1263.    break;
  1264.  
  1265. case CELLTYPE_STRING:
  1266.    cellType = LEPC_TYPE_STRING;
  1267.    RxstringToCstring(workBuffer, theValue);                        // get value
  1268.    cellSize = (USHORT)( theValue.strlength + 1);
  1269.    value = workBuffer;
  1270.    break;
  1271. default: return 40;                                        // invalid cell type
  1272. }                                                                  // of switch
  1273. rc = LepMcSetCellValue(range, coord, cellType, cellSize, value);
  1274. return 0;
  1275. }                                                       // of RxstringToCstring
  1276.  
  1277.  
  1278. /*****************************************************************************/
  1279. /*****************************************************************************/
  1280. /*                                                                           */
  1281. /* Th-th-that's all, folks!                                                  */
  1282. /*                                                                           */
  1283. /*****************************************************************************/
  1284. /*****************************************************************************/
  1285.