home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1994 June / NEBULA_SE.ISO / SourceCode / MiscKit / Source / MiscXmodem.m < prev    next >
Encoding:
Text File  |  1994-03-28  |  17.7 KB  |  774 lines

  1. //
  2. // MiscXmodem.m: spawn and control a thread performing a XMODEM file transfer
  3. //        Written by Eric Norum Copyright (c) 1994 by Eric Norum.
  4. //                Version 1.0.  All rights reserved.
  5. //        This notice may not be removed from this source code.
  6. //
  7. //    This object is included in the MiscKit by permission from the author
  8. //    and its use is governed by the MiscKit license, found in the file
  9. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  10. //    for a list of all applicable permissions and restrictions.
  11. //    
  12.  
  13. /*
  14.  * Xmodem file transfer
  15.  *
  16.  * Based on ``XMODEM/YMODEM PROTOCOL REFERENCE -- A compendium of documents
  17.  * describing the XMODEM and YMODEM File Transfer Protocols'', Chuck Forsberg,
  18.  * Omen Technology Incorporated, Portland, Oregon, 1988.
  19.  */
  20. #import <misckit/misckit.h>
  21. /* #import "MiscXmodem.h" /* */
  22.  
  23. /*
  24.  * All timeouts are in seconds
  25.  */
  26. #define TRANSMIT_TIMEOUT                60
  27. #define TRANSMIT_BAD_NAK_LIMIT            5
  28. #define RECEIVE_PACKET_TIMEOUT            10
  29. #define RECEIVE_CHARACTER_TIMEOUT        1
  30. #define RECEIVE_CRC_ADAPT_TIMEOUT        3
  31. #define RECEIVE_PACKET_RETRY_LIMIT        6
  32. #define RECEIVE_CRC_ADAPT_RETRY_LIMIT    7
  33.  
  34. #define SOH        '\001'
  35. #define STX        '\002'
  36. #define EOT        '\004'
  37. #define ACK        '\006'
  38. #define NAK        '\025'
  39. #define CTRLZ    '\032'
  40. #define CRC_NAK    'C'
  41.  
  42. #define MESSAGETYPE_HAVE_PACKET    0
  43. #define MESSAGETYPE_WANT_PACKET    1
  44. #define MESSAGETYPE_RETRY        2
  45. #define MESSAGETYPE_FINAL        3
  46.  
  47. #define MAX_REPLY_SIZE        200
  48.  
  49. struct packetMessage {
  50.     msg_header_t    h;
  51. };
  52.  
  53. struct retryMessage {
  54.     msg_header_t    h;
  55.     msg_type_t        t1;
  56.     int                retry;
  57. };
  58.  
  59. struct finalMessage {
  60.     msg_header_t    h;
  61.     msg_type_t        t1;
  62.     char            cbuf[1];
  63. };
  64.  
  65. /*
  66.  * See makecrc.c for details.
  67.  */
  68. #define UPDATE_CRC(c,crc) (crctab[(((crc)>>8) & 0xFF)] ^ (((crc)<<8) | (c)))
  69. static const unsigned short crctab[256] = {
  70.       0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50A5,  0x60C6,  0x70E7,
  71.       0x8108,  0x9129,  0xA14A,  0xB16B,  0xC18C,  0xD1AD,  0xE1CE,  0xF1EF,
  72.       0x1231,  0x0210,  0x3273,  0x2252,  0x52B5,  0x4294,  0x72F7,  0x62D6,
  73.       0x9339,  0x8318,  0xB37B,  0xA35A,  0xD3BD,  0xC39C,  0xF3FF,  0xE3DE,
  74.       0x2462,  0x3443,  0x0420,  0x1401,  0x64E6,  0x74C7,  0x44A4,  0x5485,
  75.       0xA56A,  0xB54B,  0x8528,  0x9509,  0xE5EE,  0xF5CF,  0xC5AC,  0xD58D,
  76.       0x3653,  0x2672,  0x1611,  0x0630,  0x76D7,  0x66F6,  0x5695,  0x46B4,
  77.       0xB75B,  0xA77A,  0x9719,  0x8738,  0xF7DF,  0xE7FE,  0xD79D,  0xC7BC,
  78.       0x48C4,  0x58E5,  0x6886,  0x78A7,  0x0840,  0x1861,  0x2802,  0x3823,
  79.       0xC9CC,  0xD9ED,  0xE98E,  0xF9AF,  0x8948,  0x9969,  0xA90A,  0xB92B,
  80.       0x5AF5,  0x4AD4,  0x7AB7,  0x6A96,  0x1A71,  0x0A50,  0x3A33,  0x2A12,
  81.       0xDBFD,  0xCBDC,  0xFBBF,  0xEB9E,  0x9B79,  0x8B58,  0xBB3B,  0xAB1A,
  82.       0x6CA6,  0x7C87,  0x4CE4,  0x5CC5,  0x2C22,  0x3C03,  0x0C60,  0x1C41,
  83.       0xEDAE,  0xFD8F,  0xCDEC,  0xDDCD,  0xAD2A,  0xBD0B,  0x8D68,  0x9D49,
  84.       0x7E97,  0x6EB6,  0x5ED5,  0x4EF4,  0x3E13,  0x2E32,  0x1E51,  0x0E70,
  85.       0xFF9F,  0xEFBE,  0xDFDD,  0xCFFC,  0xBF1B,  0xAF3A,  0x9F59,  0x8F78,
  86.       0x9188,  0x81A9,  0xB1CA,  0xA1EB,  0xD10C,  0xC12D,  0xF14E,  0xE16F,
  87.       0x1080,  0x00A1,  0x30C2,  0x20E3,  0x5004,  0x4025,  0x7046,  0x6067,
  88.       0x83B9,  0x9398,  0xA3FB,  0xB3DA,  0xC33D,  0xD31C,  0xE37F,  0xF35E,
  89.       0x02B1,  0x1290,  0x22F3,  0x32D2,  0x4235,  0x5214,  0x6277,  0x7256,
  90.       0xB5EA,  0xA5CB,  0x95A8,  0x8589,  0xF56E,  0xE54F,  0xD52C,  0xC50D,
  91.       0x34E2,  0x24C3,  0x14A0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
  92.       0xA7DB,  0xB7FA,  0x8799,  0x97B8,  0xE75F,  0xF77E,  0xC71D,  0xD73C,
  93.       0x26D3,  0x36F2,  0x0691,  0x16B0,  0x6657,  0x7676,  0x4615,  0x5634,
  94.       0xD94C,  0xC96D,  0xF90E,  0xE92F,  0x99C8,  0x89E9,  0xB98A,  0xA9AB,
  95.       0x5844,  0x4865,  0x7806,  0x6827,  0x18C0,  0x08E1,  0x3882,  0x28A3,
  96.       0xCB7D,  0xDB5C,  0xEB3F,  0xFB1E,  0x8BF9,  0x9BD8,  0xABBB,  0xBB9A,
  97.       0x4A75,  0x5A54,  0x6A37,  0x7A16,  0x0AF1,  0x1AD0,  0x2AB3,  0x3A92,
  98.       0xFD2E,  0xED0F,  0xDD6C,  0xCD4D,  0xBDAA,  0xAD8B,  0x9DE8,  0x8DC9,
  99.       0x7C26,  0x6C07,  0x5C64,  0x4C45,  0x3CA2,  0x2C83,  0x1CE0,  0x0CC1,
  100.       0xEF1F,  0xFF3E,  0xCF5D,  0xDF7C,  0xAF9B,  0xBFBA,  0x8FD9,  0x9FF8,
  101.       0x6E17,  0x7E36,  0x4E55,  0x5E74,  0x2E93,  0x3EB2,  0x0ED1,  0x1EF0
  102. };
  103.  
  104. @implementation MiscXmodem
  105.  
  106. /*
  107.  * Send a message to the `top end'
  108.  */
  109. static void
  110. sendPacketMessage (struct MiscXmodem *p, int type)
  111. {
  112.     struct packetMessage msg;
  113.     kern_return_t r;
  114.  
  115.     msg.h.msg_id = type;
  116.     msg.h.msg_size = sizeof msg;
  117.     msg.h.msg_simple = TRUE;
  118.     msg.h.msg_type = MSG_TYPE_NORMAL;
  119.     msg.h.msg_remote_port = p->messagePort;
  120.     msg.h.msg_local_port = p->replyPort;
  121.  
  122.     r = msg_send ((msg_header_t *)&msg, MSG_OPTION_NONE, 0);
  123.     if (r != SEND_SUCCESS)
  124.         cthread_exit (NULL);
  125.  
  126.     /*
  127.      * Wait for the top end to finish with the packet
  128.      */
  129.     r = msg_receive ((msg_header_t *)&msg, MSG_OPTION_NONE, 0);
  130.     if (r != RCV_SUCCESS)
  131.         cthread_exit (NULL);
  132. }
  133.  
  134. static void
  135. sendRetryMessage (struct MiscXmodem *p, int retryNumber)
  136. {
  137.     struct retryMessage msg;
  138.     kern_return_t r;
  139.  
  140.     msg.h.msg_id = MESSAGETYPE_RETRY;
  141.     msg.h.msg_size = sizeof msg;
  142.     msg.h.msg_simple = TRUE;
  143.     msg.h.msg_type = MSG_TYPE_NORMAL;
  144.     msg.h.msg_remote_port = p->messagePort;
  145.     msg.h.msg_local_port = PORT_NULL;
  146.  
  147.     msg.t1.msg_type_name = MSG_TYPE_INTEGER_32;
  148.     msg.t1.msg_type_size = 32;
  149.     msg.t1.msg_type_inline = TRUE;
  150.     msg.t1.msg_type_longform = FALSE;
  151.     msg.t1.msg_type_deallocate = FALSE;
  152.     msg.retry = retryNumber;
  153.  
  154.     r = msg_send ((msg_header_t *)&msg, MSG_OPTION_NONE, 0);
  155.     if (r != SEND_SUCCESS)
  156.         cthread_exit (NULL);
  157. }
  158.  
  159. static void
  160. sendFinalMessage (struct MiscXmodem *p, char *aString)
  161. {
  162.     int length, size;
  163.     struct finalMessage *msgp;
  164.  
  165.     length = strlen (aString) + 1;
  166.     size = sizeof *msgp  - 1 + length;
  167.     msgp = alloca (size);
  168.     msgp->h.msg_id = MESSAGETYPE_FINAL;
  169.     msgp->h.msg_size = size;
  170.     msgp->h.msg_simple = TRUE;
  171.     msgp->h.msg_type = MSG_TYPE_NORMAL;
  172.     msgp->h.msg_remote_port = p->messagePort;
  173.     msgp->h.msg_local_port = PORT_NULL;
  174.  
  175.     msgp->t1.msg_type_name = MSG_TYPE_STRING;
  176.     msgp->t1.msg_type_size = 8 * length;
  177.     msgp->t1.msg_type_inline = TRUE;
  178.     msgp->t1.msg_type_longform = FALSE;
  179.     msgp->t1.msg_type_deallocate = FALSE;
  180.     strcpy (msgp->cbuf, aString);
  181.  
  182.     msg_send ((msg_header_t *)msgp, MSG_OPTION_NONE, 0);
  183.     cthread_exit (0);
  184. }
  185.  
  186. /*
  187.  * Report a failed system call
  188.  */
  189. static void
  190. failedSysCall (struct MiscXmodem *p, const char *aString)
  191. {
  192.     char cbuf[200];
  193.  
  194.     sprintf (cbuf, "%s failed: %s", aString, strerror (cthread_errno ()));
  195.     sendFinalMessage (p, cbuf);
  196. }
  197.  
  198. /*
  199.  * Get characters from TTY
  200.  */
  201. static BOOL
  202. readWithTimeout(struct MiscXmodem *p, unsigned char *cp, int length, int seconds)
  203. {
  204.     int i;
  205.     struct timeval tv;
  206.     fd_set readFdSet;
  207.  
  208.     while (length) {
  209.         tv.tv_usec = 0;
  210.         tv.tv_sec = seconds;
  211.         readFdSet = p->checkFdSet;
  212.         i = select (p->fd+1, &readFdSet, NULL, NULL, &tv);
  213.         if (i < 0)
  214.             failedSysCall (p, "Select");
  215.         if (i == 0)
  216.             return (NO);
  217.         i = read (p->fd, cp, length);
  218.         if (i < 0)
  219.             failedSysCall (p, "Read");
  220.         if (i == 0)
  221.             sendFinalMessage (p, "Unexpected EOF");
  222.         cp += i;
  223.         length -= i;
  224.     }
  225.     return YES;
  226. }
  227.         
  228. static int
  229. getCharacterWithTimeout(struct MiscXmodem *p, int seconds)
  230. {
  231.     unsigned char c;
  232.  
  233.     if (readWithTimeout (p, &c, 1, seconds) == NO)
  234.         return EOF;
  235.     return c;
  236. }
  237.         
  238. /*
  239.  * Clean out TTY read queue
  240.  */
  241. static void
  242. flushTTY (struct MiscXmodem *p)
  243. {
  244.     while (getCharacterWithTimeout (p, 1) != EOF)
  245.         continue;
  246. }
  247.  
  248. /*
  249.  * Send a character
  250.  */
  251. static void
  252. sendChar (struct MiscXmodem *p, unsigned char c)
  253. {
  254.     if (write (p->fd, &c, 1) != 1)
  255.         failedSysCall (p, "Character write");
  256. }
  257.     
  258. static int
  259. computePacketSize (struct MiscXmodem *p)
  260. {
  261.     switch (p->packetBuffer[0]) {
  262.     case SOH:
  263.         return 3 + 128 + (p->useCRC ? 2 : 1);
  264.         break;
  265.  
  266.     case STX:
  267.         return 3 + 1024 + (p->useCRC ? 2 : 1);
  268.         break;
  269.  
  270.     case EOT:
  271.         return 1;
  272.         break;
  273.  
  274.     default:
  275.         return 0;
  276.     }
  277. }
  278.  
  279. /*
  280.  * Get packet
  281.  */
  282. static BOOL
  283. getPacket (struct MiscXmodem *p)
  284. {
  285.     int c;
  286.     int nleft;
  287.  
  288.     c = EOF;
  289.     if (p->isDynamicCheckPacket) {
  290.         int pass;
  291.         for (pass = 0 ; pass < RECEIVE_CRC_ADAPT_RETRY_LIMIT ; pass++) {
  292.             sendChar (p, CRC_NAK);
  293.             c = getCharacterWithTimeout (p, RECEIVE_CRC_ADAPT_TIMEOUT);
  294.             if (c != EOF)
  295.                 break;
  296.         }
  297.         p->isDynamicCheckPacket = NO;
  298.         if (c == EOF) {
  299.             p->useCRC = NO;
  300.             sendChar (p, NAK);
  301.         }
  302.     }
  303.     if (c == EOF) {
  304.         c = getCharacterWithTimeout (p, RECEIVE_PACKET_TIMEOUT);
  305.         if (c == EOF)
  306.             return NO;
  307.     }
  308.     p->packetBuffer[0] = c;
  309.     nleft = computePacketSize (p) - 1;
  310.     if (nleft < 0)
  311.         return NO;
  312.     if (readWithTimeout (p, p->packetBuffer+1, nleft, RECEIVE_CHARACTER_TIMEOUT) == NO)
  313.         return NO;
  314.     return YES;
  315. }
  316.  
  317. /*
  318.  * Compute checksum/CRC
  319.  */
  320. static void
  321. computeChecksum (unsigned char *result, unsigned char *cp, int length)
  322. {
  323.     int checksum = 0;
  324.  
  325.     while (length--)
  326.         checksum += *cp++;
  327.     *result = checksum;
  328. }
  329.  
  330. static void
  331. computeCRC (unsigned char *hi, unsigned char *lo, unsigned char *cp, int length)
  332. {
  333.     int crc = 0;
  334.  
  335.     while (length--) {
  336.         unsigned char c = *cp++;
  337.         crc = UPDATE_CRC (c, crc);
  338.     }
  339.     *hi = crc >> 8;
  340.     *lo = crc;
  341. }
  342.  
  343. /*
  344.  * Check that packet is valid
  345.  */
  346. static BOOL
  347. verifyPacket (struct MiscXmodem *p)
  348. {
  349.     unsigned char *cp = p->packetBuffer;
  350.     int dataCount;
  351.     int packetNumber;
  352.  
  353.     switch (*cp) {
  354.     default:
  355.         return NO;
  356.  
  357.     case EOT:
  358.         return YES;
  359.  
  360.     case SOH:
  361.         dataCount = 128;
  362.         break;
  363.  
  364.     case STX:
  365.         dataCount = 1024;
  366.         break;
  367.     }
  368.     cp++;
  369.     packetNumber = *cp;
  370.     if (packetNumber != (~*(cp+1) & 0xFF))
  371.         return NO;
  372.     if (packetNumber == p->lastPacketNumber)
  373.         return YES;
  374.     if (p->lastPacketNumber < 0)
  375.         p->lastPacketNumber = 0;
  376.     if (packetNumber != ((p->lastPacketNumber + 1) & 0xFF))
  377.         return NO;
  378.     cp += 2;
  379.     if (p->useCRC) {
  380.         unsigned char lo, hi;
  381.         computeCRC (&hi, &lo, cp, dataCount+2);
  382.         if (hi || lo)
  383.             return NO;
  384.     }
  385.     else {
  386.         unsigned char checksum;
  387.         computeChecksum (&checksum, cp, dataCount);
  388.         cp += dataCount;
  389.         if (*cp != checksum)
  390.             return NO;
  391.     }
  392.     p->lastPacketNumber = packetNumber;
  393.     return YES;
  394. }
  395.  
  396. /*
  397.  * Xmodem thread entry point to receive data
  398.  */
  399. static void *
  400. getFile (void *ap)
  401. {
  402.     struct MiscXmodem *p = (struct MiscXmodem *)ap;
  403.     int retry;
  404.  
  405.     for (;;) {
  406.         retry = 0;
  407.         for (;;) {
  408.             if (retry != 0)
  409.                 sendRetryMessage (p, retry);
  410.             if (getPacket (p) && verifyPacket (p)) {
  411.                 sendChar (p, ACK);
  412.                 if (p->packetBuffer[0] == EOT)
  413.                     sendFinalMessage (p, "");
  414.                 sendPacketMessage (p, MESSAGETYPE_HAVE_PACKET);
  415.                 break;
  416.             }
  417.             if (retry++ == RECEIVE_PACKET_RETRY_LIMIT)
  418.                 sendFinalMessage (p, "Too many bad packetts");
  419.             flushTTY (p);
  420.             sendChar (p, NAK);
  421.         }
  422.     }
  423. }
  424.  
  425. /*
  426.  * Send packet
  427.  */
  428. static void
  429. sendPacket (struct MiscXmodem *p)
  430. {
  431.     int pksize;
  432.     int retry;
  433.     int badNakCount;
  434.  
  435.     pksize = computePacketSize (p);
  436.     if (pksize > 1) {
  437.         p->packetBuffer[1] = ++p->lastPacketNumber;
  438.         p->packetBuffer[2] = ~p->packetBuffer[1];
  439.         if (p->useCRC) {
  440.             p->packetBuffer[pksize-2] = 0;
  441.             p->packetBuffer[pksize-1] = 0;
  442.             computeCRC (p->packetBuffer+pksize-2, p->packetBuffer+pksize-1,
  443.                                                 p->packetBuffer+3, pksize-3);
  444.         }
  445.         else {
  446.             computeChecksum (p->packetBuffer+pksize-1, p->packetBuffer+3, pksize-3);
  447.         }
  448.     }
  449.     retry = 0;
  450.     badNakCount = 0;
  451.     for (;;) {
  452.         if (write (p->fd, p->packetBuffer, pksize) != pksize)
  453.             failedSysCall (p, "Packet write");
  454.         switch (getCharacterWithTimeout (p, TRANSMIT_TIMEOUT)) {
  455.         case EOF:
  456.             sendFinalMessage (p, "Timed out waiting for reply");
  457.  
  458.         case ACK:
  459.             return;
  460.  
  461.         case NAK:
  462.             badNakCount = 0;
  463.             break;
  464.  
  465.         default:
  466.             if (++badNakCount == TRANSMIT_BAD_NAK_LIMIT)
  467.                 sendFinalMessage (p, "Too many bad NAK's");
  468.             break;
  469.         }
  470.         sendRetryMessage (p, ++retry);
  471.     }
  472. }
  473.  
  474. /*
  475.  * Wait for receiver to start
  476.  */
  477. static void
  478. awaitReceiver (struct MiscXmodem *p)
  479. {
  480.     for (;;) {
  481.         switch (getCharacterWithTimeout (p, TRANSMIT_TIMEOUT)) {
  482.         case EOF:
  483.             sendFinalMessage (p, "Timed out waiting for receiver to start");
  484.  
  485.         case NAK:
  486.             p->useCRC = NO;
  487.             return;
  488.  
  489.         case CRC_NAK:
  490.             p->useCRC = YES;
  491.             return;
  492.         }
  493.     }
  494. }
  495.  
  496. /*
  497.  * Xmodem thread entry point to send data
  498.  */
  499. static void *
  500. putFile (void *ap)
  501. {
  502.     struct MiscXmodem *p = (struct MiscXmodem *)ap;
  503.  
  504.     p->lastPacketNumber = 0;
  505.     awaitReceiver (p);
  506.     sendPacketMessage (p, MESSAGETYPE_WANT_PACKET);
  507.     for (;;) {
  508.         sendPacket (p);
  509.         if (p->packetBuffer[0] == EOT)
  510.             sendFinalMessage (p, "");
  511.         sendPacketMessage (p, MESSAGETYPE_WANT_PACKET);
  512.     }
  513. }
  514.  
  515. /*
  516.  * `Top End' routines
  517.  */
  518. - (void)sendSmallPacket
  519. {
  520.     int ncopy;
  521.  
  522.     if (smallPacketCount >= 128)
  523.         ncopy = 128;
  524.     else
  525.         ncopy = smallPacketCount;
  526.     packetBuffer[0] = SOH;
  527.     memcpy (packetBuffer+3, smallPacketData+smallPacketIndex, ncopy);
  528.     if (ncopy < 128)
  529.         memset (packetBuffer+3+ncopy, CTRLZ, 128-ncopy);
  530.     smallPacketIndex += ncopy;
  531.     smallPacketCount -= ncopy;
  532.     kbytes += 128.0 / 1024.0;
  533. }
  534.  
  535. /*
  536.  * Handle message from the file transfer thread
  537.  */
  538. - (void)handleMessage:(msg_header_t *)msg
  539. {
  540.     kern_return_t r;
  541.  
  542.     switch (msg->msg_id) {
  543.     case MESSAGETYPE_HAVE_PACKET:
  544.         {
  545.         struct packetMessage *msgp = (struct packetMessage *)msg;
  546.         int count = ((packetBuffer[0] == SOH) ? 128 : 1024);
  547.  
  548.         goodPacketCount++;
  549.         kbytes += (float)count / 1024.0;
  550.         if (isText) {
  551.             unsigned char *cp = packetBuffer + 3;
  552.  
  553.             while (count--) {
  554.                 if (*cp == CTRLZ)
  555.                     break;
  556.                 if (*cp != '\r')
  557.                     NXPutc (stream, *cp);
  558.                 cp++;
  559.             }
  560.         }
  561.         else {
  562.             NXWrite (stream, packetBuffer+3, count);
  563.         }
  564.         msgp->h.msg_local_port = PORT_NULL;
  565.         r = msg_send ((msg_header_t *)msgp, MSG_OPTION_NONE, 0);
  566.         if (r != SEND_SUCCESS)
  567.             abort ();
  568.         if ([delegate respondsTo:@selector(didTransferPacket:kbytes:)])
  569.             [delegate didTransferPacket:goodPacketCount kbytes:kbytes];
  570.         }
  571.         break;
  572.  
  573.     case MESSAGETYPE_WANT_PACKET:
  574.         {
  575.         struct packetMessage *msgp = (struct packetMessage *)msg;
  576.         int nread;
  577.         int fullPacketSize = use1kPackets ? 1024 : 128;
  578.         if (goodPacketCount && [delegate respondsTo:@selector(didTransferPacket:kbytes:)])
  579.             [delegate didTransferPacket:goodPacketCount kbytes:kbytes];
  580.         if (smallPacketCount) {
  581.             [self sendSmallPacket];
  582.         }
  583.         else {
  584.             if (isText) {
  585.                 unsigned char *cp = packetBuffer + 3;
  586.                 int c;
  587.  
  588.                 nread = 0;
  589.                 while (nread < fullPacketSize) {
  590.                     if (newlineFlag) {
  591.                         c = '\n';
  592.                         newlineFlag = NO;
  593.                     }
  594.                     else {
  595.                         c = NXGetc (stream);
  596.                         if (c == '\n') {
  597.                             newlineFlag = YES;
  598.                             c = '\r';
  599.                         }
  600.                     }
  601.                     if (c == EOF)
  602.                         break;
  603.                     *cp++ = c;
  604.                     nread++;
  605.                 }
  606.             }
  607.             else {
  608.                 nread = NXRead (stream, packetBuffer+3, fullPacketSize);
  609.             }
  610.             if (nread == 0) {
  611.                 packetBuffer[0] = EOT;
  612.             }
  613.             else if (nread == fullPacketSize) {
  614.                 packetBuffer[0] = use1kPackets ? STX : SOH;
  615.                 kbytes += use1kPackets ? 1.0 : 1.0 / 8.0;
  616.             }
  617.             else {
  618.                 smallPacketIndex = 0;
  619.                 smallPacketCount = nread;
  620.                 memcpy (smallPacketData, packetBuffer+3, nread);
  621.                 [self sendSmallPacket];
  622.             }
  623.         }
  624.         msgp->h.msg_local_port = PORT_NULL;
  625.         r = msg_send ((msg_header_t *)msgp, MSG_OPTION_NONE, 0);
  626.         if (r != SEND_SUCCESS)
  627.             abort ();
  628.         goodPacketCount++;
  629.         }
  630.         break;
  631.  
  632.     case MESSAGETYPE_RETRY:
  633.         {
  634.         struct retryMessage *msgp = (struct retryMessage *)msg;
  635.         if ([delegate respondsTo:@selector(didRetryNumber:)])
  636.             [delegate didRetryNumber:msgp->retry];
  637.         }
  638.         break;
  639.  
  640.     case MESSAGETYPE_FINAL:
  641.         {
  642.         struct finalMessage *msgp = (struct finalMessage *)msg;
  643.         DPSRemovePort (messagePort);
  644.         port_deallocate (task_self(), messagePort);
  645.         port_deallocate (task_self(), replyPort);
  646.         cthread_join (xmodemThread);
  647.         isActive = NO;
  648.         if ([delegate respondsTo:@selector(didFinish:)])
  649.             [delegate didFinish:msgp->cbuf];
  650.         }
  651.         if (serialPortWasSuspended == NO)
  652.             [serialPort continue];
  653.         break;
  654.     }
  655. }
  656.  
  657. /*
  658.  * Get message from `Bottom end'
  659.  */
  660. static void
  661. messageHandler (msg_header_t *msg, void *ap)
  662. {
  663.     [(MiscXmodem *)ap handleMessage:msg];
  664. }
  665.  
  666. /*
  667.  * Transfer file in appropriate direction
  668.  */
  669. - transferBetweenStream:(NXStream *)aStream serialPort:aSerialPort toStream:(BOOL)toStream
  670. {
  671.     if (isActive)
  672.         return nil;
  673.     if (isDynamicCheck) {
  674.         useCRC = YES;
  675.         isDynamicCheckPacket = YES;
  676.     }
  677.     else  {
  678.         isDynamicCheckPacket = NO;
  679.     }
  680.     isActive = YES;
  681.     newlineFlag = NO;
  682.     lastPacketNumber = -1;
  683.     goodPacketCount = 0;
  684.     smallPacketIndex = smallPacketCount = 0;
  685.     kbytes = 0;
  686.  
  687.     stream = aStream;
  688.     serialPort = aSerialPort;
  689.     if ((serialPortWasSuspended = [serialPort suspended]) == NO)
  690.         [serialPort suspend];
  691.     fd = [serialPort filedes];
  692.     FD_ZERO (&checkFdSet);
  693.     FD_SET (fd, &checkFdSet);
  694.  
  695.     port_allocate (task_self(), &messagePort);
  696.     port_allocate (task_self(), &replyPort);
  697.     DPSAddPort (messagePort, messageHandler, MAX_REPLY_SIZE, self, NX_BASETHRESHOLD);
  698.     if (toStream)
  699.         xmodemThread = cthread_fork (getFile, self);
  700.     else
  701.         xmodemThread = cthread_fork (putFile, self);
  702.     return self;
  703. }
  704.     
  705. - sendToStream:(NXStream *)aStream fromSerialPort:aSerialPort
  706. {
  707.     return [self transferBetweenStream:aStream serialPort:aSerialPort toStream:YES];
  708. }
  709.  
  710. - sendToSerialPort:aSerialPort fromStream:(NXStream *)aStream
  711. {
  712.     return [self transferBetweenStream:aStream serialPort:aSerialPort toStream:NO];
  713. }
  714.  
  715. - init
  716. {
  717.     [super init];
  718.     isText = YES;
  719.     use1kPackets = YES;
  720.     isDynamicCheck = YES;
  721.     useCRC = YES;
  722.     return self;
  723. }
  724.  
  725. - free
  726. {
  727.     if (isActive) {
  728.         DPSRemovePort (messagePort);
  729.         port_deallocate (task_self(), messagePort);
  730.         port_deallocate (task_self(), replyPort);
  731.  
  732.         /*
  733.          * I hope this works.  I wish they had supplied `cthread_terminate'.
  734.          */
  735.         cthread_abort (xmodemThread);
  736.         cthread_join (xmodemThread);
  737.  
  738.         if (serialPortWasSuspended == NO)
  739.             [serialPort continue];
  740.     }
  741.     [super free];
  742.     return self;
  743. }
  744.  
  745. - setFileTypeText:(BOOL)flag        { isText = flag; return self; }
  746. - xmodemSet1kPackets:(BOOL)flag            { use1kPackets = flag; return self; }
  747. - xmodemSetCheckTypeDynamic:(BOOL)flag    { isDynamicCheck = flag; return self; }
  748. - xmodemSetCheckTypeCRC:(BOOL)flag        { useCRC = flag; return self; }
  749. - (BOOL)xmodemIsActive                { return isActive; }
  750. - (BOOL)xmodemIsFileTypeText        { return isText; }
  751. - (BOOL)xmodemIs1kPackets            { return use1kPackets; }
  752. - (BOOL)xmodemIsCheckTypeDynamic    { return isDynamicCheck; }
  753. - (BOOL)xmodemIsCheckTypeCRC        { return useCRC; }
  754.  
  755. /*
  756.  * Delegate methods
  757.  */
  758. - setDelegate:id
  759. {
  760.     delegate = id;
  761.     return self;
  762. }
  763.  
  764. - delegate
  765. {
  766.     return delegate;
  767. }
  768.  
  769. - didTransferPacket:(int)anInt kbytes:(float)aFloat { return self; }
  770. - didRetryNumber:(int)anInt { return self; }
  771. - didFinish:(const char *)failureCode { return self; }
  772.  
  773. @end
  774.