home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / ENTERPRS / CPM / TERMS / RZMP-SRC.LZH / RZMP4.C < prev    next >
Text File  |  2000-06-30  |  15KB  |  726 lines

  1. /************************ START OF RZMP MODULE 3 ****************************/
  2.  
  3. /* sz.c By Chuck Forsberg modified for cp/m by Hal Maney */
  4. /* Further modified rjm to remove xmodem/ymodem stuff for rzmp */
  5.  
  6. #define RZMP
  7.  
  8. #include "zmp.h"
  9. #include "zmodem.h"
  10.  
  11. #ifdef   AZTEC_C
  12. #include "libc.h"
  13. #else
  14. #include <stdio.h>
  15. #endif
  16.  
  17. #include <setjmp.h>
  18. jmp_buf jb_stop;
  19.  
  20.  
  21. /*
  22.  * Attention string to be executed by receiver to interrupt streaming data
  23.  *  when an error is detected.  A pause (0336) may be needed before the
  24.  *  ^C (03) or after it. 0337 causes a break to be sent.
  25.  */
  26.  
  27. #define SLEEP 0336
  28.  
  29. char *ltoa(), *alloc(), *grabmem();
  30. char Myattn[] = { CTRLC,SLEEP,0 };
  31.  
  32. unsigned Txwindow = 0;    /* Control the size of the transmitted window */
  33. unsigned Txwspac;          /* Spacing between zcrcq requests */
  34. unsigned Txwcnt;          /* Counter used to space ack requests */
  35. int Noeofseen;
  36. int Totsecs;              /* total number of sectors this file */
  37. char *Txbuf;
  38. int Filcnt;               /* count of number of files opened */
  39. unsigned Rxbuflen = 16384;    /* Receiver's max buffer length */
  40. int Rxflags = 0;
  41. long Bytcnt;
  42. long Lastread;              /* Beginning offset of last buffer read */
  43. int Lastn;                 /* Count of last buffer read or -1 */
  44. int Dontread;              /* Don't read the buffer, it's still there */
  45. long Lastsync;              /* Last offset to which we got a ZRPOS */
  46. int Beenhereb4;           /* How many times we've been ZRPOS'd same place */
  47. int Incnt;              /* count for chars not read from the Cpmbuf */
  48.  
  49. wcsend(argc, argp)
  50. int argc;                     /* nr of files to send */
  51. char *argp[];                 /* list of file names */
  52. {
  53.     int n, status;
  54.  
  55.     slabel();
  56.     QuitFlag = FALSE;
  57.     Zctlesc = 0;
  58.     Incnt = 0;
  59.     Baudrate = Baudtable[Current.cbaudindex];
  60.     Filcnt = Errors = 0;
  61.  
  62. #ifdef AZTEC_C
  63.     Fd = 0;
  64. #else
  65.     Fd = -1;
  66. #endif
  67.  
  68.     Txbuf = alloc(KSIZE);
  69.     if (allocerror(Txbuf))
  70.         return NERROR;      
  71.     Cpmbuf = grabmem(&Cpbufsize);
  72.     if (allocerror(Cpmbuf))
  73.         return NERROR;
  74.     Cpindex = 0;                        /* just in case */
  75.     Crcflag  = FALSE;
  76.     Firstsec = TRUE;
  77.     Bytcnt = -1;
  78.     Rxtimeout = 600;   
  79.     savecurs();
  80.     hidecurs();
  81.     box();
  82.     status = NERROR;
  83.     report(PROTOCOL,"ZMODEM Send");
  84.     stohdr(0L);
  85.     zshhdr(ZRQINIT, Txhdr);
  86.     if (getzrxinit()==NERROR)
  87.         goto badreturn;
  88.     for (n=0; n<argc; ++n) {
  89.         strcpy(Pathname,argp[n]);
  90.         if (Private) {
  91.             deldrive(Pathname); /* remove any du: */
  92.             addu(Pathname,Dprivdrive,Dprivuser);
  93.         }
  94.         if (checkname(Pathname))
  95.             continue;    /* bad filetype */
  96.         clrreports();
  97.         Totsecs = 0;
  98.         if (opabort() || wcs(Pathname) == NERROR)
  99.             goto badreturn;
  100.         tfclose();
  101.         (*Downloads)++;        /* only bump count if successful */
  102.         savelog(Pathname);    /* and write to disk */
  103.     }
  104.     Totsecs = 0;
  105.     if (Filcnt==0) {    /* we couldn't open ANY files */
  106.         canit();
  107.         goto badreturn;
  108.     }
  109.     zperr("Complete",FALSE);
  110.     saybibi();
  111.     status = OK;
  112.  
  113. badreturn:
  114.     free(Cpmbuf);
  115.     free(Txbuf);
  116.     showcurs();
  117.     restcurs();
  118.     if (status == NERROR)
  119.         tfclose();
  120.     return status;
  121. }
  122.  
  123. wcs(oname)
  124. char *oname;
  125. {
  126.     unsigned length;
  127.     long flen;
  128.  
  129. #ifdef AZTEC_C
  130.     if ((Fd=fopen(oname,"rb"))==BUFIOT) {
  131. #else
  132.     if ((Fd=open(oname,0))==UBIOT) {
  133. #endif
  134.  
  135.     zperr("Can't open file",TRUE);
  136.     wait(2);
  137.         return OK;    /* pass over it, there may be others */
  138.     }
  139.     ++Noeofseen;  
  140.     Lastread = 0L;  
  141.     Lastn = -1; 
  142.     Dontread = FALSE;
  143.     ++Filcnt;
  144.     fstat(oname,&Fs);
  145.     switch (wctxpn(oname)) {    /* transmit path name */
  146.         case NERROR:
  147.             canit();    /* Send CAN */
  148.             return NERROR;
  149.  
  150.         case ZSKIP:
  151.             return OK;
  152.     }
  153.     length = Fs.records;
  154.     flen = (long)length * 128;
  155.     return 0;
  156. }
  157.  
  158. /*
  159.  * generate and transmit pathname block consisting of
  160.  *  pathname (null terminated),
  161.  *  file length, mode time (null) and file mode (null)
  162.  *  in octal.
  163.  *  N.B.: modifies the passed name, may extend it!
  164.  */
  165. wctxpn(name)
  166. char *name;
  167. {
  168.     static char *p;
  169.     char buf[20];
  170.     static unsigned length;
  171.     static long nrbytes;
  172.  
  173.     memset(Txbuf,'\0',KSIZE);
  174.     length = Fs.records;
  175.     nrbytes = (long)length * 128;
  176.     report(PATHNAME,name);
  177.     lreport(FILESIZE,nrbytes);
  178.     dreport(FBLOCKS,length);
  179.     report(SENDTIME,ttime(nrbytes));   
  180.     strcpy(Txbuf,name);
  181.     deldrive(Txbuf);        /* remove drive ind if any */
  182.     p = Txbuf + strlen(Txbuf);
  183.     ++p;
  184.     strcpy(p,ltoa(nrbytes,buf));
  185.     return zsendfile(Txbuf, 1+strlen(p)+(p-Txbuf));
  186. }
  187.  
  188. /* itoa - convert n to characters in s. */
  189. char *itoa(n, s)
  190. char s[];
  191. short n;
  192. {
  193.     static short c, k;
  194.     static char *p, *q;
  195.  
  196.     if ((k = n) < 0)    /* record sign */
  197.         n = -n;     /* make n positive */
  198.     q = p = s;
  199.     do {        /* generate digits in reverse order */
  200.         *p++ = n % 10 + '0';  /* get next digit */
  201.     } while ((n /= 10) > 0);    /* delete it */
  202.     if (k < 0) *p++ = '-';
  203.     *p = 0;
  204. /* reverse string in place */
  205.     while (q < --p) {
  206.         c = *q; *q++ = *p; *p = c; }
  207.     return (s);
  208. }
  209.  
  210. /* ltoa - convert n to characters in s. */
  211. char *ltoa(n, s)
  212. char s[];
  213. long n;
  214. {
  215.     static long c, k;
  216.     static char *p, *q;
  217.  
  218.     if ((k = n) < 0)    /* record sign */
  219.         n = -n;     /* make n positive */
  220.     q = p = s;
  221.     do {        /* generate digits in reverse order */
  222.         *p++ = n % 10 + '0';  /* get next digit */
  223.     } while ((n /= 10) > 0);    /* delete it */
  224.     if (k < 0) *p++ = '-';
  225.     *p = 0;
  226. /* reverse string in place */
  227.     while (q < --p) {
  228.         c = *q; *q++ = *p; *p = c; }
  229.     return (s);
  230. }
  231.  
  232. /* fill buf with count chars padding with ^Z for CPM */
  233.  
  234. filbuf(buf, count)
  235. char *buf;
  236. int count;
  237. {
  238.     static int c, m;
  239.  
  240.     c = m = newload(buf, count);
  241.     if (m <= 0)
  242.         return 0;
  243.     while (m < count)
  244.         buf[m++] = CTRLZ;
  245.     return c;
  246. }
  247.  
  248. newload(buf, count)
  249. int count;
  250. char *buf;
  251. {
  252.     static int j;
  253.  
  254.     j = 0;
  255.     while (count--) {
  256.         if (Incnt <= 0) {
  257.  
  258. #ifdef AZTEC_C
  259.             Incnt = fread( Cpmbuf, 1, Cpbufsize, Fd);
  260. #else
  261.             Incnt = read( Fd, Cpmbuf, Cpbufsize );
  262. #endif
  263.  
  264.             Cpindex = 0;      
  265.             if (Incnt <= 0)
  266.                 break;
  267.         }
  268.         buf[j++] = Cpmbuf[Cpindex++];
  269.         --Incnt;
  270.     }
  271.     return (j ? j : -1);
  272. }
  273.  
  274. /*
  275.  * Get the receiver's init parameters
  276.  */
  277.  
  278. getzrxinit()
  279. {
  280.     static int n;
  281.  
  282.     for (n=10; --n>=0; ) {
  283.         if (opabort())
  284.                  return NERROR;
  285.         switch (zgethdr(Rxhdr, 1)) {
  286.         case ZCHALLENGE:    /* Echo receiver's challenge numbr */
  287.             stohdr(Rxpos);
  288.             zshhdr(ZACK, Txhdr);
  289.             continue;
  290.         case ZCOMMAND:        /* They didn't see out ZRQINIT */
  291.             stohdr(0L);
  292.             zshhdr(ZRQINIT, Txhdr);
  293.             continue;
  294.         case ZRINIT:
  295.             Rxflags = 0377 & Rxhdr[ZF0];
  296.             Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
  297.             Zctlesc |= Rxflags & TESCCTL;
  298.             Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
  299.             return (sendzsinit());
  300.         case ZCAN:
  301.         case TIMEOUT:
  302.             return NERROR;
  303.         case ZRQINIT:
  304.             if (Rxhdr[ZF0] == ZCOMMAND)
  305.                 continue;
  306.         default:
  307.             zshhdr(ZNAK, Txhdr);
  308.             continue;
  309.         }
  310.     }
  311.     return NERROR;
  312. }
  313.  
  314. /* Send send-init information */
  315.  
  316. sendzsinit()
  317. {
  318.     int tries;
  319.  
  320.     stohdr(0L);        /* All flags are undefined */
  321.     strcpy(Txbuf,Myattn);    /* Copy Attn string */
  322.     for (tries = 0; tries < 20; tries++)
  323.     {
  324.         if (opabort())
  325.             return NERROR;
  326.         zsbhdr(ZSINIT,Txhdr);    /* Send binary header */
  327.         zsdata(Txbuf,strlen(Txbuf) + 1,ZCRCW);    /* Send string */
  328.         if (zgethdr(Rxhdr,0) == ZACK)
  329.             return OK;
  330.         zperr("Bad ACK: ZSINIT",FALSE);
  331.     }
  332.     return NERROR;
  333. }
  334.  
  335. /* Send file name and related info */
  336.  
  337. zsendfile(buf, blen)
  338. char *buf;
  339. int blen;
  340. {
  341.     static int c;
  342.  
  343.     for (;;) {
  344.         if (opabort())
  345.             return NERROR;
  346.         Txhdr[ZF0] = 0;    /* file conversion request */
  347.         Txhdr[ZF1] = 0;    /* file management request */
  348.         Txhdr[ZF2] = 0;    /* file transport request */
  349.         Txhdr[ZF3] = 0;
  350.         zsbhdr(ZFILE, Txhdr);
  351.         zsdata(buf, blen, ZCRCW);
  352. again:
  353.         c = zgethdr(Rxhdr, 1);
  354.         switch (c) {
  355.         case ZRINIT:
  356.             while ((c = readline(INTRATIME)) > 0)
  357.                 if (c == ZPAD) {
  358.                     goto again;
  359.                 }
  360.             /* **** FALL THRU TO **** */
  361.         default:
  362.             continue;
  363.         case ZCAN:
  364.         case TIMEOUT:
  365.         case ZABORT:
  366.         case ZFIN:
  367.             return NERROR;
  368.         case ZSKIP:
  369.             return c;
  370.         case ZRPOS:
  371.             /*
  372.              * Suppress zcrcw request otherwise triggered by
  373.              * lastyunc==Bytcnt
  374.              */
  375.             Lastsync = (Bytcnt = Txpos = Rxpos) -1L;
  376.  
  377. #ifdef AZTEC_C
  378.             fseek(Fd, Rxpos, 0);   /* absolute offset */
  379. #else
  380.             lseek(Fd, Rxpos, 0);   /* absolute offset */
  381. #endif
  382.  
  383.                  clrline(KBYTES);
  384.                Incnt = 0;
  385.                  Dontread = FALSE;
  386.             c = zsndfdata();
  387.             Sending = FALSE;
  388.             return c;
  389.         }
  390.     }
  391. }
  392.  
  393. /* Send the data in the file */
  394.  
  395. zsndfdata()
  396. {
  397.     static int c, e, n;
  398.     static int newcnt;
  399.     static long tcount;
  400.     static int junkcount;      /* Counts garbage chars received by TX */
  401.  
  402.     tcount = 0L;
  403.     Blklen = 128;
  404.     if (Baudrate > 300)
  405.         Blklen = 256;
  406.     if (Baudrate > 1200)
  407.         Blklen = 512;
  408.     if (Baudrate > 2400)
  409.         Blklen = KSIZE;
  410.     if (Rxbuflen && Blklen>Rxbuflen)
  411.         Blklen = Rxbuflen;
  412.     Lrxpos = 0L;
  413.     junkcount = 0;
  414.     Beenhereb4 = FALSE;
  415.     Sending = Firstsec = TRUE;
  416. somemore:
  417.     if (NULL) {
  418. waitack:
  419.         junkcount = 0;
  420.         c = getinsync(0);
  421.         if (QuitFlag)
  422.             return NERROR;
  423. gotack:
  424.         if (setjmp(jb_stop)) {    /* come here if rx stops us */
  425. rxint:
  426.             c = getinsync(1);
  427.         }
  428.  
  429.         switch (c) {
  430.             default:
  431.             case ZCAN:
  432.                 return NERROR;
  433.             case ZSKIP:
  434.                 return c;
  435.             case ZACK:
  436.             case ZRPOS:
  437.                 break;
  438.             case ZRINIT:
  439.                 return OK;
  440.         }
  441.         /*
  442.          * If the reverse channel can be tested for data,
  443.          *  this logic may be used to detect error packets
  444.          *  sent by the receiver, in place of setjmp/longjmp
  445.          *  minprdy() returns non 0 if a character is available
  446.          */
  447.         while (minprdy()) {
  448.             if (QuitFlag)
  449.                 return NERROR;
  450.             switch (readline(1)) {
  451.             case CTRLC:
  452.             case CAN:
  453.             case ZPAD:
  454.                 goto rxint;
  455.             case XOFF:        /* Wait a while for an XON */
  456.             case XOFF|0200:
  457.                 readline(100);
  458.             }
  459.         }
  460.     }
  461.  
  462.     if (setjmp(jb_stop)) {    /* rx interrupt */
  463.         c = getinsync(1);
  464.         if (c == ZACK)
  465.             goto gotanother;
  466.         purgeline();
  467.     /* zcrce - dinna wanna starta ping-pong game */
  468.         zsdata(Txbuf, 0, ZCRCE);
  469.         goto gotack;
  470.     }
  471.  
  472.     newcnt = Rxbuflen;
  473.     Txwcnt = 0;
  474.     stohdr(Txpos);
  475.     zsbhdr(ZDATA, Txhdr);
  476.     do {
  477.         if (QuitFlag)
  478.             return NERROR;
  479.         if (Dontread) {
  480.             n = Lastn;
  481.         } 
  482.         else {
  483.             n = filbuf(Txbuf, Blklen);
  484.             Lastread = Txpos;  
  485.             Lastn = n;
  486.         }
  487.         Dontread = FALSE;
  488.         if (n < Blklen)
  489.             e = ZCRCE;
  490.         else if (junkcount > 3)
  491.             e = ZCRCW;
  492.         else if (Bytcnt == Lastsync)
  493.             e = ZCRCW;
  494.         else if (Rxbuflen && (newcnt -= n) <= 0)
  495.             e = ZCRCW;
  496.         else if (Txwindow && (Txwcnt += n) >= Txwspac) {
  497.             Txwcnt = 0;  
  498.             e = ZCRCQ;
  499.         }
  500.         else
  501.             e = ZCRCG;
  502.         zsdata(Txbuf, n, e);
  503.         Txpos += (long)n;
  504.         Bytcnt = Txpos;
  505.         crcrept(Crc32t);    /* praps report crc mode */
  506.         lreport(KBYTES,Bytcnt);
  507.         if (e == ZCRCW)
  508.             goto waitack;
  509.  
  510.        /*
  511.          * If the reverse channel can be tested for data,
  512.          *  this logic may be used to detect error packets
  513.          *  sent by the receiver, in place of setjmp/longjmp
  514.          *  minprdy() returns non 0 if a character is available
  515.          */
  516.  
  517.         while (minprdy()) {
  518.             if (QuitFlag)
  519.                 return NERROR;
  520.             switch (readline(1)) {
  521.                    case CAN:
  522.                 case CTRLC:
  523.                 case ZPAD:
  524.                     c = getinsync(1);
  525.                     if (c == ZACK)
  526.                         break;
  527.                     purgeline();
  528.         /* zcrce - dinna wanna starta ping-pong game */
  529.                     zsdata(Txbuf, 0, ZCRCE);
  530.                     goto gotack;
  531.                 case XOFF:     /* Wait a while for an XON */
  532.                 case XOFF|0200:
  533.                     readline(100);
  534.                 default:
  535.                     ++junkcount;
  536.  
  537.             }
  538. gotanother:;
  539.         }
  540.         if (Txwindow) {
  541.             while ((tcount = Txpos - Lrxpos) >= Txwindow) {
  542.                 if (QuitFlag)
  543.                     return NERROR;
  544.                 if (e != ZCRCQ)
  545.                     zsdata(Txbuf, 0, e = ZCRCQ);
  546.                 c = getinsync(1);
  547.                 if (c != ZACK) {
  548.                     purgeline();
  549.                     zsdata(Txbuf, 0, ZCRCE);
  550.                     goto gotack;
  551.                 }
  552.             }
  553.         }
  554.     } 
  555.     while (n == Blklen);
  556.  
  557.     for (;;) {
  558.         if (QuitFlag)
  559.             return NERROR;
  560.         stohdr(Txpos);
  561.         zsbhdr(ZEOF, Txhdr);
  562.         switch (getinsync(0)) {
  563.         case ZACK:
  564.             continue;
  565.         case ZRPOS:
  566.             goto somemore;
  567.         case ZRINIT:
  568.             return OK;
  569.         case ZSKIP:
  570.             return c;
  571.         default:
  572.             return NERROR;
  573.         }
  574.     }
  575. }
  576.  
  577. /*
  578.  * Respond to receiver's complaint, get back in sync with receiver
  579.  */
  580.  
  581. getinsync(flag)      /* flag means that there was an error */
  582. int flag;
  583. {
  584.     static int c;
  585.     unsigned u;
  586.  
  587.     for (;;) {
  588.         if (opabort())
  589.             return NERROR;
  590.         c = zgethdr(Rxhdr, 0);
  591.         c = c < FRTYPES ? c : FRTYPES-1;
  592.         sprintf(Buf,"Got %s", frametypes[c+FTOFFSET]);
  593.         zperr(Buf,flag);
  594.         switch (c) {
  595.         case ZCAN:
  596.         case ZABORT:
  597.         case ZFIN:
  598.         case TIMEOUT:
  599.             return NERROR;
  600.         case ZRPOS:
  601.             /* ************************************* */
  602.             /*  If sending to a modem beuufer, you   */
  603.             /*   might send a break at this point to */
  604.             /*   dump the modem's buffer.         */
  605.  
  606.             if (Lastn >= 0 && Lastread == Rxpos) {
  607.                 Dontread = TRUE;
  608.             } 
  609.             else {
  610.  
  611. #ifdef AZTEC_C
  612.                    u = fseek(Fd, Rxpos, 0);   /* absolute offset */
  613.                 if (u != EOF)
  614.                     uneof(Fd);    /* Reset EOF flag */
  615. #else
  616.                    u = lseek(Fd, Rxpos, 0);   /* absolute offset */
  617. #endif
  618.  
  619.                         clrline(KBYTES);
  620.                         Incnt = 0;
  621.             }
  622.             Bytcnt = Lrxpos = Txpos = Rxpos;
  623.             if (Lastsync == Rxpos) {
  624.                 if (++Beenhereb4 > 4)
  625.                     if (Blklen > 256)
  626.                         Blklen /= 2;
  627.             }
  628.             Lastsync = Rxpos;
  629.             return c;
  630.         case ZACK:
  631.             Lrxpos = Rxpos;
  632.             if (flag || Txpos == Rxpos)
  633.                 return ZACK;
  634.             continue;
  635.         case ZRINIT:
  636.         case ZSKIP:
  637.             return c;
  638.         case NERROR:
  639.         default:
  640.             zsbhdr(ZNAK, Txhdr);
  641.             continue;
  642.         }
  643.     }
  644. }
  645.  
  646. /* Say "bibi" to the receiver, try to do it cleanly */
  647.  
  648. saybibi()
  649. {
  650.     for (;;) {
  651.         stohdr(0L);        /* CAF Was zsbhdr - minor change */
  652.         zshhdr(ZFIN, Txhdr);    /*  to make debugging easier */
  653.         switch (zgethdr(Rxhdr, 0)) {
  654.         case ZFIN:
  655.             xmchout('O'); 
  656.             xmchout('O'); 
  657.         case ZCAN:
  658.         case TIMEOUT:
  659.             return;
  660.         }
  661.     }
  662. }
  663.  
  664. char *
  665. ttime(fsize)
  666. long fsize;
  667. {
  668.     static int efficiency, cps, seconds;
  669.     static char buffer[10];
  670.  
  671.     efficiency = 9;
  672.     cps = (Baudrate/100) * efficiency;   
  673.     seconds = (int)(fsize/cps);     
  674.     sprintf(buffer,"%d:%02d",seconds/60,seconds%60);
  675.     return buffer;
  676. }
  677.  
  678. tfclose()          /* close file if still open */
  679. {
  680. #ifdef AZTEC_C
  681.     if (Fd)
  682.         fclose(Fd);
  683.     Fd = 0;
  684. #else
  685.     if (Fd >= 0)
  686.         close(Fd);
  687.     Fd = -1;
  688. #endif
  689. }
  690.  
  691. /* output a character to the modem and test the receive channel */
  692. xmchout(c)
  693. char c;
  694. {
  695.     testrxc(200);    /* test receive channel in case xoff */
  696.     mcharout(c);    /* then send it */
  697. }
  698.  
  699. /* Test receive channel for xon/xoff and interrupt while sending */
  700. testrxc(timeout)
  701. short timeout;        /* timeout in seconds */
  702. {
  703.     static char c;
  704.  
  705.     c = minprdy() ? mcharinp() : '\0';    /* get a character if any */
  706.     if (c) {
  707.         if (c == CTRLS) {
  708.             while (((c = readline(10)) != CTRLQ) && timeout--);
  709.             if (c < 1)
  710.                 c = CTRLC;    /* timeout */
  711.         }
  712.         StopFlag = ((c == CTRLC) || (c == CTRLX) || (c == '*'));
  713.         if (StopFlag && Sending)
  714.             longjmp(jb_stop,c);    /* zmodem transmit interrupt */
  715.     }
  716. }
  717.  
  718. /* print send mode label */
  719. slabel()
  720. {
  721.     putlabel("SEND FILE mode:  Press ESC to Abort...");
  722. }
  723.  
  724. /************************** END OF MODULE 3 *********************************/
  725.  
  726.