home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_200 / 260_01 / rz.c < prev    next >
Text File  |  1988-02-25  |  34KB  |  1,413 lines

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