Linux specific tools

The code described here is specific to VFX Forth for Linux. Do not rely on any of the words documented here being present in any other VFX Forth implementation.

Shell operations

The VFX Forth console supports a number of command shell operations.

Primitives

The words in this section are used to build the tools.

: csplit        \ caddr len char -- raddr rlen laddr llen
Extract a substring at the start of caddr/len, returning the string raddr/rlen which includes char (if found) and the string laddr/llen which contains the text to left of char. If the string does not contain the character, raddr is caddr+len and rlen=0.

: xtype         \ caddr len --
As TYPE, but LF characters cause a CR. This factor copes with some user-written generic I/O devices that do not implement TYPE correctly.

: >pShell       \ z$ -- ior
Execute the given zero-terminated string as a shell command, write any output to the current output device, and return the result code from the popen() call. This word provides consistent action regardless of whether operation is running in a console or is detached. This is the default action of (>xShell) below.

defer >xShell   \ z$ -- ior
Execute the given zero-terminated string as a shell command, and return the result code from the relevant system call such that zero=success. Most words that cause shell actions use >xShell as a primitive. To use a raw system call instead as the action use:

  assign ssystem to-do >xShell

: (>Shell)      \ z$ -- ior
Execute the given zero-terminated string as a shell command, and return the result code from >xShell above.

: >system       \ z$ -- ior
Execute the given zero-terminated string as a shell command using the system() API call, and return the result code.

: >Shell        \ z$ --
Execute the given zero-terminated string as a shell command using (>Shell) above. Output from the command is written to the current output device. Text macros are expanded before the operation.

: ShellCmd      \ caddr len --
Execute the given caddr/len string as a shell command.

: ShellLine     \ caddr len --
Execute the given caddr/len string as a shell command. Before execution, the remainder of the input line is added to the given string.

: $shell        \ cmd$ tail$ --
Take the command and tail counted strings and execute them as a shell command using system(). Text macros are expanded.

: $linux        \ cmd$ tail$ --
A synonym for $shell.

Command operations

: sh            \ -- ; "command"
Ask the host operating system to execute the supplied command line.

: ls            \ -- ; "[spec]"
Display file information based on the supplied specification.

: dir           \ -- ; "[spec]"
Display file information based on the supplied specification. As LS but with colouring.

: makedir         \ -- ; "name"
Create a new subdirectory from the current working one. This word has been renamed to avoid a name conflict with the system mkdir() API.

: deldir         \ -- ; "name"
Remove a specified subdirectory. You can only remove an empty directory. This word has been renamed to avoid a name conflict with the system rmdir() API.

: rm            \ -- ; "spec"
Delete a single file or group of files as described by the given file specification. The wildcard '*' may also be used.

: cat           \ -- ; "spec"
Perform an ASCII display of a file or group of files. No filtering of the data is performed. This command should not be used to list binary files.

: pwd           \ --
Display the currently active working directory using the shell pwd command.

: cwd            \ -- ; ["name"]
Attempt to change current working directory either as an offset from the current directory or as a complete path. The wildcard '*' can be used to match the first directory. If there is no tail, CWD displays the current directory. No shell functions are used. Text macros are expanded.

: cd             \ -- ; ["name"]
A synonym for *fo{CWD}. Use *fo{CWD} as *fo{CD} will be removed in a future release to avoid conflicts with hex numbers.

Linux signal handling

Structures

Signal numbers See /usr/src/linux-2.6.8-24/include/asm-i386/sigcontext.h

#define SIGHUP           1
#define SIGINT           2
#define SIGQUIT          3
#define SIGILL           4
#define SIGTRAP          5
#define SIGABRT          6
#define SIGIOT           6
#define SIGBUS           7
#define SIGFPE           8
#define SIGKILL          9
#define SIGUSR1         10
#define SIGSEGV         11
#define SIGUSR2         12
#define SIGPIPE         13
#define SIGALRM         14
#define SIGTERM         15
#define SIGSTKFLT       16
#define SIGCHLD         17
#define SIGCONT         18
#define SIGSTOP         19
#define SIGTSTP         20
#define SIGTTIN         21
#define SIGTTOU         22
#define SIGURG          23
#define SIGXCPU         24
#define SIGXFSZ         25
#define SIGVTALRM       26
#define SIGPROF         27
#define SIGWINCH        28
#define SIGIO           29
#define SIGPOLL         SIGIO
#define SIGPWR          30
#define SIGSYS          31
#define SIGUNUSED       31

struct /fpstate \ -- len
Regular FPU environment. See /usr/src/linux-2.6.8-24/include/asm-i386/sigcontext.h.

struct /_libc_fpstate   \ -- len
The libc FPU environment. See /usr/src/linux-2.6.8-24/include/asm-i386/ucontext.h.

struct /sigcontext      \ -- len
CPU sigcontext structure. See /usr/src/linux-2.6.8-24/include/asm-i386/sigcontext.h. Note that this is not the same as a ucontext structure.

Stack description structure.

struct /gregset_t       \ -- len
CPU gregset_t structure. See /usr/src/linux-2.6.8-24/include/asm-i386/ucontext.h.

struct /mcontext        \ -- len
System uncontext structure returned by signal handlers. for Linux i32 this is the same as the /sigcontext structure.

struct /ucontext        \ -- len
System uncontext structure returned by signal handlers.

struct /sigaction       \ -- len
System sigaction structure.

Signal handling

The following is the stack structure seen by the siginfo signal handler.



   |             |
   +-------------+
   | Return code |              8 bytes of code to return to kernel context at
   |             |              end of the handler run via a special system call
   |             |              (now a dummy left as a signature for debuggers
   |             |              as many systems don't allow execution from the
   |             |              stack)
   +-------------+
   | Floating    |              FPU state if FPU has been used by this process
   | point state |
   +-------------+
   | User        |              POSIX extension user context for the signal
   | Context     |         handler.  Includes register contents etc, many
+->|             |              of which are modifiable by the signal handler
|  +-------------+
|  | Siginfo     |              Traditional signal information structure.
|  | structure   |<-+   Duplicates/simplifies some of User Context
|  +-------------+  |
+--| void *      |       |      Pointer to POSIX user context
   +-------------+  |
   | siginfo_t * |--+   Pointer to Siginfo structure
   +-------------+
   | Signum      |              Actual signal number generated
   +-------------+
   | Return      |              Originally this pointed at the stack based
   | address     |              return code above.  Now points directly at the
   |             |              sigreturn syscall gate in the vDSO
   +-------------+

create sigNames \ -- addr
Holds the signal numbers and names as counted strings.

: .sigName      \ n --
Given a signal number, display its name.

: .RSitem       \ x --
Display an item retrieved from the faulting return stack.

: .SigContext   \ sc --
Display data from the sigcontext structure.

: SigThrow      \ --
Runs the O/S THROW action.

3 0 callback: SigGenTrap        \ signum *siginfo *ucontext --
Generic trap handler that causes a -57005 THROW on return. Callbacks are documented in a separate section of the manual.

1 value -NestedSigs?    \ -- x
Set non-zero to cause an exit if a nested signal exception occurs in (SigGenTrap) below.

1 value SigPause?       \ -- x
Set non-zero to cause a pause when a signal is processed in (SigGenTrap) below.

: (SigGenTrap)  \ signum *siginfo *ucontext --
Action of SigGenTrap. Displays an error message. On return to Linux, a Forth THROW will occur.

: setSignal     \ callback signum --
Set a signal handler to execute the given callback. The callback must have the stack effect ( signum *siginfo *ucontext -- )

: setSigTraps   \ --
Install the SigGenTrap signal handler for signals SIGILL, SIGFPE and SIGSEGV. Performed at startup.

Error variables

VFX Forth for Linux uses many functions from the libc shared library. The thread local error variables are exposed.

AliasedExtern: errno int * __errno_location( void );
errno is the well known errno C thread local variable used by libraries and system calls. Can be read by @ and written by !

AliasedExtern: h_errno int * __h_errno_location( void );
h_errno is the h_errno C thread local variable. Can be read by @ and written by !

Environment variables

: ReadEnv       \ naddr1 nlen -- vaddr vlen
Read the environment variable whose name is given by naddr/nlen and return the string. If there is no such variable vaddr is zNull and vlen is zero.

: WriteEnv      \ vaddr vlen naddr nlen --
Write the string value vaddr/vlen to the environment variable named by vaddr/vlen.

: DelEnv        \ naddr nlen --
Delete the environment variable naddr/nlen.

: EnvMacro:     \ naddr nlen "<var>" -- ; -- caddr
Create a text macro called <var> that queries the environment variable named by naddr/nlen the returned string is a counted string. Use in the form:

  s" HOME" EnvMacro: $home

By convention, environment macro names start with a '$'.

s" HOME" EnvMacro: $home
Text macro for the home directory.

Critical sections

Critical sections are implemented using the standard Linux semaphore structures and calls.

16 constant /sem_t      \ -- len
Size of a Linux i32/ARM sem_t structure. You can treat this as an opaque type that you do not have to deal with directly. All you have to do is to reserve memory for it, e.g.

  /sem_t buffer: MyCritSec

: InitCritSec   \ sem --
Initialise the critical section. This must be done before using it.

: TermCritSec   \ sem --
Delete the critical section associated with the smaphore. This releases internal Linux data, the /sem_t structure is still available but needs to be initialised again before reuse. Nothing should be waiting on the seamphore before calling TermCritSec.

: [CritSec      \ sem --
Wait until the section is available and lock it. Does not call PAUSE.

: CritSec]      \ sem --
Unlock the section.

: CritSec?      \ sem -- u
Returns the section's counter, where non-zero indicates that it is available, or zero when it is locked. Returns zero on error.

The critical section words use Linux semaphores, which are counted semaphores. Thus when using critical sections you must be careful to match the use of [CritSec and

Millisecond timer

The Linux ticker frequency varies between implementations. The code in this section provides simple tools to return and handle a millisecond ticker.

: (ticks)       \ -- ms ; return ticks in ms
Return the system ticker in milliseconds. Treat this as a 32 bit unsigned value that wraps around on overflow.

: SetTicks      \ --
Calibrate the Linux ticker and install it as the action of TICKS. Performed at start up.

5 value tickStepMs      \ -- ms
Minimum interval and granularity used by tick-ms below.

: tick-ms       \ ms --
Waits for at least ms milliseconds. Uses PAUSE every tickStepMs. This is the default action of MS, which is DEFERred.

Time handling

A structure to mimic the timeval structure for libc

  4 field tv_sec        \ seconds
  4 field tv_usec       \ microseconds
end-struct

A structure to mimic the tm structure for libc

: td>epoch      \ seconds mins hours day month year -- epoch
Returns the seconds since the start of the epoch. The input time is treated as GMT/UTC.

: epoch>td      \ epoch -- seconds mins hours day month year
Converts an epochal second into a GMT/UTC time and date.

Microsecond wait

Useful for low level tuning code, e.g. USB devices with a 1 ms frame timing.

: microsleep    \ us -- ; max 1 million - 1
Sleeps for up the given number of microseconds, max 999999

Time and date

These functions rely on the ANS Forth word TIME&DATE ( -- s m h dd mm yyyy ) and the non-standard DOW ( -- dow, 0=Sun) to get the day of the week.

create days$    \ -- addr
String containing 3 character text for the days of the week.

create months   \ -- addr
String containing 3 character text for the months.

: .dow          \ dow --
Display day of week.

: .2r           \ n --
Display n as a two digit number with leading zeros.

: .4r           \ n --
Display n as a four digit number with leading zeros.

: .Time&Date    \ s m h dd mm yy --
Display the system time The format is:

  hh:mm:ss dd Mmm yyyy

: .AnsiDate     \ zone --
Display the day of week, date and time. If zone is 0 GMT (system time) is displayed, otherwise local time is displayed. The format is:

  dow, hh:mm:ss dd Mmm yyyy [GMT]

Hardware I/O port access

The following notes are for developers working under x86-32 versions of Linux. Under normal use, direct access to I/O ports is forbidden. However, if you are running with root privilege, you can use the glibc functions ioperm() and iopl() to enable and disable port access.

code pc@        \ port -- b ; read port
Read a byte from the hardware control port supplied.

code pc!        \ b port -- ; write port
Write the supplied byte to the selected hardware control port.

code pw@        \ port -- w ; read port
Read a 16 bit word from the hardware control port supplied.

code pw!        \ w port -- ; write port
Write the supplied 16 bit word to the selected hardware control port.

code pl@        \ port -- x ; read port
Read 32 bits from the hardware control port supplied.

code pl!        \ x port -- ; write port
Write the supplied 32 bits to the selected hardware control port.

: +Ports        \ port #ports -- ior
Enable access to a range of ports starting at port. Return 0 on success. Port numbers must be in the range 0..$3FF. You must have root permissions.

: -Ports        \ port #ports -- ior
Disable access to a range of ports starting at port. Return 0 on success. Port numbers must be in the range 0..$3FF. You must have root permissions.

: PlayNote      \ hertz ms --
Play a note on the internal PC speaker. Ports $42, $43 and $61 must be enabled first.

: pio-test      \ --
A test routine for hardware access. Enables ports $40..$6F and confirms access. If you hear a familiar tune, all is well!

Program launch status

Programs can be launched in several ways.

This code allows you to determine how the program was launched.

0 value AppPPID         \ -- x
This application's parent's process ID.

0 value AppPGRP         \ -- x
This application's process group.

0 value ctPGRP          \ -- x
The controlling terminal's process group.

0 value AppLaunch       \ -- x
How the application was launched:

: TestLaunch    \ --
Set the data above to determine how the application was launched. Run at program launch.

Folders and Files

: dirExists?    \ caddr len -- flag
Return true if a directory exists. Macros are expanded.

: create-dir    \ caddr len -- ior
Create a directory, returning zero on success. Macros are expanded. Default permissions are used.

: forceDir      \ caddr len -- ior
Create the directory if it does not exist. Macros are expanded.

: +dirSep       \ c$ --
Add a directory separator to the end of a counted string.

: prepFileName  \ caddr len --
Convert '\' and '/' characters in-place as required by the operating system.

: prepDirName   \ caddr --
Force the counted string at caddr to end with a '\' character.

: makeDirLevels \ caddr1 len1 caddr2 len -- ior
The string caddr1/len1 represents a directory that must already exist. Caddr2/len2 represents additional directory levels that may or may not already exist. The additional levels are created if they do not exist. Successful operation returns 0. For example, to create the directory /Users/stephen/jim/foo/bar, you could use

  s" /Users/stephen"  s" jim/foo/bar"  makedirlevels

: copy-file     \ src srclen dest destlen nooverwrite -- ior
Copy the file. If nooverwrite is non-zero and the destination exists, an error is returned.