/***************************************************************************
 *                                                                         *
 *   FTFVIEW.C                                                             *
 *                                                                         *
 *   Copyright (c) 1992-1997 Galacticomm, Inc.      All Rights Reserved.   *
 *                                                                         *
 *   File Transfer Software ZIP/ARC/ZOO/LZH/ICE/PSE/ZSE file viewer        *
 *                                                                         *
 *   With all operating-system-specific functions removed (I/O, disk,      *
 *   memory, time), the pure file transfer algorithm can be isolated.      *
 *                                                                         *
 *                 - Bob Stein (glue) and Rob Rose (technology)  1/21/92   *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "ftf.h"
#include "ftfview.h"

#define FILREV "$Revision: 9 $"

/*--- View transmitter states ---*/
#define VEWOPN    1                               /* open file for transmit */
#define VEWFID    2                            /* check type, output header */
#define VEWFIL    3                             /* output info on each file */
#define VEWEND    4                                              /* wind up */
#define VEWFND    5           /* searching ZIP2 file for end of central dir */


/*--- ARC/ZIP/etc display structures ---*/

#define VEWCHUNK 128               /* amount of data to read for each file */
#define ZP2CHUNK 2048              /* data to read to check for ZIP2 file  */
#define CDRLEN   22                /* valid end of central dir size        */

CHAR *arcdsc[]={
     "",
     "ARC ",
     "ZIP ",
     "LZH/ICE ",
     "ZOO ",
     "self-extracting ZIP ",
     "self-extracting PAK ",
     "ZIP ",
     "self-extracting ZIP "};

static
CHAR *arcerr[]={
     "Not a compressed file",
     "ARC file is not properly formatted",
     "ZIP file is not properly formatted",
     "LZH/ICE file is not properly formatted",
     "ZOO file is not properly formatted",
     "Self-extracting ZIP file is not properly formatted",
     "Self-extracting PAK file is not properly formatted",
     "ZIP file is not properly formatted",
     "Self-extracting ZIP file is not properly formatted"};

struct filhead {         /* 30 bytes+filename needed */
     CHAR headr[4];                /* Should be 0x04034B50                 */
     SHORT versneed;               /* version of pkzip needed to extract   */
     SHORT flags;                  /* general purpose bitflags             */
     SHORT method;                 /* compression method                   */
     SHORT ftime;                  /* file time                            */
     SHORT fdate;                  /* file date                            */
     ULONG crc32;                  /* file crc                             */
     LONG compsz;                  /* compressed size                      */
     LONG normsz;                  /* uncompressed size                    */
     SHORT namesz;                 /* filename size                        */
     SHORT xtrasz;                 /* extra field size                     */
};

struct flddef filheadFDA[]={
     { CVTFLD_CHAR   ,4,fldoff(filhead,headr)    , NULL },
     { CVTFLD_SHORT  ,5,fldoff(filhead,versneed) , NULL },
     { CVTFLD_LONG   ,3,fldoff(filhead,crc32)    , NULL },
     { CVTFLD_SHORT  ,2,fldoff(filhead,namesz)   , NULL },
     { CVTFLD_END    ,0,0                        , NULL }
};

struct dirhead {         /* 46+filename */
     CHAR headr[4];      /* Should be 0x02014B50                      */
     SHORT versmade;     /* version of pkzip that created             */
     SHORT versneed;     /* version of pkzip needed to extract        */
     SHORT flags;        /* general purpose bitflags                  */
     SHORT method;       /* compression method                        */
     SHORT ftime;        /* file time                                 */
     SHORT fdate;        /* file date                                 */
     ULONG crc32;        /* file crc                                */
     LONG compsz;        /* compressed size                           */
     LONG normsz;        /* uncompressed size                         */
     SHORT namesz;       /* filename size                             */
     SHORT xtrasz;       /* extra field size                          */
     SHORT commsz;       /* file comment length                       */
     SHORT dsknst;       /* disk number start                         */
     SHORT infatr;       /* internal file attributes                  */
     LONG extatr;        /* external file attributes                  */
     LONG offshd;        /* offset to local header                    */
};

struct flddef dirheadFDA[]={
     { CVTFLD_CHAR   ,4,fldoff(dirhead,headr)   ,NULL },
     { CVTFLD_SHORT  ,6,fldoff(dirhead,versmade),NULL },
     { CVTFLD_LONG   ,3,fldoff(dirhead,crc32)   ,NULL },
     { CVTFLD_SHORT  ,5,fldoff(dirhead,namesz)  ,NULL },
     { CVTFLD_LONG   ,2,fldoff(dirhead,extatr)  ,NULL },
     { CVTFLD_END    ,0,0                       ,NULL }
};

struct endcdr {          /* end of central directory record           */
     CHAR headr[4];      /* should be 0x06054b50                      */
     SHORT diskno;       /* number of this disk                       */
     SHORT ctrbeg;       /* number of disk with start of central dir  */
     SHORT ctrcnt;       /* number of entries in central dir on disk  */
     SHORT totcnt;       /* total number of central dir entries       */
     ULONG ctrsiz;       /* size of the central directory          */
     LONG ctroff;        /* offset of start of central directory      */
     SHORT commsz;       /* zip file comment length                   */
};

struct flddef endcdrFDA[]={
     { CVTFLD_CHAR  ,4,fldoff(endcdr,headr)  ,NULL },
     { CVTFLD_SHORT ,4,fldoff(endcdr,diskno) ,NULL },
     { CVTFLD_LONG  ,2,fldoff(endcdr,ctrsiz) ,NULL },
     { CVTFLD_SHORT ,1,fldoff(endcdr,commsz) ,NULL },
     { CVTFLD_END   ,0,0                     ,NULL }
};

struct archead {
     CHAR signat;        /* Signature byte of 26 (1Ah)                */
     CHAR hdrtyp;        /* Header ID                                 */
     CHAR fname[13];     /* Filename                                  */
     LONG size;          /* Size of compressed file                   */
     SHORT date;         /* File date                                 */
     SHORT time;         /* File time                                 */
     USHORT crc;         /* file CRC-16                               */
     LONG length;        /* uncompressed file length (header ID >1)   */
};

struct flddef archeadFDA[]={
     { CVTFLD_CHAR  ,15,fldoff(archead,signat) ,NULL },
     { CVTFLD_LONG  ,1 ,fldoff(archead,size)   ,NULL },
     { CVTFLD_SHORT ,3 ,fldoff(archead,date)   ,NULL },
     { CVTFLD_LONG  ,1 ,fldoff(archead,length) ,NULL },
     { CVTFLD_END   ,0 ,0                      ,NULL }
};

struct icehead {
     CHAR hdrtyp[2];     /* header type                               */
     CHAR encod[5];      /* encode type                               */
     LONG size;          /* bytes after compression                   */
     LONG length;        /* bytes originally                          */
     SHORT time;
     SHORT date;
     SHORT attir;        /* file attributes                           */
     CHAR fnlen;         /* length of filename                        */
};

struct flddef iceheadFDA[]={
     { CVTFLD_CHAR  ,7,fldoff(icehead,hdrtyp) ,NULL },
     { CVTFLD_LONG  ,2,fldoff(icehead,size)   ,NULL },
     { CVTFLD_SHORT ,3,fldoff(icehead,time)   ,NULL },
     { CVTFLD_CHAR  ,1,fldoff(icehead,fnlen)  ,NULL },
     { CVTFLD_END   ,0,0                      ,NULL }
};

struct zoohead {
     ULONG head;              /* should be 0xFDC4A7DCL                */
     CHAR type;               /* directory type (should be 1)         */
     CHAR meth;               /* packing method                       */
     LONG next;               /* offset to next directory entry       */
     LONG data;               /* offset that this file starts at      */
     SHORT date;
     SHORT time;
     USHORT crc;
     LONG length;             /* bytes originally */
     LONG size;               /* bytes after compression */
     CHAR majver;
     CHAR minver;
     CHAR del;
     CHAR struc;
     LONG comment;            /* offset to comment; zero if none      */
     USHORT cmtsize;          /* length of comment; 0 if none         */
     CHAR fname[13];          /* filename                             */
};

struct flddef zooheadFDA[]={
     { CVTFLD_LONG  ,1 ,fldoff(zoohead,head)   ,NULL },
     { CVTFLD_CHAR  ,2 ,fldoff(zoohead,type)   ,NULL },
     { CVTFLD_LONG  ,2 ,fldoff(zoohead,next)   ,NULL },
     { CVTFLD_SHORT ,3 ,fldoff(zoohead,date)   ,NULL },
     { CVTFLD_LONG  ,2 ,fldoff(zoohead,length) ,NULL },
     { CVTFLD_CHAR  ,4 ,fldoff(zoohead,majver) ,NULL },
     { CVTFLD_LONG  ,1 ,fldoff(zoohead,comment),NULL },
     { CVTFLD_SHORT ,1 ,fldoff(zoohead,cmtsize),NULL },
     { CVTFLD_CHAR  ,13,fldoff(zoohead,fname)  ,NULL },
     { CVTFLD_END   ,0 ,0                      ,NULL }
};

                         /* Deflate compression flavors for PKZIP V2  */
#define NORCMP   0x0000  /*   Normal compression (-en)                */
#define MAXCMP   0x0002  /*   Maximum compression (-ex)               */
#define FSTCMP   0x0004  /*   Fast compression (-ef)                  */
#define SFSCMP   (MAXCMP+FSTCMP)   /* Super Fast compression (-es)    */

                         /* ZIP compression methods                   */
#define DEFLAT      8    /*   This file is Deflated                   */

static
CHAR *zoomth[2]={"Stored  ","LevZimp "};

static
CHAR *methds[]={"Stored  ","Shrunk  ","Reduce1 ","Reduce2 ","Reduce3 ",
                "Reduce4 ","Implode ","--------","Deflat"};

static
CHAR *arcmth[10]={"ENDOFARC","Stored  ","Stored  ","Packed  ","Squeezed",
                  "crunched","crunched","crunched","Crunched","Squashed"};
/* Chances of a false ID:
       ZOO format:  1 in       4,228,250,625
       ARC format:  1 in           1,658,137
       PSE format:  1 in       4,228,250,625
       ZIP format:  1 in   1,078,203,909,370
       ZSE format:  1 in 274,941,996,891,000
   ICE,LZH format:  1 in     107,820,390,937
*/

static
CHAR vewhdr[]="\r\
 Length   Method    Size  Ratio   Date    Time     CRC     Name\r\
 ------  --------  ------ -----   ----    ----    ------   ----\r\
";                                 /* view ARC/ZIP/LZH file announce/header */

static VOID arpli (CHAR *fname,INT fnleng,LONG length,CHAR *meth,LONG size,
             INT date,INT time,ULONG crc,INT cflag);
static INT zpprfr(VOID);
static CHAR *zipmth(struct dirhead *);
static INT arprfr(VOID);
static INT icprfr(VOID);
static INT zoprfr(VOID);
static VOID vewini(VOID);
static VOID vewsrt(VOID);
static VOID vewins(CHAR *stg);
static INT vewctn(VOID);
static VOID z2fend(VOID);

/*--- The Rob Rose ARC/ZIP/etc view routines ---*/

static VOID
arpli(                                                  /* report on 1 file */
CHAR *fname,                      /* name of file (may not be 0-terminated) */
INT fnleng,             /* max length of file name (not incl 0-term if any) */
LONG length,                                    /* bytes before compression */
CHAR *meth,                                        /* method of compression */
LONG size,                                       /* bytes after compression */
INT date,                                          /* DOS-style date & time */
INT time,
ULONG crc,                                                           /* CRC */
INT cflag)                                              /* 1=comments 0=not */
{
     CHAR buff[80];
     INT ratio;     /* percentage of reduction (e.g. 75% ==> 1/4 orig size) */

     if (length != 0) {
          ratio=100-(INT)((100*size)/length);
     }             /* note: LHARC program actually displays 100%-this ratio */
     else {
          ratio=0;
     }
     sprintf(buff,"%7ld  %8.8s %7ld %3d%%  %8.8s  %5.5s  %8.8lx   ",
             length,meth,size,ratio,ncdate(date),nctime(time),crc);
     ftfous(buff);
     sprintf(buff,"%-12.*s",min(79,fnleng),fname);
     ftfous(buff);
     if (cflag) {
          ftfous(" C");
     }
     ftfous("\r");
}

static INT
zpprfr(VOID)                               /* output one PKZIP record entry */
{                                     /* returns 1=done -1=error 0=continue */
     INT hdr;
     INT rval=0;
     struct dirhead dhdr;
     struct filhead fhdr;

     hdr=ftfbuf[2]+ftfbuf[3]*256;
     switch (hdr) {
     case 0x01EF:                   /* Self-Extracting Header               */
          vewscb->pos=0x31F0L;
          break;
     case 0x0201:                   /* Directory record                     */
          cvtData(ftfbuf,&dhdr,sizeof(struct dirhead),dirheadFDA,
                  CVTPACKED,CVTSERVER,CHAN_NUL);
          arpli(ftfbuf+0x2E,
                dhdr.namesz,
                dhdr.normsz,
                zipmth(&dhdr),
                dhdr.compsz,
                dhdr.fdate,
                dhdr.ftime,
                dhdr.crc32,
                dhdr.commsz);
          vewscb->pos+=dhdr.namesz+dhdr.xtrasz+dhdr.commsz+0x2e;
          break;
     case 0x0403:                   /* File header record                   */
          cvtData(ftfbuf,&fhdr,sizeof(struct filhead),filheadFDA,
                  CVTPACKED,CVTSERVER,CHAN_NUL);
          vewscb->pos+=fhdr.namesz+fhdr.xtrasz+fhdr.compsz+0x1e;
          break;
     case 0x0605:                   /* End of Central Dir Record            */
          rval=1;
          break;
     default:
          rval=-1;
          break;
     }
     return(rval);
}

static CHAR *                           /*   returns zip compression method*/
zipmth(                                 /* determines zip compression meth */
struct dirhead *dhdr)                   /*   pointer to directory head rec */
{
     static CHAR mthbuf[10];

     strcpy(mthbuf,methds[dhdr->method]);
     if (dhdr->method == DEFLAT) {     /* PKZIP V2x deflate method */
          if ((dhdr->flags&SFSCMP) == SFSCMP) {
               strcat(mthbuf,"S");
          }
          else if (dhdr->flags&FSTCMP) {
               strcat(mthbuf,"F");
          }
          else if (dhdr->flags&MAXCMP) {
               strcat(mthbuf,"X");
          }
          else {
               strcat(mthbuf,"N");
          }
     }
     return(mthbuf);
}

static INT
arprfr(VOID)                                 /* output one ARC record entry */
{                                     /* returns 1=done -1=error 0=continue */
     struct archead ahdr;

     cvtData(ftfbuf,&ahdr,sizeof(struct archead),archeadFDA,CVTPACKED,CVTSERVER,
             CHAN_NUL);
     if (vewscb->pos == 0L && ftfbuf[0] == 'M' && ftfbuf[1] == 'Z') {
          vewscb->pos=0x3352L;
          return(0);
     }
     if (*ftfbuf != 0x1a) {
          return(-1);
     }
     if (ahdr.hdrtyp == 0) {
          return(1);
     }
     arpli(ahdr.fname,
           12,
           ahdr.length,
           arcmth[(SHORT)ahdr.hdrtyp],
           ahdr.size,
           ahdr.date,
           ahdr.time,
           (LONG)ahdr.crc,
           0);
     vewscb->pos+=ahdr.size+0x1d;
     return(0);
}

static INT
icprfr(VOID)                                 /* output one ICE record entry */
{                                     /* returns 1=done -1=error 0=continue */
     ULONG crc;
     CHAR mbf[10];
     struct icehead ihdr;

     cvtData(ftfbuf,&ihdr,sizeof(struct icehead),iceheadFDA,CVTPACKED,CVTSERVER,
             CHAN_NUL);
     if (ftfbuf[2] != '-' && ftfbuf[6] != '-') {
          return(-1);
     }
     if (ihdr.hdrtyp[0] == 0) {
          return(1);
     }
     setmem(mbf,10,'\0');
     strncpy(mbf,ihdr.encod,5);
     crc=ftfbuf[22+ihdr.fnlen]+256*ftfbuf[23+ihdr.fnlen];
     arpli(ftfbuf+22,
           min(ihdr.fnlen,18),
           ihdr.length,
           mbf,
           ihdr.size,
           ihdr.date,
           ihdr.time,
           crc,
           0);
     vewscb->pos+=ihdr.fnlen+ihdr.size+(ihdr.encod[3] == '5' ? 0x1B : 0x18);
     return(0);
}

static INT
zoprfr(VOID)                                 /* output one ZOO record entry */
{                                     /* returns 1=done -1=error 0=continue */
     struct zoohead ohdr;

     if (vewscb->pos == 0L) {            /* Skip header portion             */
          vewscb->pos=20L;
          return(0);
     }
     if (vewscb->pos == 20L) {           /* Directory header                */
          cvtData(ftfbuf+4,&vewscb->pos,sizeof(LONG),longFDA,
                  CVTPACKED,CVTSERVER,CHAN_NUL);
          return(0);
     }
     cvtData(ftfbuf,&ohdr,sizeof(struct zoohead),zooheadFDA,CVTPACKED,CVTSERVER,
             CHAN_NUL);
     if (ohdr.head != 0xFDC4A7DCL) {    /* Look for                        */
          return(-1);
     }
     if (ohdr.length == 0
      && ohdr.size == 0
      && ohdr.date == 0
      && ohdr.time == 0
      && ohdr.meth == 0
      && ohdr.crc == 0) {
          return(1);
     }
     arpli(ohdr.fname,
           12,
           ohdr.length,
           zoomth[(SHORT)ohdr.meth],
           ohdr.size,
           ohdr.date,
           ohdr.time,
           (ULONG)ohdr.crc,
           (INT)ohdr.comment);
     vewscb->pos=ohdr.next;
     return(0);
}

INT
zafilid(                                              /* Identify File type */
CHAR *recbuf)                               /* first VIDCHUNK bytes of file */
{                                                        /* returns file id */
     INT i;

     if (strncmp(recbuf,"ZOO ",4) == 0) {
          return(ZOOFILE);
     }
     if (*recbuf == 26) {
          if (recbuf[1] > 9) {
               return(UNKFILE);
          }
          for (i=2 ; i < 15 ; i++) {
               if (recbuf[i] == '\0') {
                    break;
               }
               if (recbuf[i] < ' ' || recbuf[i] > '}') {
                    return(UNKFILE);
               }
          }
          return(ARCFILE);
     }
     if (recbuf[0] == 'M'
      && recbuf[1] == 'Z'
      && recbuf[2] == 'R'
      && recbuf[3] == 0x01) {
          return(PSEFILE);
     }
     if (recbuf[0] == 'P'
      && recbuf[1] == 'K'
      && ((recbuf[2] == 0x03
      && recbuf[3] == 0x04)
      || (recbuf[2] == 0x07
      && recbuf[3] == 0x08))) {
          return(recbuf[4] < 0x14 ? ZIPFILE : ZP2FILE);
     }
     if (recbuf[0] == 'M'
      && recbuf[1] == 'Z') {
          if (recbuf[0x32] == 'P'
           && recbuf[0x33] == 'K') {
               return(ZSEFILE);
          }
          if (recbuf[0x1E] == 'P'
           && recbuf[0x1F] == 'K'
           && recbuf[0x20] == 'L'
           && recbuf[0x21] == 'I'
           && recbuf[0x22] == 'T'
           && recbuf[0x23] == 'E') {
               return(ZS2FILE);
          }
     }
     if (recbuf[2] == '-'
      && recbuf[3] == 'l'
      && recbuf[4] == 'h'
      && recbuf[5] >= '1' && recbuf[5] <= '9'
      && recbuf[6] == '-') {
          return(ICEFILE);
     }
     return(UNKFILE);
}

/*--- The FTF.H interface for 'V' protocol output ---*/

static VOID
vewini(VOID)                                           /* Initialize Viewer */
{
     if (fbleng < max(VIDCHUNK,ZP2CHUNK)) {
          fbleng=max(VIDCHUNK,ZP2CHUNK);
     }
}

static VOID
vewsrt(VOID)                                      /* Begin viewing sesssion */
{
     setmem(vewscb,sizeof(*vewscb),0);
     ftfnew(VEWOPN);
}

static VOID
vewins(                         /* Handle incoming line during View session */
CHAR *stg)
{
     ftfabt(stg == NULL ? "" : "Operator abort");
}

static INT
vewctn(VOID)                                            /* Continue Viewing */
{
     INT rc;

     switch (ftfscb->state) {
     case VEWOPN:
          if (ftfxop() == 0) {
               ftfsrt();
               ftfnew(VEWFID);
          }
          else {
               ftfabt("Cannot find file");
          }
          break;
     case VEWFID:
          if ((rc=ftfxrd(ftfbuf,VIDCHUNK)) < VIDCHUNK) {
               ftfabt(rc < 0 ? "Error reading file." : arcerr[0]);
          }
          else if ((vewscb->filid=zafilid(ftfbuf)) == UNKFILE) {
               ftfabt(arcerr[0]);
          }
          else {
               if ((vewscb->filid == ZP2FILE || vewscb->filid != ZS2FILE)
                 && ftfbuf[2] == 0x07 && ftfbuf[3] == 0x08) {
                    ftfabt("Unable to view zip files spanning multiple disks");
                    return(1);
               }
               ftfous(vewhdr);
               vewscb->pos=0L;
               if (vewscb->filid != ZP2FILE && vewscb->filid != ZS2FILE) {
                    ftfnew(VEWFIL);
               }
               else {
                    if (ftfscb->estbyt > (LONG)ZP2CHUNK) {
                         vewscb->pos=ftfscb->estbyt-(LONG)ZP2CHUNK;
                    }
                    ftfnew(VEWFND);
               }
          }
          break;
     case VEWFIL:
          if (ftfoba() < 100) {
               break;
          }
          ftfxsk(vewscb->pos);
          if (ftfxrd(ftfbuf,VEWCHUNK) <= 0) {
               ftfabt(arcerr[(SHORT)vewscb->filid]);
               break;
          }
          switch(vewscb->filid) {
          case ARCFILE:
          case PSEFILE:
               rc=arprfr();
               break;
          case ZIPFILE:
          case ZSEFILE:
          case ZP2FILE:
          case ZS2FILE:
               rc=zpprfr();
               break;
          case ICEFILE:
               rc=icprfr();
               break;
          case ZOOFILE:
               rc=zoprfr();
               break;
          default:
               ftfabt("Invalid type-code for compressed file");
               rc=0;
               break;
          }
          if (rc == -1) {
               ftfabt(arcerr[(SHORT)vewscb->filid]);
          }
          else if (rc == 1) {
               ftfnew(VEWEND);
          }
          ftfscb->tckact=ftftck;
          ftfact=1;
          break;
     case VEWFND:
          ftfxsk(vewscb->pos);
          if (ftfxrd(ftfbuf,
              ftfscb->estbyt > (LONG)ZP2CHUNK ? ZP2CHUNK
                                              : (INT)ftfscb->estbyt) <= 0) {
               ftfabt(arcerr[(SHORT)vewscb->filid]);
               break;
          }
          z2fend();
          break;
     case FTFABORT:
          ftfxca();
          return(-1);
     case VEWEND:
          if (ftfoba() == ftfomt) {
               ftfxcl(1);
               ftfscb->isopen=0;
               ftfscb->actfil++;
               return(0);
          }
          break;
     }
     return(1);
}

static VOID
z2fend(VOID)                      /* find end of central dir record in ZIP2 */
{
     CHAR *ptr;
     INT cnt;
     LONG oldpos;
     INT byts;
     struct endcdr endcdr;

     oldpos=vewscb->pos;
     byts=ftfscb->estbyt > (LONG)ZP2CHUNK ? ZP2CHUNK : (INT)ftfscb->estbyt;
     cnt=byts-CDRLEN;
     ptr=&ftfbuf[cnt];
     while (cnt >= 0) {
          if (*ptr == 'P'
            && *(ptr+1) == 'K'
            && *(ptr+2) == 5
            && *(ptr+3) == 6) {
               cvtData(&ftfbuf[cnt],&endcdr,sizeof(struct endcdr),endcdrFDA,
                       CVTPACKED,CVTSERVER,CHAN_NUL);
               vewscb->pos=endcdr.ctroff;
               ftfnew(VEWFIL);
               return;
          }
          ptr--;
          cnt--;
     }
     if (oldpos != 0L) {
          vewscb->pos-=(LONG)(ZP2CHUNK-CDRLEN);
          if (vewscb->pos < 0L) {
               vewscb->pos=0L;
          }
     }
     else {
          ftfabt(arcerr[(SHORT)vewscb->filid]);
     }
}

struct ftfpsp ftpvew={                /* ARC/ZIP/etc file View transmitting */
     NULL,
     "V",                                  /* 1-3 code letters for protocol */
     "View compressed file",                            /* name of protocol */
     FTFXMT+FTFASC+FTF7BT+FTFAFN+FTFXTD,       /* protocol capability flags */
     sizeof(struct vewdat),        /* total length of session control block */
     0,         /* .byttmo                             default byte timeout */
     0,         /* .paktmo                           default packet timeout */
     0,         /* .retrys                              default max retries */
     0L,        /* .window   max window size (packets/bytes as appropriate) */
     0,         /* .paksiz                        packet size 0=auto-figure */
     vewini,    /* .initze()    Initialize this protocol (recompute scblen) */
     vewsrt,    /* .start()                                Start a transfer */
     vewctn,    /* .contin()              Continuously call, 1=more, 0=done */
     NULL,      /* .hdlinc()                       Handle one incoming byte */
     vewins,    /* .hdlins()                   Handle incoming line of text */
     ftfabt,    /* .term()        Initiate graceful termination of transfer */
     ftfxca,    /* .abort()  Immediately unconditionally abort the transfer */
     NULL,      /* .hdlinb()              Handle an array of incoming bytes */
     "",        /* .secur                App-specific security of some kind */
     {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
