00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00032 #include "../../common/common.h"
00033 #include "../ymodem_tx.h"
00034 #include "../ymodem_rx.h"
00035
00036
00037 #include <stdlib.h>
00038 #include <stdarg.h>
00039 #include <stdio.h>
00040 #include <string.h>
00041
00042
00089 #ifdef LINUX
00090 #define DEFAULT_PORT 0
00091 #else
00092 #define DEFAULT_PORT 1
00093 #endif
00094 #define DEFAULT_BAUD 115200
00095 #define DEFAULT_TIMEOUT 30
00096 #define DEFAULT_LOG_TIMEOUT 10
00097 #define MAX_LOG_END_STRING_LENGTH 128
00100 const char* ProgramName;
00101 unsigned PortNum = DEFAULT_PORT;
00102 unsigned PortBaud = DEFAULT_BAUD;
00103 unsigned Timeout = DEFAULT_TIMEOUT*1000;
00104 bool XModemFlag = false;
00105 bool CrcFlag = false;
00106 bool KModeFlag = false;
00107 bool GModeFlag = false;
00108 bool SendFlag = false;
00109 bool ReceiveFlag = false;
00110 const char* FileName = 0;
00111 const char* DstFileName = 0;
00112 const char* LogFileName = 0;
00113 unsigned LogTimeout = DEFAULT_LOG_TIMEOUT*1000;
00114 const char* LogEndString = 0;
00115 size_t LogEndStringLength = 0;
00116 bool LogEcho = false;
00122 enum Error
00123 {
00124 ErrorNoMemory = -500,
00125 ErrorLogFileError = -501
00126 };
00127
00128 #define STRINGIFY2(a) #a
00129 #define STRINGIFY(a) STRINGIFY2(a)
00134 void Help()
00135 {
00136 printf( "Usage: %s [OPTION]... FILE [DST-FILE]\n"
00137 "Tranfer a file using X-Modem or Y-Modem protocols\n\n"
00138 " -s[y|x|k] Send file.\n"
00139 " If 'y' is specifieied Y-Modem protocol is used. (Default)\n"
00140 " If 'x' is specifieied X-Modem protocol is used.\n"
00141 " If 'k' is specifieied X-Modem 1K protocol is used.\n"
00142 " -r[y|g|x|c] Receive file.\n"
00143 " If 'y' is specifieied Y-Modem protocol is used. (Default)\n"
00144 " If 'g' is specifieied Y-Modem G protocol is used.\n"
00145 " If 'x' is specifieied X-Modem protocol is used.\n"
00146 " If 'c' is specifieied X-Modem CRC protocol is used.\n"
00147 " -pPORT Use communications port PORT. Default is " STRINGIFY(DEFAULT_PORT) "\n"
00148 " -bBAUD Set baud rate for port to BAUD. Default is " STRINGIFY(DEFAULT_BAUD) "\n"
00149 " -tTIMEOUT Set timeout for transfer start (in seconds). Default is " STRINGIFY(DEFAULT_TIMEOUT) "\n"
00150 " -lfLOG Capture log after transfer and write it to file LOG\n"
00151 " -le Echo the captured log to STDOUT.\n"
00152 " -ltLOGTIMEOUT Timeout for log capture (in seconds). Default is " STRINGIFY(DEFAULT_LOG_TIMEOUT) "\n"
00153 " -lxSTRING End log capture when STRING is received\n"
00154 "\n"
00155 "FILE is optional when receiving a file by Y-Modem: options -ry or -rg\n"
00156 "DST-FILE is the name sent to receiver when using Y-Modem. Default is FILE.\n"
00157 ,ProgramName
00158 );
00159 exit(1);
00160 }
00161
00162
00170 void UsageError(const char *format, ...)
00171 {
00172 va_list args;
00173 va_start(args,format);
00174 vfprintf(stderr,format,args);
00175 va_end(args);
00176 fprintf(stderr,"\nTry '%s -h' for more information.\n",ProgramName);
00177 exit(1);
00178 }
00179
00180
00189 void Error(int error,const char *format, ...)
00190 {
00191 va_list args;
00192 va_start(args,format);
00193 vfprintf(stderr,format,args);
00194 va_end(args);
00195 if(error<0)
00196 fprintf(stderr,". Error %d",error);
00197 fprintf(stderr,"\n");
00198 exit(error);
00199 }
00200
00201
00212 bool GetIntArg(const char* argStr, unsigned& arg)
00213 {
00214 char* argEnd;
00215 unsigned long val = strtoul(argStr,&argEnd,10);
00216 if(*argEnd)
00217 return false;
00218 arg = (unsigned)val;
00219 return true;
00220 }
00221
00222
00234 bool GetTimeoutArg(const char* argStr, unsigned& arg)
00235 {
00236 if(!GetIntArg(argStr,arg))
00237 return false;
00238
00239 if(arg<(~(unsigned)0)/1000u)
00240 arg *= 1000;
00241 else
00242 arg = ~(unsigned)0;
00243 return true;
00244 }
00245
00246
00253 void ParseArgs(int argc, char** argv)
00254 {
00255 ProgramName = *argv;
00256
00257
00258 while(--argc)
00259 {
00260 char* arg = *++argv;
00261 if(arg[0]=='"')
00262 {
00263
00264 int len = strlen(arg);
00265 if(arg[len-1]=='"')
00266 {
00267 arg[len-1] = 0;
00268 ++arg;
00269 *argv = arg;
00270 }
00271 }
00272 if(arg[0]!='-')
00273 {
00274
00275 if(DstFileName)
00276 UsageError("Too many file arguments");
00277 if(FileName)
00278 DstFileName = arg;
00279 else
00280 FileName = arg;
00281 continue;
00282 }
00283
00284 ++arg;
00285 switch(*arg++)
00286 {
00287 case 'p':
00288 if(!GetIntArg(arg,PortNum))
00289 UsageError("Invalid PORT argument");
00290 break;
00291
00292 case 'b':
00293 if(!GetIntArg(arg,PortBaud))
00294 UsageError("Invalid BAUD argument");
00295 break;
00296
00297 case 't':
00298 if(!GetTimeoutArg(arg,Timeout))
00299 UsageError("Invalid TIMEOUT argument");
00300 break;
00301
00302 case 'h':
00303 Help();
00304
00305 case 's':
00306 SendFlag = true;
00307 XModemFlag = false;
00308 if(arg[0]!=0)
00309 {
00310 if(arg[1]!=0)
00311 goto invalid;
00312 if(arg[0]=='y')
00313 ;
00314 else if(arg[0]=='x')
00315 XModemFlag = true, KModeFlag = false;
00316 else if(arg[0]=='k')
00317 XModemFlag = true, KModeFlag = true;
00318 else
00319 goto invalid;
00320 }
00321 break;
00322
00323 case 'r':
00324 ReceiveFlag = true;
00325 XModemFlag = false;
00326 GModeFlag = false;
00327 if(arg[0]!=0)
00328 {
00329 if(arg[1]!=0)
00330 goto invalid;
00331 if(arg[0]=='y')
00332 ;
00333 else if(arg[0]=='g')
00334 GModeFlag = true;
00335 else if(arg[0]=='x')
00336 CrcFlag = false, XModemFlag = true;
00337 else if(arg[0]=='c')
00338 CrcFlag = true, XModemFlag = true;
00339 else
00340 goto invalid;
00341 }
00342 break;
00343
00344 case 'l':
00345 switch(*arg++)
00346 {
00347 case 'f':
00348 LogFileName = arg;
00349 break;
00350 case 'e':
00351 LogEcho = true;
00352 break;
00353 case 't':
00354 if(!GetTimeoutArg(arg,LogTimeout))
00355 UsageError("Invalid LOGTIMEOUT argument");
00356 break;
00357 case 'x':
00358 LogEndStringLength = strlen(arg);
00359 LogEndString = arg;
00360 if(LogEndStringLength>MAX_LOG_END_STRING_LENGTH)
00361 UsageError("String too long in -lx option. Max length is " STRINGIFY(MAX_LOG_END_STRING_LENGTH) );
00362 break;
00363 default:
00364 goto invalid;
00365 }
00366 break;
00367
00368 default:
00369 goto invalid;
00370 }
00371 continue;
00372 }
00373 return;
00374
00375 invalid:
00376 UsageError("Invalid option '%s'",*argv);
00377 }
00378
00379
00383 class InFile : public YModemTx::InStream
00384 {
00385 public:
00386 InFile()
00387 : File(0)
00388 {
00389 }
00390
00398 int Open(const char* fileName)
00399 {
00400 File = fopen(fileName,"rb");
00401 if(!File)
00402 return YModemTx::ErrorInputStreamError;
00403
00404 if(fseek(File,0,SEEK_END))
00405 {
00406 fclose(File);
00407 return YModemTx::ErrorInputStreamError;
00408 }
00409 TotalSize = ftell(File);
00410 if(fseek(File,0,SEEK_SET))
00411 {
00412 fclose(File);
00413 return YModemTx::ErrorInputStreamError;
00414 }
00415 TransferredSize = 0;
00416 return 0;
00417 }
00418
00422 void Close()
00423 {
00424 if(File)
00425 {
00426 fclose(File);
00427 File = 0;
00428 }
00429 }
00430
00436 inline size_t Size()
00437 {
00438 return TotalSize;
00439 }
00440
00449 int In(uint8_t* data, size_t size)
00450 {
00451 int percent = TotalSize ? ((uint64_t)TransferredSize*(uint64_t)100)/(uint64_t)TotalSize : 0;
00452 printf("%8d bytes of %d - %3d%%\r",TransferredSize, TotalSize, percent);
00453 fflush(stdout);
00454 size=fread(data,sizeof(uint8_t),size,File);
00455 if(size)
00456 {
00457 TransferredSize += size;
00458 return size;
00459 }
00460 if(TransferredSize!=TotalSize)
00461 return YModemTx::ErrorInputStreamError;
00462 return 0;
00463 }
00464 private:
00465 FILE* File;
00466 size_t TotalSize;
00467 size_t TransferredSize;
00468 };
00469
00470
00474 class OutFile : public YModemRx::OutStream
00475 {
00476 public:
00477 OutFile()
00478 : File(0)
00479 {
00480 }
00481
00493 int Open(const char* fileName, size_t size)
00494 {
00495 if(File)
00496 {
00497 if(TransferredSize)
00498 return YModemRx::ErrorOutputStreamError;
00499 }
00500 else
00501 {
00502 File = fopen(fileName,"w+b");
00503 if(!File)
00504 return YModemRx::ErrorOutputStreamError;
00505 printf("Receiving %s...\n",fileName);
00506 }
00507 TotalSize = size;
00508 TransferredSize = 0;
00509 return 0;
00510 }
00511
00515 void Close()
00516 {
00517 if(File)
00518 {
00519 fclose(File);
00520 File = 0;
00521 }
00522 }
00523
00532 int Out(const uint8_t* data, size_t size)
00533 {
00534 if(!File)
00535 return YModemRx::ErrorOutputStreamError;
00536
00537 if(TotalSize)
00538 {
00539 int percent = TotalSize ? ((uint64_t)TransferredSize*(uint64_t)100)/(uint64_t)TotalSize : 0;
00540 printf("%8d bytes of %d - %3d%%\r",TransferredSize, TotalSize, percent);
00541 fflush(stdout);
00542 }
00543 else
00544 {
00545 printf("%8d bytes\r",TransferredSize);
00546 fflush(stdout);
00547 }
00548
00549 if(TotalSize)
00550 {
00551 size_t maxSize = TotalSize-TransferredSize;
00552 if(size>maxSize)
00553 size = maxSize;
00554 if(!maxSize)
00555 return 0;
00556 }
00557
00558 if(size!=fwrite(data,sizeof(uint8_t),size,File))
00559 return YModemRx::ErrorOutputStreamError;
00560 TransferredSize += size;
00561 return size;
00562 }
00563 private:
00564 FILE* File;
00565 size_t TotalSize;
00566 size_t TransferredSize;
00567 };
00568
00569
00570 FILE* LogFile = 0;
00579 void CaptureLog(SerialPort* port)
00580 {
00581 printf("Capturing log to %s...\n",LogFileName);
00582 fflush(stdout);
00583 uint8_t logBuffer[1024+MAX_LOG_END_STRING_LENGTH];
00584 size_t bufferOffset = 0;
00585 for(;;)
00586 {
00587 uint8_t* start = logBuffer+bufferOffset;
00588 uint8_t* end = logBuffer+sizeof(logBuffer);
00589 size_t length = end-start;
00590 int result = port->In(start,length,LogTimeout);
00591 if(!result)
00592 {
00593 fclose(LogFile);
00594 printf("Log capture timeout.\n");
00595 return;
00596 }
00597 if(result<0)
00598 {
00599 fclose(LogFile);
00600 Error(result,"Error during log capture");
00601 }
00602
00603
00604 length = result;
00605 end = start+length;
00606 if(fwrite(start,1,length,LogFile)!=length)
00607 {
00608 fclose(LogFile);
00609 Error(ErrorLogFileError,"File error during log capture");
00610 }
00611 fflush(LogFile);
00612
00613 if(LogEcho)
00614 {
00615 #ifndef WIN32
00616 fwrite(start,1,length,stdout);
00617 #else
00618
00619 for(size_t i=0; i<length; ++i)
00620 if(start[i]!=13)
00621 fwrite(start+i,1,1,stdout);
00622 #endif
00623 fflush(stdout);
00624 }
00625
00626
00627 if(!LogEndStringLength)
00628 continue;
00629 uint8_t* endString = (uint8_t*)LogEndString;
00630 uint8_t firstEndChar = endString[0];
00631 size_t endStringLength = LogEndStringLength;
00632 uint8_t* scanEnd = end-endStringLength;
00633 uint8_t* scan = logBuffer;
00634 while(scan<=scanEnd)
00635 {
00636 match_next: if(*scan++!=firstEndChar)
00637 continue;
00638
00639 uint8_t* ptr = scan;
00640 size_t endStringMatch = 0;
00641 while(++endStringMatch<endStringLength)
00642 if(*ptr++!=endString[endStringMatch])
00643 goto match_next;
00644
00645 fclose(LogFile);
00646 printf("Log capture termination string found.\n");
00647 return;
00648 }
00649 bufferOffset = end-scan;
00650 memcpy(logBuffer,scan,bufferOffset);
00651 }
00652 }
00653
00654
00660 void Send(SerialPort* port)
00661 {
00662 if(!FileName)
00663 UsageError("File argument missing");
00664
00665 if(!DstFileName)
00666 DstFileName = FileName;
00667
00668 InFile source;
00669 int error = source.Open(FileName);
00670 if(error)
00671 Error(error,"Can't open file '%s'",FileName);
00672
00673 printf("Sending %s...\n",FileName);
00674 YModemTx ymodem(*port);
00675 if(XModemFlag)
00676 error = ymodem.SendX(source,Timeout,KModeFlag);
00677 else
00678 error = ymodem.SendY(DstFileName,source.Size(),source,Timeout);
00679 if(error)
00680 Error(error,"Error during file transfer");
00681
00682 printf("\nSent OK\n");
00683 source.Close();
00684 }
00685
00686
00692 void Receive(SerialPort* port)
00693 {
00694 OutFile destination;
00695 YModemRx ymodem(*port);
00696 int error;
00697 if(XModemFlag)
00698 {
00699 if(!FileName)
00700 UsageError("File argument missing");
00701 error = destination.Open(FileName,0);
00702 if(error)
00703 Error(error,"Can't create file '%s'",FileName);
00704 error = ymodem.ReceiveX(destination,Timeout,CrcFlag);
00705 }
00706 else
00707 {
00708 if(FileName)
00709 {
00710 error = destination.Open(FileName,0);
00711 if(error)
00712 Error(error,"Can't create file '%s'",FileName);
00713 }
00714 error = ymodem.ReceiveY(destination,Timeout,GModeFlag);
00715 }
00716 if(error)
00717 Error(error,"Error during file transfer");
00718
00719 printf("\nReceived OK\n");
00720 destination.Close();
00721 }
00722
00723
00732 int main(int argc, char** argv)
00733 {
00734 ParseArgs(argc,argv);
00735
00736 SerialPort* port = SerialPort::New();
00737 if(!port)
00738 Error(ErrorNoMemory,"Can't create port");
00739
00740 int error = port->Open(PortNum);
00741 if(error)
00742 Error(error,"Can't open port");
00743
00744 error = port->Initialise(PortBaud);
00745 if(error)
00746 Error(error,"Can't initialise port");
00747
00748 if(ReceiveFlag)
00749 Receive(port);
00750 else
00751 {
00752 if(LogFileName)
00753 {
00754 LogFile = fopen(LogFileName,"w+b");
00755 if(!LogFile)
00756 Error(ErrorLogFileError,"Can't create LogFile file '%s'",LogFileName);
00757 }
00758 if(SendFlag || FileName)
00759 Send(port);
00760 if(LogFileName)
00761 CaptureLog(port);
00762 }
00763
00764 port->Close();
00765 delete port;
00766
00767 return 0;
00768 }
00769
00770
00772