commit 91954e159ec6549ee9d8fed662bb87a6b93ae99c
parent 2037fe5694a4821f3029e051d5a667de85b42037
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date: Wed, 19 Oct 2022 06:20:40 +0000
add gitignore and progression on djb library imports
Diffstat:
53 files changed, 1832 insertions(+), 4 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,6 @@
+*.obj
+*.exe
+.dep.*
+.lock.*
+build/
+!build/*.do
diff --git a/doc/io/QUELLE b/doc/io/QUELLE
@@ -0,0 +1 @@
+Sun 13 Jul 2003 16:43:42 UTC
diff --git a/doc/io/README.md b/doc/io/README.md
@@ -0,0 +1,3 @@
+# The io library - notes
+
+This copy of io.html was last modified Sun 13 Jul 2003 16:43:42 UTC (Epoch +1058114622)
diff --git a/doc/io/io.html b/doc/io/io.html
@@ -0,0 +1,702 @@
+<html>
+<body>
+<a href="../djb.html">D. J. Bernstein</a>
+<h1>The io library interface</h1>
+This page starts with
+<a href="#intro">an introduction to the io library</a>.
+It continues by describing the io functions for
+<a href="#create">creating descriptors</a>,
+<a href="#read">reading data</a>,
+<a href="#write">writing data</a>,
+<a href="#timeout">setting time limits on descriptors</a>,
+<a href="#wait">waiting for ready descriptors</a>,
+and
+<a href="#absorb">using descriptors created in other ways</a>.
+<h2><a name="intro">Introduction to the io library</a></h2>
+A UNIX program receives information from external sources
+through ``file descriptors.''
+Despite the word ``file,''
+these sources are not necessarily files on disk;
+a file descriptor might be a ``socket''
+receiving information from the <tt>cnn.com</tt> web server,
+for example,
+or a ``tty'' receiving information from the user's keyboard.
+<p>
+Similarly,
+a UNIX program provides information to external sinks
+through file descriptors.
+<p>
+The io (input/output) library manages file descriptors.
+It can
+<ul>
+<li>create descriptors talking to disk files, other programs, etc.;
+<li>receive (``read'') data from a descriptor,
+if the descriptor has data to provide;
+<li>provide (``write'') data to a descriptor,
+if the descriptor has space for data;
+<li>if no descriptors are ready for reading or writing,
+pause until the situation changes; and
+<li>identify the descriptors that are ready
+for reading or writing.
+</ul>
+The descriptors are identified by nonnegative integers (0, 1, 2, etc.)
+stored in <tt>int64</tt> variables.
+<p>
+For example, a program that wants to read a file from disk
+can use <tt>io_readfile()</tt> to create a descriptor for the file,
+<tt>io_tryread()</tt> repeatedly to receive information from the file,
+and then <tt>io_close()</tt> to eliminate the descriptor.
+<p>
+The following program uses the io library
+to copy standard input (descriptor 0)
+to standard output (descriptor 1),
+with a 65536-byte buffer in the middle:
+<pre>
+ #include "io.h"
+
+ char buf[65536];
+ int64 readpos = 0;
+ int64 writepos = 0;
+ int flageof = 0;
+
+ int main()
+ {
+ int64 r;
+
+ if (!io_fd(0)) return 111;
+ if (!io_fd(1)) return 111;
+
+ for (;;) {
+ if (flageof && writepos >= readpos) return 0;
+
+ if (!flageof && readpos < sizeof buf) {
+ r = io_tryread(0,buf + readpos,sizeof buf - readpos);
+ if (r <= -2) return 111; /* read error other than <tt>EAGAIN</tt> */
+ if (r == 0) flageof = 1;
+ if (r > 0) readpos += r;
+ }
+
+ if (writepos < readpos) {
+ r = io_trywrite(1,buf + writepos,readpos - writepos);
+ if (r <= -2) return 111; /* write error other than <tt>EAGAIN</tt> */
+ if (r > 0) {
+ writepos += r;
+ if (writepos == readpos) readpos = writepos = 0;
+ /* if writepos is big, might want to left-shift buffer here */
+ }
+ }
+ }
+ }
+</pre>
+<tt>io_fd</tt> makes file descriptors 0 and 1 visible to the io library.
+<tt>io_tryread(0,...)</tt>
+reads data if descriptor 0 is readable,
+and <tt>io_trywrite(1,...)</tt>
+writes data if descriptor 1 is writable.
+If the buffer is partially full,
+the program will check both descriptors,
+rather than delaying further reads until the current data is written.
+<p>
+If descriptor 0 is unreadable (or there is no space in the buffer for new data)
+and descriptor 1 is unwritable (or the buffer is empty),
+the above program busy-loops:
+it calls <tt>io_tryread()</tt> and <tt>io_trywrite()</tt> repeatedly,
+making no progress until the situation changes.
+Here is a smarter program
+that instead goes to sleep until the situation changes,
+leaving the CPU free for other programs:
+<pre>
+ #include "io.h"
+
+ char buf[65536];
+ int64 readpos = 0;
+ int64 writepos = 0;
+ int flageof = 0;
+
+ int main()
+ {
+ int64 r;
+
+ if (!io_fd(0)) return 111;
+ if (!io_fd(1)) return 111;
+
+ for (;;) {
+ if (flageof && writepos >= readpos) return 0;
+
+ if (!flageof && readpos < sizeof buf) io_wantread(0);
+ if (writepos < readpos) io_wantwrite(1);
+ io_wait();
+ if (!flageof && readpos < sizeof buf) io_dontwantread(0);
+ if (writepos < readpos) io_dontwantwrite(1);
+
+ if (!flageof && readpos < sizeof buf) {
+ r = io_tryread(0,buf + readpos,sizeof buf - readpos);
+ if (r <= -2) return 111; /* read error other than <tt>EAGAIN</tt> */
+ if (r == 0) flageof = 1;
+ if (r > 0) readpos += r;
+ }
+
+ if (writepos < readpos) {
+ r = io_trywrite(1,buf + writepos,readpos - writepos);
+ if (r <= -2) return 111; /* write error other than <tt>EAGAIN</tt> */
+ if (r > 0) {
+ writepos += r;
+ if (writepos == readpos) readpos = writepos = 0;
+ /* if writepos is big, might want to left-shift buffer here */
+ }
+ }
+ }
+ }
+</pre>
+<tt>io_wait</tt> watches the descriptors previously indicated by
+<tt>io_wantread</tt> and <tt>io_wantwrite</tt>.
+<hr>
+<h2><a name="create">Creating descriptors</a></h2>
+<pre>
+ #include "io.h"
+ char <i>s</i>[];
+ int64 <i>d</i>;
+ io_readfile(&<i>d</i>,<i>s</i>);
+</pre>
+<tt>io_readfile</tt> sets <tt><i>d</i></tt> to the number of a new descriptor
+reading from the disk file named <tt><i>s</i></tt>,
+and returns 1.
+<p>
+If something goes wrong,
+<tt>io_readfile</tt> sets <tt>errno</tt> to indicate the error,
+and returns 0;
+it does not create a new descriptor,
+and it does not touch <tt><i>d</i></tt>.
+<hr>
+<pre>
+ #include "io.h"
+ char <i>s</i>[];
+ int64 <i>d</i>;
+ io_createfile(&<i>d</i>,<i>s</i>);
+</pre>
+<tt>io_createfile</tt> sets <tt><i>d</i></tt> to the number of a new descriptor
+writing to the disk file named <tt><i>s</i></tt>,
+and returns 1.
+If <tt><i>s</i></tt> already existed, it is truncated to length 0;
+otherwise, it is created, with mode 0600.
+<p>
+If something goes wrong,
+<tt>io_createfile</tt> sets <tt>errno</tt> to indicate the error,
+and returns 0;
+it does not create a new descriptor,
+and it does not touch <tt><i>d</i></tt>.
+(However, it may have truncated or created the file.)
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>[2];
+ io_pipe(<i>d</i>);
+</pre>
+<tt>io_pipe</tt> creates a new UNIX ``pipe.''
+The pipe can receive data and provide data;
+any bytes written to the pipe
+can then be read from the pipe in the same order.
+<p>
+A pipe is typically stored in an 8192-byte memory buffer;
+the exact number depends on the UNIX kernel.
+Bytes are written to the end of the buffer
+and read from the beginning of the buffer.
+Once a byte has been read, it is eliminated from the buffer,
+making space for another byte to be written;
+readers cannot ``rewind'' a pipe to read old data.
+Once 8192 bytes have been written to the buffer,
+the pipe will not be ready for further writing
+until some of the bytes have been read.
+Once all the bytes written have been read,
+the pipe will not be ready for further reading
+until more bytes are written.
+<p>
+<tt>io_pipe</tt> sets <tt><i>d</i>[0]</tt>
+to the number of a new descriptor reading from the pipe,
+and sets <tt><i>d</i>[1]</tt>
+to the number of a new descriptor writing to the pipe.
+It then returns 1 to indicate success.
+If something goes wrong,
+<tt>io_pipe</tt> returns 0, setting <tt>errno</tt> to indicate the error;
+in this case
+it frees any memory that it allocated for the new pipe,
+and it leaves <tt><i>d</i></tt> alone.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ io_close(<i>d</i>);
+</pre>
+<tt>io_close</tt> eliminates the descriptor numbered <tt><i>d</i></tt>.
+This usually does not mean eliminating the object that the descriptor
+is talking to.
+(For example, if a descriptor writes to a named disk file,
+closing the descriptor will not remove the file;
+it simply removes one way of writing to the file.
+On the other hand, a pipe disappears as soon as no descriptors refer to it.)
+<p>
+<tt>io_close</tt> has no return value;
+it always succeeds in deallocating the memory used for the descriptor.
+If <tt><i>d</i></tt> is not the number of a descriptor,
+<tt>io_close</tt> has no effect.
+<hr>
+<h2><a name="read">Reading data</a></h2>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ char <i>buf</i>[];
+ int64 <i>len</i>;
+ int64 <i>result</i>;
+ <i>result</i> = io_tryread(<i>d</i>,<i>buf</i>,<i>len</i>);
+</pre>
+<tt>io_tryread</tt> tries to read <tt><i>len</i></tt> bytes of data
+from descriptor <tt><i>d</i></tt>
+into
+<tt><i>buf</i>[0]</tt>,
+<tt><i>buf</i>[1]</tt>,
+...,
+<tt><i>buf</i>[<i>len</i>-1]</tt>.
+(The effects are undefined if <tt><i>len</i></tt> is 0 or smaller.)
+There are several possible results:
+<ul>
+<li><tt>io_tryread</tt> returns an integer between 1 and <tt><i>len</i></tt>:
+This number of bytes was available for immediate reading;
+the bytes were read into the beginning of <tt><i>buf</i></tt>.
+Note that this number can be, and often is, smaller than <tt><i>len</i></tt>;
+you must not assume that <tt>io_tryread</tt>
+always succeeds in reading exactly <tt><i>len</i></tt> bytes.
+<li><tt>io_tryread</tt> returns 0:
+No bytes were read,
+because the descriptor is at end of file.
+For example,
+this descriptor has reached the end of a disk file,
+or is reading an empty pipe that has been closed by all writers.
+<li><tt>io_tryread</tt> returns -1,
+setting <tt>errno</tt> to <tt>EAGAIN</tt>:
+No bytes were read,
+because the descriptor is not ready.
+For example, the descriptor is reading an empty pipe
+that could still be written to.
+<li><tt>io_tryread</tt> returns -3,
+setting <tt>errno</tt> to something other than <tt>EAGAIN</tt>:
+No bytes were read,
+because the read attempt encountered a persistent error,
+such as a serious disk failure (<tt>EIO</tt>),
+an unreachable network (<tt>ENETUNREACH</tt>),
+or an invalid descriptor number (<tt>EBADF</tt>).
+</ul>
+<tt>io_tryread</tt> does not pause waiting for a descriptor that is not ready.
+If you want to pause, use <tt>io_waitread</tt> or <tt>io_wait</tt>.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ char <i>buf</i>[];
+ int64 <i>len</i>;
+ int64 <i>result</i>;
+ <i>result</i> = io_waitread(<i>d</i>,<i>buf</i>,<i>len</i>);
+</pre>
+<tt>io_waitread</tt> tries to read <tt><i>len</i></tt> bytes of data
+from descriptor <tt><i>d</i></tt>
+into
+<tt><i>buf</i>[0]</tt>,
+<tt><i>buf</i>[1]</tt>,
+...,
+<tt><i>buf</i>[<i>len</i>-1]</tt>,
+pausing if necessary so that the descriptor is ready.
+(The effects are undefined if <tt><i>len</i></tt> is 0 or smaller.)
+There are several possible results:
+<ul>
+<li><tt>io_waitread</tt> returns an integer between 1 and <tt><i>len</i></tt>:
+This number of bytes was available for reading;
+the bytes were read into the beginning of <tt><i>buf</i></tt>.
+Note that this number can be, and often is, smaller than <tt><i>len</i></tt>;
+you must not assume that <tt>io_waitread</tt>
+always succeeds in reading exactly <tt><i>len</i></tt> bytes.
+<li><tt>io_waitread</tt> returns 0:
+No bytes were read,
+because the descriptor is at end of file.
+For example,
+this descriptor has reached the end of a disk file,
+or is reading an empty pipe that has been closed by all writers.
+<li><tt>io_waitread</tt> returns -3,
+setting <tt>errno</tt> to something other than <tt>EAGAIN</tt>:
+No bytes were read,
+because the read attempt encountered a persistent error,
+such as a serious disk failure (<tt>EIO</tt>),
+an unreachable network (<tt>ENETUNREACH</tt>),
+or an invalid descriptor number (<tt>EBADF</tt>).
+</ul>
+<hr>
+<h2><a name="write">Writing data</a></h2>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ const char <i>buf</i>[];
+ int64 <i>len</i>;
+ int64 <i>result</i>;
+ <i>result</i> = io_trywrite(<i>d</i>,<i>buf</i>,<i>len</i>);
+</pre>
+<tt>io_trywrite</tt> tries to write <tt><i>len</i></tt> bytes of data
+from
+<tt><i>buf</i>[0]</tt>,
+<tt><i>buf</i>[1]</tt>,
+...,
+<tt><i>buf</i>[<i>len</i>-1]</tt>
+to descriptor <tt><i>d</i></tt>.
+(The effects are undefined if <tt><i>len</i></tt> is 0 or smaller.)
+There are several possible results:
+<ul>
+<li><tt>io_trywrite</tt> returns an integer between 1 and <tt><i>len</i></tt>:
+This number of bytes was immediately written
+from the beginning of <tt><i>buf</i></tt>.
+Note that this number can be, and often is, smaller than <tt><i>len</i></tt>;
+you must not assume that <tt>io_trywrite</tt>
+always succeeds in writing exactly <tt><i>len</i></tt> bytes.
+<li><tt>io_trywrite</tt> returns -1,
+setting <tt>errno</tt> to <tt>EAGAIN</tt>:
+No bytes were written,
+because the descriptor is not ready.
+For example, the descriptor is writing to a full pipe
+that could still be read.
+<li><tt>io_trywrite</tt> returns -3,
+setting <tt>errno</tt> to something other than <tt>EAGAIN</tt>:
+No bytes were written,
+because the write attempt encountered a persistent error,
+such as a serious disk failure (<tt>EIO</tt>),
+a pipe that can no longer be read (<tt>EPIPE</tt>),
+an unreachable network (<tt>ENETUNREACH</tt>),
+or an invalid descriptor number (<tt>EBADF</tt>).
+</ul>
+<tt>io_trywrite</tt> does not pause waiting for a descriptor that is not ready.
+If you want to pause, use <tt>io_waitwrite</tt> or <tt>io_wait</tt>.
+<p>
+Once upon a time,
+many UNIX programs neglected to check the success of their writes.
+They would often encounter <tt>EPIPE</tt>,
+and would blithely continue writing,
+rather than exiting with an appropriate exit code.
+The UNIX kernel developers decided to send a <tt>SIGPIPE</tt> signal,
+which terminates the process by default,
+along with returning <tt>EPIPE</tt>.
+This papers over the problem without fixing it:
+the same programs ignore other errors such as <tt>EIO</tt>.
+One hopes that the programs have been fixed by now;
+kernels nevertheless continue to generate the <tt>SIGPIPE</tt> signal.
+The first time <tt>io_trywrite</tt> or <tt>io_waitwrite</tt> is called,
+it arranges for <tt>SIGPIPE</tt> to be ignored.
+(Technically, for <tt>SIGPIPE</tt> to be caught by an empty signal handler,
+so this doesn't affect child processes.)
+Do not use <tt>SIGPIPE</tt> elsewhere in the program.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ const char <i>buf</i>[];
+ int64 <i>len</i>;
+ int64 <i>result</i>;
+ <i>result</i> = io_waitwrite(<i>d</i>,<i>buf</i>,<i>len</i>);
+</pre>
+<tt>io_waitwrite</tt> tries to write <tt><i>len</i></tt> bytes of data
+from
+<tt><i>buf</i>[0]</tt>,
+<tt><i>buf</i>[1]</tt>,
+...,
+<tt><i>buf</i>[<i>len</i>-1]</tt>
+to descriptor <tt><i>d</i></tt>,
+pausing (perhaps repeatedly) until the descriptor is ready.
+(The effects are undefined if <tt><i>len</i></tt> is 0 or smaller.)
+There are several possible results:
+<ul>
+<li><tt>io_waitwrite</tt> returns <tt><i>len</i></tt>:
+All the bytes were written.
+<li><tt>io_waitwrite</tt> returns something between <tt>0</tt>
+and <tt><i>len</i>-1</tt>,
+setting <tt>errno</tt> to something other than <tt>EAGAIN</tt>:
+After this number of bytes was written,
+no further bytes were written,
+because the write attempt encountered a persistent error,
+such as a serious disk failure (<tt>EIO</tt>),
+a pipe that can no longer be read (<tt>EPIPE</tt>),
+an unreachable network (<tt>ENETUNREACH</tt>),
+or an invalid descriptor number (<tt>EBADF</tt>).
+</ul>
+<hr>
+<h2><a name="timeout">Setting time limits on descriptors</a></h2>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ tai6464 <i>t</i>;
+ io_timeout(<i>d</i>,<i>t</i>);
+</pre>
+The io library keeps track of an optional ``timeout'' for each descriptor.
+The timeout is a specific moment in time,
+stored in a <tt>tai6464</tt> variable.
+<p>
+<tt>io_timeout(<i>d</i>,<i>t</i>)</tt>
+sets the timeout for descriptor <tt><i>d</i></tt>
+to <tt><i>t</i></tt>.
+<p>
+<tt>io_timeout</tt> has no return value; it always succeeds.
+(Space to store the timeout was already allocated as part of the descriptor.)
+It has no effect
+if <tt><i>d</i></tt> is not the number of a descriptor.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ char <i>buf</i>[];
+ int64 <i>len</i>;
+ int64 <i>result</i>;
+ <i>result</i> = io_tryreadtimeout(<i>d</i>,<i>buf</i>,<i>len</i>);
+</pre>
+<tt>io_tryreadtimeout</tt> is identical to <tt>io_tryread</tt>,
+with the following exception: if
+<ul>
+<li><tt>io_tryread</tt> returns -1 (the descriptor is not ready for reading),
+and
+<li>the descriptor has a timeout, and
+<li>the read attempt was after the descriptor's timeout,
+</ul>
+then <tt>io_tryreadtimeout</tt> instead returns -2,
+with <tt>errno</tt> set to <tt>ETIMEDOUT</tt>.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ const char <i>buf</i>[];
+ int64 <i>len</i>;
+ int64 <i>result</i>;
+ <i>result</i> = io_trywritetimeout(<i>d</i>,<i>buf</i>,<i>len</i>);
+</pre>
+<tt>io_trywritetimeout</tt> is identical to <tt>io_trywrite</tt>,
+with the following exception: if
+<ul>
+<li><tt>io_trywrite</tt> returns -1 (the descriptor is not ready for writing),
+and
+<li>the descriptor has a timeout, and
+<li>the write attempt was after the descriptor's timeout,
+</ul>
+then <tt>io_trywritetimeout</tt> instead returns -2,
+with <tt>errno</tt> set to <tt>ETIMEDOUT</tt>.
+<hr>
+<h2><a name="wait">Waiting for ready descriptors</a></h2>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ io_wantread(<i>d</i>);
+ io_wantwrite(<i>d</i>);
+ io_dontwantread(<i>d</i>);
+ io_dontwantwrite(<i>d</i>);
+</pre>
+For each descriptor,
+the io library keeps track of the number of parts of the current program
+interested in reading the descriptor.
+The number is incremented by <tt>io_wantread</tt>
+and decremented by <tt>io_dontwantread</tt>.
+(The effects are undefined if the number is decremented below 0.)
+The number is 0 when the descriptor is created.
+Closing a descriptor implicitly sets the number to 0.
+<p>
+Similar comments apply to <tt>io_wantwrite</tt> and <tt>io_dontwantwrite</tt>.
+<p>
+These functions have no return value; they always succeed.
+(Space to store the numbers was already allocated as part of the descriptor.)
+They have no effect if <tt><i>d</i></tt> is not the number of a descriptor.
+<p>
+You do not have to indicate interest in a descriptor
+before using the descriptor.
+The importance of <tt>io_wantread</tt> and <tt>io_wantwrite</tt>
+is their interaction with <tt>io_wait</tt>,
+<tt>io_waituntil</tt>, and <tt>io_check</tt>.
+<hr>
+<pre>
+ #include "io.h"
+ tai6464 <i>t</i>;
+ io_wait();
+ io_waituntil(<i>t</i>);
+ io_check();
+</pre>
+<tt>io_wait()</tt>
+checks the descriptors that the program is interested in
+to see whether any of them are ready.
+If none of them are ready,
+<tt>io_wait()</tt>
+tries to pause until one of them is ready,
+so that it does not take time away
+from other programs running on the same computer.
+<p>
+<tt>io_wait</tt> pays attention to timeouts:
+if a descriptor reaches its timeout,
+and the program is interested in reading or writing that descriptor,
+<tt>io_wait</tt> will return promptly.
+<p>
+Under some circumstances,
+<tt>io_wait</tt> will return even though no interesting descriptors are ready.
+Do not assume that a descriptor is ready
+merely because <tt>io_wait</tt> has returned.
+<p>
+<tt>io_wait</tt> is <i>not</i> interrupted by the delivery of a signal.
+Programs that expect interruption are unreliable:
+they will block if the same signal
+is delivered a moment before <tt>io_wait</tt>.
+The correct way to handle signals is with the
+<a href="../docs/selfpipe.html">self-pipe trick</a>.
+<p>
+<tt>io_waituntil(<i>t</i>)</tt>
+is like <tt>io_wait()</tt>
+but does not wait (noticeably) past time <tt><i>t</i></tt>.
+<tt>io_check()</tt>
+is like <tt>io_wait()</tt>
+but does not wait at all;
+its importance is its interaction
+with <tt>io_canread</tt> and <tt>io_canwrite</tt>.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ <i>d</i> = io_canread();
+ <i>d</i> = io_canwrite();
+</pre>
+<tt>io_wait</tt>
+saves a list of numbers of the descriptors
+that are ready to be read (or that have timed out)
+and that the program is interested in reading.
+<tt>io_canread</tt> returns the next descriptor number on the list,
+or -1 if there are no more descriptor numbers on the list.
+The list is reset by the next call to <tt>io_wait</tt>.
+<p>
+<tt>io_waituntil</tt> and <tt>io_check</tt>
+interact with <tt>io_canread</tt> in the same way that <tt>io_wait</tt> does.
+<tt>io_wait</tt>, <tt>io_waituntil</tt>, and <tt>io_check</tt>
+share one list of descriptors.
+<p>
+Do not assume that data can actually be read
+merely because <tt>io_canread</tt> has pointed to a descriptor.
+Data that was readable a moment ago
+could already have been read by another program.
+Even worse, the low-level UNIX routines
+used by <tt>io_wait</tt> could have failed to allocate memory;
+in this case, <tt>io_canread</tt> has no choice but to indicate
+that all descriptors are ready.
+You must check the results of <tt>io_tryread</tt>.
+<p>
+Similar comments apply to <tt>io_canwrite</tt>.
+<p>
+You do not need to use <tt>io_canread</tt> and <tt>io_canwrite</tt>.
+You can call <tt>io_wait</tt>,
+try <tt>io_tryread</tt> and <tt>io_trywrite</tt> for every descriptor,
+and repeat.
+However,
+if you have thousands of descriptors of which only a few are ready,
+most of the effort of trying descriptors will be wasted.
+It is faster to focus on the descriptors indicated by <tt>io_canread</tt>
+and <tt>io_canwrite</tt>.
+<p>
+(The <tt>io_wait</tt> implementation and underlying UNIX kernel routines
+might have their own speed problems with thousands of descriptors,
+but this problem can be solved without any change in programs that use
+<tt>io_wait</tt>.)
+<hr>
+<h2><a name="absorb">Using descriptors created in other ways</a></h2>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ io_fd(<i>d</i>);
+</pre>
+There is a slight difference between the io library's view of descriptors
+and the UNIX view of descriptors:
+the io library has its own list of descriptors,
+and will not work with UNIX descriptors that aren't on the list.
+In particular:
+<ul>
+<li>When a program starts,
+it typically ``inherits'' (is started with) UNIX descriptors
+numbered 0 (stdin: standard input),
+1 (stdout: standard output), and
+2 (stderr: standard error).
+More sophisticated UNIX programming
+often involves more startup descriptors.
+In contrast, the io library starts with an empty list.
+The startup descriptors should be passed to <tt>io_fd</tt>.
+<li>Various low-level mechanisms outside the io library,
+such as the UNIX <tt>socket</tt> function,
+create UNIX descriptors that aren't on the io list.
+These descriptors should be passed to
+<tt>io_closeonexec</tt>, <tt>io_nonblock</tt>, and <tt>io_fd</tt>.
+</ul>
+The <tt>io_fd</tt> function
+adds the UNIX descriptor numbered <tt>d</tt> to the io list,
+and returns 1.
+If the descriptor is already on the list,
+<tt>io_fd</tt> returns 1 without doing anything.
+If something goes wrong (such as running out of memory),
+<tt>io_fd</tt> returns 0, setting <tt>errno</tt> to indicate the error.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ io_nonblock(<i>d</i>);
+</pre>
+<tt>io_nonblock</tt> puts UNIX descriptor <tt><i>d</i></tt>
+into ``non-blocking mode.''
+Calling <tt>io_nonblock(<i>d</i>)</tt>
+<i>before</i> <tt>io_fd(<i>d</i>)</tt> saves some time in
+<tt>io_tryread</tt> and <tt>io_trywrite</tt>.
+<p>
+Actually, current UNIX kernels
+do not support non-blocking <i>descriptors</i>;
+they support non-blocking <i>open files</i>.
+Furthermore, many programs will break if they encounter non-blocking mode.
+This means that
+you must not use <tt>io_nonblock</tt> for a descriptor
+inherited from another program.
+<p>
+<tt>io_nonblock</tt> has no return value; it always succeeds.
+If <tt><i>d</i></tt> is not the number of a UNIX descriptor,
+<tt>io_nonblock</tt> has no effect.
+<p>
+If <tt>io_fd</tt> is given a descriptor in blocking mode,
+<tt>io_tryread</tt> and <tt>io_trywrite</tt> go through
+the following contortions to avoid blocking:
+<ol>
+<li>Stop if <tt>poll</tt> says that the descriptor is not ready.
+Otherwise there's a good chance, but not a guarantee:
+even if <tt>poll</tt> says the descriptor is ready,
+the descriptor might not be ready a moment later.
+(Furthermore, <tt>poll</tt> can fail on some systems.)
+<li>Catch <tt>SIGALRM</tt>.
+<tt>SIGALRM</tt> must not be blocked,
+and must not be used elsewhere in the program.
+<li>Set an interval timer
+so that any blocking call will be interrupted by <tt>SIGALRM</tt>
+within 10 milliseconds.
+(Current UNIX kernels do not allow any shorter interval.)
+Of course, this may still mean a 10-millisecond delay.
+</ol>
+If <tt>io_fd</tt> is given a descriptor in non-blocking mode
+(or a descriptor for a regular disk file),
+<tt>io_tryread</tt> and <tt>io_trywrite</tt> avoid these contortions.
+<p>
+Future versions of the io library may use
+<a href="../unix/nonblock.html">kernel extensions</a>
+to avoid these contortions.
+<hr>
+<pre>
+ #include "io.h"
+ int64 <i>d</i>;
+ io_closeonexec(<i>d</i>);
+</pre>
+<tt>io_closeonexec</tt> arranges for UNIX descriptor <tt><i>d</i></tt>
+to <i>not</i> be inherited by children of the current process.
+You should do this for any descriptor obtained from a UNIX function
+such as <tt>socket</tt> and <tt>open</tt>;
+it's an unfortunate historical accident that the UNIX functions don't do this.
+(If you want a descriptor to be inherited by a child process,
+arrange that when you create the child process.)
+<p>
+<tt>io_closeonexec</tt> has no return value; it always succeeds.
+If <tt><i>d</i></tt> is not the number of a UNIX descriptor,
+<tt>io_closeonexec</tt> has no effect.
+</body>
+</html>
diff --git a/doc/npthread/README.md b/doc/npthread/README.md
@@ -1,13 +1,13 @@
# The npthread library - notes
-npthread.html was retrieved Sat 21 Jun 2003 00:03:18 UTC (Epoch +1056153798)
+This copy of npthread.html was last modified Sat 21 Jun 2003 00:03:18 UTC (Epoch +1056153798)
This npthread aims to be capable of exact conformance with the Bernstein documented npthreads by default. If you define NPTHREAD_VPTR, this compliance is lost, but the 'n' your f(n) is called with is expected to be a void pointer instead of an int64. I believe this use to be much more likely to be useful for most users.
-If you include sc_npthread.h as well as npthread.h, you would gain access to additional thread call conditions to be determined. In thread-safe sc_npthread, those call conditions may involve pthread trylock successes, or data (from another true thread) in the rendezvous area.
+If you include sc_npthread.h as well as npthread.h, you would gain access to additional thread call conditions to be determined. In thread-safe sc_npthread, those call conditions may involve pthread trylock successes, or data (from another true thread) in a mailbox area.
Your threads should have a mechanism for saving their state, so if they choose to cut mid-procedure (e.g. starving of data from a fildes or the rendezvous area, or of a locked resource, or buffered I/O needs to drain before continuing), they can resume processing. In 2000, Simon Tatham designed a header capable of helping you do this. It is available under the MIT licence. <https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html>
-User-defined lights (npthread_watchflag and npthread_waveflag) could be used to implement the message and trylock notes.
+User-defined lights (npthread_watchflag and npthread_waveflag) could be used internally to implement the message and trylock notes.
This package requires tai support, which can be had from many different places.
diff --git a/head/io/io.h b/head/io/io.h
@@ -0,0 +1,91 @@
+#ifndef _SC_IO_IO_H
+#define _SC_IO_IO_H
+
+#define int64 int64_t
+
+#ifndef BERNSTEINLY_CORRECT
+#define taitype tai6464
+#else
+#define taitype taia
+#endif
+
+extern int io_readfile(int64 *d, const char *s);
+extern int io_createfile(int64 *d, const char *s);
+extern int io_pipe(int64 *d); // must be of length 2
+
+/* io_close always succeeds in deallocating the memory
+ * used by the io library related to this, but the underlying
+ * close(fd) system call may give an error return. in such a case
+ * io_close is to be considered successful, but if you aren't
+ * BERNSTEINLY_CORRECT you can harvest the output.
+ */
+#ifndef BERNSTEINLY_CORRECT
+extern int io_close(int64 d);
+#else
+extern void io_close(int64 d);
+#endif
+
+extern int64 io_tryread(int64, const char *, int64);
+extern int64 io_waitread(int64, const char *, int64);
+
+extern int64 io_trywrite(int64, const char *, int64);
+extern int64 io_waitwrite(int64, const char *, int64);
+
+
+/* in thread-safe io, timeout can return -1 EAGAIN if a writer
+ * lock to the structure is not currently available.
+ * waittimeout will instead hang the current thread until the
+ * lock comes up.
+ * thread-safe CORRECT io will always hang.
+ * for reliably nonblock timeout sets, always use trytimeout,
+ * and for reliably block timeout sets, always use waittimeout.
+ * both of these functions are extensions in Suitcase io.
+ */
+#ifndef BERNSTEINLY_CORRECT
+extern void io_timeout(int64, taitype);
+#define io_waittimeout(i, t) io_timeout(i, t)
+extern int io_trytimeout(int64, taitype);
+#else
+extern int io_timeout(int64, taitype);
+extern void io_waittimeout(int64, taitype);
+#define io_trytimeout(i, t) io_timeout(i, t)
+#endif
+
+extern void io_wantread(int64);
+extern void io_wantwrite(int64);
+extern void io_dontwantread(int64);
+extern void io_dontwantwrite(int64);
+
+/* io_trywant* family functions will return -1 EAGAIN if a writer
+ * lock to the structure is not currently available.
+ * this enables a thread to go to sleep with the desired value in
+ * its TLS, and an alarm connected to the lock available light,
+ * if it can't set the lock right now.
+ * bernstein standard versions of these functions will instead
+ * hang the current thread until the lock is available.
+ */
+#ifndef BERNSTEINLY_CORRECT
+extern int io_trywantread(int64);
+extern int io_trywantwrite(int64);
+extern int io_trydontwantread(int64);
+extern int io_trydontwantwrite(int64);
+#endif
+
+/* int versions of io_wait family functions will return the number
+ * of FDs they've put into the queue, or on systems with native event
+ * queues, the number of FDs the OS reports
+ */
+#ifndef BERNSTEINLY_CORRECT
+extern int64 io_waituntil(taitype);
+extern int64 io_wait();
+extern int64 io_check();
+#else
+extern void io_waituntil(taitype);
+extern void io_wait();
+extern void io_check();
+#endif
+
+extern int64 io_canread();
+extern int64 io_canwrite();
+
+#endif // _SC_IO_IO_H
diff --git a/head/npthread/npthread.h b/head/npthread/npthread.h
@@ -0,0 +1,42 @@
+#ifndef NPTHREAD_NPTHREAD_H
+#define NPTHREAD_NPTHREAD_H
+
+#include <sys/types.h>
+
+#define int64 int64_t
+
+#ifndef BERNSTEINLY_CORRECT
+#define thunktype void*
+#define taitype tai6464
+#else
+#define taitype taia
+#define thunktype int64
+#endif
+
+#define threadnotype uint64_t
+
+extern int npthread_add(int (*f)(thunktype n), thunktype);
+extern int npthread_addsignal(int (*f)(thunktype n), thunktype, int);
+
+extern int npthread_start(void);
+extern threadnotype *_sc_curthread();
+/* In the thread-unsafe version,
+ * this will be implemented using a static.
+ * in the thread-safe version, we will use pthread keys.
+ * all of this will be transparent to the thread.
+ */
+
+extern void npthread_jump(int (*f)(thunktype n), thunktype);
+extern void npthread_important(void);
+extern void npthread_unimportant(void);
+extern void npthread_child(pid_t);
+extern void npthread_childstatus(void); // uses thread local storage
+
+extern int64 npthread_newflag(void);
+extern void npthread_watchflag(int64);
+extern void npthread_waveflag(int64);
+
+extern void npthread_read(int64); // wants io_fd_t (int64)
+extern void npthread_write(int64);
+
+#endif // NPTHREAD_NPTHREAD_H
diff --git a/head/tai/caldate.h b/head/tai/caldate.h
@@ -0,0 +1,19 @@
+#ifndef CALDATE_H
+#define CALDATE_H
+
+struct caldate {
+ long year;
+ int month;
+ int day;
+} ;
+
+extern unsigned int caldate_fmt();
+extern unsigned int caldate_scan();
+
+extern void caldate_frommjd();
+extern long caldate_mjd();
+extern void caldate_normalize();
+
+extern void caldate_easter();
+
+#endif
diff --git a/head/tai/caltime.h b/head/tai/caltime.h
@@ -0,0 +1,20 @@
+#ifndef CALTIME_H
+#define CALTIME_H
+
+#include "caldate.h"
+
+struct caltime {
+ struct caldate date;
+ int hour;
+ int minute;
+ int second;
+ long offset;
+} ;
+
+extern void caltime_tai();
+extern void caltime_utc();
+
+extern unsigned int caltime_fmt();
+extern unsigned int caltime_scan();
+
+#endif
diff --git a/head/tai/leapsecs.h b/head/tai/leapsecs.h
@@ -0,0 +1,10 @@
+#ifndef LEAPSECS_H
+#define LEAPSECS_H
+
+extern int leapsecs_init();
+extern int leapsecs_read();
+
+extern void leapsecs_add();
+extern int leapsecs_sub();
+
+#endif
diff --git a/head/tai/tai.h b/head/tai/tai.h
@@ -0,0 +1,22 @@
+#ifndef TAI_H
+#define TAI_H
+
+#include "uint64.h"
+
+struct tai {
+ uint64 x;
+} ;
+
+extern void tai_now();
+
+#define tai_approx(t) ((double) ((t)->x))
+
+extern void tai_add();
+extern void tai_sub();
+#define tai_less(t,u) ((t)->x < (u)->x)
+
+#define TAI_PACK 8
+extern void tai_pack();
+extern void tai_unpack();
+
+#endif
diff --git a/head/tai/taia.h b/head/tai/taia.h
@@ -0,0 +1,31 @@
+#ifndef TAIA_H
+#define TAIA_H
+
+#include "tai.h"
+
+struct taia {
+ struct tai sec;
+ unsigned long nano; /* 0...999999999 */
+ unsigned long atto; /* 0...999999999 */
+} ;
+
+extern void taia_tai();
+
+extern void taia_now();
+
+extern double taia_approx();
+extern double taia_frac();
+
+extern void taia_add();
+extern void taia_sub();
+extern void taia_half();
+extern int taia_less();
+
+#define TAIA_PACK 16
+extern void taia_pack();
+extern void taia_unpack();
+
+#define TAIA_FMTFRAC 19
+extern unsigned int taia_fmtfrac();
+
+#endif
diff --git a/head/tai/uint64.h b/head/tai/uint64.h
@@ -0,0 +1,6 @@
+#ifndef UINT64_H
+#define UINT64_H
+
+typedef unsigned long long uint64;
+
+#endif
diff --git a/src/configlib.sh b/src/configlib.sh
@@ -0,0 +1,21 @@
+setvar() {
+ $1="$2"
+}
+
+# int getccvar setvar var ifundef
+get1ccvar() {
+ if test ! -f "conf-compile/$2"; then
+ if test ! -f "conf-compile/defaults/$2"; then
+ if test ! -z "$3"; then
+ $1 "$3"
+ else
+ return 1 # could not give a valid argument to param setvar
+ fi
+ else
+ $1 "$(head -n1 "conf-compile/defaults/$2")"
+ fi
+ else
+ $1 "$(head -n1 "conf-compile/$2")"
+ fi
+ return 0
+}
diff --git a/src/eloop/QUELLE b/src/eloop/QUELLE
@@ -2,4 +2,4 @@
The headers and source files herewithin ultimately come from the event loop facility of MXF-Remote, which was retrieved from https://git.umbrellix.net/nightmaremail version @6f7024a6c31f36d306a02de9804cb9d93cd67a7c, concerning file descriptor and protegé process management.
-This portion requires skalibs and the included uthash header.
+This portion requires skalibs (or another source of tain and iopause_stamp) and the included uthash header.
diff --git a/src/io/QUELLE b/src/io/QUELLE
@@ -0,0 +1,3 @@
+/pkg/umbrellix.net/prog/suitcase/src/io Statement of Origin
+
+This is my (Ellenor et al Bjornsdottir's) original work, albeit drafting some inspiration from the fact that this thas already been done before by other authors.
diff --git a/src/tai/QUELLE b/src/tai/QUELLE
@@ -0,0 +1,5 @@
+/pkg/umbrellix.net/prog/suitcase/src/tai Statement of Origin
+
+This is basically a straight extraction of the libtai library, in version 0.60, as published by Daniel Bernstein.
+
+I've cleaned up function declarations so that they are in the more modern style.
diff --git a/src/tai/caldate_fmjd.c b/src/tai/caldate_fmjd.c
@@ -0,0 +1,48 @@
+#include <tai/caldate.h>
+
+void caldate_frommjd(cd,day,pwday,pyday)
+struct caldate *cd;
+long day;
+int *pwday;
+int *pyday;
+{
+ long year;
+ long month;
+ int yday;
+
+ year = day / 146097L;
+ day %= 146097L;
+ day += 678881L;
+ while (day >= 146097L) { day -= 146097L; ++year; }
+
+ /* year * 146097 + day - 678881 is MJD; 0 <= day < 146097 */
+ /* 2000-03-01, MJD 51604, is year 5, day 0 */
+
+ if (pwday) *pwday = (day + 3) % 7;
+
+ year *= 4;
+ if (day == 146096L) { year += 3; day = 36524L; }
+ else { year += day / 36524L; day %= 36524L; }
+ year *= 25;
+ year += day / 1461;
+ day %= 1461;
+ year *= 4;
+
+ yday = (day < 306);
+ if (day == 1460) { year += 3; day = 365; }
+ else { year += day / 365; day %= 365; }
+ yday += day;
+
+ day *= 10;
+ month = (day + 5) / 306;
+ day = (day + 5) % 306;
+ day /= 10;
+ if (month >= 10) { yday -= 306; ++year; month -= 10; }
+ else { yday += 59; month += 2; }
+
+ cd->year = year;
+ cd->month = month + 1;
+ cd->day = day + 1;
+
+ if (pyday) *pyday = yday;
+}
diff --git a/src/tai/caldate_fmt.c b/src/tai/caldate_fmt.c
@@ -0,0 +1,23 @@
+#include <tai/caldate.h>
+
+unsigned int caldate_fmt(char *s, struct caldate *cd)
+{
+ long x;
+ int i = 0;
+
+ x = cd->year; if (x < 0) x = -x; do { ++i; x /= 10; } while(x);
+
+ if (s) {
+ x = cd->year;
+ if (x < 0) { x = -x; *s++ = '-'; }
+ s += i; do { *--s = '0' + (x % 10); x /= 10; } while(x); s += i;
+
+ x = cd->month;
+ s[0] = '-'; s[2] = '0' + (x % 10); x /= 10; s[1] = '0' + (x % 10);
+
+ x = cd->day;
+ s[3] = '-'; s[5] = '0' + (x % 10); x /= 10; s[4] = '0' + (x % 10);
+ }
+
+ return (cd->year < 0) + i + 6;
+}
diff --git a/src/tai/caldate_mjd.c b/src/tai/caldate_mjd.c
@@ -0,0 +1,43 @@
+#include <tai/caldate.h>
+
+static unsigned long times365[4] = { 0, 365, 730, 1095 } ;
+static unsigned long times36524[4] = { 0, 36524UL, 73048UL, 109572UL } ;
+static unsigned long montab[12] =
+{ 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337 } ;
+/* month length after february is (306 * m + 5) / 10 */
+
+long caldate_mjd(struct caldate *cd)
+{
+ long y;
+ long m;
+ long d;
+
+ d = cd->day - 678882L;
+ m = cd->month - 1;
+ y = cd->year;
+
+ d += 146097L * (y / 400);
+ y %= 400;
+
+ if (m >= 2) m -= 2; else { m += 10; --y; }
+
+ y += (m / 12);
+ m %= 12;
+ if (m < 0) { m += 12; --y; }
+
+ d += montab[m];
+
+ d += 146097L * (y / 400);
+ y %= 400;
+ if (y < 0) { y += 400; d -= 146097L; }
+
+ d += times365[y & 3];
+ y >>= 2;
+
+ d += 1461L * (y % 25);
+ y /= 25;
+
+ d += times36524[y & 3];
+
+ return d;
+}
diff --git a/src/tai/caldate_norm.c b/src/tai/caldate_norm.c
@@ -0,0 +1,6 @@
+#include <tai/caldate.h>
+
+void caldate_normalize(struct caldate *cd)
+{
+ caldate_frommjd(cd,caldate_mjd(cd),(int *) 0,(int *) 0);
+}
diff --git a/src/tai/caldate_scan.c b/src/tai/caldate_scan.c
@@ -0,0 +1,23 @@
+#include <tai/caldate.h>
+
+unsigned int caldate_scan(char *s, struct caldate *cd)
+{
+ int sign = 1;
+ char *t = s;
+ unsigned long z;
+ unsigned long c;
+
+ if (*t == '-') { ++t; sign = -1; }
+ z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
+ cd->year = z * sign;
+
+ if (*t++ != '-') return 0;
+ z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
+ cd->month = z;
+
+ if (*t++ != '-') return 0;
+ z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
+ cd->day = z;
+
+ return t - s;
+}
diff --git a/src/tai/caldate_ster.c b/src/tai/caldate_ster.c
@@ -0,0 +1,23 @@
+#include <tai/caldate.h>
+
+void caldate_easter(struct caldate *cd)
+{
+ long c;
+ long t;
+ long j;
+ long n;
+ long y;
+
+ y = cd->year;
+
+ c = (y / 100) + 1;
+ t = 210 - (((c * 3) / 4) % 210);
+ j = y % 19;
+ n = 57 - ((14 + j * 11 + (c * 8 + 5) / 25 + t) % 30);
+ if ((n == 56) && (j > 10)) --n;
+ if (n == 57) --n;
+ n -= ((((y % 28) * 5) / 4 + t + n + 2) % 7);
+
+ if (n < 32) { cd->month = 3; cd->day = n; }
+ else { cd->month = 4; cd->day = n - 31; }
+}
diff --git a/src/tai/caltime_fmt.c b/src/tai/caltime_fmt.c
@@ -0,0 +1,43 @@
+#include <tai/caldate.h>
+#include <tai/caltime.h>
+
+unsigned int caltime_fmt(char *s, struct caltime *ct)
+{
+ unsigned int result;
+ long x;
+
+ result = caldate_fmt(s,&ct->date);
+
+ if (s) {
+ s += result;
+
+ x = ct->hour;
+ s[0] = ' ';
+ s[2] = '0' + (x % 10); x /= 10;
+ s[1] = '0' + (x % 10);
+ s += 3;
+
+ x = ct->minute;
+ s[0] = ':';
+ s[2] = '0' + (x % 10); x /= 10;
+ s[1] = '0' + (x % 10);
+ s += 3;
+
+ x = ct->second;
+ s[0] = ':';
+ s[2] = '0' + (x % 10); x /= 10;
+ s[1] = '0' + (x % 10);
+ s += 3;
+
+ s[0] = ' ';
+ x = ct->offset;
+ if (x < 0) { s[1] = '-'; x = -x; } else s[1] = '+';
+
+ s[5] = '0' + (x % 10); x /= 10;
+ s[4] = '0' + (x % 6); x /= 6;
+ s[3] = '0' + (x % 10); x /= 10;
+ s[2] = '0' + (x % 10);
+ }
+
+ return result + 15;
+}
diff --git a/src/tai/caltime_scan.c b/src/tai/caltime_scan.c
@@ -0,0 +1,38 @@
+#include <tai/caltime.h>
+
+unsigned int caltime_scan(char *s, struct caltime *ct)
+{
+ char *t = s;
+ unsigned long z;
+ unsigned long c;
+ int sign;
+
+ t += caldate_scan(t,&ct->date);
+
+ while ((*t == ' ') || (*t == '\t') || (*t == 'T')) ++t;
+ z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
+ ct->hour = z;
+
+ if (*t++ != ':') return 0;
+ z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
+ ct->minute = z;
+
+ if (*t != ':')
+ ct->second = 0;
+ else {
+ ++t;
+ z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
+ ct->second = z;
+ }
+
+ while ((*t == ' ') || (*t == '\t')) ++t;
+ if (*t == '+') sign = 1; else if (*t == '-') sign = -1; else return 0;
+ ++t;
+ c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = c;
+ c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = z * 10 + c;
+ c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = z * 6 + c;
+ c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = z * 10 + c;
+ ct->offset = z * sign;
+
+ return t - s;
+}
diff --git a/src/tai/caltime_tai.c b/src/tai/caltime_tai.c
@@ -0,0 +1,23 @@
+#include <tai/tai.h>
+#include <tai/leapsecs.h>
+#include <tai/caldate.h>
+#include <tai/caltime.h>
+
+/* XXX: breaks tai encapsulation */
+
+void caltime_tai(struct caltime *ct, struct tai *t)
+{
+ long day;
+ long s;
+
+ /* XXX: check for overflow? */
+
+ day = caldate_mjd(&ct->date);
+
+ s = ct->hour * 60 + ct->minute;
+ s = (s - ct->offset) * 60 + ct->second;
+
+ t->x = day * 86400ULL + 4611686014920671114ULL + (long long) s;
+
+ leapsecs_add(t,ct->second == 60);
+}
diff --git a/src/tai/caltime_utc.c b/src/tai/caltime_utc.c
@@ -0,0 +1,31 @@
+#include <tai/tai.h>
+#include <tai/leapsecs.h>
+#include <tai/caldate.h>
+#include <tai/caltime.h>
+
+/* XXX: breaks tai encapsulation */
+
+void caltime_utc(struct caltime *ct, struct tai *t, int *pwday, int *pyday)
+{
+ struct tai t2 = *t;
+ uint64 u;
+ int leap;
+ long s;
+
+ /* XXX: check for overfow? */
+
+ leap = leapsecs_sub(&t2);
+ u = t2.x;
+
+ u += 58486;
+ s = u % 86400ULL;
+
+ ct->second = (s % 60) + leap; s /= 60;
+ ct->minute = s % 60; s /= 60;
+ ct->hour = s;
+
+ u /= 86400ULL;
+ caldate_frommjd(&ct->date,/*XXX*/(long) (u - 53375995543064ULL),pwday,pyday);
+
+ ct->offset = 0;
+}
diff --git a/src/tai/check.c b/src/tai/check.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <time.h>
+#include <tai/tai.h>
+#include <tai/leapsecs.h>
+#include <tai/caltime.h>
+
+char line[100];
+
+char *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ;
+
+char out[101];
+char x[TAI_PACK];
+
+int main(int argc, char **argv)
+{
+ struct tai t;
+ struct tai t2;
+ struct caltime ct;
+ struct caltime ct2;
+ int weekday;
+ int yearday;
+ int i;
+ double packerr;
+
+ if (leapsecs_init() == -1)
+ printf("unable to init leapsecs\n");
+
+ while (fgets(line,sizeof line,stdin))
+ if (!caltime_scan(line,&ct))
+ printf("unable to parse\n");
+ else {
+ caltime_tai(&ct,&t);
+ caltime_utc(&ct2,&t,&weekday,&yearday);
+ tai_pack(x,&t);
+ tai_unpack(x,&t2);
+ tai_sub(&t2,&t2,&t);
+ packerr = tai_approx(&t2);
+ for (i = 0;i < TAI_PACK;++i)
+ printf("%2.2x",(unsigned long) (unsigned char) x[i]);
+ if (packerr)
+ printf(" packerr=%f",packerr);
+ printf(" %03d %s",yearday,dayname[weekday]);
+ if (caltime_fmt((char *) 0,&ct2) + 1 < sizeof out) {
+ out[caltime_fmt(out,&ct2)] = 0;
+ printf(" %s",out);
+ }
+ printf("\n");
+ }
+ exit(0);
+}
diff --git a/src/tai/easter.c b/src/tai/easter.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <tai/caldate.h>
+
+char *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ;
+
+char out[101];
+
+int main(int argc, char **argv)
+{
+ struct caldate cd;
+ long day;
+ int weekday;
+ int yearday;
+ int i;
+
+ while (*++argv) {
+ cd.year = atoi(*argv);
+ if (cd.year > 0) {
+ caldate_easter(&cd);
+ day = caldate_mjd(&cd);
+ caldate_frommjd(&cd,day,&weekday,&yearday);
+ if (caldate_fmt((char *) 0,&cd) + 1 >= sizeof out) exit(1);
+ out[caldate_fmt(out,&cd)] = 0;
+ printf("%s %s yearday %d mjd %d\n",dayname[weekday],out,yearday,day);
+ }
+ }
+ exit(0);
+}
diff --git a/src/tai/leapsecs.c b/src/tai/leapsecs.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <tai/tai.h>
+#include <tai/leapsecs.h>
+#include <tai/caldate.h>
+
+/* XXX: breaks tai encapsulation */
+
+/* XXX: output here has to be binary; DOS redirection uses ASCII */
+
+char line[100];
+
+int main(int argc, char **argv)
+{
+ struct caldate cd;
+ struct tai t;
+ char x[TAI_PACK];
+ long leaps = 0;
+
+ while (fgets(line,sizeof line,stdin))
+ if (line[0] == '+')
+ if (caldate_scan(line + 1,&cd)) {
+ t.x = (caldate_mjd(&cd) + 1) * 86400ULL + 4611686014920671114ULL + leaps++;
+ tai_pack(x,&t);
+ fwrite(x,TAI_PACK,1,stdout);
+ }
+
+ exit(0);
+}
diff --git a/src/tai/leapsecs_add.c b/src/tai/leapsecs_add.c
@@ -0,0 +1,24 @@
+#include <tai/leapsecs.h>
+#include <tai/tai.h>
+
+/* XXX: breaks tai encapsulation */
+
+extern struct tai *leapsecs;
+extern int leapsecs_num;
+
+void leapsecs_add(struct tai *t, int hit)
+{
+ int i;
+ uint64 u;
+
+ if (leapsecs_init() == -1) return;
+
+ u = t->x;
+
+ for (i = 0;i < leapsecs_num;++i) {
+ if (u < leapsecs[i].x) break;
+ if (!hit || (u > leapsecs[i].x)) ++u;
+ }
+
+ t->x = u;
+}
diff --git a/src/tai/leapsecs_init.c b/src/tai/leapsecs_init.c
@@ -0,0 +1,11 @@
+#include <tai/leapsecs.h>
+
+static int flaginit = 0;
+
+int leapsecs_init()
+{
+ if (flaginit) return 0;
+ if (leapsecs_read() == -1) return -1;
+ flaginit = 1;
+ return 0;
+}
diff --git a/src/tai/leapsecs_read.c b/src/tai/leapsecs_read.c
@@ -0,0 +1,50 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+// extern int errno; // XXX Thread safety
+#include <tai/tai.h>
+#include <tai/leapsecs.h>
+
+struct tai *leapsecs = 0;
+int leapsecs_num = 0;
+
+int leapsecs_read()
+{
+ int fd;
+ struct stat st;
+ struct tai *t;
+ int n;
+ int i;
+ struct tai u;
+
+ fd = open("/etc/leapsecs.dat",O_RDONLY | O_NDELAY);
+ if (fd == -1) {
+ if (errno != ENOENT) return -1;
+ if (leapsecs) free(leapsecs);
+ leapsecs = 0;
+ leapsecs_num = 0;
+ return 0;
+ }
+
+ if (fstat(fd,&st) == -1) { close(fd); return -1; }
+
+ t = (struct tai *) malloc(st.st_size);
+ if (!t) { close(fd); return -1; }
+
+ n = read(fd,(char *) t,st.st_size);
+ close(fd);
+ if (n != st.st_size) { free(t); return -1; }
+
+ n /= sizeof(struct tai);
+
+ for (i = 0;i < n;++i) {
+ tai_unpack((char *) &t[i],&u);
+ t[i] = u;
+ }
+
+ if (leapsecs) free(leapsecs);
+
+ leapsecs = t;
+ leapsecs_num = n;
+}
diff --git a/src/tai/leapsecs_sub.c b/src/tai/leapsecs_sub.c
@@ -0,0 +1,28 @@
+#include <tai/leapsecs.h>
+#include <tai/tai.h>
+
+/* XXX: breaks tai encapsulation */
+
+extern struct tai *leapsecs;
+extern int leapsecs_num;
+
+int leapsecs_sub(struct tai *t)
+{
+ int i;
+ uint64 u;
+ int s;
+
+ if (leapsecs_init() == -1) return 0;
+
+ u = t->x;
+ s = 0;
+
+ for (i = 0;i < leapsecs_num;++i) {
+ if (u < leapsecs[i].x) break;
+ ++s;
+ if (u == leapsecs[i].x) { t->x = u - s; return 1; }
+ }
+
+ t->x = u - s;
+ return 0;
+}
diff --git a/src/tai/nowutc.c b/src/tai/nowutc.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <tai/leapsecs.h>
+#include <tai/tai.h>
+#include <tai/taia.h>
+#include <tai/caltime.h>
+
+struct taia now;
+struct tai sec;
+struct caltime ct;
+
+char x[TAIA_FMTFRAC];
+
+int main(int argc, char **argv)
+{
+ if (leapsecs_init() == -1) {
+ fprintf(stderr,"utcnow: fatal: unable to init leapsecs\n");
+ exit(111);
+ }
+
+ taia_now(&now);
+ x[taia_fmtfrac(x,&now)] = 0;
+
+ taia_tai(&now,&sec);
+ caltime_utc(&ct,&sec,(int *) 0,(int *) 0);
+
+ printf("%d-%02d-%02d %02d:%02d:%02d.%s\n"
+ ,ct.date.year
+ ,ct.date.month
+ ,ct.date.day
+ ,ct.hour
+ ,ct.minute
+ ,ct.second
+ ,x
+ );
+
+ exit(0);
+}
diff --git a/src/tai/tai_add.c b/src/tai/tai_add.c
@@ -0,0 +1,6 @@
+#include <tai/tai.h>
+
+void tai_add(struct tai *t, struct tai *u, struct tai *v)
+{
+ t->x = u->x + v->x;
+}
diff --git a/src/tai/tai_now.c b/src/tai/tai_now.c
@@ -0,0 +1,7 @@
+#include <time.h>
+#include <tai/tai.h>
+
+void tai_now(struct tai *t)
+{
+ t->x = 4611686018427387914ULL + (uint64) time((long *) 0);
+}
diff --git a/src/tai/tai_pack.c b/src/tai/tai_pack.c
@@ -0,0 +1,16 @@
+#include <tai/tai.h>
+
+void tai_pack(char *s, struct tai *t)
+{
+ uint64 x;
+
+ x = t->x;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x & 255; x >>= 8;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
diff --git a/src/tai/tai_sub.c b/src/tai/tai_sub.c
@@ -0,0 +1,6 @@
+#include <tai/tai.h>
+
+void tai_sub(struct tai *t, struct tai *u, struct tai *v)
+{
+ t->x = u->x - v->x;
+}
diff --git a/src/tai/tai_unpack.c b/src/tai/tai_unpack.c
@@ -0,0 +1,16 @@
+#include <tai/tai.h>
+
+void tai_unpack(char *s, struct tai *t)
+{
+ uint64 x;
+
+ x = (unsigned char) s[0];
+ x <<= 8; x += (unsigned char) s[1];
+ x <<= 8; x += (unsigned char) s[2];
+ x <<= 8; x += (unsigned char) s[3];
+ x <<= 8; x += (unsigned char) s[4];
+ x <<= 8; x += (unsigned char) s[5];
+ x <<= 8; x += (unsigned char) s[6];
+ x <<= 8; x += (unsigned char) s[7];
+ t->x = x;
+}
diff --git a/src/tai/taia_add.c b/src/tai/taia_add.c
@@ -0,0 +1,18 @@
+#include <tai/taia.h>
+
+/* XXX: breaks tai encapsulation */
+
+void taia_add(struct taia *t, struct taia *u, struct taia *v)
+{
+ t->sec.x = u->sec.x + v->sec.x;
+ t->nano = u->nano + v->nano;
+ t->atto = u->atto + v->atto;
+ if (t->atto > 999999999UL) {
+ t->atto -= 1000000000UL;
+ ++t->nano;
+ }
+ if (t->nano > 999999999UL) {
+ t->nano -= 1000000000UL;
+ ++t->sec.x;
+ }
+}
diff --git a/src/tai/taia_approx.c b/src/tai/taia_approx.c
@@ -0,0 +1,6 @@
+#include <tai/taia.h>
+
+double taia_approx(struct taia *t)
+{
+ return tai_approx(&t->sec) + taia_frac(t);
+}
diff --git a/src/tai/taia_fmtfrac.c b/src/tai/taia_fmtfrac.c
@@ -0,0 +1,31 @@
+#include <tai/taia.h>
+
+unsigned int taia_fmtfrac(char *s, struct taia *t)
+{
+ unsigned long x;
+
+ if (s) {
+ x = t->atto;
+ s[17] = '0' + (x % 10); x /= 10;
+ s[16] = '0' + (x % 10); x /= 10;
+ s[15] = '0' + (x % 10); x /= 10;
+ s[14] = '0' + (x % 10); x /= 10;
+ s[13] = '0' + (x % 10); x /= 10;
+ s[12] = '0' + (x % 10); x /= 10;
+ s[11] = '0' + (x % 10); x /= 10;
+ s[10] = '0' + (x % 10); x /= 10;
+ s[9] = '0' + (x % 10);
+ x = t->nano;
+ s[8] = '0' + (x % 10); x /= 10;
+ s[7] = '0' + (x % 10); x /= 10;
+ s[6] = '0' + (x % 10); x /= 10;
+ s[5] = '0' + (x % 10); x /= 10;
+ s[4] = '0' + (x % 10); x /= 10;
+ s[3] = '0' + (x % 10); x /= 10;
+ s[2] = '0' + (x % 10); x /= 10;
+ s[1] = '0' + (x % 10); x /= 10;
+ s[0] = '0' + (x % 10);
+ }
+
+ return 18;
+}
diff --git a/src/tai/taia_frac.c b/src/tai/taia_frac.c
@@ -0,0 +1,6 @@
+#include <tai/taia.h>
+
+double taia_frac(struct taia *t)
+{
+ return (t->atto * 0.000000001 + t->nano) * 0.000000001;
+}
diff --git a/src/tai/taia_half.c b/src/tai/taia_half.c
@@ -0,0 +1,12 @@
+#include <tai/taia.h>
+
+/* XXX: breaks tai encapsulation */
+
+void taia_half(struct taia *t, struct taia *u)
+{
+ t->atto = u->atto >> 1;
+ if (u->nano & 1) t->atto += 500000000UL;
+ t->nano = u->nano >> 1;
+ if (u->sec.x & 1) t->nano += 500000000UL;
+ t->sec.x = u->sec.x >> 1;
+}
diff --git a/src/tai/taia_less.c b/src/tai/taia_less.c
@@ -0,0 +1,12 @@
+#include <tai/taia.h>
+
+/* XXX: breaks tai encapsulation */
+
+int taia_less(struct taia *t, struct taia *u)
+{
+ if (t->sec.x < u->sec.x) return 1;
+ if (t->sec.x > u->sec.x) return 0;
+ if (t->nano < u->nano) return 1;
+ if (t->nano > u->nano) return 0;
+ return t->atto < u->atto;
+}
diff --git a/src/tai/taia_now.c b/src/tai/taia_now.c
@@ -0,0 +1,14 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <tai/taia.h>
+
+/* XXX: breaks tai encapsulation */
+
+void taia_now(struct taia *t)
+{
+ struct timeval now;
+ gettimeofday(&now,(struct timezone *) 0);
+ t->sec.x = 4611686018427387914ULL + (uint64) now.tv_sec;
+ t->nano = 1000 * now.tv_usec + 500;
+ t->atto = 0;
+}
diff --git a/src/tai/taia_pack.c b/src/tai/taia_pack.c
@@ -0,0 +1,20 @@
+#include <tai/taia.h>
+
+void taia_pack(char *s, struct taia *t)
+{
+ unsigned long x;
+
+ tai_pack(s,&t->sec);
+ s += 8;
+
+ x = t->atto;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x;
+ x = t->nano;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
diff --git a/src/tai/taia_sub.c b/src/tai/taia_sub.c
@@ -0,0 +1,21 @@
+#include <tai/taia.h>
+
+/* XXX: breaks tai encapsulation */
+
+void taia_sub(struct taia *t, struct taia *u, struct taia *v)
+{
+ unsigned long unano = u->nano;
+ unsigned long uatto = u->atto;
+
+ t->sec.x = u->sec.x - v->sec.x;
+ t->nano = unano - v->nano;
+ t->atto = uatto - v->atto;
+ if (t->atto > uatto) {
+ t->atto += 1000000000UL;
+ --t->nano;
+ }
+ if (t->nano > unano) {
+ t->nano += 1000000000UL;
+ --t->sec.x;
+ }
+}
diff --git a/src/tai/taia_tai.c b/src/tai/taia_tai.c
@@ -0,0 +1,6 @@
+#include <tai/taia.h>
+
+void taia_tai(struct taia *ta, struct tai *t)
+{
+ *t = ta->sec;
+}
diff --git a/src/tai/taia_unpack.c b/src/tai/taia_unpack.c
@@ -0,0 +1,20 @@
+#include <tai/taia.h>
+
+void taia_unpack(char *s, struct taia *t)
+{
+ unsigned long x;
+
+ tai_unpack(s,&t->sec);
+ s += 8;
+
+ x = (unsigned char) s[4];
+ x <<= 8; x += (unsigned char) s[5];
+ x <<= 8; x += (unsigned char) s[6];
+ x <<= 8; x += (unsigned char) s[7];
+ t->atto = x;
+ x = (unsigned char) s[0];
+ x <<= 8; x += (unsigned char) s[1];
+ x <<= 8; x += (unsigned char) s[2];
+ x <<= 8; x += (unsigned char) s[3];
+ t->nano = x;
+}
diff --git a/src/tai/trycpp.c b/src/tai/trycpp.c
@@ -0,0 +1,7 @@
+void main()
+{
+#ifdef NeXT
+ printf("nextstep\n"); exit(0);
+#endif
+ printf("unknown\n"); exit(0);
+}
diff --git a/src/tai/yearcal.c b/src/tai/yearcal.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <tai/caldate.h>
+
+char *montab[] = {
+ "January"
+, "February"
+, "March"
+, "April"
+, "May"
+, "June"
+, "July"
+, "August"
+, "September"
+, "October"
+, "November"
+, "December"
+} ;
+
+int main(int argc, char **argv)
+{
+ int year;
+ long daystart;
+ long dayend;
+ long day;
+ int weekday;
+ struct caldate cd;
+
+ while (*++argv) {
+ year = atoi(*argv);
+
+ cd.year = year;
+ cd.month = 1;
+ cd.day = 1;
+ daystart = caldate_mjd(&cd);
+ cd.year = year + 1;
+ dayend = caldate_mjd(&cd);
+
+ while ((daystart + 3) % 7) --daystart;
+ while ((dayend + 3) % 7) ++dayend;
+
+ for (day = daystart;day < dayend;++day) {
+ caldate_frommjd(&cd,day,&weekday,(int *) 0);
+ if (cd.year != year)
+ printf(" ");
+ else {
+ if (cd.month & 1)
+ if (cd.day < 10)
+ printf(" %d%c%d ",cd.day % 10,8,cd.day % 10);
+ else
+ printf("%d%c%d%d%c%d ",cd.day / 10,8,cd.day / 10,cd.day % 10,8,cd.day % 10);
+ else
+ printf("%2d ",cd.day);
+ if (weekday == 6) {
+ if ((cd.day >= 15) && (cd.day < 22))
+ printf(" %s %d\n",montab[cd.month - 1],year);
+ else
+ printf("\n");
+ }
+ }
+ }
+ printf("\n");
+ }
+
+ exit(0);
+}