Skip to content

Operating-system interface

These extensions expose process state and synchronous subprocess execution. They are in EGCL-EXT. Their signatures differ from some similarly named SBCL extensions; use this dictionary rather than substituting package names in an SBCL call.

Environment

egcl-ext:getenv

Function (egcl-ext:getenv name) → string or nil

Returns the process environment variable named by the string name. Returns nil when it is absent or cannot be represented by the host environment API. The current evaluator does not enforce the specification's proposed :env sandbox capability check on this operation.

(or (egcl-ext:getenv "HOME") "no home directory")

Current directory

egcl-ext:getcwd

Function (egcl-ext:getcwd) → namestring or nil

Returns the process working directory as a string with a trailing slash, or nil if the host query fails. This is process state; it is not the same thing as a dynamically bound Lisp *default-pathname-defaults*.

Subprocesses

egcl-ext:run-program

Function (egcl-ext:run-program command) → exit-code, stdout, stderr

Runs a command synchronously and captures standard output and standard error. A list of strings names the executable and its arguments directly. On Unix, a string command is interpreted by /bin/sh -c.

The returned code is -1 when the host provides no ordinary exit code. Captured bytes are decoded into strings with replacement for invalid UTF-8. A failure to launch the command signals a file error. A nonzero child exit status is a returned status, not by itself a Lisp launch error.

This entry point does not accept SBCL's run-program keyword interface. It returns strings rather than a process object or live stream. Streaming and asynchronous process management are not supplied by this function.

(multiple-value-bind (code output errors)
    (egcl-ext:run-program '("printf" "%s" "hello"))
  (list code output errors))
;; => (0 "hello" "")

Use a list when no shell expansion is needed. A string intentionally permits shell syntax and must not be assembled from untrusted fragments as though it were an argument list. Sandbox mode denies the operation.

Command-line arguments

*command-line-args*

Variable list of strings

Contains the application arguments after the CLI's -- separator. Without arguments it is nil.

egcl report.lisp -- first second

In that script the variable is ("first" "second"). It does not include the executable name, script pathname, or interpreter options.

egcl-ext:raw-command-line-arguments

Function (egcl-ext:raw-command-line-arguments) → list of strings

Returns the host argument vector, including the executable as its first item. This is a lower-level interface used by integration libraries. It is distinct from the parsed application arguments above.

See Starting and stopping for invocation order and init-file behavior, and Compilation for the compiler observation functions previously grouped with these operating-system calls.

String and octet conversion

Function (egcl-ext:string-to-octets string &key (external-format :utf-8) (start 0) end null-terminate) → byte vector

Function (egcl-ext:octets-to-string vector &key (external-format :utf-8) (start 0) end) → string

These functions convert UTF-8 or ASCII text for binary files and foreign buffers. Encoding bounds count characters; decoding bounds count bytes. end defaults to sequence length. :null-terminate t appends one zero byte when encoding; decoding does not treat zero as a terminator, so pass the position of the first zero as :end when reading a C string. Inputs are not modified and results are fresh.

Supported format names are :utf-8, :utf8, :ascii, and :us-ascii; :default means UTF-8. ASCII encoding rejects non-ASCII characters. Both decoders replace malformed input with U+FFFD, rather than signalling as SBCL's default decoder does. Other external formats are not supported.

Atomic updates

Macro (egcl-ext:cas place old new) → previous value

Macro (egcl-ext:atomic-incf place &optional (delta 1)) → previous value

Macro (egcl-ext:atomic-decf place &optional (delta 1)) → previous value

CAS compares the current value of place with old using EQ. On a match it stores new; otherwise it leaves the place unchanged. It returns the value it observed in either case, not a success flag. Compare that result with the expected value using EQ to determine whether the exchange succeeded.

Place CAS ATOMIC-INCF / ATOMIC-DECF
Special variable, written as a bare symbol Yes Yes
Structure-slot accessor, such as (counter-value counter) Yes Yes
(svref simple-vector index) Yes Yes
(car cons) or (cdr cons) Yes No
(symbol-value symbol) or (symbol-plist symbol) Yes No
(slot-value instance slot-name) for an instance-allocated slot Yes No

These are not general replacements for SETF or INCF: arbitrary SETF places and ordinary lexical variables are not supported. Symbol value cells refer to the current dynamic binding when one exists, otherwise to the global binding. Updating a thread-local dynamic binding does not update another thread's binding. Place subforms and value/delta forms are evaluated once, with place subforms evaluated before the values.

ATOMIC-INCF adds delta; ATOMIC-DECF subtracts it. Both the stored value and delta must be fixnums or a TYPE-ERROR is signalled. Overflow signals ARITHMETIC-ERROR rather than wrapping or promoting to a bignum. A failed type or overflow check does not store a replacement value. Unlike INCF and DECF, these macros return the value before the update.

(let ((cell (cons :empty nil)))
  (list (egcl-ext:cas (car cell) :empty :ready)
        (egcl-ext:cas (car cell) :empty :other)
        (car cell)))
;; => (:EMPTY :READY :READY)

(let ((counter (vector 10)))
  (list (egcl-ext:atomic-incf (svref counter 0) 3)
        (egcl-ext:atomic-decf (svref counter 0))
        (svref counter 0)))
;; => (10 13 12)

Shared-cell updates use lock-free compare-and-exchange operations: success has acquire/release ordering and a failed comparison has acquire ordering. Arithmetic updates retry if another updater wins the comparison. This is not a transaction across several places and does not make an arbitrary object thread-safe. All concurrent writers of the same cell must follow an atomic protocol; do not mix these operations with unsynchronized SETF, INCF, or DECF, or assume an ordinary read supplies the required synchronization.

Memory ordering

Function (egcl-ext:memory-barrier &optional (kind :full)) → nil

Function (egcl-ext:load-barrier) → nil

Function (egcl-ext:store-barrier) → nil

These operations order memory accesses on the calling native thread. :read and load-barrier use an acquire fence; :write and store-barrier use a release fence; :full uses a sequentially consistent fence. :data-dependency uses the stronger acquire fence. Unknown kinds signal program-error.

For a shared-memory publication protocol, acquire after reading the publication word and before consuming its payload; release after producing the payload and before publishing its word. The publication word also needs an appropriate atomic access protocol. A fence does not make an arbitrary memory access atomic or wait for another thread to arrive.

The public functions are available now. The current implementation calls a runtime fence helper; dedicated x86 T1/T2 intrinsic lowering is still pending. The interface promises memory ordering, not a particular instruction sequence or call latency.