|
|
|
FINC is a set of C networking extensions to John Sadler's Ficl (Forth Inspired Command Language) interpreter. These are the first extensions I wrote and I'm working on becoming a more experienced Forth programmer. (I've read about Forth over the years and I once bought a Forth cartridge for my Commodore 64 [HES 64 Forth?] - but I immediately returned it because the instruction booklet was missing!)
Ficl was based on:
I assume "DP" stands for "draft proposal", as the linked-to document was the last draft of the standard submitted to ANSI and not the official, published standard (which costs money to obtain). The newest standard is 2012 (and counting) Forth; in addition to the evolving standard, there are lots of interesting proposals and discussions at the website:
Caveats about Ficl:
- I had problems with optimization in — I'm guessing — different versions of gcc. ficl and my programs worked fine under Fedora Core 2 (!), OpenSolaris 10, and on the Nintendo DS. The programs acted strangely under Fedora 11 and FreeBSD 7.2. Problems included multi-line "... [IF] ... [ELSE] ... [THEN]"s not working, but working okay if mashed into a single line; and the stack maintaining coherence during the evaluation of a file, but containing unrelated contents afterwards (e.g., entering DROP at the command line would add two items of garbage to the stack). I finally got the programs to work correctly by building Ficl without the "-O" optimization flag. (The "-fPIC" flag for the shared library didn't seem to make any difference when used for the static library too.)
- When I had a Windows 98 SE machine, ficl would build in Visual C++ 6.0 without errors, but it would crash when loading the softcore definitions. When built on a Windows 2000 (or later) machine, ficl seemed to work just fine. I was never able to track this problem down.
- Ficl's PICK word is implemented incorrectly, counting items on the stack beginning at 1 rather than 0. FILE-POSITION and FILE-SIZE return single unsigned integers rather than the required double unsigned integers. The String word set (except for COMPARE) is missing. See the beginning of "
utility.fr
" for work-arounds. (I haven't modified the Ficl distribution because I want people to be able to download the official version from the Ficl website and use Ficl as is.)
Still, I like Ficl because it's another example of Tiny Code. The FTH Forth Scripting Language embeds its own, heavily-modified copy of Ficl that fixes some of the problems above.
This library has the actual FINC extensions and can be linked with any Ficl-based program (along with the CSOFT library mentioned below). Sets of words can be left in or left out by calling or not calling the functions that register the words:
#include "finc.h" /* Forth-Inspired Network Commands. */ ... /* Initialize FICL. */ ficlSystem *sys = ficlSystemCreate (NULL) ; /* Compile extra words. */ ficlSystemCompileExtras (sys) ; /* Compile FINC extensions. */ buildWordsDRS (sys) ; buildWordsLFN (sys) ; buildWordsMISC (sys) ; buildWordsNET (sys) ; buildWordsIOX (sys) ; buildWordsSKT (sys) ; buildWordsTCP (sys) ; ...
finc (description and command-line invocation) is the basic stand-alone ficl intepreter with network extensions.
nicl (description and command-line invocation) is a network server that provides each client with its own Ficl virtual machine with network extensions.
ECHOD (source) is an example of an event-loop-based network server that simultaneously handles multiple clients. Since this was my first "big" Forth program (i.e., a small program), the in-line comments are mostly stack descriptions to help me keep track of what was on the stack and where. The IOX-ONIO command, in particular, having 5 arguments, required some major "stackrobatics"! Refactoring, if not revisiting, is definitely in order. Still, the program shows the basic structure for writing an event-loop-based network server.
HTTPD (source) and UTILITY (source) implement a web server that simultaneously serves pages to multiple clients. The server is currently in use on my home LAN serving pages from a local copy of my web site. As I expected, the web server also runs on a Nintendo DS, serving out web pages over wireless connections! (Instructions for installing and running the web server on a DS.)
Ficl is downloaded from its
SourceForge website.
There are two Makefiles, "Makefile.ansi
" and
"Makefile.linux
". I've had no trouble building Ficl with the
Linux Makefile on Unix-based systems. I manually copy the executable, the
header files, and the libraries to the /usr/local
directory tree.
(Don't forget to copy the ficlplatform/*.h
header files to
/usr/local/include/ficl/ficlplatform/
!) See the
Nintendo DS installation instructions
for information on building Ficl for the DS.
The Ficl distribution also includes the Microsoft Visual C++ 6.0 workspace and
project files. If you're using Visual Studio, it will automatically convert
the files to Visual Studio's format. Ficl offers a number of options for
building Ficl in Windows; I use the single-threaded debug mode. As a result,
the Ficl library that FINC links to is
"\ficl\lib\debug\singlethreaded\ficl.lib
". (The header files are
in the top-level "\ficl\
" directory.)
The source code for FINC (the library and the applications) is included in my CSOFT distribution. Building the software is a simple matter:
libgpl
directory and run the appropriate Makefile. If you don't see an
appropriate Makefile, Makefile.linux
will usually work
for GCC-based builds. (Ignore the warning messages about "long long"s
coming out of the CORBA code and about "signedness" that might come
out of the networking code.)
libxdr
directory and run the appropriate Makefile
to build libxdr.
csoft
workspace or solution. Doing so will automatically
build both libgpl and libxdr.
finc
directory and build everything
(libfinc.a, finc, and
nicl) using the appropriate Makefile.
finc
workspace or solution.
In all cases, you might need to modify FINC's Makefiles or Visual C++/Studio
project files to point to where the Ficl header files and libraries are found.
The UNIX Makefiles assume the header files are in
"/usr/local/include/ficl/
" and the library is in
"/usr/local/lib/
". The Windows project files assume the Ficl
header files are found in a Ficl source directory at the same level as the
csoft
directory; the Ficl library is
"\ficl\lib\debug\singlethreaded\ficl.lib
".
The CSOFT library and the FINC library and application are covered by the MIT License. (Also see the Ficl License.) Basically, you are free to use the software however you see fit, in commercial or non-commerical applications. I only ask that, if your time permits, you report any bugs or portability problems you encounter. Suggestions for improvements and enhancements are welcome, but I do not guarantee I will act upon them.
The NET word set defines words for translating IP addresses, host names, and service ports:
- "host" NET-ADDR
- address dotted? NET-HOST
- "service" udp? NET-PORT
Source: words_net.c
NET-ADDR NET ( c-addr u1 -- u2 )
Lookup the host name represented by c-addr,u1 and return its IP address in network-byte-order in u2.
NET-HOST NET ( u1 f -- c-addr u2 )
Lookup the IP address u1 and return the corresponding host name string as c-addr,u2. If the flag f is true, return the address in dotted IP format. The string is NUL-terminated and stored internally; it should be used or duplicated before calling NET-HOST again.
NET-PORT NET ( c-addr u f -- i )
Lookup the service name c-addr,u in the network services database (the "/etc/services" file) and return the corresponding port number. If flag f is true, the "udp" port is returned; otherwise, the "tcp" port is returned.
The SKT word set defines words for monitoring sockets:
- SKT-CLEANUP
- fd SKT-PEER
- fd SKT-PORT
- fd SKT-READABLE?
- fd recvSize sendSize SKT-SETBUF
- SKT-STARTUP
- fd SKT-UP?
- fd SKT-WRITEABLE?
Source: words_skt.c
SKT-CLEANUP SKT ( -- ior )
Shut down the socket library; ior returns an error indication.
SKT-PEER SKT ( fd -- u )
Determine the host at the other end of network socket connection fd and returns its IP address in u in network-byte-order.
SKT-PORT SKT ( fd -- n )
Get the number of the port to which socket fd is bound; -1 is returned in the event of an error.
SKT-READABLE? SKT ( fd -- f )
Check if data is waiting to be read from socket fd; return true if the socket is readable and false otherwise.
SKT-SETBUF SKT ( fd n1 n2 -- ior )
Set the size of socket fd's receive buffer to n1 bytes and the size of its send buffer to n2 bytes; ior returns an error indication. If a buffer size is less than zero, the respective buffer retains its current size.
SKT-STARTUP SKT ( -- ior )
Start up the socket library; ior returns an error indication.
SKT-UP? SKT ( fd -- f )
Check socket fd to see if its network connection is still up; return true if the connection is up and false otherwise.
SKT-WRITEABLE? SKT ( fd -- f )
Check if data can be written to socket fd; return true if the socket is writeable and false otherwise.
The TCP word set defines words for establishing and communicating over TCP/IP network connections:
- endpoint timeout TCP-ANSWER
- "service[@host]" noWait? TCP-CALL
- endpoint timeout destroy? TCP-COMPLETE
- value TCP-DEBUG
- endpoint TCP-DESTROY
- endpoint TCP-FD
- port backlog TCP-LISTEN
- endpoint TCP-NAME
- endpoint TCP-PENDING?
- buffer length endpoint timeout TCP-READ
- endpoint TCP-READABLE?
- endpoint TCP-UP?
- buffer length endpoint timeout TCP-WRITE
- endpoint TCP-WRITEABLE?
Source: words_tcp.c
TCP-ANSWER TCP ( ep1 r -- ep2 0 | ior )
Wait at most r seconds for a connection request to be received on listening endpoint ep1. If a request is received, accept the request. The operating system automatically creates a new endpoint ep2 (the "data" endpoint) through which the server can talk to the client. The I/O result indicates the status of answering the connection request.
TCP-CALL TCP ( c-addr u f -- ep 0 | ior )
Request a network connection to the server at c-addr/u ("service[@host]"). If the no-wait flag f is false, TCP-CALL waits until the connection is established (or refused) before returning. If the no-wait flag f is false, TCP-CALL waits until the connection is established (or refused) before returning. If the no-wait flag f is true, TCP-CALL initiates the connection attempt and returns immediately; the application should subsequently invoke TCP-COMPLETE to complete the connection. In all cases, the data endpoint is returned on the stack, along with the I/O result of the connection attempt.
TCP-COMPLETE TCP ( ep r f -- ior )
Wait for an asynchronous, network connection attempt to complete. Invoking TCP-CALL in no-wait mode initiates an attempt to connect to a network server. At some later time, the application must call TCP-COMPLETE to complete the connection attempt (if it is fated to complete).
Timeout r specifies the maximum amount of time (in seconds) that the caller wishes to wait for the call to complete. A negative timeout (e.g., -1E0) causes an infinite wait; a zero timeout (0E0) causes an immediate return if the connection is not yet established.
If the connection attempt times out or otherwise fails and flag f is true, TCP-COMPLETE will automatically destroy the endpoint. This mode is useful when the application plans to make a single go/no-go call to TCP-COMPLETE.
If, under the same circumstances, flag f is false, TCP-COMPLETE will NOT destroy the endpoint; the application is responsible for executing TCP-DESTROY explicitly. This mode is useful when the application plans to periodically call TCP-COMPLETE (perhaps with a timeout of 0E0) until the connection is successfully established.
In all cases, the I/O result is returned on the stack: 0 if the connection was established, EWOULDBLOCK if the timeout period expired, and ERRNO otherwise.
TCP-DEBUG TCP ( n -- )
Set the TCP/IP networking debug flag to n. A value of 0 disables debug; a non-zero value enables debug. Debug is written to standard output.
TCP-DESTROY TCP ( ep -- ior )
Close a listening or data endpoint; the endpoint should no longer be referenced.
TCP-FD TCP ( ep -- fd )
Get a listening or data endpoint's socket.
TCP-LISTEN TCP ( n1 n2 -- ep 0 | ior )
Create a "listening" endpoint bound to port n1 at which the application will listen for connection requests from clients; at most n2 requests may be pending. The listening endpoint ep and the I/O result of creating the endpoint are returned on the stack. The application uses the TCP-ANSWER word to accept incoming connection requests.
TCP-NAME TCP ( ep -- c-addr u )
Get a listening or data endpoint's name and return it as c-addr/u. The string is NUL-terminated and stored internally; it should be used or duplicated before calling TCP-NAME again. An address of NULL and a length of zero are returned in the event of an error.
TCP-PENDING? TCP ( ep -- f )
Check if any connection requests from potential clients are waiting to be answered on listening endpoint ep; return true if requests are pending and false otherwise. This word should only be applied to listening endpoints created by TCP-LISTEN.
TCP-READ TCP ( c-addr n ep r -- u ior )
Read n bytes of data into buffer c-addr from network connection ep. The actual number of bytes read, u, and the I/O result are returned on the stack.
Because of the way network I/O works, a single record written to a connection by one task may be read in multiple "chunks" by the task at the other end of the connection. TCP-READ takes this into account and, if you ask it for 100 bytes, it will automatically perform however many network reads are necessary to collect the 100 bytes.
If n is negative, TCP-READ returns after reading the first "chunk" of input received; the number of bytes read from that first "chunk" is limited to the absolute value of n. The actual number of bytes read is returned as u on the stack.
Timeout r specifies the maximum amount of time (in seconds) that the application wishes to wait for the first data to arrive. A negative timeout (e.g., -1E0) causes an infinite wait; a zero timeout (0E0) allows a read only if input is immediately available. If the timeout expires before the requested amount of data has been read, the actual number of bytes read is returned on the stack as u, along with an I/O result of EWOULDBLOCK.
TCP-READABLE? TCP ( ep -- f )
Check if data is waiting to be read from network connection ep; return true if the connection is readable and false otherwise. This word is equivalent to "endpoint TCP-FD SKT-READABLE?".
TCP-UP? TCP ( ep -- f )
Check if network connection ep is still up; return true if the connection is up and false otherwise. This word is equivalent to "endpoint TCP-FD SKT-UP?".
TCP-WRITE TCP ( c-addr u1 ep r -- u2 ior )
Write u1 bytes of data from buffer c-addr to network connection ep. The actual number of bytes written, u2, and the I/O result are returned on the stack.
Because of the way network I/O works, attempting to output a given amount of data to a network connection may require multiple network writes. TCP-WRITE takes this into account and, if you ask it to output 100 bytes, it will perform however many network writes are necessary to output the full 100 bytes of data to the connection.
Timeout r specifies the maximum amount of time (in seconds) that the application wishes to wait for the data to be output. A negative timeout (e.g., -1E0) causes an infinite wait; TCP-WRITE will wait as long as necessary to output all of the data. A zero timeout (0E0) specifies no wait: if the connection is not ready for writing, TCP-WRITE returns immediately; if the connection is ready for writing, TCP-WRITE returns after outputting whatever it can.
If the timeout expires before the requested amount of data has been written, the actual number of bytes written is returned on the stack as u2, along with an I/O result of EWOULDBLOCK.
TCP-WRITEABLE? TCP ( fd -- f )
Check if data can be written to network connection ep; return true if the connection is writeable and false otherwise. This word is equivalent to "endpoint TCP-FD SKT-WRITEABLE?".
The LFN word set defines words for sending and receiving LF-terminated text over a network connection:
- endpoint "options" LFN-CREATE
- value LFN-DEBUG
- stream LFN-DESTROY
- stream LFN-FD
- stream timeout LFN-GETLINE
- stream LFN-NAME
- string stream crlf timeout LFN-PUTLINE
- buffer length stream timeout LFN-READ
- stream LFN-READABLE?
- stream LFN-UP?
- buffer length stream timeout LFN-WRITE
- stream LFN-WRITEABLE?
Source: words_lfn.c
LFN-CREATE LFN ( ep c-addr u -- st 0 | ior )
Create a LF-terminated network stream on top of previously-created network endpoint ep (i.e., using TCP-ANSWER or TCP-CALL). The stream takes ownership of the endpoint; the endpoint will automatically be destroyed when the stream is destroyed. The stream is returned on the stack as st, along with the I/O result ior.
The c-addr/u string contains zero or more of the following UNIX command line-style options:
- "-input size"
- specifies the size of the stream's internal input buffer; the default is 2048 bytes. NOTE that this is only a limit on the input buffer, not on incoming strings.
- "-output length"
- specifies the maximum output message size for the stream; the default is 2047 bytes.
LFN-DEBUG LFN ( n -- )
Set the LF-terminated networking debug flag to n. A value of 0 disables debug; a non-zero value enables debug. Debug is written to standard output.
LFN-DESTROY LFN ( st -- ior )
Close LF-terminated network stream st and its underlying TCP/IP endpoint; the stream should no longer be referenced.
LFN-FD LFN ( st -- fd )
Get LF-terminated stream st's socket.
LFN-GETLINE LFN ( st r -- c-addr u ior )
Read the next CR/LF-delimited line of input from LF-terminated network stream st and return it as c-addr/u. The string is NUL-terminated and stored internally; it should be used or duplicated before calling LFN-GETLINE on this stream again. An address of NULL and a length of zero are returned in the event of an error.
Timeout r specifies the maximum amount of time (in seconds) that the application wishes to wait for the next line to be read. A negative timeout (e.g., -1E0) causes an infinite wait; a zero timeout (0E0) allows a read only if input is immediately available. If the timeout expires before a line of input has been read, the I/O result is EWOULDBLOCK.
LFN-NAME LFN ( st -- c-addr u )
Get LF-terminated network stream st's name and return it as c-addr/u. The string is NUL-terminated and stored internally; it should be used or duplicated before calling LFN-NAME again. An address of NULL and a length of zero are returned in the event of an error.
LFN-PUTLINE LFN ( c-addr u st n r -- ior )
Write the string c-addr/u to LF-terminated network stream st. The I/O result is returned on the stack.
Bit mask n specifies line terminators to be appended to the output string: 0 = no terminator, 1 = LF only, 2 = CR only, and 3 = CR/LF. Zero is typically used if the application explicitly puts the line terminators in the output string.
Timeout r specifies the maximum amount of time (in seconds) that the application wishes to wait for the line to be output. A negative timeout (e.g., -1E0) causes an infinite wait. A zero timeout (0E0) specifies no wait: if the connection is not ready for writing, LFN-PUTLINE returns immediately; if the connection is ready for writing, LFN-PUTLINE returns after outputting whatever it can. If the timeout expires before the output line has been written, an I/O result of EWOULDBLOCK is returned on the stack.
LFN-READ LFN ( c-addr n st r -- u ior )
Read n bytes of unformatted data into buffer c-addr from LF-terminated network stream st. The actual number of bytes read, u, and the I/O result are returned on the stack.
Because of the way network I/O works, a single record written to a connection by one task may be read in multiple "chunks" by the task at the other end of the connection. LFN-READ takes this into account and, if you ask it for 100 bytes, it will automatically perform however many network reads are necessary to collect the 100 bytes.
If n is negative, LFN-READ returns after reading the first "chunk" of input received; the number of bytes read from that first "chunk" is limited to the absolute value of n. The actual number of bytes read is returned as u on the stack.
Timeout r specifies the maximum amount of time (in seconds) that the application wishes to wait for the first data to arrive. A negative timeout (e.g., -1E0) causes an infinite wait; a zero timeout (0E0) allows a read only if input is immediately available. If the timeout expires before the requested amount of data has been read, the actual number of bytes read is returned on the stack as u, along with an I/O result of EWOULDBLOCK.
LFN-READABLE? LFN ( st -- f )
Check if data is waiting to be read from LF-terminated network stream st; return true if the stream is readable and false otherwise. Because input is buffered, LFN-READABLE? is not equivalent to "stream LFN-FD SKT-READABLE?". LFN-ISREADBLE first checks for currently buffered input; if there is none, LFN-READABLE? then checks the socket.
LFN-UP? LFN ( st -- f )
Check LF-terminated network stream st to see if its network connection is still up; return true if the connection is up and false otherwise.
LFN-WRITE LFN ( c-addr u1 st r -- u2 ior )
Write u1 bytes of unformatted data from buffer c-addr to LF-terminated network stream st. The actual number of bytes written, u2, and the I/O result are returned on the stack.
Because of the way network I/O works, attempting to output a given amount of data to a network connection may require multiple network writes. LFN-WRITE takes this into account and, if you ask it to output 100 bytes, it will perform however many network writes are necessary to output the full 100 bytes of data to the connection.
Timeout r specifies the maximum amount of time (in seconds) that the application wishes to wait for the data to be output. A negative timeout (e.g., -1E0) causes an infinite wait; LFN-WRITE will wait as long as necessary to output all of the data. A zero timeout (0E0) specifies no wait: if the connection is not ready for writing, LFN-WRITE returns immediately; if the connection is ready for writing, LFN-WRITE returns after outputting whatever it can.
If the timeout expires before the requested amount of data has been written, the actual number of bytes written is returned on the stack as u2, along with an I/O result of EWOULDBLOCK.
LFN-WRITEABLE? LFN ( st -- f )
Check if data can be written to LF-terminated network stream st; return true if the stream is writeable and false otherwise.
The IOX word set defines words for monitoring and responding to network I/O events:
- seconds user word dispatcher IOX-AFTER
- callback IOX-CANCEL
- IOX-CREATE
- value IOX-DEBUG
- dispatcher IOX-DESTROY
- callback IOX-DISPATCHER
- seconds user word dispatcher IOX-EVERY
- dispatcher timeout IOX-MONITOR
- socket mode user word dispatcher IOX-ONIO
- user word dispatcher IOX-WHENIDLE
IOX-AFTER, IOX-EVERY, IOX-ONIO, and IOX-WHENIDLE register callbacks to be invoked when monitored events occur. When a monitored event occurs, the specified word is executed with three arguments:
( cb c-addr n -- )
where cb is the handle returned when the callback was registered, c-addr is the user data supplied by the application when the callback was registered, and n is a bit mask specifying the reason(s) for the callback (0x1=readable, 0x2=writeable, 0x4=OOB, 0x8=timer, 0x10=idle). Constants are predefined for the reasons:
- IOX_READ - socket is readable.
- IOX_WRITE - socket is writeable.
- IOX_EXCEPT - socket has out-of-bound input available for reading.
- IOX_IO - the bit-wise OR of the three conditions above.
- IOX_FIRE - timer has fired.
- IOX_IDLE - dispatcher is idle.
Source: words_iox.c
IOX-AFTER IOX ( r c-addr xt dp -- cb )
Register a single-shot timer of duration r seconds with I/O event dispatcher dp. A handle, cb, is returned on the stack and can be used to cancel the callback with IOX-CANCEL.
When the timer expires, (i) callback handle cb, user data c-addr, and reason n (IOX_FIRE) are pushed on the stack and (ii) execution token xt is executed. After xt completes, the callback is automatically cancelled.
IOX-CANCEL IOX ( cb -- ior )
Cancel callback cb; the callback should no longer be referenced. The I/O result of cancelling the callback is returned on the stack.
IOX-CREATE IOX ( -- dp 0 | ior )
Create an I/O event dispatcher. The dispatcher is returned on the stack as dp, along with the I/O result ior.
IOX-DEBUG IOX ( n -- )
Set the I/O event dispatching debug flag to n. A value of 0 disables debug; a non-zero value enables debug. Debug is written to standard output.
IOX-DESTROY IOX ( dp -- ior )
Destroy I/O event dispatcher dp; the dispatcher should no longer be referenced.
IOX-DISPATCHER IOX ( cb -- dp )
Get callback cb's dispatcher dp. This capability is useful when a callback needs to access its dispatcher. For example, a callback that answers a network connection request may wish to register an I/O callback for the new data connection.
IOX-EVERY IOX ( r c-addr xt dp -- cb )
Register a periodic timer of interval r seconds with I/O event dispatcher dp. A handle, cb, is returned on the stack and can be used to cancel the callback with IOX-CANCEL.
Each time the timer fires, (i) callback handle cb, user data c-addr, and reason n (IOX_FIRE) are pushed on the stack and (ii) execution token xt is executed. Unless explicity cancelled, the timer will continue firing every r seconds.
IOX-MONITOR IOX ( dp r -- ior )
Monitor and dispatch I/O events, timers, and idle tasks for r seconds using dispatcher dp. If r is less than zero, the dispatcher will monitor events forever. The I/O result of monitoring events is returned on the stack.
IOX-ONIO IOX ( fd n c-addr xt dp -- cb )
Register I/O file descriptor fd with I/O event dispatcher dp. Mask n is the bit-wise OR of the types of I/O events to monitor: IOX_READ for input-pending, IOX_WRITE for output-ready, and IOX_EXCEPT for OOB-input-pending. A handle, cb, is returned on the stack and can be used to cancel the callback with IOX-CANCEL.
When an event of the monitored types is detected on the I/O source, (i) callback handle cb, user data c-addr, and reason n (IOX_READ, IOX_WRITE, or IOX_EXCEPT) are pushed on the stack and (ii) execution token xt is executed.
To register a callback for a TCP/IP listening socket (created by TCP-LISTEN), specify IOX_READ as the event type to be monitored. When a connection request is received from a client, the listening socket becomes readable. The callback should then execute TCP-ANSWER to accept the connection request.
IOX-WHENIDLE IOX ( c-addr xt dp -- cb )
Register an idle task with I/O event dispatcher dp. A handle, cb, is returned on the stack and can be used to cancel the callback with IOX-CANCEL.
When the dispatcher is idle, (i) callback handle cb, user data c-addr, and reason n (IOX_IDLE) are pushed on the stack and (ii) execution token xt is executed.
The DRS word set defines words for scanning files in a directory.
- "pathname" DRS-CREATE
- scan DRS-DESTROY
- "pathname" DRS-DIRECTORY?
- scan DRS-FIRST
- scan DRS-NEXT
- scan DRS-COUNT
- scan index DRS-GET
The DRS-FIRST and DRS-NEXT words are useful for sequencing through files in a BEGIN loop. DRS-COUNT and DRS-GET are suited for DO loops.
Source: words_drs.c
DRS-CREATE DRS ( c-addr u -- scan 0 | ior )
Create a directory scan for the directory specified in the c-addr/u pathname string. The pathname may contain wildcard characters for the files in the directory. If the scan is successfully created, the scan is returned on the stack with a status of zero. If there was an error, only a non-zero I/O result is returned on the stack.
DRS-DESTROY DRS ( scan -- ior )
Destroy a directory scan and return the I/O result ior on the stack.
DRS-DIRECTORY? DRS ( c-addr u -- flag )
Determine if the c-addr/u pathname string refers to a directory. Return (i) true if the pathname does refer to a directory and (ii) false if it doesn't or if there was an error.
Ficl does have the DPANS94 FILE-STATUS word (from the optional File-Access
word set), but FILE-STATUS returns an implementation-defined status value.
In particular, Ficl returns different values under Windows than it does on
other platforms (e.g., UNIX). DRS-DIRECTORY? always uses the UNIX
stat(2)
call, which is available on Windows.
DRS-FIRST DRS ( scan -- c-addr u | 0 )
Get the first matching file in a directory scan. The file's full pathname is returned on the stack as c-addr/u. Zero (0) is returned if there are no matching files in the scan.
DRS-NEXT DRS ( scan -- c-addr u | 0 )
Get the next matching file in a directory scan. The file's full pathname is returned on the stack as c-addr/u. Zero (0) is returned if there are no more matching files in the scan.
DRS-COUNT DRS ( scan -- u )
Get the number of files, u, in a directory scan that matched the wildcard file specification supplied to DRS-CREATE.
DRS-GET DRS ( scan n -- c-addr u | 0 )
Get the indexed, n, matching file in a directory scan. Indices are numbered from 1 to the value returned by DRS-COUNT. The file's full pathname is returned on the stack as c-addr/u. Zero (0) is returned if the index is out of range. Getting a file name by index does not affect the sequence of file names returned by DRS-FIRST and DRS-NEXT.
The TV word set defines words for manipulating UNIX timeval structures. A UNIX timeval structure consists of two signed integer values represeting the number of seconds and microseconds since the start of January 1, 1970. (Strictly speaking, the fields in a UNIX timeval structure are not necessarily signed and not necessarily integers. See the Wikipedia entries for Unix time and time_t for detailed information about the complexities of even Unix's simple time scheme.) The timeval fields are placed on the stack with the most significant seconds field first. For example, TV-TOD returns the time-of-day on the stack as two signed integers:
TV-TOD ( -- seconds microseconds )
NOTE that these can't be treated as double-cell numbers, since a value of zero seconds, -123,000 microseconds has no indication in the seconds field that the time value is negative. Hence, I put the most significant seconds field first on the stack to prevent any confusion.
- time1 time2 TV-ADD
- time1 time2 TV-COMPARE
- time inLocal? "format" TV-SHOW
- time1 time2 TV-SUBTRACT
- TV-TOD
Source: words_tv.c
TV-ADD TV ( time1 time2 -- time3 )
Add two UNIX timevals, time1 plus time2, and return the sum, time3.
TV-COMPARE TV ( time1 time2 -- n )
Compare two UNIX timevals, time1 and time2, and return a signed integer indicating the result of the comparison: -1 if time1 is less than time2, zero if time1 equals time2, and +1 if time1 is greater than time2.
TV-SHOW TV ( time inLocal? c-addr1 u1 -- c-addr2 u2 | 0 )
Format a binary timeval, time, in ASCII using the strftime(3) format specified by c-addr1/u1. If inLocal? is true, the local time is used; otherwise the GMT is used. The formatted time is returned on the stack as c-addr2/u2, a string whose storage is private to the TV-SHOW word. If the conversion fails for any reason, only zero (0) is returned on the stack.
NOTE: If the O/S library does not support the gmtime(3) and localtime(3) functions, the binary time is assumed to be GMT and the inLocal? argument is ignored. If the O/S library does not support strftime(3), the format argument is ignored and the binary time is formatted as "YYYY-DOY-HR:MN:SC.MLS". Experiment with TV-SHOW to determine into which category your FINC build falls.
TV-SUBTRACT TV ( time1 time2 -- time3 )
Subtract two UNIX timevals, time1 minus time2, and return the difference, time3.
TV-TOD TV ( -- time )
Get the current time-of-day in seconds and microsecond since the start of January 1, 1970.
The MISC word set defines a miscellaneous collection of unrelated words.
- "name" GETENV
Source: words_misc.c
GETENV MISC ( c-addr1 u1 -- c-addr2 u2 | 0 )
Get the value of the environment variable whose name is c-addr1/u1. The value, c-addr2/u2, is returned on the stack. If the named environment variable is not defined, then 0 is returned instead of c-addr2/u2.