home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / modem / rzsz.arc / SZ.C < prev    next >
C/C++ Source or Header  |  1987-08-21  |  31KB  |  1,470 lines

  1. #define VERSION "sz 1.35 08-21-87"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*% cc -M0 -Ox -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz
  5.  *
  6.  * sz.c By Chuck Forsberg
  7.  *
  8.  *    cc -O sz.c -o sz        USG (SYS III/V) Unix
  9.  *    cc -O -DSVR2 sz.c -o sz        Sys V Release 2 with non-blocking input
  10.  *                    Define to allow reverse channel checking
  11.  *    cc -O -DV7  sz.c -o sz        Unix Version 7, 2.8 - 4.3 BSD
  12.  *
  13.  *    cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz    Xenix
  14.  *
  15.  *    ln sz sb            **** All versions ****
  16.  *    ln sz sx            **** All versions ****
  17.  *
  18.  *
  19.  *  ******* Some systems (Venix, Coherent, Regulus) do not *******
  20.  *  ******* support tty raw mode read(2) identically to    *******
  21.  *  ******* Unix. ONEREAD must be defined to force one     *******
  22.  *  ******* character reads for these systems.           *******
  23.  *
  24.  * A program for Unix to send files and commands to computers running
  25.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM.
  26.  *
  27.  *  Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM.
  28.  *
  29.  *  USG UNIX (3.0) ioctl conventions courtesy Jeff Martin
  30.  *
  31.  * 1.34 implements tx backchannel garbage count and ZCRCW after ZRPOS
  32.  * in accordance with the 7-31-87 ZMODEM Protocol Description
  33.  */
  34.  
  35.  
  36. char *substr(), *getenv();
  37.  
  38. #define LOGFILE "/tmp/szlog"
  39.  
  40. #include <stdio.h>
  41. #include <signal.h>
  42. #include <setjmp.h>
  43. #include <ctype.h>
  44.  
  45. #define PATHLEN 256
  46. #define OK 0
  47. #define FALSE 0
  48. #define TRUE 1
  49. #define ERROR (-1)
  50.  
  51. #define HOWMANY 2
  52. int Zmodem=0;        /* ZMODEM protocol requested */
  53. unsigned Baudrate;
  54. unsigned Txwindow;    /* Control the size of the transmitted window */
  55. unsigned Txwspac;    /* Spacing between zcrcq requests */
  56. unsigned Txwcnt;    /* Counter used to space ack requests */
  57. long Lrxpos;        /* Receiver's last reported offset */
  58. int Fromcu = 0;        /* Were called from cu or yam */
  59. int errors;
  60. #include "rbsb.c"    /* most of the system dependent stuff here */
  61.  
  62. /*
  63.  * Attention string to be executed by receiver to interrupt streaming data
  64.  *  when an error is detected.  A pause (0336) may be needed before the
  65.  *  ^C (03) or after it.
  66.  */
  67. #ifdef READCHECK
  68. char Myattn[] = { 0 };
  69. #else
  70. #ifdef USG
  71. char Myattn[] = { 03, 0336, 0 };
  72. #else
  73. char Myattn[] = { 0 };
  74. #endif
  75. #endif
  76.  
  77. FILE *in;
  78.  
  79. /* Ward Christensen / CP/M parameters - Don't change these! */
  80. #define ENQ 005
  81. #define CAN ('X'&037)
  82. #define XOFF ('s'&037)
  83. #define XON ('q'&037)
  84. #define SOH 1
  85. #define STX 2
  86. #define EOT 4
  87. #define ACK 6
  88. #define NAK 025
  89. #define CPMEOF 032
  90. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  91. #define WANTG 0107    /* Send G not NAK to get nonstop batch xmsn */
  92. #define TIMEOUT (-2)
  93. #define RCDO (-3)
  94. #define RETRYMAX 10
  95. #define SECSIZ 128    /* cp/m's Magic Number record size */
  96. #define KSIZE 1024
  97.  
  98. char Lastrx;
  99. char Crcflg;
  100. int Wcsmask=0377;
  101. int Verbose=0;
  102. int Modem=0;        /* MODEM - don't send pathnames */
  103. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  104. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  105. int Ascii=0;        /* Add CR's for brain damaged programs */
  106. int Fullname=0;        /* transmit full pathname */
  107. int Unlinkafter=0;    /* Unlink file after it is sent */
  108. int Dottoslash=0;    /* Change foo.bar.baz to foo/bar/baz */
  109. int firstsec;
  110. int errcnt=0;        /* number of files unreadable */
  111. int blklen=SECSIZ;        /* length of transmitted records */
  112. int Optiong;        /* Let it rip no wait for sector ACK's */
  113. int Noeofseen;
  114. int Totsecs;        /* total number of sectors this file */
  115. char txbuf[KSIZE];
  116. int Filcnt=0;        /* count of number of files opened */
  117. int Lfseen=0;
  118. unsigned Rxbuflen = 16384;    /* Receiver's max buffer length */
  119. int Tframlen = 0;    /* Override for tx frame length */
  120. int blkopt=0;        /* Override value for zmodem blklen */
  121. int Rxflags = 0;
  122. long bytcnt;
  123. int Wantfcs32 = TRUE;    /* want to send 32 bit FCS */
  124. char Lzconv;    /* Local ZMODEM file conversion request */
  125. char Lzmanag;    /* Local ZMODEM file management request */
  126. int Lskipnocor;
  127. char Lztrans;
  128. char zconv;        /* ZMODEM file conversion request */
  129. char zmanag;        /* ZMODEM file management request */
  130. char ztrans;        /* ZMODEM file transport request */
  131. int Command;        /* Send a command, then exit. */
  132. char *Cmdstr;        /* Pointer to the command string */
  133. int Cmdtries = 11;
  134. int Cmdack1;        /* Rx ACKs command, then do it */
  135. int Exitcode;
  136. int Testattn;        /* Force receiver to send Attn, etc with qbf. */
  137. char *qbf="The quick brown fox jumped over the lazy dog's back 1234567890\r\n";
  138. long Lastread;        /* Beginning offset of last buffer read */
  139. int Lastn;        /* Count of last buffer read or -1 */
  140. int Dontread;        /* Don't read the buffer, it's still there */
  141. long Lastsync;        /* Last offset to which we got a ZRPOS */
  142. int Beenhereb4;        /* How many times we've been ZRPOS'd same place */
  143.  
  144. jmp_buf tohere;        /* For the interrupt on RX timeout */
  145. jmp_buf intrjmp;    /* For the interrupt on RX CAN */
  146.  
  147. /* called by signal interrupt or terminate to clean things up */
  148. bibi(n)
  149. {
  150.     canit(); fflush(stdout); mode(0);
  151.     fprintf(stderr, "sz: caught signal %d; exiting\n", n);
  152.     if (n == SIGQUIT)
  153.         abort();
  154.     exit(128+n);
  155. }
  156. /* Called when Zmodem gets an interrupt (^X) */
  157. onintr()
  158. {
  159.     signal(SIGINT, SIG_IGN);
  160.     longjmp(intrjmp, -1);
  161. }
  162.  
  163.  
  164. #define sendline(c) putchar(c & Wcsmask)
  165.  
  166. #define xsendline(c) putchar(c)
  167.  
  168. flushmo()
  169. {
  170.     fflush(stdout);
  171. }
  172.  
  173. int Zctlesc;    /* Encode control characters */
  174. int Nozmodem = 0;    /* If invoked as "sb" */
  175. char *Progname = "sz";
  176. int Zrwindow = 1400;    /* RX window size (controls garbage count) */
  177. #include "zm.c"
  178.  
  179.  
  180. main(argc, argv)
  181. char *argv[];
  182. {
  183.     register char *cp;
  184.     register npats;
  185.     int agcnt; char **agcv;
  186.     char **patts;
  187.     static char xXbuf[BUFSIZ];
  188.  
  189.     if ((cp = getenv("ZNULLS")) && *cp)
  190.         Znulls = atoi(cp);
  191.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  192.         Restricted=TRUE;
  193.     chkinvok(argv[0]);
  194.  
  195.     Rxtimeout = 600;
  196.     npats=0;
  197.     if (argc<2)
  198.         usage();
  199.     setbuf(stdout, xXbuf);        
  200.     while (--argc) {
  201.         cp = *++argv;
  202.         if (*cp++ == '-' && *cp) {
  203.             while ( *cp) {
  204.                 switch(*cp++) {
  205.                 case '+':
  206.                     Lzmanag = ZMAPND; break;
  207.                 case '1':
  208.                     iofd = 1; break;
  209. #ifdef CSTOPB
  210.                 case '2':
  211.                     Twostop = TRUE; break;
  212. #endif
  213.                 case '7':
  214.                     Wcsmask=0177; break;
  215.                 case 'a':
  216.                     Lzconv = ZCNL;
  217.                     Ascii = TRUE; break;
  218.                 case 'b':
  219.                     Lzconv = ZCBIN; break;
  220.                 case 'C':
  221.                     if (--argc < 1) {
  222.                         usage();
  223.                     }
  224.                     Cmdtries = atoi(*++argv);
  225.                     break;
  226.                 case 'i':
  227.                     Cmdack1 = ZCACK1;
  228.                     /* **** FALL THROUGH TO **** */
  229.                 case 'c':
  230.                     if (--argc != 1) {
  231.                         usage();
  232.                     }
  233.                     Command = TRUE;
  234.                     Cmdstr = *++argv;
  235.                     break;
  236.                 case 'd':
  237.                     ++Dottoslash;
  238.                     /* **** FALL THROUGH TO **** */
  239.                 case 'f':
  240.                     Fullname=TRUE; break;
  241.                 case 'e':
  242.                     Zctlesc = 1; break;
  243.                 case 'k':
  244.                     blklen=KSIZE; break;
  245.                 case 'L':
  246.                     if (--argc < 1) {
  247.                         usage();
  248.                     }
  249.                     blkopt = atoi(*++argv);
  250.                     if (blkopt<24 || blkopt>1024)
  251.                         usage();
  252.                     break;
  253.                 case 'l':
  254.                     if (--argc < 1) {
  255.                         usage();
  256.                     }
  257.                     Tframlen = atoi(*++argv);
  258.                     if (Tframlen<32 || Tframlen>1024)
  259.                         usage();
  260.                     break;
  261.                 case 'N':
  262.                     Lzmanag = ZMNEWL;  break;
  263.                 case 'n':
  264.                     Lzmanag = ZMNEW;  break;
  265.                 case 'o':
  266.                     Wantfcs32 = FALSE; break;
  267.                 case 'p':
  268.                     Lzmanag = ZMPROT;  break;
  269.                 case 'r':
  270.                     Lzconv = ZCRESUM;
  271.                 case 'q':
  272.                     Quiet=TRUE; Verbose=0; break;
  273.                 case 't':
  274.                     if (--argc < 1) {
  275.                         usage();
  276.                     }
  277.                     Rxtimeout = atoi(*++argv);
  278.                     if (Rxtimeout<10 || Rxtimeout>1000)
  279.                         usage();
  280.                     break;
  281.                 case 'T':
  282.                     Testattn = TRUE; break;
  283.                 case 'u':
  284.                     ++Unlinkafter; break;
  285.                 case 'v':
  286.                     ++Verbose; break;
  287.                 case 'w':
  288.                     if (--argc < 1) {
  289.                         usage();
  290.                     }
  291.                     Txwindow = atoi(*++argv);
  292.                     if (Txwindow < 256)
  293.                         Txwindow = 256;
  294.                     Txwindow = (Txwindow/64) * 64;
  295.                     Txwspac = Txwindow/4;
  296.                     if (blkopt > Txwspac
  297.                      || (!blkopt && Txwspac < 1024))
  298.                         blkopt = Txwspac;
  299.                     break;
  300.                 case 'X':
  301.                     ++Modem; break;
  302.                 case 'Y':
  303.                     Lskipnocor = TRUE;
  304.                     /* **** FALLL THROUGH TO **** */
  305.                 case 'y':
  306.                     Lzmanag = ZMCLOB; break;
  307.                 default:
  308.                     usage();
  309.                 }
  310.             }
  311.         }
  312.         else if ( !npats && argc>0) {
  313.             if (argv[0][0]) {
  314.                 npats=argc;
  315.                 patts=argv;
  316.                 if ( !strcmp(*patts, "-"))
  317.                     iofd = 1;
  318.             }
  319.         }
  320.     }
  321.     if (npats < 1 && !Command) 
  322.         usage();
  323.     if (Verbose) {
  324.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  325.             printf("Can't open log file %s\n",LOGFILE);
  326.             exit(0200);
  327.         }
  328.         setbuf(stderr, NULL);
  329.     }
  330.     if ((Fromcu=from_cu()) && !Quiet) {
  331.         if (Verbose == 0)
  332.             Verbose = 2;
  333.     }
  334.  
  335.     mode(1);
  336.  
  337.     if (signal(SIGINT, bibi) == SIG_IGN) {
  338.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  339.     } else {
  340.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  341.     }
  342.     if ( !Fromcu)
  343.         signal(SIGQUIT, SIG_IGN);
  344.     signal(SIGTERM, bibi);
  345.  
  346.     if ( !Modem) {
  347.         if (!Nozmodem) {
  348.             printf("rz\r");  fflush(stdout);
  349.         }
  350.         if (!Command && !Quiet && Verbose != 1) {
  351.             fprintf(stderr, "%s: %d file%s requested:\r\n",
  352.              Progname, npats, npats>1?"s":"");
  353.             for ( agcnt=npats, agcv=patts; --agcnt>=0; ) {
  354.                 fprintf(stderr, "%s ", *agcv++);
  355.             }
  356.             fprintf(stderr, "\r\n");
  357.             printf("\r\n\bSending in Batch Mode\r\n");
  358.         }
  359.         if (!Nozmodem) {
  360.             stohdr(0L);
  361.             if (Command)
  362.                 Txhdr[ZF0] = ZCOMMAND;
  363.             zshhdr(ZRQINIT, Txhdr);
  364.         }
  365.     }
  366.     fflush(stdout);
  367.  
  368.     if (Command) {
  369.         if (getzrxinit()) {
  370.             Exitcode=0200; canit();
  371.         }
  372.         else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
  373.             Exitcode=0200; canit();
  374.         }
  375.     } else if (wcsend(npats, patts)==ERROR) {
  376.         Exitcode=0200;
  377.         canit();
  378.     }
  379.     fflush(stdout);
  380.     mode(0);
  381.     exit((errcnt != 0) | Exitcode);
  382.     /*NOTREACHED*/
  383. }
  384.  
  385. wcsend(argc, argp)
  386. char *argp[];
  387. {
  388.     register n;
  389.  
  390.     Crcflg=FALSE;
  391.     firstsec=TRUE;
  392.     bytcnt = -1;
  393.     for (n=0; n<argc; ++n) {
  394.         Totsecs = 0;
  395.         if (wcs(argp[n])==ERROR)
  396.             return ERROR;
  397.     }
  398.     Totsecs = 0;
  399.     if (Filcnt==0) {    /* bitch if we couldn't open ANY files */
  400.         if (1) {
  401.             Command = TRUE;
  402.             Cmdstr = "echo \"sz: Can't open any requested files\"";
  403.             if (getnak()) {
  404.                 Exitcode=0200; canit();
  405.             }
  406.             if (!Zmodem)
  407.                 canit();
  408.             else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
  409.                 Exitcode=0200; canit();
  410.             }
  411.             Exitcode = 1; return OK;
  412.         }
  413.         canit();
  414.         fprintf(stderr,"\r\nCan't open any requested files.\r\n");
  415.         return ERROR;
  416.     }
  417.     if (Zmodem)
  418.         saybibi();
  419.     else
  420.         wctxpn("");
  421.     return OK;
  422. }
  423.  
  424. wcs(oname)
  425. char *oname;
  426. {
  427.     register c;
  428.     register char *p;
  429.     struct stat f;
  430.     char name[PATHLEN];
  431.  
  432.     strcpy(name, oname);
  433.  
  434.     if (Restricted) {
  435.         /* restrict pathnames to current tree or uucppublic */
  436.         if ( substr(name, "../")
  437.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  438.             canit();
  439.             fprintf(stderr,"\r\nsz:\tSecurity Violation\r\n");
  440.             return ERROR;
  441.         }
  442.     }
  443.  
  444.     if ( !strcmp(oname, "-")) {
  445.         if ((p = getenv("ONAME")) && *p)
  446.             strcpy(name, p);
  447.         else
  448.             sprintf(name, "s%d.sz", getpid());
  449.         in = stdin;
  450.     }
  451.     else if ((in=fopen(oname, "r"))==NULL) {
  452.         ++errcnt;
  453.         return OK;    /* pass over it, there may be others */
  454.     }
  455.     ++Noeofseen;  Lastread = 0;  Lastn = -1; Dontread = FALSE;
  456.     /* Check for directory or block special files */
  457.     fstat(fileno(in), &f);
  458.     c = f.st_mode & S_IFMT;
  459.     if (c == S_IFDIR || c == S_IFBLK) {
  460.         fclose(in);
  461.         return OK;
  462.     }
  463.  
  464.     ++Filcnt;
  465.     switch (wctxpn(name)) {
  466.     case ERROR:
  467.         return ERROR;
  468.     case ZSKIP:
  469.         return OK;
  470.     }
  471.     if (!Zmodem && wctx(f.st_size)==ERROR)
  472.         return ERROR;
  473.     if (Unlinkafter)
  474.         unlink(oname);
  475.     return 0;
  476. }
  477.  
  478. /*
  479.  * generate and transmit pathname block consisting of
  480.  *  pathname (null terminated),
  481.  *  file length, mode time and file mode in octal
  482.  *  as provided by the Unix fstat call.
  483.  *  N.B.: modifies the passed name, may extend it!
  484.  */
  485. wctxpn(name)
  486. char *name;
  487. {
  488.     register char *p, *q;
  489.     char name2[PATHLEN];
  490.     struct stat f;
  491.  
  492.     if (Modem) {
  493.         if ((in!=stdin) && *name && fstat(fileno(in), &f)!= -1) {
  494.             fprintf(stderr, "Sending %s, %ld blocks: ",
  495.               name, f.st_size>>7);
  496.         }
  497.         fprintf(stderr, "Give your local XMODEM receive command now.\r\n");
  498.         return OK;
  499.     }
  500.     zperr("Awaiting pathname nak for %s", *name?name:"<END>");
  501.     if ( !Zmodem)
  502.         if (getnak())
  503.             return ERROR;
  504.  
  505.     q = (char *) 0;
  506.     if (Dottoslash) {        /* change . to . */
  507.         for (p=name; *p; ++p) {
  508.             if (*p == '/')
  509.                 q = p;
  510.             else if (*p == '.')
  511.                 *(q=p) = '/';
  512.         }
  513.         if (q && strlen(++q) > 8) {    /* If name>8 chars */
  514.             q += 8;            /*   make it .ext */
  515.             strcpy(name2, q);    /* save excess of name */
  516.             *q = '.';
  517.             strcpy(++q, name2);    /* add it back */
  518.         }
  519.     }
  520.  
  521.     for (p=name, q=txbuf ; *p; )
  522.         if ((*q++ = *p++) == '/' && !Fullname)
  523.             q = txbuf;
  524.     *q++ = 0;
  525.     p=q;
  526.     while (q < (txbuf + KSIZE))
  527.         *q++ = 0;
  528.     if (!Ascii && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1)
  529.         sprintf(p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode);
  530.     /* force 1k blocks if name won't fit in 128 byte block */
  531.     if (txbuf[125])
  532.         blklen=KSIZE;
  533.     else {        /* A little goodie for IMP/KMD */
  534.         if (Zmodem)
  535.             blklen = SECSIZ;
  536.         txbuf[127] = (f.st_size + 127) >>7;
  537.         txbuf[126] = (f.st_size + 127) >>15;
  538.     }
  539.     if (Zmodem)
  540.         return zsendfile(txbuf, 1+strlen(p)+(p-txbuf));
  541.     if (wcputsec(txbuf, 0, SECSIZ)==ERROR)
  542.         return ERROR;
  543.     return OK;
  544. }
  545.  
  546. getnak()
  547. {
  548.     register firstch;
  549.  
  550.     Lastrx = 0;
  551.     for (;;) {
  552.         switch (firstch = readock(800,1)) {
  553.         case ZPAD:
  554.             if (getzrxinit())
  555.                 return ERROR;
  556.             Ascii = 0;
  557.             return FALSE;
  558.         case TIMEOUT:
  559.             zperr("Timeout on pathname");
  560.             return TRUE;
  561.         case WANTG:
  562. #ifdef USG
  563.             mode(2);    /* Set cbreak, XON/XOFF, etc. */
  564. #endif
  565.             Optiong = TRUE;
  566.             blklen=KSIZE;
  567.         case WANTCRC:
  568.             Crcflg = TRUE;
  569.         case NAK:
  570.             return FALSE;
  571.         case CAN:
  572.             if ((firstch = readock(20,1)) == CAN && Lastrx == CAN)
  573.                 return TRUE;
  574.         default:
  575.             break;
  576.         }
  577.         Lastrx = firstch;
  578.     }
  579. }
  580.  
  581.  
  582. wctx(flen)
  583. long flen;
  584. {
  585.     register int thisblklen;
  586.     register int sectnum, attempts, firstch;
  587.     long charssent;
  588.  
  589.     charssent = 0;  firstsec=TRUE;  thisblklen = blklen;
  590.     vfile("wctx:file length=%ld", flen);
  591.  
  592.     while ((firstch=readock(Rxtimeout, 2))!=NAK && firstch != WANTCRC
  593.       && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN)
  594.         ;
  595.     if (firstch==CAN) {
  596.         zperr("Receiver CANcelled");
  597.         return ERROR;
  598.     }
  599.     if (firstch==WANTCRC)
  600.         Crcflg=TRUE;
  601.     if (firstch==WANTG)
  602.         Crcflg=TRUE;
  603.     sectnum=0;
  604.     for (;;) {
  605.         if (flen <= (charssent + 896L))
  606.             thisblklen = 128;
  607.         if ( !filbuf(txbuf, thisblklen))
  608.             break;
  609.         if (wcputsec(txbuf, ++sectnum, thisblklen)==ERROR)
  610.             return ERROR;
  611.         charssent += thisblklen;
  612.     }
  613.     fclose(in);
  614.     attempts=0;
  615.     do {
  616.         purgeline();
  617.         sendline(EOT);
  618.         fflush(stdout);
  619.         ++attempts;
  620.     }
  621.         while ((firstch=(readock(Rxtimeout, 1)) != ACK) && attempts < RETRYMAX);
  622.     if (attempts == RETRYMAX) {
  623.         zperr("No ACK on EOT");
  624.         return ERROR;
  625.     }
  626.     else
  627.         return OK;
  628. }
  629.  
  630. wcputsec(buf, sectnum, cseclen)
  631. char *buf;
  632. int sectnum;
  633. int cseclen;    /* data length of this sector to send */
  634. {
  635.     register checksum, wcj;
  636.     register char *cp;
  637.     unsigned oldcrc;
  638.     int firstch;
  639.     int attempts;
  640.  
  641.     firstch=0;    /* part of logic to detect CAN CAN */
  642.  
  643.     if (Verbose>2)
  644.         fprintf(stderr, "Sector %3d %2dk\n", Totsecs, Totsecs/8 );
  645.     else if (Verbose>1)
  646.         fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 );
  647.     for (attempts=0; attempts <= RETRYMAX; attempts++) {
  648.         Lastrx= firstch;
  649.         sendline(cseclen==KSIZE?STX:SOH);
  650.         sendline(sectnum);
  651.         sendline(-sectnum -1);
  652.         oldcrc=checksum=0;
  653.         for (wcj=cseclen,cp=buf; --wcj>=0; ) {
  654.             sendline(*cp);
  655.             oldcrc=updcrc((0377& *cp), oldcrc);
  656.             checksum += *cp++;
  657.         }
  658.         if (Crcflg) {
  659.             oldcrc=updcrc(0,updcrc(0,oldcrc));
  660.             sendline((int)oldcrc>>8);
  661.             sendline((int)oldcrc);
  662.         }
  663.         else
  664.             sendline(checksum);
  665.  
  666.         if (Optiong) {
  667.             firstsec = FALSE; return OK;
  668.         }
  669.         firstch = readock(Rxtimeout, (Noeofseen&§num) ? 2:1);
  670. gotnak:
  671.         switch (firstch) {
  672.         case CAN:
  673.             if(Lastrx == CAN) {
  674. cancan:
  675.                 zperr("Cancelled");  return ERROR;
  676.             }
  677.             break;
  678.         case TIMEOUT:
  679.             zperr("Timeout on sector ACK"); continue;
  680.         case WANTCRC:
  681.             if (firstsec)
  682.                 Crcflg = TRUE;
  683.         case NAK:
  684.             zperr("NAK on sector"); continue;
  685.         case ACK: 
  686.             firstsec=FALSE;
  687.             Totsecs += (cseclen>>7);
  688.             return OK;
  689.         case ERROR:
  690.             zperr("Got burst for sector ACK"); break;
  691.         default:
  692.             zperr("Got %02x for sector ACK", firstch); break;
  693.         }
  694.         for (;;) {
  695.             Lastrx = firstch;
  696.             if ((firstch = readock(Rxtimeout, 2)) == TIMEOUT)
  697.                 break;
  698.             if (firstch == NAK || firstch == WANTCRC)
  699.                 goto gotnak;
  700.             if (firstch == CAN && Lastrx == CAN)
  701.                 goto cancan;
  702.         }
  703.     }
  704.     zperr("Retry Count Exceeded");
  705.     return ERROR;
  706. }
  707.  
  708. /* fill buf with count chars padding with ^Z for CPM */
  709. filbuf(buf, count)
  710. register char *buf;
  711. {
  712.     register c, m;
  713.  
  714.     if ( !Ascii) {
  715.         m = read(fileno(in), buf, count);
  716.         if (m <= 0)
  717.             return 0;
  718.         while (m < count)
  719.             buf[m++] = 032;
  720.         return count;
  721.     }
  722.     m=count;
  723.     if (Lfseen) {
  724.         *buf++ = 012; --m; Lfseen = 0;
  725.     }
  726.     while ((c=getc(in))!=EOF) {
  727.         if (c == 012) {
  728.             *buf++ = 015;
  729.             if (--m == 0) {
  730.                 Lfseen = TRUE; break;
  731.             }
  732.         }
  733.         *buf++ =c;
  734.         if (--m == 0)
  735.             break;
  736.     }
  737.     if (m==count)
  738.         return 0;
  739.     else
  740.         while (--m>=0)
  741.             *buf++ = CPMEOF;
  742.     return count;
  743. }
  744. /* fill buf with count chars */
  745. zfilbuf(buf, count)
  746. register char *buf;
  747. {
  748.     register c, m;
  749.  
  750.     m=count;
  751.     while ((c=getc(in))!=EOF) {
  752.         *buf++ =c;
  753.         if (--m == 0)
  754.             break;
  755.     }
  756.     return (count - m);
  757. }
  758.  
  759. /* VARARGS1 */
  760. vfile(f, a, b, c)
  761. register char *f;
  762. {
  763.     if (Verbose > 2) {
  764.         fprintf(stderr, f, a, b, c);
  765.         fprintf(stderr, "\n");
  766.     }
  767. }
  768.  
  769.  
  770. alrm()
  771. {
  772.     longjmp(tohere, -1);
  773. }
  774.  
  775.  
  776. /*
  777.  * readock(timeout, count) reads character(s) from file descriptor 0
  778.  *  (1 <= count <= 3)
  779.  * it attempts to read count characters. If it gets more than one,
  780.  * it is an error unless all are CAN
  781.  * (otherwise, only normal response is ACK, CAN, or C)
  782.  *  Only looks for one if Optiong, which signifies cbreak, not raw input
  783.  *
  784.  * timeout is in tenths of seconds
  785.  */
  786. readock(timeout, count)
  787. {
  788.     register int c;
  789.     static char byt[5];
  790.  
  791.     if (Optiong)
  792.         count = 1;    /* Special hack for cbreak */
  793.  
  794.     fflush(stdout);
  795.     if (setjmp(tohere)) {
  796.         zperr("TIMEOUT");
  797.         return TIMEOUT;
  798.     }
  799.     c = timeout/10;
  800.     if (c<2)
  801.         c=2;
  802.     if (Verbose>5) {
  803.         fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);
  804.         byt[1] = 0;
  805.     }
  806.     signal(SIGALRM, alrm); alarm(c);
  807. #ifdef ONEREAD
  808.     c=read(iofd, byt, 1);        /* regulus raw read is unique */
  809. #else
  810.     c=read(iofd, byt, count);
  811. #endif
  812.     alarm(0);
  813.     if (Verbose>5)
  814.         fprintf(stderr, "ret cnt=%d %x %x\n", c, byt[0], byt[1]);
  815.     if (c<1)
  816.         return TIMEOUT;
  817.     if (c==1)
  818.         return (byt[0]&0377);
  819.     else
  820.         while (c)
  821.             if (byt[--c] != CAN)
  822.                 return ERROR;
  823.     return CAN;
  824. }
  825. readline(n)
  826. {
  827.     return (readock(n, 1));
  828. }
  829.  
  830.  
  831. purgeline()
  832. {
  833. #ifdef USG
  834.     ioctl(iofd, TCFLSH, 0);
  835. #else
  836.     lseek(iofd, 0L, 2);
  837. #endif
  838. }
  839.  
  840.  
  841. /* send cancel string to get the other end to shut up */
  842. canit()
  843. {
  844.     static char canistr[] = {
  845.      24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
  846.     };
  847.  
  848.     printf(canistr);
  849.     fflush(stdout);
  850. }
  851.  
  852. /*
  853.  * Log an error
  854.  */
  855. /*VARARGS1*/
  856. zperr(s,p,u)
  857. char *s, *p, *u;
  858. {
  859.     if (Verbose <= 0)
  860.         return;
  861.     fprintf(stderr, "Error %d: ", errors);
  862.     fprintf(stderr, s, p, u);
  863.     fprintf(stderr, "\n");
  864. }
  865.  
  866. /*
  867.  * return 1 iff stdout and stderr are different devices
  868.  *  indicating this program operating with a modem on a
  869.  *  different line
  870.  */
  871. from_cu()
  872. {
  873.     struct stat a, b;
  874.     fstat(1, &a); fstat(2, &b);
  875.     return (a.st_rdev != b.st_rdev);
  876. }
  877.  
  878. /*
  879.  * substr(string, token) searches for token in string s
  880.  * returns pointer to token within string if found, NULL otherwise
  881.  */
  882. char *
  883. substr(s, t)
  884. register char *s,*t;
  885. {
  886.     register char *ss,*tt;
  887.     /* search for first char of token */
  888.     for (ss=s; *s; s++)
  889.         if (*s == *t)
  890.             /* compare token with substring */
  891.             for (ss=s,tt=t; ;) {
  892.                 if (*tt == 0)
  893.                     return s;
  894.                 if (*ss++ != *tt++)
  895.                     break;
  896.             }
  897.     return NULL;
  898. }
  899.  
  900. char *babble[] = {
  901.     "Send file(s) with ZMODEM/YMODEM/XMODEM Protocol",
  902.     "    (Y) = Option applies to YMODEM only",
  903.     "    (Z) = Option applies to ZMODEM only",
  904.     "Usage:    sz [-12+abdefkLlNnquvwYy] [-] file ...",
  905.     "    sz [-12Ceqv] -c COMMAND",
  906.     "    sb [-12adfkquv] [-] file ...",
  907.     "    sx [-12akquv] [-] file",
  908.     "    1 Use stdout for modem input",
  909. #ifdef CSTOPB
  910.     "    2 Use 2 stop bits",
  911. #endif
  912.     "    + Append to existing destination file (Z)",
  913.     "    a (ASCII) change NL to CR/LF",
  914.     "    b Binary file transfer override",
  915.     "    c send COMMAND (Z)",
  916.     "    d Change '.' to '/' in pathnames (Y/Z)",
  917.     "    e Escape all control characters (Z)",
  918.     "    f send Full pathname (Y/Z)",
  919.     "    i send COMMAND, ack Immediately (Z)",
  920.     "    k Send 1024 byte packets (Y)",
  921.     "    L N Limit subpacket length to N bytes (Z)",
  922.     "    l N Limit frame length to N bytes (l>=L) (Z)",
  923.     "    n send file if source newer (Z)",
  924.     "    N send file if source newer or longer (Z)",
  925.     "    o Use 16 bit CRC instead of 32 bit CRC (Z)",
  926.     "    p Protect existing destination file (Z)",
  927.     "    r Resume/Recover interrupted file transfer (Z)",
  928.     "    q Quiet (no progress reports)",
  929.     "    u Unlink file after transmission",
  930.     "    v Verbose - provide debugging information",
  931.     "    w N Window is N bytes (Z)",
  932.     "    Y Yes, overwrite existing file, skip if not present at rx (Z)",
  933.     "    y Yes, overwrite existing file (Z)",
  934.     "- as pathname sends standard input as sPID.sz or environment ONAME",
  935.     ""
  936. };
  937.  
  938. usage()
  939. {
  940.     char **pp;
  941.  
  942.     for (pp=babble; **pp; ++pp)
  943.         fprintf(stderr, "%s\n", *pp);
  944.     fprintf(stderr, "%s for %s by Chuck Forsberg  ", VERSION, OS);
  945.     exit(1);
  946. }
  947.  
  948. /*
  949.  * Get the receiver's init parameters
  950.  */
  951. getzrxinit()
  952. {
  953.     register n;
  954.     struct stat f;
  955.  
  956.     for (n=10; --n>=0; ) {
  957.         
  958.         switch (zgethdr(Rxhdr, 1)) {
  959.         case ZCHALLENGE:    /* Echo receiver's challenge numbr */
  960.             stohdr(Rxpos);
  961.             zshhdr(ZACK, Txhdr);
  962.             continue;
  963.         case ZCOMMAND:        /* They didn't see out ZRQINIT */
  964.             stohdr(0L);
  965.             zshhdr(ZRQINIT, Txhdr);
  966.             continue;
  967.         case ZRINIT:
  968.             Rxflags = 0377 & Rxhdr[ZF0];
  969.             Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
  970.             Zctlesc |= Rxflags & TESCCTL;
  971.             Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
  972.             vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
  973.             if ( !Fromcu)
  974.                 signal(SIGINT, SIG_IGN);
  975. #ifdef USG
  976.             mode(2);    /* Set cbreak, XON/XOFF, etc. */
  977. #endif
  978. #ifndef READCHECK
  979. #ifndef USG
  980.             /* Use 1024 byte frames if no sample/interrupt */
  981.             if (Rxbuflen < 32 || Rxbuflen > 1024) {
  982.                 Rxbuflen = 1024;
  983.                 vfile("Rxbuflen=%d", Rxbuflen);
  984.             }
  985. #endif
  986. #endif
  987.             /* Override to force shorter frame length */
  988.             if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32))
  989.                 Rxbuflen = Tframlen;
  990.             if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024))
  991.                 Rxbuflen = Tframlen;
  992.             vfile("Rxbuflen=%d", Rxbuflen);
  993.  
  994.             /* If using a pipe for testing set lower buf len */
  995.             fstat(iofd, &f);
  996.             if ((f.st_mode & S_IFMT) != S_IFCHR
  997.               && (Rxbuflen == 0 || Rxbuflen > 4096))
  998.                 Rxbuflen = 4096;
  999.             /*
  1000.              * If input is not a regular file, force ACK's each 1024
  1001.              *  (A smarter strategey could be used here ...)
  1002.              */
  1003.             fstat(fileno(in), &f);
  1004.             if (((f.st_mode & S_IFMT) != S_IFREG)
  1005.               && (Rxbuflen == 0 || Rxbuflen > 1024))
  1006.                 Rxbuflen = 1024;
  1007.             vfile("Rxbuflen=%d", Rxbuflen);
  1008.  
  1009.             return (sendzsinit());
  1010.         case ZCAN:
  1011.         case TIMEOUT:
  1012.             return ERROR;
  1013.         case ZRQINIT:
  1014.             if (Rxhdr[ZF0] == ZCOMMAND)
  1015.                 continue;
  1016.         default:
  1017.             zshhdr(ZNAK, Txhdr);
  1018.             continue;
  1019.         }
  1020.     }
  1021.     return ERROR;
  1022. }
  1023.  
  1024. /* Send send-init information */
  1025. sendzsinit()
  1026. {
  1027.     register c;
  1028.  
  1029.     if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL)))
  1030.         return OK;
  1031.     errors = 0;
  1032.     for (;;) {
  1033.         stohdr(0L);
  1034.         if (Zctlesc) {
  1035.             Txhdr[ZF0] |= TESCCTL; zshhdr(ZSINIT, Txhdr);
  1036.         }
  1037.         else
  1038.             zsbhdr(ZSINIT, Txhdr);
  1039.         zsdata(Myattn, 1+strlen(Myattn), ZCRCW);
  1040.         c = zgethdr(Rxhdr, 1);
  1041.         switch (c) {
  1042.         case ZCAN:
  1043.             return ERROR;
  1044.         case ZACK:
  1045.             return OK;
  1046.         default:
  1047.             if (++errors > 19)
  1048.                 return ERROR;
  1049.             continue;
  1050.         }
  1051.     }
  1052. }
  1053.  
  1054. /* Send file name and related info */
  1055. zsendfile(buf, blen)
  1056. char *buf;
  1057. {
  1058.     register c;
  1059.  
  1060.     for (;;) {
  1061.         Txhdr[ZF0] = Lzconv;    /* file conversion request */
  1062.         Txhdr[ZF1] = Lzmanag;    /* file management request */
  1063.         if (Lskipnocor)
  1064.             Txhdr[ZF1] |= ZMSKNOLOC;
  1065.         Txhdr[ZF2] = Lztrans;    /* file transport request */
  1066.         Txhdr[ZF3] = 0;
  1067.         zsbhdr(ZFILE, Txhdr);
  1068.         zsdata(buf, blen, ZCRCW);
  1069. again:
  1070.         c = zgethdr(Rxhdr, 1);
  1071.         switch (c) {
  1072.         case ZRINIT:
  1073.             while ((c = readline(50)) > 0)
  1074.                 if (c == ZPAD) {
  1075.                     goto again;
  1076.                 }
  1077.             /* **** FALL THRU TO **** */
  1078.         default:
  1079.             continue;
  1080.         case ZCAN:
  1081.         case TIMEOUT:
  1082.         case ZABORT:
  1083.         case ZFIN:
  1084.             return ERROR;
  1085.         case ZSKIP:
  1086.             fclose(in); return c;
  1087.         case ZRPOS:
  1088.             /*
  1089.              * Suppress zcrcw request otherwise triggered by
  1090.              * lastyunc==bytcnt
  1091.              */
  1092.             Lastsync = (bytcnt = Txpos = Rxpos) -1;
  1093.             fseek(in, Rxpos, 0);
  1094.             Dontread = FALSE;
  1095.             return zsendfdata();
  1096.         }
  1097.     }
  1098. }
  1099.  
  1100. /* Send the data in the file */
  1101. zsendfdata()
  1102. {
  1103.     register c, e, n;
  1104.     register newcnt;
  1105.     register long tcount = 0;
  1106.     int junkcount;        /* Counts garbage chars received by TX */
  1107.     static int tleft = 6;    /* Counter for test mode */
  1108.  
  1109.     if (Baudrate > 300)
  1110.         blklen = 256;
  1111.     if (Baudrate > 1200)
  1112.         blklen = 512;
  1113.     if (Baudrate > 2400)
  1114.         blklen = KSIZE;
  1115.     if (Rxbuflen && blklen>Rxbuflen)
  1116.         blklen = Rxbuflen;
  1117.     if (blkopt && blklen > blkopt)
  1118.         blklen = blkopt;
  1119.     vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
  1120.     vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac);
  1121.     Lrxpos = 0;
  1122.     junkcount = 0;
  1123.     Beenhereb4 = FALSE;
  1124. somemore:
  1125.     if (setjmp(intrjmp)) {
  1126. waitack:
  1127.         junkcount = 0;
  1128.         c = getinsync(0);
  1129. gotack:
  1130.         switch (c) {
  1131.         default:
  1132.         case ZCAN:
  1133.             fclose(in);
  1134.             return ERROR;
  1135.         case ZSKIP:
  1136.             fclose(in);
  1137.             return c;
  1138.         case ZACK:
  1139.         case ZRPOS:
  1140.             break;
  1141.         case ZRINIT:
  1142.             return OK;
  1143.         }
  1144. #ifdef READCHECK
  1145.         /*
  1146.          * If the reverse channel can be tested for data,
  1147.          *  this logic may be used to detect error packets
  1148.          *  sent by the receiver, in place of setjmp/longjmp
  1149.          *  rdchk(fdes) returns non 0 if a character is available
  1150.          */
  1151.         while (rdchk(iofd)) {
  1152. #ifdef SVR2
  1153.             switch (checked)
  1154. #else
  1155.             switch (readline(1))
  1156. #endif
  1157.             {
  1158.             case CAN:
  1159.             case ZPAD:
  1160.                 c = getinsync(1);
  1161.                 goto gotack;
  1162.             case XOFF:        /* Wait a while for an XON */
  1163.             case XOFF|0200:
  1164.                 readline(100);
  1165.             }
  1166.         }
  1167. #endif
  1168.     }
  1169.  
  1170.     if ( !Fromcu)
  1171.         signal(SIGINT, onintr);
  1172.     newcnt = Rxbuflen;
  1173.     Txwcnt = 0;
  1174.     stohdr(Txpos);
  1175.     zsbhdr(ZDATA, Txhdr);
  1176.  
  1177.     /*
  1178.      * Special testing mode.  This should force receiver to Attn,ZRPOS
  1179.      *  many times.  Each time the signal should be caught, causing the
  1180.      *  file to be started over from the beginning.
  1181.      */
  1182.     if (Testattn) {
  1183.         if ( --tleft)
  1184.             while (tcount < 20000) {
  1185.                 printf(qbf); fflush(stdout);
  1186.                 tcount += strlen(qbf);
  1187. #ifdef READCHECK
  1188.                 while (rdchk(iofd)) {
  1189. #ifdef SVR2
  1190.                     switch (checked)
  1191. #else
  1192.                     switch (readline(1))
  1193. #endif
  1194.                     {
  1195.                     case CAN:
  1196.                     case ZPAD:
  1197. #ifdef TCFLSH
  1198.                         ioctl(iofd, TCFLSH, 1);
  1199. #endif
  1200.                         goto waitack;
  1201.                     case XOFF:    /* Wait for XON */
  1202.                     case XOFF|0200:
  1203.                         readline(100);
  1204.                     }
  1205.                 }
  1206. #endif
  1207.             }
  1208.         signal(SIGINT, SIG_IGN); canit();
  1209.         sleep(3); purgeline(); mode(0);
  1210.         printf("\nsz: Tcount = %ld\n", tcount);
  1211.         if (tleft) {
  1212.             printf("ERROR: Interrupts Not Caught\n");
  1213.             exit(1);
  1214.         }
  1215.         exit(0);
  1216.     }
  1217.  
  1218.     do {
  1219.         if (Dontread) {
  1220.             n = Lastn;
  1221.         } else {
  1222.             n = zfilbuf(txbuf, blklen);
  1223.             Lastread = Txpos;  Lastn = n;
  1224.         }
  1225.         Dontread = FALSE;
  1226.         if (n < blklen)
  1227.             e = ZCRCE;
  1228.         else if (junkcount > 3)
  1229.             e = ZCRCW;
  1230.         else if (bytcnt == Lastsync)
  1231.             e = ZCRCW;
  1232.         else if (Rxbuflen && (newcnt -= n) <= 0)
  1233.             e = ZCRCW;
  1234.         else if (Txwindow && (Txwcnt += n) >= Txwspac) {
  1235.             Txwcnt = 0;  e = ZCRCQ;
  1236.         }
  1237.         else
  1238.             e = ZCRCG;
  1239.         if (Verbose>1)
  1240.             fprintf(stderr, "\r%7ld ZMODEM%s    ",
  1241.               Txpos, Crc32t?" CRC-32":"");
  1242.         zsdata(txbuf, n, e);
  1243.         bytcnt = Txpos += n;
  1244.         if (e == ZCRCW)
  1245.             goto waitack;
  1246. #ifdef READCHECK
  1247.         /*
  1248.          * If the reverse channel can be tested for data,
  1249.          *  this logic may be used to detect error packets
  1250.          *  sent by the receiver, in place of setjmp/longjmp
  1251.          *  rdchk(fdes) returns non 0 if a character is available
  1252.          */
  1253.         fflush(stdout);
  1254.         while (rdchk(iofd)) {
  1255. #ifdef SVR2
  1256.             switch (checked)
  1257. #else
  1258.             switch (readline(1))
  1259. #endif
  1260.             {
  1261.             case CAN:
  1262.             case ZPAD:
  1263.                 c = getinsync(1);
  1264.                 if (c == ZACK)
  1265.                     break;
  1266. #ifdef TCFLSH
  1267.                 ioctl(iofd, TCFLSH, 1);
  1268. #endif
  1269.                 /* zcrce - dinna wanna starta ping-pong game */
  1270.                 zsdata(txbuf, 0, ZCRCE);
  1271.                 goto gotack;
  1272.             case XOFF:        /* Wait a while for an XON */
  1273.             case XOFF|0200:
  1274.                 readline(100);
  1275.             default:
  1276.                 ++junkcount;
  1277.             }
  1278.         }
  1279. #endif    /* READCHECK */
  1280.         if (Txwindow) {
  1281.             while ((tcount = Txpos - Lrxpos) >= Txwindow) {
  1282.                 vfile("%ld window >= %u", tcount, Txwindow);
  1283.                 if (e != ZCRCQ)
  1284.                     zsdata(txbuf, 0, e = ZCRCQ);
  1285.                 c = getinsync(1);
  1286.                 if (c != ZACK) {
  1287. #ifdef TCFLSH
  1288.                     ioctl(iofd, TCFLSH, 1);
  1289. #endif
  1290.                     zsdata(txbuf, 0, ZCRCE);
  1291.                     goto gotack;
  1292.                 }
  1293.             }
  1294.             vfile("window = %ld", tcount);
  1295.         }
  1296.     } while (n == blklen);
  1297.     if ( !Fromcu)
  1298.         signal(SIGINT, SIG_IGN);
  1299.  
  1300.     for (;;) {
  1301.         stohdr(Txpos);
  1302.         zsbhdr(ZEOF, Txhdr);
  1303.         switch (getinsync(0)) {
  1304.         case ZACK:
  1305.             continue;
  1306.         case ZRPOS:
  1307.             goto somemore;
  1308.         case ZRINIT:
  1309.             return OK;
  1310.         case ZSKIP:
  1311.             fclose(in);
  1312.             return c;
  1313.         default:
  1314.             fclose(in);
  1315.             return ERROR;
  1316.         }
  1317.     }
  1318. }
  1319.  
  1320. /*
  1321.  * Respond to receiver's complaint, get back in sync with receiver
  1322.  */
  1323. getinsync(flag)
  1324. {
  1325.     register c;
  1326.  
  1327.     for (;;) {
  1328.         if (Testattn) {
  1329.             printf("\r\n\n\n***** Signal Caught *****\r\n");
  1330.             Rxpos = 0; c = ZRPOS;
  1331.         } else
  1332.             c = zgethdr(Rxhdr, 0);
  1333.         switch (c) {
  1334.         case ZCAN:
  1335.         case ZABORT:
  1336.         case ZFIN:
  1337.         case TIMEOUT:
  1338.             return ERROR;
  1339.         case ZRPOS:
  1340.             if (Lastn >= 0 && Lastread == Rxpos) {
  1341.                 Dontread = TRUE;
  1342.             } else {
  1343.                 clearerr(in);    /* In case file EOF seen */
  1344.                 fseek(in, Rxpos, 0);
  1345.             }
  1346.             bytcnt = Lrxpos = Txpos = Rxpos;
  1347.             if (Lastsync == Rxpos) {
  1348.                 if (++Beenhereb4 > 4)
  1349.                     if (blklen > 256)
  1350.                         blklen /= 2;
  1351.             }
  1352.             Lastsync = Rxpos;
  1353.             return c;
  1354.         case ZACK:
  1355.             Lrxpos = Rxpos;
  1356.             if (flag || Txpos == Rxpos)
  1357.                 return ZACK;
  1358.             continue;
  1359.         case ZRINIT:
  1360.         case ZSKIP:
  1361.             fclose(in);
  1362.             return c;
  1363.         case ERROR:
  1364.         default:
  1365.             zsbhdr(ZNAK, Txhdr);
  1366.             continue;
  1367.         }
  1368.     }
  1369. }
  1370.  
  1371.  
  1372. /* Say "bibi" to the receiver, try to do it cleanly */
  1373. saybibi()
  1374. {
  1375.     for (;;) {
  1376.         stohdr(0L);
  1377.         zsbhdr(ZFIN, Txhdr);
  1378.         switch (zgethdr(Rxhdr, 0)) {
  1379.         case ZFIN:
  1380.             sendline('O'); sendline('O'); flushmo();
  1381.         case ZCAN:
  1382.         case TIMEOUT:
  1383.             return;
  1384.         }
  1385.     }
  1386. }
  1387.  
  1388. /* Local screen character display function */
  1389. bttyout(c)
  1390. {
  1391.     if (Verbose)
  1392.         putc(c, stderr);
  1393. }
  1394.  
  1395. /* Send command and related info */
  1396. zsendcmd(buf, blen)
  1397. char *buf;
  1398. {
  1399.     register c;
  1400.     long cmdnum;
  1401.  
  1402.     cmdnum = getpid();
  1403.     errors = 0;
  1404.     for (;;) {
  1405.         stohdr(cmdnum);
  1406.         Txhdr[ZF0] = Cmdack1;
  1407.         zsbhdr(ZCOMMAND, Txhdr);
  1408.         zsdata(buf, blen, ZCRCW);
  1409. listen:
  1410.         Rxtimeout = 100;        /* Ten second wait for resp. */
  1411.         c = zgethdr(Rxhdr, 1);
  1412.  
  1413.         switch (c) {
  1414.         case ZRINIT:
  1415.             goto listen;    /* CAF 8-21-87 */
  1416.         case ERROR:
  1417.         case TIMEOUT:
  1418.             if (++errors > Cmdtries)
  1419.                 return ERROR;
  1420.             continue;
  1421.         case ZCAN:
  1422.         case ZABORT:
  1423.         case ZFIN:
  1424.         case ZSKIP:
  1425.         case ZRPOS:
  1426.             return ERROR;
  1427.         default:
  1428.             if (++errors > 20)
  1429.                 return ERROR;
  1430.             continue;
  1431.         case ZCOMPL:
  1432.             Exitcode = Rxpos;
  1433.             saybibi();
  1434.             return OK;
  1435.         case ZRQINIT:
  1436.             vfile("******** RZ *******");
  1437.             system("rz");
  1438.             vfile("******** SZ *******");
  1439.             goto listen;
  1440.         }
  1441.     }
  1442. }
  1443.  
  1444. /*
  1445.  * If called as sb use YMODEM protocol
  1446.  */
  1447. chkinvok(s)
  1448. char *s;
  1449. {
  1450.     register char *p;
  1451.  
  1452.     p = s;
  1453.     while (*p == '-')
  1454.         s = ++p;
  1455.     while (*p)
  1456.         if (*p++ == '/')
  1457.             s = p;
  1458.     if (*s == 'v') {
  1459.         Verbose=1; ++s;
  1460.     }
  1461.     Progname = s;
  1462.     if (s[0]=='s' && s[1]=='b') {
  1463.         Nozmodem = TRUE; blklen=KSIZE;
  1464.     }
  1465.     if (s[0]=='s' && s[1]=='x') {
  1466.         Modem = TRUE;
  1467.     }
  1468. }
  1469. /* End of sz.c */
  1470.