Archive

Author Archive

ISPF Edit ‘Count’ macro

August 22, 2017 Leave a comment

Just a little macro I put together to count the number of occurrences of a string in a file when editing it.

/* rexx */ 
ADDRESS ISREDIT "MACRO (text)" 
 
if text = '' then do 
 say 'Nothing to search for. Enter text to search for ith cmd' 
 return 0 
 end 
 
address isredit "find all "text 
address isredit "(num,lines) = FIND_COUNTS" 
num=num+0 
lines=lines+0 
s='' 
if num > 1 then s='s' 
ls='' 
if lines > 1 then ls='s' 
on='' 
if lines > 0 then on = ' on 'lines' line'ls 
say 'Found 'num' occurance's' of 'text||on

To run it, just type count followed by the string to search for, and any other parameters such as the column number on the command line when in edit and press enter. E.G.

COUNT ‘some string’

or

COUNT NAME 1

 

 

 

 

Advertisements
Categories: Uncategorized

The humble EJECT statement…

March 17, 2017 Leave a comment

Back in the day when dinosaurs still roamed the earth and programmers still knew how to use a card punch, there were printers. Not your desktop printer of today but huge big, noisy things with all the characters on a chain, a ‘ribbon’ as wide as the paper that fed vertically on rollers and a a row of hammers that stuck the correct character as it went past on the chain, if you got it setup right that is! These things also used a paper loop for carriage control that told it where various spots where on the page, in particular the top of the page. Happy days!

In those days your program listing got printed out, so to make it more readable you’d put EJECT statements into the source code, usually before a subroutine so that the subroutine and all the comments about what it did, its inputs and outputs etc; (you did document all that stuff  didn’t you?) were at the top of a page.

Fast (or not) forward to today and I doubt anyone actually prints program listings anymore. If you are anything like me it’s all online, either in SDSF, sent to a member of a ‘listings’ dataset or, if your source is off platform, your tool set grabs the output after the assembly has run and FTPs it back to your workstation/PC where again, you can view it online.

So, since the output is now all just one big stream, why would you bother putting EJECT statements into your source, other than from habit?

Now I will admit that I did this from habit until I got to thinking about it (hence this post!) and there is actually a very good reason to include EJECT statements in your source, especially before the start of a subroutine.

Here’s a little test program:

TEST     CSECT                   
         BAKR  14,0              
         LR    R10,R15           
         USING TEST,R10          
         USING WSA,R2            
*                                
*                                
         XR    R15,R15           
         PR                      
         LTORG                   
*                                
SUB1     DS    0H                
         LA    R1,FIELD1         
         XR    R15,R15           
         BR    R14               
*                                
         LTORG                   
*                                
WSA      DSECT                   
FIELD1   DS    F'0'              
FIELD2   DS    F'0'              
*                                
         END

If you assemble this, the output looks like this:

                                     20 TEST     RMODE ANY          
000000                00000 00018    21 TEST     CSECT              
000000 B240 00E0                     22          BAKR  14,0         
000004 18AF                          23          LR    R10,R15      
                 R:A  00000          24          USING TEST,R10     
                 R:2  00000          25          USING WSA,R2       
                                     26 *                           
                                     27 *                           
000006 17FF                          28          XR    R15,R15      
000008 0101                          29          PR                 
000010                               30          LTORG              
                                     31 *                           
000010                               32 SUB1     DS    0H           
000010 4110 2000            00000    33          LA    R1,FIELD1    
000014 17FF                          34          XR    R15,R15      
000016 07FE                          35          BR    R14          
                                     36 *                           
000018                               37          LTORG              
                                     38 *                           
000000                00000 00008    39 WSA      DSECT              
000000                               40 FIELD1   DS    F'0'         
000004                               41 FIELD2   DS    F'0'         
                                     42 *                           
                                     43          END

In this code, I have let the using for WSA in the main line code ‘fall through’ into the sub routine (something I normally hate to do except for working storage based on R13). The only way you can tell that the LA,R1,FIELD1 instruction in the subroutine is using R2 as a base register for WSA is by looking at the assembled instruction.

However, if you add an EJECT before the SUB1 label like this:

         EJECT               
SUB1     DS    0H            
         LA    R1,FIELD1

The output now looks like this:

000000                00000 00018    21 TEST     CSECT              
000000 B240 00E0                     22          BAKR  14,0         
000004 18AF                          23          LR    R10,R15      
                 R:A  00000          24          USING TEST,R10     
                 R:2  00000          25          USING WSA,R2       
                                     26 *                           
                                     27 *                           
000006 17FF                          28          XR    R15,R15      
000008 0101                          29          PR                 
000010                               30          LTORG              
                                     31 *                           
                                                                    
  Active Usings: WSA(X'1000'),R2  TEST(X'1000'),R10                 
  Loc  Object Code    Addr1 Addr2  Stmt   Source Statement          
000010                               33 SUB1     DS    0H           
000010 4110 2000            00000    34          LA    R1,FIELD1    
000014 17FF                          35          XR    R15,R15      
000016 07FE                          36          BR    R14          
                                     37 *                           
000018                               38          LTORG              
                                     39 *                           
000000                00000 00008    40 WSA      DSECT              
000000                               41 FIELD1   DS    F'0'         
000004                               42 FIELD2   DS    F'0'         
                                     43 *                           
                                     44          END

The difference is that you now get one or more lines (depends on how many usings are active) that show the current usings in effect at the very start of the subroutine. As a result I can see exactly which usings are active and whether I have inadvertently ‘inherited’ one from an earlier routine that should have been dropped.

As a matter of coding style, I prefer to always drop all active usings, including code base regs except for working storage that is based on R13, at the end of each routine. That way I
know I am using the correct registers and usings in each routine and by putting an EJECT statement in the source before each routine, I can easily see from the listing that I am not inadvertently inheriting a using from an earlier block of code that may allow the code to assemble but ultimately fail when it runs because I picked up the wrong base register.

Categories: Uncategorized

Drag and Drop with Regina Rexx…

June 13, 2016 Leave a comment

Since I’m a mainframe guy I’ve spent most of my career writing REXX whenever I needed a quick script which means that I know REXX pretty well. Lately I’ve been playing with REXX on Windows thanks to an implementation of REXX called  Regina Rexx, see here.

Of course, one of the things you can do on Windows is drag and drop so I wanted to be able to drop a file onto a Regina REXX program and have the REXX program then process the file.

Typically you access input parms in a REXX exec by using the “parse arg varname” statement but I found that this did not work when dropping a file onto a Rexx exec on my Windows machine.

After some research I found that I needed to create a shortcut to the Regina REXX.EXE program with a parameter that is the path and name to the REXX program to run. You then have to drop your files onto the SHORTCUT.

 

So let’s say I have the following exec called “test.rexx” on my desktop:

parse arg parms
say parms
say "Press enter to end" 
parse pull .

I then create a shortcut on my desktop that looks like this:

"C:\Program Files\rexx.org\Regina\rexx.exe" "C:\Users\ltlfrari\Desktop\test.rexx"

So if I drop a file called “test.txt” onto the shortcut, this is the output that I see:

RexxDropTestOutput

Basically the exec receives the full path and file name as an input argument.

 

 

 

Categories: Uncategorized

Conditional Assembly Language…

May 10, 2016 Leave a comment

If you’ve ever read or written a macro you have no doubt at least seen conditional assembly language. It’s all that AIF and AGO stuff that forms a sort of ‘program’ within the macro so that it can generate code or whatever depending on whatever the input parameters are.

What’s really cool though is that it is not just limited to macros, you can use it within open code as well. So you might ask ‘why would you need to do that?’ but even if you don’t ask, here’s one interesting situation that came up recently.

I had some code that used a macro to generate a DSECT to map a control block. However we were switching version of the product that supplied the macro and a field within the macro had changed names even though it’s content had not. The result was that my code would only assemble with one version of the macro since with the other one it would get a not found error for the changed label. Since I did not want to have to co-ordinate my source code change with a build tool change the problem I had was how to make my source code support both versions of the macro and DSECT that it generated?

In case you have not guessed, the answer is conditional assembly language.

Here’s an example.

The old macro/DSECT:

         MYMACRO
SOMENAME DSECT
MYFIELD  DS    CL8

The new version of the macro/DSECT

        MYMACRO
SOMENAME DSECT
NEWNAME  DS    CL8

 

 

So my code originally looked something like this:

         USING SOMENAME,R2
         CLC   MYFIELD,=CL8'AAAAAAAA'

Obviously if I switch to the new macro library, my assembly will fail since the field ‘MYFIELD’ is no longer defined within the DSECT.

However, what you can do is to test to see if the variable ‘MYFIELD’ is defined and if not then conditionally change the code that gets assembled. Thus:

         USING SOMENAME,R2
         AIF   (T'MYFIELD EQ 'U').NEWMAC
         CLC   MYFIELD,=CL8'AAAAAAAA'
         AGO   .CONT
.NEWMAC  ANOP
         CLC   NEWNAME,=CL8'AAAAAAAA'
.CONT    ANOP

 

The AIF tests to see if the ‘type’ specification for the field MYFIELD is ‘U’, that is undefined. If it is undefined that means it has not been seen by the assembler (yet) so jump to the label .NEWMAC and continue to generate the code from there, which of course generates the code using the new field label of NEWNAME.

If the field MYFIELD is not ‘undefined’ then the assembler generates the code using the old field name, MYFIELD and then jumps (AGO) to the label .CONT to continue the assembly.

As a result, no matter which version of the macro library I am using, my code still assembles and works correctly.

There are other ways of achieving the same effect; For example by using the conditional assembly language to control the redefining of the old or renamed symbol to a common name and using that common name in the open code.

One gotcha though to be aware of. The macro/DSECT has to be defined in the source code BEFORE the conditional assembly code. If it is defined after the conditional code then, since the assembler has not seen either field at the time it encounters the test for the field being defined, it will always treat it as being undefined which would cause an assembly error when using the old macro/DSECT library because it would generate the code to use the new field name.

Categories: Coding, Mainframe

Share…

August 5, 2015 Leave a comment

I shall be at Share in Orlando next week so if you seem me (picture on my about page), please do say hi.

Categories: Uncategorized Tags: ,

Tools – COPYMBR

July 22, 2015 1 comment

During my daily work I often need to copy members from one PDS to another. The Partitioned data sets are always the same ones, just different members. I could use the ISPF Move/Copy Utility screens but it quickly gets tiresome to have to keep entering the data set and member information into the screens.

Since I’ve got my REXX library allocated to SYSPROC so that I can run execs from it directly using the TSO execname command on the ISPF command line I put together the following exec called COPYMBR to automate the process:

/* rexx                                              */
/* exec to copy a member from one pds to another     */

parse arg frommbr tombr '/' repl
tombr = strip(tombr)
upper repl
if frommbr = '' then do
   say "Syntax is COPYMBR from_mbr [to_mbr] [/r]"
   say "Specify to_mbr name to rename the copied member"
   say "Specify /r to replace the member in the target PDS"
   return 0
end
if tombr='' then tombr=frommbr

if length(frommbr) > 8 then do
   say "Source member name greater than 8 chars"
   return 0
end
if length(tombr) > 8 then do
   say "Target member name greater than 8 chars"
   return 0
end

address ispexec
"CONTROL ERRORS RETURN"
indsn="'"userid()||".LOAD'"     /* Source PDS */
outdsn="'"userid()||".LOAD2'"   /* Target PDS */
"LMINIT DATAID(IN) DATASET("indsn") ENQ(SHR)"
"LMINIT DATAID(OUT) DATASET("outdsn") ENQ(SHR)"

opt=''
if repl = 'R' then opt = "REPLACE'
"LMCOPY FROMID("in") FROMMEM("frommbr") TODATAID("out") ",
       "TOMEM("tombr") "opt
lastcc=rc
ins=''
if lastcc = 0 then nop
else if lastcc=12 then do
   if frommbr <> tombr then ins = 'as '||tombr
   say frommbr' not copied'||ins
   say tombr' already exists on target library.'
   end
else if lastcc=8 then do
   say frommbr' not found in source library.'
   end
else do
   say 'rc from LMCOPY='lastcc
   end

"LMFREE DATAID("in")"
"LMFREE DATAID("out")"
return 0

You can edit the lines marked “Source PDS” and “Target PDS” to specify any compatible load libraries. I just used my LOAD and LOAD2 libraries as an example.

The general syntax of the COPYMBR command on the ISPF command line is then:

TSO COPYMBR member_name [new_name] [/r]

Where:

member_name is the name of the member to copy from the source PDS

new_name is optional and is the new name to assign to the member on the target PDS. If not specified it defaults to the same name as the source member.

/r tells the exec to replace the member (or new member name if renaming) on the target PDS.

Note that when the copy is successful, the exec does NOT output any messages so there’s no need to press the enter key to clear a message except when there is something wrong.

Examples:

TSO COPYMBR MYPROG1 /r

Copy MYPROG1 from the source PDS to the target PDS and replace any existing version.

TSO COPYMBR MYPROG1 MYPROG2

Copy MYPROG1 from the source PDS to the target PDS and rename it to MYPROG2. Do not do the copy if MYPROG2 already exists on the target PDS.

TSO COPYMBR MYPROG1 MYPROG2 /r

Copy MYPROG1 from the source PDS to the target PDS and rename it to MYPROG2. Replace any existing version of MYPROG2 on the target PDS.

Categories: Tools Tags:

Customizing SYSPROC at TSO Logon time

July 17, 2015 3 comments

One of the things I like to do is to allocate my own REXX library to SYSPROC at TSO logon time so that I can execute REXX commands simply by typing in “TSO command_name”  on the ISPF command line.

On the TSO logon screen I have a command setup to invoke a REXX exec that will do that for me:

TSO_Logon_Screen

BEFORE just says to allocate my REXX library before the existing allocations so that anything in my library overrides anything in the existing allocation with the same name (useful for testing changes to existing EXECs). Specify ‘AFTER’ or let it default to allocate your library AFTER the existing allocations.

This is the exec (called ALLOCREX in the above screen shot):

/* rexx                                                    */
/* logon exec to concat my rexx library to SYSPROC so that */
/* I can run my own rexx commands easily without having    */
/* to specify the dataset name. EG TSO MYEXEC              */
/*                                                         */

parse arg opt                 /* BEFORE or AFTER (default) */

lib=userid()||".REXX"         /* dsn of rexx lib to alloc  */
if x> 4 then do
   say "Unable to locate '"lib"'."
   say 'SYSPROC alloc not changed.'
   signal exit
   end

rc=ddns('SYSPROC')    /* get current SYSPROC allocation */

/* extract dsns and check for my rexx lib already alloc */
list=''
comma=''
do while queued() > 0
   parse pull dsn
   if dsn=lib then do
      say lib 'already concat to SYSPROC.'
      say 'SYSPROC alloc not changed.'
      signal exit
   end
   list=list||comma||"'"||strip(dsn)||"'"
   comma=','
end
oldlist=list

/* add my rex lib to the list and realloc the DD */
if opt = 'BEFORE' then do
   list="'"lib"',"||list
end
else do
   list=list||",'"lib"'"
end
"ALLOC F(SYSPROC) DATASET("list") SHR REUSE"
allocrc=rc
if allocrc <> 0 then do
   say 'Alloc of SYSPROC faiuled with rc='allocrc
   say 'Restoring original allocation'
   "ALLOC F(SYSPROC) DATASET("oldlist") SHR REUSE"
   end

/* queue an ISPF command so this exec can end */
exit:
queue 'ISPF'
return 0
/*---------------------------------------------------------*/
ddns: procedure
/*---------------------------------------------------------*/
/*  Find dsns allocated to a specified ddname              */
/*  Output returned on the stack                           */
/*---------------------------------------------------------*/
/*                                                         */
parse arg ddname
upper ddname
x=outtrap("out.","*","noconcat",1)
address TSO 'listalc status'
if out.0 = 0  then do
  return 0    /* Nothing found */
  end

foundDD="N"
do i=1 to out.0 by 2
   parse var out.i dsn .
   j=i+1
   ddn=strip(substr(out.j,3,8))
   if ddn <> "" & ddn = ddname then foundDD="Y"
   if ddn <> "" & ddn <> ddname then foundDD="N"
   if foundDD = "Y" then do
      push dsn
   end
end
return 0
Categories: Tools Tags: