Discussion:
Command Line Versus Command Line
(too old to reply)
Lawrence D'Oliveiro
2024-05-23 04:16:57 UTC
Permalink
The traditional DEC concept of a “command line” is a simple string/array
of characters. For example, you see this in the “foreign” command buffer
in VMS, which can be passed from one program to another.

VMS really wants you to use a DCL-style syntax for your command lines. It
does weird things like uppercasing text in the command buffer, except for
parts in quotes, which are left as-is, but then the quotes are also left
in place, so command-line parsers have to worry about dealing with text
that might or might not be quoted.

And also implicit in all this is that the command will be executed by
sending a request to some CLI/shell.

Compare the Unix concept of a command line: it is not a “line” at all, but
an array of strings. One program can directly execute another without
having to go through a shell: it can construct and pass this argument
array without having to worry about “special” characters that might be
(mis)interpreted by some CLI/shell intermediary -- unless you choose to go
through such an intermediary.

One might argue that DCL-style syntax is much more sophisticated than what
is supported by your typical *nix command-line program. But then again,
the sheer wealth of *nix command-line tools, compared to what is available
on VMS, shows that the *nix-style command line is far more versatile. In
*nix, most of the sophistication is delegated to the shell language that
is used to invoke these programs, while the programs themselves can
usually parse their arguments using a fairly modest amount of code, with
little or no need for some more sophisticated command-line-handling
library.

And just to add to the fun, CP/M, MS-DOS and then Windows NT all copied
the basic DEC-style command line architecture, while neglecting the
crucial part of a built-in command parser to handle more sophisticated
syntax. So Microsoft’s recent rediscovery that “the command line is cool”
remains hampered by this brain-dead underlying architecture.
Scott Dorsey
2024-05-23 10:19:27 UTC
Permalink
Post by Lawrence D'Oliveiro
And just to add to the fun, CP/M, MS-DOS and then Windows NT all copied
the basic DEC-style command line architecture, while neglecting the
crucial part of a built-in command parser to handle more sophisticated
syntax. So Microsoft’s recent rediscovery that “the command line is cool”
remains hampered by this brain-dead underlying architecture.
To give them credit, Microsoft finally realized that command.com was
hopeless and started from first principles with Powershell. Powershell
is easily as good as anything DEC had in 1974 and as such is a huge
advance for the desktop.
--scott
--
"C'est un Nagra. C'est suisse, et tres, tres precis."
Arne Vajhøj
2024-05-23 13:28:16 UTC
Permalink
Post by Scott Dorsey
Post by Lawrence D'Oliveiro
And just to add to the fun, CP/M, MS-DOS and then Windows NT all copied
the basic DEC-style command line architecture, while neglecting the
crucial part of a built-in command parser to handle more sophisticated
syntax. So Microsoft’s recent rediscovery that “the command line is cool”
remains hampered by this brain-dead underlying architecture.
To give them credit, Microsoft finally realized that command.com was
hopeless and started from first principles with Powershell. Powershell
is easily as good as anything DEC had in 1974 and as such is a huge
advance for the desktop.
Just so that everyone know the history:

command.com - 1980
cmd.exe - 1987 (backwards compatible with command.com, but significantly
extended functionality, often via obscure syntax though)
Windows Script Host / VBScript - 1995 (not interactive, scripts only,
but very powerful via WMI)
PowerShell - 2006

Not super new.

:-)

Arne
Michael S
2024-05-23 16:02:39 UTC
Permalink
On Thu, 23 May 2024 09:28:16 -0400
Post by Arne Vajhøj
Post by Scott Dorsey
Post by Lawrence D'Oliveiro
And just to add to the fun, CP/M, MS-DOS and then Windows NT all
copied the basic DEC-style command line architecture, while
neglecting the crucial part of a built-in command parser to handle
more sophisticated syntax. So Microsoft’s recent rediscovery that
“the command line is cool” remains hampered by this brain-dead
underlying architecture.
To give them credit, Microsoft finally realized that command.com was
hopeless and started from first principles with Powershell.
Powershell is easily as good as anything DEC had in 1974 and as
such is a huge advance for the desktop.
command.com - 1980
cmd.exe - 1987 (backwards compatible with command.com, but
significantly extended functionality, often via obscure syntax though)
Thanks.
Until reading your post I didn't realize that cmd.exe existed before Nt
3.1 (1993).
Personally I often feel that cmd.exe is an improvement on it's
successor.
Post by Arne Vajhøj
Windows Script Host / VBScript - 1995 (not interactive, scripts only,
but very powerful via WMI)
PowerShell - 2006
Not super new.
:-)
Arne
BTW, I tried to figure out what "command line architecture" Lawrence is
talking about, but didn't quite succeed.
As far as Win32 CreateProcess() function is concerned, command line
is just a string. If caller omits applicationName parameter then
system does small amount of parsing to find application name in command
line. But when caller provides applicationName parameter, which is a
most logical thing to do in any non-interactive/non-shell scenario,
then OS does not care at all about content of command line. In both
cases OS does not modify command line and passes it to child
process as is.
So, the only difference between *nix and Windows is array of
ASCIIZ strings (today, of UTF8 strings) vs single, possibly very long,
null-terminated ASCII or UTF16 string.
Arne Vajhøj
2024-05-23 16:41:24 UTC
Permalink
Post by Michael S
On Thu, 23 May 2024 09:28:16 -0400
Post by Arne Vajhøj
Post by Scott Dorsey
Post by Lawrence D'Oliveiro
And just to add to the fun, CP/M, MS-DOS and then Windows NT all
copied the basic DEC-style command line architecture, while
neglecting the crucial part of a built-in command parser to handle
more sophisticated syntax. So Microsoft’s recent rediscovery that
“the command line is cool” remains hampered by this brain-dead
underlying architecture.
To give them credit, Microsoft finally realized that command.com was
hopeless and started from first principles with Powershell.
Powershell is easily as good as anything DEC had in 1974 and as
such is a huge advance for the desktop.
command.com - 1980
cmd.exe - 1987 (backwards compatible with command.com, but
significantly extended functionality, often via obscure syntax though)
Thanks.
Until reading your post I didn't realize that cmd.exe existed before Nt
3.1 (1993).
Personally I often feel that cmd.exe is an improvement on it's
successor.
Post by Arne Vajhøj
Windows Script Host / VBScript - 1995 (not interactive, scripts only,
but very powerful via WMI)
PowerShell - 2006
Not super new.
BTW, I tried to figure out what "command line architecture" Lawrence is
talking about, but didn't quite succeed.
Neither did I.
Post by Michael S
As far as Win32 CreateProcess() function is concerned, command line
is just a string. If caller omits applicationName parameter then
system does small amount of parsing to find application name in command
line. But when caller provides applicationName parameter, which is a
most logical thing to do in any non-interactive/non-shell scenario,
then OS does not care at all about content of command line. In both
cases OS does not modify command line and passes it to child
process as is.
The concept is pretty common.

VMS LIB$SPAWN and C system also use full command line.
Post by Michael S
So, the only difference between *nix and Windows is array of
ASCIIZ strings (today, of UTF8 strings) vs single, possibly very long,
null-terminated ASCII or UTF16 string.
In most practical usages it does not matter at all.

But it can matter if one needs to construct a single command
line from multiple arguments and those arguments contain
spaces or shell operators.

Example:
https://nvd.nist.gov/vuln/detail/CVE-2024-24576

Arne
chrisq
2024-05-23 17:37:35 UTC
Permalink
Post by Arne Vajhøj
Post by Michael S
On Thu, 23 May 2024 09:28:16 -0400
Post by Arne Vajhøj
Post by Scott Dorsey
Post by Lawrence D'Oliveiro
And just to add to the fun, CP/M, MS-DOS and then Windows NT all
copied the basic DEC-style command line architecture, while
neglecting the crucial part of a built-in command parser to handle
more sophisticated syntax. So Microsoft’s recent rediscovery that
“the command line is cool” remains hampered by this brain-dead
underlying architecture.
To give them credit, Microsoft finally realized that command.com was
hopeless and started from first principles with Powershell.
Powershell is easily as good as anything DEC had in 1974 and as
such is a huge advance for the desktop.
command.com - 1980
cmd.exe - 1987 (backwards compatible with command.com, but
significantly extended functionality, often via obscure syntax though)
Thanks.
Until reading your post I didn't realize that cmd.exe existed before Nt
3.1 (1993).
Personally I often feel that cmd.exe is an improvement on it's
successor.
Post by Arne Vajhøj
Windows Script Host / VBScript - 1995 (not interactive, scripts only,
but very powerful via WMI)
PowerShell - 2006
Not super new.
BTW, I tried to figure out what "command line architecture" Lawrence is
talking about, but didn't quite succeed.
Neither did I.
Post by Michael S
As far as Win32 CreateProcess() function is concerned, command line
is just a string. If caller omits applicationName parameter then
system does small amount of parsing to find application name in command
line. But when caller provides applicationName parameter, which is a
most logical thing to do in any non-interactive/non-shell scenario,
then OS does not care at all about content of command line. In both
cases OS does not modify command line and passes it to child
process as is.
The concept is pretty common.
VMS LIB$SPAWN and C system also use full command line.
Post by Michael S
So, the only difference between *nix and Windows is array of
ASCIIZ strings (today, of UTF8 strings) vs single, possibly very long,
null-terminated ASCII or UTF16 string.
In most practical usages it does not matter at all.
But it can matter if one needs to construct a single command
line from multiple arguments and those arguments contain
spaces or shell operators.
  https://nvd.nist.gov/vuln/detail/CVE-2024-24576
Arne
Unix shell just uses single quotes to enclose spaced arguments, which
are then removed by the shell, before storing the argument as pointer
to a null terminated array of char. Neat, clean and simple.

At parser level, there has to be some agreed token(s) to delimit the
command line arguments and spaces or tabs are still the simplest way
to handle that. Things like spaces in filenames are a bit of an
abomination, but thank uSoft for that. How would you handle it ?...

Chris
Michael S
2024-05-23 19:38:53 UTC
Permalink
On Thu, 23 May 2024 18:37:35 +0100
Post by chrisq
Post by Arne Vajhøj
Post by Michael S
On Thu, 23 May 2024 09:28:16 -0400
Post by Arne Vajhøj
Post by Scott Dorsey
Post by Lawrence D'Oliveiro
And just to add to the fun, CP/M, MS-DOS and then Windows NT all
copied the basic DEC-style command line architecture, while
neglecting the crucial part of a built-in command parser to
handle more sophisticated syntax. So Microsoft’s recent
rediscovery that “the command line is cool” remains hampered by
this brain-dead underlying architecture.
To give them credit, Microsoft finally realized that command.com
was hopeless and started from first principles with Powershell.
Powershell is easily as good as anything DEC had in 1974 and as
such is a huge advance for the desktop.
command.com - 1980
cmd.exe - 1987 (backwards compatible with command.com, but
significantly extended functionality, often via obscure syntax though)
Thanks.
Until reading your post I didn't realize that cmd.exe existed
before Nt 3.1 (1993).
Personally I often feel that cmd.exe is an improvement on it's
successor.
Post by Arne Vajhøj
Windows Script Host / VBScript - 1995 (not interactive, scripts
only, but very powerful via WMI)
PowerShell - 2006
Not super new.
BTW, I tried to figure out what "command line architecture"
Lawrence is talking about, but didn't quite succeed.
Neither did I.
Post by Michael S
As far as Win32 CreateProcess() function is concerned, command line
is just a string. If caller omits applicationName parameter then
system does small amount of parsing to find application name in
command line. But when caller provides applicationName parameter,
which is a most logical thing to do in any
non-interactive/non-shell scenario, then OS does not care at all
about content of command line. In both cases OS does not modify
command line and passes it to child process as is.
The concept is pretty common.
VMS LIB$SPAWN and C system also use full command line.
Post by Michael S
So, the only difference between *nix and Windows is array of
ASCIIZ strings (today, of UTF8 strings) vs single, possibly very
long, null-terminated ASCII or UTF16 string.
In most practical usages it does not matter at all.
But it can matter if one needs to construct a single command
line from multiple arguments and those arguments contain
spaces or shell operators.
  https://nvd.nist.gov/vuln/detail/CVE-2024-24576
Arne
Unix shell just uses single quotes to enclose spaced arguments, which
are then removed by the shell, before storing the argument as pointer
to a null terminated array of char. Neat, clean and simple.
Moving pointers between address spaces is never very simple. Moving
variable-length arrays of pointers adds another level of complexity on
top of it.
Not a rocket science, but not as simple as copying a single monolithic
string.
Post by chrisq
At parser level, there has to be some agreed token(s) to delimit the
command line arguments and spaces or tabs are still the simplest way
to handle that. Things like spaces in filenames are a bit of an
abomination, but thank uSoft for that. How would you handle it ?...
Chris
Microsoft's own conventions are documented here:
https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170
They provide default parser
https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
And, of course, they have it built-in in C start-up library code.

But when you have control over both parent and child processes, you do
not have to follow Microsoft's conventions if you prefer some different
way.
chrisq
2024-05-24 11:38:01 UTC
Permalink
On 5/23/24 20:38, Michael S wrote:

<snipped>
Post by Michael S
Moving pointers between address spaces is never very simple. Moving
variable-length arrays of pointers adds another level of complexity on
top of it.
Not a rocket science, but not as simple as copying a single monolithic
string.
Perhaps, but from a programmers point of view, the elegance of the shell
command line method is that on entry to main(), both the arg count and
args are separated and ready to verify and use. All the background
legwork is done for you.

If you just hand the whole command line to a program, you have to
parse and separate the args, for every program you write, very bug
prone and added effort. Whole idea of good system design is to free
the programmer from repetition, and mundane stuff that the system
should deal with...

Chris
Arne Vajhøj
2024-05-24 12:13:04 UTC
Permalink
Post by chrisq
Perhaps, but from a programmers point of view, the elegance of the shell
command line method is that on entry to main(), both the arg count and
args are separated and ready to verify and use. All the background
legwork is done for you.
If you just hand the whole command line to a program, you have to
parse and separate the args, for every program you write, very bug
prone and added effort. Whole idea of good system design is to free
the programmer from repetition, and mundane stuff that the system
should deal with...
I don't think it is a big problem on the consuming side - the
problem is on the producing side.

A lot of the most popular programming languages provides
split arguments to the program also on operating systems where
command line is considered a single string.

And for other languages the operating system likely provide
functionality to do the split. On VMS it is the CLI$
routines. Michael linked to CommandLineToArgv for
Windows.

Of course developers can still fuck it up with
a DIY solution, but if people choose DIY over what
is already provided for them, then they chose.

It is the other direction that is more tricky. You have
N separated arguments and need to produce a command
line that will actually be split into the same N
arguments. Not a super common requirement. But tricky
to get 100% correct.

As illustrated by the Rust issue.

Arne
Michael S
2024-05-24 13:32:25 UTC
Permalink
On Fri, 24 May 2024 08:13:04 -0400
Post by Arne Vajhøj
As illustrated by the Rust issue.
I was not able to figure out what exactly Rust guys were trying to
achieve. Feeding cmd.exe with command line from untrusted source and
expecting no harm sounds like mission impossible.
That is, impossible when you run cmd.exe under privileged account.
It is possible when you run it under sufficiently deprived account, but
that is orthogonal to parsing of command line.
Arne Vajhøj
2024-05-24 13:42:49 UTC
Permalink
Post by Michael S
On Fri, 24 May 2024 08:13:04 -0400
Post by Arne Vajhøj
As illustrated by the Rust issue.
I was not able to figure out what exactly Rust guys were trying to
achieve. Feeding cmd.exe with command line from untrusted source and
expecting no harm sounds like mission impossible.
That is, impossible when you run cmd.exe under privileged account.
It is possible when you run it under sufficiently deprived account, but
that is orthogonal to parsing of command line.
To my very limited understanding then the problem was that:

Command::new("program").arg("a1").arg("a2")

ended up as:

program a1 a2

not:

program "a1" "a2"

which is fine but is also meant that:

Command::new("program").arg("a1").arg("a2 x y z")

ended up as:

program a1 a2 x y z

not:

program "a1" "a2 x y z"

which is not so fine.

It is definitely a functional problem.

And if the security depends on program treating the two
arguments securely, then it is also a security problem.

Arne
Michael S
2024-05-24 15:11:25 UTC
Permalink
On Fri, 24 May 2024 09:42:49 -0400
Post by Arne Vajhøj
Post by Michael S
On Fri, 24 May 2024 08:13:04 -0400
Post by Arne Vajhøj
As illustrated by the Rust issue.
I was not able to figure out what exactly Rust guys were trying to
achieve. Feeding cmd.exe with command line from untrusted source and
expecting no harm sounds like mission impossible.
That is, impossible when you run cmd.exe under privileged account.
It is possible when you run it under sufficiently deprived account,
but that is orthogonal to parsing of command line.
Command::new("program").arg("a1").arg("a2")
program a1 a2
program "a1" "a2"
Command::new("program").arg("a1").arg("a2 x y z")
program a1 a2 x y z
program "a1" "a2 x y z"
which is not so fine.
It is definitely a functional problem.
And if the security depends on program treating the two
arguments securely, then it is also a security problem.
Arne
It seems like the simplest solution is to not try to run batch files by
means of spawn("cmd.exe", ...) or CreateProcess("cmd.exe", ...).
They could have use more specialized function: system() from C RTL or
ShellExecuteEx() from Win32 API. The former is easier to use, the later
works as expected in wider range of host console environments, most
importantly, it works from mintty.
Arne Vajhøj
2024-05-24 15:28:05 UTC
Permalink
Post by Michael S
On Fri, 24 May 2024 09:42:49 -0400
Post by Arne Vajhøj
Post by Michael S
On Fri, 24 May 2024 08:13:04 -0400
Post by Arne Vajhøj
As illustrated by the Rust issue.
I was not able to figure out what exactly Rust guys were trying to
achieve. Feeding cmd.exe with command line from untrusted source and
expecting no harm sounds like mission impossible.
That is, impossible when you run cmd.exe under privileged account.
It is possible when you run it under sufficiently deprived account,
but that is orthogonal to parsing of command line.
Command::new("program").arg("a1").arg("a2")
program a1 a2
program "a1" "a2"
Command::new("program").arg("a1").arg("a2 x y z")
program a1 a2 x y z
program "a1" "a2 x y z"
which is not so fine.
It is definitely a functional problem.
And if the security depends on program treating the two
arguments securely, then it is also a security problem.
It seems like the simplest solution is to not try to run batch files by
means of spawn("cmd.exe", ...) or CreateProcess("cmd.exe", ...).
They could have use more specialized function: system() from C RTL or
ShellExecuteEx() from Win32 API. The former is easier to use, the later
works as expected in wider range of host console environments, most
importantly, it works from mintty.
Both system and ShellExecuteEx still take all parameters as a single
string, which require some non-trivial conversion from array of
parameters to that string.

Arne
Michael S
2024-05-24 16:05:40 UTC
Permalink
On Fri, 24 May 2024 11:28:05 -0400
Post by Arne Vajhøj
Post by Michael S
On Fri, 24 May 2024 09:42:49 -0400
Post by Arne Vajhøj
Post by Michael S
On Fri, 24 May 2024 08:13:04 -0400
Post by Arne Vajhøj
As illustrated by the Rust issue.
I was not able to figure out what exactly Rust guys were trying to
achieve. Feeding cmd.exe with command line from untrusted source
and expecting no harm sounds like mission impossible.
That is, impossible when you run cmd.exe under privileged account.
It is possible when you run it under sufficiently deprived
account, but that is orthogonal to parsing of command line.
Command::new("program").arg("a1").arg("a2")
program a1 a2
program "a1" "a2"
Command::new("program").arg("a1").arg("a2 x y z")
program a1 a2 x y z
program "a1" "a2 x y z"
which is not so fine.
It is definitely a functional problem.
And if the security depends on program treating the two
arguments securely, then it is also a security problem.
It seems like the simplest solution is to not try to run batch
files by means of spawn("cmd.exe", ...) or CreateProcess("cmd.exe",
...). They could have use more specialized function: system() from
C RTL or ShellExecuteEx() from Win32 API. The former is easier to
use, the later works as expected in wider range of host console
environments, most importantly, it works from mintty.
Both system and ShellExecuteEx still take all parameters as a single
string, which require some non-trivial conversion from array of
parameters to that string.
Arne
Conversion is actually trivial - dumb double-quoting.
There is an opposite problem - the child will get parameters together
with double quotes.
But at least the biggest of their problems (complicated undocumented
rules of cmd.exe) goes away. The rule, applied by CommandLineToArgv()
are non-complicated and documented.
Arne Vajhøj
2024-05-25 00:02:46 UTC
Permalink
Post by Michael S
On Fri, 24 May 2024 11:28:05 -0400
Post by Arne Vajhøj
Post by Michael S
It seems like the simplest solution is to not try to run batch
files by means of spawn("cmd.exe", ...) or CreateProcess("cmd.exe",
...). They could have use more specialized function: system() from
C RTL or ShellExecuteEx() from Win32 API. The former is easier to
use, the later works as expected in wider range of host console
environments, most importantly, it works from mintty.
Both system and ShellExecuteEx still take all parameters as a single
string, which require some non-trivial conversion from array of
parameters to that string.
Conversion is actually trivial - dumb double-quoting.
Maybe.
Post by Michael S
There is an opposite problem - the child will get parameters together
with double quotes.
They should be stripped by the shell.

Arne
Michael S
2024-05-25 23:31:00 UTC
Permalink
On Fri, 24 May 2024 20:02:46 -0400
Post by Arne Vajhøj
Post by Michael S
On Fri, 24 May 2024 11:28:05 -0400
Post by Arne Vajhøj
Post by Michael S
It seems like the simplest solution is to not try to run batch
files by means of spawn("cmd.exe", ...) or
CreateProcess("cmd.exe", ...). They could have use more
specialized function: system() from C RTL or ShellExecuteEx()
from Win32 API. The former is easier to use, the later works as
expected in wider range of host console environments, most
importantly, it works from mintty.
Both system and ShellExecuteEx still take all parameters as a
single string, which require some non-trivial conversion from
array of parameters to that string.
Conversion is actually trivial - dumb double-quoting.
Maybe.
Post by Michael S
There is an opposite problem - the child will get parameters
together with double quotes.
They should be stripped by the shell.
It does not work like that.
For programs that use CommandLineToArgv() either directly or via C-alike
sturtup code, you'll get double quotes stripped. But internal commands
in batch file, e.g echo, will see the double quotes. It's probably
the most harmful for batch files that use 'IF %N==string' logic.
Post by Arne Vajhøj
Arne
Lawrence D'Oliveiro
2024-05-25 00:20:43 UTC
Permalink
Post by Michael S
Conversion is actually trivial - dumb double-quoting.
What do you do if the value has quotes?
Arne Vajhøj
2024-05-25 00:27:38 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Michael S
Conversion is actually trivial - dumb double-quoting.
What do you do if the value has quotes?
1 double-quote -> 2 double-quotes

Arne
Lawrence D'Oliveiro
2024-05-25 00:42:57 UTC
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
Post by Michael S
Conversion is actually trivial - dumb double-quoting.
What do you do if the value has quotes?
1 double-quote -> 2 double-quotes
Assuming the receiving end implements the same convention. Otherwise you
have trouble.
Arne Vajhøj
2024-05-25 00:58:23 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
Post by Michael S
Conversion is actually trivial - dumb double-quoting.
What do you do if the value has quotes?
1 double-quote -> 2 double-quotes
Assuming the receiving end implements the same convention. Otherwise you
have trouble.
????

The receiving end does not see any of the added double quotes.

Arne
Arne Vajhøj
2024-05-25 01:11:27 UTC
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
Post by Michael S
Conversion is actually trivial - dumb double-quoting.
What do you do if the value has quotes?
1 double-quote -> 2 double-quotes
Assuming the receiving end implements the same convention. Otherwise you
have trouble.
????
The receiving end does not see any of the added double quotes.
Windows use ' -> "" and Linux (bash) use " -> \".

But receiver does not see any of it.

Windows:

C:\Work\Python>type arg.py
from sys import argv

for i in range(len(argv)):
print('%d : %s' % (i, argv[i]))

C:\Work\Python>python arg.py a "b c" "d""e"
0 : arg.py
1 : a
2 : b c
3 : d"e

Linux:

***@arnepc6:~$ cat arg.py
from sys import argv

for i in range(len(argv)):
print('%d : %s' % (i, argv[i]))
***@arnepc6:~$ python3 arg.py a "b c" "d\"e"
0 : arg.py
1 : a
2 : b c
3 : d"e

Arne
Arne Vajhøj
2024-05-25 01:12:50 UTC
Permalink
Post by Arne Vajhøj
Windows use ' -> "" and Linux (bash) use " -> \".
Windows use " -> "" and Linux (bash) use " -> \".

Arne
Lawrence D'Oliveiro
2024-05-25 01:35:11 UTC
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
Post by Michael S
Conversion is actually trivial - dumb double-quoting.
What do you do if the value has quotes?
1 double-quote -> 2 double-quotes
Assuming the receiving end implements the same convention. Otherwise
you have trouble.
????
The receiving end does not see any of the added double quotes.
It does on VMS, and I think on Windows too, at the raw-buffer level.
Craig A. Berry
2024-05-24 16:45:27 UTC
Permalink
Post by Arne Vajhøj
Post by Michael S
On Fri, 24 May 2024 09:42:49 -0400
Post by Arne Vajhøj
Post by Michael S
On Fri, 24 May 2024 08:13:04 -0400
Post by Arne Vajhøj
As illustrated by the Rust issue.
I was not able to figure out what exactly Rust guys were trying to
achieve. Feeding cmd.exe with command line from untrusted source and
expecting no harm sounds like mission impossible.
That is, impossible when you run cmd.exe under privileged account.
It is possible when you run it under sufficiently deprived account,
but that is orthogonal to parsing of command line.
Command::new("program").arg("a1").arg("a2")
program a1 a2
program "a1" "a2"
Command::new("program").arg("a1").arg("a2 x y z")
program a1 a2 x y z
program "a1" "a2 x y z"
which is not so fine.
It is definitely a functional problem.
And if the security depends on program treating the two
arguments securely, then it is also a security problem.
It seems like the simplest solution is to not try to run batch files by
means of spawn("cmd.exe", ...) or CreateProcess("cmd.exe", ...).
They could have use more specialized function: system() from C RTL or
ShellExecuteEx() from Win32 API. The former is easier to use, the later
works as expected in wider range of host console environments, most
importantly, it works from mintty.
Both system and ShellExecuteEx still take all parameters as a single
string, which require some non-trivial conversion from array of
parameters to that string.
The Windows CRT has _spawn() [1] which looks pretty similar to
posix_spawn() [2]. With either one you pass arguments or an array of
arguments rather than a complete command line. From the docs it sounds
like on Windows the arguments get concatenated under the hood, but at
least "someone else" is doing that rather than each program having to
take responsibility for it.

There was a mention sometime in the last couple of years that
posix_spawn() is being added to the VMS CRTL.

[1]
https://learn.microsoft.com/en-us/cpp/c-runtime-library/spawn-wspawn-functions?view=msvc-170

[2]
https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/posix_spawn.html
Arne Vajhøj
2024-05-25 00:12:39 UTC
Permalink
Post by Craig A. Berry
Post by Arne Vajhøj
Post by Michael S
It seems like the simplest solution is to not try to run batch files by
means of spawn("cmd.exe", ...) or CreateProcess("cmd.exe", ...).
They could have use more specialized function: system() from C RTL or
ShellExecuteEx() from Win32 API. The former is easier to use, the later
works as expected in wider range of host console environments, most
importantly, it works from mintty.
Both system and ShellExecuteEx still take all parameters as a single
string, which require some non-trivial conversion from array of
parameters to that string.
The Windows CRT has _spawn() [1] which looks pretty similar to
posix_spawn() [2].  With either one you pass arguments or an array of
arguments rather than a complete command line.  From the docs it sounds
like on Windows the arguments get concatenated under the hood, but at
least "someone else" is doing that rather than each program having to
take responsibility for it.
[1]
https://learn.microsoft.com/en-us/cpp/c-runtime-library/spawn-wspawn-functions?view=msvc-170

That looked very promising.

But a test did keep the optimism.

parmdump.c:

#include <stdio.h>

int main(int argc, char *argv[])
{
for(int i = 0; i < argc; i++)
{
printf("%d : %s\n", i, argv[i]);
}
return 0;
}

demo.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <process.h>

void bad(const char *a1, const char *a2)
{
printf("Bad\n");
char cmd[1000];
snprintf(cmd, sizeof(cmd), "parmdump %s %s", a1, a2);
system(cmd);
}

void mystrcat(char *to, const char *from)
{
int ix = strlen(to);
for(int i = 0; i <= strlen(from); i++)
{
if(from[i] == '"')
{
to[ix] = from[i];
ix++;
to[ix] = from[i];
ix++;
}
else
{
to[ix] = from[i];
ix++;
}
}
}

void hack(const char *a1, const char *a2)
{
char *pto, *pfrom;
printf("Hack\n");
char cmd[1000];
strcpy(cmd, "parmdump \"");
mystrcat(cmd, a1);
strcat(cmd, "\" \"");
mystrcat(cmd, a2);
strcat(cmd, "\"");
system(cmd);
}

void latest_l(const char *a1, const char *a2)
{
printf("Latest (l)\n");
_spawnl(_P_WAIT, "parmdump", a1, a2, NULL);
}

void latest_v(const char *a1, const char *a2)
{
const char *argv[] = { a1, a2, NULL };
printf("Latest (v)\n");
_spawnv(_P_WAIT, "parmdump", argv);
}

void test(const char *a1, const char *a2)
{
bad(a1, a2);
hack(a1, a2);
latest_l(a1, a2);
latest_v(a1, a2);
}

int main()
{
test("a", "b");
test("a", "b c d");
test("a", "b\"c");
return 0;
}

Output:

Bad
0 : parmdump
1 : a
2 : b
Hack
0 : parmdump
1 : a
2 : b
Latest (l)
0 : a
1 : b
Latest (v)
0 : a
1 : b
Bad
0 : parmdump
1 : a
2 : b
3 : c
4 : d
Hack
0 : parmdump
1 : a
2 : b c d
Latest (l)
0 : a
1 : b
2 : c
3 : d
Latest (v)
0 : a
1 : b
2 : c
3 : d
Bad
0 : parmdump
1 : a
2 : bc
Hack
0 : parmdump
1 : a
2 : b"c
Latest (l)
0 : a
1 : bc
Latest (v)
0 : a
1 : bc

Unless I am doing something wrong then these _spawn functions still
expect the caller to handle the proper quoting to avoid problems.
Post by Craig A. Berry
There was a mention sometime in the last couple of years that
posix_spawn() is being added to the VMS CRTL.
[2]
https://pubs.opengroup.org/onlinepubs/9699919799.2013edition/functions/posix_spawn.html
I don't see it in 9.2-2.

Arne
Lawrence D'Oliveiro
2024-05-24 23:05:28 UTC
Permalink
It seems like the simplest solution is to not try to run batch files ...
That was part of the problem. Remember that, in Windows, if you pass a
command with no extension, it tries appending things like .BAT and .EXE,
to try to find a match. So you can’t really be sure beforehand that the
command will *not* run a batch file.
Lawrence D'Oliveiro
2024-05-24 23:03:50 UTC
Permalink
Post by Michael S
I was not able to figure out what exactly Rust guys were trying to
achieve.
They were trying to achieve the same result you get on a *nix system with
essentially no work at all.
Dan Cross
2024-05-28 14:15:18 UTC
Permalink
Post by Michael S
On Thu, 23 May 2024 18:37:35 +0100
Post by chrisq
Post by Arne Vajhøj
Post by Michael S
On Thu, 23 May 2024 09:28:16 -0400
Post by Arne Vajhøj
Post by Scott Dorsey
Post by Lawrence D'Oliveiro
And just to add to the fun, CP/M, MS-DOS and then Windows NT all
copied the basic DEC-style command line architecture, while
neglecting the crucial part of a built-in command parser to
handle more sophisticated syntax. So Microsoft’s recent
rediscovery that “the command line is cool” remains hampered by
this brain-dead underlying architecture.
To give them credit, Microsoft finally realized that command.com
was hopeless and started from first principles with Powershell.
Powershell is easily as good as anything DEC had in 1974 and as
such is a huge advance for the desktop.
command.com - 1980
cmd.exe - 1987 (backwards compatible with command.com, but
significantly extended functionality, often via obscure syntax though)
Thanks.
Until reading your post I didn't realize that cmd.exe existed
before Nt 3.1 (1993).
Personally I often feel that cmd.exe is an improvement on it's
successor.
Post by Arne Vajhøj
Windows Script Host / VBScript - 1995 (not interactive, scripts
only, but very powerful via WMI)
PowerShell - 2006
Not super new.
BTW, I tried to figure out what "command line architecture"
Lawrence is talking about, but didn't quite succeed.
Neither did I.
Post by Michael S
As far as Win32 CreateProcess() function is concerned, command line
is just a string. If caller omits applicationName parameter then
system does small amount of parsing to find application name in
command line. But when caller provides applicationName parameter,
which is a most logical thing to do in any
non-interactive/non-shell scenario, then OS does not care at all
about content of command line. In both cases OS does not modify
command line and passes it to child process as is.
The concept is pretty common.
VMS LIB$SPAWN and C system also use full command line.
Post by Michael S
So, the only difference between *nix and Windows is array of
ASCIIZ strings (today, of UTF8 strings) vs single, possibly very
long, null-terminated ASCII or UTF16 string.
In most practical usages it does not matter at all.
But it can matter if one needs to construct a single command
line from multiple arguments and those arguments contain
spaces or shell operators.
  https://nvd.nist.gov/vuln/detail/CVE-2024-24576
Unix shell just uses single quotes to enclose spaced arguments, which
are then removed by the shell, before storing the argument as pointer
to a null terminated array of char. Neat, clean and simple.
Moving pointers between address spaces is never very simple. Moving
variable-length arrays of pointers adds another level of complexity on
top of it.
I'm not sure how that's particularly relevant; moving argument
strings between address spaces in Unix is handled by the kernel,
not userspace programs (which call a library routine to invoke
the relevant system call). The above quote was about
tokenization, which in Unix shells tends to be very simple.

- Dan C.

Bob Eager
2024-05-23 21:28:23 UTC
Permalink
Post by chrisq
Unix shell just uses single quotes to enclose spaced arguments, which
are then removed by the shell, before storing the argument as pointer to
a null terminated array of char. Neat, clean and simple.
Actually, single quotes OR double quotes.

The difference is that single quotes prevent further interpretation of the
arguments, and double quotes don't (so that you can have shell variables
in there, for instance)
Lawrence D'Oliveiro
2024-05-23 22:45:34 UTC
Permalink
Post by Bob Eager
The difference is that single quotes prevent further interpretation of
the arguments, and double quotes don't (so that you can have shell
variables in there, for instance)
And what happens if you have no quotes at all?

Then the variable value is subject to word-splitting, which it is not if
it is within double quotes.

The nice thing is, that’s all there is to the variable-substitution rules.
“Special” characters in variable values have no special significance.
Lawrence D'Oliveiro
2024-05-23 22:43:30 UTC
Permalink
Post by chrisq
Unix shell just uses single quotes to enclose spaced arguments, which
are then removed by the shell, before storing the argument as pointer to
a null terminated array of char. Neat, clean and simple.
Actually, there’s a bit more to it than that, if you want to avoid the
pitfalls.

Which is why it is often so much simpler just to execute a program
directly, without going through a shell, if you don’t need the
capabilities of the shell language as such.
Lawrence D'Oliveiro
2024-05-23 22:47:13 UTC
Permalink
Post by Arne Vajhøj
https://nvd.nist.gov/vuln/detail/CVE-2024-24576
Yup, that sounds like the one. Got a full 10 out of 10 vulnerability
score, as I recall.
Lawrence D'Oliveiro
2024-05-23 22:41:37 UTC
Permalink
As far as Win32 CreateProcess() function is concerned, command line is
just a string.
Which is what I said.
If caller omits applicationName parameter then system
does small amount of parsing to find application name in command line.
This is already starting to set off alarm bells ...
But when caller provides applicationName parameter, which is a most
logical thing to do in any non-interactive/non-shell scenario, then OS
does not care at all about content of command line.
There is an issue if the program being executed is CMD.EXE itself. On
*nix, the same array-of-strings rule applies to a shell as to any other
program, but on Windows, the shell has to do its own parsing of the
command line. There was a security vulnerability recently that was related
to this: the vulnerability was present only on Windows, because of this
(mis)feature, not on *nix.
Arne Vajhøj
2024-05-23 19:05:42 UTC
Permalink
Post by Lawrence D'Oliveiro
VMS really wants you to use a DCL-style syntax for your command lines. It
does weird things like uppercasing text in the command buffer, except for
parts in quotes, which are left as-is, but then the quotes are also left
in place, so command-line parsers have to worry about dealing with text
that might or might not be quoted.
That is an oversimplified view on a complex issue.

real: ABC def "GIH" "jkl"
parse=traditional:
CLI : ABC DEF GIH jkl
FC : ABC DEF "GIH" "jkl"
Mix : ABC DEF GIH jkl
C : abc def GIH jkl
DCL : ABC DEF GIH jkl
parse=extended:
CLI : ABC DEF GIH jkl
FC : ABC def "GIH" "jkl"
Mix : ABC DEF GIH jkl
C : ABC def GIH jkl
DCL : ABC DEF GIH jkl

For details see below.

Arne

$ type cli.for
program cli
integer*4 p1len,p2len,p3len,p4len
character*80 p1,p2,p3,p4
call cli$get_value('p1',p1,p1len)
call cli$get_value('p2',p2,p2len)
call cli$get_value('p3',p3,p3len)
call cli$get_value('p4',p4,p4len)
write(*,*) 'CLI : '//p1(1:p1len)//' '//p2(1:p2len)//' '
+ //p3(1:p3len)//' '//p4(1:p4len)
end
$ type cli.cld
define verb cli
image "sys$disk:[]cli.exe"
parameter p1
parameter p2
parameter p3
parameter p4
$ for cli
$ link cli
$ set comm cli.cld
$ type mix.for
program mix
integer*4 cmdlen
character*80 cmd
integer*4 p1len,p2len,p3len,p4len
character*80 p1,p2,p3,p4
external mixcld
call lib$get_foreign(cmd,,cmdlen)
call cli$dcl_parse('cli '//cmd(1:cmdlen),mixcld,,,)
call cli$get_value('p1',p1,p1len)
call cli$get_value('p2',p2,p2len)
call cli$get_value('p3',p3,p3len)
call cli$get_value('p4',p4,p4len)
write(*,*) 'Mix : '//p1(1:p1len)//' '//p2(1:p2len)//' '
+ //p3(1:p3len)//' '//p4(1:p4len)
end
$ type mixcld.cld
module mixcld
define verb cli
parameter p1
parameter p2
parameter p3
parameter p4
$ for mix
$ set command/object mixcld
$ link mix + mixcld
$ type fc.for
program fc
integer*4 cmdlen
character*80 cmd
call lib$get_foreign(cmd,,cmdlen)
write(*,*) 'FC : '//cmd(1:cmdlen)
end
$ for fc
$ link fc
$ type c.c
#include <stdio.h>

int main(int argc, char *argv[])
{
printf("C :");
for(int i = 1; i < argc; i++) printf(" %s", argv[i]);
printf("\n");
return 0;
}
$ cc c
$ link c
$ type dcl.com
$ write sys$output "DCL : ''p1' ''p2' ''p3' ''p4'"
Craig A. Berry
2024-05-23 22:32:33 UTC
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
VMS really wants you to use a DCL-style syntax for your command lines. It
does weird things like uppercasing text in the command buffer, except for
parts in quotes, which are left as-is, but then the quotes are also left
in place, so command-line parsers have to worry about dealing with text
that might or might not be quoted.
That is an oversimplified view on a complex issue.
real: ABC def "GIH" "jkl"
CLI : ABC DEF GIH jkl
FC  : ABC DEF "GIH" "jkl"
Mix : ABC DEF GIH jkl
C   : abc def GIH jkl
DCL : ABC DEF GIH jkl
CLI : ABC DEF GIH jkl
FC  : ABC def "GIH" "jkl"
Mix : ABC DEF GIH jkl
C   : ABC def GIH jkl
DCL : ABC DEF GIH jkl
For details see below.
Arne
$ type cli.for
      program cli
      integer*4 p1len,p2len,p3len,p4len
      character*80 p1,p2,p3,p4
      call cli$get_value('p1',p1,p1len)
      call cli$get_value('p2',p2,p2len)
      call cli$get_value('p3',p3,p3len)
      call cli$get_value('p4',p4,p4len)
      write(*,*) 'CLI : '//p1(1:p1len)//' '//p2(1:p2len)//' '
     +                   //p3(1:p3len)//' '//p4(1:p4len)
      end
$ type cli.cld
define verb cli
   image "sys$disk:[]cli.exe"
   parameter p1
   parameter p2
   parameter p3
   parameter p4
$ for cli
$ link cli
$ set comm cli.cld
$ type mix.for
      program mix
      integer*4 cmdlen
      character*80 cmd
      integer*4 p1len,p2len,p3len,p4len
      character*80 p1,p2,p3,p4
      external mixcld
      call lib$get_foreign(cmd,,cmdlen)
      call cli$dcl_parse('cli '//cmd(1:cmdlen),mixcld,,,)
      call cli$get_value('p1',p1,p1len)
      call cli$get_value('p2',p2,p2len)
      call cli$get_value('p3',p3,p3len)
      call cli$get_value('p4',p4,p4len)
      write(*,*) 'Mix : '//p1(1:p1len)//' '//p2(1:p2len)//' '
     +                   //p3(1:p3len)//' '//p4(1:p4len)
      end
$ type mixcld.cld
module mixcld
define verb cli
   parameter p1
   parameter p2
   parameter p3
   parameter p4
$ for mix
$ set command/object mixcld
$ link mix + mixcld
$ type fc.for
      program fc
      integer*4 cmdlen
      character*80 cmd
      call lib$get_foreign(cmd,,cmdlen)
      write(*,*) 'FC  : '//cmd(1:cmdlen)
      end
$ for fc
$ link fc
$ type c.c
#include <stdio.h>
int main(int argc, char *argv[])
{
    printf("C   :");
    for(int i = 1; i < argc; i++) printf(" %s", argv[i]);
    printf("\n");
    return 0;
}
$ cc c
$ link c
$ type dcl.com
$ write sys$output "DCL : ''p1' ''p2' ''p3' ''p4'"
Good summary. Did you have DECC$ARGV_PARSE_STYLE defined for the C example?
Arne Vajhøj
2024-05-23 23:22:38 UTC
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
VMS really wants you to use a DCL-style syntax for your command lines. It
does weird things like uppercasing text in the command buffer, except for
parts in quotes, which are left as-is, but then the quotes are also left
in place, so command-line parsers have to worry about dealing with text
that might or might not be quoted.
That is an oversimplified view on a complex issue.
real: ABC def "GIH" "jkl"
CLI : ABC DEF GIH jkl
FC  : ABC DEF "GIH" "jkl"
Mix : ABC DEF GIH jkl
C   : abc def GIH jkl
DCL : ABC DEF GIH jkl
CLI : ABC DEF GIH jkl
FC  : ABC def "GIH" "jkl"
Mix : ABC DEF GIH jkl
C   : ABC def GIH jkl
DCL : ABC DEF GIH jkl
Good summary.  Did you have DECC$ARGV_PARSE_STYLE defined for the C
example?
Looks that way:

$ sh log *pars*

(LNM$PROCESS_TABLE)

"DECC$ARGV_PARSE_STYLE" = "enable"

Note that I have not set it explicit - something
has set it for me.

A little investigation reveals that it is Java:

$ sear disk0:[sys0.syscommon.openjdk$80.com]java$setup.com decc
$ define'defopt' decc$fd_locking true
$ define'defopt' decc$efs_case_preserve enable ! Enable ODS-5 names
$ define'defopt' decc$readdir_dropdotnotype enable
$ define'defopt' decc$efs_charset enable
$ define'defopt' decc$argv_parse_style enable

Arne
Chris Townley
2024-05-23 23:34:55 UTC
Permalink
<snip>
Arne
Why do you all feed the troll/Village Idiot?
--
Chris
Craig A. Berry
2024-05-24 01:46:17 UTC
Permalink
Post by Arne Vajhøj
Post by Arne Vajhøj
real: ABC def "GIH" "jkl"
C   : ABC def GIH jkl
Good summary.  Did you have DECC$ARGV_PARSE_STYLE defined for the C
example?
$ sh log *pars*
(LNM$PROCESS_TABLE)
  "DECC$ARGV_PARSE_STYLE" = "enable"
Note that I have not set it explicit - something
has set it for me.
I think if you deassign it you'll get abc not ABC.
Post by Arne Vajhøj
$ sear disk0:[sys0.syscommon.openjdk$80.com]java$setup.com decc
$ define'defopt' decc$fd_locking true
$ define'defopt' decc$efs_case_preserve enable  ! Enable ODS-5 names
$ define'defopt' decc$readdir_dropdotnotype enable
$ define'defopt' decc$efs_charset enable
$ define'defopt' decc$argv_parse_style enable
Yeah, I believe this has been fixed so OpenJDK now sets what it needs in
in a LIB$INITIALIZE routine, but they left the startup command
procedures as-is.
Arne Vajhøj
2024-05-24 13:00:43 UTC
Permalink
Post by Craig A. Berry
Post by Arne Vajhøj
Post by Arne Vajhøj
real: ABC def "GIH" "jkl"
C   : ABC def GIH jkl
Good summary.  Did you have DECC$ARGV_PARSE_STYLE defined for the C
example?
$ sh log *pars*
(LNM$PROCESS_TABLE)
   "DECC$ARGV_PARSE_STYLE" = "enable"
I think if you deassign it you'll get abc not ABC.
Confirmed.

real: ABC def "GIH" "jkl"
parse=traditional:
CLI : ABC DEF GIH jkl
FC : ABC DEF "GIH" "jkl"
Mix : ABC DEF GIH jkl
C : abc def GIH jkl (DECC$ARGV_PARSE_STYLE = (null))
C : abc def GIH jkl (DECC$ARGV_PARSE_STYLE = enable)
DCL : ABC DEF GIH jkl
parse=extended:
CLI : ABC DEF GIH jkl
FC : ABC def "GIH" "jkl"
Mix : ABC DEF GIH jkl
C : abc def GIH jkl (DECC$ARGV_PARSE_STYLE = (null))
C : ABC def GIH jkl (DECC$ARGV_PARSE_STYLE = enable)
DCL : ABC DEF GIH jkl

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
printf("C :");
for(int i = 1; i < argc; i++) printf(" %s", argv[i]);
printf(" (DECC$ARGV_PARSE_STYLE = %s)\n",
getenv("DECC$ARGV_PARSE_STYLE"));
return 0;
}

Arne
Lawrence D'Oliveiro
2024-05-23 22:36:23 UTC
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
VMS really wants you to use a DCL-style syntax for your command lines.
It does weird things like uppercasing text in the command buffer,
except for parts in quotes, which are left as-is, but then the quotes
are also left in place, so command-line parsers have to worry about
dealing with text that might or might not be quoted.
That is an oversimplified view on a complex issue.
What you have done is rely on library routines to impose a commonality on
the underlying raw command buffer contents. And those library routines are
CLI-specific, as I recall; notwithstanding their “cli$” name prefix, they
are part of DCL itself.

Like I said, on *nix systems, there is no automatic assumption that you
need the intervention of a CLI/shell just for one program to execute
another.
Arne Vajhøj
2024-05-23 23:36:49 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
VMS really wants you to use a DCL-style syntax for your command lines.
It does weird things like uppercasing text in the command buffer,
except for parts in quotes, which are left as-is, but then the quotes
are also left in place, so command-line parsers have to worry about
dealing with text that might or might not be quoted.
That is an oversimplified view on a complex issue.
What you have done is rely on library routines to impose a commonality on
the underlying raw command buffer contents.
Then you have not read it careful enough. The point is that different
approaches have different behavior - the exact opposite of impose
commonality.
Post by Lawrence D'Oliveiro
And those library routines are
CLI-specific,
I doubt that. I would expect any proper CLI on VMS to work with them
Post by Lawrence D'Oliveiro
as I recall; notwithstanding their “cli$” name prefix, they
are part of DCL itself.
No. They are utility routines.

And they reside in STARLET.OLB module CLI$INTERFACE.
Post by Lawrence D'Oliveiro
Like I said, on *nix systems, there is no automatic assumption that you
need the intervention of a CLI/shell just for one program to execute
another.
On *nix there is little difference between a shell and another
program. Huge difference on VMS.

Arne
Arne Vajhøj
2024-05-24 00:00:06 UTC
Permalink
Post by Arne Vajhøj
                                              And those library
routines are
CLI-specific,
I doubt that. I would expect any proper CLI on VMS to work with them
Anyone running VMS 4.x could verify by logging in
with /CLI=MCR and try running a program calling
CLI$ routines.
Post by Arne Vajhøj
as I recall; notwithstanding their “cli$” name prefix, they
are part of DCL itself.
No. They are utility routines.
And they reside in STARLET.OLB module CLI$INTERFACE.
Arne
Lawrence D'Oliveiro
2024-05-24 00:58:03 UTC
Permalink
Post by Arne Vajhøj
Post by Lawrence D'Oliveiro
as I recall; notwithstanding their “cli$” name prefix, they
are part of DCL itself.
No. They are utility routines.
And they reside in STARLET.OLB module CLI$INTERFACE.
I can’t see them in the VMSRTL map files, at least as of VMS 4.0
<http://bitsavers.trailing-edge.com/pdf/dec/vax/microfiche/vms-source-listings/AH-BT13A-SE__VAX-VMS_V4.0_SRC_LST_MCRF/AH-BT13A-SE__VAX-VMS_V4.0_SRC_LST_MCRF/109__VMSRTL/>.

I’m pretty sure you’ll find that the symbols, in whatever libraries
you find them in, are calls into DCL.
Post by Arne Vajhøj
On *nix there is little difference between a shell and another program.
Huge difference on VMS.
Which is part of my point.
Arne Vajhøj
2024-05-23 19:14:36 UTC
Permalink
Post by Lawrence D'Oliveiro
And also implicit in all this is that the command will be executed by
sending a request to some CLI/shell.
You can start a process with no CLI. SYS$CREPC or
some $ RUN commands.

But yes if you want to use command line arguments then
you need a command line interface present.

Arne
Lawrence D'Oliveiro
2024-05-23 22:37:50 UTC
Permalink
But yes if you want to use command line arguments then you need a
command line interface present.
That leads to its own share of bugs, because you need to make sure to
properly escape (or avoid, if you cannot escape) characts that might have
special meaning to the CLI.

And failure to do this can lead to security vulnerabilities.
Loading...