This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. |
This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. |
#include
directives, followed by declarations, followed by the main program. A literate presentation is
So let's go ahead and define our #include
s, shall we?
localdefs.h
file contains whatever local definitions a particular wftk
installation needs: particularly the locations of the process definition repository and the
datasheet repository (remember that in this version both of these are simply directories in
the local filesystem, but that later we'll have much more flexible options for both locations.)
Next we define all our data structures and helper functions. XML and expat are rather core
to this. Under the rubric of "XML" we'll define our central XML data structures and the
functions we'll use to work with them. The use of the expat parser to parse XML text into
these structures will be in a separate section because it's not trivial.
struct
definition (_element
,
_attr
, and _list
respectively) and a pointer typedef (XML
,
ATTR
, and ELEMENTLIST
). My apologies that the names don't match up,
but it makes sense to me: the struct
names reflect a lower-level appreciation
for what the objects are, while the typedefs relect a higher-level view of what they're to be
used for. Yeah. Anyway, that's my left-brain rationalization for an essentially right-brained
nomenclature.
xml_read()
function, which uses expat
to parse an XML file. That's on the next page because that makes it more convenient to
document the handlers used.
So here's my budding XML manipulation API:
xml_write
)
and the other writes just the content of the element (xml_writecontent
).
xml_firstelem
which is just lie xml_first
except that it doesn't see plain text elements.
startElement
handler, then, does a great deal of the work of creating
XML data structures. The userData
parameter points to the immediate parent
of the node being encountered. When we open a new node, we allocate the data structure
and copy attributes, append the new node to its parent, then we set userData to point to the
new node -- when the element closes, we move userData up the chain back to the parent.
In the case of an empty element, expat fortunately calls first the open handler, then the
close handler, so whether we have an explicitly empty element or not doesn't matter.
It's astounding how much simpler this startElement
is than the corresponding
handler in xmltools!
next
member, of course, is to allow commands to be strung together into a linked
list. Note that this "command language" is really simple, because the "commands" are really
notifications from the task manager that something has happened which the engine needs to act
upon. Maybe "messages" would be a better terminology -- but nope, they're commands now.
Live with it.
Well, so the other thing we want to define here is a handy little function to tack a command
onto a list of commands.
vprintf
.
The output stream is always on stdout
.
datasheet_dirty
flag is set, then the datasheet will be written when the
interpreter finishes.
So let's find our process definition. The procdef identifier we're given is a general name;
we have to find the current version of that procdef and mark that version as the one we're
actually using. Otherwise if changes are made, things are going to get rapidly out of whack.
So in the process definition repository, there is a file procdef_versions.txt
for procdef ID 'procdef'. That consists of lines of text, the first tab-delimited field of
which is the number of a version. The last line, therefore, identifies the current version.
For version 2a
of procdef procdef
, the actual definition XML will be
in the file procdef_2a.xml
. That's what we'll write into our datasheet as our
governing procdef.
And yeah, I'm using a goto here. Sue me.
script
command allows us to use command files (or stdin) to
signify several events at once. Note that all commands in a single run must be
applied to the same process, and that process is specified on the command line. The commands
to be found in a script file lack the process ID, because it's already been specified.
In keeping with the idea that these commands are really state transitions, we will simply
tack commands from script files onto the end of the current list. Thus we really don't
care that scripts run in order -- all this is conceptually happening simultaneously.
struct
in a daemon version of the core engine, but for a command-line
version, globals make more sense.
Our three chief data structures are the process definition, the datasheet, and the command stack.
command_add
to start off the
list.
The big switch
is horrendous but I'm too rushed to think of an elegant way to
do this -- get yer name here, folks! Step right up and tell me how it's done!