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

  1. #define VERSION "1.26 08-21-87"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*% cc -M0 -Ox -K -i % -o rz; size rz;
  5. <-xtx-*> cc386 -Ox rz.c -o $B/rz;  size $B/rz
  6.  *
  7.  * rz.c By Chuck Forsberg
  8.  *
  9.  *    cc -O rz.c -o rz        USG (3.0) Unix
  10.  *     cc -O -DV7  rz.c -o rz        Unix V7, BSD 2.8 - 4.3
  11.  *
  12.  *    ln rz rb;  ln rz rx            For either system
  13.  *
  14.  *    ln rz /usr/bin/rzrmail        For remote mail.  Make this the
  15.  *                    login shell. rzrmail then calls
  16.  *                    rmail(1) to deliver mail.
  17.  *
  18.  *
  19.  *  Unix is a trademark of Western Electric Company
  20.  *
  21.  * A program for Unix to receive files and commands from computers running
  22.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
  23.  *  rz uses Unix buffered input to reduce wasted CPU time.
  24.  *
  25.  * Iff the program is invoked by rzCOMMAND, output is piped to 
  26.  * "COMMAND filename"
  27.  *
  28.  *  Some systems (Venix, Coherent, Regulus) may not support tty raw mode
  29.  *  read(2) the same way as Unix. ONEREAD must be defined to force one
  30.  *  character reads for these systems. Added 7-01-84 CAF
  31.  *
  32.  *  Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF 
  33.  *
  34.  *  BIX added 6-30-87 to support BIX(TM) upload protocol used by the
  35.  *  Byte Information Exchange.
  36.  *
  37.  *  NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN]
  38.  *  doesn't work properly (even though it compiles without error!),
  39.  *
  40.  *  HOWMANY may be tuned for best performance
  41.  *
  42.  *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
  43.  */
  44. #define LOGFILE "/tmp/rzlog"
  45.  
  46. #include <stdio.h>
  47. #include <signal.h>
  48. #include <setjmp.h>
  49. #include <ctype.h>
  50. FILE *popen();
  51.  
  52. #define OK 0
  53. #define FALSE 0
  54. #define TRUE 1
  55. #define ERROR (-1)
  56.  
  57. /*
  58.  * Max value for HOWMANY is 255.
  59.  *   A larger value reduces system overhead but may evoke kernel bugs.
  60.  *   133 corresponds to an XMODEM/CRC sector
  61.  */
  62. #ifndef HOWMANY
  63. #define HOWMANY 133
  64. #endif
  65.  
  66. int Zmodem=0;        /* ZMODEM protocol requested */
  67. int Nozmodem = 0;    /* If invoked as "rb" */
  68. unsigned Baudrate;
  69. #include "rbsb.c"    /* most of the system dependent stuff here */
  70.  
  71. char *substr();
  72. FILE *fout;
  73.  
  74. /*
  75.  * Routine to calculate the free bytes on the current file system
  76.  *  ~0 means many free bytes (unknown)
  77.  */
  78. long getfree()
  79. {
  80.     return(~0L);    /* many free bytes ... */
  81. }
  82.  
  83. /* Ward Christensen / CP/M parameters - Don't change these! */
  84. #define ENQ 005
  85. #define CAN ('X'&037)
  86. #define XOFF ('s'&037)
  87. #define XON ('q'&037)
  88. #define SOH 1
  89. #define STX 2
  90. #define EOT 4
  91. #define ACK 6
  92. #define NAK 025
  93. #define CPMEOF 032
  94. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  95. #define TIMEOUT (-2)
  96. #define RCDO (-3)
  97. #define ERRORMAX 5
  98. #define RETRYMAX 5
  99. #define WCEOT (-10)
  100. #define SECSIZ 128    /* cp/m's Magic Number record size */
  101. #define PATHLEN 257    /* ready for 4.2 bsd ? */
  102. #define KSIZE 1024    /* record size with k option */
  103. #define UNIXFILE 0x8000    /* happens to the the S_IFREG file mask bit for stat */
  104.  
  105. int Lastrx;
  106. int Crcflg;
  107. int Firstsec;
  108. int Eofseen;        /* indicates cpm eof (^Z) has been received */
  109. int errors;
  110. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  111. #ifdef ONEREAD
  112. /* Sorry, Regulus and some others don't work right in raw mode! */
  113. int Readnum = 1;    /* Number of bytes to ask for in read() from modem */
  114. #else
  115. int Readnum = HOWMANY;    /* Number of bytes to ask for in read() from modem */
  116. #endif
  117.  
  118. #define DEFBYTL 2000000000L    /* default rx file size */
  119. long Bytesleft;        /* number of bytes of incoming file left */
  120. long Modtime;        /* Unix style mod time for incoming file */
  121. short Filemode;        /* Unix style mode for incoming file */
  122. char Pathname[PATHLEN];
  123. char *Progname;        /* the name by which we were called */
  124.  
  125. int Batch=0;
  126. int Wcsmask=0377;
  127. int Topipe=0;
  128. int MakeLCPathname=TRUE;    /* make received pathname lower case */
  129. int Verbose=0;
  130. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  131. int Nflag = 0;        /* Don't really transfer files */
  132. int Rxbinary=FALSE;    /* receive all files in bin mode */
  133. int Rxascii=FALSE;    /* receive files in ascii (translate) mode */
  134. int Thisbinary;        /* current file is to be received in bin mode */
  135. int Blklen;        /* record length of received packets */
  136. char secbuf[KSIZE+1];
  137. char linbuf[HOWMANY];
  138. int Lleft=0;        /* number of characters in linbuf */
  139. time_t timep[2];
  140. char Lzmanag;        /* Local file management request */
  141. char zconv;        /* ZMODEM file conversion request */
  142. char zmanag;        /* ZMODEM file management request */
  143. char ztrans;        /* ZMODEM file transport request */
  144. int Zctlesc;        /* Encode control characters */
  145. int Zrwindow = 1400;    /* RX window size (controls garbage count) */
  146.  
  147. jmp_buf tohere;        /* For the interrupt on RX timeout */
  148.  
  149. #include "zm.c"
  150.  
  151. int tryzhdrtype=ZRINIT;    /* Header type to send corresponding to Last rx close */
  152.  
  153. alrm()
  154. {
  155.     longjmp(tohere, -1);
  156. }
  157.  
  158. /* called by signal interrupt or terminate to clean things up */
  159. bibi(n)
  160. {
  161.     if (Zmodem)
  162.         zmputs(Attn);
  163.     canit(); mode(0);
  164.     fprintf(stderr, "rz: caught signal %d; exiting", n);
  165.     exit(128+n);
  166. }
  167.  
  168. main(argc, argv)
  169. char *argv[];
  170. {
  171.     register char *cp;
  172.     register npats;
  173.     char *virgin, **patts;
  174.     char *getenv();
  175.     int exitcode;
  176.  
  177.     Rxtimeout = 100;
  178.     setbuf(stderr, NULL);
  179.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  180.         Restricted=TRUE;
  181.  
  182.     chkinvok(virgin=argv[0]);    /* if called as [-]rzCOMMAND set flag */
  183.     npats = 0;
  184.     while (--argc) {
  185.         cp = *++argv;
  186.         if (*cp == '-') {
  187.             while( *++cp) {
  188.                 switch(*cp) {
  189.                 case '+':
  190.                     Lzmanag = ZMAPND; break;
  191.                 case '1':
  192.                     iofd = 1; break;
  193.                 case '7':
  194.                     Wcsmask = 0177;
  195.                 case 'a':
  196.                     Rxascii=TRUE;  break;
  197.                 case 'b':
  198.                     Rxbinary=TRUE; break;
  199.                 case 'c':
  200.                     Crcflg=TRUE; break;
  201.                 case 'D':
  202.                     Nflag = TRUE; break;
  203.                 case 'e':
  204.                     Zctlesc = 1; break;
  205.                 case 'p':
  206.                     Lzmanag = ZMPROT;  break;
  207.                 case 'q':
  208.                     Quiet=TRUE; Verbose=0; break;
  209.                 case 't':
  210.                     if (--argc < 1) {
  211.                         usage();
  212.                     }
  213.                     Rxtimeout = atoi(*++argv);
  214.                     if (Rxtimeout<10 || Rxtimeout>1000)
  215.                         usage();
  216.                     break;
  217.                 case 'w':
  218.                     if (--argc < 1) {
  219.                         usage();
  220.                     }
  221.                     Zrwindow = atoi(*++argv);
  222.                     break;
  223.                 case 'u':
  224.                     MakeLCPathname=FALSE; break;
  225.                 case 'v':
  226.                     ++Verbose; break;
  227.                 default:
  228.                     usage();
  229.                 }
  230.             }
  231.         }
  232.         else if ( !npats && argc>0) {
  233.             if (argv[0][0]) {
  234.                 npats=argc;
  235.                 patts=argv;
  236.             }
  237.         }
  238.     }
  239.     if (npats > 1)
  240.         usage();
  241.     if (Verbose) {
  242.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  243.             printf("Can't open log file %s\n",LOGFILE);
  244.             exit(0200);
  245.         }
  246.         setbuf(stderr, NULL);
  247.         fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
  248.     }
  249.     if (fromcu() && !Quiet) {
  250.         if (Verbose == 0)
  251.             Verbose = 2;
  252.     }
  253.     mode(1);
  254.     if (signal(SIGINT, bibi) == SIG_IGN) {
  255.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  256.     }
  257.     else {
  258.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  259.     }
  260.     signal(SIGTERM, bibi);
  261.     if (wcreceive(npats, patts)==ERROR) {
  262.         exitcode=0200;
  263.         canit();
  264.     }
  265.     mode(0);
  266.     if (exitcode && !Zmodem)    /* bellow again with all thy might. */
  267.         canit();
  268.     exit(exitcode);
  269. }
  270.  
  271.  
  272. usage()
  273. {
  274.     fprintf(stderr,"%s %s for %s by Chuck Forsberg\n",
  275.       Progname, VERSION, OS);
  276.     fprintf(stderr,"Usage:    rz [-1abeuv]        (ZMODEM Batch)\n");
  277.     fprintf(stderr,"or    rb [-1abuv]        (YMODEM Batch)\n");
  278.     fprintf(stderr,"or    rx [-1abcv] file    (XMODEM or XMODEM-1k)\n");
  279.     fprintf(stderr,"      -1 For cu(1): Use fd 1 for input\n");
  280.     fprintf(stderr,"      -a ASCII transfer (strip CR)\n");
  281.     fprintf(stderr,"      -b Binary transfer for all files\n");
  282.     fprintf(stderr,"      -c Use 16 bit CRC    (XMODEM)\n");
  283.     fprintf(stderr,"      -e Ignore control characters    (ZMODEM)\n");
  284.     fprintf(stderr,"      -v Verbose more v's give more info\n");
  285.     exit(1);
  286. }
  287. /*
  288.  *  Debugging information output interface routine
  289.  */
  290. /* VARARGS1 */
  291. vfile(f, a, b, c)
  292. register char *f;
  293. {
  294.     if (Verbose > 2) {
  295.         fprintf(stderr, f, a, b, c);
  296.         fprintf(stderr, "\n");
  297.     }
  298. }
  299.  
  300. /*
  301.  * Let's receive something already.
  302.  */
  303.  
  304. char *rbmsg =
  305. "%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n";
  306.  
  307. wcreceive(argc, argp)
  308. char **argp;
  309. {
  310.     register c;
  311.  
  312.     if (Batch || argc==0) {
  313.         Crcflg=(Wcsmask==0377);
  314.         if ( !Quiet)
  315.             fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz");
  316.         if (c=tryz()) {
  317.             if (c == ZCOMPL)
  318.                 return OK;
  319.             if (c == ERROR)
  320.                 goto fubar;
  321.             c = rzfiles();
  322.             if (c)
  323.                 goto fubar;
  324.         } else {
  325.             for (;;) {
  326.                 if (wcrxpn(secbuf)== ERROR)
  327.                     goto fubar;
  328.                 if (secbuf[0]==0)
  329.                     return OK;
  330.                 if (procheader(secbuf) == ERROR)
  331.                     goto fubar;
  332.                 if (wcrx()==ERROR)
  333.                     goto fubar;
  334.             }
  335.         }
  336.     } else {
  337.         Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  338.  
  339.         procheader(""); strcpy(Pathname, *argp); checkpath(Pathname);
  340.         fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
  341.         if ((fout=fopen(Pathname, "w")) == NULL)
  342.             return ERROR;
  343.         if (wcrx()==ERROR)
  344.             goto fubar;
  345.     }
  346.     return OK;
  347. fubar:
  348.     canit();
  349.     if (Topipe && fout) {
  350.         pclose(fout);  return ERROR;
  351.     }
  352.     if (fout)
  353.         fclose(fout);
  354.     if (Restricted) {
  355.         unlink(Pathname);
  356.         fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
  357.     }
  358.     return ERROR;
  359. }
  360.  
  361.  
  362. /*
  363.  * Fetch a pathname from the other end as a C ctyle ASCIZ string.
  364.  * Length is indeterminate as long as less than Blklen
  365.  * A null string represents no more files (YMODEM)
  366.  */
  367. wcrxpn(rpn)
  368. char *rpn;    /* receive a pathname */
  369. {
  370.     register c;
  371.  
  372. #ifdef NFGVMIN
  373.     readline(1);
  374. #else
  375.     purgeline();
  376. #endif
  377.  
  378. et_tu:
  379.     Firstsec=TRUE;  Eofseen=FALSE;
  380.     sendline(Crcflg?WANTCRC:NAK);
  381.     Lleft=0;    /* Do read next time ... */
  382.     while ((c = wcgetsec(rpn, 100)) != 0) {
  383.         if (c == WCEOT) {
  384.             zperr( "Pathname fetch returned %d", c);
  385.             sendline(ACK);
  386.             Lleft=0;    /* Do read next time ... */
  387.             readline(1);
  388.             goto et_tu;
  389.         }
  390.         return ERROR;
  391.     }
  392.     sendline(ACK);
  393.     return OK;
  394. }
  395.  
  396. /*
  397.  * Adapted from CMODEM13.C, written by
  398.  * Jack M. Wierda and Roderick W. Hart
  399.  */
  400.  
  401. wcrx()
  402. {
  403.     register int sectnum, sectcurr;
  404.     register char sendchar;
  405.     register char *p;
  406.     int cblklen;            /* bytes to dump this block */
  407.  
  408.     Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
  409.     sendchar=Crcflg?WANTCRC:NAK;
  410.  
  411.     for (;;) {
  412.         sendline(sendchar);    /* send it now, we're ready! */
  413.         Lleft=0;    /* Do read next time ... */
  414.         sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
  415.         report(sectcurr);
  416.         if (sectcurr==(sectnum+1 &Wcsmask)) {
  417.             sectnum++;
  418.             cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
  419.             if (putsec(secbuf, cblklen)==ERROR)
  420.                 return ERROR;
  421.             if ((Bytesleft-=cblklen) < 0)
  422.                 Bytesleft = 0;
  423.             sendchar=ACK;
  424.         }
  425.         else if (sectcurr==(sectnum&Wcsmask)) {
  426.             zperr( "Received dup Sector");
  427.             sendchar=ACK;
  428.         }
  429.         else if (sectcurr==WCEOT) {
  430.             if (closeit())
  431.                 return ERROR;
  432.             sendline(ACK);
  433.             Lleft=0;    /* Do read next time ... */
  434.             return OK;
  435.         }
  436.         else if (sectcurr==ERROR)
  437.             return ERROR;
  438.         else {
  439.             zperr( "Sync Error");
  440.             return ERROR;
  441.         }
  442.     }
  443. }
  444.  
  445. /*
  446.  * Wcgetsec fetches a Ward Christensen type sector.
  447.  * Returns sector number encountered or ERROR if valid sector not received,
  448.  * or CAN CAN received
  449.  * or WCEOT if eot sector
  450.  * time is timeout for first char, set to 4 seconds thereafter
  451.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  452.  *    (Caller must do that when he is good and ready to get next sector)
  453.  */
  454.  
  455. wcgetsec(rxbuf, maxtime)
  456. char *rxbuf;
  457. int maxtime;
  458. {
  459.     register checksum, wcj, firstch;
  460.     register unsigned short oldcrc;
  461.     register char *p;
  462.     int sectcurr;
  463.  
  464.     for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
  465.  
  466.         if ((firstch=readline(maxtime))==STX) {
  467.             Blklen=KSIZE; goto get2;
  468.         }
  469.         if (firstch==SOH) {
  470.             Blklen=SECSIZ;
  471. get2:
  472.             sectcurr=readline(1);
  473.             if ((sectcurr+(oldcrc=readline(1)))==Wcsmask) {
  474.                 oldcrc=checksum=0;
  475.                 for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
  476.                     if ((firstch=readline(1)) < 0)
  477.                         goto bilge;
  478.                     oldcrc=updcrc(firstch, oldcrc);
  479.                     checksum += (*p++ = firstch);
  480.                 }
  481.                 if ((firstch=readline(1)) < 0)
  482.                     goto bilge;
  483.                 if (Crcflg) {
  484.                     oldcrc=updcrc(firstch, oldcrc);
  485.                     if ((firstch=readline(1)) < 0)
  486.                         goto bilge;
  487.                     oldcrc=updcrc(firstch, oldcrc);
  488.                     if (oldcrc & 0xFFFF)
  489.                         zperr( "CRC Error");
  490.                     else {
  491.                         Firstsec=FALSE;
  492.                         return sectcurr;
  493.                     }
  494.                 }
  495.                 else if (((checksum-firstch)&Wcsmask)==0) {
  496.                     Firstsec=FALSE;
  497.                     return sectcurr;
  498.                 }
  499.                 else
  500.                     zperr( "Checksum Error");
  501.             }
  502.             else
  503.                 zperr("Sector number garbled");
  504.         }
  505.         /* make sure eot really is eot and not just mixmash */
  506. #ifdef NFGVMIN
  507.         else if (firstch==EOT && readline(1)==TIMEOUT)
  508.             return WCEOT;
  509. #else
  510.         else if (firstch==EOT && Lleft==0)
  511.             return WCEOT;
  512. #endif
  513.         else if (firstch==CAN) {
  514.             if (Lastrx==CAN) {
  515.                 zperr( "Sender CANcelled");
  516.                 return ERROR;
  517.             } else {
  518.                 Lastrx=CAN;
  519.                 continue;
  520.             }
  521.         }
  522.         else if (firstch==TIMEOUT) {
  523.             if (Firstsec)
  524.                 goto humbug;
  525. bilge:
  526.             zperr( "TIMEOUT");
  527.         }
  528.         else
  529.             zperr( "Got 0%o sector header", firstch);
  530.  
  531. humbug:
  532.         Lastrx=0;
  533.         while(readline(1)!=TIMEOUT)
  534.             ;
  535.         if (Firstsec) {
  536.             sendline(Crcflg?WANTCRC:NAK);
  537.             Lleft=0;    /* Do read next time ... */
  538.         } else {
  539.             maxtime=40; sendline(NAK);
  540.             Lleft=0;    /* Do read next time ... */
  541.         }
  542.     }
  543.     /* try to stop the bubble machine. */
  544.     canit();
  545.     return ERROR;
  546. }
  547.  
  548. /*
  549.  * This version of readline is reasoably well suited for
  550.  * reading many characters.
  551.  *  (except, currently, for the Regulus version!)
  552.  *
  553.  * timeout is in tenths of seconds
  554.  */
  555. readline(timeout)
  556. int timeout;
  557. {
  558.     register n;
  559.     static char *cdq;    /* pointer for removing chars from linbuf */
  560.  
  561.     if (--Lleft >= 0) {
  562.         if (Verbose > 8) {
  563.             fprintf(stderr, "%02x ", *cdq&0377);
  564.         }
  565.         return (*cdq++ & Wcsmask);
  566.     }
  567.     n = timeout/10;
  568.     if (n < 2)
  569.         n = 3;
  570.     if (Verbose > 5)
  571.         fprintf(stderr, "Calling read: alarm=%d  Readnum=%d ",
  572.           n, Readnum);
  573.     if (setjmp(tohere)) {
  574. #ifdef TIOCFLUSH
  575. /*        ioctl(iofd, TIOCFLUSH, 0); */
  576. #endif
  577.         Lleft = 0;
  578.         if (Verbose>1)
  579.             fprintf(stderr, "Readline:TIMEOUT\n");
  580.         return TIMEOUT;
  581.     }
  582.     signal(SIGALRM, alrm); alarm(n);
  583.     Lleft=read(iofd, cdq=linbuf, Readnum);
  584.     alarm(0);
  585.     if (Verbose > 5) {
  586.         fprintf(stderr, "Read returned %d bytes\n", Lleft);
  587.     }
  588.     if (Lleft < 1)
  589.         return TIMEOUT;
  590.     --Lleft;
  591.     if (Verbose > 8) {
  592.         fprintf(stderr, "%02x ", *cdq&0377);
  593.     }
  594.     return (*cdq++ & Wcsmask);
  595. }
  596.  
  597.  
  598.  
  599. /*
  600.  * Purge the modem input queue of all characters
  601.  */
  602. purgeline()
  603. {
  604.     Lleft = 0;
  605. #ifdef USG
  606.     ioctl(iofd, TCFLSH, 0);
  607. #else
  608.     lseek(iofd, 0L, 2);
  609. #endif
  610. }
  611.  
  612.  
  613. /*
  614.  * Process incoming file information header
  615.  */
  616. procheader(name)
  617. char *name;
  618. {
  619.     register char *openmode, *p, **pp;
  620.  
  621.     /* set default parameters and overrides */
  622.     openmode = "w";
  623.     Thisbinary = (!Rxascii) || Rxbinary;
  624.     if (Lzmanag)
  625.         zmanag = Lzmanag;
  626.  
  627.     /*
  628.      *  Process ZMODEM remote file management requests
  629.      */
  630.     if (!Rxbinary && zconv == ZCNL)    /* Remote ASCII override */
  631.         Thisbinary = 0;
  632.     if (zconv == ZCBIN)    /* Remote Binary override */
  633.         Thisbinary = TRUE;
  634.     else if (zmanag == ZMAPND)
  635.         openmode = "a";
  636.  
  637. #ifndef BIX
  638.     /* ZMPROT check for existing file */
  639.     if (zmanag == ZMPROT && (fout=fopen(name, "r"))) {
  640.         fclose(fout);  return ERROR;
  641.     }
  642. #endif
  643.  
  644.     Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  645.  
  646.     p = name + 1 + strlen(name);
  647.     if (*p) {    /* file coming from Unix or DOS system */
  648.         sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
  649.         if (Filemode & UNIXFILE)
  650.             ++Thisbinary;
  651.         if (Verbose) {
  652.             fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
  653.               name, Bytesleft, Modtime, Filemode);
  654.         }
  655.     }
  656.  
  657. #ifdef BIX
  658.     if ((fout=fopen("scratchpad", openmode)) == NULL)
  659.         return ERROR;
  660.     return OK;
  661. #else
  662.  
  663.     else {        /* File coming from CP/M system */
  664.         for (p=name; *p; ++p)        /* change / to _ */
  665.             if ( *p == '/')
  666.                 *p = '_';
  667.  
  668.         if ( *--p == '.')        /* zap trailing period */
  669.             *p = 0;
  670.     }
  671.  
  672.     if (!Zmodem && MakeLCPathname && !IsAnyLower(name))
  673.         uncaps(name);
  674.     if (Topipe) {
  675.         sprintf(Pathname, "%s %s", Progname+2, name);
  676.         if (Verbose)
  677.             fprintf(stderr,  "Topipe: %s %s\n",
  678.               Pathname, Thisbinary?"BIN":"ASCII");
  679.         if ((fout=popen(Pathname, "w")) == NULL)
  680.             return ERROR;
  681.     } else {
  682.         strcpy(Pathname, name);
  683.         if (Verbose) {
  684.             fprintf(stderr,  "Receiving %s %s %s\n",
  685.               name, Thisbinary?"BIN":"ASCII", openmode);
  686.         }
  687.         checkpath(name);
  688.         if (Nflag)
  689.             name = "/dev/null";
  690.         if ((fout=fopen(name, openmode)) == NULL)
  691.             return ERROR;
  692.     }
  693.     return OK;
  694. #endif /* BIX */
  695. }
  696.  
  697. /*
  698.  * Putsec writes the n characters of buf to receive file fout.
  699.  *  If not in binary mode, carriage returns, and all characters
  700.  *  starting with CPMEOF are discarded.
  701.  */
  702. putsec(buf, n)
  703. char *buf;
  704. register n;
  705. {
  706.     register char *p;
  707.  
  708.     if (Thisbinary) {
  709.         for (p=buf; --n>=0; )
  710.             putc( *p++, fout);
  711.     }
  712.     else {
  713.         if (Eofseen)
  714.             return OK;
  715.         for (p=buf; --n>=0; ++p ) {
  716.             if ( *p == '\r')
  717.                 continue;
  718.             if (*p == CPMEOF) {
  719.                 Eofseen=TRUE; return OK;
  720.             }
  721.             putc(*p ,fout);
  722.         }
  723.     }
  724.     return OK;
  725. }
  726.  
  727. /*
  728.  *  Send a character to modem.  Small is beautiful.
  729.  */
  730. sendline(c)
  731. {
  732.     char d;
  733.  
  734.     d = c;
  735.     if (Verbose>6)
  736.         fprintf(stderr, "Sendline: %x\n", c);
  737.     write(1, &d, 1);
  738. }
  739.  
  740. xsendline(c)
  741. {
  742.     sendline(c);
  743. }
  744.  
  745. flushmo() {}
  746.  
  747.  
  748.  
  749.  
  750. /* make string s lower case */
  751. uncaps(s)
  752. register char *s;
  753. {
  754.     for ( ; *s; ++s)
  755.         if (isupper(*s))
  756.             *s = tolower(*s);
  757. }
  758. /*
  759.  * IsAnyLower returns TRUE if string s has lower case letters.
  760.  */
  761. IsAnyLower(s)
  762. register char *s;
  763. {
  764.     for ( ; *s; ++s)
  765.         if (islower(*s))
  766.             return TRUE;
  767.     return FALSE;
  768. }
  769.  
  770. /*
  771.  * substr(string, token) searches for token in string s
  772.  * returns pointer to token within string if found, NULL otherwise
  773.  */
  774. char *
  775. substr(s, t)
  776. register char *s,*t;
  777. {
  778.     register char *ss,*tt;
  779.     /* search for first char of token */
  780.     for (ss=s; *s; s++)
  781.         if (*s == *t)
  782.             /* compare token with substring */
  783.             for (ss=s,tt=t; ;) {
  784.                 if (*tt == 0)
  785.                     return s;
  786.                 if (*ss++ != *tt++)
  787.                     break;
  788.             }
  789.     return NULL;
  790. }
  791.  
  792. /*
  793.  * Log an error
  794.  */
  795. /*VARARGS1*/
  796. zperr(s,p,u)
  797. char *s, *p, *u;
  798. {
  799.     if (Verbose <= 0)
  800.         return;
  801.     fprintf(stderr, "Error %d: ", errors);
  802.     fprintf(stderr, s, p, u);
  803.     fprintf(stderr, "\n");
  804. }
  805.  
  806. /* send cancel string to get the other end to shut up */
  807. canit()
  808. {
  809.     static char canistr[] = {
  810.      24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
  811.     };
  812.  
  813.     printf(canistr);
  814.     Lleft=0;    /* Do read next time ... */
  815.     fflush(stdout);
  816. }
  817.  
  818.  
  819. /*
  820.  * Return 1 iff stdout and stderr are different devices
  821.  *  indicating this program operating with a modem on a
  822.  *  different line
  823.  */
  824. fromcu()
  825. {
  826.     struct stat a, b;
  827.     fstat(1, &a); fstat(2, &b);
  828.     return (a.st_rdev != b.st_rdev);
  829. }
  830.  
  831. report(sct)
  832. int sct;
  833. {
  834.     if (Verbose>1)
  835.         fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
  836. }
  837.  
  838. /*
  839.  * If called as [-][dir/../]vrzCOMMAND set Verbose to 1
  840.  * If called as [-][dir/../]rzCOMMAND set the pipe flag
  841.  * If called as rb use YMODEM protocol
  842.  */
  843. chkinvok(s)
  844. char *s;
  845. {
  846.     register char *p;
  847.  
  848.     p = s;
  849.     while (*p == '-')
  850.         s = ++p;
  851.     while (*p)
  852.         if (*p++ == '/')
  853.             s = p;
  854.     if (*s == 'v') {
  855.         Verbose=1; ++s;
  856.     }
  857.     Progname = s;
  858.     if (s[0]=='r' && s[1]=='b')
  859.         Nozmodem = TRUE;
  860.     if (s[2] && s[0]=='r' && s[1]=='b')
  861.         Topipe=TRUE;
  862.     if (s[2] && s[0]=='r' && s[1]=='z')
  863.         Topipe=TRUE;
  864. }
  865.  
  866. /*
  867.  * Totalitarian Communist pathname processing
  868.  */
  869. checkpath(name)
  870. char *name;
  871. {
  872.     if (Restricted) {
  873.         if (fopen(name, "r") != NULL) {
  874.             canit();
  875.             fprintf(stderr, "\r\nrz: %s exists\n", name);
  876.             bibi(-1);
  877.         }
  878.         /* restrict pathnames to current tree or uucppublic */
  879.         if ( substr(name, "../")
  880.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  881.             canit();
  882.             fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n");
  883.             bibi(-1);
  884.         }
  885.     }
  886. }
  887.  
  888. /*
  889.  * Initialize for Zmodem receive attempt, try to activate Zmodem sender
  890.  *  Handles ZSINIT frame
  891.  *  Return ZFILE if Zmodem filename received, -1 on error,
  892.  *   ZCOMPL if transaction finished,  else 0
  893.  */
  894. tryz()
  895. {
  896.     register c, n;
  897.     register cmdzack1flg;
  898.  
  899.     if (Nozmodem)        /* Check for "rb" program name */
  900.         return 0;
  901.  
  902.  
  903.     for (n=Zmodem?15:5; --n>=0; ) {
  904.         /* Set buffer length (0) and capability flags */
  905.         stohdr(0L);
  906. #ifdef CANBREAK
  907.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
  908. #else
  909.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
  910. #endif
  911.         if (Zctlesc)
  912.             Txhdr[ZF0] |= TESCCTL;
  913.         zshhdr(tryzhdrtype, Txhdr);
  914.         if (tryzhdrtype == ZSKIP)    /* Don't skip too far */
  915.             tryzhdrtype = ZRINIT;    /* CAF 8-21-87 */
  916. again:
  917.         switch (zgethdr(Rxhdr, 0)) {
  918.         case ZRQINIT:
  919.             continue;
  920.         case ZEOF:
  921.             continue;
  922.         case TIMEOUT:
  923.             continue;
  924.         case ZFILE:
  925.             zconv = Rxhdr[ZF0];
  926.             zmanag = Rxhdr[ZF1];
  927.             ztrans = Rxhdr[ZF2];
  928.             tryzhdrtype = ZRINIT;
  929.             c = zrdata(secbuf, KSIZE);
  930.             mode(3);
  931.             if (c == GOTCRCW)
  932.                 return ZFILE;
  933.             zshhdr(ZNAK, Txhdr);
  934.             goto again;
  935.         case ZSINIT:
  936.             Zctlesc = TESCCTL & Rxhdr[ZF0];
  937.             if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
  938.                 zshhdr(ZACK, Txhdr);
  939.                 goto again;
  940.             }
  941.             zshhdr(ZNAK, Txhdr);
  942.             goto again;
  943.         case ZFREECNT:
  944.             stohdr(getfree());
  945.             zshhdr(ZACK, Txhdr);
  946.             goto again;
  947.         case ZCOMMAND:
  948.             cmdzack1flg = Rxhdr[ZF0];
  949.             if (zrdata(secbuf, KSIZE) == GOTCRCW) {
  950.                 if (cmdzack1flg & ZCACK1)
  951.                     stohdr(0L);
  952.                 else
  953.                     stohdr((long)sys2(secbuf));
  954.                 purgeline();    /* dump impatient questions */
  955.                 do {
  956.                     zshhdr(ZCOMPL, Txhdr);
  957.                 }
  958.                 while (++errors<20 && zgethdr(Rxhdr,1) != ZFIN);
  959.                 ackbibi();
  960.                 if (cmdzack1flg & ZCACK1)
  961.                     exec2(secbuf);
  962.                 return ZCOMPL;
  963.             }
  964.             zshhdr(ZNAK, Txhdr); goto again;
  965.         case ZCOMPL:
  966.             goto again;
  967.         default:
  968.             continue;
  969.         case ZFIN:
  970.             ackbibi(); return ZCOMPL;
  971.         case ZCAN:
  972.             return ERROR;
  973.         }
  974.     }
  975.     return 0;
  976. }
  977.  
  978. /*
  979.  * Receive 1 or more files with ZMODEM protocol
  980.  */
  981. rzfiles()
  982. {
  983.     register c;
  984.  
  985.     for (;;) {
  986.         switch (c = rzfile()) {
  987.         case ZEOF:
  988.         case ZSKIP:
  989.             switch (tryz()) {
  990.             case ZCOMPL:
  991.                 return OK;
  992.             default:
  993.                 return ERROR;
  994.             case ZFILE:
  995.                 break;
  996.             }
  997.             continue;
  998.         default:
  999.             return c;
  1000.         case ERROR:
  1001.             return ERROR;
  1002.         }
  1003.     }
  1004. }
  1005.  
  1006. /*
  1007.  * Receive a file with ZMODEM protocol
  1008.  *  Assumes file name frame is in secbuf
  1009.  */
  1010. rzfile()
  1011. {
  1012.     register c, n;
  1013.     long rxbytes;
  1014.  
  1015.     Eofseen=FALSE;
  1016.     if (procheader(secbuf) == ERROR) {
  1017.         return (tryzhdrtype = ZSKIP);
  1018.     }
  1019.  
  1020.     n = 20; rxbytes = 0l;
  1021.  
  1022.     for (;;) {
  1023.         stohdr(rxbytes);
  1024.         zshhdr(ZRPOS, Txhdr);
  1025. nxthdr:
  1026.         switch (c = zgethdr(Rxhdr, 0)) {
  1027.         default:
  1028.             vfile("rzfile: zgethdr returned %d", c);
  1029.             return ERROR;
  1030.         case ZNAK:
  1031.         case TIMEOUT:
  1032.             if ( --n < 0) {
  1033.                 vfile("rzfile: zgethdr returned %d", c);
  1034.                 return ERROR;
  1035.             }
  1036.         case ZFILE:
  1037.             zrdata(secbuf, KSIZE);
  1038.             continue;
  1039.         case ZEOF:
  1040.             if (rclhdr(Rxhdr) != rxbytes) {
  1041.                 /*
  1042.                  * Ignore eof if it's at wrong place - force
  1043.                  *  a timeout because the eof might have gone
  1044.                  *  out before we sent our zrpos.
  1045.                  */
  1046.                 errors = 0;  goto nxthdr;
  1047.             }
  1048.             if (closeit()) {
  1049.                 tryzhdrtype = ZFERR;
  1050.                 vfile("rzfile: closeit returned <> 0");
  1051.                 return ERROR;
  1052.             }
  1053.             vfile("rzfile: normal EOF");
  1054.             return c;
  1055.         case ERROR:    /* Too much garbage in header search error */
  1056.             if ( --n < 0) {
  1057.                 vfile("rzfile: zgethdr returned %d", c);
  1058.                 return ERROR;
  1059.             }
  1060.             zmputs(Attn);
  1061.             continue;
  1062.         case ZDATA:
  1063.             if (rclhdr(Rxhdr) != rxbytes) {
  1064.                 if ( --n < 0) {
  1065.                     return ERROR;
  1066.                 }
  1067.                 zmputs(Attn);  continue;
  1068.             }
  1069. moredata:
  1070.             if (Verbose>1)
  1071.                 fprintf(stderr, "\r%7ld ZMODEM%s    ",
  1072.                   rxbytes, Crc32?" CRC-32":"");
  1073.             switch (c = zrdata(secbuf, KSIZE)) {
  1074.             case ZCAN:
  1075.                 vfile("rzfile: zgethdr returned %d", c);
  1076.                 return ERROR;
  1077.             case ERROR:    /* CRC error */
  1078.                 if ( --n < 0) {
  1079.                     vfile("rzfile: zgethdr returned %d", c);
  1080.                     return ERROR;
  1081.                 }
  1082.                 zmputs(Attn);
  1083.                 continue;
  1084.             case TIMEOUT:
  1085.                 if ( --n < 0) {
  1086.                     vfile("rzfile: zgethdr returned %d", c);
  1087.                     return ERROR;
  1088.                 }
  1089.                 continue;
  1090.             case GOTCRCW:
  1091.                 n = 20;
  1092.                 putsec(secbuf, Rxcount);
  1093.                 rxbytes += Rxcount;
  1094.                 stohdr(rxbytes);
  1095.                 zshhdr(ZACK, Txhdr);
  1096.                 sendline(XON);
  1097.                 goto nxthdr;
  1098.             case GOTCRCQ:
  1099.                 n = 20;
  1100.                 putsec(secbuf, Rxcount);
  1101.                 rxbytes += Rxcount;
  1102.                 stohdr(rxbytes);
  1103.                 zshhdr(ZACK, Txhdr);
  1104.                 goto moredata;
  1105.             case GOTCRCG:
  1106.                 n = 20;
  1107.                 putsec(secbuf, Rxcount);
  1108.                 rxbytes += Rxcount;
  1109.                 goto moredata;
  1110.             case GOTCRCE:
  1111.                 n = 20;
  1112.                 putsec(secbuf, Rxcount);
  1113.                 rxbytes += Rxcount;
  1114.                 goto nxthdr;
  1115.             }
  1116.         }
  1117.     }
  1118. }
  1119.  
  1120. /*
  1121.  * Send a string to the modem, processing for \336 (sleep 1 sec)
  1122.  *   and \335 (break signal)
  1123.  */
  1124. zmputs(s)
  1125. char *s;
  1126. {
  1127.     register c;
  1128.  
  1129.     while (*s) {
  1130.         switch (c = *s++) {
  1131.         case '\336':
  1132.             sleep(1); continue;
  1133.         case '\335':
  1134.             sendbrk(); continue;
  1135.         default:
  1136.             sendline(c);
  1137.         }
  1138.     }
  1139. }
  1140.  
  1141. /*
  1142.  * Close the receive dataset, return OK or ERROR
  1143.  */
  1144. closeit()
  1145. {
  1146.     if (Topipe) {
  1147.         if (pclose(fout)) {
  1148.             return ERROR;
  1149.         }
  1150.         return OK;
  1151.     }
  1152.     if (fclose(fout)==ERROR) {
  1153.         fprintf(stderr, "file close ERROR\n");
  1154.         return ERROR;
  1155.     }
  1156.     if (Modtime) {
  1157.         timep[0] = time(NULL);
  1158.         timep[1] = Modtime;
  1159.         utime(Pathname, timep);
  1160.     }
  1161.     if (Filemode)
  1162.         chmod(Pathname, (07777 & Filemode));
  1163.     return OK;
  1164. }
  1165.  
  1166. /*
  1167.  * Ack a ZFIN packet, let byegones be byegones
  1168.  */
  1169. ackbibi()
  1170. {
  1171.     register n;
  1172.  
  1173.     vfile("ackbibi:");
  1174.     Readnum = 1;
  1175.     stohdr(0L);
  1176.     for (n=3; --n>=0; ) {
  1177.         purgeline();
  1178.         zshhdr(ZFIN, Txhdr);
  1179.         switch (readline(100)) {
  1180.         case 'O':
  1181.             readline(1);    /* Discard 2nd 'O' */
  1182.             vfile("ackbibi complete");
  1183.             return;
  1184.         case RCDO:
  1185.             return;
  1186.         case TIMEOUT:
  1187.         default:
  1188.             break;
  1189.         }
  1190.     }
  1191. }
  1192.  
  1193.  
  1194.  
  1195. /*
  1196.  * Local console output simulation
  1197.  */
  1198. bttyout(c)
  1199. {
  1200.     if (Verbose || fromcu())
  1201.         putc(c, stderr);
  1202. }
  1203.  
  1204. /*
  1205.  * Strip leading ! if present, do shell escape. 
  1206.  */
  1207. sys2(s)
  1208. register char *s;
  1209. {
  1210.     if (*s == '!')
  1211.         ++s;
  1212.     return system(s);
  1213. }
  1214. /*
  1215.  * Strip leading ! if present, do exec.
  1216.  */
  1217. exec2(s)
  1218. register char *s;
  1219. {
  1220.     if (*s == '!')
  1221.         ++s;
  1222.     mode(0);
  1223.     execl("/bin/sh", "sh", "-c", s);
  1224. }
  1225. /* End of rz.c */
  1226.