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,2007 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 #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     // convert to milliseconds...
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     // parse arguments...
00258     while(--argc)
00259         {
00260         char* arg = *++argv;
00261         if(arg[0]=='"')
00262             {
00263             // remove "" from around argument...
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             // arg doesn't start with '-' so it must be the name of the file...
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; // skip initial '-'
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                         ; // default
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                         ; // default
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         // find file size...
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; // we've not been Open()ed
00536         // show progress...
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         // limit data to size of file...
00549         if(TotalSize)
00550             {
00551             size_t maxSize = TotalSize-TransferredSize;
00552             if(size>maxSize)
00553                 size = maxSize;
00554             if(!maxSize)
00555                 return 0; // end
00556             }
00557         // write data...
00558         if(size!=fwrite(data,sizeof(uint8_t),size,File))
00559             return YModemRx::ErrorOutputStreamError; // write failed
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         // save data to log...
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             // Hack for windows because it expands LF character to CR+LF
00619             for(size_t i=0; i<length; ++i)
00620                 if(start[i]!=13) // ignore CR characters
00621                     fwrite(start+i,1,1,stdout);
00622 #endif
00623             fflush(stdout);
00624             }
00625 
00626         // check for terminating string...
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  // End of group
00772 

Generated by  doxygen 1.6.1