blob: 53ea2362987298c86f24b636190a2499db0da0d6 [file] [log] [blame]
char *fnsv = "C-Kermit functions, 8.0.223, 1 May 2003";
char *nm[] = { "Disabled", "Local only", "Remote only", "Enabled" };
/* C K C F N S -- System-independent Kermit protocol support functions. */
/* ...Part 1 (others moved to ckcfn2,3 to make this module smaller) */
/*
Author: Frank da Cruz <fdc@columbia.edu>,
Columbia University Academic Information Systems, New York City.
Copyright (C) 1985, 2004,
Trustees of Columbia University in the City of New York.
All rights reserved. See the C-Kermit COPYING.TXT file or the
copyright text in the ckcmai.c module for disclaimer and permissions.
*/
/*
System-dependent primitives defined in:
ck?tio.c -- terminal (communications) i/o
cx?fio.c -- file i/o, directory structure
*/
#include "ckcsym.h" /* Needed for Stratus VOS */
#include "ckcasc.h" /* ASCII symbols */
#include "ckcdeb.h" /* Debug formats, typedefs, etc. */
#include "ckcker.h" /* Symbol definitions for Kermit */
#include "ckcxla.h" /* Character set symbols */
#include "ckcnet.h" /* VMS definition of TCPSOCKET */
#ifdef OS2
#ifdef OS2ONLY
#include <os2.h>
#endif /* OS2ONLY */
#include "ckocon.h"
#endif /* OS2 */
int docrc = 0; /* Accumulate CRC for \v(crc16) */
long crc16 = 0L; /* File CRC = \v(crc16) */
int gnferror = 0; /* gnfile() failure reason */
extern CHAR feol;
extern int byteorder, xflg, what, fmask, cxseen, czseen, nscanfile, sysindex;
extern int xcmdsrc, dispos, matchfifo;
extern long ffc;
extern int inserver;
extern int nolinks;
#ifdef VMSORUNIX
extern int zgfs_dir;
#ifdef CKSYMLINK
extern int zgfs_link;
#endif /* CKSYMLINK */
#endif /* VMSORUNIX */
#ifndef NOXFER
#ifndef NOICP
#ifndef NOSPL
extern char * clcmds;
extern int haveurl;
#ifdef CK_APC
extern int apcactive, adl_ask;
#endif /* CK_APC */
#endif /* NOSPL */
#endif /* NOICP */
extern int remfile;
/* (move these prototypes to the appropriate .h files...) */
#ifdef COMMENT
/* Not used */
#ifdef VMS
_PROTOTYP( int getvnum, (char *) );
#endif /* VMS */
#endif /* COMMENT */
_PROTOTYP( static int bgetpkt, (int) );
#ifndef NOCSETS
_PROTOTYP( int lookup, (struct keytab[], char *, int, int *) );
#endif /* NOCSETS */
#ifndef NOSPL
_PROTOTYP( int zzstring, (char *, char **, int *) );
#endif /* NOSPL */
#ifdef OS2ORUNIX
_PROTOTYP( long zfsize, (char *) );
#endif /* OS2ORUNIX */
#ifdef OS2
#include <io.h>
#ifdef OS2ONLY
#include <os2.h>
#endif /* OS2ONLY */
#endif /* OS2 */
#ifdef VMS
#include <errno.h>
#endif /* VMS */
/* Externals from ckcmai.c */
extern int srvcdmsg, srvidl, idletmo;
extern char * cdmsgfile[];
extern int spsiz, spmax, rpsiz, timint, srvtim, rtimo, npad, ebq, ebqflg,
rpt, rptq, rptflg, capas, keep, fncact, pkttim, autopar, spsizr, xitsta;
extern int pktnum, bctr, bctu, bctl, clfils, sbufnum, protocol,
size, osize, spktl, nfils, ckwarn, timef, spsizf, sndtyp, rcvtyp, success;
extern int parity, turn, network, whatru, fsecs, justone, slostart,
ckdelay, displa, mypadn, moving, recursive, nettype;
extern long filcnt, flci, flco, tlci, tlco, tfc, fsize, sendstart, rs_len;
extern long filrej, oldcps, cps, peakcps, ccu, ccp, calibrate, filestatus;
extern int fblksiz, frecl, frecfm, forg, fcctrl, fdispla, skipbup;
extern int spackets, rpackets, timeouts, retrans, crunched, wmax, wcur;
extern int hcflg, binary, fncnv, b_save, f_save, server;
extern int nakstate, discard, rejection, local, xfermode, interrupted;
extern int rq, rqf, sq, wslots, wslotn, wslotr, winlo, urpsiz, rln;
extern int fnspath, fnrpath, eofmethod, diractive, whatru2, wearealike;
extern int atcapr, atcapb, atcapu;
extern int lpcapr, lpcapb, lpcapu;
extern int swcapr, swcapb, swcapu;
extern int lscapr, lscapb, lscapu;
extern int rscapr, rscapb, rscapu;
extern int rptena, rptmin;
extern int sseqtbl[];
extern int numerrs, nzxopts;
extern long rptn;
extern int maxtry;
extern int stdouf;
extern int sendmode;
extern int carrier, ttprty;
extern int g_fnrpath;
#ifdef TCPSOCKET
extern int ttnproto;
#endif /* TCPSOCKET */
#ifndef NOSPL
extern int sndxin, sndxhi, sndxlo;
#endif /* NOSPL */
extern int g_binary, g_fncnv;
#ifdef GFTIMER
extern CKFLOAT fpfsecs;
#endif /* GFTIMER */
#ifdef OS2
extern struct zattr iattr;
#endif /* OS2 */
#ifdef PIPESEND
extern int usepipes;
#endif /* PIPESEND */
extern int pipesend;
#ifdef STREAMING
extern int streamrq, streaming, streamed, streamok;
#endif /* STREAMING */
extern int reliable, clearrq, cleared, urclear;
extern int
atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko,
attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso;
extern int bigsbsiz, bigrbsiz;
extern char *versio;
extern char *filefile;
extern char whoareu[], * cksysid;
#ifndef NOSERVER
extern int ngetpath;
extern char * getpath[];
extern int fromgetpath;
#endif /* NOSERVER */
#ifdef CK_LOGIN
extern int isguest;
#endif /* CK_LOGIN */
extern int srvcmdlen;
extern CHAR *srvcmd, * epktmsg;
extern CHAR padch, mypadc, eol, seol, ctlq, myctlq, sstate, myrptq;
extern CHAR *data, padbuf[], stchr, mystch;
extern CHAR *srvptr;
extern CHAR *rdatap;
extern char *cmarg, *cmarg2, **cmlist, filnam[], ofilnam[];
extern char *rfspec, *prfspec, *rrfspec, *prrfspec, *sfspec, *psfspec, *rfspec;
extern char fspec[];
extern int fspeclen;
#ifndef NOMSEND
extern struct filelist * filehead, * filenext;
extern int addlist;
#endif /* NOMSEND */
_PROTOTYP( int lslook, (unsigned int b) ); /* Locking Shift Lookahead */
_PROTOTYP( int szeof, (CHAR *s) );
_PROTOTYP( VOID fnlist, (void) );
#endif /* NOXFER */
/* Character set Translation */
#ifndef NOCSETS
extern int tcharset, fcharset, dcset7, dcset8;
extern int fcs_save, tcs_save;
extern int ntcsets, xlatype, cseqtab[];
extern struct csinfo tcsinfo[], fcsinfo[];
extern int r_cset, s_cset, afcset[];
#ifdef UNICODE
extern int ucsorder, fileorder;
#endif /* UNICODE */
_PROTOTYP( CHAR ident, (CHAR) ); /* Identity translation function */
/* Arrays of and pointers to character translation functions */
#ifdef CK_ANSIC
extern CHAR (*rx)(CHAR); /* Pointer to input character translation function */
extern CHAR (*sx)(CHAR); /* Pointer to output character translation function */
extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Byte-to-Byte Send */
extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Byte-to-Byte Recv */
#ifdef UNICODE
extern int (*xut)(USHORT); /* Translation function UCS to TCS */
extern int (*xuf)(USHORT); /* Translation function UCS to FCS */
extern USHORT (*xtu)(CHAR); /* Translation function TCS to UCS */
extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */
#endif /* UNICODE */
#else /* The same declarations again for non-ANSI comilers... */
extern CHAR (*rx)();
extern CHAR (*sx)();
extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])();
#ifdef UNICODE
extern int (*xut)();
extern int (*xuf)();
extern USHORT (*xtu)();
extern USHORT (*xfu)();
#endif /* UNICODE */
#endif /* CK_ANSIC */
#endif /* NOCSETS */
/* (PWP) external def. of things used in buffered file input and output */
#ifdef DYNAMIC
extern char *zinbuffer, *zoutbuffer;
#else
extern char zinbuffer[], zoutbuffer[];
#endif /* DYNAMIC */
extern char *zinptr, *zoutptr;
extern int zincnt, zoutcnt, zobufsize, xfrxla;
extern long crcta[], crctb[]; /* CRC-16 generation tables */
extern int rseqtbl[]; /* Rec'd-packet sequence # table */
#ifndef NOXFER
/* Criteria used by gnfile()... */
char sndafter[19] = { NUL, NUL };
char sndbefore[19] = { NUL, NUL };
char sndnafter[19] = { NUL, NUL };
char sndnbefore[19] = { NUL, NUL };
char *sndexcept[NSNDEXCEPT] = { NULL, NULL };
char *rcvexcept[NSNDEXCEPT] = { NULL, NULL };
long sndsmaller = -1L;
long sndlarger = -1L;
/* Variables defined in this module but shared by other modules. */
int xfrbel = 1;
char * ofperms = ""; /* Output file permissions */
int autopath = 0; /* SET RECEIVE PATHNAMES AUTO flag */
#ifdef CALIBRATE
#define CAL_O 3
#define CAL_M 253
int cal_j = 0;
CHAR
cal_a[] = {
16, 45, 98, 3, 52, 41, 14, 7, 76,165,122, 11,104, 77,166, 15,
160, 93, 18, 19,112, 85, 54, 23,232,213, 90, 27, 12, 81,126, 31,
4,205, 34, 35,144, 73,110, 39, 28,133,218, 43,156, 65,102, 47,
84, 61, 50, 51,208,117, 86, 55, 8,245, 74, 59, 44,125,222, 63,
80, 1,162, 67,116,105,206, 71,120, 9,250, 75, 88, 97, 6, 79,
100,221, 82, 83, 36, 89, 94, 87, 40, 21,106, 91,236,145,150, 95,
228, 33,130, 99,148,137,198,103,108,169, 42,107,184,129, 78,111,
0, 49,114,115, 32,121,254,119,172, 57,138,123,152,177, 22,127,
240,193, 2,131,176, 5, 38,135,204,229, 10,139,200,161,174,143,
128, 17,146,147, 68,153, 30,151, 72,217,170,155, 24,209, 62,159,
64,225,194,163,244,201, 70,167,216,197,234,171,188,109,230,175,
212,113,178,179,132,185,190,183,136,249,202,187, 92,241,118,191,
48,237, 66,195, 96,233,142,199,248, 37, 58,203, 60, 13,134,207,
20, 29,210,211,164,149,182,215,220, 25, 26,219,124,157,246,223,
180,141,226,227,192,101,238,231, 56, 69,154,235,252,173, 46,239,
224,253,242,243,196, 53,214,247,168,181,186,251,140,189,158,255
};
#endif /* CALIBRATE */
char * rf_err = "Error receiving file"; /* rcvfil() error message */
#ifdef CK_SPEED
short ctlp[256] = { /* Control-Prefix table */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* C0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* G0 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, /* DEL */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* C1 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* G1 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 /* 255 */
};
#endif /* CK_SPEED */
int sndsrc; /* Flag for where to get names of files to send: */
/* -1: znext() function */
/* 0: stdin */
/* >0: list in cmlist or other list */
/* -9: calibrate */
int memstr; /* Flag for input from memory string */
int funcstr; /* Flag for input from function */
int bestlen = 0;
int maxsend = 0;
int gnf_binary = 0; /* Prevailing xfer mode for gnfile */
#ifdef pdp11
#define MYINITLEN 32
#else
#define MYINITLEN 100
#endif /* pdp11 */
CHAR myinit[MYINITLEN]; /* Copy of my Send-Init data */
/* Variables local to this module */
#ifdef TLOG
#ifndef XYZ_INTERNAL
static
#endif /* XYZ_INTERNAL */
char *fncnam[] = {
"rename", "replace", "backup", "append", "discard", "ask",
"update", "dates-differ", ""
};
#endif /* TLOG */
static char *memptr; /* Pointer for memory strings */
#ifdef VMS
extern int batch;
#else
extern int backgrd;
#endif /* VMS */
#ifdef CK_CTRLZ
static int lastchar = 0;
#endif /* CK_CTRLZ */
#ifdef CK_ANSIC
static int (*funcptr)(void); /* Pointer for function strings */
#else
static int (*funcptr)();
#endif /* CK_ANSIC */
#ifdef pdp11
#define CMDSTRL 50
static char cmdstr[50]; /* System command string. */
#else
#ifdef BIGBUFOK
#define CMDSTRL 1024
#else
#define CMDSTRL 256
#endif /* BIGBUFOK */
static char cmdstr[CMDSTRL+1];
#endif /* pdp11 */
static int drain; /* For draining stacked-up ACKs. */
static int first; /* Flag for first char from input */
static CHAR t; /* Current character */
#ifdef COMMENT
static CHAR next; /* Next character */
#endif /* COMMENT */
static int ebqsent = 0; /* 8th-bit prefix bid that I sent */
static int lsstate = 0; /* Locking shift state */
static int lsquote = 0; /* Locking shift quote */
extern int quiet;
/* E N C S T R -- Encode a string from memory. */
/*
Call this instead of getpkt() if source is a string, rather than a file.
Note: Character set translation is never done in this case.
*/
#ifdef COMMENT
#define ENCBUFL 200
#ifndef pdp11
CHAR encbuf[ENCBUFL];
#else
/* This is gross, but the pdp11 root segment is out of space */
/* Will allocate it in ckuusr.c. */
extern CHAR encbuf[];
#endif /* pdp11 */
#endif /* COMMENT */
/*
Encode packet data from a string in memory rather than from a file.
Returns the length of the encoded string on success, -1 if the string
could not be completely encoded into the currently negotiated data
field length.
*/
int
#ifdef CK_ANSIC
encstr(CHAR *s)
#else /* CK_ANSIC */
encstr(s) CHAR* s;
#endif /* CK_ANSIC */
{
/*
Recoded 30 Jul 94 to use the regular data buffer and the negotiated
maximum packet size. Previously we were limited to the length of encbuf[].
Also, to return a failure code if the entire encoded string would not fit.
Modified 14 Jul 98 to return length of encoded string.
*/
int m, rc, slen; char *p;
if (!data) { /* Watch out for null pointers. */
debug(F100,"SERIOUS ERROR: encstr data == NULL","",0);
return(-1);
}
if (!s) s = (CHAR *)""; /* Ditto. */
slen = strlen((char *)s); /* Length of source string. */
debug(F111,"encstr",s,slen);
rc = 0; /* Return code. */
m = memstr; p = memptr; /* Save these. */
memptr = (char *)s; /* Point to the string. */
debug(F101,"encstr memptr 1","",memptr);
memstr = 1; /* Flag memory string as source. */
first = 1; /* Initialize character lookahead. */
*data = NUL; /* In case s is empty */
debug(F101,"encstr spsiz","",spsiz);
rc = getpkt(spsiz,0); /* Fill a packet from the string. */
debug(F101,"encstr getpkt rc","",rc);
if (rc > -1 && memptr < (char *)(s + slen)) { /* Means we didn't encode */
rc = -1; /* the whole string. */
debug(F101,"encstr string too big","",size);
}
debug(F101,"encstr getpkt rc","",rc);
memstr = m; /* Restore memory string flag */
memptr = p; /* and pointer */
first = 1; /* Put this back as we found it. */
return(rc);
}
/* Output functions passed to 'decode': */
int /* Put character in server command buffer */
#ifdef CK_ANSIC
putsrv(char c)
#else
putsrv(c) register char c;
#endif /* CK_ANSIC */
/* putsrv */ {
*srvptr++ = c;
*srvptr = '\0'; /* Make sure buffer is null-terminated */
return(0);
}
int /* Output character to console. */
#ifdef CK_ANSIC
puttrm(char c)
#else
puttrm(c) register char c;
#endif /* CK_ANSIC */
/* puttrm */ {
extern int rcdactive;
#ifndef NOSPL
extern char * qbufp; /* If REMOTE QUERY active, */
extern int query, qbufn; /* also store response in */
if (query && qbufn++ < 1024) { /* query buffer. */
*qbufp++ = c;
*qbufp = NUL;
}
if (!query || !xcmdsrc)
#endif /* NOSPL */
/*
This routine is used (among other things) for printing the server's answer
to a REMOTE command. But REMOTE CD is special because it isn't really
asking for an answer from the server. Thus some people want to suppress
the confirmation message (e.g. when the server sends back the actual path
of the directory CD'd to), and expect to be able to do this with SET QUIET
ON. But they would not want SET QUIET ON to suppress the other server
replies, which are pointless without their answers. Thus the "rcdactive"
flag (REMOTE CD command is active). Thu Oct 10 16:38:21 2002
*/
if (!(quiet && rcdactive)) /* gross, yuk */
conoc(c);
return(0);
}
#endif /* NOXFER */
int /* Output char to file. */
#ifdef CK_ANSIC
putmfil(char c) /* Just like putfil but to ZMFILE */
#else /* rather than ZOFILE... */
putmfil(c) register char c;
#endif /* CK_ANSIC */
/* putmfil */ {
debug(F000,"putfil","",c);
if (zchout(ZMFILE, (char) (c & fmask)) < 0) {
czseen = 1;
debug(F101,"putfil zchout write error, setting czseen","",1);
return(-1);
}
return(0);
}
int /* Output char to nowhere. */
#ifdef CK_ANSIC
putnowhere(char c)
#else
putnowhere(c) register char c;
#endif /* CK_ANSIC */
/* putnowhere */ {
return(0);
}
int /* Output char to file. */
#ifdef CK_ANSIC
putfil(char c)
#else
putfil(c) register char c;
#endif /* CK_ANSIC */
/* putfil */ {
debug(F000,"putfil","",c);
if (zchout(ZOFILE, (char) (c & fmask)) < 0) {
czseen = 1; /* If write error... */
debug(F101,"putfil zchout write error, setting czseen","",1);
return(-1);
}
return(0);
}
/*
The following function is a wrapper for putfil(). The only reason for its
existence is to be passed as a function pointer to decode(), which treats
putfil() itself specially -- bypassing it and using an internal macro
instead to speed things up. Use zputfil() instead of putfil() in cases where
we do not want this to happen, e.g. when we need to send output to the file
with a mixture of zchout() and zsout()/zsoutl() calls (as is the case with
incoming short-form REMOTE command replies redirected to a file), which would
otherwise result in data written to the file out of order.
*/
int
#ifdef CK_ANSIC
zputfil(char c)
#else
zputfil(c) register char c;
#endif /* CK_ANSIC */
/* zputfil */ {
return(putfil(c));
}
#ifndef NOXFER
/* D E C O D E -- Kermit packet decoding procedure */
/*
Call with string to be decoded and an output function.
Returns 0 on success, -1 on failure (e.g. disk full).
This is the "inner loop" when receiving files, and must be coded as
efficiently as possible. Note some potential problems: if a packet
is badly formed, having a prefixed sequence ending prematurely, this
function, as coded, could read past the end of the packet. This has
never happened, thus the additional (time-consuming) tests have not
been added.
*/
static CHAR *xdbuf; /* Global version of decode()'s buffer pointer */
/* for use by translation functions. */
/* Function for pushing a character onto decode()'s input stream. */
VOID
#ifdef CK_ANSIC
zdstuff(CHAR c)
#else
zdstuff(c) CHAR c;
#endif /* CK_ANSIC */
/* zdstuff */ {
xdbuf--; /* Back up the pointer. */
*xdbuf = c; /* Stuff the character. */
}
#ifdef CKTUNING
/*
Trimmed-down packet decoder for binary-mode no-parity transfers.
decode() is the full version.
*/
int
#ifdef CK_ANSIC
bdecode(CHAR *buf, int (*fn)(char))
#else
bdecode(buf,fn) register CHAR *buf; register int (*fn)();
#endif /* CK_ANSIC */
/* bdecode */ {
register unsigned int a, a7; /* Various copies of current char */
int ccpflg; /* For Ctrl-unprefixing stats */
int t; /* Int version of character */
int len;
long z; /* For CRC calculation */
CHAR c; /* Current character */
if (!binary || parity || fn != putfil) /* JUST IN CASE */
return(decode(buf,fn,1));
debug(F100,"BDECODE","",0);
xdbuf = buf; /* Global copy of source pointer. */
len = rln; /* Number of bytes in data field */
while (len > 0) {
a = *xdbuf++ & 0xff; /* Get next character */
len--;
rpt = 0; /* Initialize repeat count. */
if (a == rptq && rptflg) { /* Got a repeat prefix? */
rpt = xunchar(*xdbuf++ & 0xFF); /* Yes, get the repeat count, */
rptn += rpt;
a = *xdbuf++ & 0xFF; /* and get the prefixed character. */
len -= 2;
}
ccpflg = 0; /* Control prefix flag. */
if (a == ctlq) { /* If control prefix, */
a = *xdbuf++ & 0xFF; /* get its operand */
len--;
a7 = a & 0x7F; /* and its low 7 bits. */
if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') { /* Controllify */
a = ctl(a); /* if in control range. */
a7 = a & 0x7F;
ccpflg = 1; /* Note that we did this */
ccp++; /* Count for stats */
}
} else a7 = a & 0x7f; /* Not control quote */
if (a7 < 32 || a7 == 127) /* A bare control character? */
if (!ccpflg) ccu++; /* Count it */
if (!rpt) rpt = 1;
for (; rpt > 0; rpt--) { /* Output the char RPT times */
#ifdef CALIBRATE
if (calibrate) {
ffc++;
continue;
}
#endif /* CALIBRATE */
#ifdef OS2
if (xflg && !remfile) { /* Write to virtual screen */
char _a;
_a = a & fmask;
t = conoc(_a);
if (t < 1)
t = -1;
} else
#endif /* OS2 */
t = zmchout(a & fmask); /* zmchout is a macro */
if (t < 0) {
debug(F101,"bdecode write error - errno","",errno);
return(-1);
}
ffc++; /* Count the character */
if (docrc && !remfile) { /* Update file CRC */
c = a; /* Force conversion to unsigned char */
z = crc16 ^ (long)c;
crc16 = (crc16 >> 8) ^
(crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
}
}
#ifdef CK_CTRLZ
lastchar = a;
#endif /* CK_CTRLZ */
}
return(0);
}
#endif /* CKTUNING */
#endif /* NOXFER */
/* P N B Y T E -- Output next byte to file or other destination */
static long offc = 0L;
static int
#ifdef CK_ANSIC
pnbyte(CHAR c, int (*fn)(char))
#else
pnbyte(c,fn) CHAR c; int (*fn)();
#endif /* CK_ANSIC */
/* pnbyte */ {
int rc;
long z;
#ifdef OS2
#ifndef NOXFER
if (xflg && !remfile) { /* Write to virtual screen */
char _a;
_a = c & fmask;
rc = conoc(_a);
if (rc < 1)
return(-1);
} else
#endif /* NOXFER */
#endif /* OS2 */
{
if (fn == putfil) { /* Execute output function */
rc = zmchout(c); /* to-file macro (fast) */
} else if (!fn) {
rc = putchar(c); /* to-screen macro (fast) */
} else {
rc = (*fn)(c); /* function call (not as fast) */
}
if (rc < 0)
return(rc);
}
/*
Both xgnbyte() and xpnbyte() increment ffc (the file byte counter).
During file transfer, only one of these functions is called. However,
the TRANSLATE command is likely to call them both. offc, therefore,
contains the output byte count, necessary for handling the UCS-2 BOM.
NOTE: It might be safe to just test for W_SEND, FTP or not.
*/
if ((what & (W_FTP|W_SEND)) != (W_FTP|W_SEND)) {
offc++; /* Count the byte */
ffc++; /* Count the byte */
}
#ifndef NOXFER
if (docrc && !xflg && !remfile) { /* Update file CRC */
z = crc16 ^ (long)c;
crc16 = (crc16 >> 8) ^
(crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
}
#endif /* NOXFER */
return(1);
}
/*
X P N B Y T E -- Translate and put next byte to file.
Only for Unicode. Call with next untranslated byte from incoming
byte stream, which can be in any Transfer Character Set: UCS-2, UTF-8,
Latin-1, Latin-Hebrew, etc. Translates to the file character set and
writes bytes to the output file. Call with character to translate
as an int, plus the transfer character set (to translate from) and the
file character set (to translate to), or -1,0,0 to reset the UCS-2 byte
number (which should be done at the beginning of a file). If the transfer
(source) character-set is UCS-2, bytes MUST arrive in Big-Endian order.
Returns:
-1: On error
0: Nothing to write (mid-sequence)
>0: Number of bytes written.
*/
#ifdef KANJI
static int jstate = 0, jx = 0; /* For outputting JIS-7 */
static char jbuf[16] = { NUL, NUL };
#endif /* KANJI */
int
#ifdef CK_ANSIC
xpnbyte(int a, int tcs, int fcs, int (*fn)(char))
#else
xpnbyte(a,tcs,fcs,fn) int a, tcs, fcs; int (*fn)();
#endif /* CK_ANSIC */
/* xpnbyte */ {
#ifdef UNICODE
extern int ucsbom; /* Byte order */
#endif /* UNICODE */
/* CHAR c; */ /* Unsigned char worker */
static union ck_short uc, eu, sj; /* UCS-2, EUC, and Shift-JIS workers */
USHORT ch; /* ditto... */
USHORT * us = NULL; /* ditto... */
int c7, rc, haveuc = 0; /* Return code and UCS-2 flag */
int utferror = 0; /* UTF-8 error */
static int bn = 0; /* UCS-2 byte number */
int swapping = 0; /* Swapping UCS bytes to output? */
/* swapping must be 0 or 1 */
if (a == -1 && (tcs | fcs) == 0) { /* Reset in case previous run */
bn = 0; /* left bn at 1... */
offc = 0L;
debug(F101,"xpnbyte RESET","",bn);
return(0);
}
debug(F001,"xpnbyte a","",a);
#ifdef UNICODE
/*
byteorder = hardware byte order of this machine.
ucsorder = override by SET FILE UCS BYTE-ORDER command.
fileorder = byte order of UCS-2 input file detected from BOM.
swapping applies only when output charset is UCS-2.
*/
if (ucsorder != 1 && ucsorder != 0) /* Also just in case... */
ucsorder = byteorder;
if ((byteorder && !ucsorder) || (!byteorder && fileorder))
swapping = 1; /* Swapping bytes to output */
#ifdef COMMENT
debug(F101,"xpnbyte ucsorder","",ucsorder);
debug(F101,"xpnbyte swapping","",swapping);
#endif /* COMMENT */
if (tcs == TC_UTF8) { /* 'a' is from a UTF-8 stream */
ch = a;
if (fcs == TC_UTF8) /* Output is UTF-8 too */
return(pnbyte(ch,fn)); /* so just copy. */
rc = utf8_to_ucs2(ch,&us); /* Otherwise convert to UCS-2 */
if (rc == 0) { /* Done with this sequence */
uc.x_short = *us; /* We have a Unicode */
haveuc = 1;
} else if (rc < 0) { /* Error */
debug(F101,"xpnbyte UTF-8 conversion error","",rc);
haveuc = 1; /* Replace by U+FFFD */
uc.x_short = *us;
utferror = 1;
} else /* Sequence incomplete */
return(0);
} else if (tcs == TC_UCS2) { /* 'a' is UCS-2 */
/* Here we have incoming UCS-2 in guaranteed Big Endian order */
/* so we must exchange bytes if local machine is Little Endian. */
switch (bn) { /* Which byte? */
case 0: /* High... */
uc.x_char[byteorder] = (unsigned)a & 0xff;
bn++;
return(0); /* Wait for next */
case 1: /* Low... */
uc.x_char[1-byteorder] = (unsigned)a & 0xff;
bn = 0; /* Done with sequence */
haveuc = 1; /* Have a Unicode */
}
} else
#endif /* UNICODE */
#ifdef KANJI /* Whether UNICODE is defined or not */
if (tcs == TC_JEUC) { /* Incoming Japanese EUC */
int bad = 0;
static int kanji = 0; /* Flags set in case 0 for case 1 */
static int kana = 0;
switch (bn) { /* Byte number */
case 0: /* Byte 0 */
eu.x_short = 0;
if ((a & 0x80) == 0) {
sj.x_short = (unsigned)a & 0xff; /* Single byte */
kanji = kana = 0;
} else { /* Double byte */
c7 = a & 0x7f;
if (c7 > 0x20 && c7 < 0x7f) { /* Kanji */
eu.x_char[byteorder] = (CHAR) a; /* Store first byte */
bn++; /* Set up for second byte */
kanji = 1;
kana = 0;
return(0);
} else if (a == 0x8e) { /* SS2 -- Katakana prefix */
eu.x_char[byteorder] = (CHAR) a; /* Save it */
bn++;
kana = 1;
kanji = 0;
return(0);
} else {
bad++;
}
}
break;
case 1: /* Byte 1 */
bn = 0;
if (kanji) {
eu.x_char[1-byteorder] = (CHAR) a;
sj.x_short = eu_to_sj(eu.x_short);
break;
} else if (kana) {
sj.x_short = (CHAR) (a | 0x80);
break;
} else { /* (shouldn't happen) */
bad++;
}
}
/* Come here with one Shift-JIS character */
#ifdef UNICODE
if (bad) {
uc.x_short = 0xfffd;
} else {
uc.x_short = sj_to_un(sj.x_short); /* Convert to Unicode */
}
haveuc = 1;
#endif /* UNICODE */
} else
#endif /* KANJI */
#ifdef UNICODE
uc.x_short = (unsigned)a & 0xff; /* Latin-1 or whatever... */
/* Come here with uc = the character to be translated. */
/* If (haveuc) it's UCS-2 in native order, otherwise it's a byte. */
debug(F101,"xpnbyte haveuc","",haveuc);
if (haveuc) { /* If we have a Unicode... */
debug(F001,"xpnbyte uc.x_short","[A]",uc.x_short);
debug(F101,"xpnbyte feol","",feol);
if (what & W_XFER) { /* If transferring a file */
if (feol && uc.x_short == CR) { /* handle eol conversion. */
return(0);
} else if (feol && uc.x_short == LF) {
uc.x_short = feol;
}
}
debug(F001,"xpnbyte uc.x_short","[B]",uc.x_short);
if (fcs == FC_UCS2) { /* And FCS is UCS-2 */
/* Write out the bytes in the appropriate byte order */
int count = 0;
#ifndef IKSDONLY
#ifdef OS2
extern int k95stdout,wherex[],wherey[];
extern unsigned char colorcmd;
union {
USHORT ucs2;
UCHAR bytes[2];
} output;
#endif /* OS2 */
#endif /* IKSDONLY */
if (offc == 0L && ucsbom) { /* Beginning of file? */
#ifndef IKSDONLY
#ifdef OS2
if (fn == NULL && !k95stdout && !inserver) {
offc++;
#ifdef COMMENT
/* Don't print the BOM to the display */
output.bytes[0] = (!ucsorder ? 0xff : 0xfe);
output.bytes[1] = (!ucsorder ? 0xfe : 0xff);
VscrnWrtUCS2StrAtt(VCMD,
&output.ucs2,
1,
wherey[VCMD],
wherex[VCMD],
&colorcmd
);
#endif /* COMMENT */
} else
#endif /* OS2 */
#endif /* IKSDONLY */
{
if ((rc = pnbyte((ucsorder ? 0xff : 0xfe),fn)) < 0)
return(rc); /* BOM */
if ((rc = pnbyte((ucsorder ? 0xfe : 0xff),fn)) < 0)
return(rc);
}
count += 2;
}
if (utferror) {
#ifndef IKSDONLY
#ifdef OS2
if (fn == NULL && !k95stdout && !inserver) {
offc++;
output.bytes[0] = (!ucsorder ? 0xfd : 0xff);
output.bytes[1] = (!ucsorder ? 0xff : 0xfd);
VscrnWrtUCS2StrAtt(VCMD,
&output.ucs2,
1,
wherey[VCMD],
wherex[VCMD],
&colorcmd
);
} else
#endif /* OS2 */
#endif /* IKSDONLY */
{
if ((rc = pnbyte((ucsorder ? 0xfd : 0xff),fn)) < 0)
return(rc);
if ((rc = pnbyte((ucsorder ? 0xff : 0xfd),fn)) < 0)
return(rc);
}
count += 2;
}
#ifndef IKSDONLY
#ifdef OS2
if (fn == NULL && !k95stdout && !inserver) {
offc++;
output.bytes[0] = uc.x_char[1-swapping];
output.bytes[1] = uc.x_char[swapping];
VscrnWrtUCS2StrAtt(VCMD,
&output.ucs2,
1,
wherey[VCMD],
wherex[VCMD],
&colorcmd
);
} else
#endif /* OS2 */
#endif /* IKSDONLY */
{
if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0)
return(rc);
if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0)
return(rc);
}
count += 2;
return(count);
} else if (fcs == FC_UTF8) { /* Convert to UTF-8 */
CHAR * buf = NULL;
int i, count;
if (utferror) {
if ((rc = pnbyte((ucsorder ? 0xbd : 0xff),fn)) < 0)
return(rc);
if ((rc = pnbyte((ucsorder ? 0xff : 0xbd),fn)) < 0)
return(rc);
}
if ((count = ucs2_to_utf8(uc.x_short,&buf)) < 1)
return(-1);
debug(F011,"xpnbyte buf",buf,count);
for (i = 0; i < count; i++)
if ((rc = pnbyte(buf[i],fn)) < 0)
return(rc);
if (utferror)
count += 2;
return(count);
} else { /* Translate UCS-2 to byte */
if (uc.x_short == 0x2028 || uc.x_short == 0x2029) {
if (utferror)
pnbyte(UNK,fn);
if (feol)
return(pnbyte((CHAR)feol,fn));
if ((rc = pnbyte((CHAR)CR,fn)) < 0)
return(rc);
if ((rc = pnbyte((CHAR)LF,fn)) < 0)
return(rc);
else
return(utferror ? 3 : 2);
} else if (xuf) { /* UCS-to-FCS function */
int x = 0;
if (utferror)
pnbyte(UNK,fn);
if ((rc = (*xuf)(uc.x_short)) < 0) /* These can fail... */
ch = UNK;
else
ch = (unsigned)((unsigned)rc & 0xffff);
x = pnbyte(ch,fn);
if (x < 0)
return(x);
else if (utferror)
x++;
return(x);
#ifdef KANJI
/* Also see the non-Unicode Kanji section further down in this function. */
} else if (fcsinfo[fcs].alphabet == AL_JAPAN) {
/* Translate UCS-2 to Japanese set */
debug(F001,"xpnbyte uc","",uc.x_short);
sj.x_short = un_to_sj(uc.x_short); /* First to Shift-JIS */
debug(F001,"xpnbyte sj","",sj.x_short);
switch (fcs) { /* File character set */
case FC_SHJIS: /* Shift-JIS -- just output it */
if (sj.x_char[byteorder]) /* But not high byte if zero */
if ((rc = pnbyte((CHAR)sj.x_char[byteorder],fn)) < 0)
return(rc);
if ((rc = pnbyte((CHAR)sj.x_char[1-byteorder],fn)) < 0)
return(rc);
return(2);
case FC_JEUC: /* EUC-JP */
eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
debug(F001,"xpnbyte eu","",eu.x_short);
if (eu.x_short == 0xffff) { /* Bad */
if ((rc = pnbyte(UNK,fn)) < 0)
return(rc);
return(1);
} else { /* Good */
int count = 0; /* Write high byte if not zero */
if (eu.x_char[byteorder]) {
if ((rc=pnbyte((CHAR)eu.x_char[byteorder],fn)) < 0)
return(rc);
count++;
}
/* Always write low byte */
if ((rc = pnbyte((CHAR)eu.x_char[1-byteorder],fn)) < 0)
return(rc);
count++;
return(count);
}
break;
case FC_JIS7: /* JIS-7 */
case FC_JDEC: /* DEC Kanji */
eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
if (eu.x_short == 0xffff) { /* Bad */
debug(F001,"xpnbyte bad eu","",eu.x_short);
if ((rc = pnbyte(UNK,fn)) < 0)
return(rc);
return(1);
} else { /* Good */
int i;
/* Use another name - 'a' hides parameter */
/* It's OK as is but causes compiler warnings */
char a = eu.x_char[1-byteorder]; /* Low byte */
debug(F001,"xpnbyte eu","",eu.x_short);
if (eu.x_char[byteorder] == 0) { /* Roman */
switch (jstate) {
case 1: /* Current state is Katakana */
jbuf[0] = 0x0f; /* SI */
jbuf[1] = a;
jx = 2;
break;
case 2: /* Current state is Kanji */
jbuf[0] = 0x1b; /* ESC */
jbuf[1] = 0x28; /* ( */
jbuf[2] = 0x4a; /* J */
jbuf[3] = a;
jx = 4;
break;
default: /* Current state is Roman */
jbuf[0] = a;
jx = 1;
break;
}
jstate = 0; /* New state is Roman */
} else if (eu.x_char[byteorder] == 0x8e) { /* Kana */
jx = 0;
switch (jstate) {
case 2: /* from Kanji */
jbuf[jx++] = 0x1b; /* ESC */
jbuf[jx++] = 0x28; /* ( */
jbuf[jx++] = 0x4a; /* J */
case 0: /* from Roman */
jbuf[jx++] = 0x0e; /* SO */
default: /* State is already Kana*/
jbuf[jx++] = (a & 0x7f); /* and the char */
break;
}
jstate = 1; /* New state is Katakana */
} else { /* Kanji */
jx = 0;
switch (jstate) {
case 1: /* Current state is Katakana */
jbuf[jx++] = 0x0f; /* SI */
case 0: /* Current state is Roman */
jbuf[jx++] = 0x1b; /* ESC */
jbuf[jx++] = 0x24; /* $ */
jbuf[jx++] = 0x42; /* B */
default: /* Current state is already Kanji */
jbuf[jx++] = eu.x_char[byteorder] & 0x7f;
jbuf[jx++] = eu.x_char[1-byteorder] & 0x7f;
break;
}
jstate = 2; /* Set state to Kanji */
}
for (i = 0; i < jx; i++) /* Output the result */
if ((rc = pnbyte(jbuf[i],fn)) < 0)
return(rc);
return(jx); /* Return its length */
}
}
#endif /* KANJI */
} else { /* No translation function */
int count = 0;
if (utferror) {
if ((rc = pnbyte((ucsorder ? 0xfd : 0xff),fn)) < 0)
return(rc);
if ((rc = pnbyte((ucsorder ? 0xff : 0xfd),fn)) < 0)
return(rc);
count += 2;
}
if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0)
return(rc);
if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0)
return(rc);
count += 2;
return(count);
}
}
} else { /* Byte to Unicode */
if (xtu) { /* TCS-to-UCS function */
if (((tcsinfo[tcs].size > 128) && (uc.x_short & 0x80)) ||
tcsinfo[tcs].size <= 128)
uc.x_short = (*xtu)(uc.x_short);
}
if (fcs == FC_UCS2) { /* And FCS is UCS-2 */
/* Write out the bytes in the appropriate byte order */
if (offc == 0 && ucsbom) { /* Beginning of file? */
if ((rc = pnbyte((ucsorder ? 0xff : 0xfe),fn)) < 0) /* BOM */
return(rc);
if ((rc = pnbyte((ucsorder ? 0xfe : 0xff),fn)) < 0)
return(rc);
}
if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0)
return(rc);
if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0)
return(rc);
return(2);
} else if (fcs == FC_UTF8) { /* Convert to UTF-8 */
CHAR * buf = NULL;
int i, count;
if ((count = ucs2_to_utf8(uc.x_short,&buf)) < 1)
return(-1);
for (i = 0; i < count; i++)
if ((rc = pnbyte(buf[i],fn)) < 0)
return(rc);
return(count);
} else {
debug(F100,"xpnbyte impossible combination","",0);
return(-1);
}
}
#else
#ifdef KANJI
/*
This almost, but not quite, duplicates the Kanji section above.
There is no doubt a way to combine the sections more elegantly,
but probably only at the expense of additional execution overhead.
As matters stand, be careful to reflect any changes in this section
to the other Kanji section above.
*/
if (tcs == TC_JEUC) { /* Incoming Japanese EUC */
int count = 0;
switch (fcs) { /* File character set */
case FC_SHJIS: /* Shift-JIS -- just output it */
if (sj.x_char[byteorder]) /* But not high byte if zero */
if ((rc = pnbyte((CHAR)sj.x_char[byteorder],fn)) < 0)
return(rc);
count++;
if ((rc = pnbyte((CHAR)sj.x_char[1-byteorder],fn)) < 0)
return(rc);
count++;
return(count);
case FC_JEUC: /* EUC-JP */
eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
debug(F001,"xpnbyte FC_JEUC eu","",eu.x_short);
if (eu.x_short == 0xffff) { /* Bad */
if ((rc = pnbyte(UNK,fn)) < 0)
return(rc);
return(1);
} else { /* Good */
int count = 0; /* Write high byte if not zero */
if (eu.x_char[byteorder]) {
if ((rc = pnbyte((CHAR)eu.x_char[byteorder],fn)) < 0)
return(rc);
count++;
}
/* Always write low byte */
if ((rc = pnbyte((CHAR)eu.x_char[1-byteorder],fn)) < 0)
return(rc);
count++;
return(count);
}
break;
case FC_JIS7: /* JIS-7 */
case FC_JDEC: /* DEC Kanji */
eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */
if (eu.x_short == 0xffff) { /* Bad */
debug(F001,"xpnbyte FC_JIS7 bad eu","",eu.x_short);
if ((rc = pnbyte(UNK,fn)) < 0)
return(rc);
return(1);
} else { /* Good */
int i;
char a = eu.x_char[1-byteorder]; /* Low byte */
debug(F001,"xpnbyte FC_JIS7 eu","",eu.x_short);
if (eu.x_char[byteorder] == 0) { /* Roman */
switch (jstate) {
case 1: /* Current state is Katakana */
jbuf[0] = 0x0f; /* SI */
jbuf[1] = a;
jx = 2;
break;
case 2: /* Current state is Kanji */
jbuf[0] = 0x1b; /* ESC */
jbuf[1] = 0x28; /* ( */
jbuf[2] = 0x4a; /* J */
jbuf[3] = a;
jx = 4;
break;
default: /* Current state is Roman */
jbuf[0] = a;
jx = 1;
break;
}
jstate = 0; /* New state is Roman */
} else if (eu.x_char[byteorder] == 0x8e) { /* Kana */
jx = 0;
switch (jstate) {
case 2: /* from Kanji */
jbuf[jx++] = 0x1b; /* ESC */
jbuf[jx++] = 0x28; /* ( */
jbuf[jx++] = 0x4a; /* J */
case 0: /* from Roman */
jbuf[jx++] = 0x0e; /* SO */
default: /* State is already Kana*/
jbuf[jx++] = (a & 0x7f); /* and the char */
break;
}
jstate = 1; /* New state is Katakana */
} else { /* Kanji */
jx = 0;
switch (jstate) {
case 1: /* Current state is Katakana */
jbuf[jx++] = 0x0f; /* SI */
case 0: /* Current state is Roman */
jbuf[jx++] = 0x1b; /* ESC */
jbuf[jx++] = 0x24; /* $ */
jbuf[jx++] = 0x42; /* B */
default: /* Current state is already Kanji */
jbuf[jx++] = eu.x_char[byteorder] & 0x7f;
jbuf[jx++] = eu.x_char[1-byteorder] & 0x7f;
break;
}
jstate = 2; /* Set state to Kanji */
}
for (i = 0; i < jx; i++) /* Output the result */
if ((rc = pnbyte(jbuf[i],fn)) < 0)
return(rc);
return(jx); /* Return its length */
}
default:
if (sj.x_short < 0x80)
return(sj.x_short);
else
return('?');
}
}
#endif /* KANJI */
#endif /* UNICODE */
debug(F100,"xpnbyte BAD FALLTHRU","",0);
return(-1);
}
#ifndef NOXFER
/* D E C O D E -- Kermit Data-packet decoder */
int
#ifdef CK_ANSIC
decode(CHAR *buf, int (*fn)(char), int xlate)
#else
decode(buf,fn,xlate) register CHAR *buf; register int (*fn)(); int xlate;
#endif /* CK_ANSIC */
/* decode */ {
register unsigned int a, a7, a8, b8; /* Various copies of current char */
int t; /* Int version of character */
int ssflg; /* Character was single-shifted */
int ccpflg; /* For Ctrl-unprefixing stats */
int len;
long z;
CHAR c;
/*
Catch the case in which we are asked to decode into a file that is not open,
for example, if the user interrupted the transfer, but the other Kermit
keeps sending.
*/
if ((cxseen || czseen || discard) && (fn == putfil))
return(0);
#ifdef COMMENT
#ifdef CKTUNING
if (binary && !parity)
return(bdecode(buf,fn));
#endif /* CKTUNING */
#endif /* COMMENT */
debug(F100,"DECODE","",0);
xdbuf = buf; /* Make global copy of pointer. */
rpt = 0; /* Initialize repeat count. */
len = rln; /* Number of bytes in data field */
while (len > 0) { /* Loop for each byte */
a = *xdbuf++ & 0xff; /* Get next character */
len--;
if (a == rptq && rptflg) { /* Got a repeat prefix? */
rpt = xunchar(*xdbuf++ & 0xFF); /* Yes, get the repeat count, */
rptn += rpt;
a = *xdbuf++ & 0xFF; /* and get the prefixed character. */
len -= 2;
}
b8 = lsstate ? 0200 : 0; /* 8th-bit value from SHIFT-STATE */
if (ebqflg && a == ebq) { /* Have 8th-bit prefix? */
b8 ^= 0200; /* Yes, invert the 8th bit's value, */
ssflg = 1; /* remember we did this, */
a = *xdbuf++ & 0xFF; /* and get the prefixed character. */
len--;
} else ssflg = 0;
ccpflg = 0;
if (a == ctlq) { /* If control prefix, */
a = *xdbuf++ & 0xFF; /* get its operand */
len--;
a7 = a & 0x7F; /* and its low 7 bits. */
if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') { /* Controllify */
a = ctl(a); /* if in control range. */
a7 = a & 0x7F;
ccpflg = 1; /* Note that we did this */
ccp++; /* Count for stats */
}
} else a7 = a & 0x7f; /* Not control quote */
if (a7 < 32 || a7 == 127) { /* Control character? */
if (!ccpflg) ccu++; /* A bare one, count it */
if (lscapu) { /* If doing locking shifts... */
if (lsstate) /* If SHIFTED */
a8 = (a & ~b8) & 0xFF; /* Invert meaning of 8th bit */
else /* otherwise */
a8 = a | b8; /* OR in 8th bit */
/* If we're not in a quoted sequence */
if (!lsquote && (!lsstate || !ssflg)) {
if (a8 == DLE) { /* Check for DLE quote */
lsquote = 1; /* prefixed by single shift! */
continue;
} else if (a8 == SO) { /* Check for Shift-Out */
lsstate = 1; /* SHIFT-STATE = SHIFTED */
continue;
} else if (a8 == SI) { /* or Shift-In */
lsstate = 0; /* SHIFT-STATE = UNSHIFTED */
continue;
}
} else lsquote = 0;
}
}
a |= b8; /* OR in the 8th bit */
if (rpt == 0) rpt = 1; /* If no repeats, then one */
#ifndef NOCSETS
if (!binary) { /* If in text mode, */
if (tcharset != TC_UCS2) {
if (feol && a == CR) /* Convert CRLF to newline char */
continue;
if (feol && a == LF)
a = feol;
}
if (xlatype == XLA_BYTE) /* Byte-for-byte - do it now */
if (xlate && rx) a = (*rx)((CHAR) a);
}
#endif /* NOCSETS */
/* (PWP) Decoding speedup via buffered output and a macro... */
if (fn == putfil) {
for (; rpt > 0; rpt--) { /* Output the char RPT times */
#ifdef CALIBRATE
if (calibrate) {
ffc++;
continue;
}
#endif /* CALIBRATE */
/* Note: The Unicode and Kanji sections can probably be combined now; */
/* the Unicode method (xpnbyte()) covers Kanji too. */
#ifdef UNICODE
if (!binary && xlatype == XLA_UNICODE)
t = xpnbyte((unsigned)((unsigned)a & 0xff),
tcharset,
fcharset,
fn
);
else
#endif /* UNICODE */
#ifdef KANJI
if (!binary && tcharset == TC_JEUC &&
fcharset != FC_JEUC) { /* Translating from J-EUC */
if (ffc == 0L) xkanjf();
if (xkanji(a,fn) < 0) /* to something else? */
return(-1);
else t = 1;
} else
#endif /* KANJI */
{
#ifdef OS2
if (xflg && !remfile) { /* Write to virtual screen */
char _a;
_a = a & fmask;
t = conoc(_a);
if (t < 1)
t = -1;
} else
#endif /* OS2 */
t = zmchout(a & fmask); /* zmchout is a macro */
}
if (t < 0) {
debug(F101,"decode write errno","",errno);
return(-1);
}
#ifdef UNICODE
if (xlatype != XLA_UNICODE || binary) {
ffc++; /* Count the character */
if (docrc && !xflg && !remfile) { /* Update file CRC */
c = a; /* Force conversion to unsigned char */
z = crc16 ^ (long)c;
crc16 = (crc16 >> 8) ^
(crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
}
}
#endif /* UNICODE */
}
} else { /* Output to something else. */
a &= fmask; /* Apply file mask */
for (; rpt > 0; rpt--) { /* Output the char RPT times */
#ifdef CALIBRATE
if (calibrate) {
ffc++;
continue;
}
#endif /* CALIBRATE */
if ((*fn)((char) a) < 0) return(-1); /* Send to output func. */
}
}
#ifdef CK_CTRLZ
lastchar = a;
#endif /* CK_CTRLZ */
}
return(0);
}
/* G E T P K T -- Fill a packet data field */
/*
Gets characters from the current source -- file or memory string.
Encodes the data into the packet, filling the packet optimally.
Set first = 1 when calling for the first time on a given input stream
(string or file).
Call with:
bufmax -- current send-packet size
xlate -- flag: 0 to skip character-set translation, 1 to translate
Uses global variables:
t -- current character.
first -- flag: 1 to start up, 0 for input in progress, -1 for EOF.
next -- next character (not used any more).
data -- pointer to the packet data buffer.
size -- number of characters in the data buffer.
memstr - flag that input is coming from a memory string instead of a file.
memptr - pointer to string in memory.
(*sx)() character set translation function
Returns:
The size as value of the function, and also sets global "size",
and fills (and null-terminates) the global data array.
Returns:
0 on EOF.
-1 on fatal (internal) error.
-3 on timeout (e.g. when reading data from a pipe).
Rewritten by Paul W. Placeway (PWP) of Ohio State University, March 1989.
Incorporates old getchx() and encode() inline to reduce function calls,
uses buffered input for much-improved efficiency, and clears up some
confusion with line termination (CRLF vs LF vs CR).
Rewritten again by Frank da Cruz to incorporate locking shift mechanism,
May 1991. And again in 1998 for efficiency, etc, with a separate
bgetpkt() split out for binary-mode no-parity transfers.
*/
/*
Note: Separate Kanji support dates from circa 1991 and now (1999) can most
likely be combined with the the Unicode support: the xgnbyte()/xpnbyte()
mechanism works for both Unicode and Kanji.
*/
#ifdef KANJI
int
kgetf(
#ifdef CK_ANSIC
VOID
#endif /* CK_ANSIC */
) {
if (funcstr)
return((*funcptr)());
else
return(zminchar());
}
int
kgetm(
#ifdef CK_ANSIC
VOID
#endif /* CK_ANSIC */
) {
int x;
if ((x = *memptr++)) return(x);
else return(-1);
}
#endif /* KANJI */
/*
Lookahead function to decide whether locking shift is worth it. Looks at
the next four input characters to see if all of their 8th bits match the
argument. Call with 0 or 0200. Returns 1 on match, 0 if they don't match.
If we don't happen to have at least 4 more characters waiting in the input
buffer, returns 1. Note that zinptr points two characters ahead of the
current character because of repeat-count lookahead.
*/
int
lslook(b) unsigned int b; { /* Locking Shift Lookahead */
int i;
if (zincnt < 3) /* If not enough chars in buffer, */
return(1); /* force shift-state switch. */
b &= 0200; /* Force argument to proper form. */
for (i = -1; i < 3; i++) /* Look at next 5 characters to */
if (((*(zinptr+i)) & 0200) != b) /* see if all their 8th bits match. */
return(0); /* They don't. */
return(1); /* They do. */
}
/* Routine to compute maximum data length for packet to be filled */
int
maxdata() { /* Get maximum data length */
int n, len;
debug(F101,"maxdata spsiz 1","",spsiz);
if (spsiz < 0) /* How could this happen? */
spsiz = DSPSIZ;
debug(F101,"maxdata spsiz 2","",spsiz);
n = spsiz - 5; /* Space for Data and Checksum */
if (n > 92 && n < 96) n = 92; /* "Short" Long packets don't pay */
if (n > 92 && lpcapu == 0) /* If long packets needed, */
n = 92; /* make sure they've been negotiated */
len = n - bctl; /* Space for data */
if (n > 92) len -= 3; /* Long packet needs header chksum */
debug(F101,"maxdata len 1","",len);
if (len < 0) len = 10;
debug(F101,"maxdata len 2","",len);
return(len);
}
static CHAR leftover[9] = { '\0','\0','\0','\0','\0','\0','\0','\0','\0' };
static int nleft = 0;
#ifdef CKTUNING
/*
When CKTUNING is defined we use this special trimmed-down version of getpkt
to speed up binary-mode no-parity transfers. When CKTUNING is not defined,
or for text-mode or parity transfers, we use the regular getpkt() function.
Call just like getpkt() but test first for transfer mode and parity. NOTE:
This routine is only to be called when sending a real file -- not for
filenames, server responses, etc, because it only reads from the input file.
See getpkt() for more detailed commentary.
*/
static int
bgetpkt(bufmax) int bufmax; {
register CHAR rt = t, rnext;
register CHAR *dp, *odp, *p1, *p2;
register int x = 0, a7;
CHAR xxrc, xxcq; /* Pieces of prefixed sequence */
long z; /* A long worker (for CRC) */
if (!binary || parity || memstr) /* JUST IN CASE caller didn't test */
return(getpkt(bufmax,!binary));
if (!data) {
debug(F100,"SERIOUS ERROR: bgetpkt data == NULL","",0);
return(-1);
}
dp = data; /* Point to packet data buffer */
size = 0; /* And initialize its size */
bufmax = maxdata(); /* Get maximum data length */
#ifdef DEBUG
if (deblog)
debug(F101,"bgetpkt bufmax","",bufmax);
#endif /* DEBUG */
if (first == 1) { /* If first character of this file.. */
ffc = 0L; /* reset file character counter */
#ifdef COMMENT
/* Moved to below */
first = 0; /* Next character won't be first */
#endif /* COMMENT */
*leftover = '\0'; /* Discard any interrupted leftovers */
nleft = 0;
/* Get first character of file into rt, watching out for null file */
#ifdef CALIBRATE
if (calibrate) {
#ifdef NORANDOM
rt = 17;
#else
rt = cal_a[rand() & 0xff];
#endif /* NORANDOM */
first = 0;
} else
#endif /* CALIBRATE */
if ((x = zminchar()) < 0) { /* EOF or error */
if (x == -3) { /* Timeout. */
size = (dp - data);
debug(F101,"bgetpkt timeout size","",size);
return((size == 0) ? x : size);
}
first = -1;
size = 0;
if (x == -2) { /* Error */
debug(F100,"bgetpkt: input error","",0);
cxseen = 1; /* Interrupt the file transfer */
} else {
debug(F100,"bgetpkt empty file","",0);
}
return(0);
}
first = 0; /* Next char will not be the first */
ffc++; /* Count a file character */
rt = (CHAR) x; /* Convert int to char */
if (docrc && (what & W_SEND)) { /* Accumulate file crc */
z = crc16 ^ (long)rt;
crc16 = (crc16 >> 8) ^
(crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
}
rt &= fmask; /* Apply SET FILE BYTESIZE mask */
} else if (first == -1 && nleft == 0) { /* EOF from last time */
return(size = 0);
}
/*
Here we handle characters that were encoded for the last packet but
did not fit, and so were saved in the "leftover" array.
*/
if (nleft) {
for (p1 = leftover; nleft > 0; nleft--) /* Copy leftovers */
*dp++ = *p1++;
*leftover = '\0'; /* Delete leftovers */
nleft = 0;
}
if (first == -1) /* Handle EOF */
return(size = (dp - data));
/* Now fill up the rest of the packet. */
rpt = 0; /* Initialize character repeat count */
while (first > -1) { /* Until EOF... */
#ifdef CALIBRATE
if (calibrate) { /* We generate our own "file" */
if (ffc >= calibrate) { /* EOF */
first = -1;
ffc--;
} else { /* Generate next character */
if (cal_j > CAL_M * ffc)
cal_j = cal_a[ffc & 0xff];
x = (unsigned)cal_a[(cal_j & 0xff)];
if (x == rt) x ^= 2;
}
ffc++;
cal_j += (unsigned int)(ffc + CAL_O);
} else
#endif /* CALIBRATE */
if ((x = zminchar()) < 0) { /* Check for EOF */
if (x == -3) { /* Timeout. */
t = rt;
size = (dp-data);
debug(F101,"bgetpkt timeout size","",size);
return((size == 0) ? x : size);
}
first = -1; /* Flag eof for next time. */
if (x == -2) cxseen = 1; /* If error, cancel this file. */
} else {
ffc++; /* Count the character */
if (docrc && (what & W_SEND)) { /* Accumulate file crc */
z = crc16 ^ (long)((CHAR)x & 0xff);
crc16 = (crc16 >> 8) ^
(crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
}
}
rnext = (CHAR) (x & fmask); /* Apply file mask */
/*
At this point, the character we just read is in rnext,
and the character we are about to encode into the packet is in rt.
*/
odp = dp; /* Remember where we started. */
xxrc = xxcq = NUL; /* Clear these. */
/*
Now encode the character according to the options that are in effect:
ctlp[]: whether this control character needs prefixing.
rptflg: repeat counts enabled.
Other options don't apply in this routine.
*/
if (rptflg && (rt == rnext) && (first == 0)) { /* Got a run... */
if (++rpt < 94) { /* Below max, just count */
continue; /* go back and get another */
} else if (rpt == 94) { /* Reached max, must dump */
xxrc = (CHAR) tochar(rpt); /* Put the repeat count here */
rptn += rpt; /* Accumulate it for statistics */
rpt = 0; /* And reset it */
}
} else if (rpt > 0) { /* End of run */
xxrc = (CHAR)tochar(++rpt); /* The count */
rptn += rpt; /* For stats */
rpt = 0; /* Reset repeat count */
}
a7 = rt & 0177; /* Get low 7 bits of character */
if (
#ifdef CK_SPEED
ctlp[(unsigned)(rt & 0xff)] /* Lop off any "sign" extension */
#else
(a7 < SP) || (a7 == DEL)
#endif /* CK_SPEED */
) { /* Do control prefixing if necessary */
xxcq = myctlq; /* The prefix */
ccp++; /* Count it */
rt = (CHAR) ctl(rt); /* Uncontrollify the character */
}
#ifdef CK_SPEED
else if ((a7 < SP) || (a7 == DEL)) /* Count an unprefixed one */
ccu++;
#endif /* CK_SPEED */
if (a7 == myctlq) /* Always prefix the control prefix */
xxcq = myctlq;
if ((rptflg) && (a7 == rptq)) /* If it's the repeat prefix, */
xxcq = myctlq; /* prefix it if doing repeat counts */
/* Now construct the prefixed sequence */
if (xxrc) { /* Repeat count */
#ifdef COMMENT
if (xxrc == (CHAR) '"' && !xxcq) { /* 2 in a row & not prefixed */
*dp++ = rt; /* So just do this */
} else { /* More than two or prefixed */
*dp++ = (CHAR) rptq; *dp++ = xxrc; /* Emit repeat sequence */
}
#else /* CHECK THIS */
if (xxrc == (CHAR) '"' && !xxcq) { /* 2 in a row & not prefixed */
if (dp == data) {
*dp++ = rt; /* So just do this */
} else if (*(dp-1) == rt) {
*dp++ = (CHAR) rptq;
*dp++ = xxrc; /* Emit repeat sequence */
} else {
*dp++ = rt; /* So just do this */
}
} else { /* More than two or prefixed */
*dp++ = (CHAR) rptq;
*dp++ = xxrc; /* Emit repeat sequence */
}
#endif /* COMMENT */
}
if (xxcq) { *dp++ = myctlq; } /* Control prefix */
*dp++ = rt; /* Finally, the character itself */
rt = rnext; /* Next character is now current. */
/* Done encoding the character. Now take care of packet buffer overflow. */
size = dp - data; /* How many bytes we put in buffer. */
if (size >= bufmax) { /* If too big, save some for next. */
*dp = '\0'; /* Mark the end. */
if (size > bufmax) { /* if packet is overfull */
/* Copy the part that doesn't fit into the leftover buffer, */
/* taking care not to split a prefixed sequence. */
int i;
nleft = dp - odp;
p1 = leftover;
p2 = odp;
for (i = 0; i < nleft; i++)
*p1++ = *p2++;
size = odp - data; /* Return truncated packet. */
*odp = '\0'; /* Mark the new end */
}
t = rt; /* Save for next time */
return(size);
}
} /* Otherwise, keep filling. */
size = dp - data; /* End of file */
*dp = '\0'; /* Mark the end of the data. */
return(size); /* Return partially filled last packet. */
}
#endif /* CKTUNING */
VOID
dofilcrc(c) int c; { /* Accumulate file crc */
long z;
z = crc16 ^ (long)c;
crc16 = (crc16 >> 8) ^
(crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]);
}
/* For SENDing from an array... */
int
agnbyte() { /* Get next byte from array */
#ifndef NOSPL
char c;
static int save = 0; /* For CRLF */
static char ** ap = NULL; /* Array pointer */
static char * p = NULL; /* Character pointer */
static int i = 0, n = 0; /* Array index and limit */
extern int a_dim[]; /* Array dimension */
if (!ap) { /* First time thru */
ap = sndarray; /* Set up array pointers */
if (!ap || (i = sndxlo) > a_dim[sndxin]) {
sndarray = NULL;
ap = NULL;
return(-1);
}
p = ap[i]; /* Point to first element in range */
n = sndxhi; /* Index of last element in range */
if (sndxhi > a_dim[sndxin]) /* Adjust if necessary */
n = a_dim[sndxin];
}
if (save) { /* If anything saved */
c = save; /* unsave it */
save = 0; /* and return it */
return(c & 0xff);
}
if (i > n) { /* No more elements */
sndarray = NULL;
ap = NULL;
return(-1);
}
if (!p) /* Source pointer is NULL */
c = NUL; /* this means an empty line */
else /* Source pointer not NULL */
c = *p++; /* Next char */
if (!c) { /* Char is empty? */
if (!binary) { /* Text: end of line. */
if (feol) { /* Supply NL */
c = feol;
} else { /* or CRLF */
save = LF;
c = CR;
}
p = ap[++i];
return(c & 0xff);
}
while (i++ < n) { /* Binary - get next element */
p = ap[i];
if (!p) /* Empty line? */
continue; /* Ignore it and get another */
c = *p++; /* Get next char */
if (!c) /* Emtpy char? */
continue; /* Ignore it and get another */
return(c & 0xff); /* Not empty - return it */
}
sndarray = NULL;
ap = NULL;
return(-1); /* Done */
}
return(c & 0xff); /* Char is not empty */
#else
sndarray = NULL;
return(-1);
#endif /* NOSPL */
}
#endif /* NOXFER */
#ifndef NOCSETS
static CHAR xlabuf[32] = { 0, 0, 0, 0, 0, 0, 0, 0 };
static int xlacount = 0;
static int xlaptr = 0;
/* static USHORT lastucs2 = 0; */
/*
X G N B Y T E -- Get next translated byte from the input file.
Returns the next byte that is to be put into the packet, already translated.
This isolates getpkt() from having to know anything about translation,
single- vs multibyte character sets, one-to-many vs many-to-one, etc, but it
has rather high overhead, so don't call it unless you know translation is
needed to or from Unicode, Japanese, or other multibyte character set.
Call with:
fcs: File character set (source, file we are reading from)
tcs: Target character set (use an FC_xxx code, not a TC_xxx code)
Returns:
>= 0: A translated byte suitable for writing.
< 0: Fatal error (such as EOF on input source).
As of Sat Sep 7 18:37:41 2002:
When the output character-set is UCS-2, bytes are ALWAYS returned in
big-endian order (previously they could also be returned in LE order
under certain conditions, which was just way too confusing).
*/
int
#ifdef CK_ANSIC
xgnbyte(int tcs, int fcs, int (*fn)(void))
#else /* CK_ANSIC */
xgnbyte(tcs,fcs,fn) int tcs, fcs, (*fn)();
#endif /* CK_ANSIC */
/* xgnbyte */ {
_PROTOTYP( int (*xx), (USHORT) ) = NULL;
int haveuc = 0; /* Flag for have Unicode character */
#ifdef KANJI
int havesj = 0; /* Have Shift-JIS character */
int haveeu = 0; /* Have EUC-JP character */
#endif /* KANJI */
int rc = -1, x = 0, flag = 0;
int utferror = 0;
int eolflag = 0;
unsigned int xc, thischar;
static int swapping = 0;
CHAR rt;
/* USHORT ch; */
#ifdef UNICODE
union ck_short uc;
#endif /* UNICODE */
#ifdef KANJI
union ck_short sj, eu; /* Shift-JIS character */
#endif /* KANJI */
#ifdef KANJI
sj.x_short = 0;
#endif /* KANJI */
#ifdef DEBUG
if (deblog && ffc == 0) {
debug(F101,"xgnbyte initial swap","",swapping);
}
#endif /* DEBUG */
if (xlacount-- > 0) { /* We already have some */
x = xlabuf[xlaptr++];
debug(F001,"xgnbyte from buf","",x);
return(x);
}
if (xlatype != XLA_NONE) { /* Not not translating... */
haveuc = 0;
#ifdef UNICODE
if (fcs == FC_UCS2) { /* UCS-2: Read two bytes */
if (ffc == 0) /* Beginning of file? */
swapping = 0; /* Reset byte-swapping flag */
uc.x_short = 0;
bomskip:
x = fn ? (*fn)() : zminchar(); /* Get first byte */
debug(F001,"zminchar swapping","",swapping);
debug(F001,"zminchar C0","",x);
flag = 1; /* Remember we called zminchar() */
if (x > -1) { /* Didn't fail */
ffc++; /* Count a file byte */
uc.x_char[swapping] = x & 0xff;
#ifndef NOXFER
if (docrc && (what & W_SEND))
dofilcrc(x);
#endif /* NOXFER */
x = fn ? (*fn)() : zminchar(); /* Get second byte */
if (x > -1) { /* If didn't fail */
debug(F001,"zminchar C1","",x);
ffc++; /* count another file byte */
uc.x_char[1-swapping] = x & 0xff;
haveuc = 1; /* And remember we have Unicode */
#ifndef NOXFER
if (docrc && (what & W_SEND))
dofilcrc(x);
#endif /* NOXFER */
if (ffc == 2) { /* Second char of file */
debug(F001,"xgnbyte 1st UCS2","",uc.x_short);
debug(F111,"xgnbyte fileorder","A",fileorder);
if (fileorder < 0) /* Byte order of this file */
fileorder = ucsorder; /* Default is ucsorder */
if (fileorder > 1)
fileorder = 1;
debug(F111,"xgnbyte fileorder","B",fileorder);
if (uc.x_short == (USHORT)0xfeff) {
swapping = 0;
debug(F101,
"xgnbyte UCS2 goodbom swap","",swapping);
fileorder = byteorder; /* Note: NOT 0 */
goto bomskip;
} else if (uc.x_short == (USHORT)0xfffe) {
swapping = 1;
debug(F101,
"xgnbyte UCS2 badbom swap","",swapping);
fileorder = (1 - byteorder); /* Note: NOT 1 */
goto bomskip;
} else if ((byteorder && !fileorder) || /* No BOM */
(!byteorder && fileorder > 0)) {
/* fileorder might have been set by scanfile() */
CHAR c;
c = uc.x_char[0];
uc.x_char[0] = uc.x_char[1];
uc.x_char[1] = c;
swapping = 1;
debug(F111,"xgnbyte UCS2 noBOM swap","A",swapping);
} else {
swapping = 0;
debug(F111,"xgnbyte UCS2 noBOM swap","B",swapping);
}
debug(F111,"xgnbyte fileorder","C",fileorder);
}
} else
return(x);
} else
return(x);
debug(F001,"xgnbyte UCS2","",uc.x_short);
} else if (fcs == FC_UTF8) { /* File is UTF-8 */
CHAR ch = 0; /* Data types needed for API... */
USHORT * us = NULL;
uc.x_short = 0;
flag = 1; /* We (will) have called zminchar() */
/* Read source bytes */
while ((x = fn ? (*fn)() : zminchar()) > -1) {
ffc++; /* Got a byte - count it */
#ifndef NOXFER
if (docrc && (what & W_SEND))
dofilcrc(x);
#endif /* NOXFER */
ch = x;
rc = utf8_to_ucs2(ch,&us); /* Convert to UCS-2 */
if (rc == 0) { /* Done */
uc.x_short = *us;
haveuc = 1;
break;
} else if (rc < 0) { /* Error */
utferror = 1;
debug(F101,"xgnbyte UTF-8 input error","",rc);
haveuc = 1;
uc.x_short = *us;
break;
}
}
if (x < 0)
return(x);
debug(F001,"xgnbyte UTF8->UCS2","",uc.x_short);
}
#endif /* UNICODE */
#ifdef KANJI
#ifdef UNICODE
else
#endif /* UNICODE */
if (fcsinfo[fcs].alphabet == AL_JAPAN) { /* Japanese source file */
int c7, x, y;
if (fcs == FC_JIS7) { /* If file charset is JIS-7 */
if (ffc == 0L) /* If first byte of file */
j7init(); /* Initialize JIS-7 parser */
x = getj7(); /* Get a JIS-7 byte */
} else /* Otherwise */
x = fn ? (*fn)() : zminchar(); /* Just get byte */
if (x < 0) { /* Propogate EOF or error */
debug(F100,"XGNBYTE EOF","",0);
return(x);
}
debug(F001,"XGNBYTE x","",x);
ffc++; /* Count */
#ifndef NOXFER
if (docrc && (what & W_SEND)) dofilcrc(x); /* Do CRC */
#endif /* NOXFER */
switch (fcs) { /* What next depends on charset */
case FC_SHJIS: /* Shift-JIS */
if ((x <= 0x80) || /* Any 7-bit char... */
(x >= 0xa0 && x <= 0xdf)) { /* or halfwidth Katakana */
sj.x_short = (USHORT) x; /* we read one byte. */
} else { /* Anything else */
if ((y = fn ? (*fn)() : zminchar()) < 0) /* get another */
return(y);
#ifndef NOXFER
if (docrc && (what & W_SEND)) dofilcrc(y);
#endif /* NOXFER */
ffc++;
sj.x_char[byteorder] = (CHAR) x;
sj.x_char[1-byteorder] = (CHAR) y;
}
break;
case FC_JIS7: /* JIS-7 */
case FC_JDEC: /* DEC Kanji */
case FC_JEUC: /* EUC-JP */
if ((x & 0x80) == 0) { /* Convert to Shift-JIS */
sj.x_short = (USHORT) x; /* C0 or G0: one byte */
eu.x_short = (USHORT) x;
haveeu = 1;
} else {
c7 = x & 0x7f;
if (c7 > 0x20 && c7 < 0x7f) { /* Kanji: two bytes */
if ((y = (fcs == FC_JEUC) ?
(fn ? (*fn)() : zminchar()) :
getj7() /* ^^^ */
) < 0)
return(y);
ffc++;
#ifndef NOXFER
if (docrc && (what & W_SEND)) dofilcrc(y);
#endif /* NOXFER */
eu.x_char[byteorder] = (CHAR) x;
eu.x_char[1-byteorder] = (CHAR) y;
sj.x_short = eu_to_sj(eu.x_short);
haveeu = 1;
} else if (x == 0x8e) { /* SS2 Katakana prefix: 2 bytes */
if ((y = (fcs == FC_JIS7) ?
getj7() : /* ^^^ */
(fn ? (*fn)() : zminchar())
) < 0)
return(y);
ffc++;
#ifndef NOXFER
if (docrc && (what & W_SEND)) dofilcrc(y);
#endif /* NOXFER */
sj.x_short = y | 0x80;
debug(F001,"XGNBYTE KANA SJ","",sj.x_short);
} else {
/* Something that translates to U+FFFD */
sj.x_short = UNKSJIS;
}
}
break;
}
havesj = 1; /* Have Shift-JIS */
#ifdef UNICODE
uc.x_short = sj_to_un(sj.x_short); /* Translate to UCS-2 */
haveuc = 1; /* Have Unicode */
#endif /* UNICODE */
flag = 1; /* Have a char */
}
#endif /* KANJI */
}
if (!flag) { /* If no character was read yet... */
if ((x = (fn ? (*fn)() : zminchar())) > -1) /* read one now */
ffc++;
debug(F101,"xgnbyte zminchar 1","",x);
if (x < 0)
return(x);
haveuc = 0;
}
#ifdef UNICODE
if (haveuc) {
thischar = uc.x_short;
/* lastucs2 = uc.x_short; */
} else
#endif /* UNICODE */
thischar = x;
debug(F001,"xgnbyte thischar",haveuc ? "[UNICODE]" : "[other]",thischar);
#ifdef CK_CTRLZ /* SET EOF CTRLZ */
if (eofmethod == XYEOF_Z && !binary && thischar == 26) {
debug(F100,"xgnbyte EOF on Ctrl-Z 1","",0);
return(-1);
}
#endif /* CK_CTRLZ */
#ifdef UNICODE
if (!haveuc) /* If not Unicode... */
#endif /* UNICODE */
x &= fmask; /* Apply SET FILE BYTESIZE mask */
switch (xlatype) { /* Translation type... */
#ifdef UNICODE
case XLA_UNICODE: { /* Unicode is involved */
xc = 0;
/*
Here we must choose the appropriate translation function. If we are being
called by getpkt() (i.e. when transferring a file), we are translating from
Unicode to the Transfer Character Set and therefore must use the function
pointed to by xut. Otherwise, e.g. during TRANSLATE, CONNECT, TRANSMIT, etc,
we are translating from Unicode to the File Character Set and so must call
the function pointed to by xuf. There might be a cleaner way to set this
up but I don't think so. For example, setxlatype() might have been called
too soon and so might not have known whether it was a file transfer or a
local operation.
*/
xx = (what & W_SEND) ? xut : xuf;
eolflag = 0;
if (haveuc) { /* File is Unicode */
/* See Unicode TR13, "Converting to Other Character Sets" */
if (uc.x_short == 0x2028 || /* Line Separator? */
uc.x_short == 0x2029 || /* Paragraph Separator */
(feol && (uc.x_short == (USHORT)feol))
) {
debug(F001,"xgnbyte uc eol","",uc.x_short);
rc = 0;
eolflag = 1; /* Don't translate and handle later */
}
if (xx && !eolflag) { /* UCS-to-TCS function (UCS->byte) */
rc = (*xx)(uc.x_short); /* These can fail... */
debug(F101,"xgnbyte xx rc","",rc);
if (rc < 0) /* If it can't be translated */
uc.x_short = UNK; /* Put unknown-character symbol */
else
uc.x_short = (unsigned)((unsigned)rc & 0xffff);
debug(F101,"xgnbyte xx uc","",uc.x_short);
}
#ifdef KANJI
if (tcs == FC_JEUC) { /* Translating to EUC-JP */
USHORT sj = 0;
union ck_short eu;
debug(F001,"xgnbyte UCS->EUC UCS","",uc.x_short);
if (!havesj) /* If we don't already have it */
sj = un_to_sj(uc.x_short); /* convert to Shift-JIS */
eu.x_short = sj_to_eu(sj);
debug(F001,"xgnbyte UCS->EUC EUC","",eu.x_short);
xlaptr = 0;
xlacount = 0;
if (eolflag) {
if (what & W_SEND) {
xlabuf[xlacount++] = LF;
return(CR);
} else {
return(feol);
}
}
if (eu.x_char[byteorder]) { /* Two bytes */
rc = eu.x_char[byteorder];
xlabuf[xlacount++] = eu.x_char[1-byteorder];
debug(F001,"xgnbyte UCS->EUC xlabuf[0]","",xlabuf[0]);
} else { /* One byte */
rc = eu.x_char[1-byteorder];
}
debug(F101,"xgnbyte UCS->EUC xlacount","",xlacount);
debug(F001,"xgnbyte UCS->EUC rc","",rc);
return(rc);
} else
#endif /* KANJI */
if (tcs != FC_UCS2 && tcs != FC_UTF8) {
if (uc.x_short & 0xff00) { /* Decoding error */
debug(F001,"xgnbyte decoding error","",uc.x_short);
return(-2);
} else
return((unsigned int)(uc.x_short & 0xff));
}
xc = uc.x_short;
} else { /* File is not Unicode */
USHORT ch;
/* Translate from single FCS byte to UCS-2 */
/*
This is a bit nonobvious... The blah_u() (Blah-to-Unicode) routines are
called only with pieces of character sets, in the ISO 2022 sense. So,
for example, if ch is a Latin-1 character, we call the translation
routine only if it is in the right half; if it's in the left half, it
isn't translated, and in fact will give the wrong result if sent to the
translation function. That's because those functions were designed for
use with the ISO 2022 G0..G3 sets, not for file transfer. On the other
hand, if it's a 7-bit character set, we *do* call the translation
function. (To put it another way, the left half of any 8-bit character
set is ASCII and therefore doesn't need to be translated but 7-bit sets
such as ISO 646 German do need translation).
*/
ch = (unsigned)(thischar & 0xff);
if (((fcsinfo[fcs].size > 128) && (ch & 0x80)) ||
fcsinfo[fcs].size <= 128) {
if (xfu) { /* FCS-to-UCS function */
ch = (*xfu)(ch);
}
}
xc = ch;
}
/* At this point we have a UCS-2 character in native format */
/* (Big Endian or Little Endian) in xc, which is an unsigned int. */
debug(F001,"xgnbyte xc","",xc);
if (tcs == FC_UTF8) { /* Now convert to UTF-8 */
USHORT c; /* NOTE: this is FC_UTF8 on purpose! */
CHAR * buf = NULL;
int i, k = 0, x;
xlaptr = 0;
if (utferror) {
xlabuf[k++] = 0xff;
xlabuf[k++] = 0xbd;
}
if (eolflag) { /* We detected EOL in source file */
if (what & W_SEND) { /* Convert to CRLF */
xlabuf[k++] = LF;
xlacount = k;
return((unsigned int)CR);
#ifdef COMMENT
} else { /* Or to local line-end */
xlacount = k;
return((unsigned int)feol);
#endif /* COMMENT */
}
}
c = xc;
if ((x = ucs2_to_utf8(c,&buf)) < 1) {
debug(F101,"xgnbyte ucs2_to_utf8 error","",c);
return(-2);
}
debug(F101,"xgnbyte UTF8 buf[0]","",buf[0]);
for (i = 1; i < x; i++) {
xlabuf[k+i-1] = buf[i];
debug(F111,"xgnbyte UTF8 xlabuf",ckitoa(i-1),buf[i]);
}
xlaptr = 0;
xlacount = x - 1;
debug(F101,"xgnbyte UTF8 xlacount","",xlacount);
return((unsigned int)buf[0]);
} else { /* Or keep it as UCS-2 */
int k = 0;
CHAR c;
xlaptr = 0;
if (utferror) {
xlabuf[k++] = 0xff;
xlabuf[k++] = 0xfd;
debug(F101,"xgnbyte error","",k);
}
if (eolflag) { /* We detected EOL in source file */
if (what & W_SEND) { /* Convert to CRLF */
xlabuf[k++] = CR;
xlabuf[k++] = NUL;
xlabuf[k++] = LF;
xlacount = k;
debug(F101,"xgnbyte send CRLF","",k);
return(0); /* Return NUL */
} else { /* Or to local line-end */
#ifdef COMMENT
/* This bypasses byte swapping that we might need */
xlabuf[k++] = (CHAR)feol;
xlacount = k;
debug(F101,"xgnbyte send feol","",k);
return(0); /* Return NUL */
#else
xc = (CHAR)feol;
#endif /* COMMENT */
}
}
/* In which order should we return the bytes? */
#ifdef COMMENT
if ( (what & W_SEND) || (what & W_FTP) || (fileorder == 0)) {
#endif /* COMMENT */
/* ALWAYS RETURN IN BIG ENDIAN ORDER... 7 Sep 2002 */
/* xgnbyte() is almost always used to feed xpnbyte() */
/* which requires bytes in BE order. In cases where */
/* xgnbyte is used in isolation, the caller can swap */
/* bytes itself afterwards. */
xlabuf[k++] = (xc >> 8) & 0xff; /* Big Endian */
xlabuf[k++] = xc & 0xff;
debug(F001,"xgnbyte->UCS2BE",
ckitox((int)xlabuf[0]),xlabuf[1]);
#ifdef COMMENT
} else { /* Little Endian */
xlabuf[k++] = xc & 0xff;
xlabuf[k++] = (xc >> 8) & 0xff;
debug(F001,"xgnbyte->UCS2LE",
ckitox((int)xlabuf[0]),xlabuf[1]);
}
#endif /* COMMENT */
c = xlabuf[0];
xlaptr = 1;
xlacount = k-1;
debug(F101,"xgnbyte c","",c);
debug(F101,"xgnbyte xlaptr","",xlaptr);
debug(F011,"xgnbyte xlabuf",xlabuf,xlacount);
return((unsigned int)c);
}
}
#endif /* UNICODE */
case XLA_NONE:
return((fn ? (*fn)() : zminchar()));
case XLA_BYTE: /* Byte-for-Byte translation */
rt = x;
if (sx)
rt = (*sx)(rt);
#ifdef UNICODE
if (utferror) {
xlaptr = 0;
xlacount = 1;
xlabuf[0] = rt;
return(UNK);
} else
#endif /* UNICODE */
return((unsigned int)rt);
#ifdef KANJI
case XLA_JAPAN: /* Come here with Shift-JIS */
if (tcs == FC_JEUC) { /* It better be... */
xlaptr = 0;
xlacount = 0;
if (!havesj) {
printf("BAD BAD\n");
return(-2);
}
if (!haveeu) /* We might already have EUC too */
eu.x_short = sj_to_eu(sj.x_short);
if (eu.x_char[byteorder]) {
xlabuf[xlacount++] = eu.x_char[1-byteorder];
return(eu.x_char[byteorder]);
} else {
return(eu.x_char[1-byteorder]);
}