Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members

ymodem_main.cpp

Go to the documentation of this file.
00001 /*
00002 This program is distributed under the terms of the 'MIT license'. The text
00003 of this licence follows...
00004 
00005 Copyright (c) 2006 J.D.Medhurst (a.k.a. Tixy)
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a copy
00008 of this software and associated documentation files (the "Software"), to deal
00009 in the Software without restriction, including without limitation the rights
00010 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00011 copies of the Software, and to permit persons to whom the Software is
00012 furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00020 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00022 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00023 THE SOFTWARE.
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     // convert to milliseconds...
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     // parse arguments...
00252     while(--argc)
00253         {
00254         char* arg = *++argv;
00255         if(arg[0]=='"')
00256             {
00257             // remove "" from around argument...
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             // arg doesn't start with '-' so it must be the name of the file...
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; // skip initial '-'
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                         ; // default
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                         ; // default
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         // find file size...
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; // we've not been Open()ed
00530         // show progress...
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         // limit data to size of file...
00543         if(TotalSize)
00544             {
00545             size_t maxSize = TotalSize-TransferredSize;
00546             if(size>maxSize)
00547                 size = maxSize;
00548             if(!maxSize)
00549                 return 0; // end
00550             }
00551         // write data...
00552         if(size!=fwrite(data,sizeof(uint8_t),size,File))
00553             return YModemRx::ErrorOutputStreamError; // write failed
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         // save data to log...
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             // Hack for windows because it expands LF character to CR+LF
00613             for(size_t i=0; i<length; ++i)
00614                 if(start[i]!=13) // ignore CR characters
00615                     fwrite(start+i,1,1,stdout);
00616 #endif
00617             fflush(stdout);
00618             }
00619 
00620         // check for terminating string...
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  // End of group
00766 

Generated by  doxygen 1.4.4