Function RunRexx() Foundation

Executes a REXX script file.

Syntax
RunRexx( <cScriptFileName> [, <cArguments,...>] ) --> xReturn
Parameters
<cScriptFileName>
<cCmdFileName> is a character string which contains the file name of the script file to be executed.
<cArguments,...>
<cArguments,...> is a comma-separated list of character expressions passed as arguments to the script file. Any number of arguments can be passed.
Return

The function RunRexx() returns the return value of the REXX script file.

Description

RunRexx() executes a script file and passes optional arguments to the REXX procedure in this file.

REXX is an interpreter language which automates program execution and allows for easy changes. Using REXX, procedures are programmed which can be called and executed using a REXX command processor. Open Object Rexx (ooRexx) is an open-source REXX interpreter which can be used for executing REXX scripts under Windows, see https://www.oorexx.org.

REXX procedures are typically stored in files with the extension .rex. These are script files comparable to .bat files in DOS. REXX script can be replaced, processed and changed with a simple text editor. They differ from .bat files in that they must always begin with a comment line (/* Comment */) so the characters "/*" must be the first two characters in the file. These characters are key identifiers for the REXX interpreter.

All commands and functions of REXX are usable within a REXX file. If such a file is started by RunRexx() from an Xbase++ application, the REXX procedure runs in the address space of the Xbase++ application. RunRexx() extends the language scope of REXX to all globally visible functions, procedures and dynamic variables of the calling Xbase++ application. This means that all Xbase++ functions and procedures can be called and executed within the REXX script. That includes all the functions of the Xbase++ runtime library and all globally visible user-defined functions and procedures. Also, all dynamic variables visible when the call RunRexx() occurs are visible. Access to lexically scoped variables and calls to functions or procedures declared as STATIC is not possible. The program operation of an Xbase++ application can be changed without compile and link cycles since REXX script files can simply be processed with a text editor.

Passing of arguments to REXX

The function RunRexx() can pass many arguments to the REXX procedure using <cArguments,...>. The passed arguments must always have the data type "character", since REXX is a character based interpreter language. To access the passed arguments within the REXX procedure, REXX provides the command ARG and the function ARG(). The command ARG is generally needed when a REXX procedure is started from the command line. The following REXX procedure demonstrates this and receives two arguments:

/* 
* File:  rexxarg1.rex 
*        Parameter passing from the command line to REXX 
*/ 

ARG StringA StringB               /* Two arguments are received  */ 

SAY ""                            /* Display arguments           */ 
SAY "The first argument is: " StringA 
SAY "The second argument is: " StringB 

The procedure rexxarg1.rex can be started from the command line using the rexx command. It displays the passed arguments on the screen.

[C:\XPP\samples\Rexx]rexx rexxarg1.rex a1 a2 
The first argument is: A1 
The second argument is: A2 

[C:\XPP\samples\Rexx]rexx rexxarg1.rex a1 a2 a3 
The first argument is: A1 
The second argument is: A2 A3 

When called from the command line, the REXX procedure receives a single character string. The command ARG converts the character string to upper case letters and dissects it at blank spaces to correspond to the number of arguments declared with ARG. The same behavior is also produced with call of the procedure by RunRexx().

#include "simpleio.ch" 

PROCEDURE Main 
LOCAL cRexxArg := "a1 a2" 

RunRexx("rexxarg1.rex", cRexxArg)   // output with command ARG: 
                                    // The first argument is: A1 
RETURN                                 // The second argument is: A2 

PROCEDURE AppSys() 
RETURN 

Since RunRexx() can pass any number of arguments to a REXX procedure, the use of the REXX function ARG() is important. This function reads the passed arguments individually without changing the case.

/* 
*   File: rexxarg2.rex 
*         Parameter passing from Xbase++ to REXX 
*/ 

nArgs = ARG()                        /* number of passed arguments */ 

SAY "" 
DO i = 1 TO nArgs                    /* displays arguments      */ 
  SAY "The "i". argument is: " ARG(i) 
END 
RETURN "End of the REXX procedure"   /* Return value to Xbase++ */ 

The procedure rexxarg2.rex displays all passed arguments on the screen. Additionally, it returns a character string as a return value for RunRexx(). From the view of an Xbase++ application rexxarg2.rex provides the following result (the screen output is indicated as comments):

#include "simpleio.ch" 

PROCEDURE Main 
LOCAL cRexxArg := "a1 a2", cRexxResult := "No return value" 
RunRexx("rexxarg2.rex", cRexxArg)   // The 1. argument is: a1 a2 

cRexxResult := RunRexx("rexxarg2.rex", "a1", "a2", "10") 
                                    // The 1. argument is: a1 
                                    // The 2. argument is: a2 
                                    // The 3. argument is: 10 

? cRexxResult                       // End of the REXX procedure 
RETURN 

PROCEDURE AppSys() 
RETURN 

Accessing Xbase++ variables and functions

When Xbase++ functions are called from a REXX procedure, the Xbase++ function call must always be enclosed in quotation marks. This also applies when accessing dynamic variables. Generally, Xbase++ specific syntax must be enclosed in quotation marks in a REXX procedure.

/* 
* File: rexxarg3.rex 
*       Access to Xbase++ variables and call of functions 
*/ 

SAY ""                             /* Bring cursor to first column   */ 
"cRexxArg"                         /* Read Xbase++ memory variable   */ 
rxValue = RC                       /* Assign value of REXX variable  */ 
SAY rxValue                        /* "RC" to new REXX variable      */ 

"Upper(cRexxArg)"                  /* Execute Xbase++ function       */ 
rxValue = RC                       /* Assign value of REXX variable  */ 
SAY rxValue                        /* "RC" to new REXX variable      */ 

"MEMVAR->cRexxArg := " ,           /* Change value of an Xbase++     */ 
          "'changed from REXX'"   /* variable (note line            */ 
                                  /* continuation character ","     */ 

The contents of dynamic Xbase++ variables (see MEMVAR and FIELD declaration) are read in a REXX procedure by including the variable name. The variable name must be on a separate command line. The value of the variable is assigned at execution of the command line to the general REXX variable "RC" (RC stands for Return Code) and must be saved in a new REXX variable if it is needed for further processing. Generally, the variable RC contains the result of the Xbase++ specific operation, such as the return value of an Xbase++ function.

All Xbase++ specific instructions within the REXX procedure must be syntactically formulated so that they can also be compiled with the macro operator (the Xbase++ runtime compiler actually interprets the REXX command lines directed to Xbase++). The exceptions are command lines that appear on multiple lines. With REXX the character for a line continuation is the comma, not the semicolon as in Xbase++.

The execution of the REXX procedure rexxarg3.rex with RunRexx() leads to the following result:

#include "simpleio.ch" 

PROCEDURE Main 
MEMVAR cRexxArg 
PRIVATE cRexxArg := "test" 

? cRexxArg                          // test 

RunRexx("rexxarg3.rex")             // test 
                                    // TEST 
? cRexxArg                          // changed from REXX 
RETURN 

PROCEDURE AppSys() 
RETURN 

Communication between Xbase++ and REXX

The fact that Xbase++ functions can be called from a REXX procedure permits "communication" between the Xbase++ application and the REXX procedure. If a REXX procedure calls an Xbase++ user-defined function, that function must be available when the REXX procedure is run. The user-defined function RexxCallsXbase() demonstrates how this is accomplished.

/* 
*   File: rexxarg4.rex 
*         "Communication" between Xbase++ and REXX 
*/ 

"dDate := Date()"                  /* create PRIVATE variable       */ 
                                  /* in Xbase++                    */ 
SAY "" 
SAY "REXX calls Xbase++"           /* call user-defined             */ 
"RexxCallsXbase('dDate')"          /* Xbase++ function              */ 
SAY "" 
SAY "Return value from Xbase++:"   /* display return value          */ 
SAY "" RC                          /* of the function               */ 

RETURN "REXX is finished"          /* return value to Xbase++       */ 

The procedure rexxarg4.rex creates a new PRIVATE variable dDate in the Xbase++ application and assigns the function value of Date() to it with the inline assignment operator. RexxCallsXbase() is then called and the name of the new variable dDate is passed to Xbase++ as a character string. The lifetime of this PRIVATE variable is the execution time of the REXX procedure rexxarg4.rex. The variable is therefore visible in the function RexxCallsXbase() and so access to dDate within Xbase++ is possible.

#include "simpleio.ch" 

************** 
PROCEDURE Main 
LOCAL cRexxResult 

? "Xbase++ calls REXX"                // Xbase++ calls REXX 
cRexxResult := RunRexx("rexxarg4.rex")// REXX calls Xbase++ 
                                      // Xbase++ is here again 
                                      // dDate 
                                      // 12/06/94 
                                      // Return value from Xbase++: 
                                      //  Xbase++ returns to REXX 
? "Return value from REXX:"           // Return value from REXX: 
? " "+cRexxResult                     //  REXX is finished 
? "Xbase++ is finished"               // Xbase++ is finished 
RETURN 

********************************** 
FUNCTION RexxCallsXbase( xValue ) 
? "Xbase++ is here again"           // Xbase++ is here again 
? xValue                            // dDate 
? &xValue                           // 12/24/94 
RETURN "Xbase++ returns to REXX" 

****************** 
PROCEDURE AppSys() 
RETURN 

The function RexxCallsXbase() receives a variable name from the REXX procedure and the contents of the variable dDate created in the REXX procedure can be read in Xbase++ with the macro operator. Of course this procedure is only used for demonstration. The value of dDate could be passed directly to the function which is more efficient. In order to do this, the variable identifier must not be enclosed in single quotation marks:

"RexxCallsXbase(dDate)"         /* call Xbase++function      */ 

Used in connection with RunRexx(), REXX offers an ideal opportunity for the creation of "data driven programs". The definition of data occurs within a REXX procedure. It generates data and transmits it to the Xbase++ application.

Select a subcommand handler

With the command ADDRESS, REXX allows instructions to be run in a specific command environment. The environment in which REXX instructions are actually processed is called the "Subcommand handler". Since an Xbase++ application always runs within a command shell, generally two subcommand handlers are available during the execution of REXX procedures. When a REXX procedure is executed by RunRexx(), subcommand handlers are addressed with REXX through symbolic names, which are "XBPP" and "CMD". The subcommand handler "CMD" is the command shell, and "XBPP" is the Xbase++ application.

With the REXX command ADDRESS CMD, instructions are sent to the command shell. In this way, operating system commands can be executed in the background without affecting the Xbase++ application.

/* 
* File: settime.rex 
*       Change the system time 
*/ 

ARG newTime                         /* read argument                */ 

IF newTime = "" THEN                /* no new time given            */ 
   NOP                              /* No OPeration                 */ 
ELSE 
   ADDRESS CMD "TIME" newTime       /* send command TIME            */ 
                                    /* to command shell             */ 

EXIT                                /* back to calling process      */ 

Using the REXX procedure settime.rex the system time can be reset from an Xbase++ application. The operating system command "TIME" is directed by ADDRESS to the command shell. An example is in the following Xbase++ program:

#include "inkey.ch" 

PROCEDURE Main 
LOCAL GetList := {}, cTime, nHour, nMinute, nSecond 
SetColor("N/BG,W+/B") 
CLS 

cTime   := Time() 
nHour   := Val(SubStr(cTime,1,2)) 
nMinute := Val(SubStr(cTime,4,2)) 
nSecond := Val(SubStr(cTime,7,2)) 

@ 0,2 SAY "Sets system time. Terminate with Esc" 
@ 2,2 SAY " Hour:"   GET nHour   PICTURE "99" RANGE 0,23 
@ 4,2 SAY " Minute:" GET nMinute PICTURE "99" RANGE 0,59 
@ 6,2 SAY "Second:"  GET nSecond PICTURE "99" RANGE 0,59 

DO WHILE LastKey() <> K_ESC 
   READ SAVE 
   IF Updated() 
      cTime := Str(nHour   ,2) + ":" + ; 
               Str(nMinute ,2) + ":" + ; 
               Str(nSecond ,2) 
      RunRexx( "settime.rex", cTime ) 
   ENDIF 
ENDDO 
RETURN 

Interprocess communication

Using REXX and RunRexx(), communication can take place between different processes (interprocess communication). The REXX command RXQUEUE and the REXX function RXQUEUE() are used to accomplish this. These functions manage queues through which data exchange between two Xbase++ applications is possible. An example program for this technique is in the discussion of the function RunShell().

The REXX command PULL is not permitted in REXX files executed with RunRexx(). PULL waits for data to arrive in the queue STDIN. This queue is needed and managed by each Xbase++ application. The command PULL can bring an Xbase++ application to a halt.

Examples
REXX - Basic program for RunRexx() examples
// The example program is composed of three parts: the procedure 
// "Main" permits a repeated call of a REXX file. 
// The procedure callRexx() executes a REXX file. 
// The function RexxCallsXbase() is called from REXX procedures. 
// See also the following examples of REXX files. 
#include "simpleio.ch" 
#include "inkey.ch" 
#include "vt100.ch" 

************** 
PROCEDURE Main 
LOCAL cFile := "dummy" 

DO WHILE .NOT. Empty(cFile)        // enable repeated call 
   CLS() 
   ? "Example for RunRexx(). Exit with Esc." 
   ? "REXX files: rexxa1.rex, rexxa2.rex, rexxa3.rex" 
   ? 
   ? "Enter name of the REXX file (RETURN to quit): " 
   cFile := GetFile() 

   IF File(cFile) 
      callRexx( cFile )             // call REXX file 
   ELSEIF .NOT. Empty(cFile) 
      ? "File "+cFile +" is not available!" 
      DoWait( "Press a key." ) 
   ENDIF 
ENDDO 

?? VT_SGRRESET        
RETURN 

****************************** 
* Procedure which calls REXX * 
****************************** 
PROCEDURE callRexx( cFile ) 
LOCAL nKey 
LOCAL RexxResult 

DispBeginRexx() 

// Execute REXX file 
RexxResult := RunRexx( cFile, "Xbase++ leaves...", ; 
                              "REXX takes over control" ) 
? 
DispEndRexx() 
IF ! Empty(RexxResult) 
   ? 
   ? "Xbase++ has received a return value from REXX !" 
   ? "The value is:" 
   ? 
   ? VT_FGWHITE+VT_BGRED + RexxResult + VT_FGWHITE+VT_BGGREEN 
   ? 
ENDIF 

DoWait( "Press a key..." ) 
RETURN 

****************************************** 
* Function which is called from REXX     * 
****************************************** 
FUNCTION RexxCallsXbase( xValue ) 
LOCAL cType := "" 

DispBeginXppCall() 
? 
? "REXX has called a user-defined function of Xbase++!" 
? "ProcName() is:", ProcName() 

IF PCount() > 0 
   ? "The Xbase++ application has received a parameter from REXX!" 
   ? "It is:" 
   ? 
   ? VT_FGWHITE+VT_BGRED + xValue + VT_FGWHITE+VT_BGGREEN 
   ? 
   ? "The data type 'Type()' is:", ( cType := Type(xValue) ) 

   IF .NOT. "U" $ cType 
      ? "The received parameter is now compiled" 
      DoWait( "Press a key..." ) 
      IF cType == "B" 
         Eval( &xValue )            // compile and execute 
      ELSE                          // code block with & 
        ? &xValue                   // compile return value with & 
      ENDIF 
   ENDIF 
ENDIF 

? "The user-defined function from Xbase++ returns control to REXX." 
DoWait( "Press a key..." ) 
DispEndXppCall() 
? 
RETURN "The function "+ProcName()+"() is finished." 


****************************************** 
* Helper function for getting a file name* 
****************************************** 
FUNCTION GetFile() 
LOCAL cFile := Space( 40 ) 

?? VT_DECSC+VT_BGBLUE+VT_FGWHITE + cFile + VT_DECSR 
ACCEPT TO cFile 
?? VT_FGBLACK+VT_BGCYAN 
RETURN Alltrim(cFile) 


****************************************** 
* Helper function for clearing screen    * 
****************************************** 
PROCEDURE CLS() 
?? VT_CHA(0)+VT_VPA(0) 
?? VT_FGBLACK+VT_BGCYAN+VT_ED 
RETURN 

****************************************** 
* Helper function for visually hiliting  * 
* beginning of REXX context              * 
****************************************** 
PROCEDURE DispBeginRexx() 
? VT_FGWHITE+VT_BGGREEN + "XBASE++ >>>>> REXX" + VT_EL(0) 
? VT_FGWHITE+VT_BGBLUE+VT_ED(0) 
RETURN 


****************************************** 
* Helper function for visually hiliting  * 
* end of REXX context                    * 
****************************************** 
PROCEDURE DispEndRexx() 
? VT_FGWHITE+VT_BGGREEN + "XBASE++ <<<<< REXX" + VT_EL(0) 
RETURN 


****************************************** 
* Helper function for visually hiliting  * 
* beginning of Xbase++ call from REXX    * 
****************************************** 
PROCEDURE DispBeginXppCall() 
?? VT_FGWHITE+VT_BGGREEN + "REXX >>>>> XBASE++" + VT_ED(0) 
RETURN 


****************************************** 
* Helper function for visually hiliting  * 
* end of Xbase++ call from REXX          * 
****************************************** 
PROCEDURE DispEndXppCall() 
? "REXX <<<< XBASE++" + VT_FGWHITE+VT_BGBLUE 
RETURN 


****************************************** 
* Wait for a key press                   * 
****************************************** 
PROCEDURE DoWait( cTxt ) 
LOCAL nDummy 

? cTxt 
ACCEPT TO nDummy 
RETURN 


PROCEDURE AppSys() 
RETURN 
REXX - Communication between Xbase++ and REXX

/* 
* rexxa1.rex - REXX example program for Xbase++ 
*/ 

PARSE SOURCE Who_Am_I              /* determine file name from CMD  */ 

SAY ""                             /* display blank line            */ 
DO i=1 TO ARG()                    /* display arguments which       */ 
  SAY ARG(i)                      /* were passed from Xbase++      */ 
END 

SAY ""                             /* display with REXX commands    */ 
SAY "REXX welcomes you..." 
SAY "My name is:"            Who_Am_I        /* this file           */ 
SAY "I was called from:" ADDRESS()           /* Xbase++ program     */ 
SAY "" 
SAY "I will now call a user-defined Xbase++ function " 
SAY "The name of the function is: RexxCallsXbase()." 
SAY "The function will receive a character string from me..." 
SAY "" 
SAY "It is: 'Hi Xbase++, here is REXX'" 
SAY "Press a key..." 
SAY "" 

"FReadStr(-10,1)"                  /* call Xbase++ function; get key*/ 
                                  /* from STDIN                    */ 
                                  /* call UDF and pass a           */ 
                                  /* parameter                     */ 
"RexxCallsXbase( 'Hi Xbase++, here is REXX' )" 

SAY "" 
SAY "Maybe Xbase++ has returned something?!" 
SAY "I will look in my global variable 'RC' (Return Code)." 
SAY "Xbase++ has returned the following:" 
SAY "" 
SAY RC                             /* display Xbase++ return value  */ 
SAY "" 
SAY "The REXX procedure is now finished." 

SAY "Press a key..." 
"FReadStr(-10,1)"                  /* call Xbase++ function; get key*/ 
                                  /* from STDIN                    */ 
RETURN "REXX returns control back to Xbase++" 
REXX - Create a DBF structure in REXX

/* 
* rexxa2.rex - REXX example program for Xbase++ 
*/ 

PARSE SOURCE Who_Am_I             /* determine file name from CMD   */ 

SAY ""                            /* display empty row              */ 
DO i=1 TO ARG()                   /* display arguments which        */ 
  SAY ARG(i)                     /* were passed from Xbase++       */ 
END 

SAY ""                            /* display with REXX commands     */ 
SAY "REXX welcomes you..." 
SAY "My name is:"            Who_Am_I     /* this file              */ 
SAY "I was called from:" ADDRESS()        /* Xbase++ program        */ 
SAY "" 
SAY "This is an example of data driven programming!" 
SAY "I will create an array for the definition of a DBF file." 

                                 /* create PRIVATE variable        */ 
"aStructure := Array(0)"          /* in the Xbase++ application     */ 
                                 /* with := inline assignment!     */ 

                                 /* call Xbase++ function          */ 
"AAdd( aStructure, {'FIRSTNAME', 'C', 20, 0 } )" 
"AAdd( aStructure, {'LASTNAME' , 'C', 40, 0 } )" 

SAY "I have created a PRIVATE array with two field definitions." 
SAY "" 
SAY "Now I call 'RexxCallsXbase()'and pass the name" 
SAY "of the array created by me. Let's see what Xbase++ does with it." 
SAY "Press a key..." 

"FReadStr(-10,1)"                  /* call Xbase++ function; get key*/ 
                                  /* from STDIN                    */ 
"RexxCallsXbase('aStructure')" 

SAY "" 
SAY "Maybe Xbase++ has returned something?!" 
SAY "I will look in my global variable 'RC' (Return Code)." 
SAY "Xbase++ has returned the following:" 
SAY "" 
SAY RC                             /* display Xbase++ return value  */ 
SAY "" 
SAY "The REXX procedure is now finished." 
SAY "Press a key..." 

"FReadStr(-10,1)"                  /* call Xbase++ function; get key*/ 
                                  /* from STDIN                    */ 
RETURN "REXX wishes you a good trip" 
REXX - Select subcommand handler
/* 
* rexxa3.rex - REXX example program for Xbase++ 
*/ 

PARSE SOURCE Who_Am_I              /* determine file name from CMD  */ 

SAY ""                             /* display empty row              */ 
DO i=1 TO ARG()                    /* displays arguments which       */ 
  SAY ARG(i)                      /* were passed from Xbase++       */ 
END 

SAY ""                             /* display with REXX commands     */ 
SAY "REXX welcomes you..." 
SAY "My name is:"            Who_Am_I      /* this file              */ 
SAY "I was called from:" ADDRESS()         /* Xbase++ program        */ 
SAY "" 
SAY "This is an example for changing the subcommand handler" 
SAY "I will call the command 'DIR *.*' without Xbase++ seeing it!" 
SAY "Press a key..." 

"FReadStr(-10,1)"                  /* call Xbase++ function; get key*/ 
                                  /* from STDIN                    */ 

ADDRESS CMD "DIR *.* > DIR.TXT"    /* subcommand handler is CMD      */ 
                                  /* output directory in file       */ 

SAY "The output of the directory occurred in the file 'DIR.TXT'." 
SAY "" 
SAY "Now I will read the file into an Xbase++ PRIVATE variable and" 
SAY "apply MemoRead() because it is so simple." 
SAY "Press a key..." 
"FReadStr(-10,1)"                  /* call Xbase++ function; get key*/ 
                                  /* from STDIN                    */ 

"cText := MemoRead( 'DIR.TXT' )"   /* create PRIVATE variable      */ 
                                  /* in Xbase++ and read file     */ 

SAY "" 
SAY "Now watch carefully:" 
SAY "" 
SAY "I will pass a code block to the user-defined function" 
SAY "RexxCallsXbase() which Xbase++ will execute." 
SAY "The code block is:" 

"cBlock := '{|| MemoEdit(cText) }'" 
SAY RC 

SAY "Press a key..." 
"FReadStr(-10,1)"                  /* call Xbase++ function; get key*/ 
                                  /* from STDIN                    */ 
                                  /* subcommand handler is Xbase++ */ 
ADDRESS Xbpp "RexxCallsXbase( cBlock )" 

SAY "" 
SAY "Maybe Xbase++ has returned something?!" 
SAY "I will look in my global variable 'RC' (Return Code)." 
SAY "Xbase++ has returned the following:" 
SAY "" 
SAY RC                             /* display Xbase++ return value */ 
SAY "" 
SAY "The REXX procedure has almost finished," 
SAY "I must still delete the file 'DIR.TXT'." 

ADDRESS CMD "DEL DIR.TXT"          /* change subcommand handler     */ 
                                  /* and delete file               */ 

RETURN "That was a good job!" 
Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.