by Olin Shivers (original author), John Cowan (editor and shepherd), Harold Ancell (implementer and editor)
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-170@nospamsrfi.schemers.org
. To
subscribe to the list, follow these
instructions. You can access previous messages via the
mailing list archive.
The host environment is the set of resources, such as the filesystem, network and processes, that are managed by the operating system on top of which a Scheme program is executing. This SRFI specifies some of the ways the host environment can be accessed from within a Scheme program. It does so by leveraging widespread support for POSIX, the Portable Operating System Interface standardized by the IEEE. Not all of the functions of this SRFI are available on all operating systems.
The I/O and other environmental procedures provided by the various Scheme standards were designed at a time when operating systems were far more diverse than they are today, and therefore portability was difficult or impossible to achieve. In addition, Scheme has historically focused on programming-language features rather than the practical needs of mainstream software development. Consequently, none of the standards provide more than a limited set of operations. Individual implementations often provide much more, but in incompatible ways.
This SRFI uses the IEEE 1003 POSIX.1-2017 standard to provide maximally portable access to the services of the operating system on which typical Scheme implementations run. Almost all operating systems today support all or part of POSIX, so the use of this SRFI is mostly portable, but implementations are definitely not portable. However, an implementation of this SRFI can be layered over many existing implementation-specific interfaces, or directly over a C FFI. It is even possible to implement it on top of the JVM and CLR virtual machines.
This SRFI describes a specific POSIX API for Scheme. Rather than attempting to compromise between existing implementations, the scsh system call specification was chosen as a base document. Consequently, this SRFI is a reduced and heavily edited version of Chapter 3, "System Calls" of version 0.6.7 of the Scsh Reference Manual. The numbered headers are aligned with those used in the Reference Manual.
Scsh 0.6.7 was chosen for two main reasons. It is fairly old, so most of its operations, even those which were non-POSIX at the time (2006) are now included in POSIX, and it has few or no operations that aren't POSIX at all. In addition, it is politically fairly neutral, being tied to an obsolete version of Scheme 48, an implementation which is not being actively developed. Scsh 0.7 exists and runs on the current version of Scheme 48 (see Implementation section), but was not used in designing this SRFI because it is incompletely documented.
This SRFI makes no effort to provide all 81 headers, 1191 interfaces, and 51 data types of full POSIX. Instead it provides access to a reasonable number of highly portable interfaces (many of them even available on Windows) with wrappers to make them more Scheme-like. In particular, this SRFI excludes:
Most operations on file descriptors other than converting between them and Scheme ports.
The ability to manipulate the file system based on
file descriptors rather than names, as with
openat()
.
Everything to do with the creation and management of subprocesses and communication with them. The low-level POSIX operations are tricky to use, and a future SRFI will provide a higher-level interface.
Networking operations: see SRFI 106.
Date and time operations, as the POSIX date and time operations are irregular and awkward. This SRFI provides only the current time.
Access to command-line arguments and environment variables, as well as provisions for exiting a process, as they are already provided by R7RS Small.
POSIX file locking, which is notoriously broken as designed. Consider the use of dot files (or, if necessary, non-portable facilities) instead.
All provisions for signal handling, as they are complex and require deep integration with the particular Scheme implementation.
Memory-mapped I/O, locales, syslog, System V message queues, and pseudo-TTYs, as they are considered specialized and out of scope. Future SRFIs may provide interfaces for them.
The use of colons to join record names and fields into the name of a record accessor is a convention of Scheme 48, on which scsh is built.
Note: This SRFI is already very long, and adding detailed examples would make it even longer. However, the Chibi test suite is a good source of usage examples.
Implementations of this SRFI on non-POSIX systems, especially Windows, must provide all the procedure names and syntax keywords. However, if the specified action is not possible, the procedure should either take no action and return some reasonable default value, or signal an exception.
A Scheme implementation that supports both this SRFI and multiple threads of control must ensure that when a thread invokes a blocking procedure such as an R[4567]RS I/O operation, only that thread is blocked and not any other concurrently running ones. Because a user-visible thread may be multiplexed on a single POSIX thread, be in a 1:1 relationship with POSIX threads, or run on different POSIX threads at different times in its life cycle, there can be no guarantees that a POSIX function which is MT-Safe (that is, thread-safe in the sense of POSIX threads) is safe for user-visible threads.
Several of this SRFI's procedures either accept or return a time object
that is fully compatible with the time objects of
SRFI 19. It contains at least three values: the number of elapsed seconds
since a given epoch, the number of
elapsed nanoseconds since the beginning of the specified second,
and a symbol that represents the epoch and is the same as
the value of either time-utc
or time-monotonic
as exported by SRFI 19.
The system clock is not required to report time at full nanosecond
resolution, nor is anything guaranteed about accuracy.
The C binding of POSIX places an error number in the
global variable errno
to report an error, along with
(in most cases) returning a sentinel value such as -1.
However, the procedures of this SRFI work differently. Rather than
reporting errors as return values, they report errors by signaling
condition objects satisfying the predicate posix-error?
defined below.
This SRFI provides three procedures which will typically be shims over whatever the implementation uses to report such errors:
(posix-error? obj)
→ boolean
This procedure returns#t
ifobj
is a condition object that describes a POSIX error, and#f
otherwise.
(posix-error-name posix-error)
→ symbol
This procedure returns a symbol that is the name associated with the value of
errno
when the POSIX function reported an error. This can be used to provide programmatic recovery when a POSIX function can return more than one value oferrno
.Because the
errno
codes are not standardized across different POSIX systems, but the associated names (bound by a#define
in the file/usr/include/errno.h
) are the same for the most part, this function returns the name rather than the code.For example,
ENOENT
(a reference was made to a file or a directory that does not exist) almost always corresponds to anerrno
value of 2. But althoughETIMEDOUT
(meaning that a TCP connection has been unresponsive for too long) is standardized by POSIX, it has aerrno
value of 110 on Linux, 60 on FreeBSD, and 116 on Cygwin.
(posix-error-message posix-error)
→ string
This procedure returns a string that is an error message reflecting the value oferrno
when the POSIX function reported an error. This string may be, or may include, the output of thestrerror()
function, and is useful for reporting the cause of the error to the user. It may or may not be localized.
This SRFI also recommends (but does not require) that the following additional information be retrievable by other means:
errno
(an exact integer)errno
Dealing with POSIX file descriptors in a Scheme environment is difficult.
In POSIX, open files are part of the process environment, and are referenced
by small exact integers called file descriptors. Open file descriptors are
the fundamental way I/O redirections are passed to subprocesses and executed
programs, since file descriptors are preserved across fork
and
exec
operations.
Scheme, on the other hand, uses ports for specifying I/O sources and sinks. Ports are garbage-collected Scheme objects, not integers. When a port is garbage collected, it is effectively closed, but whether the underlying file descriptor is closed is left as an implementation detail. Because file descriptors are just integers, it's impossible to garbage collect them.
Ideally, a Scheme program could only use ports and not file descriptors. But code written in any language, including Scheme, needs to descend to the file descriptor level in at least two circumstances: when interfacing with foreign code, and when interfacing with a subprocess.
This causes a problem. Suppose we have a Scheme port constructed on top of file descriptor 3. We intend to execute a successor program that will expect this file descriptor. If we drop references to the port, the garbage collector may prematurely close file 3 before the successor program starts.
Unfortunately, there is no even vaguely portable solution to the general problem. Scsh and Guile undertake heroic measures to open new file descriptors for ports when the old file descriptors are repurposed for something else, and to track when closing a port implies closing its file descriptor or not. But doing so involves more changes than an implementation should have to make in order to provide this SRFI.
Consequently, this SRFI assumes that file descriptors will only
be used at the edges of the program, and that most I/O operations will be
performed on ports.
As an exception, open-file
is provided, because
it allows arguments that the Scheme standard does not. It returns
a port of a specified type.
binary-input
textual-input
binary-output
textual-output
binary-input/output
Constants whose values represent the type of port to be returned byopen-file
orfd->port
. The textual ports use the same character encoding applied by default in the underlying implementation. The value ofbinary-input/output
represents a binary port that allows both input and output operations, as discussed in SRFI 181.
buffer-none
buffer-block
buffer-line
Constants whose values represent, respectively: the absence of port buffering,
where bytes are intended to appear from the source or at the destination as soon as possible;
buffering with a block of implementation-dependent size;
and buffering line by line, where a line is terminated
by a newline byte #xA
.
The default is implementation-dependent.
(open-file fname port-type flags [permission-bits [buffer-mode] ])
→ port POSIX open()
Opens the file named by fname and returns a port of the type specified by port-type. Flags is an integer bitmask, composed by adding together any of the following constants:open/append
,open/create
,open/exclusive
,open/nofollow
,open/truncate
. (The POSIX flags O_RDONLY, R_WRONLY, and O_RDWR are inferred from the port type.) Permission-bits defaults to#o666
, but are masked by the current umask.
(fd->port fd port-type [buffer-mode])
→ port
This procedure wraps a newly created port around the specified file descriptor, effectively importing it into the Scheme world. The most common use of this procedure is for a file descriptor other than 0, 1, 2 (standard input, standard output, standard error) that is already open when the Scheme program starts. It is an error if a port already exists that encapsulates fd, or if an attempt is made to use
fd->port
twice on the same fd.
The following procedures allow access to the computer's file system.
(create-directory fname [permission-bits])
→ undefined POSIX mkdir()
(create-fifo fname [permission-bits])
→ undefined POSIX mkfifo()
(create-hard-link old-fname new-fname)
→ undefined POSIX link()
(create-symlink old-fname new-fname)
→ undefined POSIX symlink()
These procedures create objects of various kinds in the file system. If an object with the same name already exists, an exception is signaled.
The permission-bits for
create-directory
default to#o775
, and forcreate-fifo
to#o664
, but are masked by the current umask.
(read-symlink fname)
→ string POSIX readlink()
Return the filename referenced by the symlink fname.
(rename-file old-fname new-fname)
→ undefined POSIX rename()
If you override an existing object, then old-fname and new-fname must type-match — either both directories, or both non-directories. This is required by the semantics of POSIXrename()
.Calling
rename-file
on a symbolic link will rename the symbolic link, not the file it refers to.Remark: There is an unfortunate atomicity problem with therename-file
procedure: if you create filenew-fname
sometime betweenrename-file
's existence check and the actual rename operation, your file will be clobbered withold-fname
. There is no way to prevent this problem; at least it is highly unlikely to occur in practice.
(delete-directory fname)
→ undefined POSIX rmdir()
This procedure deletes directories from the file system. An error is signaled if fname is not a directory or is not empty.
(set-file-owner fname uid gid)
→ undefined POSIX chown()
This procedure sets the owner and group of a file specified by supplying the filename. If the uid argument is the constantowner/unchanged
, the owner is not changed; if the gid argument is the constantgroup/unchanged
, the group is not changed. Setting file ownership usually requires root privileges. This procedure follows symlinks and changes the files to which they refer.
(set-file-times fname [access-time-object modify-time-object])
→ undefined POSIX utimensat()
This procedure sets the access and modified times for the file fname to the supplied time object values. It is an error if they are not of typetime-utc
. If neither time argument is supplied, they are both taken to be the current time. The constantstime/now
andtime/unchanged
are bound to values used to specify the current time and an unchanged time respectively. It is an error if exactly one time is provided. This procedure will follow symlinks and set the times of the file to which it refers. If the procedure completes successfully, the file's time of last status-change (ctime
) is set to the current time.
(truncate-file fname/port len)
→ undefined POSIX truncate()
The specified file is truncated to len bytes in length.
(file-info fname/port follow?)
→ file-info-record POSIX stat()
The file-info
procedure
returns a file-info record containing useful
information about a file.
If the follow? flag is true the procedure will follow symlinks and
report on the file to which they refer. If follow? is false
the procedure checks the actual file itself, even if it's a symlink.
The follow? flag is ignored if the file argument is a port.
(file-info? obj)
→ boolean Returns#t
if obj is a file-info record and#f
otherwise.
(file-info:device file-info)
→ integer (file-info:inode file-info)
→ integer (file-info:mode file-info)
→ integer (file-info:nlinks file-info)
→ integer (file-info:uid file-info)
→ integer (file-info:gid file-info)
→ integer (file-info:rdev file-info)
→ integer (file-info:size file-info)
→ integer (file-info:blksize file-info)
→ integer (file-info:blocks file-info)
→ integer (file-info:atime file-info)
→ time-object (file-info:mtime file-info)
→ time-object (file-info:ctime file-info)
→ time-object Returns the device number, inode number, mode (permission and file type bits), number of hard links, user id, group id, device ID if file is special, size in bytes, optimal blocksize for I/O, number of 512B blocks allocated, last access time, last modification time, and last change of status times (using time objects of type
time-utc
) stored in file-info respectively.Although POSIX does not standardize bit positions in the file mode, the following assignments are de facto standards (all except the socket, symlink, and fifo values have been present and unchanged since the Sixth Edition of Research Unix):
#o140000 socket file #o120000 symbolic link file #o100000 regular file #o60000 block device file #o40000 directory file #o20000 character device file #o10000 fifo or pipe file #o4000 setuid file #o2000 setgid file #o1000 sticky directory (restrictions on deletion) #o400 user read permission #o200 user write permission #o100 user execute permission #o40 group read permission #o20 group write permission #o10 group execute permission #o4 other user read permission #o2 other user write permission #o1 other user execute permissionNote that to distinguish between file types it is necessary to examine several bits.
(file-info-directory? file-info)
→ boolean POSIX S_ISDIR()
(file-info-fifo? file-info)
→ boolean POSIX S_ISFIFO()
(file-info-symlink? file-info)
→ boolean POSIX S_ISLNK()
(file-info-regular? file-info)
→ boolean POSIX S_ISREG()
(file-info-socket? file-info)
→ boolean POSIX S_IFIFO()
(file-info-device? file-info)
→ boolean POSIX S_ISCHR() || S_ISBLK()
These procedures are file-type predicates that test the file type stored in file-info.This SRFI does not provide a special means for checking the permission bits in a
file-info
record, though they are available infile-info:mode
. There are several problems with such procedures. First, there's an atomicity issue. In between checking permissions for a file and then trying an operation on the file, another process could change the permissions, so a return value from these functions guarantees nothing. Second, POSIX special-cases permission checking when the uid is 0 (root
) — if the file exists, root is assumed to have the requested permission. However, not even root can write a file stored on a read-only file system, such as a CD-ROM.
(set-file-mode fname mode-bits)
→ undefined POSIX chmod()
This procedure sets the mode bits of a file specified by supplying the filename. This procedure follows symlinks and changes the files to which they refer.
(directory-files dir [dotfiles?])
→ string list Return a list of filenames in directory dir. The dotfiles? flag (default#f
) causes files beginning with.
to be included in the list. Regardless of the value of dotfiles?, the two files.
and..
are never returned.The directory dir is not prepended to each filename in the result list. That is,
returns(directory-files "/etc")
not("chown" "exports" "fstab"
...
)To use the filenames in the returned list, the programmer can either manually prepend the directory, or change to the directory before using the filenames.("/etc/chown" "/etc/exports" "/etc/fstab"
...
)
(make-directory-files-generator dir [dotfiles?])
→ generator Return a SRFI 158 generator of the filenames in directory dir. The dotfiles? flag (default#f
) causes files beginning with.
to be included in the list. Regardless of the value of dotfiles?, the two files.
and..
are never returned.Like
directory-files
above, the directory dir is not prepended to each filename in the results the generator returns.The generator approach is particularly useful when the number of items in a directory might be "huge", which has been a common paradigm when using a file system as a document database.
Note that the generator must be run to exhaustion to close the underlying open directory object.
(open-directory dir [dot-files?])
→ directory-object POSIX opendir()
(read-directory directory-object)
→ string or eof-object POSIX readdir()
(close-directory directory-object)
→ undefined POSIX closedir()
These procedures implement an interface to the
opendir()
/readdir()
/closedir()
family of functions for processing directories.The
open-directory
procedure opens the directory with the specified pathname for reading, returning an opaque directory object. Thenread-directory
returns the name of the next available file, or the end-of-file object if there are no more files. Thedot-files?
argument controls whether filenames beginning with ".
" are returned. If it is#f
, which is the default, they are not. The filenames.
and..
are never returned. Finally,close-directory
closes a directory object.
(real-path path)
→ string POSIX realpath()
Returns an absolute pathname derived from pathname that names the same file and whose resolution does not involve dot (.
), dot-dot (..
), or symlinks.
(file-space path-or-port)
→ exact integer POSIX statvfs(), fstatvfs()
Returns the amount of free space in bytes on the same volume as the file path (if it is a string) or the file open on port (if it is a port). This allows the application to detect if the disk is getting full. Use path if the file has not yet been created, or port if it is already open.
temp-file-prefix
string parameterSRFI 39 or R7RS parameter that returns a string when invoked. Its initial value is the value of the environment variableTMPDIR
concatenated with"/pid"
ifTMPDIR
is set and to"/tmp/pid"
otherwise, where pid is the id of the current process. On Windows, the temporary directory's name is not fixed, and must be obtained by theGetTempPath()
API function.
(create-temp-file [prefix])
→ string Creates a new temporary file and returns its name. The optional argument specifies the filename prefix to use, and defaults to the result of invokingtemp-file-prefix
. The procedure generates a sequence of filenames that have prefix as a common prefix, looking for a filename that doesn't already exist in the file system. When it finds one, it creates it with permission#o600
and returns the filename. (The file permission can be changed to a more permissive permission withset-file-mode
after being created.)This file is guaranteed to be brand new. No other process will have it open. This procedure does not simply return a filename that is very likely to be unused. It returns a filename that definitely did not exist at the moment
create-temp-file
created it.It is not necessary for the process's pid to be a part of the filename for the uniqueness guarantees to hold. The pid component of the default prefix simply serves to scatter the name searches into sparse regions, so that collisions are less likely to occur. This speeds things up, but does not affect correctness.
(call-with-temporary-filename maker [prefix])
→ object+ This procedure can be used to perform certain atomic transactions on the file system involving filenames. Some examples:
- Linking a file to a fresh backup temp name.
- Creating and opening an unused, secure temp file.
- Creating an unused temporary directory.
This procedure uses prefix to generate a series of trial filenames. Prefix is a string, and defaults to the value of invoking
temp-file-prefix
. File names are generated by concatenating prefix with a varying string.The maker procedure is called serially on each filename generated. It must return at least one value; it may return multiple values. If the first return value is
#f
or if maker signals an exception indicating that the file exists,call-with-temporary-filename
will loop, generating a new filename and calling maker again. If the first return value is true, the loop is terminated, returning whatever value(s) maker returned.After a number of unsuccessful trials,
call-with-temporary-filename
may give up, in which case an exception is signaled or propagated.To rename a file to a temporary name:
Recall that this SRFI reports procedure failure by signaling an error. This is critical for this example — the programmer can assume that if the(call-with-temporary-filename (lambda (backup) (create-hard-link old-file backup) backup) ".temp.") ; Keep link in current working directory (delete-file old-file)call-with-temporary-filename
call returns, it returns successfully. So the followingdelete-file
call can be reliably invoked, safe in the knowledge that the backup link has definitely been established.To create a unique temporary directory:
Similar operations can be used to generate unique fifos, or to return values other than the new filename (for example, an open port).(call-with-temporary-filename (lambda (dir) (create-directory dir) dir) "/tmp/tempdir.")
(umask)
→ exact integer POSIX umask()
Returns the current file protection mask, or umask, as an exact integer. Whenever a file is created, the specified or default permissions are bitwise-anded with the complement of the umask before they are used.
(set-umask! umask)
→ unspecified POSIX umask()
Sets the file protection mask to the exact integer umask and returns an unspecified value.
Warning: Although POSIX specifies that changing the umask affects all threads in the current process, some Scheme implementations maintain a separate simulated umask for each thread. As a result, the effects of this procedure in a multi-threaded program are only partly predictable. This SRFI recommends (but does not require) that in multi-threaded programs the mask be set in the primordial thread before any other threads are created and never changed again.
(current-directory)
→ string POSIX getcwd()
Returns the current directory as a string containing an absolute pathname. Whenever a file is referenced with a relative path, it is interpreted as relative to this directory.
(set-current-directory! new-directory)
→ unspecified POSIX chdir()
Sets the current directory to new-directory and returns an unspecified value.
Warning: Although POSIX specifies that changing the current directory affects all threads in the current process, some Scheme implementations maintain a separate simulated current directory for each thread. As a result, the effects of this procedure in a multi-threaded program are only partly predictable. This SRFI recommends (but does not require) that in multi-threaded programs the current directory be set in the primordial thread before any other threads are created and never changed again.
(pid)
→ exact integer POSIX getpid()
Retrieves the process id for the current process.
(nice [delta])
→ exact integer POSIX nice()
Increments the niceness of the current process by delta. The lower the niceness value is, the more the process is favored during scheduling. If
delta
is not specified, the increment is 1.Real-time processes are not affected by
nice
.
(user-uid)
→ exact integer POSIX getuid()
(user-gid)
→ exact integer POSIX getgid()
(user-effective-uid)
→ exact integer POSIX geteuid()
(user-effective-gid)
→ exact integer POSIX getegid()
(user-supplementary-gids)
→ exact integer list POSIX getgroups()
For the calling process, these routines get the specified data. The scsh procedureuser-login-name
, which usesgetlogin()
orgetlogin_r()
, can be simulated with(user-info:name (user-info (user-uid)))
, using procedures that are described in the next section.
These procedures are used to access the user and group databases
(for example, the ones traditionally stored in /etc/passwd
and /etc/group
).
(user-info uid/name)
→ record POSIX getpwuid/getpwnam()
Return auser-info
record giving the recorded information for a particular user. The uid/name argument is either an exact integer user id or a string user name. If uid/name does not identify an existing user,#f
is returned; this does not constitute an error situation, and callers must be prepared to handle it.
(user-info? obj)
→ boolean Returns#t
if obj is a user-info record and#f
otherwise.
(user-info:name user-info)
→ string (user-info:uid user-info)
→ exact integer (user-info:gid user-info)
→ exact integer (user-info:home-dir user-info)
→ string (user-info:shell user-info)
→ string Returns the user name, user id, group id, home directory,
and shell path stored in user-info respectively.
An implementation returns #f
for any unavailable items.
(user-info:full-name user-info)
→ string Returns the contents of the pw_gecos
field
stored in user-info.
Although this field is not part of POSIX,
it has been part of all Unix variants since at least
the Sixth Edition of Research Unix.
It normally contains the user's full name, but may contain additional
system-specific information;
on Windows, it contains exactly the full name.
(user-info:parsed-full-name user-info)
→ string list Returns a parsed and expanded version of the raw string returned by
user-info:full-name
. The raw value is split on commas, creating a list of strings to be returned. All ampersands in the first element of the list are replaced byuser-info:name
, which is capitalized if it starts with an ASCII lowercase letter.However, on Windows the implementation is completely different:
user-info:parsed-full-name
returns a list with a single element, the result ofuser-info:full-name
. No comma splitting or ampersand substitution is performed.The meaning of the first element of the returned list is the user's full name on all known systems. The remaining elements have varying meaning. For example, on BSD systems, the second through fourth elements are the user's work location, the user's work phone number, and the user's home phone number, respectively. On Cygwin, the second element is the Windows SID corresponding to this user; further elements depend on Cygwin-specific entries in the
/etc/nsswitch.conf
file.
(group-info gid/name)
→ record POSIX getgrgid/getgrnam()
Return agroup-info
record giving the recorded information for a particular group. The gid/name argument is either an exact integer group id or a string group name. If gid/name does not identify an existing group,#f
is returned; this does not constitute an error situation, and callers must be prepared to handle it.
(group-info? obj)
→ boolean Returns#t
if obj is a group-info record and#f
otherwise.
(group-info:name group-info)
→ string (group-info:gid group-info)
→ exact integer Returns the group name and group id stored in group-info. The list of group member ids can be retrieved with the above (user-supplementary-gids)
(posix-time)
→time-object POSIX clock_gettime()
The
posix-time
procedure returns the current time as a time object of typetime-utc
, which represents the time since the POSIX epoch (midnight January 1, 1970 Universal Time), excluding leap seconds. It uses the POSIXCLOCK_REALTIME
clock.
(monotonic-time)
→time-object POSIX clock_gettime()
The
monotonic-time
procedure returns the current time as a time object of typetime-monotonic
, which represents the time since an arbitrary epoch. This epoch is arbitrary, but cannot change after the current program begins to run. It is guaranteed that a call tomonotonic-time
cannot return a time earlier than a previous call tomonotonic-time
. This is not guaranteed forposix-time
because the system's POSIX clock is sometimes turned backward to correct local clock drift. It uses the POSIXCLOCK_MONOTONIC
clock.
R7RS provides two procedures , originally from SRFI 98, to get access to environment variables:
(get-environment-variables)
(get-environment-variable name)
Setters have not yet been standardized, so this SRFI provides them.
The main reason to set environment variables is that some C libraries
depend on their values: for example, localization depends on LANG
.
If a program needs to change to a different locale, the most reliable way
is to set the LANG
variable to a new value.
(set-environment-variable! name value)
→ undefined POSIX setenv
Change the value of the environment variable name to be value. Both name and value are strings. If name is not defined at the time of call, a new variable is added; if name is defined, its old value is discarded and replaced by value. If name or value are invalid according to the operating system, an exception is signaled. Mutating name or value after the call must not change the name or value of the environment variable.
(delete-environment-variable! name)
→ undefined POSIX unsetenv
Remove the environment variable name such that a subsequent(get-environment-variable name)
would return#f
. If the variable cannot be removed, an exception is signaled. If name does not currently have a value, the call silently succeeds.
Concurrency note: Under most operating systems, environment variables are per-process, not per-thread. Multi-threaded implementations should guard against race conditions.
Naming note: Many Scheme implementations (including scsh)
use the short procedure names getenv
and setenv
(or minor variations). Since the long
name get-environment-variable
has been canonized by
R7RS and SRFI 98, this SRFI follows that pattern instead.
Since environment variables are OS variables rather than Scheme variables,
it's debatable whether procedures to modify them should bear
the !
suffix. MIT Scheme has the suffixed names and we
are not aware of any Scheme implementation with the unsuffixed names,
so the suffix is kept in this SRFI.
(terminal? port)
→ boolean POSIX isatty()
Returns true if the argument is a file descriptor that is open on a terminal. Raises an exception if the underlying call toisatty()
returns an error other thanENOTTY
. This procedure is useful when writing programs that change their behavior when their standard input or output is a terminal. Because it is expected to be called before doing input or output on the terminal, it accepts a port rather than an file descriptor object.
There are two implementations of this SRFI, Scsh version 0.7,
which can be found at GitHub in the scsh repository of scheme,
and a Chibi Scheme (srfi 170)
library. You can find the Chibi Scheme example implementation,
and build notes for scsh, in their own srfi
subdirectories.
Notes on the exceptions and deviations between this SRFI and
the Chibi implementation can be found in those subdirectories, at
srfi/scsh/NOTES.html
,
and between this SRFI and scsh 0.7 in
srfi/chibi-scheme/NOTES.html
.
Thanks to Olin Shivers, sine quo non, and all the Scheme implementors who have followed his work. Thanks also to all the participants in the SRFI mailing list.
(Why quo? Because in sine qua non the pronoun is feminine, agreeing with re 'thing' understood, whereas Olin is masculine.)
Special thanks to Alex Shinn for coming up with the idea of FDOs.
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.
This SRFI is derived from the documentation for
scsh, whose copyright notice, from
the COPYING
file, is reprinted here:
Copyright (c) 1993-2003 Richard Kelsey and Jonathan ReesCopyright (c) 1994-2003 by Olin Shivers and Brian D. Carlstrom.Copyright (c) 1999-2003 by Martin Gasbichler.Copyright (c) 2001-2003 by Michael Sperber.Copyright (c) 2019-2020 by John Cowan and Harold Ancell.All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- The name of the authors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.