TSO-REXX Frequently Asked Questions

Источник: erroneousbee.github.io

Скачать в формате HTML

 

Last-modified: 01 Aug 2004
Version: 1.4q

TSO-REXX Frequently Asked Questions


Contents

  1. Introduction
    1. Changes made to the FAQ at this release
    2. To Do
  2. Using Rexx.
    1. How do I Run My Rexx Exec.
    2. How do I allocate Exec libraries and other datasets to my TSO or ISPF session.
    3. Where can I find Rexx manuals and documentation.
    4. How do I find or access the current level of a GDG.
    5. How do I access data in control blocks such as jobname.
    6. How can I access a calling execs variables.
    7. How do I implement job control with (and for) Rexx
    8. Where are the Standard Rexx I/O functions.
    9. How do I replicate CLIST's WRITENR functionality.
    10. Why does Outtrap() not trap all output.
    11. EXECIO, why does it require 'Enter' more than once.
    12. How do I List Datasets and PDS members.
    13. How do I access data held on the JES spool.
    14. What do unusual return codes such as -3 and 0196 mean.
    15. How do I pass parms to my ISPF Edit macro.
    16. Why are my procedure arguments in upper case.
  3. Rexx and Different environments.
    1. OS/390 Unix Systems Services
    2. ISPF Services
    3. ISPF Services in Batch
    4. Load Modules (I.e. Assembled programs)
    5. SQL
    6. Console Command Environment
    7. Rexx aware editors
    8. Rexx Compilers
    9. Using Rexx as CGI programs with the Web Server.
  4. Style.
    1. What are the most effective ways to use comments?
    2. EXECIO.
    3. What case should I program in, upper or lower?
    4. Functions and procedures
    5. Logical (Boolean) variables and functions.
    6. Symbolic names.
    7. Internal and external calls
    8. How can I improve performance of my exec?
    9. How can my systems programmer improve performance?
    10. What obscure coding tricks are there?
    11. Using Non-Alphanumeric characters.
    12. Are there any other style hints?
  5. Appendices.
    1. Code Examples
    2. Other Sources of Information
    3. Subscribing to the TSO-REXX List
    4. Glossary

Introduction

This FAQ is for REXX/MVS, that is, REXX for IBM mainframes (MVS, OS/390 and VM). It is unofficial and based on questions asked in the TSO-REXX listserv group based at LISTSERV@VM.MARIST.EDU.

As is the case with all FAQs, this document is a work in progress. Additions, corrections, and comments are very welcome. Please send any correspondence to webspace@neilhancock.co.uk

If this FAQ or the List Archivedoes not answer your Rexx questions, do not email me, but instead subscribe to the TSO-REXX List where your questions will be usually be answered promptly by many knowlegable people.

Thanks for contributions from:
Jon Alexander, Stephen Bacher, Tim Larsen, John McDonald, and all those who posed and answered questions on the mail list.

In coding examples, screen output is indicated by ==> within a comment.
Latest edits are marked in bold in the contents section.

Changes made to the FAQ at this release.

  • 1.4q - Update links and whatnot, add EXECIO tip, add PARSE ARG.
  • 1.4p - New coding trick. Assorted submissions and updates (Thanks!).
  • 1.4o - Assorted minor things from the list, edit macro parms.
  • 1.4n - R.I.P. Deja. ReMarQ too. Code examples moved external to FAQ.
  • 1.4m - Performance updated. Coding tricks added.
  • 1.4l - LIBDEF/ALLOC/ALTLIB question and sample.
  • 1.4k - Subscription Info added.
  • 1.4j - Rexx in Batch added. More info added to. RXSQL noted. Docs question added.
  • 1.4i - More reformatting, Some answers reviewed and amended.
  • 1.4h - Minor formatting.
  • 1.4g - Corrections submitted by Peter Tillier. Reinstated rexx information sources

To Do

  • Is Posix (USS) info still up to date and correct?
  • Inline JCL technique with IEBGENER to temp PDS.
  • XMLify.
  • Scare up some useful google keywords.

Using Rexx.

How do I Run My Rexx Exec.

Most of this is described in the User Guide, or in the guide for the appropriate environment.

For the TSO environment:

  • Make sure the Exec starts with the /* REXX */ comment.
  • TSO EXEC 'hlq.your.dsn(member)' runs Rexx in exactly the same way as it would a CLIST.
  • TSO EX yourlib(member) E uses a suffix of EXEC and the TSO profile prefix to form a full DSN of 'your_prefix.yourlib.EXEC(member)'
  • The SYSEXEC concatenation can be used to run execs using TSO execname. The SYSPROC concatenation will do just as well, but is really intended for CLISTS.
  • TSO %execname parameter_string allows you to pass a parameter string to the exec, note that this string is only a single argument, not multiple arguments as can be used when calling from within an exec. The '%' instructs TSO to not use loadlib concatenations when constructing the search path.

For running Rexx in batch:

  • Create a batch environment for the Exec to run in with PGM=IRXJCL, PGM=IKJEFT01 or PGM=IKJEFT1B
  • IRXJCL is a straight batch environment
  • IKJEFT01 and IKJEFT1B are the TSO in batch programs.
  • Put the Rexx exec libraries in the SYSEXEC DD concatenation.
  • Call your exec via the PARM='yourprog' or as input in the SYSTSIN DD
  • See the Code Examples appendix for examples.

For more information, RTFM.

How do I allocate Exec libraries and other datasets to my TSO or ISPF session.

  • ALTLIB ACTIVATE can be used to dynamically add an Exec or Clist library to the search path. I don't know whether these ATLIBs are stacked in the way LIBDEFs can be.
  • LIBDEF can be used to dynamically allocate other libraries, such as ISPF panel libraries. Use the STACK option to avoid corrupting the environment for nested calls to applications. An example LIBDEF/ALTLIB is in an appendix.
  • Your allocations at logon are controlled by the JCL procedure used to initiate your TSO environment. This is called your TSO LOGON PROCEDURE, and it will be under the control of your systems programmer.
  • To override the LOGON proc and allocate your own SYSEXEC, ISPPLIB, etc concatenations, the TSO ISRDDN command can create a handy CLIST to use as your base. To generate the TSO ALLOC commands needed to replicate your current allocations, type the CLIST command on the command line. Edit and save the Clist created, and run it at logon (outside of ISPF) to allocate your own environment.

Where can I find Rexx manuals and documentation.

IBM Online Library in HTML and PDF form, clisk on the 'elements and features' links.

The Rexx Reference and Guide are in the TSO section.
ISPF interfaces ( E.g. ISPF Edit Macro commands) are documented in the ISPF section.
There is a manual for Rexx in the Unix System Services environment.
Interfaces for other applications will be documented with that application.

How do I find or access the current level of a GDG.

Use Outtrap() on the output of TSO LISTCAT.

See RXGDGV() in the appendices.

How do I access data in control blocks such as jobname?

Use the Storage() function to extract the data from control blocks.

/* REXX Get taskname from TCB */
cvt = storage(10,4) /* FLCCVT-PSA data area */
tcbp = storage(d2x(c2d(cvt)),4) /* CVTTCBP */
tcb = storage(d2x(c2d(tcbp)+4),4)
tiot = storage(d2x(c2d(tcb)+12),4) /* TCBTIO */
say strip(storage(d2x(c2d(tiot)),8)) /* TIOCNJOB */
/* I have lost the original author of this code.
Thanks whoever you are - NWH */

How can I access a calling execs variables.

Rexx doesn't allow an exec to access the symbols used by another exec. A classic example of this is creating an exec to sort the contents of a stemmed symbol.

Some suggested methods are:

  • Use ISPF variable pools to pass values across execs
  • Pass all symbols as arguments and returned values (I.e. Use PARSE ARG args and RETURN value).
  • Create your own function in assembler to pass stems across. Refer to the IRXEXCOM macro interface in the IBM Rexx manuals.
  • Generate executable code as the returned string and use INTERPRET on it to make the variables available in the external program.
  • Use the stack, see NEWSTACK, DELSTACK, PUSH, PULL, QUEUE, etc, in the manual.

How do I implement job control with (and for) Rexx

The obvious answer is to use JCL condition code processing, possibly running a Rexx exec at the end of each job to perform error recovery, message issuing, etc. See the ISPF in batch discussion for more information.

Another solution if the JCL is part of a single application is to pass checkpoint information between an application server and executing tasks in a dataset. E.g. A server may create and prime a checkpoint dataset, and then submit a job that updates the dataset with diagnostic information that the server will interrogate when the job has finished.

Jon Alexander writes:

SDSF offers a batch interface. I don't have the documentation here at home, but it can be found as a whole chapter in the SDSF Users Guide (Chapter 15 seems to stick out in my head). I can't swear that you can "interrogate" sysout data or condition codes from this interface, so you will need to validate this also. If you can, then you can build a set of REXX to call this interface from within a TSO environment.

HOWEVER, you must still consider how you will implement this facility. Let's say that you want to monitor the majority of your batch work. You must "schedule" this routine to run after every single batch task, inputting the jobname and JES number for the SDd. This is not an easy task from within CA-7...

CA's (formerly Legent's) JOBTRAC scheduling product provides a facility to run a REXX routine at the termination of each batch event, and also provides an interface to their "SDSF counterpart" called SYSVIEW/E which will definitely allow you to display sysout text and conditions codes. They distribute a sample REXX with their maintenance tape that provides most of the code for what you would want to do, but I'm not sure you really want to change your production scheduler just to solve this one problem.

So, to make a long answer even longer, I would recommend that you get away from looking at the SDSF data, and possibly try to exploit one of the following:

- Implement the SHARE sample of an IEFACTRT exit that generates WTOs with step condition code results, and use your local automation package to intercept the WTO and perform an automation process.

- See if you can exploit CA-11 or CA-7 as far as a user exit point or extracting their database info, and take actions based on that

- Implement your own IEFACTRT exit (there are a lot of samples out there) to perform the actions you need (yeah, I know, I'd rather do it in REXX also)

- Get with your CA rep and see if CA-7 will be incorporating any of the REXX technology that CA-JOBTRAC currently uses and possibly a time frame on the availability

Where are the Standard Rexx I/O functions

Linein(), Lineout(), Stream() etc. have not been implemented in MVS/Rexx *as far as I am aware*. I/O can be done using the functionality of EXECIO.
A function package giving I/O functions for the OE environment only is available from IBM's Download website.

How do I replicate CLIST's WRITENR functionality?

The Rexx say command places a carriage return and line feed at the end of each say command execution.
Some suggestions for creating a say command that behaves in the same way as CLIST's WRITENR are:

Use a CLIST call.

Use CHAROUT(). However this I/O function is not implemented in MVS/Rexx.

The free utility XWRITENR (and also XPROC) are available from ftp.mackinney.com and the CBT tape

Why does Outtrap() not trap all output

The Outtrap() function does not trap output written directly to the screen by the TPUT macro, but only traps output from the PUTLINE macro. Use PUTLINE when writing assembler programs, and your program will be of more use in a Rexx environment.

TSO Session Manager can be used to trap and manage all output that is normally written to the screen. Session Manager may require some setting up by your Systems Programer, who will have to look things up in the TSO manuals.

EXECIO, why does it require 'Enter' more than once

When using "EXECIO * DISKW DDNAME", Rexx will continue pulling input from the stack and from terminal input (depending on the users TSO PROMPT() value) until it gets a null line. So a users will have to hit enter once to generate a null line.

Solutions are:

  • Queue a null line before writing data from the stack.
  • Use the Queued() built in function instead of '*'.
  • Only EXECIO the required number of lines from terminal input.
    E.g.
    "EXECIO 1 DISKR DDNAME (FINIS)" will only read a single line from the stack or terminal input.
    "EXECIO "queued()" DISKW DDNAME (FINIS)" will write the entire stack without requiring a null line to terminate output.
  • See also EXECIO under Style.

How do I List Datasets and PDS members.

There are various methods for listing datasets and PDS members.

  • LISTDS - TSO dataset list command. Use the MEMBERS subcommand to get the members of a PDS.
  • LM utilities - ISPF library management utilities. Use LMMLIST with LMINIT to list the members. See ISPF services manual for details.
  • EXECIO of the PDS directory. A PDS directory can be allocated (LRECL=256) and read with EXECIO. This method does not work with PDSEs and cannot be guaranteed to work in the future.
  • Create your own access routines in assembler using the BPAM access method.
  • CSI - Catalog Search Interface. A high performance catalog search routine shipped by IBM. Use the LINKMVS environment to use it.
  • FDR - Dataset management tools. Innovations FDR package supplies a tool for reporting upon datasets (FDREPORT) that can be customised for use with Rexx under TSO.

How do I access data held on the JES spool.

JES3 sites can extract data off of the JES3 spool using E-JES.

The TSO STATUS command gives limited information about jobs on the spool. The TSO OUTPUT command can reteive some data from the spool. The  TSO CANCEL command can be used to stop jobs, and TSO SUBMIT to them.

Brave souls can try the JES interface macros. Try looking at the CBTTape for examples.

JES2 users can brave the perils of SDSF in batch or other third party products

What do unusual return codes such as -3 and 0196 mean?

Basically they are either decimalised abend codes or indicate a problem with the environment.

E.g. 0196 is decimal for hex 0C4, -3 indicates external environment not found. This is explained further in the Rexx manuals.

How do I pass parms to my ISPF Edit macro.

From command line calls use ISPEXEC "MACRO (parms)"

From ISPEXEC EDIT call use VPUT and VGET

Why are my procedure arguments in upper case.

It may be because you are  using ARG instead of PARSE ARG
PARSE ARG arg01 , arg02 /* Arguments parsed with no uppercasing */
ARG arg01, arg02 /* arguments are uppercased */


Also note that the command line on most panels has CAPS(ON) set. Notable execptions (where case is preserved)
are ISPF edit and ISPF 6.




Rexx and Different environments.

OS/390 Unix System Services

address SYSCALL and address SH give you the ability to run execs that use OpenEdition MVS services. From outside of the shell use the Syscall() function to activate the environment.

See Manual 'Using REXX to Access Unix System Services.' (or whatever its called this week).

There are some pitfalls in this environment:

  • External names are uppercased. So RXGDGV() will work whilst rxgdgv() will fail not found.
  • Newly created executables may need to be made executable with a 'chmod' command.
  • The syscall environment initialises some variables which may be the same as those you use.
    See MTM_RDWR in the mount example below.
  • The method of passing variables into the environment calls is different from 'classic' MVS calls.
    E.g. mount example.
    /* REXX mount example */
    path = /tmp/testpath/mountpoint
    dsn  = 'HFS.TEST.MOUNT'
    address SYSCALL "mount (path) (dsn) HFS "MTM_RDWR
    say 'Rc='rc 'Retval='retval 'Errno='errno
  • Command return codes are not exactly passed via the usual rc symbol, but through new symbols retval and errno.
  • The syscall environment initialises __argv. and __environment. stems.

ISPF Services

address ISPEXEC is detailed in the REXX and ISPF manuals. Examples can be found using the MODELcommand in the ISPF editor.

Some specific areas covered by ISPF services are:

  • PDS interrogation and manipulation using the LM utilities E.g. LMCOPY to copy datasets and PDS members.
  • ISPF table access with the TB utilities. E.g. TBSCAN to find a table record.
  • Edit and Browse with ISPF EDIT and ISPF EDIT macros.
  • Panel services with DISPLAY, ADDPOP, and others.

There is a known problem in ISPF table services, where a freed table may not be truly free (its ISPF trying to be eficient), causing the TSO FREE to fail. To avoid the problem, open a fake table, e.g. ADDRESS ISPEXEC "TBOPEN FAKETAB LIBRARY(FAKELIB) NOWRITE"

ISPF Services in Batch

ISPF services can be used by a batch job, although panel display and user interaction may be a little limited. Use ISPSTART running under IKJEFT01 to initiate the ISPF environment, see the manuals for this.

There can be some difficulties when ISPF return codes >=8 cause ISPF to terminate. Firstly, the termination will pass a zero RC back to the batch job, most terminations can be controlled using ISPEXEC CONTROL, see below for an example. Secondly, the ISPF RC has to be passed back via profile variable ZISPFRC, as the Rexx RC is ignored by ISPF.

Lastly, BDISPMAX and BREDIMAX errors, often caused by ISPF trying to display a panel (which it can't in batch, of course) don't appear to be controlled by CONTROL, and cause abending programs to return zero RCs to the batch job. I suggest setting RC=4 as normal completion, and treating RC=0 as an abend in the job control.

/* REXX - Controlling ISPF errors example */
address ISPEXEC                                                    
                                                                   
/* If an ISPF error occurs, then return control to the Rexx */     
say 'Setting CONTROL ERRORS RETURN'                                
"CONTROL ERRORS RETURN"                                            
                                                                   
/* Call LMFREE for a nonexistent dataid. Returns rc = 10 ,         
   which is not usually terminated by ISPF anyway */               
say 'Calling LMFREE'                                               
"LMFREE DATAID(EEYORE)"                                            
say 'LMFREE rc='rc                                                 
                                                                   
/* Call LMOPEN with a bad parameter. Returns rc = 20 ,             
   which causes ISPF to cancel and is thus influenced by           
   the CONTROL ERRORS RETURN */                                    
say 'Calling LMINIT'                                               
"LMINIT DATAID(PIGLET) DATAFISH('HADDOCK')"                        
say 'LMINIT rc='rc                                              
                                                                   
/* Call EDIT with a non existent edit macro. I cannot get this to  
   return control to the Rexx, and it terminates here with the     
   'ISPP330 BDISPMAX exceeded' message. The JCL completion code    
   is an not very useful '00' */                                   
say 'Calling EDIT'                                                 
"EDIT DATASET('NWH.GENERAL.EXEC(ISPFTEST)') MACRO(MCGYVER)"        
say 'EDIT rc='rc                                                   
                                                                   
/* Pass the current value of rc back to job control */             
zispfrc = rc                                                       
address ISPEXEC "VPUT (ZISPFRC)"                                   
                                                                   
/* This EXIT return code will not make it back past ISPF, but it   
   would have if just TSO was involved. As ISPF is active, it      
   is overridden with the value of ZISPFRC, and a message appears  
   in the SYSTSPRT output */                                       
exit 12                                                            

See the Appendices for an example JCL procedure.

Load Modules (I.e. Assembled programs)

address MVS, LINKMVS, ATTCHMVS, etc. are detailed in REXX manuals. These allow the user to pass parameter lists into the load module.

Creating and accessing Rexx external functions using compilers and assembler is also documented in the Rexx manuals. Examples of using the IRXEXCOM interface are on the CBTTape.

It is well worth understanding APF authorisation for load modules. Read the manuals for full details, but briefly, APF authority abends are usually a result of the loss of an authorised environment caused by 'dirty' programs in the user address space or non APF authorised libraries in the STEPLIB concatenation.
Some items to look out for are:

  • The SETPROG console command.
  • TSOCMDxx PARMLIB member for TSO command authorisation.
  • Assembling programs with AC=1
  • 'Problem' programs. A problem program is any non APF authorised program.

SQL

DB2 for zOS has a Rexx interface, introduced with DB2 version 5, and described in the latest IBM DB2 manuals. The interface supplies the DSNREXX  environment (as in ADDRESS DSNREXX "EXECSQL SELECT FROM yadda.yadda"). 

Console Command Environment

Kevin McGrath writes:
MVS TSO/REXX will issue MVS commands using TSO's MCS console support. Look in Appendix "D" and "E" of the TSO/E REXX Reference. You must run under TSO, either interactive or batch. REXX provides a host command environment. I've attached a simple example.

/* REXX under TSO */
Parse arg command
Address TSO "CONSOLE ACTIVATE"        /*  start a console session.   */
Address CONSOLE "CART(REXXMVS)"       /*  Establish a CART token.    */
Address CONSOLE command               /*  Issue command.             */
getcode = GETMSG('prtmsg.','sol','REXXMVS',,60) /* get response      */
If getcode = 0 then                   /*  got response?              */
  Do i = 1 to prtmsg.0
    Say prtmsg.i                      /*  do something with response */
  End
Else
  Say "GETMSG ERROR RETRIEVING MESSAGE.  RETURN CODE IS" GETCODE
Address TSO "CONSOLE DEACTIVATE"      /* stop the console session.   */
Exit

Neil Hancock adds:
Your security setup may restrict use of the console environment. The relevant RACF profile is listable using RLIST TSOAUTH CONSOLE ALL. There are also user profile OPERPARM segments involved.
Note that solicited messages are not necessarily available from subsystems such as JES, RACF, etc. You may have to issue the command, and process unsolicited messages to retrieve information from subsystem commands.

Rexx aware editors

ISPF edit supplies a color hilite facility that is a must if it is available. ISPF edit also supplies models for ISPEXEC services, although you may have to adjust the style to your own. Many *nix editors understand Rexx, and Kate (part of KDE) uses a simple XML markup that allows highlighting of any language.

Rexx Compilers

There are Rexx compilers available, including one from IBM. Ask on the newsgroup for Information on what is currently available.

A freeware utility that converts Rexx source into a tokenised form (as is used by LLA / VLF) is rumoured to exist.

Using Rexx as CGI programs with the Web Server.
Rexx can be used to create CGI programs that access MVS based resources for Web browser based users.
All the Rexx program is doing is to write a dynamic html page to the POSIX sysout stream. This can be done using the Rexx say command.
The BonusPak supplies two shell commands; cgiutils for writing http headers (for passing html versions and cookies to the browser, etc), andcgiparse for reading environment variables, including forms data.
For the GET CGI method, the CGI parameters are available in environment variable QUERY_STRING.
Environment variables are available in the Rexx stemmed variable __environment. and the parms can be extracted with the parse command. Note that use of interpret to extract parms is insecure. A user may manage to pass raw rexx commands into the CGI exec.
For pre OS/390 V1 R3 systems, the Rexx say command wraps at 80 characters. Use SYSCALL "write .." instead.
Some browsers convert special characters to an escaped notation.
Access to Classic MVS resources can be done by using Rexx function shcmd to trap the output from commands run under TSO.
Commands can be run under TSO using the tsocmd shell command.
Both shcmd and tsocmd are available from the IBM Kingston ftp site.


Style.

These style hints are mostly aimed at programmers from other MVS and TSO languages, especially CLIST. Your site may have its own conventions and these should take precedence over anything said here. You may also prefer to work in your own style, and that too is perfectly acceptable.

What are the most effective ways to use comments?

  • You must have REXX as part of the first line of Rexx, which must also be a comment. It seems sensible to take the opportunity to say something about what the program is about in this opening comment.
  • Only open and close a comment that goes across many lines once. There are no real performance benefits in this, only your code will contain fewer extraneous characters, and this will help you see the wood for the trees.
  • It makes little sense to comment every line no matter how trivial. Rexx isn't assembler and most commands are self evident.
  • Consider commenting what is being terminated at an END statement, particularly select statements and do loops that cover more than one screenfull of program.
  • Don't squeeze comments down the right hand side of the code.
    This restricts the insertion of maintenance code, and more importantly, the restricted space causes the comments to become abbreviated past the point of becoming cryptic.

EXECIO.

Close any brackets. It makes the code look neater.

Use address MVS "EXECIO" in preference to address TSO "EXECIO". You then avoid unexpected occurances of rc(-3) when you run the exec under IRXJCL instead of IKJEFT01.

Tim Larsen writes:
With DISKW - use the queued() built-in, instead of a null-record and "*".
If you forget the null-record ("QUEUE'') you will get a prompt, depending on the TSO PROFILE!
E.g. "EXECIO" queued() "DISKW DDNAME (FINIS)"

 

What case should I program in, upper or lower?
Readability can be improved by writing Rexx as if it were a human language and not as if it were JCL or assembly code. Avoid having the entire program in upper case, this is known as shouting and does nothing to help readability. It is common to write commands addressed to external environments in upper case, and to use mixed case for Rexx commands and symbols.

Functions and procedures

If a call returns a value that is not a return code, it may be better to call it as a function rather than a procedure.

E.g. Consider: if Linein(needle,haystack) then say 'Hello'
versus: call Linein needle,haystack
if result = 1 then say 'Hello'

Make the names of functions and procedures meaningful

Logical (Boolean) variables and functions.
Symbols with a value of 1 are logically true, 0 logically false. All other values give an error when tested for logical truth.

Functions may return logical values, but don't confuse functions returning logical values with those that return numeric values.

E.g. if Datatype(var,'NUM') then say 'Hello' /* Is OK */
if Pos(needle,haystack) then say 'Hello' /* May abend */

Symbolic names.
Symbolic (or variable) names are not limited to eight characters as in some other languages, this allows more scope for meaningful names. The names used will depend largely on the programmers preferences and style of code writing, but here are some suggestions to consider.

Use long variable names and separate words with an underscore.
E.g. Table_name = 'MAILTAB'
Underscores are a useful way of hiding variables from ISPF.

Use i,j,k,etc as loop control symbols. Execs that do not hide symbols using the procedure command may suffer from reuse of loop control symbols.
E.g. do i = 1 to 10

Prefix variable names with specific characters to show the type of data the variable contains, such as logical, numeric, flags, etc.
I often prefix variable names with '?' to stop variable substitution occurring if stemmed variables.
E.g.
count = 2 ; year = '1945' ; stem.count.?year = year /* STEM.2.?YEAR = '1945' */
Bboolean = 1; if Bboolean then say 'hello'
Fflag = 'A'; if Fflag = 'A' then say 'hello'
@address = STORAGE(something)
#numeric = 10 + 12

See also the section on using non-alphanumeric characters

Internal and external calls
Rexx allows calls to be made to execs that reside outside of the currently executing exec. This allows programmers to use prewritten modules, and to split large amounts of code up into manageable chunks. However there is a performance overhead in doing this. Here are some other points to consider:

Some aspects of an exec may be relevant to other execs, and you may wish to supply a routine that is usable by other execs performing similar tasks. Consider creating external routines that supply a robust and generic service (e.g. catalogue listing, mail sending, interfaces to data, etc.) so that other developers may benefit from reduced development time.

Exit external routines with RETURN rather than EXIT. This facilitates the copying of the routine into an exec for performance benefits.

Don't use an external call to a routine that is called recursively, the resulting I/O will drop the exec performance.

The inability to share variables across external procedures may lead you to choose to use internal procedures over external ones .

How can I improve performance of my exec?
In general, using an appropriate and well tuned algorythm/solution is better than using specific 'hacks'.

Address external environments directly using ADDRESS. For multiple calls to the same environment set the environment before entering into the multiple calls.
E.g. address TSO "ALLOC FI(...) DA(....) SHR"

Internalise external routines that are called repeatedly. Then they won't have to be loaded and tokenised by the interpreter every time they are called.

Use single letter vars for loop control. TSO/Rexx has some fast-path optimisation for code such as DO I = 1 to 1000.

Using the string functions ( POS(), LEFT(), etc ) on a single very large string, rather than searching through many stemmed vars.

Use terse coding, avoid unnecessary commands.
E.g. Consider:
if symbol = 'FRED' then say 'Hello'
Versus:
if symbol = 'FRED' then ,
do
say 'Hello'
end

Embed function calls within each other rather than build up a variable over several lines of code.
E.g. junk = Left(Overlay('A',Userid()||Random(999),1,1),8)

Use a compiler. Expect 100% improvement for number maniplulation (adding and whatnot), 30% for string manipulation (using the string functions), and no real improvement when interfacing to external environments.

Performance test your code using the SYSVAR('SYSCPU') function and not the TIME() function.

How can my systems programmer improve performance.

Use VLF. See IKJEXEC in Init and Tuning manual. Note that VLF is not used for external procedure and function calls, only the EXEC interface, like what is used when you TSO EXEC MYLIB(MYEXEC).

Use the environment tables in SAMPLIB if you have environments that are not part of the distribution tables.

Supply well written external routines for non trivial tasks performed by user code. This may deter users from writing inefficient code to perform the task themselves.

What obscure coding tricks are there.

These tricks are not necessarily recommended, as they can reduce the readability of the code. They can also speed things up, particularly those tricks using PARSE.

Use a boolean check.
sign = TRANSLATE( (var<0) , '+-' , '01' )
boolvar = (test=true)

Use the TRANSLATE() function to reorder or insert chars in a string. (Thanks to Doug Nadel)
say TRANSLATE('ab,cde,fgh,ijk,lmn,opq,rst,uvw,xyz' , myNumber , 'abcdefghijklmnopqrstuvwxyz')
say TRANSLATE('abcdefgh ijklmnop qrstuvwx yz123456' , hexChars , 'abcdefghijklmnopqrstuvwxyz123456')

Use functions such as DATE() to check if some data conforms to a type. Use ON SYNTAX to trap the error.
junk = DATE(,testDate,'S') /* Will give a syntax error is testDate is not a standard date */

The many variations of DO. Read the Rexx Reference and Rexx Guide for inspiration.

Use PARSE to initialise variables.
parse value '1 2 3 6 4' with var1 var2 var3 var4 var5 .

Use PARSE instead of SUBSTR().
parse var string =(colA) var1 +(lenA) .,
                 =(colB) var2 +(lenB) .,
                 =(colC) var3 +(lenC) . 

is functionally equivalent to: 
var1 = SUBSTR(string,colA,lenA) 
var2 = SUBSTR(string,colB,lenB)
var3 = SUBSTR(string,colC,lenC) 

Use PARSE to split out bits from a string:
parse value X2B(C2X('AA'x)) ,                                      
       with x80 2 x40 3 x20 4 x10 5 x08 6 x04 7 x02 8 x01       
   

Use PARSE to increment a stem counter and assign a value to the new last stem (Attributed to  Les Koehler @ IBM Tampa).
stem.0 = 1  ; stem.1 = 'cheese'

parse value     stem.0+1  'mice'    ,
       with  1  x         stem.x    ,
             1  stem.0    .         ;

say stem.0 /* ==> '2'      */
say stem.1 /* ==> 'cheese' */
say stem.2 /* ==> 'mice'   */
say x      /* ==> '2'      */

 

Using Non-Alphanumeric characters.

Stephen Bacher writes on the subject of using non-alphanumeric characters:
"I disagree strongly with (... the use of ...) non-alphameric characters (like ! # % ) in REXX variable names. Extensions to REXX, including object-oriented extensions, may use these characters. Including them in variable names is asking for eventual breakage IMHO."

Another reason to avoid using the EBCDIC special characters (these include currency symbols, hash, at, double quotes, etc.) is because these characters may have different display meanings in foreign EBCDIC chacacter sets.

Are there any other style hints?

  • Leave space at the end of line for continuations and short 'date changed' comments.
  • Avoid ambiguous or confusing abbreviations in symbol names and comments.
    E.g. 'lst' and '1st' appear similar, and will a casual reader know whether a symbol 'lstPointer' is referring to a 'list pointer' or a 'last pointer'
  • See the Style Guide for a more detailed style discussion

Appendices.


Code Examples

This is a sample JCL procedure to run ISPF in batch.

//ISPFPROC PROC PREFU='user.lib',PREFS='SYS1',CMD=
//ISPF    EXEC PGM=IKJEFT01,DYNAMNBR=30,           
//        PARM='ISPSTART CMD(&CMD)'  
//*
//ISPPROF DD DISP=NEW,UNIT=SYSSQ,SPACE=(TRK,(1,1,1)),
//        DCB=(LRECL=80,BLKSIZE=8000,RECFM=FBA)
//*
//SYSEXEC DD DISP=SHR,DSN=&PREFU..EXEC
//        DD DISP=SHR,DSN=&PREFS..SISPEXEC
//SYSPROC DD DISP=SHR,DSN=&PREFS..SISPCLIB
//ISPPLIB DD DISP=SHR,DSN=&PREFU..PANELS
//        DD DISP=SHR,DSN=&PREFS..SISPPENU
//ISPMLIB DD DISP=SHR,DSN=&PREFU..MSGS
//        DD DISP=SHR,DSN=&PREFS..SISPMENU
//ISPSLIB DD DISP=SHR,DSN=&PREFU..SKELS 
//        DD DISP=SHR,DSN=&PREFS..SISPSENU
//ISPTLIB DD DISP=SHR,DSN=&PREFU..TABLES
//        DD DISP=SHR,DSN=&PREFS..SISPTENU
//ISPTABL DD DISP=SHR,DSN=&PREFU..TABLES
//ISPLOG  DD DUMMY,DCB=(RECFM=VA,LRECL=125,BLKSIZE=129)
//ISPLIST DD DUMMY,DCB=(RECFM=VA,LRECL=125,BLKSIZE=129)
//ISPLST1 DD DISP=NEW,UNIT=SYSSQ,SPACE=(CYL,(1,1)),
//        DCB=(LRECL=121,BLKSIZE=1210,RECFM=FBA)
//ISPLST2 DD DISP=NEW,UNIT=SYSSQ,SPACE=(CYL,(1,1)),
//        DCB=(LRECL=121,BLKSIZE=1210,RECFM=FBA)
//ISPCTL1 DD DISP=NEW,UNIT=SYSSQ,SPACE=(CYL,(1,1)),
//        DCB=(LRECL=80,BLKSIZE=800,RECFM=FB)
//ISPCTL2 DD DISP=NEW,UNIT=SYSSQ,SPACE=(CYL,(1,1)),
//        DCB=(LRECL=80,BLKSIZE=800,RECFM=FB)
//ISPWRK1 DD DISP=NEW,UNIT=SYSSQ,SPACE=(CYL,(1,1)),
//        DCB=(LRECL=256,BLKSIZE=2560,RECFM=FB)
//ISPWRK2 DD DISP=NEW,UNIT=SYSSQ,SPACE=(CYL,(1,1)),
//        DCB=(LRECL=256,BLKSIZE=2560,RECFM=FB)
//*
//SYSUDUMP DD DUMMY
//SYSTSPRT DD SYSOUT=Q
//SYSPRINT DD DUMMY
//SYSTSIN  DD DUMMY
// PEND

And run it thus:

//TESTISPF EXEC ISPFPROC,
//         PARM.ISPF='ISPSTART CMD(%REXXPROG TESTPARM)', 
//         PREFU='ISP'
//* or alternativly, 
//TESTISPF EXEC ISPFPROC,
//         CMD='%TESTPROG TESTPARM'
//         PREFU='ISP'  

If ISPF is not needed, try using IRXJCL:

//IRXJCL1  EXEC PGM=IRXJCL,PARM='%execname'
//SYSEXEC  DD DISP=SHR,DSN=your.exec.library
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*

or TSO in batch:

//IRXTSO   EXEC PGM=IKJEFT01
//* You could use PARM='%execname testparms' on exec statement
//SYSEXEC  DD DISP=SHR,DSN=your.exec.library
//SYSTSPRT DD  SYSOUT = *
//SYSTSIN  DD  *
%execname testparms
/*
//* You could run more than one exec sequentially from SYSTSIN
//* You can also run straight TSO commands like TSO PROF PREF(FRED)

This Exec shows how to temporarily LIBDEF ISPF datasets to your session, ALTLIB an Exec library to your session, call an ISPF application (under a new application Id to avoid name collisions with other ISPF profile var. names), and then unstack the LIBDEFs, and deactivate the alternative Exec library.

address ISPEXEC "LIBDEF ISPPLIB DATASET ID('user.PANELS') STACK"
address ISPEXEC "LIBDEF ISPMLIB DATASET ID('user.MSGS')   STACK"
address ISPEXEC "LIBDEF ISPTLIB DATASET ID('user.TABLES') STACK"
address TSO     "ALTLIB ACTIVATE APPLICATION(EXEC) DATASET('user.EXEC')"
address ISPEXEC "SELECT CMD(%yourexec "your exec options") NEWAPPL(yourappl) PASSLIB"
address ISPEXEC "LIBDEF ISPTLIB"
address ISPEXEC "LIBDEF ISPMLIB"
address ISPEXEC "LIBDEF ISPPLIB"
address TSO     "ALTLIB DEACTIVATE APPLICATION(EXEC)"

Other Sources of Information.

Marist College hosts the ListServ Archives at https://www.marist.edu/htbin/wlvindex?tso-rexx
Yahoo! has a Rexx category that lists several Rexx information sites.
Rexx Book list. Try searching Amazon.
Rexx manuals, search the IBM S390 Websiteto find them online in PDF and HTML formats.
CbtTape.org has many useful utilities, including a list of Listserv lists.
Rexx newsgroup comp.lang.rexx can be found via Google

ISPF has its own active listserv at ISPF-L@listserv.nd.edu, Send message text 'SUBSCRIBE ISPF-L Your Name' to LISTSERV@listserv.nd.edu and await further instructions.


Subscribing to the TSO-REXX List

Go to the TSO-Rexx list archive, scroll to the bottom of the page, and follow the instructions there.

 


Glossary

Generation Data Group.
A set of datasets referred to by their GDG base, and a generation number referring to the generation required. See MVS/Using datasets.
Built-in Function.
A standard Rexx function, e.g. SUBSTR().
External Function.
An external Rexx function, either supplied with TSO/E, e.g. SYSVAR(), or supplied in a PDS in Rexx source or Load Module form, e.g. RXGDGV() in appendix C.
Internal Label.
A label within an exec, either hidden (with PROCEDURE without EXPOSE), or exposed.
Profile prefix.
A TSO environment variable. The users default HLQ, prefixed to unqualified datasets to make a qulified DSN
HLQ.
High Level Qualifier. The first qualifier in a fully qualified dataset name.
DTL
Dialog Tag Language. An SGML markup language used to define ISPF panels. DTL documents are converted into panels using the ISPDTLC compile utility.