[Info-vax] Local Versus Global Command Options

Arne Vajhøj arne at vajhoej.dk
Mon Feb 17 21:41:07 EST 2025


On 2/15/2025 3:20 PM, Arne Vajhøj wrote:
> On 2/15/2025 2:22 PM, Mark Berryman wrote:
>> On *nix, one's program can define any command syntax desired since the 
>> command-line parsing is done entirely within the program.  The shell 
>> doesn't really do anything except try to expand unquoted wildcards 
>> (which somewhat limits the use of wildcards since the program cannot 
>> differentiate between a source wildcard and a destination wildcard).  
>> On VMS, DCL can do it either way.  One can define a syntax that allows 
>> DCL to do the parsing as Arne has shown, or one can tell DCL to simply 
>> pass the command line to the program and let the program do all of the 
>> parsing.  One advantage of the former is that if the command-line 
>> fails to parse, the program never even has to be activated.
> 
> A VMS program has choices:
> 1A) SET COMM and CLI$
> 1B) SET COMM/OBJ, LIB$GET_FOREIGN and CLI$
> 2) LIB$GET_FOREIGN and custom parsing
> 3) Language specific way to get individual arguments

Pascal demo:

$ type cl1a.cld
define verb cl1a
     image "sys$disk:[]cl1a"
     qualifier f, value(type=$file, required)
$ set comm cl1a
$ type cl1a.pas
[inherit('pascal$cli_routines')]
program cl1a(input,output);

type
    pstr = varying [255] of char;

var
    f : pstr;

begin
    cli$get_value('F', f.body, f.length);
    writeln('Pascal CL1A F=', f);
end.
$ pas cl1a
$ link cl1a
$ cl1a /f=z.z
Pascal CL1A F=z.z
$ type cl1bsup.cld
module cl1bsup
define verb cl1b
     qualifier f, value(type=$file, required)
$ type cl1b.pas
[inherit('sys$library:pascal$lib_routines', 
'sys$library:pascal$cli_routines')]
program cl1b(input,output);

type
    pstr = varying [255] of char;

var
    cl1bsup : [external] integer;
    cmdlin : pstr;
    f : pstr;
    i : integer;

begin
    lib$get_foreign(cmdlin.body, , cmdlin.length);
    for i := 1 to length(cmdlin) do begin
       if cmdlin[i] = '-' then begin
          cmdlin[i] := '/';
       end else if cmdlin[i] = ' ' then begin
          cmdlin[i] := '=';
       end;
    end;
    cli$dcl_parse('cl1b ' + cmdlin, cl1bsup);
    cli$get_value('F', f.body, f.length);
    writeln('Pascal CL1B F=', f);
end.
$ set comm/obj cl1bsup
$ pas cl1b
$ link cl1b + cl1bsup
$ cl1b :== $sys$disk:[]cl1b
$ cl1b /f=z.z
Pascal CL1B F=z.z
$ cl1b -f z.z
Pascal CL1B F=z.z
$ type cl2.pas
[inherit('sys$library:pascal$lib_routines')]
program cl1b(input,output);

type
    pstr = varying [255] of char;

var
    cmdlin : pstr;
    f : pstr;
    ix : integer;

begin
    lib$get_foreign(cmdlin.body, , cmdlin.length);
    ix := index(cmdlin, '-f ');
    f := substr(cmdlin, ix + 3, length(cmdlin) - ix - 2);
    writeln('Pascal CL2 F=', f);
end.
$ pas cl2
$ link cl2
$ cl2 :== $sys$disk:[]cl2
$ cl2 -f z.z
Pascal CL2 F=z.z

C demo:

$ type cl1a.cld
define verb cl1a
     image "sys$disk:[]cl1a"
     qualifier f, value(type=$file, required)
$ set comm cl1a
$ type cl1a.c
#include <stdio.h>
#include <stdint.h>

#include <descrip.h>
#include <cli$routines.h>

int main()
{
     char f[255];
     int16_t flen;
     $DESCRIPTOR(fnamedesc, "F");
     $DESCRIPTOR(fdesc, f);
     cli$get_value(&fnamedesc, &fdesc, &flen);
     f[flen] = 0;
     printf("C CL1A F=%s\n", f);
     return 0;
}

$ cc cl1a
$ link cl1a
$ cl1a /f=z.z
C CL1A F=z.z
$ type cl1bsup.cld
module cl1bsup
define verb cl1b
     qualifier f, value(type=$file, required)
$ type cl1b.c
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include <descrip.h>
#include <lib$routines.h>
#include <cli$routines.h>

globalvalue int32_t cl1bsup;

int main()
{
     char cmdlin[256];
     int16_t cmdlinlen;
     $DESCRIPTOR(cmdlindesc, cmdlin);
     lib$get_foreign(&cmdlindesc, 0, &cmdlinlen);
     cmdlin[cmdlinlen] = 0;
     for(int i = 0; i < cmdlinlen; i++)
     {
         if(cmdlin[i] == '-') cmdlin[i] = '/';
         if(cmdlin[i] == ' ') cmdlin[i] = '=';
     }
     char cmdlin2[256];
     $DESCRIPTOR(cmdlin2desc, cmdlin2);
     strcpy(cmdlin2, "CL1B ");
     strcat(cmdlin2, cmdlin);
     cmdlin2desc.dsc$w_length = strlen(cmdlin2);
     cli$dcl_parse(&cmdlin2desc, cl1bsup);
     char f[256];
     int16_t flen;
     $DESCRIPTOR(fnamedesc, "F");
     $DESCRIPTOR(fdesc, f);
     cli$get_value(&fnamedesc, &fdesc, &flen);
     f[flen] = 0;
     printf("C CL1B F=%s\n", f);
     return 0;
}

$ set comm/obj cl1bsup
$ cc cl1b
$ link cl1b + cl1bsup
$ cl1b :== $sys$disk:[]cl1b
$ cl1b /f=z.z
C CL1B F=z.z
$ cl1b -f z.z
C CL1B F=z.z
$ type cl2.c
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include <descrip.h>
#include <lib$routines.h>

int main()
{
     char cmdlin[256];
     int16_t cmdlinlen;
     $DESCRIPTOR(cmdlindesc, cmdlin);
     lib$get_foreign(&cmdlindesc, 0, &cmdlinlen);
     cmdlin[cmdlinlen] = 0;
     char *p = strstr(cmdlin, "-f");
     char f[256];
     strcpy(f, p + 3);
     printf("C CL2 F=%s\n", f);
     return 0;
}

$ cc cl2
$ link cl2
$ cl2 :== $sys$disk:[]cl2
$ cl2 -f z.z
C CL2 F=z.z
$ type cl3.c
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

int main(int argc, char *argv[])
{
     char *f = "";
     bool fnext = false;
     for(int i = 0; i < argc; i++)
     {
         if(fnext) f = argv[i];
         fnext = strcmp(argv[i], "-f") == 0;
     }
     printf("C CL3 F=%s\n", f);
     return 0;
}
$ cc cl3
$ link cl3
$ cl3 :== $sys$disk:[]cl3
$ cl3 -f z.z
C CL3 F=z.z

Python demo:

$ type cl3.py
import sys

f = ''
fnext = False
for arg in sys.argv:
     if fnext:
         f = arg
     fnext = arg == '-f'
print(f"Python C3 F={f}")
$ python cl3.py -f z.z
Python C3 F=z.z

(the CL2 parsing examples are not robust, but that is another
topic)

Arne



More information about the Info-vax mailing list