rx: strip padding from last block. closes bug 501.
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 2 Aug 2009 17:45:31 +0000 (19:45 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 2 Aug 2009 17:45:31 +0000 (19:45 +0200)
function                                             old     new   delta
rx_main                                              876     974     +98

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
miscutils/rx.c
testsuite/rx.tests [new file with mode: 0644]

index 94eb4522d33af5c379d4e5613ffc65c8de387396..3a8b6a8feac9655f07a9d8cf4ae49217f9c7e69e 100644 (file)
@@ -1,15 +1,12 @@
 /* vi: set sw=4 ts=4: */
-/*-------------------------------------------------------------------------
- * Filename:      xmodem.c
+/*
  * Copyright:     Copyright (C) 2001, Hewlett-Packard Company
  * Author:        Christopher Hoover <ch@hpl.hp.com>
  * Description:   xmodem functionality for uploading of kernels
  *                and the like
  * Created at:    Thu Dec 20 01:58:08 PST 2001
- *-----------------------------------------------------------------------*/
-/*
- * xmodem.c: xmodem functionality for uploading of kernels and
- *            the like
+ *
+ * xmodem functionality for uploading of kernels and the like
  *
  * Copyright (C) 2001 Hewlett-Packard Laboratories
  *
@@ -26,6 +23,7 @@
 #define ACK 0x06
 #define NAK 0x15
 #define BS  0x08
+#define PAD 0x1A
 
 /*
 Cf:
@@ -44,69 +42,93 @@ Cf:
 
 static int read_byte(unsigned timeout)
 {
-       char buf[1];
+       unsigned char buf;
        int n;
 
        alarm(timeout);
        /* NOT safe_read! We want ALRM to interrupt us */
-       n = read(read_fd, buf, 1);
+       n = read(read_fd, &buf, 1);
        alarm(0);
        if (n == 1)
-               return (unsigned char)buf[0];
+               return buf;
        return -1;
 }
 
 static int receive(/*int read_fd, */int file_fd)
 {
        unsigned char blockBuf[1024];
+       unsigned blockLength = 0;
        unsigned errors = 0;
        unsigned wantBlockNo = 1;
        unsigned length = 0;
        int do_crc = 1;
-       char nak = 'C';
+       char reply_char;
        unsigned timeout = TIMEOUT_LONG;
 
        /* Flush pending input */
        tcflush(read_fd, TCIFLUSH);
 
        /* Ask for CRC; if we get errors, we will go with checksum */
-       full_write(write_fd, &nak, 1);
+       reply_char = 'C';
+       full_write(write_fd, &reply_char, 1);
 
        for (;;) {
                int blockBegin;
                int blockNo, blockNoOnesCompl;
-               int blockLength;
-               int cksum_crc;  /* cksum OR crc */
+               int cksum_or_crc;
                int expected;
-               int i,j;
+               int i, j;
 
                blockBegin = read_byte(timeout);
                if (blockBegin < 0)
                        goto timeout;
 
+               /* If last block, remove padding */
+               if (blockBegin == EOT) {
+                       /* Data blocks can be padded with ^Z characters */
+                       /* This code tries to detect and remove them */
+                       if (blockLength >= 3
+                        && blockBuf[blockLength - 1] == PAD
+                        && blockBuf[blockLength - 2] == PAD
+                        && blockBuf[blockLength - 3] == PAD
+                       ) {
+                               while (blockLength
+                                  && blockBuf[blockLength - 1] == PAD
+                               ) {
+                                       blockLength--;
+                               }
+                       }
+               }
+               /* Write previously received block */
+               if (blockLength) {
+                       errno = 0;
+                       if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
+                               bb_perror_msg("can't write to file");
+                               goto fatal;
+                       }
+               }
+
                timeout = TIMEOUT;
-               nak = NAK;
+               reply_char = NAK;
 
                switch (blockBegin) {
                case SOH:
                case STX:
                        break;
-
                case EOT:
-                       nak = ACK;
-                       full_write(write_fd, &nak, 1);
+                       reply_char = ACK;
+                       full_write(write_fd, &reply_char, 1);
                        return length;
-
                default:
                        goto error;
                }
 
-               /* block no */
+               /* Block no */
                blockNo = read_byte(TIMEOUT);
                if (blockNo < 0)
                        goto timeout;
 
-               /* block no one's compliment */
+               /* Block no, in one's complement form */
                blockNoOnesCompl = read_byte(TIMEOUT);
                if (blockNoOnesCompl < 0)
                        goto timeout;
@@ -126,15 +148,15 @@ static int receive(/*int read_fd, */int file_fd)
                }
 
                if (do_crc) {
-                       cksum_crc = read_byte(TIMEOUT);
-                       if (cksum_crc < 0)
+                       cksum_or_crc = read_byte(TIMEOUT);
+                       if (cksum_or_crc < 0)
                                goto timeout;
-                       cksum_crc = (cksum_crc << 8) | read_byte(TIMEOUT);
-                       if (cksum_crc < 0)
+                       cksum_or_crc = (cksum_or_crc << 8) | read_byte(TIMEOUT);
+                       if (cksum_or_crc < 0)
                                goto timeout;
                } else {
-                       cksum_crc = read_byte(TIMEOUT);
-                       if (cksum_crc < 0)
+                       cksum_or_crc = read_byte(TIMEOUT);
+                       if (cksum_or_crc < 0)
                                goto timeout;
                }
 
@@ -155,9 +177,9 @@ static int receive(/*int read_fd, */int file_fd)
                                expected = expected ^ blockBuf[i] << 8;
                                for (j = 0; j < 8; j++) {
                                        if (expected & 0x8000)
-                                               expected = expected << 1 ^ 0x1021;
+                                               expected = (expected << 1) ^ 0x1021;
                                        else
-                                               expected = expected << 1;
+                                               expected = (expected << 1);
                                }
                        }
                        expected &= 0xffff;
@@ -166,25 +188,19 @@ static int receive(/*int read_fd, */int file_fd)
                                expected += blockBuf[i];
                        expected &= 0xff;
                }
-               if (cksum_crc != expected) {
+               if (cksum_or_crc != expected) {
                        bb_error_msg(do_crc ? "crc error, expected 0x%04x, got 0x%04x"
                                           : "checksum error, expected 0x%02x, got 0x%02x",
-                                           expected, cksum_crc);
+                                       expected, cksum_or_crc);
                        goto error;
                }
 
                wantBlockNo++;
                length += blockLength;
-
-               errno = 0;
-               if (full_write(file_fd, blockBuf, blockLength) != blockLength) {
-                       bb_perror_msg("can't write to file");
-                       goto fatal;
-               }
  next:
                errors = 0;
-               nak = ACK;
-               full_write(write_fd, &nak, 1);
+               reply_char = ACK;
+               full_write(write_fd, &reply_char, 1);
                continue;
  error:
  timeout:
@@ -192,9 +208,9 @@ static int receive(/*int read_fd, */int file_fd)
                if (errors == MAXERRORS) {
                        /* Abort */
 
-                       /* if were asking for crc, try again w/o crc */
-                       if (nak == 'C') {
-                               nak = NAK;
+                       /* If were asking for crc, try again w/o crc */
+                       if (reply_char == 'C') {
+                               reply_char = NAK;
                                errors = 0;
                                do_crc = 0;
                                goto timeout;
@@ -209,7 +225,7 @@ static int receive(/*int read_fd, */int file_fd)
                /* Flush pending input */
                tcflush(read_fd, TCIFLUSH);
 
-               full_write(write_fd, &nak, 1);
+               full_write(write_fd, &reply_char, 1);
        } /* for (;;) */
 }
 
diff --git a/testsuite/rx.tests b/testsuite/rx.tests
new file mode 100644 (file)
index 0000000..417f04b
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+# Copyright 2009 by Denys Vlasenko
+# Licensed under GPL v2, see file LICENSE for details.
+
+. testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+# Simple one-block file transfer
+# rx => 'C'
+# rx <= SOH <blockno> <255-blockno> <128 byte padded with x1A> <crc> <crc>
+# rx => ACK
+# rx <= EOT
+# rx => ACK
+testing "rx" \
+       "rx rx.OUTFILE | hexdump -vC && cat rx.OUTFILE" \
+"\
+00000000  43 06 06                                          |C..|\n\
+00000003\n\
+???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????" \
+       "" "\1\1\376\
+???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????\
+\x1A\x1A\x1A\x1A\x1A\x4B\xB0\4"
+
+rm -f rx.OUTFILE 2>/dev/null
+
+exit $FAILCOUNT