| 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]); |
| } |
| break; |
| } |
| #endif /* KANJI */ |
| |
| default: |
| debug(F101,"xgnbyte bad xlatype","",xlatype); |
| return(-2); |
| } |
| #ifdef COMMENT |
| /* |
| If there is a return() statement here, some compilers complain |
| about "statement not reached". If there is no return() statement, |
| other compilers complain that "Non-void function should return a value". |
| There is no path through this function that falls through to here. |
| */ |
| debug(F100,"xgnbyte switch failure","",0); |
| return(-2); |
| #endif /* COMMENT */ |
| } |
| #endif /* NOCSETS */ |
| |
| #ifndef NOXFER |
| |
| /* G E T P K T -- Fill a packet data field from the indicated source. */ |
| |
| /* |
| Parameters: |
| bufmax: Maximum length of entire packet. |
| xlate: Flag for whether to translate charsets when in text mode. |
| Returns: Number of characters written to packet data field, 0 or more, |
| Or -1 on failure (internal error), |
| or -3 on timeout (e.g. when reading from a pipe). |
| |
| This is the full version allowing for parity and text-mode conversions; |
| i.e. it works in all cases. Also see bgetpkt(), a special lean/mean/fast |
| packet encoder that works only for binary-mode no-parity transfers. |
| */ |
| static int uflag = 0; |
| |
| int |
| getpkt(bufmax,xlate) int bufmax, xlate; { /* Fill one packet buffer */ |
| register CHAR rt = t, rnext = NUL; /* Register shadows of the globals */ |
| register CHAR *dp, *odp, *odp2, *p1, *p2; /* pointers... */ |
| register int x; /* Loop index. */ |
| register int a7; /* Low 7 bits of character */ |
| |
| CHAR xxls, xxdl, xxrc, xxss, xxcq; /* Pieces of prefixed sequence */ |
| |
| if (binary) xlate = 0; /* We don't translate if binary */ |
| |
| if (!data) { |
| debug(F100,"SERIOUS ERROR: getpkt data == NULL","",0); |
| return(-1); |
| } |
| dp = data; /* Point to packet data buffer */ |
| size = 0; /* And initialize its size */ |
| /* |
| Assume bufmax is the receiver's total receive-packet buffer length. |
| Our whole packet has to fit into it, so we adjust the data field length. |
| We also decide optimally whether it is better to use a short-format or |
| long-format packet when we're near the borderline. |
| */ |
| bufmax = maxdata(); /* Get maximum data length */ |
| |
| if (first == 1) { /* If first character of this file.. */ |
| #ifdef UNICODE |
| /* Special end-of-line handling for Unicode */ |
| if (tcharset == TC_UCS2 || tcharset == TC_UTF8) |
| uflag = 1; |
| #endif /* UNICODE */ |
| debug(F101,"getpkt first uflag","",uflag); |
| debug(F101,"getpkt first rt","",rt); |
| if (!memstr && !funcstr) /* and real 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; |
| #ifndef NOCSETS |
| setxlatype(tcharset,fcharset); /* Set up charset translations */ |
| #endif /* NOCSETS */ |
| |
| /* Get first character of file into rt, watching out for null file */ |
| |
| #ifdef CALIBRATE |
| if (calibrate && !memstr) { |
| #ifdef NORANDOM |
| x = rt = 53; |
| #else |
| x = rt = cal_a[rand() & 0xff]; |
| #endif /* NORANDOM */ |
| first = 0; |
| ffc++; |
| } else |
| #endif /* CALIBRATE */ |
| #ifdef KANJI |
| if (xlate && tcharset == TC_JEUC) { /* Kanji text */ |
| x = zkanjf(); |
| if ((x = zkanji(memstr ? kgetm : kgetf)) < 0) { |
| first = -1; |
| size = 0; |
| if (x == -2) { |
| debug(F100,"getpkt zkanji: input error","",0); |
| cxseen = 1; |
| } else debug(F100,"getpkt zkanji: empty string/file","",0); |
| return(0); |
| } |
| rt = x; |
| first = 0; |
| if (!memstr) { |
| ffc++; |
| if (docrc && (what & W_SEND)) /* Accumulate file crc */ |
| dofilcrc((int)rt); |
| } |
| } else { /* Not Kanji text */ |
| #endif /* KANJI */ |
| if (memstr) { /* Reading data from memory string */ |
| /* This will not be Unicode */ |
| if ((rt = *memptr++) == '\0') { /* end of string ==> EOF */ |
| first = -1; |
| size = 0; |
| debug(F100,"getpkt: empty string","",0); |
| return(0); |
| } |
| first = 0; |
| } else if (funcstr) { /* Reading data from a function */ |
| /* This will not be Unicode */ |
| if ((x = (*funcptr)()) < 0) { /* End of input */ |
| first = -1; |
| size = 0; /* Empty */ |
| return(0); |
| } |
| ffc++; /* Count a file character */ |
| rt = (CHAR) x; /* Convert int to char */ |
| first = 0; |
| debug(F000,"getpkt funcstr","",rt); |
| |
| } else { /* Reading data from a file */ |
| #ifndef NOCSETS |
| if (xlate && !binary) { /* Could be Unicode */ |
| if (xlatype == XLA_UNICODE) { |
| /* Get next translated byte */ |
| x = xgnbyte(cseqtab[tcharset],fcharset,NULL); |
| debug(F101,"getpkt xgnbyte","",x); |
| } else { /* Not Unicode */ |
| x = zminchar(); /* Get next byte, translate below */ |
| debug(F101,"getpkt zminchar A","",x); |
| } |
| } else { /* Just get next byte */ |
| #endif /* NOCSETS */ |
| x = zminchar(); |
| debug(F101,"getpkt zminchar B","",x); |
| #ifndef NOCSETS |
| } |
| #endif /* NOCSETS */ |
| if (x < 0) { /* End of file or input error */ |
| if (x == -3) { /* Timeout. */ |
| size = (dp-data); |
| debug(F101,"getpkt timeout size","",size); |
| return((size == 0) ? x : size); |
| } |
| first = -1; |
| size = 0; |
| if (x == -2) { /* Error */ |
| debug(F100,"getpkt: input error","",0); |
| cxseen = 1; /* Interrupt the file transfer */ |
| } else { |
| debug(F100,"getpkt empty file","",0); |
| } |
| return(0); |
| } |
| first = 0; /* Next character won't be first */ |
| rt = (CHAR) x; /* Convert int to char */ |
| #ifndef NOCSETS |
| if (xlatype != XLA_UNICODE || binary) { |
| ffc++; |
| if (sx) |
| rt = (*sx)(rt); |
| if (docrc && (what & W_SEND)) |
| dofilcrc(x); |
| } |
| #endif /* NOCSETS */ |
| #ifdef DEBUG |
| if (deblog) |
| debug(F101,"getpkt 1st char","",rt); |
| #endif /* DEBUG */ |
| if (/* !haveuc && */ docrc && (what & W_SEND)) /* File CRC */ |
| dofilcrc(x); |
| } |
| #ifdef KANJI |
| } |
| #endif /* KANJI */ |
| /* PWP: handling of feol is done later (in the while loop)... */ |
| |
| } else if ((first == -1) && (nleft == 0)) { /* EOF from last time */ |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F101,"getpkt eof crc16","",crc16); |
| debug(F101,"getpkt eof ffc","",ffc); |
| } |
| #endif /* DEBUG */ |
| 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. |
| */ |
| debug(F101,"getpkt nleft","",nleft); |
| 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 && !memstr) { /* 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; |
| } |
| cal_j += (unsigned int)(ffc + CAL_O); |
| ffc++; |
| } else |
| #endif /* CALIBRATE */ |
| #ifdef KANJI |
| if (xlate && tcharset == TC_JEUC) { |
| if ((x = zkanji(memstr ? kgetm : kgetf)) < 0) { |
| first = -1; |
| if (x == -2) cxseen = 1; |
| } else if (!memstr) ffc++; |
| rnext = (CHAR) (x & fmask); |
| } else { |
| #endif /* KANJI */ |
| if (memstr) { /* Get next char from memory string */ |
| if ((x = *memptr++) == '\0') /* End of string means EOF */ |
| first = -1; /* Flag EOF for next time. */ |
| rnext = (CHAR) (x & fmask); /* Apply file mask */ |
| } else if (funcstr) { /* Get next char from function */ |
| if ((x = (*funcptr)()) < 0) /* End of string means EOF */ |
| first = -1; /* Flag EOF for next time. */ |
| rnext = (CHAR) (x & fmask); /* Apply file mask */ |
| } |