Thursday, December 22, 2016

REXX: Adding elements to Endevor using REXX

Ever thought about adding elements to endevor using REXX? Actually, it can be easily done just by following simple steps given below.

i. Having the following information in hand before starting with the REXX.

1. Elements types you wish to add in endevor using REXX?
2.  Compiler types for the elements you wish to add.
3. Your stages, strangs and your application region in endevor.

ii. Setup a jcl skeleton for the endevor job.

iii. identify the sysin command you need to pass to the endevor job because these commands must be dynamically generated in the REXX.

iv. provide mapping for compiler type and the elements you wish to add so that it is simpler for the people who are using the tool. For example: Cobol would be mapping to a compiler type like C02NB00.

v. define what are the parameters to be passed for the REXX. My suggestion would be collect the element type, stage and strang. For example: REXXNAME cobol U pb.

here REXXNAME is your tool name
        cobol is your element type
        U is your stage
        pb is your strang.

vi. first validate if the provided type, stage and strang are valid types.

vii. if not, please display a message with the valid types and exit the macro.

viii. if yes, then generate the sysin card for the endevor job in a stem.

viii. then write the stem in a pds member.

ix. pass the pds member as dynamic parameter to the prepared jcl skeleton.

x. then submit the job.

Congratulations! You have created a tool to add elements to endevor.

Tuesday, September 20, 2016

REXX: Checking for valid Numeric value

We shall the check if a value is numeric using the option Datatype. Datatype is a reserved word in REXX.

/* REXX */                          
vari= " 1234"                       
                                    
result = Datatype( vari , N)        
if result = 1 then say 'valid'      
else say 'invalid'                  
exit                                



The value can contain trailing or leading spaces but not spaces in between.

For example:

dataype(" 1234",N) --> valid value
dataype("1234 ",N) --> valid value
dataype(" 1234 ",N) --> valid value
dataype("12 34",N) --> invalid value

Thursday, August 25, 2016

REXX: Implementing Wait function in REXX

It always nice to wait for someone, something to happen, something to be done. Its possible in REXX to make it wait for sometime before you do some action. 

For example, checking status of a submitted job. It doesn't make sense to check the status of the job every second or micro second. 

Normally the jobs take minutes or hours or at least some seconds. If we have such a scenario, we have to run a job and wait until the job is complete. We shall introduce the wait function before checking the job status. 

As usual, REXX is such a easy and simple language. The implementation is so simple.

/*REXX*/
SECS = 20 
TIMEDIFF = 0                                                  
STARTTIME = TIME('S')                                         
SAY 'I am going to wait for 20 seconds'
DO UNTIL TIMEDIFF >= SECS                                     
 TIMEDIFF = TIME('S') - STARTTIME                             
END                                                           
SAY 'I am done with waiting for 20 seconds'
EXIT

Incase of questions or any feedback, please write your questions in the comments.

REXX: LMINIT, LMOPEN, LMMLIST, LMCLOSE, LMFREE

We shall read all member names using LISTDS "Dataset" Members. But however, I felt that this technique requires some logic to ignore certain statements before we could really get hold of the member name.

So,how to read all members of a Dataset without having any additional logical overhead?

It is possible via LMMLIST. We will go through the steps briefly in this post.

Step 1: LMINIT

LMINIT generates a DataId for the dataset.

REXX can be executed in foreground or background. When we execute from background, the DDNAMES are already allocated. In Foreground, we have to allocate the DDNames. This DDName can be provided in the LMINIT or we shall directly provide the Dataset Name. When we are providing DDname, we should use the keyword DDNAME. When we are providing the dataset, we should use the keyword DATASET.

In our example, we are using DATASET.

Dataset = "DATASET('"Your-Dataset"')"            
address "ISPEXEC"                                       
"LMINIT" Dataset "DATAID(DataId) ENQ(SHR)"

Step 2: LMOPEN

LMOPEN opens dataset associated with DataId generated using the LMINIT step.
Option INPUT means its only read only.
Option OUTPUT means its for writing the dataset. When opened with OUTPUT option, then it is necessary to have a matching LMCLOSE. If LMCLOSE is not issued for the DataID, then the DataID is not released until ISPF terminates.

"LMOPEN DATAID("DataID") OPTION(INPUT)"   

Step 3:

DO FOREVER
  "LMMLIST DATAID("DataId") OPTION(LIST) MEMBER(Member) STATS(NO)"      IF RC = 8 THEN LEAVE
   SAY MEMBER
END 

This step displays you all the member names.

Step 4:
"LMCLOSE DATAID("DATAID")" 

Close the DataID.

Step 5:
"LMFREE DATAID("DATAID")"   

Free the DataID.

Incase of questions, please drop your questions in the comments.

Monday, August 22, 2016

REXX: How to Execute a REXX inside a REXX

It is possible to execute a REXX inside another REXX using the EXEC command.

For example, we shall use this approach to Read the pdsname and member name and pass this information to an another REXX procedure which will work upon this file and produce an output file. We shall open that output file and show it to the user using the outer REXX.

Lets us name the REXX reading the PDSNAME and Member Name as REXX 1.

Lets us name the REXX processing the member as REXX 2.

Rexx 1 should call the REXX 2 using the following code

"EXEC 'Dataset where REXX 2 is residing' '"Input-file"'"  

Rexx 2 will process the Input file and produce an output file.

This output file could be opened from REXX 1 and displayed to the user using the following code.

ADDRESS ISPEXEC "EDIT DATASET("OUTPUT-FILE") "

In case of queries, please post in the comments.

REXX: How to read the Dataset and Member Name

In order to read the dataset and member name of the opened member, use the following REXX code. 

After executing this REXX code, the variable PDSNAME will contain the Dataset Name and Member will contain the member name. If the same REXX is applied to a PS file, then PS file name will be stored in variable PDSNAME and Member name will be empty. 

/*REXX*/
 "ISPEXEC CONTROL ERRORS RETURN"

 "ISREDIT MACRO (PARMS) NOPROCESS"                                              
 "ISREDIT (PDSNAME) = DATASET"                                                  
 "ISREDIT (MEMBER)  = MEMBER"          

Sunday, August 21, 2016

REXX: Left and Right Function is to beautify

Left and Right function...

Normally every programmer uses Left and Right function to strip the characters from left of a string or Right of a string.

For example:

String="hello"
Result=left(String,4)

This would REXX procedure would return Result as "hell".

REXX is normally used to automate routine manual tasks or generating codes. In order to keep the readability of the code in place, it is necessary we use the right functions to give us a standard alignment throughout the generated source.

The function Left and Right helps us a lot in the regard.

For example: Our task is to give a rating to every country in the world.

Country1="India"
Country2="USA"
Country3="Germany"

Rating1="1"
Rating2="2"
Rating3="3"

Statement1=left(Country1,20)!!left(Rating1,4)
Statement2=left(Country2,20)!!left(Rating2,4)
Statement3=left(Country3,20)!!left(Rating3,4)

This would result in a proper alignment.

Result would be...

India               1   
USA                 2   
Germany             3   

Saturday, August 20, 2016

REXX: How to delete all members from a PDS

Normally it is easy to delete all members without REXX.

The command s * d would do this for you.

Incase, if you have a list of members to be deleted from a huge list, then it is better to automate with REXX.

/*REXX*/
 PDSNAME = "Your PDS Name" 
 CALL OUTTRAP "MEM."                                                
 "LISTD" "'"PDSNAME"'" "MEMBERS"                                    
 CALL OUTTRAP "OFF"                                                 
 DO I=1 TO MEM.0                                                    
    IF MEM.I = "--MEMBERS--" THEN LEAVE                             
 END                                                                
 I = I+1                                                    
 DO J = I TO MEM.0                                                  
    PARSE VALUE MEM.J WITH MEMNAME                                
    MEMNAME=STRIP(MEMNAME)   

/* You shall have your IF or WHEN condition to delete */                                       
    CALL OUTTRAP "DUMMY."                                           
    ADDRESS TSO "DELETE '"PDSNAME"("MEMNAME")'"                     
    CALL OUTTRAP "OFF"                                              
 END                                                                 

Sunday, August 14, 2016

Translating to Lowercase in REXX

Translate command in REXX by default converts the input string to upper case and returns the value. However, it is possible to convert to lowercase as well. Lets us see how shall we do that.

Syntax for translate command:

translate(input source, replace string, search string, padding)

Code example:

/*REXX*/

UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'     
lowercase = 'abcdefghijklmnopqrstuvwxyz'     
input= 'HeLLO'

input_upper = translate(input, lowercase, uppercase)
Say 'Original Source  : ' input 
Say 'Translated Source: ' input_upper

This would return value 'hello' which will be stored in input_upper variable

Saturday, August 13, 2016

Accessing Spool(SDSF) from REXX

To automate the process of submitting JCL and analyzing the results from the spool, it is necessary to submit and read data from spool (sdsf) from REXX.

In this blog, we have already discussed how to submit a JCL(skeleton) from REXX. In this post, we will see how to capture the job data and analyze them.

Step1: Submit the job
CALL JCL_SUBMIT                                                      

Step2: Read the job status until it is done
CALL JCL_STATUS                        

Step3: Once Job is done, read its data.                            
CALL SPOOL_ACCESS                                                  

Incase of questions, please write in comments.

Code is given below. Please read instructions given below and make changes accordingly.

JCL_Submit:
TEMP_FILE="'"Your Temporary PS file"'"                           
SKL_MEM = MEMBER_NAME  --> Your skeleton JCL member name
"ALLOC FI(ISPFILE) DA("TEMP_FILE") SHR"                             
"ISPEXEC LIBDEF ISPSLIB DATASET ID('Your Skeleton PDS')"    
"ISPEXEC FTOPEN "                                                   
"ISPEXEC FTINCL "SKL_MEM" "                                         
"ISPEXEC FTCLOSE "                                                  
"ISPEXEC VGET (ISPFILE)"                                            
X=OUTTRAP("SUB.")                                                   
ADDRESS TSO "SUBMIT "TEMP_FILE"  "                                  
X=OUTTRAP("OFF")                                                    
"FREE FI(ISPFILE)"                                                  
RETURN                                                            
                                                                 
JCL_Status:

DO K = 1 TO SUB.0                                             
  PARSE VAR SUB.K 'JOB' JOBNAME '(' JOBID ')' STATUS          
END K                                                         
SAY "Your JobName Is "JOBNAME" JobID Is "JOBID                
JOBDONE = 'NO'                                                
SUBJOBNAME = STRIP(JOBNAME,B)                                 
SUBJOBID = STRIP(JOBID,B)                                     
SUBJOB = SUBJOBNAME !! "(" !! SUBJOBID !! ")"                 
DO UNTIL JOBDONE = 'YES'                                      
  X=OUTTRAP("OUT.")                                           
  ADDRESS TSO "STATUS" SUBJOB                                 
  X=OUTTRAP("OFF")                                            
  DO K = 1 TO OUT.0                                           
   PARSE VAR OUT.K 'JOB ' JOBNAME '(' JOBID ')' STATUS        
   STATUS = STRIP(STATUS)                                     
   SAY " Please wait .... Your Job Status is" STATUS          
   IF POS('ON OUTPUT QUEUE',STATUS) > 0 THEN DO               
    JOBDONE = 'YES'                                           
    CALL RETURN_CODE                                          
  END                                                         
  ELSE DO                                                     
    CALL MYWAIT 20                                            
  END                                                         
 END K                                                        
END                                                           
                                                              
MYWAIT:                                                       
ARG SECS                                                      
TIMEDIFF = 0                                                  
STARTTIME = TIME('S')                                         
DO UNTIL TIMEDIFF >= SECS                                     
 TIMEDIFF = TIME('S') - STARTTIME                             
END                                                           
RETURN                                                        
                                                              
RETURN_CODE:                                                  
RC=ISFCALLS("ON")                                             
ADDRESS SDSF "ISFEXEC ST"                                     
ISFCOLS= "JNAME JOBID RETCODE"                                
COLDTL=WORD(ISFCOLS,1)                                        
DO IX=1 TO ISFROWS                                            
 DO JX=1 TO WORDS(ISFCOLS)                                    
  COL=WORD(ISFCOLS,JX)                                        
 END                                                          
 IF JOBID.IX = SUBJOBID THEN DO                               
  SAY "Your Job Return Code is "  RETCODE.IX                  
  RETCODE=RETCODE.IX                                          
 END                                                          
END                                                           
RC=ISFCALLS("OFF")                                            
RETURN                                                        

SPOOL_ACCESS:                                                          

RC=ISFCALLS('ON')                                                       
ADDRESS SDSF "ISFEXEC ST"                                               
DO IX=1 TO JOBID.0                                                      

  IF JOBID.IX = SUBJOBID THEN  DO                                       
     PX = IX                                                            
     ADDRESS SDSF "ISFACT ST TOKEN('"TOKEN.IX"') PARM(NP ?)" ,          
             "( PREFIX JDS_"                                            
     DO JX=1 TO JDS_DDNAME.0              
                              
        IF JDS_DDNAME.JX="Your DDNAME" THEN DO                              
           STEP=STRIP(JDS_STEPN.JX)                                     

           IF STEP="Your Step Name" THEN DO                                    
               ADDRESS SDSF "ISFACT ST TOKEN('"JDS_TOKEN.JX"') PARM(NP SA)"

             DO KX = 1 TO ISFDDNAME.0                                   
                "EXECIO * DISKR" ISFDDNAME.KX "(STEM LINE. FINIS"       
                ELEMENT.0 = 0                                           

                DO LX = 1 TO LINE.0                                                                   ---- Your Logic --------
                END                                                     

             END                                                        

           END                                                          

        END                                                             

     END                                                                

  END                                                                   

END                                                                     
RC=ISFCALLS('OFF')                                                      
RETURN                                                                  

Reading and Writing files in REXX

In order to read or write a file using REXX, first step is to allocate a DDNAME. This step is required only when REXX is executed through Foreground mode. We can also execute REXX through background mode using JCL. In background mode, we aren't required to allocated DDNAME. It is auto allocated by the system.

Reading a file in REXX:

Step 1: Allocate ddname to the input file
"ALLOC FI(INDD) DA('"PDSNAME"("MEMNAME")') SHR "               

Step2: Read all records from the file and store in the stem variable Line.

The command * indicates all records, if you wish to read only one record, you can say 1 instead of * and make it in a loop.

"EXECIO * DISKR INDD (STEM LINE. FINIS"                                
Step3: Processing --- loop to process all records
DO I=1 TO LINE.0                                                
   CALL PROCESSING                                                
END                             
                                   
Step 4: This step deallocates the ddname.
"FREE FI(INDD)"                                             


Writing a file in REXX:

Writing file is similar to reading. Only difference is DISKW command is used instead of DISKR. 

Step 1: Allocate DDNAME to the output file
"ALLOC FI(OUTPUT) DA('"OUTPDS"("MEMBER")') SHR "

Step 2: Write the stem into the output file 
"EXECIO * DISKW OUTPUT (STEM WRITEDA. FINIS"    

Step 3: Deallocate the DDNAME
"FREE FI(OUTPUT)"                               


Sorting in REXX

The following sort can be used to sort a Stem. In this algorithm, Stem_Variable is the stem.

 I = COUNT                                                                      
 DO UNTIL I < 2                                                                 
    NEWI = 1                                                                    
    DO J = 1 UNTIL J >= I-1                                                     
       K = J + 1                                                                
       IF Stem_Variable.J > Stem_Variable.K THEN                                  
       DO                                                                       
          SWAP            = Stem_Variable.K                                      
          Stem_Variable.K = Stem_Variable.J                                      
          Stem_Variable.J = SWAP                                               
          NEWI = J                                                              
       END                                                                      
    END                                                                         
    I = NEWI                                                                    
 END                             

Wednesday, August 10, 2016

REXX Select Statement

This post is pretty much self explanatory. All the best.


                     /*REXX*/
                     
         MONTH = "AUG"

         SELECT                                                                 
         WHEN MONTH = 'JAN' THEN MONTH_NUM = '01'                               
         WHEN MONTH = 'FEB' THEN MONTH_NUM = '02'                               
         WHEN MONTH = 'MAR' THEN MONTH_NUM = '03'                               
         WHEN MONTH = 'APR' THEN MONTH_NUM = '04'                               
         WHEN MONTH = 'MAY' THEN MONTH_NUM = '05'                               
         WHEN MONTH = 'JUN' THEN MONTH_NUM = '06'                               
         WHEN MONTH = 'JUL' THEN MONTH_NUM = '07'                               
         WHEN MONTH = 'AUG' THEN MONTH_NUM = '08'                               
         WHEN MONTH = 'SEP' THEN MONTH_NUM = '09'                               
         WHEN MONTH = 'OCT' THEN MONTH_NUM = '10'                               
         WHEN MONTH = 'NOV' THEN MONTH_NUM = '11'                               
         WHEN MONTH = 'DEC' THEN MONTH_NUM = '12'                               
         END                                                                  
         SAY 'Month In Number :' MONTH_NUM

         EXIT  

REXX LISTD Command: Reading all member names from a PDS

When we have only handful of members in a PDS, it is easily to make a list of their names. Imagine if we have 1000 members in a PDS. Some may do it manually but some use REXX script to do this task quickly.

Lets see how to do this in REXX.

PDSNAME="Your-PDS Name"                                    
CALL OUTTRAP "MBRS."                                               
"LISTD" "'"PDSNAME"'" "MEMBERS"                                     
CALL OUTTRAP "OFF"                                                            
DO I=1 TO MBRS.0                                                    
    IF MBRS.I = "--MEMBERS--" THEN LEAVE                            
END                                                                          
I = I+1
DO MEM = I TO MBRS.0                                                
       Say "Member Name ;" MBRS.MEM
END

This code would display all the members in a PDS. Instead of displaying the member name you shall add your own logic to fulfill your requirement.

Accessing DB2 from REXX: DSNREXX

We shall access DB2 from REXX using DSNREXX. This may sound easy but in REXX the error messages are very rarely could be interpreted to the code error and corrected. Hence, it is important that all the steps given here are followed as described.

It necessary to add the steplib ddname in the REXX where you wish to connect and work in DB2.

"STEPLIB ADD DA('<Db2 Subsystem>.DBMS.LNK',                                         'SYSC.<Db2 Subsystem>.SDSNLOAD') nomsg"                            
Db2 Subsystem value be according to your DB2 installation in your organization. 
Please note that these libraries are necessary to working with DB2 in REXX.

ADDRESS DSNREXX  'CONNECT' SUBSYS                                       

SUBSYS is a variable which contains your Db2 Subsytem name. 

SQLSTMT ="SELECT * FROM " DBNAME"."TABLENAME 

here DBNAME is a variable which contains your Table Qualifier
and TABLENAME is a variable which contains your Table Name.

ADDRESS DSNREXX
"EXECSQL PREPARE S1 FROM :SQLSTMT"                                     
"EXECSQL DECLARE C1 CURSOR FOR S1"                                     
"EXECSQL OPEN C1"                                                      

 "EXECSQL FETCH C1 INTO :Your-Variable"

Your-Variable will contain the value after the execution of the SQL statement. 

"EXECSQL CLOSE C1"

"STEPLIB REMOVE DA('<DB2 Subsystem>.DBMS.LNK',
                                           'SYSC.<DB2 Subsystem>.SDSNLOAD') nomsg"

Please use the double quotes(" ") and single quotes(' ') as given above. In case of queries, please feel free to ask in the comments.

Monday, August 8, 2016

REXX Skeleton: Submitting jobs through Rexx

Submitting jobs through REXX and reading the spool through REXX gives us immense potential to automate many manual activities in mainframes.

In this post, we will see how to submit a job from REXX using the Skeleton concept.

Using Skeleton, we are able to pass values to the JCL during REXX execution. Skeleton is like a JCL template, whichever value you wish to pass during the runtime, you can setup a variable name for it and prefix it with &.

For example:

//&JOBID JOB  ('Skeleton'),'&USRID',MSGCLASS=R,  
//     MSGLEVEL=(1,1),CLASS=T,NOTIFY=&USRID,        
//     TIME=5              

In this skeleton, &USRID and &JOBID are the dynamic parameters. Hence, it is mandatory that in your REXX script, you have these variable names populated.
 

Lets have a look at the REXX. 

/*REXX*
USRID=SYSVAR(SYSUID)       /*Populates the User ID from System variable SYSUID*/

JOBID=USRID"R"                      /* Concatenates User ID with a R */

How to submit a skeleton:

TEMP_FILE="'"Your PS File--A Dummy file"'"                       
"ALLOC FI(ISPFILE) DA("TEMP_FILE") SHR"                         
"ISPEXEC LIBDEF ISPSLIB DATASET ID('Your PDS where your skeleton is residing')"
"ISPEXEC FTOPEN "                                               
"ISPEXEC FTINCL "Your Skeleton Member Name" "                                     
"ISPEXEC FTCLOSE "                                              
"ISPEXEC VGET (ISPFILE)"                                        
X=OUTTRAP("SUB.")                                               
ADDRESS TSO "SUBMIT "TEMP_FILE"  "                              
X=OUTTRAP("OFF")                                                

"FREE FI(ISPFILE)"                                              

Upon execution of this REXX, your jcl would get submitted. In case of queries, please write in comments.  
                         

Friday, August 5, 2016

REXX String Manipulation : Substr, Pos, Words

REXX String Manipulation:

i. Substr:

Variable1 = 'Hello World'
Result = Substr(Variable1, 1, 5)

This would return the first 5 characters from the variable1. 

Result would be 'Hello'.

Variable1 = 'Hello World'
Result = Substr(Variable1, 7, 5)

This would return 5 characters from the 7th character of the variable1. 

Result would be 'World'. 

ii. Pos:

Very useful command to search for a string in a Variable.

Variable1 = 'Hello World'

My Search String is 'World'.

Hence, We shall code like:

 if(Pos(Variable1,'World')>0) then Say 'World is found'.

Result would be the display of 'World is found' since Pos command returns value 7 which is greater than 0.

If the search string is not found, it would return the value -1. 

iii. Words:

Words command return the number of words in a string. 

Variable1 = 'Hello World'

Num_Words = Words(Variable1)

Num_Words would be containing the result 2 since Variable1 is containing two words. 

We shall make a loop over all the words and process each word. 

For example:

do i = 1 to Num_Words
then do
     Single_word = Word(Variable1,i)
     Call User_logic_function
end

Starting with Rexx: 'Hello World'

How to write a Simple Rexx Program?

All Rexx Program requires this statement /*REXX*/ in the first line.

Ofcourse, this is just a comment. But it is necessary. You can write anything on the comment line but remember, somewhere REXX should be there.

So here is our Hello World Program:

/*REXX*/
Say 'Hello World'
Exit

Save this code under a member in a PDS. Open the PDS, just to the left of the member, type 'EX'. Then you see your 'Hello World' program executed with the display.

Introduction

Greetings to all the viewers of this blogspot. 

Well, everything we do has a purpose. This blogs purpose is to make life easier of a mainframe programmer by posting smart techniques and ideas. Often, it is said that mainframes is dying and we switch to other technologies. This isn't a new statement by the way, this exist for over 20 years and it still continues. Mainframe is still alive but the old techniques are difficult for the 21st century programmers. Let us try to post some useful information which helps the programmers.

Cheers! Welcome to the blog!


Featured Post

REXX Skeleton: Submitting jobs through Rexx

Submitting jobs through REXX and reading the spool through REXX gives us immense potential to automate many manual activities in mainframes....