REXX Tips & Tricks:Hints for Object REXX

This section contains some hints for using Object-Oriented REXX under OS/2. It also contains some information about Object REXX for other operating systems.

Object REXX is currently available for OS/2, Windows NT, Windows 95, and Linux, and AIX.

see also
 * Creating "compiled" programs for Classic REXX and Object REXX
 * Writing filter programs in REXX
 * Using PMREXX
 * New REXXUTIL functions in Object REXX

Object REXX for OS/2

 * Name: Object-Oriented REXX for OS/2
 * Version:OBJREXX 6.00 18 May 1999
 * (special version for WARP 3; May99 Update)
 * OBJREXX 6.00 25 Mar 1998
 * (special version for WARP 3; Mar98 Update)
 * OBJREXX 6.00 11 Nov 1997
 * (special version for WARP 3; Nov97 Update, also part of WARP 4 Fixpack #6)
 * OBJREXX 6.00 21 Jul 1997
 * (special version for WARP 3)
 * (DevCon Rel 2 Vol 1)
 * OBJREXX 6.00 12 Jul 1996
 * (WARP 4)


 * Author:IBM
 * Distrib.: Part of WARP 4 and free through the web
 * Type: REXX Interpreter
 * Price: -
 * Source: WARP 4 / DevCon / (see Internet - Web Pages)

If possible, you should use the special version for WARP 3 from the IBM WebSite. Use of this version is not restricted to machines running WARP 3. It is a complete version of Object REXX and, at this time, the most up-to-date version available. This version is being promoted to users of WARP 3, since the GA version of WARP 3 did not contain a copy of Object REXX, as did WARP 4. It runs fine on WARP 4 machines.


 * Note:The CD-ROM from the book Object REXX for OS/2 WARP contains a copy of Object REXX for OS/2.

The following are lists of fixed bugs in the updated versions of Object REXX

The following is the list of fixed bugs in the version

OBJREXX 6.00 18 May 1999
ENHANCEMENTS TO OBJECT REXX

The following enhancements have been made to this Object REXX level:
 * Bugs corrected in 99/05 release:
 * Calling function Trace with an invalid option no longer causes an access violation.
 * Function FORMAT no longer creates extraneous trailing blanks for certain numbers.
 * Sliding window algorithm for two digit years adjusted for some cases in Date function.
 * Several syntax errors reported will now show the correct source line location in the user program.
 * A single LF that falls on the internal buffer boundary is no longer ignored.
 * Piped input no longer contains additional characters.
 * Literals no longer cause and error with the VALUE function.
 * DATE("S") with output separator specified now formatted correctly.
 * Memory leak when reading from streams has been removed.
 * Memory leak when creating many stream objects has been removed.
 * ARRAYIN method of the stream class no longer raises the NOTREADY condition.
 * When a stack overflow is encountered it will be handled correctly without causing an application error.
 * RANDOM(1,,99) no longer causes an application error.
 * STREAM("STDIN","C","CLOSE) no longer causes a trap.


 * Features added in 99/05 release:
 * New REXXUTIL functions:
 * SysDumpVariables
 * SysGetFileDateTime
 * SysSetFileDateTime
 * SysStemCopy
 * SysStemDelete
 * SysStemInsert
 * SysStemSort
 * SysUtilVersion
 * SysVersion
 * If the first line contains a Unix style shell definition starting with #! it will be ignored (seen as a comment).

OBJREXX 6.00 Oct 1998
ENHANCEMENTS TO OBJECT REXX

The following enhancements have been made to this Object REXX level:
 * Bugs corrected in 98/10 release:
 * Calling function Trace with an invalid option no longer causes an access violation.
 * OPTIONS EXMODE not working correctly
 * SysFileTree did not work correctly with absolute path from root (e.g. "\OS2")
 * Subclassing class STREAM could cause a trap
 * Closing files after changing directories sometimes did not work
 * Broken error message for bad hexadecimal strings
 * Internal memory handling problem
 * Trap with many threads depending on timing at thread termination
 * DO..OVER binary files caused system resources to be exhausted
 * UNINIT method not called when defined in a parent class
 * Last line was ignored by SysFileSearch when not terminated with CR/LF
 * Drop of an object didn't invoke the UNINIT method
 * Stream SEEK command no longer requires READ/WRITE attribute, if none is specified BOTH is assumed (to be compatible with classic REXX)
 * TRANSLATE("abc","","") gives incorrect result
 * Trap or unpredictable results with long parse statements
 * Accessing an unqualified filename (no path information) sometimes failed after changing the current directory
 * String comparison with padded characters fails if excess character is greater than ASCII 127
 * SysFileTree fails to find Win95 LFN directories
 * Long message evaluation expressions not protected from garbage collection


 * Features added in 98/10 release:
 * New input and output separators for DATE function according to revised ANSI standard
 * Use double dash - as line comment according to request by ANSI committee
 * Use single dash - as line continuation character according to request by ANSI committee
 * Default stream open behaviour is now non-shared (as with classic REXX).

The three new options for the stream command available to open files in shared mode are: SHARED allow reading and writing from other processes SHAREREAD allow reading from other processes SHAREWRITE allow writing from other processes e.g.: CALL STREAM "MYFILE.DAT","C","OPEN READ SHARED" CALL STREAM "LOGFILE.TXT","OPEN BOTH SHAREREAD"

OBJREXX 6.00 25 Mar 1998
The following is the list of fixed bugs in the version OBJREXX 6.00 25 Mar 1998:

ENHANCEMENTS TO OBJECT REXX

The following enhancements have been made to this Object REXX level:
 * Corrected several bugs:
 * SysFileTree error when using the 'O' option
 * Speed up processing of LINES built-in function
 * Stream Query Size returns wrong value for not existing files
 * Memory problems with objects larger than 16 MB
 * CHARS built-in function returned -1 for certain files
 * Memory leak when working with many files
 * LINES method and built-in function didn't recognize a single character after CR/LF
 * Line count wrong for files ending in CR-LF-EOF
 * WPS support not persistent between reboots

OBJREXX 6.00 11 Nov 1997
The following is the list of fixed bugs in the version OBJREXX 6.00 11 Nov 1997:

ENHANCEMENTS TO OBJECT REXX

The following enhancements have been made to this Object REXX level:
 * Corrected several bugs:
 * External programs cannot be started in the foreground
 * FILESPEC returns empty string for certain filename specifications
 * Problem with line continuation character in mixed CRLF/LF files
 * Trap after garbage collection
 * Problem parsing mixed SBCS/DBCS strings
 * Trap when trying to access memory from different processes
 * Corrected a problem with duplicate freed memory in SysIni
 * Wrong error location for duplicate method


 * Added features:
 * New functions in REXXUTIL.DLL (see New REXXUTIL functions in Object REXX)
 * Enable SysIni function to work in a non-PM environment (see New REXXUTIL functions in Object REXX)

OBJREXX 6.00 26 Feb 1997 and OBJREXX 6.00 21 Jul 1997
The following is the list of fixed bugs in the versions OBJREXX 6.00 26 Feb 1997 and OBJREXX 6.00 21 Jul 1997:

ENHANCEMENTS TO OBJECT REXX

The following enhancements have been made to this Object REXX level:


 * Corrected several bugs:
 * Trap in deep recursive algorithms
 * Problems with CR/LF literal translation in output streams
 * Data files not written if session exited with "EXIT"
 * RexxVariablePool did not return EXITNAME symbol
 * SysFileSearch quits at EOF in the middle of the file
 * Problems with intersection operations on collections
 * Bad count in SysFileSearch with mixed LF, CR/LF line feeds
 * Trap when called from IBM Internet Connection Server
 * CHARS and LINES return 0 for redirected STDIN
 * Trap in recursive invocation of STRING method
 * Delete key has no effect in PMREXX entry field
 * Trap when seeking not existing file
 * Hash value not defined for class objects
 * Error in PARSE CASELESS instruction
 * Incorrect output with .error~say
 * Standard streams not recognized with colon in name
 * Propagate error/halt conditions from OREXX image
 * Hard error popup in STREAM("A:\TEST.DAT","C","QUERY EXISTS")
 * Concurrent start of program fails sometimes
 * Problem setting OS/2 environment
 * LINEOUT does not write to correct file

Object REXX for Windows 95/Windows NT

 * Name: Object-Oriented REXX for Windows 95/Windows NT
 * Version: see Website for current version
 * Author: IBM
 * Distrib: commercial
 * Type: REXX Interpreter
 * Price: -
 * Source: IBM

In February 1997 IBM removed the Evaluation Copy of Object REXX for Windows 95/Windows NT from their web site and started to sell the Licensed version. See the Home Page for Object REXX for Windows NT and Windows 95 for additional information.

Object REXX for Linux

 * Name: Object-Oriented REXX for Linux
 * Version: see Website for current version
 * Author: IBM
 * Distrib.: free
 * Type: REXX Interpreter
 * Price: -
 * Source: (see Internet - Web Pages)
 * Note: Contains the Object REXX documentation in PostScript format

See the Web Site for more information about Object-Oriented REXX for Linux.

Object REXX for AIX

 * Name: Object-Oriented REXX for AIX
 * Version: see Website for current version
 * Author: IBM
 * Distrib.: commercial
 * Type: REXX Interpreter
 * Price: -
 * Source: (see Internet - Web Pages)

See the Web Site for more information about Object-Oriented REXX for AIX.

How to switch from Classic REXX to Object Rexx and vice versa
To switch from Object REXX to Classic REXX or vice versa execute switchrx in an OS/2 window and reboot the machine.

If you want to use the WPS support of Object REXX (only possible if Object REXX is the active REXX interpreter!) you must activate it using wpsinst + in an OS/2 window.

Use wpsinst with the parameter - to deactivate the WPS support (see also Object REXX and the WPS) Note that due to a bug in wpsinst.cmd the "wpsinst ?" does not work as expected: There's no output at all if the WPS support is not registered.

To check, which REXX version is currently running execute rexxtry parse version t; say t; in an OS/2 command window.

If the output is similar to OBJREXX 6.00 18 May 1999 your current REXX interpreter is Object REXX else not.

(see also Using Classic REXX if Object REXX is the default REXX)

Differences between Classic REXX and Object REXX
Normally, all REXX programs written in Classic REXX should run under Object REXX without any changes. But there are some minor changes and bugs in Object REXX that might lead to an error in your Classic REXX programs.

The first thing you should do, if you're got an error in a Classic REXX program running under Object REXX, is:

Look in the Object REXX online documentation; especially read the section Migration (the good old RTFM)!

If you do not not find anything related to the error there, you can take a look at the following sections with differences between Classic REXX and Object-Oriented REXX!

Please use only this order for checking for problems, because I'm not going to repeat the facts already mentioned in the Object REXX online documentation.

OK, now that it's hopefully clear what information you can find in this section, let's begin.

The function CHARS in Object REXX
The function CHARS does not work as expected in Object-Oriented REXX:

According to the online help, CHARS used on STDIN should return 1 if there are remaining characters in the stream or 0 if there are no more characters in the stream (That's also the way it used to work in Classic REXX).

In reality, CHARS always returns 0 if used for STDIN. [Tested with OBJREXX 6.00 12 Jul 1996] [Fixed in OBJREXX 6.00 26 Feb 1997 and newer versions (included in WARP 4 Fixpack #6)]

(see also and The function CHARS)

The function DIRECTORY in Object REXX
"There is a bug in OORexx/NT which is in the GA product, but will be fixed in WARP 4 Fixpack #1. (according to the developers).

The directory function only maintains 1 drive at 1 time. e.g.: say directory("c:\test1")     // c:\test1 say directory("d:\test2")     // d:\test2 say directory("C:")           // c:\   :-( say directory("D:")            // D:\   :-( This problem is NOT in the OS/2 version of Object REXX."

Source: Mario Semo

The function LINEOUT in Object REXX
The function LINEOUT seems to have a bug in Object REXX.

For example the REXX program below should write all three messages to the file TEST001.LOG in the current directory at program start. But in Object REXX, it writes only the first message to this file; the other two messages go into the files C:\OS2\TEST001.LOG and C:\MPTN\TEST001.LOG.

[Tested with OBJREXX 6.00 12 Jul 1996]

[Fixed in OBJREXX 6.00 26 Feb 1997 and newer versions (included in WARP 4 Fixpack #6)]

 /*                                                                   */ /* sample program to show a bug in the LineOut routine in Object REXX */ /*                                                                   */

/* set the name for the logfile                  */ logFileName = ILog( 'TEST001.LOG' ) /* logFileName now contains the fully qualified  */ /* name of the logfile                           */

/* write the name of the logfile to the screen   */ say 'MAIN: logFileName is : "' || logFileName || '"' say '     Current directory is : "' || directory || '"'

/* write something to the logfile                */ /* Note: This is written to the *correct* logfile */ call log 'Testing 1, currrent directory is ' || directory

/* write the name of the logfile to the screen   */ say 'MAIN: logFileName is : "' || logFileName || '"' say '     Current directory is : "' || directory || '"'

/* now change the directory                      */ call directory 'C:\OS2'

/* write the name of the logfile to the screen   */ say 'MAIN: logFileName is : "' || logFileName || '"' say '     Current directory is : "' || directory || '"'

/* ... and write something to the logfile        */ /* Note: This goes to the file C:\OS2\TEST001.LOG */ /*      and not into the correct logfile!!! */ call log 'Testing 2, currrent directory is ' || directory

/* now change the directory again                */ call directory 'C:\MPTN'

/* write the name of the logfile to the screen   */ say 'MAIN: logFileName is : "' || logFileName || '"' say '     Current directory is : "' || directory || '"'

/* ... and write something to the logfile        */ /* Note: This goes to the file C:\MPTN\TEST001.LOG*/ /*      and not into the correct logfile!!! */ call log 'Testing 3, currrent directory is ' || directory

/* write the name of the logfile to the screen   */ say 'MAIN: logFileName is : "' || logFileName || '"' say '     Current directory is : "' || directory || '"'

exit

/* -- */ /*-function: get the fully qualified name of the logfile             */ /*                                                                   */ /*-call:     ilog logfilename                                         */ /*                                                                   */ /*-where:    logfilename - name of the logfile                        */ /*                                                                   */ /*-returns:  fully qualified name of the logfile                      */ /*                                                                   */ /*                                                                    */ ILOG: PROCEDURE parse arg logFilename

/* open or create the logfile                */ logStatus = stream( logFileName, 'c', 'OPEN WRITE') if logStatus <> 'READY:' then do   say 'Error: Cannot create the logfile "' || logFileName || '"!' exit 255 end /* if */

/* close the logfile                         */ call stream logFileName, 'c', 'CLOSE'

/* get the fully qualified name of the       */ /* logfile                                   */ newLogFileName = translate( stream( logFileName, 'c', 'QUERY EXISTS' ) )

/* write the name of the logfile to the screen   */ say 'ILOG: logFileName is : "' || newLogFileName || '"' say '     Current directory is : "' || directory || '"'

RETURN newLogFileName

/* -- */ /*-function: log a message and clear the rest of the line            */ /*                                                                   */ /*-call:     log message                                              */ /*                                                                   */ /*-where:    message - message to show                                */ /*                                                                   */ /*-returns:  ''                                                       */ /*                                                                   */ /*-Note:     You do not need the 'call' keyword to use this routine. */ /*          The name of the logfile is saved in the global variable  */ /*          'logFileName'. */ /*                                                                   */ Log: PROCEDURE expose logFileName parse arg logmsg

/* write the name of the logfile to the screen   */ say ' LOG: logFileName is : "' || logFileName || '"' say '     Current directory is : "' || directory || '"'

/* error occurs also if the file is explicitly   */ /* opened before writing to the file! */ /* rc = stream( logFileName, 'c', 'OPEN WRITE')

call lineout logFileName, logmsg

/* the error does *not* occur, if the logfile is */ /* *not* closed                                  */

/* close the logfile                             */ rc = stream( logFileName, 'c', 'CLOSE')

RETURN '' 

The function LINES in Object REXX
The function LINES does not work as expected in Object-Oriented REXX:

According to the online help, LINES used on STDIN should return 1 if there are remaining characters in the stream or 0 if there are no more characters in the stream (That's also the way it used to work in Classic REXX).

In reality, LINES always returns 0 if used for STDIN. [Tested with OBJREXX 6.00 12 Jul 1996] [Fixed in OBJREXX 6.00 26 Feb 1997 and newer versions (included in WARP 4 Fixpack #6)]

(see also ; see Writing filter programs in REXX for a workaround)

The function STREAM in Object REXX
In Classic REXX, the statement myFileName = stream( "A:\test", "c"', "QUERY EXISTS" ) only returns an empty string if the drive A: is not ready. In Object-Oriented REXX this statement pops up this nice little OS/2 error box saying that the drive is not ready. [Tested with OBJREXX 6.00 12 Jul 1996] [Fixed in OBJREXX 6.00 26 Feb 1997 and newer versions (included in WARP 4 Fixpack #6)]

To avoid this, you need to put the AUTOFAIL (CONFIG.SYS statements used by OS/2) statement in your CONFIG.SYS file.

The function VALUE in Object REXX
The function VALUE fails with the error REX0040E (Incorrect call to routine) if you're trying to change the contents of an environment variable and the length of the value of another environment variable is greater than 725.

Source: Mario Semo (see EMail Addresses) [Fixed in OBJREXX 6.00 26 Feb 1997 and newer versions (included in WARP 4 Fixpack #6)]

The Signal handling in Object REXX
The handling of the halt signal seems to be buggy in Object-Oriented REXX; for example the signal handling does not work in the following REXX program.

[Tested with OBJREXX 6.00 12 Jul 1996]

[Fixed in OBJREXX 6.00 26 Feb 1997 and newer versions (included in WARP 4 Fixpack #6)]

 /*                                                                   */ /* example for a bug in the signal handling in Object REXX            */ /*                                                                   */ /* Source: Found in a message in a public news group                  */ /*                                                                   */ /* Hint: To get around the bug shown in this example, you can use the */ /*      function LineOut instead of the SAY statement. */ /*                                                                   */

call on halt

do forever say "Testing (press CTRL-C or CTRL-BREAK) ..." end;

exit

Halt:

say "Abort the program? " userInput = translate( lineIn ) if userInput = "Y" then exit

return 

Starting new VIO/Fullscreen sessions
The parameter handling of the Object REXX interpreter is different than the parameter handling of the Classic REXX interpreter if started via the START command:

In Object REXX programs started via START without a parameter the function arg( 1, 'e' ) always return true (there's always a parameter with the value " " [0x20]).

Try the code below using start test.cmd in Classic REXX and in Object REXX and compare the results to see the difference.  /* test.cmd */ parse arg arguments say say say 'The result of "arg( 1, e )" is  "' || arg( 1, 'e' ) || '".' say say 'Arguments are "' || arguments || '" (in hex: "' || c2x( arguments ) || '".)' say parse version versionString say "REXX interpreter is " versionString say

"pause" 

New features in Object REXX that are useful in Classic REXX programs also
This section contains a list of some new features in Object-Oriented REXX that are useful even for Classic REXX programs.

Please do not forget to check the version of the REXX interpreter you're using with your REXX programs before you attempt to use on of these features!

DO i OVER stem
This is a very useful enhancement of DO loops. Now you can simply walk over all elements of a stem without knowing the tails:  /* drop all stem entries                        */ drop myStem.

do i = 1 to 40 j = random( 400 ) myStem.j = 1 end /* do i = 1 to 40 */

do i over myStem. /* "i" contains the name of the next tail        */ /* (to get the value use "myStem.i")             */ say 'The ' || i || ' was at least one time set' say 'The ' || myStem.i || ' was at least one time set' end /* do i over myStem */ 

Returning a stem variable
In Object-Oriented REXX a routine can return a stem variable. Example:  /* */ test. = test1

do i = 1 to test.0 say test.i end

return

test1: /* init a local stem ... */ a.0 = 3 a.1 = 11 a.2 = 22 a.3 = 33 /* ... and return it to the calling routine. */ return a. 

Calculations and other stem variables are now possible to get or set a stem variable.
Example:  /* init a stem with sample values                                    */ do i = 1 to 500; test.i = i * 2 end /* do */ test.0 = 500

/* access the stem in the Classic way                                */

say test.5       /* (1)                                            */

j = 4+55         /* (2)                                            */ say test.j

i = test.4       /* (3)                                            */ say test.i

i = test.4       /* (4)                                            */ j = test.i say test.j

/* use the new way                                                   */

say test.[5]     /* (1)                                            */

say test.[4+55]  /* (2)                                            */

say test.[test.4] /* (3)                                           */

/* (4)                                           */  say test.[test.[test.4]] 

PARSE [upper|lower|caseless]
The PARSE instruction now supports lower and caseless parsing.

Call-by-Reference-Parameters are now possible for internal and external REXX procedures (at least for stem variables):  /* */

say say 'Sample code to show the usage of USE ARG in Object REXX ' say

j.0 = 2 j.1 = 111 j.2 = 222

say 'Values of the variables before calling the sub routine:' say say ' j.0 is ' || j.0 do k = 1 to j.0 say '  j.' || k || ' is ' || j.k  end /* do */

say say 'Now calling TestUseArg ...' say call TestUseArg j.

say say 'Values of the variables after calling the sub routine:' say say ' j.0 is ' || j.0 do k = 1 to j.0 say '  j.' || k || ' is ' || j.k  end /* do */

exit

TestUseArg: PROCEDURE use arg local_j.

/* local_j points to the global stem j.          */ local_j.0 = 3 local_j.1 = '111 - one' local_j.2 = '222 - two' local_j.3 = '333 - three'

return 

CALL
CALL now accepts variables that are evaluated before the CALL statement is executed:  /*                                                                   */

myRoutine = 'MYTEST' call (myRoutine) exit

MYTEST: say 'This is mytest!' RETURN  Plese note that the contents of the variable must be in uppercase if calling an internal routine. This is not clearly stated in the online help.

DATE
DATE is not restricted to the current date any more. Now you can use the results of this function for date calculations (e.g. How many days are between day 1 and day 2?).

STREAM
STREAM now supports some more commands and options; for example FLUSH, REPLACE (rewrite a file without doing a DEL first), and NOBUFFER. It also supports line-related positioning for files with fixed and variable length records. And there are now two different file pointers for every file -- one for reading from it and one for writing to it.

TIME
TIME is not restricted to the current time any more. This allows calculations with time stamps.

Use the environment .local for variables global to the current process. Example:  /* create a variable global to the current process                   */ .local['BS.MYVAR'] = 'This is a global variable'

/* call an external REXX routine to show that it works               */ call rexxtry "say .local['BS.MYVAR'] "

call rexxtry ".local['BS.MYVAR'] = 'This variable is set by REXXTRY'"

say .local['BS.MYVAR']

/* you can also use the following code to read the variable          */ /* But be aware of the search order for environment symbols in       */ /* Object REXX! */ say .BS.MYVAR </PRE> Note: "To avoid conflicts with future REXX-defined entries, it is recommended that entries you place in the program local environment or in the global environment include a least one period in the entry name."

Use the environment .environment for variables global to all REXX programs. Example:  /* create a variable global to all REXX programs                     */ .environment['BS.MYVAR'] = 'This is a global variable'

/* start a REXX program in another process to show that it works     */ "cmd /c rexxtry say .environment['BS.MYVAR'] "

"cmd /c rexxtry .environment['BS.MYVAR'] = 'This variable is set by REXXTRY'"

say .environment['BS.MYVAR']

/* you can also use the following code to read or change the variable */ say value( 'BS.MYVAR',,'' )

call value 'BS.MYVAR', 'Value set using the VALUE function', '' </PRE> Note that the environment .environment contains a lot of default variables that you should NOT change. To get a list of all existing variables you can use the following code:  /* show all variables in the environment .environment                */ do i over .environment say 'Variable "' || i || '" is "' || .environment[i] || '"' end /* do i over .environment */

To check if a variable is already defined, you can use the following code:

/* check if a variables is defined in the environment .environment   */ globalVar = 'BS.MYVAR' if .environment[globalVar] = .NIL then say 'Environment symbol "' || globalVar || '" is not defined.' else do       say 'Environment symbol "' || globalVar || '" is defined;' say 'the value is "' || .environment[globalVar] || '".' end /* else */ </PRE> Note: "To avoid conflicts with future REXX-defined entries, it is recommended that entries you place in the program local environment or in the global environment include a least one period in the entry name."

Use the directives ::REQUIRES and ::ROUTINE to implement external routines. Use the keyword instruction RAISE to raise a condition in the calling routine. This is very handy to avoid endless return code evaluations.

Example:  /* -- */   /* save this code in the file 'TEST1.CMD'                             */

parse source. . thisFile say 'This is ' || thisFile || '.'

/* install an error handler for user condition 4 */ call on user 4 name ErrorRaised

say 'Now calling TEST2.CMD. TEST2.CMD will raise an user condition ...'

/* now call TEST2.CMD; test2.cmd will raise an   */ /* user condition                                */ call TEST2.CMD

say 'Now ending the test program.' exit

/* -- */   /* simple handler for the user condition 4                            */ ErrorRaised: say say '*** start of user condition handler ***' say say 'Condition "' || condition( 'C') || '" raised in line ' || , sigl || '.' say 'The condition description is "' || condition( 'D' ) || '".' say 'Additional information is "' || condition( 'A' ) || '".' say say '*** end of user condition handler ***' say return

/* -- */   /* save this code to TEST2.CMD                                        */

parse arg thisArgs

parse source. . thisFile say ' This is "' || thisFile || '".' say '  Called with "' || thisArgs || '".'

/* raise a user condition to the caller          */ raise user 4 , Description 'This is a user error' , Additional 'This is additional information' exit /* -- */ </PRE> And there are a lot of very useful new functions in the REXXUTIL.DLL. See New REXXUTIL functions in Object REXX for an overview of the new functions. This section also contains information on how to use the new REXXUTIL.DLL with Classic REXX.

Again, read the section MIGRATION in the Online-Help of Object-Oriented REXX - else you might miss some of the new features.

Hints for using Object REXX
This section contains some hints for using the new features in Object REXX.

Using more than one variable pointing to a stem

In Object REXX you can have multiple variables pointing to the same stem.; e.g. after issuing b. = a. b. points to the same variable as a.. Note that this is not a copy operation - it's more like a shadow on the WPS. For example if you change the value b.4 the value al.4 is also changed:  /* first fill the stem a.                        */ do i = 1 to 10 a.i = i*i end /* do */ /* let b. point to the same stem as a.           */ b. = a.                   /* change an entry using b.                       */ b.4 = 17 /* examine the fourth entry of a.                */ say "a.4 is" a.4 /* -> "a.4 is 17"                                */ </PRE> BTW: Dropping a. in this example does not discard the variable because b. points to the same variable.

Using Classic REXX if Object REXX is the default REXX
Sometimes it's necessary to test your REXX programs under Classic REXX and under Object REXX. Therefore it would be useful to use both interpreters without rebooting each time you change the REXX interpreter.

This is possible under OS/2 if you use the following approach:
 * use Object REXX as your default REXX interpreter without the Workplace Shell support (i.e., do NOT use WPSINST)!!!
 * create a WPS Object for the REXX program REXXTRY.CMD with:
 * {|class="wikitable"


 * Title||Start Object REXX
 * Path and filename||*
 * Parameters||/C REXXTRY.CMD
 * Working directory||\
 * }
 * create a new directory
 * extract the REXX program below from this INF file and save it in the new directory with the name STARTCR.CMD
 * ensure that your LIBPATH contains the entry '.;' before the entry \OS2\DLL (You need to reboot once if you must change your LIBPATH)
 * copy the REXX.DLL from Classic REXX into the new directory with STARTCR.CMD (This is the DLL named CREXX.DLL in \OS2\DLL if Object REXX is currently the default REXX interpreter)
 * create a WPS Object for the REXX program STARTCR.CMD with the following setup
 * {|class="wikitable"
 * copy the REXX.DLL from Classic REXX into the new directory with STARTCR.CMD (This is the DLL named CREXX.DLL in \OS2\DLL if Object REXX is currently the default REXX interpreter)
 * create a WPS Object for the REXX program STARTCR.CMD with the following setup
 * {|class="wikitable"

Now you can select the REXX interpreter to use:
 * Title||Start Classic REXX
 * Path and filename||*
 * Parameters||/K .\STARTCR.CMD
 * Working directory||the directory with STARTCR.CMD
 * }
 * Working directory||the directory with STARTCR.CMD
 * }
 * }

Start the WPS object "Start Classic REXX" if you need Classic REXX or start the WPS object "Start Object REXX" if you need Object REXX. All other programs started after one of these objects will use the REXX interpreter that is loaded.

Note that it is only possible to use one REXX interpreter at a time. To switch the REXX interpreter either from Classic REXX to Object REXX or vice versa you must first close all sessions using REXX, wait a bit and start the WPS object using the other REXX interpreter. Note also, that you must unload all DLLs using REXX (for example REXXUTIL.DLL) before switching the interpreter.

And last:

Please be aware that this method does not work in all cases! /* STARTCR.CMD                                                     */ /* sample REXX cmd to start a session using Classic REXX if OO REXX */ /* is the default REXX interpreter                                 */ /*                                                                 */ /* To use this cmd you must                                         */ /* - ensure that your LIBPATH contains the entry '.;' before the   */ /*  entry \OS2\DLL                                                 */ /* - copy the REXX.DLL from Classic REXX into the directory with   */ /*  this CMD                                                       */ /* - create an Object for this CMD with                            */ /*  Path and filename = *                                          */ /*  Parameters = /K .\STARTCR.CMD                                  */ /*  Working directory: the directory with this program             */ /* - ensure that no other session using REXX is running            */ /*                                                                 */ /* Usage: STARTCR [startDir]                                        */ /*                                                                 */ /* Where: startDir = working directory to use                       */ /*                  Def.: use directory with STARTCR.CMD           */ /*                                                                 */   parse arg StartDir say say say 'Starting a CMD session using the Classic REXX interpreter ...' say parse version interpreterVersion rest say 'The current REXX Interpreter version is ' || , interpreterVersion rest if interpreterVersion <> 'REXXSAA' then do    say '07'x     say 'Error: Cannot load the DLL with the Classic REXX interpeter!' say say 'Hint: Close all sessions using REXX, wait a minute and try' say '      again.' say '@pause' 'exit' end /* if */ else do    if StartDir <> '' then call directory StartDir say 'Current working directory is "' || directory || '".' say end /* else */ exit 0 (see also How to switch from Classic REXX to Object Rexx and vice versa)

Using Object REXX and the WPS
It seems that the WPS part of Object REXX never finishes its initialisation part and therefore has to be forced to do so to use the WPS classes in Object REXX programs. You can force the finishing of the initialisation by opening the Template folder for example - but that's no general solution of course.

To test if this bug occurs on your system, simply issue the command rexxtry say .wps If the output is .WPS then the error occurs on your system.

As a workaround to the WPS Server not getting initialized you can put a program object with the following settings in your startup folder: Program = c:\os2\wpsinst.cmd Parameter = + This may help ensure that the WPS part of Object REXX finishes its initialization at startup (note that not all who have tried this have reported success).

[Fixed in OBJREXX 6.00 25 Mar 1998 and newer versions]

It seems that the WPS part of Object REXX is incompatible with some other WPS Extensions, for example Object Desktop. In this case, you must decide either to use the WPS part of Object REXX or the WPS Extension.

To use Object REXX with Object Desktop you only need to deinstall the "Enhanced Folder" Component of Object Desktop - that's the only part of Object Desktop that is affected by Object REXX.

If you want to keep Object Desktop's Enhanced Folder on your desktop, all you need to do is to NOT install the WPS classes of Object REXX (these are the classes initialized by the wpsinst.cmd program). Object REXX will still be loaded and you'll still have access to all Object REXX commands, except the ones involving the new classes.

Writing OS-independent programs in Object REXX
If you want to write operating system independent programs using Object REXX, you should keep the following differences between Object REXX for OS/2 and Object REXX for Windows 95 / Windows NT in mind:
 * in the Windows version, the environment is process-local
 * in the Windows version, host commands are executed in a new process
 * on Windows 95, host commands always return 0
 * Object REXX on Windows does not support SOM, and the DBCS support is not documented
 * in both versions (Win95 and OS/2) you should use VALUE( envVarName, envVarValue, 'ENVIRONMENT' ) to set or read environment variables
 * in both versions (Win95 and OS/2) you should use VALUE( envVarName, .NIL, 'ENVIRONMENT' ) to delete an environment variable

More information about Object REXX for the Windows platform can be found at the Object REXX home page (see Internet - Web Pages).

Adding Object REXX Support to a maintenance partition
To add Object REXX to a maintenance partition (created with BOOTOS2 for example) do the following:
 * 1) Create the maintenance partition without REXX support. (In the statements below I assume that F: is the drive letter for the maintenance partition)
 * 2) Create a new directory on the maintenance partition (e.g. F:\OREXX)
 * 3) Add the new directory to the PATH, LIBPATH and DPATH statement in the file F:\CONFIG.SYS
 * 4) Copy the following files from your existing OS/2 partition with activated Object REXX support to the new directory F:\OREXX:
 * OS2\DLL\NAMEREXX.DLL
 * OS2\DLL\PMREXXIO.DLL
 * OS2\DLL\REXX.DLL
 * OS2\DLL\REXXAPI.DLL
 * OS2\DLL\REXXCRT.DLL
 * OS2\DLL\REXXINIT.DLL
 * OS2\DLL\REXXSC.DLL
 * OS2\DLL\REXXSOM.DLL
 * OS2\DLL\REXXUTIL.DLL
 * OS2\DLL\REXXWPS.DLL
 * OS2\DLL\SOMD.DLL
 * OS2\DLL\SOMSEC.DLL
 * OS2\DLL\SOM.DLL
 * OS2\DLL\SOMEM.DLL
 * OS2\DLL\SOMIR.DLL
 * OS2\DLL\SOMS.DLL
 * OS2\DLL\SOMTC.DLL
 * OS2\DLL\SOMU.DLL
 * OS2\DLL\SOMUC.DLL
 * OS2\SYSTEM\REX.MSG
 * OS2\SYSTEM\REXH.MSG
 * OS2\REXXTRY.CMD
 * OS2\PMREXX.EXE
 * OS2\REXXC.EXE
 * OS2\REXX.ICO
 * OS2\REXX.IMG
 * OS2\WPREXX.IMP
 * 1) Copy the file BOS2REXX.EXE from the BOOTOS2-Package to the new directory.
 * 2) Add the line <tt>RUN=F:\OREXX\BOS2REXX.EXE</tt> to the file F:\CONFIG.SYS
 * 3) Reboot from the maintenance partition and test the REXX support (for example with REXXTRY)