by Lassi Kortela
This SRFI is currently in final status. Here is
an
explanation of each status that a SRFI can hold. To provide
input on this SRFI, please send email to srfi-193@nospamsrfi.schemers.org
.
To subscribe to the list, follow these
instructions. You can access previous messages via the
mailing list archive.
R6RS and R7RS define a
command-line
procedure. While a useful baseline, the
specification is not detailed enough to cover all practical
situations. This SRFI clarifies the definition of
command-line
and adds a few related procedures.
Scheme scripts, standalone executables, compilation and REPL use
are accounted for. Option parsing is out of scope.
When a Scheme implementation is run on a conventional operating system, the OS runs an executable. An executable is a file that contains a program in a format suitable for the OS. In the case of Scheme, the executable can represent:
The OS gives the executable a command line. The command line is a vector of strings. There is a ubiquitous convention that the first string represents the command name. Any remaining strings are command arguments interpreted differently by each program.
Command lines are extremely portable. Practically all general-purpose operating systems provide them. Command names cannot be reliably interpreted as filenames.
The command-line
procedure specified in
R6RS and R7RS returns a list of one or more
strings. As with OS command lines, the first string is by
convention the command name. However, (command-line)
is generally not the same as the OS command line.
When an implementation is started for the purpose of running a
Scheme program as a batch job, (command-line)
is
most often a tail of the OS command line such that (car
(command-line))
is the name of the Scheme program and
(cdr (command-line))
are the arguments given to that
program.
For example, if the OS command line is:
fantastic-scheme --script program.scm foo bar
baz
the value of (command-line)
generally is:
("program.scm" "foo" "bar" "baz")
Gambit Scheme supports running more than one program in the
same batch job, with the ability to insert a read-eval-print
loop in between any pair of programs. In that case the
command line is given to the first program in the batch that
starts with the Unix-style #!
script indicator.
Since it consumes all remaining arguments, that program also
becomes the last program in the batch.
When a Scheme program is turned into a standalone executable,
(command-line)
and the OS command line are generally
equivalent. However, the Scheme implementation’s runtime system
may omit command line options that belong to it. For example,
Gambit omits the -:
option.
In a read-eval-print loop the value of
(command-line)
is not well specified. A reasonable
convention from Chez Scheme is to use a zero-length string as the
command name in this case, with no command line arguments after
the name.
Another ambiguous situation arises when using
load
or import
to load some code into
an implementation. When the implementation loads or imports a
Scheme library or module to use as a main program, it should
perhaps set the command name to the name of the module and give
it any remaining command line arguments. If a library or module
is loaded or imported to help with another program, and not as a
main program in its own right, the command line should be
'("")
as in the REPL.
The interface defined by this SRFI is divided into two layers:
command-line
,
command-name
, command-args
)script-file
,
script-directory
)The command layer applies whenever we are running a Scheme
program that is given its own command line (in the sense that the
RnRS command-line
procedure would return
a useful command line concerning that program specifically). Such
a command line is normally a tail of the OS command line, but may
come from other places as well. Kawa offers a particularly large
number of alternative ways to set it.
The script layer applies whenever we are running a Scheme program from a source file. It does not apply to standalone, pre-compiled programs or libraries. The idea is that when running simple Scheme scripts, it is often convenient to have them read and write data files kept in the same directory as the script itself.
A third layer to access the unabridged command line of the OS process is omitted. The process command line can be difficult to access from hosted environments such as the Java Virtual Machine, which would make this SRFI more difficult to adopt.
For the purposes of this SRFI, the following are considered top-level programs:
-s
,
-script
, --script
, or
--program
).load
or equivalent from the
REPL.-e
).-m
) for the purpose of running that
module as a main program.The following are not considered top-level programs:
load
from
another file.import
(except for a
-m
flag as above).However, implementation-defined global options or optional
arguments are permitted that control whether or not
load
treats files as top-level programs.
For the purposes of this SRFI, we classify a running Scheme program as follows:
A running program can be either a command, a script, both at once, or neither.
(command-line
) ⇒ string-list
This procedure is equivalent to the R6RS and
R7RS command-line
procedure, but
specified in more detail.
R6RS definition: "Returns a nonempty list of strings. The first element is an implementation-specific name for the running top-level program. The remaining elements are command-line arguments according to the operating system’s conventions."
R7RS definition: "Returns the command line passed to the process as a list of strings. The first string corresponds to the command name, and is implementation-dependent. It is an error to mutate any of these strings."
Additional stipulations by this SRFI:
("")
is
returned.-:
in Gambit and Chicken.)load
, on the command line, in an
environment variable, etc. The command args are the args (if
any) that belong to the script.(command-line)
list
returned by the implementation, or any of the strings in
it.command-line
as a parameter object. In that case,
the implementation binds the value of the parameter as above
for the duration of the command. The program may freely rebind
command-line
to any other string list containing
at least one element, either temporarily with
parameterize
or permanently.command-line
parameter, the new binding may share structure with the old
binding.(command-name
) ⇒ string?
Returns a friendly version of (car
(command-line))
evaluated in the current lexical
environment.
If (car (command-line))
is a zero-length string,
#f
is returned to indicate "not a command".
Otherwise a friendly command name is typically derived from a filename as follows:
.exe
or .scm
.For example, both the Windows filename C:\Program
Files\Fantastic Scheme\fantastic-scheme-1.0.EXE
and the
Unix filename /usr/local/bin/fantastic-scheme-1.0
would be typically shortened to
fantastic-scheme-1.0
.
(command-args
) ⇒ string-list
Returns (cdr (command-line))
evaluated in the
current lexical environment.
(script-file
) ⇒ string?
Returns an absolute pathname pointing to the calling script. Symbolic links are not resolved.
(The script may or may not be a command; use
command-name
to find out.)
If the calling program is not a script, #f
is
returned.
Implementations must resolve the absolute pathname of a script before running that script. The script may change the working directory, thereby changing the interpretation of relative pathnames.
(script-directory
) ⇒ string?
Returns only the non-filename part of script-file
as a string. As with script-file
, this is an
absolute pathname.
The string should end with a directory separator (a forward
slash on Unix; a backslash on Windows; an appropriate character
on other operating systems) so that string-append
can be easily used to build pathnames based on it: for example,
(string-append (script-directory) "my-data-file")
.
However, if appending such a separator would make the pathname
invalid on the underlying operating system, the separator is not
added.
If the calling program is not a script, #f
is
returned.
Due to the highly environment-dependent nature of the SRFI,
there is almost nothing that can go into a portable sample
implementation. Implementations have been done for Gauche
(src/execenv.c
and
lib/srfi-193.scm
) as well as Chibi-Scheme (main.c
and lib/srfi/193.sld
).
It is not possible to implement this SRFI based on the
RnRS command-line
procedure. However, the
command-line
procedure from this SRFI can serve as a
conforming implementation of RnRS
command-line
, and implementations are encouraged to
merge these procedures where possible.
Thanks to Shiro Kawai for the initial implementation in
Gauche, for patiently reviewing iterations of the design and for
valuable feedback that solved key problems in it. The interplay
of command-line
, command-name
and
command-args
was especially tricky to figure out and
I had all but given up hope of a satisfying solution. With
Shiro’s insight a simple and natural approach was finally
found.
The Kawa Scheme language. Section 21.5 System inquiry. Corresponds to Kawa version 3.1.1. Link
The Gambit Scheme manual. Section 2.5 Scheme scripts. Corresponds to Gambit version 4.9.3. Link
© 2020 Lassi Kortela.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.