Algol 68 environment and processes
last modified November 13, 2026
A program does not run in a vacuum. The shell hands it a set of environment
variables, it may start other programs and wait for them to finish, and it can
measure how much processor time it has used. Algol 68 Genie exposes these
facilities through a few predeclared procedures: getenv reads the
environment, system runs a command, sleep pauses the
program, clock reports processor time, and stop ends
the program early.
These procedures are extensions of the implementation, not part of the Revised Report, and a program that uses them depends on Algol 68 Genie. This article describes each one and closes with a small tool that runs a command and reports how it ended.
Reading environment variables
getenv takes the name of a variable and returns its value as a
STRING. A name that is not in the environment yields an empty
string, which is a row with lower bound 1 and upper bound 0, so
UPB answers 0 and the test needs no special case.
BEGIN
STRING path = getenv("PATH");
STRING missing = getenv("A68G_NO_SUCH_VARIABLE");
IF UPB path > 0 THEN print(("PATH is set", newline)) FI;
IF UPB missing = 0 THEN print(("A68G_NO_SUCH_VARIABLE is not set", newline)) FI
END
PATH is almost always set, while the invented name is not. The
program does not need the value of a variable to find out whether it exists,
and it never has to handle a missing value as an error.
$ a68g env.a68 PATH is set A68G_NO_SUCH_VARIABLE is not set
A setting with a default
Environment variables are a convenient way to configure a script without
editing its source. The declaration initialises name from the
environment, and the conditional clause replaces an empty value with the
default before the greeting is written.
BEGIN
STRING name := getenv("A68G_NAME");
IF UPB name = 0 THEN name := "world" FI;
print(("Hello, ", name, newline))
END
The program prints Hello, world when A68G_NAME is not
set. Starting it as A68G_NAME=Ada a68g greet.a68 prints
Hello, Ada, with no change to the program.
$ a68g greet.a68 Hello, world
Starting a process
system passes its string to the shell, runs the command, waits for
it to finish, and returns a status. The command writes to the same standard
output as the program, so its line appears between the lines of the program.
BEGIN
INT rc := system("echo hello from the shell");
print(("status ", whole(rc, 0), newline));
rc := system("exit 3");
print(("exit 3 gives status ", whole(rc, 0), ", code ", whole(rc OVER 256, 0), newline))
END
The status of a command that succeeds is 0, whatever it wrote. The second call
runs exit 3, which is a shell builtin, and the value 768 shows that
the status is not simply the exit code.
$ a68g run.a68 hello from the shell status 0 exit 3 gives status 768, code 3
Exit codes and signals
A wait status packs two numbers into one integer: the exit code is multiplied
by 256 and the number of the signal that killed the process is added to it.
rc OVER 256 recovers the exit code and rc MOD 256
recovers the signal. A shell reports the same value after dividing by 256,
which is why an exit code of 3 is described as a status of 768.
BEGIN
INT rc := system("kill -TERM $$");
print(("status ", whole(rc, 0), newline));
print(("exit code ", whole(rc OVER 256, 0), ", signal ", whole(rc MOD 256, 0), newline))
END
A process killed by a signal has no exit code of its own, so the code is 0 and the signal number is 15 for the termination signal. A program that inspects the status of another program should test both halves before it decides what went wrong.
$ a68g signal.a68 status 15 exit code 0, signal 15
Waiting
sleep suspends the program for the given number of seconds, which
is useful when a script has to pace itself or wait for a file to appear. The
pause costs no processor time, and the example shows that by reading
clock before and after the call.
BEGIN
REAL before = clock;
sleep(1);
REAL after = clock;
print(("slept for one second", newline));
print(("cpu time used: ", (after - before < 0.1 | "almost none" | "some"), newline))
END
clock returns the processor time used by the program since it
started, in seconds, as a REAL. A sleep does not advance it,
because the program is not running, and the difference stays below a tenth of a
second even though a second has passed.
$ a68g wait.a68 slept for one second cpu time used: almost none
Measuring a computation
The same two readings time a piece of work. The declarations of
start and finish hold the values returned by
clock, and the difference is the processor time that the loop
used.
BEGIN
REAL start = clock;
INT total := 0;
FOR i TO 1000000 DO total +:= i MOD 7 OD;
REAL finish = clock;
print(("total ", whole(total, 0), newline));
print(("cpu time was measurable: ", (finish - start > 0.0 | "yes" | "no"), newline))
END
The exact number is a property of the machine and of the interpreter, so the program prints the result of the work and only asserts that the difference is positive. A program that compares two implementations in the same run can use the difference itself, because the machine conditions cancel out.
$ a68g timeit.a68 total 2999998 cpu time was measurable: yes
Ending a program early
stop ends the program at once, with status 0. It is the way out of
a check that fails deep inside a procedure, where returning a value would only
push the problem to the caller. The statements after stop, in the
procedure and in the block that called it, never run.
PROC check = (INT n) VOID:
IF n > 2 THEN
print(("value too large, stopping", newline));
stop
ELSE
print(("accepted ", whole(n, 0), newline))
FI;
BEGIN
FOR i TO 4 DO check(i) OD;
print(("this line never appears", newline))
END
The loop reaches 1 and 2, and the third call prints its message and stops. The
final print of the block is never reached. This implementation
also accepts abort and halt, which have the same
effect.
$ a68g stop.a68 accepted 1 accepted 2 value too large, stopping
A command runner
The arguments of a script and the system procedure combine into a
tool that runs one command and reports how it ended. The name comes from argv(4),
which is the first argument after the script itself.
BEGIN
IF argc < 4 THEN
print(("usage: a68g --script report.a68 COMMAND", newline))
ELSE
INT rc := system(argv(4));
print(("command ", argv(4), " finished with code ", whole(rc OVER 256, 0), newline))
FI
END
The command is passed to system unchanged, so the shell sees it as
a line of its own. Running the tool with true reports a code of 0,
and running it with exit 2 reports 2, because the status is
divided by 256 before it is printed.
$ a68g --script report.a68 true command true finished with code 0
Next steps
The command-line arguments
article explains argc and argv, which the last
example uses, and the files article shows how a
program reads and writes files of its own. The
reading input article covers the values
that a program takes from the terminal, and the
parallel clause article runs several
units at the same time.
Source
Revised Report on the Algorithmic Language Algol 68
In this article we have read environment variables and given them defaults,
started processes with system, decoded a wait status into an exit
code and a signal, paused a program with sleep, measured
processor time with clock, ended a program early with
stop, and built a tool that runs a command and reports its status.
The command-line arguments
article continues with the words that a script receives from its caller.
Author
List all Algol 68 tutorials.