Version flag
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-176@nospamsrfi.schemers.org
.
To subscribe to the list, follow these
instructions. You can access previous messages via the
mailing list archive.
This SRFI defines a standard command-line flag to get version information from a Scheme implementation. The output is Line-oriented S-expressions which are easy to parse from Scheme, C, and shell scripts and can co-exist with non-S-expression output. A standard vocabulary is defined; extensions are easy to make.
The implementation of this SRFI boils down to a few write calls. But careful planning has gone into the details of those calls.
There is a long tradition of complex command line programs having a version flag. This flag skips the normal operation of the program; instead, it writes version information to standard output and immediately exits. The flag is useful for:
csc
conflicts with
the C# compiler. gsc
conflicts with the
GhostScript interpreter. scheme
conflicts between
Chez Scheme and MIT Scheme.Implementation | -V |
-v |
-version |
--version |
---|---|---|---|---|
Bigloo (bigloo ) |
(verbose) | x | ||
Chez Scheme (scheme ) |
x | |||
Chibi-Scheme (chibi-scheme ) |
x | |||
Chicken (csc ) |
(verbose) | x | ||
Chicken (csi ) |
x | |||
Cyclone (cyclone ) |
x | |||
Cyclone (icyc ) |
x | |||
Dfsch (dfsch-repl ) |
x | |||
Foment (foment ) |
x | |||
Gambit (gsc ) |
x | |||
Gambit (gsi ) |
x | |||
Gauche (gosh ) |
x | (run another version) | ||
Guile (guile ) |
x | x | ||
Kawa (kawa ) |
x | |||
KSi (ksi ) |
x | x | x | |
Larceny (larceny ) |
x | x | ||
MIT Scheme (scheme ) |
x | |||
Mosh (mosh ) |
x | x | ||
Mosh (nmosh ) |
x | x | ||
Oaklisp (oaklisp ) |
x | x | x | |
Owl Lisp (ol ) |
x | x | ||
Racket (racket ) |
x | x | x | |
Sagittarius (sagittarius ) |
x | x | x | |
Sagittarius (sash ) |
x | x | x | |
Schemik (schemik ) |
x | x | ||
SCM (scm ) |
x | |||
Shoe (shoe ) |
x | x | ||
STklos (stklos ) |
x | x | x | |
Vicare (vicare ) |
x | (verbose) | x | |
Ypsilon (ypsilon ) |
x |
Cyclone and Vicare have a "version number only" flag
(cyclone -vn
and vicare --version-only
,
respectively). Gauche has a flag to load another version of
itself. These features are not addressed by this SRFI.
The first problem is that long options (-version
and --version
) do not have a universally agreed-upon
syntax. Perhaps most programs now adhere to the GNU-style
two-dash syntax. However, many prominent ones such as C compilers
and fundamental X Window System utilities continue to use the
one-dash syntax. There are many Schemes supporting only one of
those variants, and many that do not have long options at
all.
One-letter options are much more standardized; so much in fact, that almost all Unix programs have supported them ever since Unix was first published. While programs disagree on whether to interpret a group of letters after one dash as a single one-word option or as multiple one-letter options, there is no ambiguity when only one letter follows the dash. The convention is especially strong when the flag is the only argument after the program name.
Lower-case -v
is the most
popular one-letter flag in the survey. However, it is commonly
used by programs for their "verbose" flag — including a few
Scheme implementations.
Upper-case -V
is less popular
but has no known conflicting uses. (Racket uses -V
as an alias for --no-yield
but makes an exception
and treats racket -V
with no other arguments as a
version flag.) It is a strong standard for a version flag among
Unix programs in general. Hence we choose the upper-case
-V
flag for this SRFI.
Many Scheme/Lisp implementations and other Unix tools output
version information in a format that is quite stable. The idea is
that the information can be parsed by other programs and scripts.
Often the output format is almost regular but not quite. Some of
the more complex formats, while stable, are not self-consistent
since they evolved over time from an ad hoc syntax;
outside of Scheme, clisp --version
and clang
--version
are good examples.
This SRFI mandates a very simple S-expression syntax that is a subset of Scheme’s standard syntax. Implementations can easily write out the information using the standard write procedure as long as the expressions given to write are suitably constrained. Version output is naturally represented as an association list of properties and their values. Each association shall be written as a separate S-expression; the full list is implicit. The precise output format is slightly unconventional and is presented in the next part.
The Lisp tradition offers easy handling of nested data. But we sometimes have to interoperate with languages that don't.
In particular, Unix shell scripts generally parse their input
using the traditional tools awk
, grep
,
and sed
that are based around regular
expressions. Regexps are notoriously unable to handle nested
structure. Perhaps for that reason, Unix shells also make it
difficult to store nested data in variables. Even string list
handling is clumsy and error-prone.
The other classic Unix programming environment, the C language, makes it easy to represent nested data. But handling it is difficult due to the lack of standard tools. Almost all of the data processing functions in the standard library are string functions. Thus we come back to the same situation as with shell scripts: only strings are easy to process from standard C.
The following S-expression:
(version "1.2.3")
Can be easily parsed even with ancient versions of
grep
and sed
:
grep '^(version ".*".*)$' \ | sed -e 's/^(version "//' \ -e 's/".*//'
The sed
command s/whatever//
replaces whatever
with the empty string.
whatever
is a regular expression.
An equivalent parser can be written in awk
. It’s
a bit more verbose but avoids using a shell pipeline:
awk -F '[()]' \ '/^\(version .*\)$/ { sub(/^[a-z-]+ /, "", $2); gsub(/"/, "", $2); print $2 }'
Again, the Awk command sub(/whatever/, "",
$variable)
replaces the regular expression
whatever
with the empty string. gsub
replaces all occurrences on the line whereas sub
replaces only the first. Awk splits each line into numbered input
fields at the separator [()]
.
These commands work with the POSIX standard versions of these tools. They have been tested with the bare-bones versions in BusyBox, meaning they are quite conservative.
Parsing from C is surprisingly easy as well:
#include <stdio.h> #include <string.h> static char output[1024]; static const char *parse_version(void) { const char prefix[] = "\n(version \""; char *start; char *limit; if ((start = strstr(output, prefix)) == NULL) { return ""; } start += strlen(prefix); for (limit = start; *limit != '"'; limit++) { if ((*limit == '\0') || (*limit == '\n') || (*limit == '\\')) { return ""; } } *limit = '\0'; return start; } int main(void) { output[0] = '\n'; fread(&output[1], 1, sizeof(output) - 2, stdin); printf("%s\n", parse_version()); return 0; }
When reading this code, note that C implicitly initializes the
output
buffer to all all zero bytes. In order to
find (version "1.2.3")
when it is the first line of
output, a newline character \n
is artificially
prepended to the real output. When reading we additionally leave
one zero byte at the end of the output buffer to ensure the
buffer is null-terminated. The parse_version
function mutates the output buffer by replacing the closing
double-quote in (version "1.2.3")
with a null byte.
Then we can easily handle the substring 1.2.3
as a
null-terminated string.
In order for the above S-expression parsing to be robust, it relies on some concessions from the program writing the output:
At first glance, this looks like a big list of restrictions. But in practice, they are not hard to conform to for simple data.
The following LOSE forms are quite easy to parse reliably. This SRFI uses them exclusively, and the sample implementation offers ready-made parsers for them.
(symbol string…)
(symbol symbol/integer…)
(symbol (symbol symbol/integer) …)
To let implementations keep their existing version output for
backward-compatibility, LOSE parsing starts at the first line
with a left parenthesis (
at the first column. This
means that any amount of other text can come before the
S-expression part. Some Unix programs write a multi-paragraph
copyright, warranty and version message; all of that can be
preserved if desired.
This simple arrangement makes all of the following work naturally:
(
then the entire output is parsed as
the association list.(
then the parser simply returns an empty
association list. This means that all known Scheme
implementations with an existing -V
flag are
already compatible with this SRFI. It’s just a matter of adding
output, not changing anything.The best argument for using S-expressions generally is that people keep re-inventing them in less consistent and flexible formats without an objective reason. We will save time and effort by using time-tested syntax from the beginning. Easy interoperability with Scheme/Lisp is an obvious plus.
The main arguments against S-expressions is that they look foreign to non-Lisp programmers and require too many parentheses. The nesting implied by the parentheses makes them a poor fit for line-oriented tools. All the classic Unix text processing utilities are line-oriented. To interoperate with these tools we need a compromise. The easiest compromise is to write each association list entry on its own line, which leaves most lines with only one pair parentheses and no nesting. Nested lists in the output will be rare.
We could take this even further by using implied parentheses
around each line of text, so that no parentheses are needed for
most output lines. This should make the output completely
un-scary even for Unix programmers who know nothing about Lisp.
Unfortunately this syntax would make it hard to provide
backward-compatibility with the many existing output formats for
version info. Having a left parenthesis in the first column is a
simple and unambiguous rule. If there are no such syntactic
markers, parsing will be a lot harder. Most natural candidates
for a syntactic marker are also more ambiguous than a left
parenthesis. For example, property: value
pairs are
harder to detect and easier to confuse with other things.
The version output should be in an ASCII superset character
encoding, so that bytes #x01..#x7e
correspond to
those ASCII codepoints. The encoding of bytes outside this range
is unspecified; UTF-8 is recommended where possible.
ASCII space (#x20
), carriage return
(#x0d
) and line feed (#x0a
) characters
are recognized as whitespace.
On Unix, only a single line feed character (LF) is recognized as a newline.
On Windows, either a single line feed character (LF) or a carriage return followed by a line feed (CR/LF) can be used as a newline.
The version output shall conform to the following subset of Scheme syntax:
foo
with the first
character being one of [A-Za-z]
and any subsequent
characters among [A-Za-z0-9_/*+.-]
."string"
with support for the
backslash escapes \"
and \\
. Other
backslash escapes do not work reliably and should be avoided.
Strings cannot contain newlines. Non-graphic characters do not
work reliably and should be avoided.123
. No leading
zeros.() (a) (a b) (a b c)
. Exactly one space
between adjacent elements. No whitespace between the open
parenthesis and the first element. No whitespace between the
last element and the close parenthesis. No whitespace between
the open and close parentheses in an empty list. Lists can be
nested, but it generally makes line-oriented work
difficult.LOSE symbols are case-sensitive. Letter case is preserved when reading them in.
Top-level S-expressions must all be lists. A top-level open parenthesis must fall on the first column of a line, and there must be a newline immediately after a top-level close parenthesis. There must be a newline even after the last top-level list in the output, i.e. the output must end with a final newline, Unix style.
Multi-line S-expressions are not recognized by line-oriented tools and should hence be avoided; if required, then continuation lines of nested expressions must start with one space.
Missing features:
#t
and #f
(due to
interoperability concerns with other Lisps).Suggested workarounds:
(linux? #t)
use something like
(build.platform "…linux…")
or
(scheme.features … linux …)
.level1.level2.level3
.ANSI, HTML or other in-band color and text attribute markup shall not be used in the S-expression part of the output since it will confuse parsers.
An exception is if the implementation can be sure that the
output is going to a terminal (instead of a file, pipe, etc.) On
Unix, isatty()
is an adequate check.
Out-of-band markup (e.g. Windows console character attributes) may be used.
For a Scheme invoked as fantastic-scheme
, the
command line fantastic-scheme -V
(i.e.
upper-case V
preceded by one dash)
must conform to the version output format in this SRFI.
Specifically, LOSE parsing starts at the first output line
that has a left parenthesis (
in the first column
with no preceding whitespace characters. Parsing continues from
that line until the end of the output. The parser collects every
top-level S-expression into one big association list, preserving
the order. If there is no line starting with a left parenthesis,
an empty association list is returned.
This SRFI guarantees only that the above simple command
invocation, with -V
as the first command-line
argument and no other arguments, has the intended effect. The
implementation should also support the -V
flag in
other argument positions if it makes sense, and it should have
the same output format as when it is the only command line
argument, but neither of those is strictly required.
When the -V
flag is used as above, the command
shall exit with a success exit code if it:
Otherwise it shall exit with a failure exit code. On Unix and Windows, exit code 0 means success and codes 1..100 are safe to use for indicating failure.
All interpreter and compiler commands supplied by the
implementation must support the -V
flag as the only
argument.
The implementation may additionally support -v
,
-version
, --version
and/or other
version flags, but none of these are required. To present a
simple interface to users, all version flags should ideally give
the same output, but that is not required either.
Besides the compiler and interpreter commands, any other commands supplied by the implementation are also encouraged to support the same version flags, but are not required to.
The version output is allowed to differ between commands.
The -V
output may change if other flags are also
given on the command line. For example, fantastic-scheme -V
-r r6rs
and fantastic-scheme -V -r r7rs
could
give different output describing the R6RS and R7RS modes of
Fantastic Scheme, and fantastic-scheme -V
could give
yet different output describing both of them or have less
information.
If the Scheme system has a -:
flag for runtime
options (as do Chicken and Gambit), it shall recognize
-:version
and behave compatibly with the
-V
flag. If the version
runtime option
comes from an environment variable or some other place than
command line arguments, it is permitted to cause an error.
Implementations shall provide a
(version-alist) procedure that can be called
from Scheme and returns an association list of zero or more
properties according to this specification. Ideally it should
return roughly the same information as the -V
command line flag, but this is not required. It is an error for
the caller to modify the list returned.
Implementations supporting the R7RS module system shall
provide a (srfi 176)
library with the procedure.
Implementations supporting the R6RS module system shall
provide a (srfi :176)
library with the
procedure.
The procedure may also be exported by other libraries. It's implementation-defined whether or not it's imported into the default interaction environment.
If the Scheme implementation can generate standalone executables or bundles of user programs, those executables:
-:version
runtime option if
they support the -:
runtime option syntax.-V
flag
provided by the Scheme implementation, since a program is
supposed to have control over its own command line syntax and
may want to customize its own version output.In standard properties, dates and times are written in RFC 3339 format. It is a restricted form of the international standard date format specified in ISO 8601.
We always use upper-case T
for the date/time
separator and upper-case Z
for the UTC time zone
marker. The UTC offset -00:00
(i.e. negative zero)
indicates an unknown time zone.
Examples:
2019-12-14
(local date only)2019-12-14T20:00+02:00
(local date and
time)2019-12-14T18:00Z
(UTC date and time)Unix shell commands to output the current date and time in these formats:
date '+%Y-%m-%d'
date '+%Y-%m-%dT%H:%M%z' | sed
's@^.\{19\}@&:@'
date -u '+%Y-%m-%dT%H:%MZ'
The %z
conversion specification is the only
non-POSIX feature in these commands. %z
gives a
±HHMM
time zone offset under every modern Unix-like
operating system. The sed
command converts that to
±HH:MM
. The ISO C99 strftime()
function
standardizes %z
with the same format. C code to
output the above date formats is included with the sample
implementation of this SRFI. Most popular programming languages
have a ready-made library function to parse RFC 3339
timestamps.
Below is a large set of proposed standard properties. This set was designed based on actual information currently reported by various Scheme implementations in their version output.
All properties are optional. That implies any
Scheme implementation with a -V
version flag writing
only to standard output, no output lines starting with
(
already conforms to this SRFI.
The order in which the properties are given does not matter. Writers should not write properties with duplicate names; readers should pick the first property matching a given property name.
(command string…)
The command names for some Schemes differ on different operating systems and installations. Implementors typically desire a canonical command name for each command shipping with their implementation, but compromises sometimes need to be made due to name conflicts or multiple versions of the same command that need to be able to coexist. This property gives the canonical name suggested by the implementor without any optional version number.
If the executable being invoked is a multi-call binary (i.e. it can behave like more than one program depending on which argv[0] is given) or otherwise is known by more than one canonical name, then more than one string may be given.
Examples:
(command "csi") (command "gosh") (command "gsc") (command "isc") (command "scheme" "mit-scheme")
(website string)
The URL for the Scheme implementation's website.
Examples:
(website "https://practical-scheme.net/gauche")
(install-dir string)
Root directory of the Scheme installation, if it has one.
Typically, this is the directory that has bin
and
lib
subdirectories, but the meaning is
implementation-dependent.
This directory is often set by a build-time option called prefix in the implementation's configure script or makefile.
(languages symbol…)
The set of programming languages supported by the implementation. Symbols denote set membership.
The distinctions between language, language
standard, language family and dialect are
muddy. For the purposes of this property, they are all
equivalent, and any of them may be represented by a symbol in
this property. For example, r7rs
is a member of
scheme
and both should be given. Non-Scheme
languages could also be listed. If a unified Scheme and Common
Lisp implementation is ever made, it would list both
languages.
This property means that the implementation aspires to conform
to these languages to a useful degree, and if it does not, you
can open issues in the issue tracker to discuss it. Guarantees
about conformance and pedantry about language definitions are not
the point. In particular, any executable usefully characterized
as a Scheme implementation should list scheme
even
if it does not fully conform to any RnRS report.
Standard symbols include r3rs
, r4rs
,
r5rs
, r6rs
, r7rs
,
r7rs-large
, scheme
. Please coordinate
with other implementors about coining symbols for other Scheme
derivatives and non-Scheme languages.
Examples:
(languages scheme r6rs r7rs)
(encodings symbol…)
Each symbol is the lowercase IANA name of a character encoding known to be supported by this build of the Scheme implementation.
All encoding names in the IANA list can be represented as LOSE
symbols except for names with a year suffix: e.g.
ISO_8859-1:1987
.
It is permitted to list more than one alias for the same encoding.
The default encoding selected at start-up time should come first in the list. The default may change based on command line options, environment variables or other environmental factors.
The implementation may also support other encodings not listed here, either natively or via extensions.
Examples:
(encodings utf-8 iso-8859-1)
(version string)
A free-form version string in the native format preferred by the implementor. No portable information can be reliably parsed from the string, but version strings should be sortable in order from oldest to newest using typical "version sort" algorithms.
In practice, most Scheme implementations use release version numbers in major.minor.patch format. Other information such as distributor patchlevel or version control commit may be appended.
Examples:
(version "1.2.3") (version "1.11.6") (version "0.9.9_pre1") (version "1.0.188-a4a79d5") (version "4.3f")
(release string)
The most recent released version of the implementation based on which this build was made. If this is that release version, then version is identical to this. If this has patches on top, then the two versions are different.
(release.date iso-date-string)
The date on which the release was made.
Examples:
(release.date "2019-08-06")
(release.name string)
A codename for the release.
Examples:
(release.name "Grain Alcohol and Rainwater") (release.name "oxygen")
(build.date iso-date-string)
The date and time when this executable was built.
It is implementation-dependent whether the timestamp is nearer to the start or end of the build.
Examples:
(build.date "2018-09-30") (build.date "2018-09-30T02:07Z") (build.date "2018-09-30T02:07+02:00") (build.date "2018-09-30T02:07-05:30")
(build.platform string)
A free-form string identifying the computer architecture,
operating system, and/or other aspects of the computing platform
for which the executable was built. This is the platform string
in the implementation’s native format; there is no portable
information that can be reliably parsed. Often this is a
GNU-style computer-kernel-userland
triple; just as
often it is not.
Examples:
(build.platform "DarwinX8664") (build.platform "x86_64-apple-darwin18.7.0") (build.platform "macosx-unix-clang-x86-64")
(build.configure string…)
Command line arguments given to the configure script before building this Scheme implementation. A configure script is a common means for build-time configuration of programs on Unix-like operating systems. It is useful to save the options given to that script for run time: knowing them helps replicate builds and debug problems with the implementation.
Each command line argument is given as one string. S-expression string escapes are used; since the double-quoted string syntax used with S-expressions is largely compatible with Unix shells, the resulting syntax can generally be pasted to a shell with no changes.
The name of the configure script is not given. It is almost
always configure
, though that is not required.
Examples:
(build.configure "--enable-single-host") (build.configure "--prefix=/home/wiley/.local" "CC=gcc-9")
(build.git.tag string)
(build.git.branch string)
(build.git.commit string)
(build.git.modified string…)
The state of the Git version control repository from which the build was made.
These can be got from the following Unix shell command:
Tag:
git describe --tags --abbrev=0 2>/dev/null || true
Branch:
git rev-parse --abbrev-ref HEAD 2>/dev/null || true
Commit:
git rev-parse --short HEAD 2>/dev/null || true
List of files with uncommitted changes:
printf '(build.git.modified'; git diff-index --name-only HEAD 2>/dev/null | head | xargs -n 1 printf ' "%s"'; echo ')'
Examples:
(build.git.tag "3.1.1") (build.git.branch "master") (build.git.commit "8e62f718") (build.git.modified "c/char.c" "c/env.c" "c/env_unix.c" "c/scheme.h")
(image.date iso-date-string)
If this is an image-based Scheme system, the date and time when the active boot image was saved.
This may vary by command line options and environment variables if those can be used to select a different boot image.
Examples:
(image.date "2018-09-30") (image.date "2018-09-30T02:07Z") (image.date "2018-09-30T02:07+02:00") (image.date "2018-09-30T02:07-05:30")
(image.file string)
If images can be loaded by filename, string gives the filename that is used to load the active boot image.
These properties deal with things directly related to the Scheme programming language.
(scheme.id symbol)
A symbol identifying which Scheme implementation provides this executable. Together with command this can be used to figure out which command of which implementation was invoked, even in cases where two implementations use the same command name.
At the time of writing, there is no central registry for
Scheme IDs.
The Scheme
Registry tracks IDs.
Examples:
(scheme.id fantastic) (scheme.id gauche)
(scheme.srfi integer…)
Each integer gives the number of a SRFI supported
natively by the implementation. The integer 0
stands
for Feature-based conditional expansion construct,
1
stands for List Library, 176
stands for Version flag, etc.
The integers shall be listed in order from smallest to largest.
If the implementation supports a particular SRFI via a library or extension that is not built or installed, that SRFI shall not be listed.
A partially supported SRFI should not be listed unless the implementor has plans for full support and users can report missing features as bugs.
The implementation does not need to list every SRFI it supports, as there may be situations where that is hard to define. The absence of a number does not conclusively prove that SRFI is not supported.
Examples:
(scheme.srfi) (scheme.srfi 0 1 13 14 176)
(scheme.features symbol…)
The symbols should correspond to the feature list for cond-expand.
Note: Only symbols conforming to the LOSE symbol syntax can be listed for compatibility reasons. Other symbols from the feature list need to be filtered out. In particular, Scheme symbols cannot portably start with digits so LOSE does not allow it either. This is not generally a problem since Scheme implementations do not tend to have such feature identifiers, but some of them may.
Examples:
(scheme.features) (scheme.features dload ptables) (scheme.features utf-8 pthreads linux r7rs) (scheme.features chibi r7rs ratios complex uvector threads full-unicode) (scheme.features modules dynamic-loading darwin bsd macosx little-endian)
(scheme.path string…)
List of directories to search for imported libraries. Listed in order of decreasing priority: highest priority first.
If the Scheme implementation is written in the C programming language and/or has a C foreign function interface, it can provide these properties to give information about the C side of things.
(c.version string)
A free-form version string describing the C compiler or toolchain being used.
Expect this to be difficult to parse reliably, like HTTP user-agent strings.
Pre-defined Compiler Macros is a site listing the C preprocessor macros set by lots of compilers to reveal their version information.
Examples:
(c.version "GCC 9.2.0") (c.version "GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.46.4)") (c.version "Open Watcom 1.9")
(c.compile string…)
(c.link string…)
The command name and any fixed command line arguments for the C compiler/linker used to build the Scheme implementation.
If same command is used for compiling and linking, both c.compile and c.link may be given with the same values, or only c.compile may be given and c.link omitted.
Each command line argument is given as one string. S-expression string escapes are used; since the double-quoted string syntax used with S-expressions is largely compatible with Unix shells, the resulting syntax can generally be pasted to a shell with no changes.
Examples:
(c.compile "clang" "-Wall" "-Werror" "-Wextra" "-O2" "-D" "NDEBUG") (c.link "clang" "-lm")
(c.type-bits (symbol integer) …)
Tells the sizes of C data types. Each symbol is the
name of a C type and integer gives giving the width of
the type in bits. The size of type t
can be
determined in C using:
#include <limits.h> // For CHAR_BIT #define BITSIZEOF(t) (sizeof(t) * CHAR_BIT) printf("%zu\n", BITSIZEOF(void *));
It's completely up to the implementation to decide which types are listed. The list can include non-standard types from the operating system and third-party libraries as well as custom types used only by the implementation. Types do not need to be sorted by symbol or integer.
Each C type name is converted to a symbol as follows:
void *
becomes pointer
and
void (*)()
becomes
function-pointer
foo *
becomes
foo-pointer
While qualifiers like const
and
unsigned
are permitted, they are generally
superfluous since the unqualified type is the same size. All
pointer types are generally the same size as a void pointer, but
the C standard does not guarantee that.
Examples (for completeness, some silly ones are included):
(c.type-bits) (c.type-bits (pointer 64)) (c.type-bits (int 32) (long 64) (float 32) (double 64) (pointer 64) (size_t 64)) (c.type-bits (struct-timespec 128) (struct-timespec-pointer 64) (time_t 64)) (c.type-bits (long 64) (long-long 64) (intmax_t 64) (intmax_t-pointer 64)) (c.type-bits (long-double 128) (long-double-pointer 64)) (c.type-bits (char-pointer-pointer 32) (jmp_buf 416)) (c.type-bits (TCHAR 8) (DWORD 32) (WIN32_FIND_DATA 2560)) (c.type-bits (struct-__Foo_BAR__12_-pointer-pointer 64))
(jvm.symbol string)
This property can be used to expose one or more Java system properties.
The value of this property is determined at run time, not at build time.
Note: symbol is part of the property name, not a separate symbol.
Note: System properties whose names are not valid LOSE symbols cannot be included.
Note: A missing system property can either be
omitted from the output or show a null string ""
as
its value.
(jvm.java.awt.graphicsenv "sun.awt.X11GraphicsEnvironment") (jvm.java.class.path "/usr/local/share/kawa/lib/kawa.jar:") (jvm.java.version "13.0.2") (jvm.java.vm.name "OpenJDK 64-Bit Server VM") (jvm.kawa.home "/usr/local/share/kawa") (jvm.sun.jnu.encoding "UTF-8")
(os.env.symbol string)
This property can be used to expose one or more environment variables from the operating system.
The value of this property is determined at run time, not at build time.
On Windows, environment variable names are case-insensitive and should be normalized to uppercase for this SRFI.
Note: symbol is part of the property name, not a separate symbol.
Note: Environment variables whose names are not valid LOSE symbols cannot be included.
Note: A missing environment variable can
either be omitted from the output or show a null string
""
as its value.
Examples:
(os.env.LANG "en_US.UTF-8") (os.env.TERM "xterm-256color")
(os.stdio symbol symbol symbol)
Three symbols in this order:
Each symbol shall give the type of the port in the current environment. The following choices are standard:
terminal
device
socket
file
pipe
unknown
Knowing the types can be useful for debugging unusual I/O behavior in a particular environment.
Examples:
(os.stdio device pipe pipe) (os.stdio socket terminal file)
(os.uname string string string)
Three strings from the Unix
utsname
structure in this order:
sysname
(uname -s
)release
(uname -r
)machine
(uname -m
)The value of this property is determined at run time, not at build time.
Examples:
(os.uname "Linux" "5.4.11_1" "x86_64") (os.uname "Darwin" "18.7.0" "x86_64") (os.uname "Haiku" "1" "BePC") (os.uname "FreeBSD" "12.0-RELEASE-p6" "amd64") (os.uname "MSYS_NT-10.0-18362" "3.0.7-338.i686" "i686")
Implementations are free to have any number of custom properties.
The names of implementation-defined properties shall start with the implementation’s Scheme ID and a dot.
For example, if Fantastic Scheme builds varied by the phase of the moon, it could have:
(fantastic.phase-of-the-moon waxing-crescent)
If fantastic-scheme -V
gives the following
output:
Fantastic Scheme version 2.95 Copyright (C) 2003 Pyrrhic Ventures This is free software; always read the label. There is NO warranty; not even for buoyancy or fitness for high-velocity landings at sea. "Shoot for the moon. Even if you miss, you'll crash on impact." (command "fantastic-scheme") (website "https://example.com/scheme") (languages scheme r5rs r6rs r7rs) (encodings utf-8 iso-8859-1) (install-dir "/home/wiley/.local") (scheme.id fantastic) (scheme.srfi 0 1 2 176) (scheme.features bits-64 little-endian r7rs ratios exact-complex full-unicode little-endian fantastic-scheme) (scheme.path "/home/wiley/.local" "/usr/local/share/fantastic-scheme/1.x") (c.version "GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.46.4)") (c.compile "cc" "-Wall" "-O2" "-D" "SCHEME_UNIX") (c.link "cc" "-lm") (c.type-bits (int 32) (long 64) (pointer 64) (float 32) (double 64) (size_t 64)) (release "2.95") (release.date "2003-06-24") (release.name "Sheer lunacy") (build.date "2020-02-14T22:25+02:00") (build.platform "Darwin-x86_64-cc") (build.configure "--prefix=/home/wiley/.local") (build.git.tag "draft-2") (build.git.branch "for-draft-3") (build.git.commit "96d6322") (build.git.modified "implementation/scheme.c") (os.stdio terminal terminal terminal) (os.uname "Darwin" "18.7.0" "x86_64") (os.env.LANG "en_FI.UTF-8") (os.env.TERM "dumb") (fantastic.phase-of-the-moon waxing-crescent)
It is parsed into the following association list in Scheme:
((command "fantastic-scheme") (website "https://example.com/scheme") (languages scheme r5rs r6rs r7rs) (encodings utf-8 iso-8859-1) (install-dir "/home/wiley/.local") (scheme.id fantastic) (scheme.srfi 0 1 2 176) (scheme.features bits-64 little-endian r7rs ratios exact-complex full-unicode little-endian fantastic-scheme) (scheme.path "/home/wiley/.local" "/usr/local/share/fantastic-scheme/1.x") (c.version "GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.46.4)") (c.compile "cc" "-Wall" "-O2" "-D" "SCHEME_UNIX") (c.link "cc" "-lm") (c.type-bits (int 32) (long 64) (pointer 64) (float 32) (double 64) (size_t 64)) (release "2.95") (release.date "2003-06-24") (release.name "Sheer lunacy") (build.date "2020-02-14T22:25+02:00") (build.platform "Darwin-x86_64-cc") (build.configure "--prefix=/home/wiley/.local") (build.git.tag "draft-2") (build.git.branch "for-draft-3") (build.git.commit "96d6322") (build.git.modified "implementation/scheme.c") (os.stdio terminal terminal terminal) (os.uname "Darwin" "18.7.0" "x86_64") (os.env.LANG "en_FI.UTF-8") (os.env.TERM "dumb") (fantastic.phase-of-the-moon waxing-crescent))
Writing the version information can be as simple as calling write or equivalent with suitable input.
The following is a fully functional parser.
(define (read-version-alist in) (let skip-until-paren ((line-start? #t)) (let ((c (peek-char in))) (if (not (or (eof-object? c) (and line-start? (char=? c #\()))) (skip-until-paren (eqv? #\newline (read-char in))) (let read-all ((xs '())) (let ((x (read in))) (if (eof-object? x) (reverse xs) (read-all (cons x xs))))))))))
A full sample implementation is available at:
github.com/scheme-requests-for-implementation/srfi-176
It has plenty of code to gather, read and write version information in common situations:
Thanks to John Cowan for detailed feedback on all aspects of this SRFI.
Thanks to Marc Feeley and John for detailed discussions of S-expressions that can be parsed from portable shell scripts.
Thanks to Arthur Gleckler for questioning why the output of existing version flags is extended instead of making a new flag exclusively for machine-parseable output.
This SRFI started off as one of those "what if we made this simple tweak" hunches. It has now reached a ludicrous length considering the triviality of the topic. I am grateful to anyone who may want to use LOSE for another application. It will do at least a little to justify the effort spent.
Copyright © Lassi Kortela (2019)
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.