OS X specific tools

The code described here is specific to VFX Forth for Mac OS X. 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 counted 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.

: $osx          \ 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.

OS X signal handling

Structures

Signal numbers See /usr/include/sys/signal.h.

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

struct /ts32    \ -- len
Regular thread state. See /usr/include/mach/i386/_structs.h _STRUCT_X86_THREAD_STATE32.

struct /fs32    \ -- len
Regular FPU environment. See /usr/include/mach/i386/_structs.h _STRUCT_X86_FLOAT_STATE32.

struct /excpt32 \ -- len
See /usr/include/mach/i386/_structs.h _STRUCT_X86_EXCEPTION_STATE32.

8 cells constant /dbg32 \ -- len ; dr0..dr7
See /usr/include/mach/i386/_structs.h _STRUCT_X86_DEBUG_STATE32.

4 constant /sigset_t    \ -- len
size of sigset_t.

struct /sigaltstack     \ -- len
See /usr/include/sys/_structs.h

struct /mcontext        \ -- len
Machine dependent context structure. See /usr/include/i386/_structs.h.

struct /ucontext        \ -- len
See /usr/include/sys/_structs.h

struct /siginfo \ -- len
Siginfo structure in /usr/include/sys/signal.h.

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        |              OSX extension user context for the signal
   | Context     |         handler. Includes pointer to register contents
+->|             |              which are modifiable by the signal handler
|  +-------------+
|  | Siginfo     |              Traditional signal information structure.
|  | structure   |<-+   Duplicates/simplifies some of User Context
|  +-------------+  |
+--| void *      |       |      Pointer to OSX user context
   +-------------+  |
   | siginfo_t * |--+   Pointer to OSX 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.

: .mcontext     \ *mc --
Display data from the mcontext 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 OS X, 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, SIGBUS and SIGSEGV. Performed at startup.

Error variables

VFX Forth for Mac OS X uses many functions from the libSystem shared library. The thread local error variables are exposed.

AliasedExtern: errno int * __error( void );
errno is the well known errno C thread local variable used by libraries and system calls. 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 OS X semaphore structures and calls.

struct /semosx  \ -- len
We use a structure consisting of a pointer to the sem_t structure followed by a 32 byte zname.

: InitCritSec   \ *smo --
Initialise a critical section base on a /semosx structure. This must be done before using the critical section.

: TermCritSec   \ *smo --
Delete the critical section associated with the smaphore. This releases internal OS X data and closes the semaphore. Nothing should be waiting on the seamphore before calling TermCritSec.

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

: CritSec]      \ *smo --
Unlock the section.

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

Millisecond timer

The OS X 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 OS X 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.

Raw timer

The following timer tools have better resolution but are very basic.

: ns@           \ -- nanoseconds
Return a system ticker in nanoseconds.

: ms@           \ -- milliseconds
Return a system ticker in milliseconds.

Time handling

A structure to mimic the timeval structure for libc

  4 field tv_sec
  4 field tv_nsec
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.

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]

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

: (changedir)   \ caddr len -- ior ; 0=success
Expand text macros and change the current working directory. Return 0 on success.

: changeDir     \ caddr len --
Expand text macros and change the current working directory. There is no return code.

: workingdir    \ -- caddr len
Return the text for the current working directory.

: 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 slen dest dlen failexist -- ior
Copy a file. Macros are expanded. If failexist is non-zero, existing files are not overwritten.