; IPXLIB.TXT -- Documentation for IPXLIB version 0.20
; by Allen Brunson  06/01/94


******************************************************************************
*                                                                            *
***  Introduction                                                          ***
*                                                                            *
******************************************************************************

IPXLIB is a library of routines written in assembler for communication
amongst multiple PCs using the IPX protocol, the low-level language spoken
by Novell's NetWare.  It is designed to be called from C, but if you know
what you're doing, you can get it to work with C++, Pascal, or any other
language that can be made to support the C calling convention and works
with standard object file formats.  Since IPXLIB is written in assembler,
its impact on the size of your program is minimal; normally it will only add
about 10k to your program's memory requirements, depending on the size and
number of communication buffers you configure it for.

IPX is a fast and very low-level protocol, designed for maximum speed.  It
does not guarantee delivery, but Novell claims IPX packets properly arrive
at their destinations about 95 percent of the time.

IPX can be quite cumbersome to use.  Given its orientation, a great many
details of NetWare networks must be known by the programmer to use it
properly.  This library takes care of almost all of those details, allowing
you to concentrate on your program's main function rather than worrying
about IPX specifics.

This library is geared towards the needs of smaller projects, where perhaps
up to 20 or so PCs will be exchanging data.  Larger projects would probably
be best served if the programmer were to learn how to make IPX calls
directly, or by some other product.

This library does not offer any NetWare-related services, such as logging in,
logging out, copying files, getting lists of logged-on users, and so on.
Its only purpose is to allow programs running on multiple PCs to share data.

This document covers issues specific to using IPXLIB.  It assumes that you
are familiar with NetWare and IPX concepts.  It would be a good idea to
read NETWORK.TXT before tackling this file.

The file IPXTEST.TXT explains the IPXLIB demo program, IPXTEST.  IPXTEST
uses every IPXLIB routine, so it is a good reference for implementation
details.


Beta Version Restrictions
-------------------------

This document is written to accompany IPXLIB version 0.20, the second beta
release.  There are currently no known bugs.  The biggest restriction is
that IPXLIB will work only with PCs that are on the same network segment.
This limitation will be removed in the registered version.



******************************************************************************
*                                                                            *
***  Adding IPXLIB To Your Program                                         ***
*                                                                            *
****************************************************************************** 

IPXLIB currently only supports Microsoft and Borland compilers (or others
that can deal with standard object files) and real mode executables.  I am
currently working on a version for the Watcom 32-bit compiler, but I don't
have a release date yet.  If you are developing a protected-mode program,
contact me, we might be able to work something out if you know how to make
assembler work in your environment.  Also, I will be selling the IPXLIB
source code, but at much higher cost than the already assembled version.

IPXLIB is contained in one of four .LIB files, depending on your chosen
memory model.

  IPXT.LIB   Tiny model
 IPXSC.LIB   Small and Compact models
 IPXML.LIB   Medium and Large models
  IPXH.LIB   Huge model

Include one of these .LIB files in your project.

IPXLIB always uses far pointers for passed parameters, so there's no
distinction between Small and Compact (near calls) or between Medium and
Large (far calls).  The Tiny model code never makes direct references to
the data segment, so a program built with IPXT.LIB can be turned into a
.COM file.  The Huge memory model has special considerations for the data
segment register, so it requires its own .LIB file.

Include the file IPX.H in any module that will be calling the IPXLIB
routines.  It is important that you follow this rule: the IPX routines all
take far pointers to data structures, and pointers in the small data
memory models are by default near, so messy crashes will result if you don't
perform this step.

Defines in IPXCFG.H are used to set the number of send and receive packet
buffers, the number of bytes per packet, the socket number, and the number
of hops to allow.  Make a copy of this file for each program that will use
IPXLIB and then include IPXCFG.H in each source file that will call IPXLIB
routines.  See a later section called "Configuring IPXLIB With Defines in
IPXCFG.H" for more details.

The routines in IPXUTIL.C can be used to work with IPXADDRFULL structures.
While I was writing the demo program I had to manage the addresses of many
PCs; each requires an IPXADDRFULL structure.  Since this is a large
hunk of data with several unwieldy fields, I wrote routines to copy one
address to another, convert an address to a string, compare two addresses for
equality, and so on.  IPXUTIL.C is the result.  See a later section in this
file for reference.



******************************************************************************
*                                                                            *
***  An Overview Of IPXLIB Structures                                      ***
*                                                                            *
******************************************************************************

You don't need to know a whole lot about IPX structures to use IPXLIB; it
pretty much takes care of the details for you.  However, a general overview
of the five structures defined in IPX.H seems appropriate.

struct IPXADDR: This is the network, node, and socket number for a PC.  It
is enough to uniquely identify a process on a PC, but not enough info to
be able to send a packet to any process on any PC anywhere on an
internetwork.  Note that the socket field is hi-lo (Motorola) format.  This
structure is defined only for inclusion in the IPXPKTHDR structure.

struct IPXADDRFULL: This is a superset of the IPXADDR structure, adding an
immediate address field; therefore it contains enough address info to send
a packet anywhere.  I kind of "made up" this structure; nowhere in "official"
IPX do you find all four of these fields grouped together; however it seems
quite convenient for storing the addresses of other PCs you wish to
communicate with.  IPXTEST uses a group of these structures to keep track
of all other IPXTEST users; it is assumed that IPXLIB users will employ a
similar scheme.  Keep in mind that the socket field MUST be maintained in
high-low byte order or sends and receives will not complete properly.
Routines for manipulating IPXADDRFULLs are given in IPXUTIL.C.

struct IPXEVENT: This is another structure I "made up."  IPXLIB contains
two ESRs (Event Service Routines), one which IPX calls when a send is
completed, the other when a receive is completed.  Each of these ESRs
maintains a queue of events that it has been notified of (except that the
send ESR simply throws away events if the send completed without errors).
Each event is stored in a queue of IPXEVENT structures.  These queues are
maintained and processed entirely by IPXLIB functions, so you needn't
worry about this structure at all.

struct IPXPKTHDR: This is the 30-byte packet header that all IPX packets
must have.  You don't need to directly concern yourself with it; IPXLIB
will fill out the fields in IPX packet headers where necessary.

struct ECB: This is the standard IPX Event Control Block.  It contains
pointers to two "fragments," the packet header and the packet data.  An
ECB can actually have fields for any number of "fragments," but IPXLIB
always uses two, one for the header and one for the data.  Again, IPXLIB
will take care of filling out ECB fields, so you don't have to worry about
them.



******************************************************************************
*                                                                            *
***  Configuring IPXLIB with Defines In IPXCFG.H                           ***
*                                                                            *
******************************************************************************

IPXLIB is tailored to the needs of your program by setting the defines in
IPXCFG.H.  In this way, you determine how many ECB/packet pairs are
available for sending and receiving, the size of the packets, and the socket
number.  (These defines are then used as parameters to ipxStart(), described
below.)  You will also indirectly be setting how much memory the
communication data will take.

Set IPXSOCKET to the socket number you want your program to use.  This
value will be passed as a parameter to ipxStart().  Novell states that
"dynamic" socket numbers, that is, ones not officially reserved, start at
4000h and end at 7FFFh.  The default socket number is 51E7h, but you should
open up the file and change it right now, because everybody else who uses
IPXLIB will also be getting that as the default, and two programs running on
the same network with the same socket number will bump heads.  See the file
NETWORK.TXT for more information on socket numbers.

Set IPXHOPS to the maximum number of "hops" away that IPXLIB will look for
other PCs to communicate with.  The default is 3.  This value will be used
for a future routine that will find and manage bridges.  Setting IPXHOPS to
0 will mean that your program can only communicate with other PCs on the
same network segment, but will provide maximum speed and the least amount of
overhead.  Setting it to 1 will allow your program to communicate with PCs
accessible by going through only one bridge; setting it to 2 means IPXLIB
will look as far away as two bridges; and so on.  Note that for each higher
setting for IPXHOPS, the potential number of network segments that may be
encountered increases exponentially, so don't set this value any higher
than it needs to be.  Setting IPXHOPS higher than 6 may cause unpredictable
behavior.  See the file NETWORK.TXT for more information on bridges and hops.
(Currently bridge-hopping is not implemented, so this value is not yet used
for anything and is always effectively 0.)

Set IPXRECVCNT to the total number of ECB/packet pairs you wish to make
available for receiving incoming packets.  The default is 20.  The minimum
value is 0; the maximum is 250.  It is passed as a parameter to ipxStart(),
and is used to set the number of receive ECBs, packet headers, packet data
buffers, and the receive event queue size.  You can make your program
completely "deaf" to incoming packets by setting this value to 0.  If your
program will only receive packets infrequently and you're prepared to
retrieve them quickly, you might get by with setting this value to 1.  If you
anticipate receiving lots of data and your program's main loop might not get
around to processing incoming packets for several seconds, set this value to
a high number, perhaps as high as 200.  It's far better to set this number
too high than too low, because if a data packet arrives and all the ECBs are
already full, IPX will simply throw it away without so much as an error.
Each ECB/packet pair requires one ECB (48 bytes), one packet header (30
bytes), one entry in the receive event queue (4 bytes), and one packet
buffer, the size of which is set by IPXDATASIZE (see below).

Set IPXSENDCNT to the total number of ECB/packet pairs you wish to use for
sending packets.  The default is 8.  The minimum value is 0; the maximum is
250.  It is passed as a parameter to ipxStart().  It is used to set the
number of send ECBs, send packet headers, send packet buffers, and the size
of the send event queue.  You can make your program listen-only by setting
this value to 0.  Generally this number can be lower than IPXRECVCNT because
most network programs send less data than they receive and send requests are
usually carried out very quickly.  Even if all send ECBs are in use when you
want to do another send, you'll simply get an error; you're free to retry the
send a few milliseconds later.  Setting this value to 1 is plenty enough for
programs with light send needs.  Even if your program does nothing but sit in
a loop sending packets, you probably shouldn't need more than 20.  Each send
ECB/packet pair takes the same number of bytes as a receive ECB/packet pair
(see above).

Set IPXDATASIZE to the number of bytes of data you want to be able to send
(and receive) in each packet.  (Don't take the 30-byte IPX packet header
into account here; this number is only for data bytes.)  The default is 256.
The minimum size is 0 and the maximum is 546.  It's entirely possible to
send packets with zero data bytes; the receiver will know nothing more than
the sender's address, but in some contexts this might be enough.  If you'll
be sending lots of little packets, set IPXDATASIZE to a small number; big
streams of data would be better-served by large packet sizes (but this will
require more memory).  It's possible to send packets that are smaller than
IPXDATASIZE, but it's never possible to send one that's bigger than this
value.  The amount of memory taken up by this setting depends on the values
of IPXRECVCNT and IPXSENDCNT; each ECB/packet pair used for both sending and
receiving will have one buffer of this size.



******************************************************************************
*                                                                            *
***  ECB Usage Statistics                                                  ***
*                                                                            *
******************************************************************************

While I was writing IPXTEST, I found myself wondering how many ECBs the
program was actually using.  It had the default total of 8 send ECBs, but
was there ever a time when that many were in use?  The IPXLIB procedure for
sending packets will report an error if there are no send ECBs free, but
that doesn't tell you much.  Receive ECBs are even harder to gauge; if you
run out, IPX simply starts throwing packets away with no errors.

That's when I built ECB usage statistics into IPXLIB.  It keeps track of
how many receive and send ECBs are in use at any given moment in variables,
and makes two other word-sized variables available to your program,
"ipxRecvMax" and "ipxSendMax," that contain the maximum number of receive
and send ECBs that have been in use so far.  For the purposes of the
statistics, a send ECB is considered "in use" between the time when you post
it to IPX and the time that IPX notifies IPXLIB that it has been sent; a
receive ECB is considered "in use" beginning at the moment when IPX uses it
to receive a packet and is no longer in use once you collect that packet and
the ECB is put back into service.  All the IPXLIB procedures update these
variables dynamically as packets are sent and received, when the ESRs are
notified that sends and receives have completed, and so on, so they will
always be correct.

You can reset the values of "ipxRecvMax" and "ipxSendMax" to zeroes at any
time you like to start over again.

I ran IPXTEST with "flurry mode" turned on (which means it continually
sends out packets as fast as it can) and checked the statistics.  What I
discovered is that it is very rare for IPXTEST to need more than one send
ECB.  If the main loop is jiggered to favor sends over receives, then it
might need as many as four, but not usually.

Receive ECBs are a different matter.  I discovered that normally the program
gets along just fine with four or fewer receive ECBs in "flurry mode," but
that every now and then some event will steal time normally used for
processing incoming packets, like dealing with a lot of keyboard input, and
the receive ECBs will spike up to a value as high as 50 or 60.  In fact, no
matter how high I set the receive value, I could eventually get the program
into a jam bad enough that it would use every last one of its receive ECBs.
That's bad news, because it almost certainly means that there were at least
one or two more packets that were lost.  This is a good argument for writing
your program to send acknowledges for all packet transactions.



******************************************************************************
*                                                                            *
***  IPXLIB Error Values                                                   ***
*                                                                            *
******************************************************************************

IPXLIB routines generally return zero to indicate no error or some positive
error value otherwise (some routines take a pointer to a word-sized variable
and return errors there).  Possible error values are defined in IPX.H along
with a brief description for each.  All defines begin with the letters
"ipxe", or "IPX Error."  ipxeNOERR is defined as 0; other values begin at 2
and head upward from there.  Further treatment of the error values is given
in the descriptions of the IPXLIB routines.

It is strongly recommended that you use the error defines in your program
rather than the numerical values of the defines, because I reserve the right
to change the numerical values in future versions of IPXLIB.

It was my original intention to use IPX's own error return values, but this
proved to be unworkable.  Almost every IPX call can return the error code
FFh, for instance, but it means something different in every case.  I wanted
one set of error codes that always meant the same thing, so procedures in
IPXLIB all "translate" IPX's errors into one of the IPXLIB error values.

Note that, in some very rare cases, you may get an error return value other
than the ones listed in IPX.H.  Novell has released many, many different
versions of the IPX TSRs over the years, and I wouldn't be surprised if some
of them return error values not given in Novell's documentation.

Naturally IPXLIB can only translate error values that it knows about.  It is
programmed so that if IPX returns an unrecognized error value, it will pass
the unknown value straight through to your program untranslated.  Chances
are good that such an error would be in the E0h to FDh range; IPX calls
tend to return high-numbered errors.  If you get such an error value, I'd
be interested in hearing about it so I can incorporate it into future
versions of IPXLIB.



******************************************************************************
*                                                                            *
***  Using The Routines in IPXLIB                                          ***
*                                                                            *
******************************************************************************

The following is a reference guide for all the routines in IPXLIB that your
program can use.  This is NOT the place to start if you don't understand the
concepts involved.  Begin by reading NETWORK.TXT, then the earlier parts of
this file.  Use this section as a day-to-day reference for the routines.



ipxAddrLocal()
--------------

Prototype: word ipxAddrLocal(struct IPXADDRFULL far *ipxAddr);

This procedure gets the IPX address of the PC that your program is running
on.  It fills in all the fields of the IPXADDRFULL structure, including
the socket number and the immediate address, which will be the same as the
node address.

Return values:

  * ipxeNOERR: The address was collected without errors.

  * ipxeIPXNOTSTARTED: ipxStart() has not yet been called; the local
    address can't be retrieved until it is.



ipxLibVer()
-----------

Prototype: word ipxLibVer(void);

This procedure gets the version number of IPXLIB.  It is the only IPXLIB
function that can be called before ipxStart() or after ipxStop().  The major
version number is returned in the high byte and the minor version in the low
byte.  For example, a return value of 0100h would mean major version 1, minor
version 0, or 1.0.



ipxRecvChk()
------------

Prototype: word ipxRecvChk(void);

This procedure checks to see if any packets have been received and are
waiting to be picked up.  If there are, you can retrieve them with
ipxRecvPkt().

Return values:

  * FALSE: No packets are waiting.

  * TRUE: At least one packet is waiting.

  * ipxeIPXNOTSTARTED: ipxStart() hasn't been called; it isn't possible
    to check for packets yet.

If IPXRECVCNT is 0, then this procedure will always indicate that no packets
are waiting.



ipxRecvPkt()
------------

Prototype: word ipxRecvPkt(void far *data, word dataSize,
                           struct IPXADDRFULL far *ipxAddr,
                           word far *pktSize, word far *error);

This procedure checks to see if a packet has arrived, and if it has, it
passes the data in the packet and the IPX address of the PC it was
received from to your program.

Parameters:

  * void far *data: A pointer to your buffer for receiving the data.

  * word dataSize: The size, in bytes, of your buffer.

  * struct IPXADDRFULL far *ipxAddr: A structure for receiving the
    address of the sending PC.

  * word far *pktSize: The address of a word that will receive the total
    size of the data in the packet.

  * word far *error: The address of a word that will receive the error
    code associated with this packet.

Return values:

  * FALSE: No packets are waiting and nothing was copied to your data
    buffer or ipxAddr structure.  "pktSize" is zero.  "error" is also
    zero, unless ipxStart() hasn't yet been called.

  * TRUE: A packet has been received.  The sending address has been
    copied to ipxAddr and "error" has been set accordingly.  If "error"
    is set to ipxeNOERR (zero) or ipxeSOCKETNOTOPEN, a packet has been
    copied to your buffer and "pktSize" has been set to the number of
    data bytes received.  If "error" has some other nonzero value, no
    packet data has been copied and "pktSize" is set to zero.

Return values for "error:"

  * ipxeNOERR: A packet was received and copied to your buffer without
    any errors.

  * ipxeIPXNOTSTARTED: ipxStart() has not been called so no packets can
    be received.

  * ipxeRECVPKTTOOBIG: A packet was received but it was too big to fit
    in the buffer associated with the ECB (in other words, it was larger
    than IPXDATASIZE), so IPX threw it away.  No data was copied to your
    buffer, but your IPXADDRFULL structure has been filled in with the
    sender's address.

  * ipxeRECVCANCEL: IPX reports that the receive event was canceled.
    No packet data was copied.  The sender's address has been copied, but
    is probably meaningless in this case.  This should never happen under
    normal circumstances.

  * ipxeSOCKETNOTOPEN: The packet was received okay and has been copied
    to your buffer, as well as the sender's address.  While IPXLIB was
    attempting to put the ECB back into service to receive more packets,
    IPX reported that the socket referenced by the ECB is not open.  This
    should never happen under normal circumstances.

If the size of your buffer isn't large enough to hold all the received data
(this can only happen if your buffer is smaller than IPXDATASIZE), then as
much data as would fit was copied (and no error will be returned).  You can
tell this has happened if the value of "pktSize" is greater than the value
you passed as "dataSize."

If IPXRECVCNT is 0, then this procedure will always indicate that no packets
have been received.



ipxSendChk()
------------

Prototype: word ipxSendChk(void);

This procedure checks to see if any errors were encountered while trying
to send packets, and therefore whether those packets are waiting around
for you to pick them up.  If there are unsent packets, you can pick them up
with ipxSendErr().  This should be done periodically, because ECB/packet
pairs that were used for unsuccessful sends are unusable until retrieved
with ipxSendErr().

Return values:

  * FALSE: No undelivered packets are waiting.

  * TRUE: At least one undelivered packet is waiting.

  * ipxeIPXNOTSTARTED: ipxStart() hasn't been called; it isn't possible
    to check for send packet errors yet.

If IPXSENDCNT is 0, then this procedure will always indicate that no send
errors are waiting.



ipxSendErr()
------------

Prototype: word ipxSendErr(void far *data, word dataSize,
                           struct IPXADDRFULL far *ipxAddr,
                           word far *pktSize, word far *error);

This procedure checks for IPX send errors.  It is unlikely that you'll get
any, but if you do, this routine in effect gives your packet back to you so
you can retry the send or take other steps as necessary.  It should be called
every now and then even if you don't want to process send errors, because a
send ECB/packet pair that had an error is taken out of service indefinitely
until you call ipxSendErr() to free it up.  If all send ECBs are taken out
of service due to errors, then you won't be able to send any more data.

Parameters:

  * void far *data: A pointer to your buffer that will receive the
    packet that couldn't be sent (if an error is reported).

  * word dataSize: The size of your buffer, in bytes.

  * struct IPXADDRFULL far *ipxAddr: A structure to receive the address
    of the PC that the packet should have been sent to.

  * word far *pktSize: A pointer to a word to receive the size of the
    data that is being returned.

  * word far *error: A pointer to a word to receive the error value
    associated with the packet.

Return values:

  * FALSE: No undelivered packets are waiting.  "error" will be set to
    ipxeNOERR (zero) unless ipxStart() hasn't been called.

  * TRUE: An undelivered packet has been copied to your buffer, the
    total size has been set in pktSize, and the intended recipient's
    address has been copied to ipxAddr.  The ECB with the error is
    returned to service, ready to be used for the next send request.

Return values for "error:"

  * ipxeNOERR: There are no undelivered packets to be processed.

  * ipxeIPXNOTSTARTED: ipxStart() has not been called.

  * ipxeSENDBADROUTE: The packet was undeliverable.  This can happen
    if you send a packet to yourself (broadcasts count as "sending a
    packet to yourself") and there are no free receive ECBs.  If the
    packet was to another PC, then IPX could not find a route to the
    destination node; this probably indicates a problem with the
    immediate address.

  * ipxeSENDCANCEL: IPX reported that the send event was canceled.  This
    shouldn't happen under normal circumstances.

  * ipxeSENDPKTBAD: The packet was "malformed," to use Novell's term.
    It was either too short, too long, or the fragment count was wrong.
    IPXLIB takes care of all this, so this error should never be
    returned under normal circumstances.

  * ipxeSENDNETFAIL: IPX reports that the local network hardware or the
    network itself has failed.

If IPXSENDCNT is 0, then this procedure will always indicate that no send
errors are waiting.



ipxSendPkt()
------------

Prototype: word ipxSendPkt(void far *data, word dataSize,
                           struct IPXADDRFULL far *ipxAddr);

This procedure posts a send request with IPX.  No error associated with
the send will be returned, because ipxSendPkt() will return before IPX
has actually sent the packet.

Parameters:

  * void far *data: A pointer to your buffer filled with packet data to
    be sent.

  * word dataSize: The number of bytes in your buffer to be sent.  This
    does not have to be the same as the size of the buffer, if you want
    to send fewer bytes.

  * struct IPXADDRFULL far *ipxAddr: The full IPX address of the PC to
    send the packet to.  If you set the node address and immediate
    address to FFFFFFFFFFFFh, the packet will be sent to all nodes on
    the selected network segment.

Return values:

  * ipxeNOERR: The request was posted without errors.  This does NOT
    mean that the packet has been sent.

  * ipxeIPXNOTSTARTED: ipxStart() has not been called.

  * ipxeNOFREESENDECB: All send ECBs are currently in use, waiting to be
    sent by IPX (or else IPXSENDCNT is set to 0, which shouldn't be true
    if your program ever sends any packets).  You can retry the send at
    some point later in time.  If you get this error during testing, you
    can increase the number of send ECBs by setting the define IPXSENDCNT
    in IPXCFG.H to a higher value.

If the value of "dataSize" given is bigger than the value of IPXDATASIZE,
then you've given more data than the procedure can send; the number of
bytes sent will be truncated to IPXDATASIZE and no error will be returned.

The procedure ipxSendErr() should be called occasionally to check for send
errors.  If all packets are sent successfully, then ipxSendErr() will return
nothing, but keep in mind that this does NOT mean that the packet was
successfully received; it could have been lost in transit.  IPX does not
guarantee that all packets sent will be received.

IPX allows you to "send packets to yourself," so to speak.  You could call
ipxSendPkt() to send some data, setting "ipxAddr" to your own IPX address,
and then receive that same packet with ipxRecvPkt().  IPX is smart enough
to figure out that the packet doesn't have to go out onto the network and
merely copies it in memory.  This can be useful for debugging.

If a program "broadcasts" a packet to all addresses on its own network
segment by sending to node address FFFFFFFFFFFFh, then the program will
receive a copy of its own sent packet.



ipxStart()
----------

Prototype: word ipxStart(word recvCnt, word sendCnt,
                         word dataSize, word socket,
                         void far *memPtr, word memSize);

Call this as the very first IPX-related routine in your program.  No other
IPX functions (except ipxLibVer()) may be used until ipxStart() has been
called.  It performs the following functions:

  * It checks to make sure that the IPX driver is loaded.  If not, it
    returns with an error code.  If IPX is available, it saves the entry
    point used to make far calls to the IPX driver, which is needed by
    all other IPXLIB routines except ipxLibVer().

  * It opens the IPX socket number given as the parameter "socket"
    for sending and receiving packets.

  * It registers all receive ECBs and their associated packet buffers
    with IPX, making them available to receive incoming data packets.

Parameters:

  * word recvCnt: The number of receive ECB/buffer pairs to use.  Legal
    values are 0 to 250.  It is suggested that you use the define
    IPXRECVCNT in IPXCFG.H rather than passing a number directly.  Each
    receive ECB/buffer pair will require IPXCOMMSIZE bytes (a define
    in IPX.H).

  * word sendCnt: The number of send ECB/buffer pairs to use.  Legal
    values are 0 to 250.  It is suggested that you use the define
    IPXSENDCNT in IPXCFG.H rather than passing a number directly.  Each
    send ECB/buffer pair will require IPXCOMMSIZE bytes.

  * dataSize: The maximum number of data bytes per packet.  Legal values
    are 0 to 546.  It is suggested that you use the define IPXDATASIZE
    in IPXCFG.H rather than passing a number directly.

  * void far *memPtr: This is a far pointer to an allocated block of
    memory that IPXLIB will use for ECBs, buffers, queues, and so on.
    This can be memory allocated from the near heap with malloc(), from
    the far heap with farmalloc(), a pointer to a block of memory in an
    initialized or uninitialized data segment, or just about anything
    else, just so long as it's big enough to hold everything (see below).
    It doesn't have to be set to all zeroes; IPXLIB does that itself.
    The memory block must be less than 64k.

  * word memSize: The size of the memory block pointed to by memPtr.

Return values:

  * ipxeNOERR: All operations were completed without errors.

  * ipxeNOIPX: The IPX driver TSR (or TSRs) are not loaded.  IPX
    communication is not possible.

  * ipxeIPXSTARTED: ipxStart() has already been called.  It should not
    be called twice.

  * ipxeBADCOMMPARMS: One of the communication parameters given was set
    to an illegal value.  All are checked for validity except "socket."
    This value will be returned if "memPtr" is NULL.

  * ipxeMEMTOOSMALL: The value of memSize indicates that the memory
    block given is not big enough to hold all the communication data
    (see below).

  * ipxeSOCKETTABLEFULL: The selected socket cannot be opened because
    the IPX driver's socket table is full.  IPX defaults to allowing up
    to 20 open sockets.  To increase this amount, add the line
    "IPX SOCKETS = xx" to the NET.CFG file, where "xx" is the number of
    sockets required.  Sockets use some of the PC's memory, so don't
    increase the number any higher than necessary.

  * ipxeSOCKETOPEN: The requested socket was already open; ipxStart()
    did not complete.  Some other program is probably using the socket.
    This can also happen if the user "crashes out" of your program, so
    ipxStop() was never called and the socket was left open, and then
    the user tries to start it again.  In this case, a reboot will fix
    the trouble.

  * ipxeSOCKETNOTOPEN: While setting up the listen ECBs to receive
    incoming packets, IPX reported that the selected socket was not open,
    despite the fact that the socket was opened earlier with no error.
    It is extremely unlikely that this error will be reported under
    normal circumstances.

The formula for determining how much memory to pass to ipxStart() is as
follows:

  (IPXRECVCNT + IPXSENDCNT) * IPXCOMMSIZE

This is the total number of send and receive ECB/buffer pairs times the
amount of memory for each, IPXCOMMSIZE.  This is the total overhead bytes
for each plus IPXDATASIZE.  IPXCOMMSIZE is defined in IPX.H.  The user
must ensure that the total amount of memory needed is less than 64k.  If
it's greater, reduce the size of one or all of IPXRECVCNT, IPXSENDCNT, or
IPXDATASIZE, defined in IPXCFG.H, until the required size is less than
64k.

There is no point in passing more memory than the amount given by this
formula.  IPXLIB will not use any excess memory.



ipxStop()
---------

Call this as the last IPX function in your program.  If you end your program
without calling it, your computer may well crash later on when IPX tries to
store an incoming packet in a memory buffer that no longer belongs to you.

ipxStop() performs the following function:

  * It closes the IPX socket.  This has the effect of canceling any
    pending send requests and all receive ECBs.  The IPX close socket
    function never returns an error, so no form of IPX error will be
    passed to your program.

Return values:

  * ipxeNOERR: The procedure completed without errors.

  * ipxeIPXNOTSTARTED: ipxStart() was never called, so there is nothing
    to stop.

If memory was allocated for ipxStart() (as opposed to passing a pointer to
an area of a data segment), then the memory should be freed after the call
to ipxStop().



******************************************************************************
*                                                                            *
***  IPXUTIL Routines                                                      ***
*                                                                            *
******************************************************************************

Programs using IPXLIB will almost certainly have to keep track of at least a
few other PCs out on the network.  This requires keeping track of those PC's
IPX addresses, which are best kept in IPXADDRFULL structures.  You might
have to copy one address to another, compare two addresses to see if they're
the same or to sort a list, or print an address to the screen.  Since an
IPXADDRFULL structure is large and complicated, these sorts of tasks are made
more difficult.  This is exactly the problem I found myself up against when
I wrote IPXTEST.  The IPXUTIL routines are the solution I came up with.

The IPXUTIL routines are written in C and the source code is included in the
file IPXUTIL.C.  You can use or modify this code as you see fit.



ipxAddrBrd()
------------

Prototype: void ipxAddrBrd(struct IPXADDRFULL *ipxAddr);

This procedure sets the node address and immediate address fields in an
IPXADDR structure to FFFFFFFFFFFFh, which makes the address suitable for
broadcasting packets to all PCs on a given network segment.  It does
nothing at all to the network and socket fields, so those values should
already be set before performing this call.

A convenient way to set up an IPXADDRFULL structure suitable for
broadcasting packets on your local segment is to first get your local
address from the IPXLIB procedure ipxAddrLocal(), then pass that address
to ipxAddrBrd() to set the node and immediate address fields.

Note that if you broadcast a packet to all PCs on your network segment,
you yourself will also get a copy of that packet.



ipxAddrCmp()
------------

Prototype: word ipxAddrCmp(struct IPXADDRFULL *ipxAddr1,
                           struct IPXADDRFULL *ipxAddr2)

This procedure compares the network, node, and socket numbers of two
IPXADDRFULL structures.  It can be used to sort addresses into numerical
order or simply to determine if two addresses are identical.  It returns
-1 if the first address is less than the second, 0 if the addresses are
the same, or 1 if the first address is greater than the second.



ipxAddrCpy()
------------

Prototype: void ipxAddrCpy(struct IPXADDRFULL *ipxAddrDst,
                           struct IPXADDRFULL *ipxAddrSrc)

This procedure copies the entire contents of one IPXADDRFULL structure
to another.



ipxAddrStr()
------------

Prototype: char *ipxAddrStr(char *str, struct IPXADDRFULL *ipxAddr)

This procedure creates a string representation of the network and node
address in an IPXADDRFULL structure in the string pointed to by "str"
in the format xxxxxxxx:yyyyyyyyyyyy, where the x's are the network number
and the y's are the node number.  The string given as "str" must contain at
least 22 bytes.  The network and node are all that's needed to uniquely
identify one PC from any other, so many of NetWare's own utilites use
this format for displaying addresses.  It returns a pointer to the string.



ipxAddrStrLong()
----------------

Prototype: char *ipxAddrStrLong(char *str, struct IPXADDRFULL *ipxAddr)

This procedure creates a string representation of all four components of
the IPXADDRFULL structure, all separated by colons.  The string given as
"str" must contain at least 40 bytes.  This is probably not too useful
for production programs, but it sometimes comes in handy for debugging.  It
returns a pointer to the string.



******************************************************************************
*                                                                            *
***  The Future of IPXLIB                                                  ***
*                                                                            *
******************************************************************************

As I write this, I expect IPXLIB is about two weeks away from completion.
The shareware distributable version will be much like this beta copy; the
registered version will add bridge-finding code and hooks for interrupt-
driven packet reception.

I am planning to do a 32-bit version of IPXLIB compatible with the Watcom
32-bit compiler.  I'm working with a collaborator who has some experience
with it.

Also potentially in the works is a Windows version of IPXLIB, and possibly
a similar library for use with NetBIOS networks.  If you have an interest
in either of these potential projects, please let me know.

If you would like to do something with IPXLIB that I haven't taken into
account, such as use it with a different compiler, or with overlays, or
whatever, let me know.  If it's feasible, I can probably be talked into it.


Allen Brunson
6500 East 21st #2
Wichita, Kansas  67206
CompuServe: 74464,3472



******************************************************************************
*                                                                            *
***  Release History                                                       ***
*                                                                            *
******************************************************************************


Version 0.20, June 1, 1994

Second public beta release.

Changes:

  * The code in IPX.ASM was streamlined considerably, eliminating many
    small bugs.

  * ipxStart() was changed from taking no parameters to taking all
    communication parameters, including a far pointer to memory to
    user for ECBs, buffers, and so on.  IPXTEST was modified so that
    it passes a pointer to an IPXDATA structure if DEBUG is defined
    to use for communication memory or else it will allocate memory
    from the heap if DEBUG is not defined.

  * IPXDATA.C was eliminated; all communication data is now passed as
    variables to ipxStart(), including a pointer to memory that is used
    for the data that formerly was in the eliminated file.

  * Code was added to keep track of the maximum number of ECBs that have
    been used.  IPXTEST was upgraded to display or reset this information
    and to send it to other IPXTEST users.

  * Two new IPXLIB error codes were added, ipxeBADCOMMPARMS and
    ipxeMEMTOOSMALL, which ipxStart() can return if the communication
    parameters given are wrong or if the size of the memory block given
    isn't big enough to hold everything, respectively.  This shifted the
    values of all the other error codes.  The procedure err() in IPXTEST
    was upgraded to display error messages for these new codes.

  * The ECB pointer field in the IPXEVENT structure was changed from a
    far (4-byte) to near (2-byte) pointer.

  * IPXLIB was tested with Microsoft C++ version 8.0 and proven to work
    in all memory models except Tiny.  IPXTEST was modified to work with
    Microsoft C as well, although a few minor things didn't get ported,
    like the variable-length wait before sending a ping response and
    screen scrolling, because there weren't functions in the Microsoft
    run-time libraries equivalent to the ones I used in Borland C.

  * "Flurry mode," that is to say, sending as many packets as time will
    allow, was added to IPXTEST, so as to stress-test IPXLIB.

  * IPXTEST can now be "remote controlled:" It attempts to interpret the
    text of broadcast messages and directed messages as commands.

  * Several extra IPX programming considerations were added to the IPXTEST
    documentation.


Version 0.10, May 27, 1994

First public beta release.
