home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 340.lha / JModem_v1.0 / JModem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-01-23  |  25.1 KB  |  852 lines

  1.  
  2. /* JMODEM protocol implementation, version 1.1. Information extracted
  3.  * from MS DOS files JMODEM.ASM and JMODEM.DOC by Richard B. Johnson
  4.  * (version 1.20) This program was made 25.4 - 7.5 1989 by Kenneth
  5.  * Ã–sterberg.
  6.  * Command line syntax:
  7.  * JModem devname unit baud {R|S} filename ...
  8.  */
  9.  
  10. /* files to include */
  11.  
  12. #include <exec/types.h>
  13. #include <exec/nodes.h>
  14. #include <exec/memory.h>
  15. #include <exec/libraries.h>
  16. #include <exec/devices.h>
  17. #include <devices/serial.h>
  18. #include <libraries/dos.h>
  19. #include <intuition/intuition.h>
  20.  
  21. #include "protsupp.h" /* Symbols and definition for protsupp.c */
  22.  
  23. char *WinTxt[] = {
  24.   "File name        :",
  25.   "File size        :",
  26.   "Block number     :",
  27.   "Block size       :",
  28.   "Bytes transferred:",
  29.   "Status           :",
  30.   NULL
  31. };
  32.  
  33. #define TXPOS 19    /* XPos where to print information texts */
  34. #define WINWID 500  /* Width of our JModem window */
  35. #define WINHGT 80   /* Height */
  36. #define GXPOS 36    /* Gadget left edge relative to our window */
  37. #define GYPOS WINHGT - 28 /* Gadget top edge relative to window bottom */
  38.  
  39. struct windef wdef = {
  40.   WINWID,WINHGT,
  41.   GXPOS,GYPOS,
  42.   " JModem v1.0 \0receive ",
  43.   WinTxt
  44. };
  45.  
  46. /* Other definitions */
  47.  
  48. #define JC_NONE 0        /* Error codes for Jcleanup() */
  49. #define JC_IOREQ 1
  50. #define JC_CBUF 2
  51. #define JC_SBUF 3
  52.  
  53. #define NAK 21          /* Negative Acknowledge, a block was not accepted */
  54. #define SYN 22          /* Sync character, used in long handshaking */
  55. #define CAN 24          /* Cancel, used to abort transfer */
  56. #define ACK 6           /* Acknowledge, signals that all is OK so far */
  57. #define BB 0xBB         /* The 'sentinel', an escape char. in JModem */
  58. #define MAXSTRING 8192  /* Max size of a data block */
  59. #define MINSTRING 256   /* Minimum size, the value we start with */
  60. #define DSTRING 512     /* The amount we increase the block size with */
  61. #define MAXRETRIES 20   /* The number of times we try to resend a block */
  62.  
  63. /* Three data buffers are used, one receive buffer, one send buffer
  64.  * and one compress buffer. The required size of these buffers is
  65.  * MAXSTRING + 256 + 6, and the compress buffer must be a few bytes
  66.  * larger to prevent overrun when compressing. */
  67.  
  68. #define BUFSIZE1 MAXSTRING + 256L + 6L  /* The diskbuffer size */
  69. #define BUFSIZE2 BUFSIZE1 + 4L   /* The size of the 'codedbuffer' */
  70.  
  71. /* Magical JModem control byte constants */
  72.  
  73. #define NORMAL 1       /* A normal uncompressed block */
  74. #define COMP 2         /* A compressed block */
  75. #define EOF 4          /* When this is set we have the last block */
  76. #define RETRY 8        /* Not currecntly used */
  77. #define TIMEOUT 16     /* Not currently used  */
  78. #define ABORT 32       /* Transmission aborted. */
  79. #define SYNC 64        /* Noy currently used  */
  80. #define ERROR 128      /* Not currently used  */
  81.  
  82. /* A macro for sending characters */
  83.  
  84. #define SENDCHAR(x) *sbuf = (x); sendserchar(sbuf)
  85.  
  86. /* Variables visible to all routines */
  87.  
  88. int stringsize, retries;
  89. ULONG baudrate, unit;
  90. UWORD recnum;
  91. UBYTE *rbuf, *sbuf, *cbuf;
  92. int RetCode;
  93.  
  94. /* Library routines we use */
  95.  
  96. int Write(), Read();
  97. APTR AllocMem(), Open(), Lock(), Seek();
  98. void FreeMem(), Close(), DeleteFile(), Rename(), UnLock();
  99. struct Message *GetMsg();
  100.  
  101. /* Routines in this file and docrc.asm */
  102.  
  103. extern int docrc();
  104. int jsend(), readfile(), encode(), chkres(), rxchar(), docrc();
  105. void sendx(), sdata(), clearbuf(), Jcleanup();
  106. ULONG calctime();
  107. APTR htol();
  108.  
  109. /* Some strings */
  110.  
  111. char nomemstr[] = "Cannot allocate enough memory";
  112. char numform[] = "%ld";
  113.  
  114. /* The main program starts here */
  115.  
  116. void main(argc, argv)
  117. register int argc;
  118. char *argv[];
  119. {
  120.   char devname[32];          /* Name of serial device */
  121.   register char tmpchar;
  122.   register int noerrors, fcount;
  123.   struct recptrs *recptr;    /* Ptr to structure containing IORequest ptrs */
  124.  
  125.   RetCode = RETURN_FAIL;     /* Assume failure */
  126.  
  127.   if (!SetupWindow(&wdef))   /* Open the JModem window */
  128.     exit(RetCode);
  129.  
  130.   if (argc < 5) {
  131.     PText(5,TXPOS,"Too few arguments. Read JAmiga.txt");
  132.     Delay(200L);
  133.     Wcleanup(WC_NONE);
  134.     exit(RetCode);
  135.   }
  136.  
  137.   if ((rbuf = (UBYTE *)AllocMem(BUFSIZE1,MEMF_CHIP)) == NULL) {
  138.     PText(5,TXPOS,nomemstr);
  139.     Delay(200L);
  140.     Wcleanup(WC_NONE);
  141.     exit(RetCode);
  142.   }
  143.  
  144.   if ((sbuf = (UBYTE *)AllocMem(BUFSIZE1,MEMF_CHIP)) == NULL) {
  145.     PText(5,TXPOS,nomemstr);
  146.     Delay(200L);
  147.     Wcleanup(WC_NONE);
  148.     Jcleanup(JC_SBUF);
  149.   }
  150.  
  151.   if ((cbuf = (UBYTE *)AllocMem(BUFSIZE2,MEMF_CHIP)) == NULL) {
  152.     PText(5,TXPOS,nomemstr);
  153.     Delay(200L);
  154.     Wcleanup(WC_NONE);
  155.     Jcleanup(JC_CBUF);
  156.   }
  157.  
  158.   strcpy(devname,argv[1]);                  /* Name of the serial device */
  159.   unit = (ULONG)atoi(argv[2]);              /* Unit number in the device */
  160.   baudrate = (ULONG)atoi(argv[3]);          /* The baudrate */
  161.   argv += 4;
  162.   argc -= 5;
  163.   if ((baudrate < 110) || (baudrate > 19200)) {
  164.     PText(5,TXPOS,"Baudrate %ld out of range",baudrate);
  165.     Delay(200L);
  166.     Wcleanup(WC_NONE);
  167.     Jcleanup(JC_IOREQ);
  168.   }
  169.  
  170.   if (!(OpenUpSerial(&RR,&WR,BUFSIZE1,baudrate,unit,devname))) {
  171.     PText(5,TXPOS,"Can't open %s unit %ld",devname,unit);
  172.     Delay(200L);
  173.     Wcleanup(WC_NONE);
  174.     Jcleanup(JC_IOREQ);
  175.   }
  176.  
  177.   if (!(OpenUpTimer(&TR))) {
  178.     PText(5,TXPOS,"Can't open timer.device");
  179.     Delay(200L);
  180.     Wcleanup(WC_NONE);
  181.     Scleanup(SC_NONE);
  182.     Jcleanup(JC_IOREQ);
  183.   }
  184.  
  185.   tmpchar = argv[0][0];  /* This should be either R (receive) or S (send) */
  186.   argv++;                /* argv now points at the file name */
  187.   
  188.   stringsize = MINSTRING;  /* We start with the smallest block size */
  189.   noerrors = TRUE;
  190.   fcount = 0;              /* File transfer counter */
  191.   RetCode = RETURN_ERROR;
  192.   while ((argc--) && (noerrors)) {       /* Process all file names */
  193.     switch (tmpchar) {
  194.     case 'R':
  195.     case 'r':
  196.       if (!fcount) { /* Change the window title only the first time */
  197.         *(pwin->Title + strlen(pwin->Title)) = ' '; /* Remove \0 char */
  198.         SetWindowTitles(pwin,pwin->Title,-1L);      /* Show 'JModem receive' */
  199.       }
  200.       PText(0,TXPOS,"%s",argv[0]);                   /* Display file name */
  201.       PText(1,TXPOS,"?");
  202.       PText(2,TXPOS,"1");
  203.       PText(3,TXPOS,"?");
  204.       PText(4,TXPOS,"0");
  205.  
  206.       if (jrec(argv[0], rbuf, sbuf, cbuf)) {
  207.         PText(5,TXPOS,"File received!");
  208.         fcount++;
  209.       }
  210.       else {
  211.         PText(5,TXPOS,"File transfer failed!");
  212.         noerrors = FALSE;
  213.       }
  214.       break;
  215.     case 'S':
  216.     case 's':
  217.       if (!fcount)
  218.         SetWindowTitles(pwin," JModem v1.0 send ",-1L);
  219.       PText(0,TXPOS,"%s",argv[0]);
  220.       if (jsend(argv[0], rbuf, sbuf, cbuf)) {
  221.         PText(5,TXPOS,"File sent!");
  222.         fcount++;
  223.       }
  224.       else {
  225.         PText(5,TXPOS,"File transfer failed");
  226.         noerrors = FALSE;
  227.       }
  228.       break;
  229.     default:
  230.       PText(5,TXPOS,"Invalid args. R[eceive] or S[end] expected");
  231.       noerrors = FALSE;
  232.       RetCode = RETURN_FAIL;
  233.       break;
  234.     }
  235.     argv++;
  236.   }
  237.  
  238. /* The return code gives the number of files successfully transferred, or if
  239.  * no files were transferred RETURN_ERROR (10) is returned. A complete
  240.  * JModem failure (out of memory, no serial device, or invalid command line)
  241.  * results in RETURN_FAIL (20). These return codes should give a good idea
  242.  * which files were sent, and which were not. */
  243.  
  244.   if (fcount)
  245.     RetCode = fcount;
  246.  
  247.   Tcleanup(TC_NONE);
  248.   Scleanup(SC_NONE);
  249.  
  250.   Delay(200L);
  251.   Wcleanup(WC_NONE);
  252.   Jcleanup(JC_NONE);
  253. }
  254.  
  255. int jrec(name, rbuf, sbuf, cbuf)
  256. /* Receive a file from the remote system and write it to the disk
  257.  * file 'name'. The function returns TRUE if the file was successfully
  258.  * received and stored. */
  259.  
  260. char *name;
  261. UBYTE *rbuf, *sbuf, *cbuf;
  262. {
  263.   int blklen, recrec, status, success = FALSE;
  264.   register int a;
  265.   register UBYTE ctrl, *bufptr, *tmpptr;
  266.   struct IntuiMessage *imsg;
  267.   APTR file;
  268.   ULONG totlen = 0;
  269.  
  270. /* If the file name we wish to receive exists already, then add the
  271.  * suffix '.old' to the existing file. */
  272.  
  273.   if (file = Lock(name,ACCESS_READ)) {
  274.     a = strlen(name);
  275.     bufptr = (UBYTE *)AllocMem((ULONG)a+6L,MEMF_PUBLIC);
  276.     if (bufptr) {
  277.       PText(5,TXPOS,"File already exists. Appending '.old'");
  278.       tmpptr = (UBYTE *)name;
  279.       while (*tmpptr) *bufptr++ = *tmpptr++;
  280.       strcpy(bufptr,".old");
  281.       bufptr -= a;
  282.       Rename(name,bufptr);
  283.       FreeMem(bufptr,(ULONG)a+6L);
  284.     }
  285.     UnLock(file);
  286.   }
  287.  
  288. /* Open the file to receive and get into sync with sender */
  289.  
  290.   if (((file = Open(name,MODE_NEWFILE))==0) || (!rxsync())) {
  291.     sendx();
  292.     if (file) {
  293.       PText(5,TXPOS,"Syncronization failed. Aborting");
  294.       Close(file);
  295.       DeleteFile(name);
  296.     }
  297.     else
  298.       PText(5,TXPOS,"Can't open file. Aborting");
  299.   }
  300.   else {
  301.     recnum = 1;
  302.     do {
  303.       retries = MAXRETRIES;
  304.       PText(2,TXPOS,numform,(ULONG)recnum);
  305.  
  306. /* Try to get the block until no errors occurred or the retry count
  307.  * reached 0. */
  308.  
  309.       while ((retries) &&
  310.              (((status = rdata(rbuf,baudrate,&blklen)) != GS_NONE) ||
  311.              (!docrc(rbuf,blklen+6)))
  312.             ) {
  313.  
  314. /* Reception of block failed (timeout or CRC mismatch). Send NAK */
  315.  
  316.         if (--retries) {
  317.           if (CheckAbort()) {
  318.             sendx();
  319.             retries = 0;
  320.           }
  321.           else {
  322.             SENDCHAR(NAK);      /* Send NAK if receive failed */
  323.             if (status == GS_TIMEOUT)
  324.               PText(5,TXPOS,"Timeout waiting for block. Retry %ld",
  325.                                                (ULONG)MAXRETRIES-retries); 
  326.             if (status == GS_NONE)
  327.               PText(5,TXPOS,"CRC checksum mismatch. Retry %ld",
  328.                                                (ULONG)MAXRETRIES-retries);
  329.           }
  330.         }
  331.         else
  332.           PText(5,TXPOS,"Retry count exceeded. Aborting");
  333.       }
  334.  
  335.       if (CheckAbort()) {
  336.         retries = 0;
  337.         sendx();
  338.       }
  339.  
  340.       ctrl = *(rbuf + 2);         /* The control byte */
  341.       recrec = *(rbuf + 3);       /* Low byte of block number we received */
  342.       if (retries) {
  343.         bufptr = rbuf + 4;        /* Default ptr to uncompressed data */
  344.         if (ctrl & COMP) {        /* Uncompress if data is compressed */
  345.           blklen = decode(bufptr,cbuf,blklen);
  346.           bufptr = cbuf;
  347.         }
  348.  
  349.         if (ctrl & ABORT) {
  350.           sendx();
  351.           PText(5,TXPOS,"Transmission aborted by sender");
  352.           retries = 0;
  353.         }
  354.         else {
  355.           if (recrec == (recnum & 0xff)) {
  356.             if (Write(file,bufptr,(ULONG)blklen) < blklen) {
  357.               sendx();
  358.               PText(5,TXPOS,"File write failed. Aborting");
  359.               retries = 0;
  360.             }
  361.             else {
  362.               PText(5,TXPOS,"Block %ld received",(ULONG)recnum);
  363.               totlen += blklen;
  364.               PText(4,TXPOS,numform,totlen);
  365.               recnum++;                /* Update expected record number */
  366.               SENDCHAR(ACK);           /* Allow sender to continue */
  367.               checkeof(ctrl,&success); /* See if this is the last block */         
  368.             }
  369.           }
  370.           else {
  371.             PText(5,TXPOS,"Expected block %ld, received %ld",
  372.                        (ULONG)recnum & 0xff, (ULONG)recrec);
  373.             SENDCHAR(ACK);
  374.             checkeof(ctrl,&success);
  375.             if (success == TRUE) {
  376.               success = FALSE;
  377.               retries = 0;
  378.             }
  379.           }
  380.         }
  381.       }
  382.       else {
  383.         sendx();
  384.       }
  385.     } while ((!success) && (retries)); /* Continue until success or failure */
  386.  
  387. /* Find out length of file before closing it */
  388.  
  389.     bufptr = (UBYTE *)Seek(file,0L,OFFSET_CURRENT);
  390.     PText(1,TXPOS,numform,bufptr);
  391.     Close(file);                       /* Close the file */
  392.     if (bufptr == NULL)                /* Delete file if length is 0 */
  393.       DeleteFile(name);
  394.   }
  395.   if (success)
  396.     return(TRUE);
  397.   else
  398.     return(FALSE);
  399. }
  400.  
  401. checkeof(ctrl,success)
  402. register UBYTE ctrl;
  403. register int *success;
  404. {
  405.   register int a;
  406.  
  407.   if (ctrl & EOF) {                  /* This was the last block */
  408.     for (a=4; a; a--) SENDCHAR(ACK); /* Send 4 ACK's */
  409.     *success = TRUE;                 /* We have success */
  410.   }
  411. }
  412.  
  413. int jsend(name, rbuf, sbuf, cbuf)
  414. /* This routine takes a file name and sends it to the remote system
  415.  * using the JMODEM transfer protocol. The function returns TRUE if
  416.  * the file was successfully sent.
  417.  */
  418. char *name;
  419. register UBYTE *rbuf, *sbuf, *cbuf;
  420. {
  421.   UBYTE *bufptr;
  422.   int blklen = 6, status;
  423.   APTR file;
  424.   ULONG totsize = 0,fsize = 0;
  425.   register struct IntuiMessage *imsg;
  426.   register int abort = FALSE;
  427.  
  428.   retries = 0;
  429.   recnum = 0;
  430.  
  431. /* Open the source file and if that succeeds try to get in touch with
  432.  * the receiver */
  433.  
  434.   PText(2,TXPOS,"1");
  435.   PText(3,TXPOS,numform,(ULONG)stringsize);
  436.   PText(4,TXPOS,"0");
  437.  
  438.   if ((file = Open(name,MODE_OLDFILE))==0)
  439.     PText(5,TXPOS,"Could not open file");
  440.  
  441.   if ((file == 0) || (!txsynch(file,&totsize)))
  442.     sendx();                /* No sync. Abort transfer */
  443.   else {
  444.  
  445. /* File is open and sync has been established. Proceed to send the file */
  446.  
  447.     do {
  448.       fsize += (blklen - 6); /* Header is not counted in bytes transferred */
  449.       PText(4,TXPOS,"%ld  (%ld%% remains)", fsize, 100 - fsize * 100 / totsize);
  450.  
  451. /* Read the source file */
  452.  
  453.       if ((blklen = readfile(file, sbuf+4, cbuf+4, &bufptr)) >= 6) {
  454.         retries = MAXRETRIES;
  455.         do {
  456.  
  457. /* Check if user has hit the 'Abort' gadget */
  458.  
  459.           if (abort = CheckAbort()) {
  460.             PText(5,TXPOS,"User abort! Telling receiver to quit");
  461.             *(bufptr + 2) |= ABORT;   /* Set ABORT bit in control byte */
  462.             docrc(bufptr,blklen);     /* Recalculate checksum */
  463.           }
  464.  
  465. /* Send the block */
  466.  
  467.           sendserdata(bufptr,(ULONG)blklen);
  468.  
  469. /* Get ACKnowldge from receiver.
  470.  * Loops until ACK, NAK or CAN is received (or user aborted) */
  471.  
  472.           do {
  473.             if ((status = getserchar(13*50L,rbuf)) != GS_NONE) {
  474.               if (status == GS_TIMEOUT)
  475.                 PText(5,TXPOS,"Timeout waiting for response. Aborting");
  476.               abort = TRUE;
  477.             }
  478.           } while ((*rbuf != ACK) &&
  479.                    (chkres(*rbuf,&retries,&blklen) == FALSE) &&
  480.                    (abort == FALSE)); 
  481.         } while ((*rbuf != ACK) && (retries > 0) && (abort == FALSE));
  482.  
  483.         if ((retries) && (blklen > 0) && (abort == FALSE))
  484.           PText(5,TXPOS,"Block %ld sent",(ULONG)recnum);
  485.       }
  486.     } while (((*(bufptr + 2) & EOF) == 0) && (blklen >= 6) && (abort == FALSE));
  487.  
  488.     if (blklen < 0) {
  489.       sendx();
  490.       PText(5,TXPOS,"File read failed. Transmission aborted");
  491.     }
  492.   }
  493.   if (abort)
  494.     sendx();
  495.   if (file) Close(file);
  496.   if ((retries) && (abort == FALSE)) {
  497.     fsize += (blklen - 6);
  498.     PText(4,TXPOS,"%ld  (0% remains)", fsize);
  499.     return TRUE;
  500.   }
  501.   else
  502.     return FALSE;
  503. }
  504.  
  505. int txsynch(file,totsize)
  506. /* Get in sync. with remote and wait 0.3 seconds before returning */
  507. register APTR file;
  508. register APTR *totsize;
  509. {
  510.   (void) Seek(file,0L,OFFSET_END);
  511.   *totsize = Seek(file,0L,OFFSET_BEGINNING);
  512.   PText(1,TXPOS,numform,*totsize);
  513.   if (rxsync()) {
  514.     Delay(15L);
  515.     return(1);
  516.   }
  517.   else
  518.     return(0);
  519. }
  520.  
  521. int rxsync() {
  522. /* Get in syncronization with remote. Returns 0 if sync not
  523.  * achieved. */
  524.  
  525.   register int a, abort = FALSE;
  526.   register struct IntuiMessage *imsg;
  527.  
  528.   PText(5,TXPOS,"Synchronizing...");
  529.   clearbuf();
  530.  
  531. /* See if remote has sent something. If nothing is read in 2 seconds, we
  532.  * send a NAK character. This is repeated until something is read */
  533.  
  534.   while ( (!(abort = CheckAbort())) && (getserchar(100L,rbuf)==GS_TIMEOUT)) {
  535.     SENDCHAR(NAK);
  536.   }
  537.  
  538. /* If user pressed Abort gadget then cancel and return */
  539.  
  540.   if (abort) {
  541.     sendx();
  542.     return(0);
  543.   }
  544.  
  545. /* Check if we have quick syncronization */
  546.  
  547.   if ((*rbuf == ACK) || (*rbuf == NAK)) {
  548.     if (*rbuf == NAK) {
  549.       SENDCHAR(ACK);              /* Send ACK if we got NAK */
  550.     }
  551.     PText(5,TXPOS,"Synchronized");
  552.     return(1);
  553.   }
  554.  
  555. /* Well, quick sync. apparently failed. Do long handshaking with SYN
  556.  * characters */
  557.  
  558.   for (a=0; a<64; a++) 
  559.     do {
  560.       SENDCHAR(SYN);               /* Send a SYN and wait for response */
  561.       if (!(abort = CheckAbort())) {
  562.         if (getserchar(150L,rbuf) != GS_NONE) {
  563.           PText(5,TXPOS,"Synchronization not acquired");
  564.           clearbuf();
  565.           return(0);
  566.         }
  567.         if (*rbuf == CAN) {
  568.           PText(5,TXPOS,"Remote system cancelled");
  569.           clearbuf();
  570.           return(0);
  571.         }
  572.       }
  573.       else
  574.         a = 64;
  575.     } while ((*rbuf != SYN) && (!abort));
  576.  
  577.   if (!abort) {
  578.     SENDCHAR(SYN);
  579.     clearbuf();
  580.     PText(5,TXPOS,"Synchronized");
  581.     return(1);
  582.   }
  583.   else {
  584.     sendx();
  585.     return(0);
  586.   }
  587. }
  588.  
  589. void clearbuf() {
  590. /* Empty receiving buffer */
  591.   RR->IOSer.io_Command = CMD_CLEAR; /* Flush receive buffer */
  592.   DoIO(RR);
  593. }
  594.  
  595. int rdata(buffer, baud, length)
  596. /* Receive bytes from the serial port. Variable 'baud' specifies the
  597.  * current baud rate. From this value a timeout value is calculated
  598.  * once we know the length of the block we are receiving.
  599.  * The function returns an error code, and GS_NONE if all went well
  600.  * The variable 'length' gets the block size (header not included),
  601.  * which can be inspected from outside of this routine. */
  602.  
  603. UBYTE *buffer;
  604. ULONG baud;
  605. UWORD *length;
  606. {
  607.   register UWORD temp;
  608.   register UWORD slen;
  609.   ULONG timeout;
  610.   int status;
  611.  
  612. /* Get two bytes from the serial port. This is the length of the block,
  613.  * (with low byte first of course :-( Timeout value is 6 seconds. */
  614.  
  615.   *length = 0;
  616.   status = getserdata(300L,buffer,2L);
  617.   if (status == GS_NONE) {
  618.     slen = (*(buffer + 1) << 8) + *buffer; /* Extract length of string */
  619.     *length = slen - 6;
  620.     PText(3,TXPOS,numform,(ULONG)slen - 6);
  621.     buffer += 2;
  622.     timeout = 300L + ((3 * calctime(baud,slen-2)) >> 1); /* Estimate timeout */
  623.     status = getserdata(timeout,buffer,(ULONG)slen-2L);
  624.   }
  625.   return(status);
  626. }
  627.  
  628. ULONG calctime(baud,numbytes)
  629. /* This routine estimates how long it will take to receive
  630.  * a specific number of bytes over a serial link at a specific
  631.  * baud rate. The result is given in 20 millisecond (1/50 s) units
  632.  */
  633. register ULONG baud;
  634. register UWORD numbytes;
  635. {
  636.   return(400L * numbytes / baud);
  637. }
  638.  
  639. int readfile(file, sbuf, cbuf, usebuffer)
  640. /* Read file to send into data buffer and change the string size according
  641.  * to previous results. Build the string header, syntax (bytes):
  642.  * LOWLEN, HILEN, CONTROL, REC, data bytes according to string size, CRC, CRC
  643.  * Compress the read data and use the compressed version if it is shorter
  644.  * than the uncompressed. The function returns the actual number of bytes to
  645.  * transmit, or negative if an error occurred.
  646.  * Uses external variables int stringsize, UWORD recnum
  647.  */
  648. ULONG file;
  649. register UBYTE *sbuf, *cbuf, **usebuffer;
  650. {
  651.   register int rawcount,codedcount;
  652.  
  653.   if (retries == MAXRETRIES) {  /* If no transfer error occured in last block */
  654.     if (stringsize < MAXSTRING) /* and the block size is not already 8TXPOS2 */
  655.       stringsize +=DSTRING;     /* then increase block size by 512 bytes */
  656.   }
  657.   else                          /* A transfer error occurred in last block. */
  658.     if (stringsize > MINSTRING) /* If the blocksize isn't already at minimum */
  659.       stringsize >>= 2;         /* stringsize = stringsize DIV 4 */
  660.  
  661. /* Read data from file. Create compressed version of data */
  662.  
  663.   if ((rawcount = Read(file,sbuf,(LONG)stringsize)) <= 0)
  664.     return (rawcount); 
  665.   codedcount = encode(sbuf,cbuf,rawcount);
  666.  
  667. /* If compressed data is shorter than uncompressed, then use the
  668.  * compressed version. */
  669.  
  670.   if (codedcount < rawcount) {
  671.     PText(3,TXPOS,"%ld (%ld%% compressed)",
  672.                (ULONG)codedcount,
  673.                (ULONG)100 - (LONG)codedcount * 100 / rawcount);
  674.     codedcount += 6;                 /* Include the header bytes into length */
  675.     cbuf -= 4;                       /* To beginning of header */
  676.     *cbuf++ = (codedcount & 0xff);   /* Low byte of string length */
  677.     *cbuf++ = (codedcount >> 8);     /* High byte of string length */
  678.     *cbuf = COMP;                    /* Compressed block */
  679.     if (rawcount < stringsize)
  680.       *cbuf |= EOF;                  /* Set EOF flag if needed */
  681.     cbuf++;
  682.     *cbuf++ = ++recnum & 0xff;       /* Record number (low byte) */
  683.     docrc(cbuf-4,codedcount);        /* Calculate checksum for the block */
  684.     *usebuffer = cbuf-4;
  685.     PText(2,TXPOS,numform,(ULONG)recnum);
  686.     return(codedcount);
  687.   }
  688.   else {
  689.     PText(3,TXPOS,"%ld (not compressed)",(ULONG)rawcount);
  690.     rawcount += 6;
  691.     sbuf -= 4;
  692.     *sbuf++ = (rawcount & 0xff);
  693.     *sbuf++ = (rawcount >> 8);
  694.     *sbuf = NORMAL;                  /* Normal block, not compressed */
  695.     if (rawcount < stringsize)
  696.       *sbuf |= EOF;                  /* Set EOF flag if needed */
  697.     sbuf++;
  698.     *sbuf++ = ++recnum & 0xff;
  699.     docrc(sbuf-4,rawcount);
  700.     *usebuffer = sbuf-4;
  701.     PText(2,TXPOS,numform,(ULONG)recnum);
  702.     return(rawcount);
  703.   }
  704. }
  705.  
  706. int decode(srcbuf, dstbuf, srccnt)
  707. /* Uncompress data pointed to by srcbuf, place resulting data into
  708.  * buffer pointed to by dstbuf. We have srccnt bytes to uncompress.
  709.  * Returns the destination length. */
  710.  
  711. register UBYTE *srcbuf, *dstbuf;
  712. register int srccnt;
  713. {
  714.   register UBYTE chr;
  715.   register UWORD dstcnt = 0, count;
  716.  
  717.   while (srccnt-- > 0) {              /* Until whole source buffer processed */
  718.     if ((chr = *srcbuf++) != BB) {    /* Do we have the 'sentinel' byte? */
  719.       *dstbuf++ = chr;                /* Just transfer to destbuffer if not */
  720.       dstcnt++;
  721.     }
  722.     else {                            /* The 'sentinel' byte was met */
  723.        count = (*(srcbuf + 1) << 8);  /* Fetch high byte of repeat count */
  724.        count += *srcbuf;              /* Fetch low byte of repeat count */
  725.        srcbuf += 2;
  726.        chr = *srcbuf++;               /* Fetch character to repeat */
  727.        srccnt -= 3;
  728.        dstcnt += count;
  729.        for (; count > 0; *dstbuf++ = chr, count--);
  730.     }
  731.   }
  732.   return(dstcnt);
  733. }
  734.  
  735. int encode(srcbuf, dstbuf, srccnt)
  736. /* Compress data pointed to by srcbuf, place resulting data into buffer
  737.  * pointed to by dstbuf. We have srccnt bytes to compress. Returns the
  738.  * destination length */
  739.  
  740. UBYTE *srcbuf, *dstbuf;
  741. int srccnt;
  742. {
  743.   register UBYTE curchar, *tmpsrc;
  744.   register int dstcnt = 0, srcc = srccnt;
  745.   register int remaining;
  746.   register UWORD dupcnt;
  747.  
  748. /* Start compressing. If the destination buffer size gets equal or larger
  749.  * than the source buffer then the compression is immediately aborted.
  750.  * You may use the compressed version only if it is smaller than the
  751.  * uncompressed data. */
  752.  
  753.   while ((srcc > 0) && (dstcnt < srccnt)) {
  754.     curchar = *srcbuf++;   /* Get current character to process */
  755.     tmpsrc = srcbuf;       /* Ptr to next position in buffer */
  756.     dupcnt = 1;            /* Number of identical copies we have found */
  757.     remaining = --srcc;    /* Number of characters still unprocessed in buffer */
  758.     while ((*tmpsrc++ == curchar) && (remaining--))
  759.       dupcnt++;            /* Count number of identical chars */
  760.  
  761. /* Perform compression if the 'sentinel' byte 0xBB was met or the number of
  762.  * identical characters was more than 4 */
  763.  
  764.     if ((curchar == BB) || (dupcnt > 4)) {
  765.       *dstbuf++ = BB;              /* Sentinel character for identification */
  766.       *dstbuf++ = (dupcnt & 0xff); /* Store low byte of length first */
  767.       *dstbuf++ = (dupcnt >> 8);   /* then high byte */
  768.       *dstbuf++ = curchar;         /* The character we are processing */
  769.       dstcnt += 4;                 /* Update destination count */
  770.       srcbuf = tmpsrc - 1;         /* Continue reading after handled chars */
  771.       srcc -= dupcnt - 1;          /* Decrement byte count of source buf. */
  772.     }
  773.     else {                         /* Store uncompressed */
  774.       *dstbuf++ = curchar;
  775.       dstcnt++;
  776.     }
  777.   }
  778.   return (dstcnt);
  779. }
  780.  
  781. int chkres(chr,retries,len)
  782. /* We should have got a ACK, but didn't. Find out what we did get.
  783.  * This function returns FALSE if garbage was received. Returns TRUE
  784.  * if we could interpret the character */
  785. register UBYTE chr;
  786. register int *retries, *len;
  787. {
  788.   if (chr == CAN) {
  789.     sendx();               /* Receiver sent CAN. Abort transfer */
  790.     PText(5,TXPOS,"Receiver aborted. Transmission ended");
  791.     *retries = 0;
  792.     *len = 0;
  793.     return TRUE;
  794.   }
  795.   if (chr != NAK)           /* If not NAK it must be garbage. Get ACK again */
  796.     return FALSE;
  797.   else
  798.     if (--(*retries))       /* Receiver didn't get. Update retry counter */
  799.       return TRUE;          /* Resend until retry counter is zero */
  800.     else {
  801.       sendx();              /* Tell receiver to stop waiting. We stop trying */
  802.       *len = 0;
  803.       PText(5,TXPOS,"Bad connection, giving up");
  804.       return TRUE;
  805.     }
  806. }
  807.  
  808. void sendx() { 
  809. /* Send 10 CAN chars (== CTRL-X) to remote */
  810.   register int a;
  811.   for (a=10; a!=0; a--) {
  812.     SENDCHAR(CAN);
  813.   }
  814. }
  815.  
  816. APTR htol(str)
  817. /* Convert a hexadecimal string to a long integer */
  818. register char *str;
  819. {
  820.   register char chr;
  821.   register ULONG num = 0, multiplier = 1;
  822.   register int cnt = 0;
  823.   
  824.   while (*(str + cnt++));    /* Find last character in string */
  825.  
  826.   while (--cnt) {            /* Process all digits */
  827.     chr = *(str + cnt - 1);  /* Get character into a register */
  828.     if (chr >= 'a')          /* Convert to upper case letter */
  829.       chr -= 32;
  830.     num += (chr<='9') ? multiplier*(chr-'0') : multiplier*(chr-'A'+10);
  831.     multiplier <<= 4;        /* Multiplier * 16 */
  832.   }
  833.   return((APTR)num);
  834. }
  835.  
  836. void Jcleanup(num)
  837. /* Perform resource deallocation. Uses global variable 'RetCode' */
  838. register int num;
  839. {
  840.   switch(num) {
  841.   case JC_NONE:
  842.   case JC_IOREQ:
  843.     FreeMem(cbuf,BUFSIZE2);
  844.   case JC_CBUF:
  845.     FreeMem(sbuf,BUFSIZE1);
  846.   case JC_SBUF:
  847.     FreeMem(rbuf,BUFSIZE1);
  848.   default: ;
  849.   }
  850.   exit(RetCode);
  851. }
  852.