12 March 2013

Wrapping your own macro code around vendor-supplied macros without changing source

One of the great IBM z/OS performance and measurement gurus, Martin Packer, made a comment in a recent blog entry about he wished he could wrap his code around macros supplied from an external source. I told him that this was possible, and that I had recently done this with the IBM High Level Assembler Toolkit structured programming macros. In this case, I added support for using just name or !name for testing a bit flag in conjunction with a bit-flag-generating macro. The key is the OPSYN assembler command.

I will demonstrate how to do this using something I wish the DCB macro had that its ACB sibling has - the ability to use the label field as the default DDNAME=.

I apologize in advance for the crappy formatting; I still need to come up with some good formatting for the examples. Google's default doesn't do well. (Martin, yes, I still have your email with suggestions. :)  )

I will create CHS.MACLIB(DCB), which will call SYS1.MACLIB(DCB).

First, we clone the DCB prototype:

         MACRO
&NAME    DCB   &DDNAME=0,&MACRF=,                      FOUNDATION BLOCK* 
               &BFTEK=,&BFALN=,&EODAD=1,&RECFM=,&EXLST=0,     EXTENSION* 
         Snipped rest of prototype


And then we add our logic:

         PUSH  PRINT,NOPRINT         Obfuscate
         PRINT OFF,NOPRINT
DCB_     OPSYN DCB                   Change CHS.MACLIB(DCB)'s name
DCB      OPSYN ,                     DCB now refers to SYS1.MACLIB(DCB)
         POP   PRINT,NOPRINT         No more obfuscation
         LCLC  &N
&N       SETC  '&DDNAME'
         AIF   ('&N' NE '').GOTDD
&N       SETC  '&NAME'
         AIF   ('&N' NE '').GOTDD
         MNOTE 8,'Missing label or DDNAME'
         AGO   .EXIT
.*
.GOTDD   ANOP  ,

&NAME    DCB   DDNAME=&N,MACRF=&MACRF,BFTEK=&BFTEK,BFALN=&BFALN,       &
               EODAD=&EODAD,RECFM=&RECFM,EXLST=&EXLST,                 &
         Snipped rest of invocation 
.EXIT    ANOP  ,
         PUSH  PRINT,NOPRINT         Obfuscate again
         PRINT OFF,NOPRINT
DCB      OPSYN DCB_                  Now DCB refers to CHS.MACLIB(DCB)
         POP   PRINT,NOPRINT         No more obfuscation (again)

         MEND  

The magic is in the OPSYN assembler instructions. Assuming CHS.MACLIB is ahead of SYS1.MACLIB in the SYSLIB concatenation, our DCB macro is picked up first. During processing, we change the name of our DCB macro to DCB_, and then we tell the assembler that DCB from this point forward refers to whichever DCB it finds. Since our DCB is now DCB_, the assembler now burrows further into the concatenation and finds SYS1.MACLIB(DCB) when it gets to the DCB macro coded inside CHS.MACLIB(DCB), and the assembler now uses it. After the DCB invocation, OPSYN is used again to undo what we did at the beginning, so that subsequent invocations of DCB will use CHS.MACLIB(DCB).

It's a little bit confusing at first, but if you stick an MNOTE in your wrapper macro, or use MHELP and ACONTROL LIBMAC, you can see what the assembler is doing. And you'll find this can be a very powerful tool for certain situations, such as prohibiting usage of certain macro operands by developers.

No comments:

Post a Comment

Feel free to leave a comment or ask questions.