The Posix Interface
for the
Unicon Programming Language


Shamim Mohamed
daemon.eps




©1997-2002 Shamim Mohamed

This document may be reproduced and redistributed freely provided it is reproduced in its entirety.


Introduction

The Icon Programming Language[1] provides a large set of platform-independent facilities for non-numerical processing, graphics etc. Icon runs on everything from Unix machines to Amigas and Macs. This strong point, its platform independence, is also a weak point: it could not offer access to the underlying system and so could not be used as a system administration and scripting language like Perl[2]. This was a great disappointment to the author, who has had to write many Perl scripts over the years. While it is true that Perl substitutes for a congolmeration of sed, awk and shell scripts, it does so with some of the worst language features from them.

Icon, on the other hand, has always been a good clean language with lots of support for high-level control and data structures. If we could add the Unix system calls to the language, we would have the best of both worlds: a sensible and powerful VHLL as a Unix scripting language. Icon even has integrated support for X11 graphics!

This document describes a set of functions that implement access to POSIX system calls offered by the host system; it is the definitive reference for the POSIX functions of Unicon. Several functions are only available on Unix systems; programs that need to run on the multitudinous varieties of Windows should not use them.

The POSIX subsystem includes an include file posix.icn that defines the constants and the record types that are required. This file should be included by programs desirous of using the Posix interface.

All the common system calls (Section 2 of the Unix manuals) are available under the same names. Some of these are already implemented in standard Icon (chdir, rename). For system calls that return struct types, a record is created and returned; the components of the record have names that are similar to the elements of the struct.

Whenever possible, the type system of Icon is used to advantage. For example instead of having separate functions stat and fstat, the function stat calls the appropriate system procedure based on the type of the argument.

A new keyword &errno has been added to the language. If an error occurs during the execution of any of these system functions, the function will fail and &errno will be set. The string corresponding to an error ID is returned by the function sys_errstr. The first few errors (EPERM to EPIPE) seem to be common between systems, and these are defined in the header file; &errno can be directly compared against constants like ENOENT etc. In general, however, it is safer to format the error with the sys_errstr function.

Another keyword &now will return the system time expressed as an integer. This value can be used to compare file modification dates etc. Its exact meaning is implementation dependent. (On Unix systems it is the number of seconds since the epoch, i.e. 00:00:00 Jan. 1, 1970 UTC.)

Document Conventions

In this document, Unix system calls and commands are represented with the manpage section number and set in a typewriter font, thus: select(2); whereas Icon names and functions are set in a sans serif font: select().

Signals

Signals may be trapped or ignored with the trap function. The signal argument is passed by name, and may be system dependent, e.g. Linux systems don't have a SIGLOST. The signal handler defaults to the default provided by the system -- for instance, SIGHUP is ignored by default but SIGFPE will cause a core dump. See Fig. 1 for an example.

global oldhandler
     ...             
     trap("SIGFPE", sig_ignore)
     oldhandler := trap("SIGSEGV", handler)
     ...
     trap("SIGSEGV", oldhandler)
end

procedure sig_ignore(s); end
procedure handler(s)
     write("Got signal ", s)
     oldhandler(s)                  # propagate the signal
end
Figure 1: Signal handling

Spawning programs

On Unix systems, programs may be forked in the usual Unix manner of calling fork(2) and then exec*(2). The function fork behaves exactly as the Unix version; exec subsumes all of the Unix exec* system calls. The first argument is the filename of the program to execute, and the remaining arguments are the values of argv that the program will get, starting with argv[0].

if fork() = 0 then
   exec("/bin/echo", "echo", "Hello,", "world!")
Currently there is no way of passing the environment (execve style).

However, for portability to non-Unix systems, system should be used. The usual usage of fork is to manipulate file descriptors for stdin, stdout and stderr and then perform an exec; this functionality is provided by the full form of the system function.

   ...
   L := pipe()
   system(["/bin/wc", "-x"], L[1], , , "nowait")
   close(L[1])

   system("/bin/egrep -i procedure filename.c", , L[2], , "nowait")
   close(L[2])
   ...
Figure 2: A pipe implemented with select

The first argument is either a list of strings or a string, and represents the command to execute (along with its arguments); the next three arguments are files that represent the values of stdin, stdout and stderr that the new process should have; and the last signifies whether the function should wait for the new process to terminate, a value of "nowait" will cause the function to return leaving the new process running in the background.

When the string form is used, it [the string] is passed to the shell for interpretation. If you need wildcards use this form; if the arguments to the process have embedded whitespace and you don't want to mess with shell quoting, use the list form. N. B. A trailing `&' in the string form will also cause the spawned process to run in the background.

Redirecting I/O Streams

The portable way to redirect streams for spawn processes is with system as described above. However on Unix systems, dup is also available.

Fig. 3 shows pipes and fdup being used to redirect a child process' input.

L := pipe() | stop("Couldn't get pipe: ", sys_errstr(&errno))
if fork() = 0 then {
     close(L[2])
     fdup(L[1], &input)
     exec(...)
}
close(L[1])
write(L[2], ...)                     # write to child's stdin
Figure 3: A pipe from parent to child

The function filepair is similar to pipe except that the connection is bidirectional.

Networking

TCP

Icon provides a much simpler interface to BSD-style sockets. Instead of the four different system calls that are required to start a TCP/IP server on Unix systems, Icon provides only one--the "na" (network accept) flags to open. The first argument to open is the network address to connect to -- host:port for Internet domain connections, and a filename for Unix domain sockets. If the address starts with ":" i.e. no host name, the socket is opened on the same machine. The returned value from open is a file that can be used in select etc. as well as normal reading and writing.

procedure main()
     while f := open(":1888", "na") do
          if fork() = 0 then {
               servicerequest(f)
               exit()
          } else
               close(f)
     (&errno = 0) | stop("Open failed: ", sys_errstr(&errno))
end
Figure 4: An Internet TCP server
procedure finger(n)
     static fserv
     initial fserv := getserv("finger") |
          stop("Couldn't get service: ", sys_errstr(&errno))

     n ? {
          name := n; host := ""
          name := tab(upto('@')) & ="@" & host := tab(0)
     }
     if *host > 0 then write("[", host, "]")
     
     f := open(host || ":" || fserv.port, "n") |
          stop("Couldn't open connection: ", sys_errstr(&errno))

     write(f, name) | stop("Couldn't write: ", sys_errstr(&errno))
     while line := read(f) do
          write(line)
end
Figure 5: A procedure that implements finger(1)

Fig. 4 shows Icon code that implements a simple Internet domain TCP server that listens on port 1888. To connect to this server, the "n" (network connect) flag is used. Fig. 5 shows a function that connects to a `finger' server.

UDP

UDP networking is similar to the TCP examples above, except that the additional character "u" is passed to open. When a call to writes is made, a message is sent to the address that was specified when the socket was created. The send function can also be used to send a UDP datagram.

To receive a UDP datagram, the receive function is used, which returns a record with two fields: the addr field contains the address of the sender in "host:port" form, and the msg field contains the message. See Fig. 6 for an example of a UDP server program, and Fig. 7 for a UDP client. (Note: since UDP is not reliable, the receive in Fig. 7 is guarded with a select, or it might hang forever if the reply is lost.)

The difference between using send and writes is that the latter keeps the socket open between calls, whereas the former doesn't. Typically a server will use open/receive to accept messages and send to reply; a client will use open/writes. In general, use the approach that makes the meaning of the program clearer.

     f := open(":1025", "nua")
     while r := receive(f) do {
          # Process the request in r.msg
          ...
          send(r.addr, reply)
     }
Figure 6: A UDP server
procedure main(args)
   (*args = 1) | stop("Usage: rdate host")
   host := args[1]
   
   s := getserv("daytime", "udp")
   
   f := open(host||":"||s.port, "nu") |
      stop("Open failed: ", sys_errstr(&errno))

   writes(f, " ")

   if *select(f, 5000) = 0 then
      stop("Connection timed out.")

   r := receive(f)
   write("Time on ", host, " is ", r.msg)
end
Figure 7: A UDP client using `daytime'

The select() system call

The function select() implemented in Icon performs a subset of the select(2) system call. Only the `read' fd_set is passed in; no provision is made to wait for exceptions on files or to ensure that writes don't block. (For non-blocking writes, use fcntl.) It is hoped that this somewhat weaker version of select will still be usable in the majority of the cases. (Appendix B shows an implementation of the script(1) command.)

Instead of modifying the sets of file descriptors in place, select() returns a list of files that have data to be read. If the timeout expires instead, an empty list is returned. Fig. 8 shows an example of the usage.

     while *(L := select(f1, f2, f3, timeout)) = 0 do handle_timeout()
     (&errno = 0) | stop("Select failed: ", sys_errstr(&errno))

     every f := !L do {
          # Dispatch reads pending on f
          ...
     }
Figure 8: Waiting for input on multiple files

Buffering: A Caveat

Care must be taken that the buffering used by stdio does not interfere with the files being used with select. Consider this code:

     while L := select(f, &window) do
          if L[1] === f then
               c := read(f)

If the string "hello" is ready on f, Icon will try to read till the next newline character and block, even though input can be read.

When used with a network connection or with a file that has been used with select, the function reads(f, n) will perform exactly one low-level read and will try to read up to n characters, bypassing any buffering that stdio may be doing. Since the unprocessed input is still in the low-level buffers, select will work correctly.

     while L := select(f, &window) do
          if L[1] === f then
               c := reads(f, 3)

The first time reads is called, it will return the string "hel"; the subsequent call to select finds input still waiting on f so it will return, resulting in a value of "lo" being read. If the size argument is omitted, reads will read as much as it can without blocking. Important: Do not mix this usage of reads with the usual read! There lie deadlocks.

The script(1) example (Appendix B) shows the usage of select and reads.

Known bug: if you plan to use select on a file, make sure that select is called before you perform any reads calls on it. If you will be reading from it before you need to call select, this will suffice:

    select(f, 0)
If you do not do that, you will get a runtime error (180 low-level read or select mixed with buffered read) the first time you call select.

Reading directories

The open function can open directories in the manner of readdir(2). They can only be opened for reading, and every read returns the name of one entry. Entries are not guaranteed to be in any specific order. Fig. 9 shows an implementation of a simple for of ls(1).

$include "posix.icn"
link printf

procedure main(args)
   every name := !args do {
      f := open(name) | stop(sys_errstr(&errno), name)
      L := list()
      while line := read(f) do
          push(L, line)
      every write(format(stat(n := !sort(L)), n))
   }
end
     
procedure format(p, name)
   s := sprintf("%7s %4s %s %3s %-8s %-8s %8s %s %s",
           p.ino, p.blocks, p.mode, p.nlink,
           p.uid, p.gid, p.size, ctime(p.mtime)[5:17], name)
   
   s ||:= " -> " || \p.symlink

   return s
end
Figure 9: A program that lists all the files in a directory

Regular Expressions

The regexp library in the Icon Program Library may be used for regular expression searching and search-and-replace operations.
link regexp
     ...
     result := ""
     s ? {
          while j := ReFind(re) do {
               result ||:= tab(j) || replacement
               tab(ReMatch(re))
          }
          result ||:= tab(0)
     }
Figure 10: Regular expression search-and-replace

Since the Icon program has access to the operation at a finer grain, more complex operations (rather than only search-and-replace) are possible.

Information from System Files

There are four functions that read information from system files: getpw to read the password file, getgr for the group file, gethost for hostnames and getserv for network services. Called with an argument (usually a string) they perform a lookup in the system file. When called with no arguments, these functions step through the files one entry at a time.

The functions setpwent, setgrent, sethostent, setservent do the same things as their Unix counterparts, i.e. they reset the file position used by the get* routines to the beginning of the file.

They return records whose members are similar to the structs returned by the system functions like getpwuid(2), gethostbyname(2) etc.

DBM databases

DBM databases may be opened by the open function. The only permissible values for the second argument are "d" and "dr", for opening the database read/write and read-only respectively.

Values are inserted into the database with insert(d, k, v) and are read from it with fetch(d, k).

Status 

The POSIX functions described here are part of the Unicon language, hosted at Sourceforge. It is distributed as source code under the GPL. (Arizona Icon is in the public domain.)

The POSIX functions have been tested on Solaris 8, OpenBSD 3.1, FreeBSD 4.6-STABLE and Linux 2.4. They should work on all Unix or Unix-like platforms; if you have any problems, please send me email.

Of course, Icon and Unicon come with no warranties. What you see is all you get.

Acknowledgements

My thanks to Clint Jeffery for the long discussions on the right Unix interface, and for inciting me to do commit this act of implementation, and for taking charge of the Unicon language. Thanks to Steve Lumos for porting and testing on FreeBSD, and for implementing the messaging extensions -- a fine addition to the language. Thanks also to Ralph Griswold and the Icon Project for creating and maintaining a fine language.

And of course thanks to Richard Stallman, Linus Torvalds and a cast of millions!

References

1
Ralph Griswold and Madge Griswold.
The Icon Programming Language.
Peer-To-Peer Communications, San Jose, California, 3d edition, 1997.

2
Larry Wall and Randall Schwartz.
Programming Perl.
O'Reilly and Associates, Sebastopol, California, 1991.

Appendix A: Reference Manual

Additions to pre-existing functions

Some additions have been made to the arguments and functionality of these Icon functions:




open(s1, s2) : f -- Open a file

The additional options to open are:

"na" listen on a TCP network socket
"n" connect to a TCP network socket
"nau" listen on a UDP network socket
"nu" connect to a UDP network socket
"d" open a DBM database

When opening a network socket: the first argument s1 is the name of the socket to connect to: if of the form "s:i", it is an Internet domain socket on host s and port i; otherwise, it's the name of a Unix domain socket. If the host name is null, it represents the current host.

For a UDP socket, `connect' means that any writes to that file will send a datagram to that address, so that the address doesn't have to specified each time. Also, read or reads cannot be performed on a UDP socket; use receive. UDP sockets must be in the INET domain, i.e. the address must have a colon.

For a DBM database, only one modifier character may be used: if s1 is "dr" it indicates that the database should be opened in read-only mode.




system(args, stdin, stdout, stderr, waitflag) : i -- Run a process

The first argument can be either a string (as in the original meaning of system or a list of strings. In the former case, whitespace is used to separate the arguments. In the second case, each member of the list is an argument.

When a string is the first argument, it is passed to the shell to evaluate. When the list form is used, the new process is created directly. If you want to use shell wildcards etc. use the string form, and be careful about the usual shell quoting and stuff. The list form is clear and unambiguous but you don't get all those features the shell gives you.

If the waitflag argument is "nowait", system will return immediately after spawning the new process. The default is for it to wait for the spawned process to exit.

The three file arguments are the files that should be used for the new process' standard input, standard output and standard error.

The return value is an integer: the pid of the process if it was run with "nowait", otherwise the exit status from the process.




Event(W, i) : a -- return next window event

The second argument i is a timeout value in milliseconds. If the timeout expires before an event is available, then Event() fails. The default for i signifies that Event() should wait for the next window event.

New functions

Keywords

&errno If an error occurs during the execution of these functions, the function will fail and &errno will be set. The string corresponding to an error ID is returned by the function sys_errstr.

&now The system time, represented as an integer. On Unix systems, this is the number of seconds since Jan 1 1970; on other systems it will be system dependent. The only safe usage for the value returned is to use it to compare file modification dates against, or to use as an argument to ctime.




chmod(f, mode) -- Change file mode

Sets the mode of a file f to mode. The mode can be an integer mode or a string representing the change to be performed. The string is of the form

[ugoa]*[+-=][rwxRWXstugo]*

or

[r-][w-][xsS-][r-][w-][xsS-][r-][w-][xtT-]

The first group describes the set of mode bits to be changed: u is the owner set, g is the group and o is the group `other.' The character a represents all the fields. The operator (+-=) describes the operation to be performed: + adds a permission, - removes a permission, and = sets a permission. The permissions themselves are:

r read
w write
x execute
R read if any other set already has r
W write if any other set already has w
X execute if any other set already has x
s setuid (if the first part contains u
  and/or setgid if the first part contains g
t sticky if the first part has o
u the u bits on the same file
g the g bits on the same file
o the o bits on the same file

If the first group is missing, then it is treated as a except that any bits in the user's umask will not be modified in the mode.

If the mode string is of the second form i.e. as returned by stat, then it represents the mode the file is to have.




chown(f, u, g) -- Change file owner

Set the owner of a file f to owner u and group g. The user and group arguments can be numeric ID's or names.




chroot(f) -- Change root

Change the root of the filesystem to f.




ctime(t) : s -- Format a time value into local time

Converts system time (an integer) into a string in the local timezone. (On Unix systems, system time is the number of seconds since the epoch, Jan 1, 1970 00:00:00. Its meaning on other systems varies.)




crypt(s1, s2) : s -- Encrypt a password

Encrypt the password s1 with the salt s2. (The first two characters of the returned string will be the salt.)




exec(f, arg0, arg1, arg2, ...) -- Replace the executing Icon program with another.

The current program is replaced by another program f. The remaining arguments are passed to the program. Consult the exec(2) manpages for more details. Warning: f must point to an executable program, not to a shell script or Icon program. If you want to run those, the first argument to exec should be /bin/sh, or iconx, etc. Warning: This is only available on Unix systems! For portable programs use system.




fetch(d, k) : s -- Fetch a value from a dbm database

Fetch the value corresponding to key k from the dbm database d.




fcntl(file, cmd, arg) -- Miscellaneous operations on opened files

Perform a number of miscellaneous operations on the open file. See the fcntl(2) manpage for more details. Directories and dbm files cannot be arguments to fcntl.

The following characters are the possible values for cmd:

f Get flags (F_SETFL)
F Set flags (F_GETFL)
x Get close-on-exec flags (F_GETFD)
X Set close-on-exec flag (F_SETFD)
l Get file lock (F_GETLK)
L Set file lock (F_SETLK)
W Set file lock and wait (F_SETLKW)
o Get file owner or process group (F_GETOWN)
O Set file owner or process group (F_SETOWN)

In the case of L, the arg value should be a string that describes the lock. A record will be returned by F_GETLK:

record posix_lock(value, pid)

The lock string consists of three parts separated by commas: the type of lock (r, w or u), the starting position and the length. The starting position can be an offset from the beginning of the file (e.g. 23), end of the file (e.g. -50) or from the current position in the file (e.g. +200). A length of 0 means lock till EOF.

The file flags set by F_SETFL and accessed by F_GETFL are represented by these characters:

d FNDELAY
s FASYNC
a FAPPEND




fdup(src, dest) -- Copy a file descriptor

This function provides a functionality similar to the dup2(2) system call. It only needs to be used when you are concerned about the actual Unix file descriptor used, such as just before calling exec. The dest file is closed; src is made to have its Unix file descriptor; and the second file is replaced by the first. Warning: This is only available on Unix systems! For portable programs use system.




filepair() : L -- Create a pair of connected files

This is analogous to socketpair(2) - it returns a list of two files, such that writes on one will be available on the other. The connection is bi-directional, unlike pipe. The two files are indistinguishable. Caution: Typically, the pair is created just before a fork or system; after it, one process should close L[1] and the other should close L[2] or you will not get proper end-of-file notification.




flock(file, op) -- Apply or remove a lock on a file

An advisory lock is applied to the file. Consult the flock(2) manpage for more details.

The following characters can be used to make up the operation string:

s shared lock
x exclusive lock
b don't block when locking
u unlock

Locks cannot be applied to directories or dbm files.




fork() : pid -- Create a copy of the process.

This function creates a new process that is identical to the current process in all respects except in the return value. The parent gets a return value that is the PID of the child, and the child gets 0. Warning: This is only available on Unix systems; for portability use system instead.




getegid() : gid -- get group identity

Get the effective gid of the current process.




geteuid() : uid -- get user identity

Get the effective uid of the current process.




getgid() : gid -- get group identity

Get the real gid of the current process.




getgr(g) : r -- Get a group file entry

Returns a record that contains group file information. If g is null, each successive call to getgr returns the next entry. setgrent resets the sequence to the beginning. g can be a numeric gid or a group name.

Return type: record posix_group(name, passwd, gid, members)




gethost(h) : r -- Get a host entry

Returns a record that contains host information. If h is null, each successive call to gethost returns the next entry. sethostent resets the sequence to the beginning. The aliases and addresses are comma separated lists of aliases and addresses (in a.b.c.d format) respectively.

Return type: record posix_hostent(name, aliases, addresses)




getpgrp() -- Get the process group

Returns the process group of the current process.




getpid() : pid -- get process identity

Get the pid of the current process.




getppid() : pid -- get parent's identity

Get the pid of the parent process.




getpw(u) : r -- Get a password file entry

Returns a record that contains password file information. If u is null, each successive call to getpw returns the next entry and setpwent resets the sequence to the beginning. u can be a numeric uid or a user name.

Return type: record posix_password(name, passwd, uid, gid, age, comment, gecos, dir, shell)




getserv(s1, s2) : r -- Get a serv entry

Returns a record that contains serv information for the service s1 using protocol s2. If s1 is null, each successive call to getservent returns the next entry. setservent resets the sequence to the beginning.

Return type: record posix_servent(name, aliases, port, proto)

If s2 is defaulted, it will return the first matching entry.




gettimeofday() : r -- Return the time of day.

Returns the current time in seconds and microseconds since the epoch, Jan 1, 1970 00:00:00. The sec value may be converted to a date string with ctime or gtime.

Return value: record posix_timeval(sec, usec)




getuid() : uid -- get user identity

Get the real uid of the current process.




gtime(t) : s -- Format a time value into UTC

Converts a system time (an integer) into a string in UTC. (On Unix systems, the time is the number of seconds since the epoch, Jan 1, 1970 00:00:00.)




ioctl() -- Unimplemented

Since ioctl(2) relies so heavily on opaque pointers to structs, it is hard to implement in Icon; accordingly its implementation has been deferred till we can figure out what the right use model should be.

Calls to ioctl are inherently not portable. Most ioctl functions can be done with system, running programs like stty(1) or mt(1).




link(src, dest) -- Make a link

Make a (hard) link dest that points to src.




mkdir(path, mode) -- Create a new directory

Create a new directory named path with a mode of mode. The mode can be numeric or a string of the form accepted by chmod().




pipe() : L -- Create a pipe.

Create a pipe and return a list of two file objects. The first is for reading, the second is for writing.




readlink(l) : s -- Read a link

Returns the value of a symbolic link. (Obsolete.)




receive(f) : r -- Receive a UDP datagram.

This function reads a datagram addressed to the port associated with f, waiting if necessary. The returned value is a record of type posix_message(addr, msg), containing the address of the sender and the contents of the message respectively.




rmdir(d) -- Remove a directory

Removes an empty directory.




select(file1, file2, ..., timeout) : L -- Select among files

Wait for a input to become available on a number of files. If timeout is specified, it is an upper bound on the time elapsed before select returns. The unit for the timeout is milliseconds (ms). A timeout of 0 may be specified in which case select will return immediately with a list of files on which input is currently pending.

Defaults: files Wait for timeout to expire and return.
  timeout Wait forever.

Directories and dbm files cannot be arguments to select.




send(s1, s2) -- Send a UDP datagram.

This function sends a datagram to the address s1 with the contents s2.




setenv(name, value) -- Set an environment variable.

Set an environment variable named name to the value value. If it did not exist, it is created; if it did, it is overwritten.




setgrent() -- Resets the group file

This function resets and rewinds the pointer to the group file used by getgr when called with no arguments.




sethostent(stayopen) -- Resets the host file

This function resets and rewinds the pointer to the host file used by gethost. The argument defines whether the file should be kept open between calls to gethost; the default is to keep it open.




setpgrp() -- Set the process group

Sets the process group. This is the equivalent of setpgrp(0, 0) on BSD systems.




setpwent() -- Resets the password file

This function resets and rewinds the pointer to the password file used by getpw when called with no arguments.




setgid(g) -- Set the gid

Set the gid of the current process to g. Consult the SysV setgid(2) manpage.




setuid(u) -- Set the uid

Set the uid of the current process to u.Consult the SysV setuid(2) manpage.




setservent(stayopen) -- Resets the network services file

This function resets and rewinds the pointer to the srvices file used by getserv. The argument defines whether the file should be kept open between calls to getserv; the default is to keep it open.




stat(f) : r -- Get information about a file

Returns a record with information about the file f which may be a path or a file object. The times are integers that may be formatted with the ctime and mtime functions. If the file is a link, its value is in the symlink member of the returned record.

Return value: record posix_stat(dev, ino, mode, nlink, uid, gid, rdev, size, atime, mtime, ctime, blksize, blocks, symlink)

The mode is a string similar to the output of ls(1). For example, "-rwxrwsr-x" represents a plain file with a mode of 2775 (octal).




symlink(src, dest) -- Make a symbolic link

Make a symbolic link dest that points to src.




sys_errstr(&errno) : s -- Format an error id

This function returns the string corresponding to the error code. If called with an invalid code (negative, or greater than the number of error messages) this function fails.




trap(s, proc) : proc -- Trap or ignore a signal

Set up a signal handler for the signal s (the name of the signal). The old handler (if any) is returned. If proc is &null, the signal is reset to its default value.

Caveat: This is not supported with the compiler!




truncate(f, len) -- Truncate a file

Truncate the file f (which may be a path to the file, or a file object that points to the file) to be of length len.




umask(u) : u -- Set the file creation mask

Set the umask of the process to u. The old value of the umask is returned.

The umask can either be specified in the Unix way with an integer whose bits represent the permissions that should not be granted; or as a permission string. umask(8r22) and umask("rwxr-xr-x") are equivalent. The return value is the old value of the umask, and it is returned in the same format as its argument, i.e. if called with an integer it will return an integer umask, and if called with a string it will return a mode string.

With a value of &null it will return the present value of the umask (as a string) without changing that value.




utime(f, atime, mtime) -- Set file modification/access times.

Set the access time for file f to atime and the modification time to mtime. The ctime is set to the current time.




wait(pid, options) : status -- Wait for process to terminate or stop

The options argument is a string constructed from the following characters:

   
"n" NOHANG - don't wait for a child to terminate (wait will fail if there are no more)
"u" UNTRACED - report status for untraced children also

The return value is a string that represents the pid and the exit status as defined in this table:

Unix equivalent example of returned string
WIFSTOPPED(status) "1234 stopped:SIGTSTP"
WIFSIGNALLED(status) "1234 terminated:SIGHUP"
WIFEXITED(status) "1234 exit:1"
WIFCORE(status) "1234 terminated:SIGSEGV:core"

Currently the rusage facility is unimplemented.

Defaults: pid Wait for all children

Appendix B: An example

Here is an example implementation of the BSD script(1) command:

# script: capture a script of a shell session (as in BSD)
# Usage: script [-a] [filename}
# filename defaults to "typescript"

$include "posix.icn"

procedure main(L)

   if L[1] == "-a" then {
      flags := "a"; pop(L)
   } else
      flags := "w"

   # Find a pty to use
   every c1 := !"pqrs" do
      every c2 := !(&digits || "abcdef") do
         if pty := open("/dev/pty" || c1 || c2, "rw") then {
            # Aha!
            capture(fname := L[1] | "typescript", pty, c1 || c2, flags)
            stop("Script is done, file ", image(fname))
         }
   
   stop("Couldn't find a pty!")
end

procedure capture(scriptfile, pty, name, flags)

   f := open(scriptfile, flags) | stop("Couldn't open ", image(scriptfile))
   tty := open("/dev/tty" || name, "rw") | stop("Couldn't open tty!")

   shell := getenv("SHELL") | "/bin/sh"
   system([shell, "-i"], tty, tty, tty, "nowait")

   # Parent
   close(tty)
   system("stty raw -echo")

   # Handle input
   while L := select(pty, &input) do {
      if L[1] === &input then
         writes(pty, reads()) | break
      else if L[1] === pty then {
         writes(f, inp := reads(pty)) | break
         writes(inp)
      }
   }

   (&errno = 0) | write(&errout, "Unexpected error: ", sys_errstr(&errno))
   system("stty cooked echo")
   close(f)
end

 

 


© 1997-2002 Shamim Mohamed

$Date: 2004/02/21 19:52:54 $
Last modified: Wed Aug 7 14:04:31 PDT 2002