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