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 #define DEFAULT_PORT 1
00090 #define DEFAULT_BAUD 115200
00091 #define DEFAULT_TIMEOUT 30
00092 #define DEFAULT_LOG_TIMEOUT 10
00093 #define MAX_LOG_END_STRING_LENGTH 128
00096 const char* ProgramName;
00097 unsigned PortNum = DEFAULT_PORT;
00098 unsigned PortBaud = DEFAULT_BAUD;
00099 unsigned Timeout = DEFAULT_TIMEOUT*1000;
00100 bool XModemFlag = false;
00101 bool CrcFlag = false;
00102 bool KModeFlag = false;
00103 bool GModeFlag = false;
00104 bool SendFlag = false;
00105 bool ReceiveFlag = false;
00106 const char* FileName = 0;
00107 const char* DstFileName = 0;
00108 const char* LogFileName = 0;
00109 unsigned LogTimeout = DEFAULT_LOG_TIMEOUT*1000;
00110 const char* LogEndString = 0;
00111 size_t LogEndStringLength = 0;
00112 bool LogEcho = false;
00116 enum Error
00117 {
00118 ErrorNoMemory = -500,
00119 ErrorLogFileError = -501
00120 };
00121
00122 #define STRINGIFY2(a) #a
00123 #define STRINGIFY(a) STRINGIFY2(a)
00128 void Help()
00129 {
00130 printf( "Usage: %s [OPTION]... FILE [DST-FILE]\n"
00131 "Tranfer a file using X-Modem or Y-Modem protocols\n\n"
00132 " -s[y|x|k] Send file.\n"
00133 " If 'y' is specifieied Y-Modem protocol is used. (Default)\n"
00134 " If 'x' is specifieied X-Modem protocol is used.\n"
00135 " If 'k' is specifieied X-Modem 1K protocol is used.\n"
00136 " -r[y|g|x|c] Receive file.\n"
00137 " If 'y' is specifieied Y-Modem protocol is used. (Default)\n"
00138 " If 'g' is specifieied Y-Modem G protocol is used.\n"
00139 " If 'x' is specifieied X-Modem protocol is used.\n"
00140 " If 'c' is specifieied X-Modem CRC protocol is used.\n"
00141 " -pPORT Use communications port PORT. Default is " STRINGIFY(DEFAULT_PORT) "\n"
00142 " -bBAUD Set baud rate for port to BAUD. Default is " STRINGIFY(DEFAULT_BAUD) "\n"
00143 " -tTIMEOUT Set timeout for transfer start (in seconds). Default is " STRINGIFY(DEFAULT_TIMEOUT) "\n"
00144 " -lfLOG Capture log after transfer and write it to file LOG\n"
00145 " -le Echo the captured log to STDOUT.\n"
00146 " -ltLOGTIMEOUT Timeout for log capture (in seconds). Default is " STRINGIFY(DEFAULT_LOG_TIMEOUT) "\n"
00147 " -lxSTRING End log capture when STRING is received\n"
00148 "\n"
00149 "FILE is optional when receiving a file by Y-Modem: options -ry or -rg\n"
00150 "DST-FILE is the name sent to receiver when using Y-Modem. Default is FILE.\n"
00151 ,ProgramName
00152 );
00153 exit(1);
00154 }
00155
00156
00164 void UsageError(const char *format, ...)
00165 {
00166 va_list args;
00167 va_start(args,format);
00168 vfprintf(stderr,format,args);
00169 va_end(args);
00170 fprintf(stderr,"\nTry '%s -h' for more information.\n",ProgramName);
00171 exit(1);
00172 }
00173
00174
00183 void Error(int error,const char *format, ...)
00184 {
00185 va_list args;
00186 va_start(args,format);
00187 vfprintf(stderr,format,args);
00188 va_end(args);
00189 if(error<0)
00190 fprintf(stderr,". Error %d",error);
00191 fprintf(stderr,"\n");
00192 exit(error);
00193 }
00194
00195
00206 bool GetIntArg(const char* argStr, unsigned& arg)
00207 {
00208 char* argEnd;
00209 unsigned long val = strtoul(argStr,&argEnd,10);
00210 if(*argEnd)
00211 return false;
00212 arg = (unsigned)val;
00213 return true;
00214 }
00215
00216
00228 bool GetTimeoutArg(const char* argStr, unsigned& arg)
00229 {
00230 if(!GetIntArg(argStr,arg))
00231 return false;
00232
00233 if(arg<(~(unsigned)0)/1000u)
00234 arg *= 1000;
00235 else
00236 arg = ~(unsigned)0;
00237 return true;
00238 }
00239
00240
00247 void ParseArgs(int argc, char** argv)
00248 {
00249 ProgramName = *argv;
00250
00251
00252 while(--argc)
00253 {
00254 char* arg = *++argv;
00255 if(arg[0]=='"')
00256 {
00257
00258 int len = strlen(arg);
00259 if(arg[len-1]=='"')
00260 {
00261 arg[len-1] = 0;
00262 ++arg;
00263 *argv = arg;
00264 }
00265 }
00266 if(arg[0]!='-')
00267 {
00268
00269 if(DstFileName)
00270 UsageError("Too many file arguments");
00271 if(FileName)
00272 DstFileName = arg;
00273 else
00274 FileName = arg;
00275 continue;
00276 }
00277
00278 ++arg;
00279 switch(*arg++)
00280 {
00281 case 'p':
00282 if(!GetIntArg(arg,PortNum))
00283 UsageError("Invalid PORT argument");
00284 break;
00285
00286 case 'b':
00287 if(!GetIntArg(arg,PortBaud))
00288 UsageError("Invalid BAUD argument");
00289 break;
00290
00291 case 't':
00292 if(!GetTimeoutArg(arg,Timeout))
00293 UsageError("Invalid TIMEOUT argument");
00294 break;
00295
00296 case 'h':
00297 Help();
00298
00299 case 's':
00300 SendFlag = true;
00301 XModemFlag = false;
00302 if(arg[0]!=0)
00303 {
00304 if(arg[1]!=0)
00305 goto invalid;
00306 if(arg[0]=='y')
00307 ;
00308 else if(arg[0]=='x')
00309 XModemFlag = true, KModeFlag = false;
00310 else if(arg[0]=='k')
00311 XModemFlag = true, KModeFlag = true;
00312 else
00313 goto invalid;
00314 }
00315 break;
00316
00317 case 'r':
00318 ReceiveFlag = true;
00319 XModemFlag = false;
00320 GModeFlag = false;
00321 if(arg[0]!=0)
00322 {
00323 if(arg[1]!=0)
00324 goto invalid;
00325 if(arg[0]=='y')
00326 ;
00327 else if(arg[0]=='g')
00328 GModeFlag = true;
00329 else if(arg[0]=='x')
00330 CrcFlag = false, XModemFlag = true;
00331 else if(arg[0]=='c')
00332 CrcFlag = true, XModemFlag = true;
00333 else
00334 goto invalid;
00335 }
00336 break;
00337
00338 case 'l':
00339 switch(*arg++)
00340 {
00341 case 'f':
00342 LogFileName = arg;
00343 break;
00344 case 'e':
00345 LogEcho = true;
00346 break;
00347 case 't':
00348 if(!GetTimeoutArg(arg,LogTimeout))
00349 UsageError("Invalid LOGTIMEOUT argument");
00350 break;
00351 case 'x':
00352 LogEndStringLength = strlen(arg);
00353 LogEndString = arg;
00354 if(LogEndStringLength>MAX_LOG_END_STRING_LENGTH)
00355 UsageError("String too long in -lx option. Max length is " STRINGIFY(MAX_LOG_END_STRING_LENGTH) );
00356 break;
00357 default:
00358 goto invalid;
00359 }
00360 break;
00361
00362 default:
00363 goto invalid;
00364 }
00365 continue;
00366 }
00367 return;
00368
00369 invalid:
00370 UsageError("Invalid option '%s'",*argv);
00371 }
00372
00373
00377 class InFile : public YModemTx::InStream
00378 {
00379 public:
00380 InFile()
00381 : File(0)
00382 {
00383 }
00384
00392 int Open(const char* fileName)
00393 {
00394 File = fopen(fileName,"rb");
00395 if(!File)
00396 return YModemTx::ErrorInputStreamError;
00397
00398 if(fseek(File,0,SEEK_END))
00399 {
00400 fclose(File);
00401 return YModemTx::ErrorInputStreamError;
00402 }
00403 TotalSize = ftell(File);
00404 if(fseek(File,0,SEEK_SET))
00405 {
00406 fclose(File);
00407 return YModemTx::ErrorInputStreamError;
00408 }
00409 TransferredSize = 0;
00410 return 0;
00411 }
00412
00416 void Close()
00417 {
00418 if(File)
00419 {
00420 fclose(File);
00421 File = 0;
00422 }
00423 }
00424
00430 inline size_t Size()
00431 {
00432 return TotalSize;
00433 }
00434
00443 int In(uint8_t* data, size_t size)
00444 {
00445 int percent = TotalSize ? ((uint64_t)TransferredSize*(uint64_t)100)/(uint64_t)TotalSize : 0;
00446 printf("%8d bytes of %d - %3d%%\r",TransferredSize, TotalSize, percent);
00447 fflush(stdout);
00448 size=fread(data,sizeof(uint8_t),size,File);
00449 if(size)
00450 {
00451 TransferredSize += size;
00452 return size;
00453 }
00454 if(TransferredSize!=TotalSize)
00455 return YModemTx::ErrorInputStreamError;
00456 return 0;
00457 }
00458 private:
00459 FILE* File;
00460 size_t TotalSize;
00461 size_t TransferredSize;
00462 };
00463
00464
00468 class OutFile : public YModemRx::OutStream
00469 {
00470 public:
00471 OutFile()
00472 : File(0)
00473 {
00474 }
00475
00487 int Open(const char* fileName, size_t size)
00488 {
00489 if(File)
00490 {
00491 if(TransferredSize)
00492 return YModemRx::ErrorOutputStreamError;
00493 }
00494 else
00495 {
00496 File = fopen(fileName,"w+b");
00497 if(!File)
00498 return YModemRx::ErrorOutputStreamError;
00499 printf("Receiving %s...\n",fileName);
00500 }
00501 TotalSize = size;
00502 TransferredSize = 0;
00503 return 0;
00504 }
00505
00509 void Close()
00510 {
00511 if(File)
00512 {
00513 fclose(File);
00514 File = 0;
00515 }
00516 }
00517
00526 int Out(const uint8_t* data, size_t size)
00527 {
00528 if(!File)
00529 return YModemRx::ErrorOutputStreamError;
00530
00531 if(TotalSize)
00532 {
00533 int percent = TotalSize ? ((uint64_t)TransferredSize*(uint64_t)100)/(uint64_t)TotalSize : 0;
00534 printf("%8d bytes of %d - %3d%%\r",TransferredSize, TotalSize, percent);
00535 fflush(stdout);
00536 }
00537 else
00538 {
00539 printf("%8d bytes\r",TransferredSize);
00540 fflush(stdout);
00541 }
00542
00543 if(TotalSize)
00544 {
00545 size_t maxSize = TotalSize-TransferredSize;
00546 if(size>maxSize)
00547 size = maxSize;
00548 if(!maxSize)
00549 return 0;
00550 }
00551
00552 if(size!=fwrite(data,sizeof(uint8_t),size,File))
00553 return YModemRx::ErrorOutputStreamError;
00554 TransferredSize += size;
00555 return size;
00556 }
00557 private:
00558 FILE* File;
00559 size_t TotalSize;
00560 size_t TransferredSize;
00561 };
00562
00563
00564 FILE* LogFile = 0;
00573 void CaptureLog(SerialPort* port)
00574 {
00575 printf("Capturing log to %s...\n",LogFileName);
00576 fflush(stdout);
00577 uint8_t logBuffer[1024+MAX_LOG_END_STRING_LENGTH];
00578 size_t bufferOffset = 0;
00579 for(;;)
00580 {
00581 uint8_t* start = logBuffer+bufferOffset;
00582 uint8_t* end = logBuffer+sizeof(logBuffer);
00583 size_t length = end-start;
00584 int result = port->In(start,length,LogTimeout);
00585 if(!result)
00586 {
00587 fclose(LogFile);
00588 printf("Log capture timeout.\n");
00589 return;
00590 }
00591 if(result<0)
00592 {
00593 fclose(LogFile);
00594 Error(result,"Error during log capture");
00595 }
00596
00597
00598 length = result;
00599 end = start+length;
00600 if(fwrite(start,1,length,LogFile)!=length)
00601 {
00602 fclose(LogFile);
00603 Error(ErrorLogFileError,"File error during log capture");
00604 }
00605 fflush(LogFile);
00606
00607 if(LogEcho)
00608 {
00609 #ifndef WIN32
00610 fwrite(start,1,length,stdout);
00611 #else
00612
00613 for(size_t i=0; i<length; ++i)
00614 if(start[i]!=13)
00615 fwrite(start+i,1,1,stdout);
00616 #endif
00617 fflush(stdout);
00618 }
00619
00620
00621 if(!LogEndStringLength)
00622 continue;
00623 uint8_t* endString = (uint8_t*)LogEndString;
00624 uint8_t firstEndChar = endString[0];
00625 size_t endStringLength = LogEndStringLength;
00626 uint8_t* scanEnd = end-endStringLength;
00627 uint8_t* scan = logBuffer;
00628 while(scan<=scanEnd)
00629 {
00630 match_next: if(*scan++!=firstEndChar)
00631 continue;
00632
00633 uint8_t* ptr = scan;
00634 size_t endStringMatch = 0;
00635 while(++endStringMatch<endStringLength)
00636 if(*ptr++!=endString[endStringMatch])
00637 goto match_next;
00638
00639 fclose(LogFile);
00640 printf("Log capture termination string found.\n");
00641 return;
00642 }
00643 bufferOffset = end-scan;
00644 memcpy(logBuffer,scan,bufferOffset);
00645 }
00646 }
00647
00648
00654 void Send(SerialPort* port)
00655 {
00656 if(!FileName)
00657 UsageError("File argument missing");
00658
00659 if(!DstFileName)
00660 DstFileName = FileName;
00661
00662 InFile source;
00663 int error = source.Open(FileName);
00664 if(error)
00665 Error(error,"Can't open file '%s'",FileName);
00666
00667 printf("Sending %s...\n",FileName);
00668 YModemTx ymodem(*port);
00669 if(XModemFlag)
00670 error = ymodem.SendX(source,Timeout,KModeFlag);
00671 else
00672 error = ymodem.SendY(DstFileName,source.Size(),source,Timeout);
00673 if(error)
00674 Error(error,"Error during file transfer");
00675
00676 printf("\nSent OK\n");
00677 source.Close();
00678 }
00679
00680
00686 void Receive(SerialPort* port)
00687 {
00688 OutFile destination;
00689 YModemRx ymodem(*port);
00690 int error;
00691 if(XModemFlag)
00692 {
00693 if(!FileName)
00694 UsageError("File argument missing");
00695 error = destination.Open(FileName,0);
00696 if(error)
00697 Error(error,"Can't create file '%s'",FileName);
00698 error = ymodem.ReceiveX(destination,Timeout,CrcFlag);
00699 }
00700 else
00701 {
00702 if(FileName)
00703 {
00704 error = destination.Open(FileName,0);
00705 if(error)
00706 Error(error,"Can't create file '%s'",FileName);
00707 }
00708 error = ymodem.ReceiveY(destination,Timeout,GModeFlag);
00709 }
00710 if(error)
00711 Error(error,"Error during file transfer");
00712
00713 printf("\nReceived OK\n");
00714 destination.Close();
00715 }
00716
00717
00726 int main(int argc, char** argv)
00727 {
00728 ParseArgs(argc,argv);
00729
00730 SerialPort* port = SerialPort::New();
00731 if(!port)
00732 Error(ErrorNoMemory,"Can't create port");
00733
00734 int error = port->Open(PortNum);
00735 if(error)
00736 Error(error,"Can't open port");
00737
00738 error = port->Initialise(PortBaud);
00739 if(error)
00740 Error(error,"Can't initialise port");
00741
00742 if(ReceiveFlag)
00743 Receive(port);
00744 else
00745 {
00746 if(LogFileName)
00747 {
00748 LogFile = fopen(LogFileName,"w+b");
00749 if(!LogFile)
00750 Error(ErrorLogFileError,"Can't create LogFile file '%s'",LogFileName);
00751 }
00752 if(SendFlag || FileName)
00753 Send(port);
00754 if(LogFileName)
00755 CaptureLog(port);
00756 }
00757
00758 port->Close();
00759 delete port;
00760
00761 return 0;
00762 }
00763
00764
00766