I wanted to find out, from which module another Natural module was called. My goal was to make sure, that the module can only be called from a certain other module and raises an error, if a “disallowed” module calls it. I don’t want to get into the details here of why this is a bad idea in the first place đ
In Ruby, this is a one liner (see Any way to determine which object called a method?):
caller.first
As it turns out, in Natural it’s not that simple. However, it’s not that hard, either. Thanks to a forum post (see Previous Program System Variable) I was able to quickly implement a short subroutine that does the job. It uses User Exit USR0600N
(Get program level information) and looks like this:
DEFINE DATA
*
PARAMETER
01 P-CALLER (A8)
*
LOCAL
01 #NAMES (A8/1:32)
01 #LEVEL (P3/1:32)
*
01 #I (I4)
01 #STACK-SIZE (I4)
01 #INDEX-CALLER (I4)
*
END-DEFINE
*
DEFINE SUBROUTINE GET-CALLER
*
RESET P-CALLER
*
CALLNAT 'USR0600N' #NAMES(*) #LEVEL(*)
*
FOR #I 1 *OCC(#NAMES)
IF #NAMES(#I) NE ' '
#STACK-SIZE := #I
END-IF
END-FOR
*
#INDEX-CALLER := 3
IF #STACK-SIZE GE 3
P-CALLER := #NAMES(#INDEX-CALLER)
END-IF
*
END-SUBROUTINE
*
END
It can be called like this:
PERFORM GET-CALLER #CALLER
USR0600N
returns the Natural modules currently on the stack in descending order (as you would expect from a stack). So if STACK
calls STACK2
and STACK2
calls STACK3
and STACK3
calls GET-CALLER
, USR0600N
returns:
GET-CALLER (index 1; in fact, this would be the module's name, e.g. "GETCALL")
STACK3 (index 2)
STACK2 (index 3)
STACK (index 4)
This should explain the logic in GET-CALLER
above. For the call chain above, WRITE *PROGRAM 'was called by <' #CALLER '>'
results in:
STACK3 was called by <SMSTACK2>
STACK2 was called by <SMSTACK >
STACK was called by < >