| From: DEC:.REO.REOVTX::HUDSON "[email protected] - UK Software
Partner Engineering 830-4121" 13-MAR-1997 11:53:02.82
To: nm%vbormc::"[email protected]"
CC: HUDSON
Subj: RE: POINT No. 22462, Nectar, command line args
Hello Mats Nilsson
Thank-you for your ASAP question on retrieving command line arguments.
When I started to read your question, I thought "A-ha! use LIB$GET_FOREIGN".
But as I read further I realised you've been down this route and you're asking
something a little more difficult!
As you say, you can use LIB$GET_FOREIGN if the program was executed by a
foreign command (e.g. "$ fred :== $disk:[dir]fred.exe"). This is because
foreign commands are not specific to C, and VMS knows how to get them.
> VMS copies the arguments from the calling program to the argv
> parameter in the second program. My problem is that I need to write
> the second program in Ada, and Ada has no argc/argv support. How do I
> retrieve the arguments ?
Unfortunately, in the case of C doing fork/etc., it is not VMS that is copying
the arguments, it is the C RTL, which uses a mailbox to pass information from
the parent to the child.
When any C program starts, the C RTL gets in there first and looks to see if a
specific mailbox is there ("VAXC$EXECMBX"). If it isn't there, then the C RTL
uses LIB$GET_FOREIGN to populate "argc" and "argv", before transferring control
to your "main()". If the mailbox is found, then the RTL reads info from it
(which the parent will be writing) and creates "argc" and "argv" that way (as
well as setting up some other stuff like environment variables.
So as far as your C program is concerned, it just received argc and argv as
soon as it begins, but in fact a lot of work has gone on behind the scenes that
you didn't know about.
Looking at the source code for the C RTL you can see what it is doing, and I
wrote a small piece of Pascal (sorry but I can't do Ada!) which will retrieve
command line arguments by imitating what the C RTL does (look for the mailbox
and read stuff). But this doesn't work all the time for me and so I don't
think it's really a good basis for a proper solution (parent process sometimes
hangs).
I suspect there may be other things which my program should do to communicate
back to its parent, which the C RTL would obviously take care of.
I include the program below for your interest; you are welcome to use it if you
think it might be useful but if you wanted to go this route I would suggest you
get a copy of the VMS sources so you can check for yourself what else you may
need to do.
I think a more sturdy solution would be to have a C program executing as your
child, which then invokes the Ada program and passes "command line" arguments
to it in a well-defined way.
I hope this information is useful to you
Regards
Nick Hudson
Digital Software Partner Engineering
[inherit ('sys$share:pascal$lib_routines','sys$share:starlet')]
(* Program demonstrates that it is possible to read "command line arguments"
when executed by a C parent which forks
Nick Hudson, Digital, Mar-97.
This code is intended as a demonstration only!
*)
program paschild(input,output);
const
NUMARGS = 0; (* specify number of command line args *)
ARGSTRG = 1; (* specifies a command line argument *)
ENDFILE = 6; (* signifies no more messages *)
type
str = packed array [1..255] of char;
uword = [word] 0..65535;
ubyte = [byte] 0..255;
(* The C parent will send messages to the child through a mailbox. The
first byte of each message defines its type. The only three we're
interested in are NUMARGS, ARGSTRG and ENDFILE. See F_ENVIRON.LIS
in the C RTL sources for a list of all the other message types
*)
fork_msg_block = packed record
field_type : [byte] 0..255;
case integer of
0 : ( textbuf : packed array [1..511] of char; );
(* NUMARGS *) 1 : ( arg_count : integer;
fill1 : packed array [1..507] of char; );
(* ARGSTRG *) 2 : ( byte_count : ubyte;
fill2 : packed array [1..510] of char; );
end;
itmlst = packed record
buflen : uword;
itmcod : uword;
bufadr : [unsafe] unsigned;
retlen : [unsafe] unsigned;
mbz : unsigned;
end;
iosb = packed record
cond_val : uword;
trans_cnt : uword;
devdepend : unsigned;
end;
var
stat : unsigned;
my_itmlst : itmlst;
my_iosb : iosb;
msg_buf : fork_msg_block;
arg_count : integer; (* number of args we've shown to user *)
argline : packed array [1..132] of char;
arglen : uword; (* used for return from lib$get_foreign *)
translation : str; (* used to get logname translation *)
trans_len : uword;
mbx_chan : uword;
procedure check(stat : unsigned; msg : packed array [a..b:integer] of char);
begin
if not odd(stat) then
begin
writeln('Failure at ',msg);
lib$stop(stat);
end;
end;
begin
arg_count := 0;
writeln('pascal child here');
(* if the logical VAXC$EXECMBX is defined, then that should mean we've
been fork'd by a C parent, and we can use the mailbox that the
logical name points at to retrieve command line args
*)
with my_itmlst do
begin
buflen := 100;
itmcod := LNM$_STRING;
bufadr := iaddress(translation);
retlen := iaddress(trans_len);
mbz := 0;
end;
stat := $trnlnm(tabnam := 'LNM$PROCESS',
lognam := 'VAXC$EXECMBX',
itmlst := my_itmlst);
if (stat = SS$_NORMAL) then (* we've been forked from C *)
begin
writeln('Forked from C:');
stat := $assign(devnam := translation,
chan := mbx_chan);
check(stat,'assign to VAXC$EXECMBX');
msg_buf.field_type := 0;
while (msg_buf.field_type <> ENDFILE) do
begin
stat := $qiow( chan := mbx_chan,
func := IO$_READVBLK,
iosb := my_iosb,
p1 := msg_buf,
p2 := 512);
check(stat,'$qiow read from mbx');
check(my_iosb.cond_val,'$qiow (iosb) read from mbx');
if (msg_buf.field_type = NUMARGS) then
begin
write('Number of arguments = ');
writeln(msg_buf.arg_count);
end;
if (msg_buf.field_type = ARGSTRG) then
begin
arg_count := arg_count + 1;
write('Argument #', arg_count:1, ' ->');
writeln(msg_buf.fill2:msg_buf.byte_count,'<-');
end;
end;
end
else (* use lib$get_foreign *)
begin
stat := lib$get_foreign(argline,,arglen);
check(stat,'lib$get_foreign');
write('Using LIB$GET_FOREIGN, argline is ->');
writeln(argline:arglen,'<-');
end;
end.
|