672 lines
19 KiB
C
672 lines
19 KiB
C
|
/* protj.c
|
|||
|
The 'j' protocol.
|
|||
|
|
|||
|
Copyright (C) 1992 Ian Lance Taylor
|
|||
|
|
|||
|
This file is part of the Taylor UUCP package.
|
|||
|
|
|||
|
This program is free software; you can redistribute it and/or
|
|||
|
modify it under the terms of the GNU General Public License as
|
|||
|
published by the Free Software Foundation; either version 2 of the
|
|||
|
License, or (at your option) any later version.
|
|||
|
|
|||
|
This program is distributed in the hope that it will be useful, but
|
|||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|||
|
General Public License for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with this program; if not, write to the Free Software
|
|||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|||
|
|
|||
|
The author of the program may be contacted at ian@airs.com or
|
|||
|
c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
|
|||
|
*/
|
|||
|
|
|||
|
#include "uucp.h"
|
|||
|
|
|||
|
#if USE_RCS_ID
|
|||
|
const char protj_rcsid[] = "$Id: protj.c,v 1.1 1993/08/04 19:36:23 jtc Exp $";
|
|||
|
#endif
|
|||
|
|
|||
|
#include <ctype.h>
|
|||
|
#include <errno.h>
|
|||
|
|
|||
|
#include "uudefs.h"
|
|||
|
#include "conn.h"
|
|||
|
#include "trans.h"
|
|||
|
#include "system.h"
|
|||
|
#include "prot.h"
|
|||
|
|
|||
|
/* The 'j' protocol.
|
|||
|
|
|||
|
The 'j' protocol is a wrapper around the 'i' protocol, which avoids
|
|||
|
the use of certain characters, such as XON and XOFF.
|
|||
|
|
|||
|
Each 'j' protocol packet begins with a '^' character, followed by a
|
|||
|
two byte encoded size giving the total number of bytes in the
|
|||
|
packet. The first byte is HIGH, the second byte is LOW, and the
|
|||
|
number of bytes is (HIGH - 32) * 64 + (LOW - 32), where 32 <= HIGH
|
|||
|
< 127 and 32 <= LOW < 96 (i.e., HIGH and LOW are printable ASCII
|
|||
|
characters). This is followed by a '=' character. The next two
|
|||
|
bytes are the number of data bytes in the packet, using the same
|
|||
|
encoding. This is followed by a '@' character, and then that
|
|||
|
number of data bytes. The remaining bytes in the packet are
|
|||
|
indices of bytes which must be transformed, followed by a trailing
|
|||
|
'~' character. The indices are encoded in the following overly
|
|||
|
complex format.
|
|||
|
|
|||
|
Each byte index is two bytes long. The first byte in the index is
|
|||
|
INDEX-HIGH and the second is INDEX-LOW. If 32 <= INDEX-HIGH < 126,
|
|||
|
the byte index refers to the byte at position (INDEX-HIGH - 32) *
|
|||
|
32 + INDEX-LOW % 32 in the actual data, where 32 <= INDEX-LOW <
|
|||
|
127. If 32 <= INDEX-LOW < 64, then 128 must be added to the
|
|||
|
indexed byte. If 64 <= INDEX-LOW < 96, then the indexed byte must
|
|||
|
be exclusive or'red with 32. If 96 <= INDEX-LOW < 127, both
|
|||
|
operations must be performed. If INDEX-HIGH == 126, then the byte
|
|||
|
index refers to the byte at position (INDEX-LOW - 32) * 32 + 31,
|
|||
|
where 32 <= INDEX-LOW < 126. 128 must be added to the byte, and it
|
|||
|
must be exclusive or'red with 32. This unfortunately requires a
|
|||
|
special test (when encoding INDEX-LOW must be checked for 127; when
|
|||
|
decoding INDEX-HIGH must be checked for 126). It does, however,
|
|||
|
permit the byte indices field to consist exclusively of printable
|
|||
|
ASCII characters.
|
|||
|
|
|||
|
The maximum value for a byte index is (125 - 32) * 32 + 31 == 3007,
|
|||
|
so the is the maximum number of data bytes permitted. Since it is
|
|||
|
convenient to have each 'j' protocol packet correspond to each 'i'
|
|||
|
protocol packet, we restrict the 'i' protocol accordingly.
|
|||
|
|
|||
|
Note that this encoding method assumes that we can send all
|
|||
|
printable ASCII characters. */
|
|||
|
|
|||
|
/* The first byte of each packet. I just picked these values
|
|||
|
randomly, trying to get characters that were perhaps slightly less
|
|||
|
likely to appear in normal text. */
|
|||
|
#define FIRST '\136'
|
|||
|
|
|||
|
/* The fourth byte of each packet. */
|
|||
|
#define FOURTH '\075'
|
|||
|
|
|||
|
/* The seventh byte of each packet. */
|
|||
|
#define SEVENTH '\100'
|
|||
|
|
|||
|
/* The trailing byte of each packet. */
|
|||
|
#define TRAILER '\176'
|
|||
|
|
|||
|
/* The length of the header. */
|
|||
|
#define CHDRLEN (7)
|
|||
|
|
|||
|
/* Get a number of bytes encoded in a two byte length at the start of
|
|||
|
a packet. */
|
|||
|
#define CGETLENGTH(b1, b2) (((b1) - 32) * 64 + ((b2) - 32))
|
|||
|
|
|||
|
/* Set the high and low bytes of a two byte length at the start of a
|
|||
|
packet. */
|
|||
|
#define ISETLENGTH_FIRST(i) ((i) / 64 + 32)
|
|||
|
#define ISETLENGTH_SECOND(i) ((i) % 64 + 32)
|
|||
|
|
|||
|
/* The maximum packet size we support, as determined by the byte
|
|||
|
indices. */
|
|||
|
#define IMAXPACKSIZE ((125 - 32) * 32 + 31)
|
|||
|
|
|||
|
/* Amount to offset the bytes in the byte index by. */
|
|||
|
#define INDEX_OFFSET (32)
|
|||
|
|
|||
|
/* Maximum value of INDEX-LOW, before offsetting. */
|
|||
|
#define INDEX_MAX_LOW (32)
|
|||
|
|
|||
|
/* Maximum value of INDEX-HIGH, before offsetting. */
|
|||
|
#define INDEX_MAX_HIGH (94)
|
|||
|
|
|||
|
/* The set of characters to avoid. */
|
|||
|
static char *zJavoid;
|
|||
|
|
|||
|
/* The number of characters to avoid. */
|
|||
|
static size_t cJavoid;
|
|||
|
|
|||
|
/* A buffer used when sending data. */
|
|||
|
static char *zJbuf;
|
|||
|
|
|||
|
/* The end of the undecoded data in abPrecbuf. */
|
|||
|
static int iJrecend;
|
|||
|
|
|||
|
/* Local functions. */
|
|||
|
static boolean fjsend_data P((struct sconnection *qconn, const char *zsend,
|
|||
|
size_t csend, boolean fdoread));
|
|||
|
static boolean fjreceive_data P((struct sconnection *qconn, size_t cneed,
|
|||
|
size_t *pcrec, int ctimeout,
|
|||
|
boolean freport));
|
|||
|
static boolean fjprocess_data P((size_t *pcneed));
|
|||
|
|
|||
|
/* Start the protocol. We first send over the list of characters to
|
|||
|
avoid as an escape sequence, starting with FIRST and ending with
|
|||
|
TRAILER. There is no error checking done on this string. */
|
|||
|
|
|||
|
boolean
|
|||
|
fjstart (qdaemon, pzlog)
|
|||
|
struct sdaemon *qdaemon;
|
|||
|
char **pzlog;
|
|||
|
{
|
|||
|
size_t clen;
|
|||
|
char *zsend;
|
|||
|
int b;
|
|||
|
size_t cbuf, cgot;
|
|||
|
char *zbuf;
|
|||
|
int i;
|
|||
|
|
|||
|
/* Send the characters we want to avoid to the other side. */
|
|||
|
clen = strlen (zJavoid_parameter);
|
|||
|
zsend = zbufalc (clen + 3);
|
|||
|
zsend[0] = FIRST;
|
|||
|
memcpy (zsend + 1, zJavoid_parameter, clen);
|
|||
|
zsend[clen + 1] = TRAILER;
|
|||
|
zsend[clen + 2] = '\0';
|
|||
|
if (! fsend_data (qdaemon->qconn, zsend, clen + 2, TRUE))
|
|||
|
{
|
|||
|
ubuffree (zsend);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
ubuffree (zsend);
|
|||
|
|
|||
|
/* Read the characters the other side wants to avoid. */
|
|||
|
while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE))
|
|||
|
!= FIRST)
|
|||
|
{
|
|||
|
if (b < 0)
|
|||
|
{
|
|||
|
if (b == -1)
|
|||
|
ulog (LOG_ERROR, "Timed out in 'j' protocol startup");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cbuf = 20;
|
|||
|
zbuf = zbufalc (cbuf);
|
|||
|
cgot = 0;
|
|||
|
while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE))
|
|||
|
!= TRAILER)
|
|||
|
{
|
|||
|
if (b < 0)
|
|||
|
{
|
|||
|
ubuffree (zbuf);
|
|||
|
if (b == -1)
|
|||
|
ulog (LOG_ERROR, "Timed out in 'j' protocol startup");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (cgot + 1 >= cbuf)
|
|||
|
{
|
|||
|
char *znew;
|
|||
|
|
|||
|
cbuf += 20;
|
|||
|
znew = zbufalc (cbuf);
|
|||
|
memcpy (znew, zbuf, cgot);
|
|||
|
ubuffree (zbuf);
|
|||
|
zbuf = znew;
|
|||
|
}
|
|||
|
zbuf[cgot] = b;
|
|||
|
++cgot;
|
|||
|
}
|
|||
|
zbuf[cgot] = '\0';
|
|||
|
|
|||
|
/* Merge the local and remote avoid bytes into one list, translated
|
|||
|
into bytes. */
|
|||
|
cgot = cescape (zbuf);
|
|||
|
|
|||
|
clen = strlen (zJavoid_parameter);
|
|||
|
zJavoid = zbufalc (clen + cgot + 1);
|
|||
|
memcpy (zJavoid, zJavoid_parameter, clen + 1);
|
|||
|
cJavoid = cescape (zJavoid);
|
|||
|
|
|||
|
for (i = 0; i < cgot; i++)
|
|||
|
{
|
|||
|
if (memchr (zJavoid, zbuf[i], cJavoid) == NULL)
|
|||
|
{
|
|||
|
zJavoid[cJavoid] = zbuf[i];
|
|||
|
++cJavoid;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ubuffree (zbuf);
|
|||
|
|
|||
|
/* We can't avoid ASCII printable characters, since the encoding
|
|||
|
method assumes that they can always be sent. If it ever turns
|
|||
|
out to be important, a different encoding method could be used,
|
|||
|
perhaps keyed by a different FIRST character. */
|
|||
|
if (cJavoid == 0)
|
|||
|
{
|
|||
|
ulog (LOG_ERROR, "No characters to avoid in 'j' protocol");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
for (i = 0; i < cJavoid; i++)
|
|||
|
{
|
|||
|
if (zJavoid[i] >= 32 && zJavoid[i] <= 126)
|
|||
|
{
|
|||
|
ulog (LOG_ERROR, "'j' protocol can't avoid character '\\%03o'",
|
|||
|
zJavoid[i]);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* If we are avoiding XON and XOFF, use XON/XOFF handshaking. */
|
|||
|
if (memchr (zJavoid, '\021', cJavoid) != NULL
|
|||
|
&& memchr (zJavoid, '\023', cJavoid) != NULL)
|
|||
|
{
|
|||
|
if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
|
|||
|
STRIPSETTING_EIGHTBITS, XONXOFF_ON))
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
/* Let the port settle. */
|
|||
|
usysdep_sleep (2);
|
|||
|
|
|||
|
/* Allocate a buffer we use when sending data. We will probably
|
|||
|
never actually need one this big; if this code is ported to a
|
|||
|
computer with small amounts of memory, this should be changed to
|
|||
|
increase the buffer size as needed. */
|
|||
|
zJbuf = zbufalc (CHDRLEN + IMAXPACKSIZE * 3 + 1);
|
|||
|
zJbuf[0] = FIRST;
|
|||
|
zJbuf[3] = FOURTH;
|
|||
|
zJbuf[6] = SEVENTH;
|
|||
|
|
|||
|
/* iJrecend is the end of the undecoded data, and iPrecend is the
|
|||
|
end of the decoded data. At this point there is no decoded data,
|
|||
|
and we must initialize the variables accordingly. */
|
|||
|
iJrecend = iPrecend;
|
|||
|
iPrecend = iPrecstart;
|
|||
|
|
|||
|
/* Now do the 'i' protocol startup. */
|
|||
|
return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fjsend_data,
|
|||
|
fjreceive_data);
|
|||
|
}
|
|||
|
|
|||
|
/* Shut down the protocol. */
|
|||
|
|
|||
|
boolean
|
|||
|
fjshutdown (qdaemon)
|
|||
|
struct sdaemon *qdaemon;
|
|||
|
{
|
|||
|
boolean fret;
|
|||
|
|
|||
|
fret = fishutdown (qdaemon);
|
|||
|
ubuffree (zJavoid);
|
|||
|
ubuffree (zJbuf);
|
|||
|
return fret;
|
|||
|
}
|
|||
|
|
|||
|
/* Encode a packet of data and send it. This copies the data, which
|
|||
|
is a waste of time, but calling fsend_data three times (for the
|
|||
|
header, the body, and the trailer) would waste even more time. */
|
|||
|
|
|||
|
static boolean
|
|||
|
fjsend_data (qconn, zsend, csend, fdoread)
|
|||
|
struct sconnection *qconn;
|
|||
|
const char *zsend;
|
|||
|
size_t csend;
|
|||
|
boolean fdoread;
|
|||
|
{
|
|||
|
char *zput, *zindex;
|
|||
|
const char *zfrom, *zend;
|
|||
|
char bfirst, bsecond;
|
|||
|
int iprecendhold;
|
|||
|
boolean fret;
|
|||
|
|
|||
|
zput = zJbuf + CHDRLEN;
|
|||
|
zindex = zput + csend;
|
|||
|
zfrom = zsend;
|
|||
|
zend = zsend + csend;
|
|||
|
|
|||
|
/* Optimize for the common case of avoiding two characters. */
|
|||
|
bfirst = zJavoid[0];
|
|||
|
if (cJavoid <= 1)
|
|||
|
bsecond = bfirst;
|
|||
|
else
|
|||
|
bsecond = zJavoid[1];
|
|||
|
while (zfrom < zend)
|
|||
|
{
|
|||
|
char b;
|
|||
|
boolean f128, f32;
|
|||
|
int i, ihigh, ilow;
|
|||
|
|
|||
|
b = *zfrom++;
|
|||
|
if (b != bfirst && b != bsecond)
|
|||
|
{
|
|||
|
int ca;
|
|||
|
char *za;
|
|||
|
|
|||
|
if (cJavoid <= 2)
|
|||
|
{
|
|||
|
*zput++ = b;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
ca = cJavoid - 2;
|
|||
|
za = zJavoid + 2;
|
|||
|
while (ca-- != 0)
|
|||
|
if (*za++ == b)
|
|||
|
break;
|
|||
|
|
|||
|
if (ca < 0)
|
|||
|
{
|
|||
|
*zput++ = b;
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ((b & 0x80) == 0)
|
|||
|
f128 = FALSE;
|
|||
|
else
|
|||
|
{
|
|||
|
b &=~ 0x80;
|
|||
|
f128 = TRUE;
|
|||
|
}
|
|||
|
if (b >= 32 && b != 127)
|
|||
|
f32 = FALSE;
|
|||
|
else
|
|||
|
{
|
|||
|
b ^= 0x20;
|
|||
|
f32 = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
/* We must now put the byte index into the buffer. The byte
|
|||
|
index is encoded similarly to the length of the actual data,
|
|||
|
but the byte index also encodes the operations that must be
|
|||
|
performed on the byte. The first byte in the index is the
|
|||
|
most significant bits. If we only had to subtract 128 from
|
|||
|
the byte, we use the second byte directly. If we had to xor
|
|||
|
the byte with 32, we add 32 to the second byte index. If we
|
|||
|
had to perform both operations, we add 64 to the second byte
|
|||
|
index. However, if we had to perform both operations, and
|
|||
|
the second byte index was 31, then after adding 64 and
|
|||
|
offsetting by 32 we would come up with 127, which we are not
|
|||
|
permitted to use. Therefore, in this special case we set the
|
|||
|
first byte of the index to 126 and put the original first
|
|||
|
byte into the second byte position instead. This is why we
|
|||
|
could not permit the high byte of the length of the actual
|
|||
|
data to be 126. We can get away with the switch because both
|
|||
|
the value of the second byte index (31) and the operations to
|
|||
|
perform (both) are known. */
|
|||
|
i = zput - (zJbuf + CHDRLEN);
|
|||
|
ihigh = i / INDEX_MAX_LOW;
|
|||
|
ilow = i % INDEX_MAX_LOW;
|
|||
|
|
|||
|
if (f128 && ! f32)
|
|||
|
;
|
|||
|
else if (f32 && ! f128)
|
|||
|
ilow += INDEX_MAX_LOW;
|
|||
|
else
|
|||
|
{
|
|||
|
/* Both operations had to be performed. */
|
|||
|
if (ilow != INDEX_MAX_LOW - 1)
|
|||
|
ilow += 2 * INDEX_MAX_LOW;
|
|||
|
else
|
|||
|
{
|
|||
|
ilow = ihigh;
|
|||
|
ihigh = INDEX_MAX_HIGH;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
*zindex++ = ihigh + INDEX_OFFSET;
|
|||
|
*zindex++ = ilow + INDEX_OFFSET;
|
|||
|
*zput++ = b;
|
|||
|
}
|
|||
|
|
|||
|
*zindex++ = TRAILER;
|
|||
|
|
|||
|
/* Set the lengths into the buffer. zJbuf[0,3,6] were set when
|
|||
|
zJbuf was allocated, and are never changed thereafter. */
|
|||
|
zJbuf[1] = ISETLENGTH_FIRST (zindex - zJbuf);
|
|||
|
zJbuf[2] = ISETLENGTH_SECOND (zindex - zJbuf);
|
|||
|
zJbuf[4] = ISETLENGTH_FIRST (csend);
|
|||
|
zJbuf[5] = ISETLENGTH_SECOND (csend);
|
|||
|
|
|||
|
/* Send the data over the line. We must preserve iPrecend as
|
|||
|
discussed in fjreceive_data. */
|
|||
|
iprecendhold = iPrecend;
|
|||
|
iPrecend = iJrecend;
|
|||
|
fret = fsend_data (qconn, zJbuf, (size_t) (zindex - zJbuf), fdoread);
|
|||
|
iJrecend = iPrecend;
|
|||
|
iPrecend = iprecendhold;
|
|||
|
|
|||
|
/* Process any bytes that may have been placed in abPrecbuf. */
|
|||
|
if (fret && iPrecend != iJrecend)
|
|||
|
{
|
|||
|
if (! fjprocess_data ((size_t *) NULL))
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return fret;
|
|||
|
}
|
|||
|
|
|||
|
/* Receive and decode data. This is called by fiwait_for_packet. We
|
|||
|
need to be able to return decoded data between iPrecstart and
|
|||
|
iPrecend, while not losing any undecoded partial packets we may
|
|||
|
have read. We use iJrecend as a pointer to the end of the
|
|||
|
undecoded data, and set iPrecend for the decoded data. iPrecend
|
|||
|
points to the start of the undecoded data. */
|
|||
|
|
|||
|
static boolean
|
|||
|
fjreceive_data (qconn, cineed, pcrec, ctimeout, freport)
|
|||
|
struct sconnection *qconn;
|
|||
|
size_t cineed;
|
|||
|
size_t *pcrec;
|
|||
|
int ctimeout;
|
|||
|
boolean freport;
|
|||
|
{
|
|||
|
int iprecendstart;
|
|||
|
size_t cjneed;
|
|||
|
size_t crec;
|
|||
|
int cnew;
|
|||
|
|
|||
|
iprecendstart = iPrecend;
|
|||
|
|
|||
|
/* Figure out how many bytes we need to decode the next packet. */
|
|||
|
if (! fjprocess_data (&cjneed))
|
|||
|
return FALSE;
|
|||
|
|
|||
|
/* As we long as we read some data but don't have enough to decode a
|
|||
|
packet, we try to read some more. We decrease the timeout each
|
|||
|
time so that we will not wait forever if the connection starts
|
|||
|
dribbling data. */
|
|||
|
do
|
|||
|
{
|
|||
|
int iprecendhold;
|
|||
|
size_t cneed;
|
|||
|
|
|||
|
if (cjneed > cineed)
|
|||
|
cneed = cjneed;
|
|||
|
else
|
|||
|
cneed = cineed;
|
|||
|
|
|||
|
/* We are setting iPrecend to the end of the decoded data for
|
|||
|
the 'i' protocol. When we do the actual read, we have to set
|
|||
|
it to the end of the undecoded data so that any undecoded
|
|||
|
data we have received is not overwritten. */
|
|||
|
iprecendhold = iPrecend;
|
|||
|
iPrecend = iJrecend;
|
|||
|
if (! freceive_data (qconn, cneed, &crec, ctimeout, freport))
|
|||
|
return FALSE;
|
|||
|
iJrecend = iPrecend;
|
|||
|
iPrecend = iprecendhold;
|
|||
|
|
|||
|
/* Process any data we have received. This will set iPrecend to
|
|||
|
the end of the new decoded data. */
|
|||
|
if (! fjprocess_data (&cjneed))
|
|||
|
return FALSE;
|
|||
|
|
|||
|
cnew = iPrecend - iprecendstart;
|
|||
|
if (cnew < 0)
|
|||
|
cnew += CRECBUFLEN;
|
|||
|
|
|||
|
if (cnew > cineed)
|
|||
|
cineed = 0;
|
|||
|
else
|
|||
|
cineed -= cnew;
|
|||
|
|
|||
|
--ctimeout;
|
|||
|
}
|
|||
|
while (cnew == 0 && crec > 0 && ctimeout > 0);
|
|||
|
|
|||
|
DEBUG_MESSAGE1 (DEBUG_PROTO, "fjreceive_data: Got %d decoded bytes",
|
|||
|
cnew);
|
|||
|
|
|||
|
*pcrec = cnew;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
/* Decode the data in the buffer, optionally returning the number of
|
|||
|
bytes needed to complete the next packet. */
|
|||
|
|
|||
|
static boolean
|
|||
|
fjprocess_data (pcneed)
|
|||
|
size_t *pcneed;
|
|||
|
{
|
|||
|
int istart;
|
|||
|
|
|||
|
istart = iPrecend;
|
|||
|
while (istart != iJrecend)
|
|||
|
{
|
|||
|
int i, iget;
|
|||
|
char ab[CHDRLEN];
|
|||
|
int cpacket, cdata, chave;
|
|||
|
int iindex, iendindex;
|
|||
|
|
|||
|
/* Find the next occurrence of FIRST. If we have to skip some
|
|||
|
garbage bytes to get to it, zero them out (so they don't
|
|||
|
confuse the 'i' protocol) and advance iPrecend. This will
|
|||
|
save us from looking at them again. */
|
|||
|
if (abPrecbuf[istart] != FIRST)
|
|||
|
{
|
|||
|
int cintro;
|
|||
|
char *zintro;
|
|||
|
size_t cskipped;
|
|||
|
|
|||
|
cintro = iJrecend - istart;
|
|||
|
if (cintro < 0)
|
|||
|
cintro = CRECBUFLEN - istart;
|
|||
|
|
|||
|
zintro = memchr (abPrecbuf + istart, FIRST, (size_t) cintro);
|
|||
|
if (zintro == NULL)
|
|||
|
{
|
|||
|
bzero (abPrecbuf + istart, (size_t) cintro);
|
|||
|
istart = (istart + cintro) % CRECBUFLEN;
|
|||
|
iPrecend = istart;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
cskipped = zintro - (abPrecbuf + istart);
|
|||
|
bzero (abPrecbuf + istart, cskipped);
|
|||
|
istart += cskipped;
|
|||
|
iPrecend = istart;
|
|||
|
}
|
|||
|
|
|||
|
for (i = 0, iget = istart;
|
|||
|
i < CHDRLEN && iget != iJrecend;
|
|||
|
++i, iget = (iget + 1) % CRECBUFLEN)
|
|||
|
ab[i] = abPrecbuf[iget];
|
|||
|
|
|||
|
if (i < CHDRLEN)
|
|||
|
{
|
|||
|
if (pcneed != NULL)
|
|||
|
*pcneed = CHDRLEN - i;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
cpacket = CGETLENGTH (ab[1], ab[2]);
|
|||
|
cdata = CGETLENGTH (ab[4], ab[5]);
|
|||
|
|
|||
|
/* Make sure the header has the right magic characters, that the
|
|||
|
data is not larger than the packet, and that we have an even
|
|||
|
number of byte index characters. */
|
|||
|
if (ab[3] != FOURTH
|
|||
|
|| ab[6] != SEVENTH
|
|||
|
|| cdata > cpacket - CHDRLEN - 1
|
|||
|
|| (cpacket - cdata - CHDRLEN - 1) % 2 == 1)
|
|||
|
{
|
|||
|
istart = (istart + 1) % CRECBUFLEN;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
chave = iJrecend - istart;
|
|||
|
if (chave < 0)
|
|||
|
chave += CRECBUFLEN;
|
|||
|
|
|||
|
if (chave < cpacket)
|
|||
|
{
|
|||
|
if (pcneed != NULL)
|
|||
|
*pcneed = cpacket - chave;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
/* Figure out where the byte indices start and end. */
|
|||
|
iindex = (istart + CHDRLEN + cdata) % CRECBUFLEN;
|
|||
|
iendindex = (istart + cpacket - 1) % CRECBUFLEN;
|
|||
|
|
|||
|
/* Make sure the magic trailer character is there. */
|
|||
|
if (abPrecbuf[iendindex] != TRAILER)
|
|||
|
{
|
|||
|
istart = (istart + 1) % CRECBUFLEN;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
/* We have a packet to decode. The decoding process is simpler
|
|||
|
than the encoding process, since all we have to do is examine
|
|||
|
the byte indices. We zero out the byte indices as we go, so
|
|||
|
that they will not confuse the 'i' protocol. */
|
|||
|
while (iindex != iendindex)
|
|||
|
{
|
|||
|
int ihigh, ilow;
|
|||
|
boolean f32, f128;
|
|||
|
int iset;
|
|||
|
|
|||
|
ihigh = abPrecbuf[iindex] - INDEX_OFFSET;
|
|||
|
abPrecbuf[iindex] = 0;
|
|||
|
iindex = (iindex + 1) % CRECBUFLEN;
|
|||
|
ilow = abPrecbuf[iindex] - INDEX_OFFSET;
|
|||
|
abPrecbuf[iindex] = 0;
|
|||
|
iindex = (iindex + 1) % CRECBUFLEN;
|
|||
|
|
|||
|
/* Now we must undo the encoding, by adding 128 and xoring
|
|||
|
with 32 as appropriate. Which to do is encoded in the
|
|||
|
low byte, except that if the high byte is the special
|
|||
|
value 126, then the low byte is actually the high byte
|
|||
|
and both operations are performed. */
|
|||
|
f128 = TRUE;
|
|||
|
f32 = TRUE;
|
|||
|
if (ihigh == INDEX_MAX_HIGH)
|
|||
|
iset = ilow * INDEX_MAX_LOW + INDEX_MAX_LOW - 1;
|
|||
|
else
|
|||
|
{
|
|||
|
iset = ihigh * INDEX_MAX_LOW + ilow % INDEX_MAX_LOW;
|
|||
|
if (ilow < INDEX_MAX_LOW)
|
|||
|
f32 = FALSE;
|
|||
|
else if (ilow < 2 * INDEX_MAX_LOW)
|
|||
|
f128 = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
/* Now iset is the index from the start of the data to the
|
|||
|
byte to modify; adjust it to an index in abPrecbuf. */
|
|||
|
iset = (istart + CHDRLEN + iset) % CRECBUFLEN;
|
|||
|
|
|||
|
if (f128)
|
|||
|
abPrecbuf[iset] |= 0x80;
|
|||
|
if (f32)
|
|||
|
abPrecbuf[iset] ^= 0x20;
|
|||
|
}
|
|||
|
|
|||
|
/* Zero out the header and trailer to avoid confusing the 'i'
|
|||
|
protocol, and update iPrecend to the end of decoded data. */
|
|||
|
for (i = 0, iget = istart;
|
|||
|
i < CHDRLEN && iget != iJrecend;
|
|||
|
++i, iget = (iget + 1) % CRECBUFLEN)
|
|||
|
abPrecbuf[iget] = 0;
|
|||
|
abPrecbuf[iendindex] = 0;
|
|||
|
iPrecend = (iendindex + 1) % CRECBUFLEN;
|
|||
|
istart = iPrecend;
|
|||
|
}
|
|||
|
|
|||
|
if (pcneed != NULL)
|
|||
|
*pcneed = CHDRLEN + 1;
|
|||
|
return TRUE;
|
|||
|
}
|