bool CommandLine::Parse(int argc, char *argv[]) { if (argc<1) { return false; } // Split the program name into path and filename string path, name; DiskFile::SplitFilename(argv[0], path, name); argc--; argv++; // Strip ".exe" from the end if (name.size() > 4 && 0 == stricmp(".exe", name.substr(name.length()-4).c_str())) { name = name.substr(0, name.length()-4); } // Check the resulting program name if (0 == stricmp("par2create", name.c_str())) { operation = opCreate; } else if (0 == stricmp("par2verify", name.c_str())) { operation = opVerify; } else if (0 == stricmp("par2repair", name.c_str())) { operation = opRepair; } // Have we determined what operation we want? if (operation == opNone) { if (argc<2) { cerr << "Not enough command line arguments." << endl; return false; } switch (tolower(argv[0][0])) { case 'c': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "create")) operation = opCreate; break; case 'v': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "verify")) operation = opVerify; break; case 'r': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "repair")) operation = opRepair; break; } if (operation == opNone) { cerr << "Invalid operation specified: " << argv[0] << endl; return false; } argc--; argv++; } bool options = true; while (argc>0) { if (argv[0][0]) { if (options && argv[0][0] != '-') options = false; if (options) { switch (tolower(argv[0][1])) { case 'b': // Set the block count { if (operation != opCreate) { cerr << "Cannot specify block count unless creating." << endl; return false; } if (blockcount > 0) { cerr << "Cannot specify block count twice." << endl; return false; } else if (blocksize > 0) { cerr << "Cannot specify both block count and block size." << endl; return false; } char *p = &argv[0][2]; while (blockcount <= 3276 && *p && isdigit(*p)) { blockcount = blockcount * 10 + (*p - '0'); p++; } if (0 == blockcount || blockcount > 32768 || *p) { cerr << "Invalid block count option: " << argv[0] << endl; return false; } } break; case 's': // Set the block size { if (operation != opCreate) { cerr << "Cannot specify block size unless creating." << endl; return false; } if (blocksize > 0) { cerr << "Cannot specify block size twice." << endl; return false; } else if (blockcount > 0) { cerr << "Cannot specify both block count and block size." << endl; return false; } char *p = &argv[0][2]; while (blocksize <= 429496729 && *p && isdigit(*p)) { blocksize = blocksize * 10 + (*p - '0'); p++; } if (*p || blocksize == 0) { cerr << "Invalid block size option: " << argv[0] << endl; return false; } if (blocksize & 3) { cerr << "Block size must be a multiple of 4." << endl; return false; } } break; case 'r': // Set the amount of redundancy required { if (operation != opCreate) { cerr << "Cannot specify redundancy unless creating." << endl; return false; } if (redundancyset) { cerr << "Cannot specify redundancy twice." << endl; return false; } else if (recoveryblockcountset) { cerr << "Cannot specify both redundancy and recovery block count." << endl; return false; } char *p = &argv[0][2]; while (redundancy <= 10 && *p && isdigit(*p)) { redundancy = redundancy * 10 + (*p - '0'); p++; } if (redundancy > 100 || *p) { cerr << "Invalid redundancy option: " << argv[0] << endl; return false; } if (redundancy == 0 && recoveryfilecount > 0) { cerr << "Cannot set redundancy to 0 and file count > 0" << endl; return false; } redundancyset = true; } break; case 'c': // Set the number of recovery blocks to create { if (operation != opCreate) { cerr << "Cannot specify recovery block count unless creating." << endl; return false; } if (recoveryblockcountset) { cerr << "Cannot specify recovery block count twice." << endl; return false; } else if (redundancyset) { cerr << "Cannot specify both recovery block count and redundancy." << endl; return false; } char *p = &argv[0][2]; while (recoveryblockcount <= 32768 && *p && isdigit(*p)) { recoveryblockcount = recoveryblockcount * 10 + (*p - '0'); p++; } if (recoveryblockcount > 32768 || *p) { cerr << "Invalid recoveryblockcount option: " << argv[0] << endl; return false; } if (recoveryblockcount == 0 && recoveryfilecount > 0) { cerr << "Cannot set recoveryblockcount to 0 and file count > 0" << endl; return false; } recoveryblockcountset = true; } break; case 'f': // Specify the First block recovery number { if (operation != opCreate) { cerr << "Cannot specify first block number unless creating." << endl; return false; } if (firstblock > 0) { cerr << "Cannot specify first block twice." << endl; return false; } char *p = &argv[0][2]; while (firstblock <= 3276 && *p && isdigit(*p)) { firstblock = firstblock * 10 + (*p - '0'); p++; } if (firstblock > 32768 || *p) { cerr << "Invalid first block option: " << argv[0] << endl; return false; } } break; case 'u': // Specify uniformly sized recovery files { if (operation != opCreate) { cerr << "Cannot specify uniform files unless creating." << endl; return false; } if (argv[0][2]) { cerr << "Invalid option: " << argv[0] << endl; return false; } if (recoveryfilescheme != scUnknown) { cerr << "Cannot specify two recovery file size schemes." << endl; return false; } recoveryfilescheme = scUniform; } break; case 'l': // Limit the size of the recovery files { if (operation != opCreate) { cerr << "Cannot specify limit files unless creating." << endl; return false; } if (argv[0][2]) { cerr << "Invalid option: " << argv[0] << endl; return false; } if (recoveryfilescheme != scUnknown) { cerr << "Cannot specify two recovery file size schemes." << endl; return false; } if (recoveryfilecount > 0) { cerr << "Cannot specify limited size and number of files at the same time." << endl; return false; } recoveryfilescheme = scLimited; } break; case 'n': // Specify the number of recovery files { if (operation != opCreate) { cerr << "Cannot specify recovery file count unless creating." << endl; return false; } if (recoveryfilecount > 0) { cerr << "Cannot specify recovery file count twice." << endl; return false; } if (redundancyset && redundancy == 0) { cerr << "Cannot set file count when redundancy is set to 0." << endl; return false; } if (recoveryblockcountset && recoveryblockcount == 0) { cerr << "Cannot set file count when recovery block count is set to 0." << endl; return false; } if (recoveryfilescheme == scLimited) { cerr << "Cannot specify limited size and number of files at the same time." << endl; return false; } char *p = &argv[0][2]; while (*p && isdigit(*p)) { recoveryfilecount = recoveryfilecount * 10 + (*p - '0'); p++; } if (recoveryfilecount == 0 || *p) { cerr << "Invalid recovery file count option: " << argv[0] << endl; return false; } } break; case 'm': // Specify how much memory to use for output buffers { if (memorylimit > 0) { cerr << "Cannot specify memory limit twice." << endl; return false; } char *p = &argv[0][2]; while (*p && isdigit(*p)) { memorylimit = memorylimit * 10 + (*p - '0'); p++; } if (memorylimit == 0 || *p) { cerr << "Invalid memory limit option: " << argv[0] << endl; return false; } } break; case 'v': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'v') noiselevel = nlDebug; else noiselevel = nlNoisy; } break; case nlNoisy: case nlDebug: noiselevel = nlDebug; break; default: cerr << "Cannot use both -v and -q." << endl; return false; break; } } break; case 'q': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'q') noiselevel = nlSilent; else noiselevel = nlQuiet; } break; case nlQuiet: case nlSilent: noiselevel = nlSilent; break; default: cerr << "Cannot use both -v and -q." << endl; return false; break; } } break; case '-': { argc--; argv++; options = false; continue; } break; default: { cerr << "Invalid option specified: " << argv[0] << endl; return false; } } } else { list<string> *filenames; // If the argument includes wildcard characters, // search the disk for matching files if (strchr(argv[0], '*') || strchr(argv[0], '?')) { string path; string name; DiskFile::SplitFilename(argv[0], path, name); filenames = DiskFile::FindFiles(path, name); } else { //start of shell expanded * patch. -- Michael Evans //The shell might expaned * so, if we have our name and we're creating, then filter for files... if ((parfilename.length() != 0) && (operation == opCreate)) { #ifdef WIN32 if (GetFileAttributes(argv[0]) & FILE_ATTRIBUTE_DIRECTORY) // != 0, but no need... #else //Not WIN32, probably *nix struct stat st; if (!(stat(argv[0], &st) == 0 && S_ISREG(st.st_mode))) #endif { cerr << "Skipping non-regular file: " << argv[0] << endl; argc--; argv++; options = false; continue; } }//end of shell expanded * patch. -- Michael Evans filenames = new list<string>; filenames->push_back(argv[0]); } list<string>::iterator fn = filenames->begin(); while (fn != filenames->end()) { // Convert filename from command line into a full path + filename string filename = DiskFile::GetCanonicalPathname(*fn); // If this is the first file on the command line, then it // is the main PAR2 file. if (parfilename.length() == 0) { // If we are verifying or repairing, the PAR2 file must // already exist if (operation != opCreate) { // Find the last '.' in the filename string::size_type where = filename.find_last_of('.'); if (where != string::npos) { // Get what follows the last '.' string tail = filename.substr(where+1); if (0 == stricmp(tail.c_str(), "par2")) { parfilename = filename; version = verPar2; } else if (0 == stricmp(tail.c_str(), "par") || (tail.size() == 3 && tolower(tail[0]) == 'p' && isdigit(tail[1]) && isdigit(tail[2]))) { parfilename = filename; version = verPar1; } } // If we haven't figured out which version of PAR file we // are using from the file extension, then presumable the // files filename was actually the name of a data file. if (version == verUnknown) { // Check for the existence of a PAR2 of PAR file. if (DiskFile::FileExists(filename + ".par2")) { version = verPar2; parfilename = filename + ".par2"; } else if (DiskFile::FileExists(filename + ".PAR2")) { version = verPar2; parfilename = filename + ".PAR2"; } else if (DiskFile::FileExists(filename + ".par")) { version = verPar1; parfilename = filename + ".par"; } else if (DiskFile::FileExists(filename + ".PAR")) { version = verPar1; parfilename = filename + ".PAR"; } } else { // Does the specified PAR or PAR2 file exist if (!DiskFile::FileExists(filename)) { version = verUnknown; } } if (version == verUnknown) { cerr << "The recovery file does not exist: " << filename << endl; return false; } } else { // We are creating a new file version = verPar2; parfilename = filename; } } else { // All other files must exist if (!DiskFile::FileExists(filename)) { cerr << "The source file does not exist: " << filename << endl; return false; } u64 filesize = DiskFile::GetFileSize(filename); // Ignore all 0 byte files if (filesize > 0) { extrafiles.push_back(ExtraFile(filename, filesize)); // track the total size of the source files and how // big the largest one is. totalsourcesize += filesize; if (largestsourcesize < filesize) largestsourcesize = filesize; } else { cout << "Skipping 0 byte file: " << filename << endl; } } ++fn; } delete filenames; } } argc--; argv++; } if (parfilename.length() == 0) { cerr << "You must specify a Recovery file." << endl; return false; } // Default noise level if (noiselevel == nlUnknown) { noiselevel = nlNormal; } // If we a creating, check the other parameters if (operation == opCreate) { // If no recovery file size scheme is specified then use Variable if (recoveryfilescheme == scUnknown) { recoveryfilescheme = scVariable; } // If neither block count not block size is specified if (blockcount == 0 && blocksize == 0) { // Use a block count of 2000 blockcount = 2000; } // If we are creating, the source files must be given. if (extrafiles.size() == 0) { // Does the par filename include the ".par2" on the end? if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { // Yes it does. cerr << "You must specify a list of files when creating." << endl; return false; } else { // No it does not. // In that case check to see if the file exists, and if it does // assume that you wish to create par2 files for it. u64 filesize = 0; if (DiskFile::FileExists(parfilename) && (filesize = DiskFile::GetFileSize(parfilename)) > 0) { extrafiles.push_back(ExtraFile(parfilename, filesize)); // track the total size of the source files and how // big the largest one is. totalsourcesize += filesize; if (largestsourcesize < filesize) largestsourcesize = filesize; } else { // The file does not exist or it is empty. cerr << "You must specify a list of files when creating." << endl; return false; } } } // Strip the ".par2" from the end of the filename of the main PAR2 file. if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { parfilename = parfilename.substr(0, parfilename.length()-5); } // Assume a redundancy of 5% if neither redundancy or recoveryblockcount were set. if (!redundancyset && !recoveryblockcountset) { redundancy = 5; } } // Assume a memory limit of 16MB if not specified. if (memorylimit == 0) { #ifdef WIN32 u64 TotalPhysicalMemory = 0; HMODULE hLib = ::LoadLibraryA("kernel32.dll"); if (NULL != hLib) { BOOL (WINAPI *pfn)(LPMEMORYSTATUSEX) = (BOOL (WINAPI*)(LPMEMORYSTATUSEX))::GetProcAddress(hLib, "GlobalMemoryStatusEx"); if (NULL != pfn) { MEMORYSTATUSEX mse; mse.dwLength = sizeof(mse); if (pfn(&mse)) { TotalPhysicalMemory = mse.ullTotalPhys; } } ::FreeLibrary(hLib); } if (TotalPhysicalMemory == 0) { MEMORYSTATUS ms; ::ZeroMemory(&ms, sizeof(ms)); ::GlobalMemoryStatus(&ms); TotalPhysicalMemory = ms.dwTotalPhys; } if (TotalPhysicalMemory == 0) { // Assume 128MB TotalPhysicalMemory = 128 * 1048576; } // Half of total physical memory memorylimit = (size_t)(TotalPhysicalMemory / 1048576 / 2); #else memorylimit = 16; #endif } memorylimit *= 1048576; return true; }
bool CommandLine::Parse(int argc, char *argv[]) { if (argc<1) { return false; } // Split the program name into path and filename string path, name; DiskFile::SplitFilename(argv[0], path, name); argc--; argv++; if (argc>0) { if (argv[0][0] != 0 && argv[0][0] == '-') { if (argv[0][1] != 0) { switch (argv[0][1]) { case 'h': usage(); return true; case 'V': showversion(); if (argv[0][2] != 0 && argv[0][2] == 'V') { cout << endl; banner(); } return true; } } } } // Strip ".exe" from the end if (name.size() > 4 && 0 == stricmp(".exe", name.substr(name.length()-4).c_str())) { name = name.substr(0, name.length()-4); } // Check the resulting program name if (0 == stricmp("par2create", name.c_str())) { operation = opCreate; } else if (0 == stricmp("par2verify", name.c_str())) { operation = opVerify; } else if (0 == stricmp("par2repair", name.c_str())) { operation = opRepair; } // Have we determined what operation we want? if (operation == opNone) { if (argc<2) { cerr << "Not enough command line arguments." << endl; return false; } switch (tolower(argv[0][0])) { case 'c': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "create")) operation = opCreate; break; case 'v': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "verify")) operation = opVerify; break; case 'r': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "repair")) operation = opRepair; break; } if (operation == opNone) { cerr << "Invalid operation specified: " << argv[0] << endl; return false; } argc--; argv++; } bool options = true; list<string> a_filenames; while (argc>0) { if (argv[0][0]) { if (options && argv[0][0] != '-') options = false; if (options) { switch (argv[0][1]) { case 'a': { if (operation == opCreate) { string str = argv[0]; if (str == "-a") { SetParFilename(argv[1]); argc--; argv++; } else { SetParFilename(str.substr(2)); } } } break; case 'b': // Set the block count { if (operation != opCreate) { cerr << "Cannot specify block count unless creating." << endl; return false; } if (blockcount > 0) { cerr << "Cannot specify block count twice." << endl; return false; } else if (blocksize > 0) { cerr << "Cannot specify both block count and block size." << endl; return false; } char *p = &argv[0][2]; while (blockcount <= 3276 && *p && isdigit(*p)) { blockcount = blockcount * 10 + (*p - '0'); p++; } if (0 == blockcount || blockcount > 32768 || *p) { cerr << "Invalid block count option: " << argv[0] << endl; return false; } } break; case 's': // Set the block size { if (operation != opCreate) { cerr << "Cannot specify block size unless creating." << endl; return false; } if (blocksize > 0) { cerr << "Cannot specify block size twice." << endl; return false; } else if (blockcount > 0) { cerr << "Cannot specify both block count and block size." << endl; return false; } char *p = &argv[0][2]; while (blocksize <= 429496729 && *p && isdigit(*p)) { blocksize = blocksize * 10 + (*p - '0'); p++; } if (*p || blocksize == 0) { cerr << "Invalid block size option: " << argv[0] << endl; return false; } if (blocksize & 3) { cerr << "Block size must be a multiple of 4." << endl; return false; } } break; case 'r': // Set the amount of redundancy required { if (operation != opCreate) { cerr << "Cannot specify redundancy unless creating." << endl; return false; } if (redundancyset) { cerr << "Cannot specify redundancy twice." << endl; return false; } else if (recoveryblockcountset) { cerr << "Cannot specify both redundancy and recovery block count." << endl; return false; } char *p = &argv[0][2]; while (redundancy <= 10 && *p && isdigit(*p)) { redundancy = redundancy * 10 + (*p - '0'); p++; } if (redundancy > 100 || *p) { cerr << "Invalid redundancy option: " << argv[0] << endl; return false; } if (redundancy == 0 && recoveryfilecount > 0) { cerr << "Cannot set redundancy to 0 and file count > 0" << endl; return false; } redundancyset = true; } break; case 'c': // Set the number of recovery blocks to create { if (operation != opCreate) { cerr << "Cannot specify recovery block count unless creating." << endl; return false; } if (recoveryblockcountset) { cerr << "Cannot specify recovery block count twice." << endl; return false; } else if (redundancyset) { cerr << "Cannot specify both recovery block count and redundancy." << endl; return false; } char *p = &argv[0][2]; while (recoveryblockcount <= 32768 && *p && isdigit(*p)) { recoveryblockcount = recoveryblockcount * 10 + (*p - '0'); p++; } if (recoveryblockcount > 32768 || *p) { cerr << "Invalid recoveryblockcount option: " << argv[0] << endl; return false; } if (recoveryblockcount == 0 && recoveryfilecount > 0) { cerr << "Cannot set recoveryblockcount to 0 and file count > 0" << endl; return false; } recoveryblockcountset = true; } break; case 'f': // Specify the First block recovery number { if (operation != opCreate) { cerr << "Cannot specify first block number unless creating." << endl; return false; } if (firstblock > 0) { cerr << "Cannot specify first block twice." << endl; return false; } char *p = &argv[0][2]; while (firstblock <= 3276 && *p && isdigit(*p)) { firstblock = firstblock * 10 + (*p - '0'); p++; } if (firstblock > 32768 || *p) { cerr << "Invalid first block option: " << argv[0] << endl; return false; } } break; case 'u': // Specify uniformly sized recovery files { if (operation != opCreate) { cerr << "Cannot specify uniform files unless creating." << endl; return false; } if (argv[0][2]) { cerr << "Invalid option: " << argv[0] << endl; return false; } if (recoveryfilescheme != scUnknown) { cerr << "Cannot specify two recovery file size schemes." << endl; return false; } recoveryfilescheme = scUniform; } break; case 'l': // Limit the size of the recovery files { if (operation != opCreate) { cerr << "Cannot specify limit files unless creating." << endl; return false; } if (argv[0][2]) { cerr << "Invalid option: " << argv[0] << endl; return false; } if (recoveryfilescheme != scUnknown) { cerr << "Cannot specify two recovery file size schemes." << endl; return false; } if (recoveryfilecount > 0) { cerr << "Cannot specify limited size and number of files at the same time." << endl; return false; } recoveryfilescheme = scLimited; } break; case 'n': // Specify the number of recovery files { if (operation != opCreate) { cerr << "Cannot specify recovery file count unless creating." << endl; return false; } if (recoveryfilecount > 0) { cerr << "Cannot specify recovery file count twice." << endl; return false; } if (redundancyset && redundancy == 0) { cerr << "Cannot set file count when redundancy is set to 0." << endl; return false; } if (recoveryblockcountset && recoveryblockcount == 0) { cerr << "Cannot set file count when recovery block count is set to 0." << endl; return false; } if (recoveryfilescheme == scLimited) { cerr << "Cannot specify limited size and number of files at the same time." << endl; return false; } char *p = &argv[0][2]; while (*p && isdigit(*p)) { recoveryfilecount = recoveryfilecount * 10 + (*p - '0'); p++; } if (recoveryfilecount == 0 || *p) { cerr << "Invalid recovery file count option: " << argv[0] << endl; return false; } } break; case 'm': // Specify how much memory to use for output buffers { if (memorylimit > 0) { cerr << "Cannot specify memory limit twice." << endl; return false; } char *p = &argv[0][2]; while (*p && isdigit(*p)) { memorylimit = memorylimit * 10 + (*p - '0'); p++; } if (memorylimit == 0 || *p) { cerr << "Invalid memory limit option: " << argv[0] << endl; return false; } } break; case 'v': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'v') noiselevel = nlDebug; else noiselevel = nlNoisy; } break; case nlNoisy: case nlDebug: noiselevel = nlDebug; break; default: cerr << "Cannot use both -v and -q." << endl; return false; break; } } break; case 'q': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'q') noiselevel = nlSilent; else noiselevel = nlQuiet; } break; case nlQuiet: case nlSilent: noiselevel = nlSilent; break; default: cerr << "Cannot use both -v and -q." << endl; return false; break; } } break; case 'p': { if (operation != opRepair && operation != opVerify) { cerr << "Cannot specify purge unless repairing or verifying." << endl; return false; } purgefiles = true; } break; case 'h': { usage(); return false; break; } case 'R': { if (operation == opCreate) { recursive = true; } else { cerr << "Recursive has no impact except on creating." << endl; } } break; case '-': { argc--; argv++; options = false; continue; } break; default: { cerr << "Invalid option specified: " << argv[0] << endl; return false; } } } else if (parfilename.length() == 0) { string filename = argv[0]; string::size_type where; if ((where = filename.find_first_of('*')) != string::npos || (where = filename.find_first_of('?')) != string::npos) { cerr << "par2 file must not have a wildcard in it." << endl; return false; } SetParFilename(filename); } else { list<string> *filenames; string path; string name; DiskFile::SplitFilename(argv[0], path, name); filenames = DiskFile::FindFiles(path, name, recursive); list<string>::iterator fn = filenames->begin(); while (fn != filenames->end()) { // Convert filename from command line into a full path + filename string filename = DiskFile::GetCanonicalPathname(*fn); // Originally, all specified files were supposed to exist, or the program // would stop with an error message. This was not practical, for example in // a directory with files appearing and disappearing (an active download directory). // So the new rule is: when a specified file doesn't exist, it is silently skipped. if (!DiskFile::FileExists(filename)) { cout << "Ignoring non-existent source file: " << filename << endl; } else { u64 filesize = DiskFile::GetFileSize(filename); // Ignore all 0 byte files if (filesize == 0) { cout << "Skipping 0 byte file: " << filename << endl; } else if (a_filenames.end() != find(a_filenames.begin(), a_filenames.end(), filename)) { cout << "Skipping duplicate filename: " << filename << endl; } else { a_filenames.push_back(filename); extrafiles.push_back(ExtraFile(filename, filesize)); // track the total size of the source files and how // big the largest one is. totalsourcesize += filesize; if (largestsourcesize < filesize) largestsourcesize = filesize; } } //end file exists ++fn; } delete filenames; } } argc--; argv++; } if (parfilename.length() == 0) { cerr << "You must specify a Recovery file." << endl; return false; } // Default noise level if (noiselevel == nlUnknown) { noiselevel = nlNormal; } // If we a creating, check the other parameters if (operation == opCreate) { // If no recovery file size scheme is specified then use Variable if (recoveryfilescheme == scUnknown) { recoveryfilescheme = scVariable; } // If neither block count not block size is specified if (blockcount == 0 && blocksize == 0) { // Use a block count of 2000 blockcount = 2000; } // If we are creating, the source files must be given. if (extrafiles.size() == 0) { // Does the par filename include the ".par2" on the end? if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { // Yes it does. cerr << "You must specify a list of files when creating." << endl; return false; } else { // No it does not. // In that case check to see if the file exists, and if it does // assume that you wish to create par2 files for it. u64 filesize = 0; if (DiskFile::FileExists(parfilename) && (filesize = DiskFile::GetFileSize(parfilename)) > 0) { extrafiles.push_back(ExtraFile(parfilename, filesize)); // track the total size of the source files and how // big the largest one is. totalsourcesize += filesize; if (largestsourcesize < filesize) largestsourcesize = filesize; } else { // The file does not exist or it is empty. cerr << "You must specify a list of files when creating." << endl; return false; } } } // Strip the ".par2" from the end of the filename of the main PAR2 file. if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { parfilename = parfilename.substr(0, parfilename.length()-5); } // Assume a redundancy of 5% if neither redundancy or recoveryblockcount were set. if (!redundancyset && !recoveryblockcountset) { redundancy = 5; } } // Assume a memory limit of 16MB if not specified. if (memorylimit == 0) { #ifdef WIN32 u64 TotalPhysicalMemory = 0; HMODULE hLib = ::LoadLibraryA("kernel32.dll"); if (NULL != hLib) { BOOL (WINAPI *pfn)(LPMEMORYSTATUSEX) = (BOOL (WINAPI*)(LPMEMORYSTATUSEX))::GetProcAddress(hLib, "GlobalMemoryStatusEx"); if (NULL != pfn) { MEMORYSTATUSEX mse; mse.dwLength = sizeof(mse); if (pfn(&mse)) { TotalPhysicalMemory = mse.ullTotalPhys; } } ::FreeLibrary(hLib); } if (TotalPhysicalMemory == 0) { MEMORYSTATUS ms; ::ZeroMemory(&ms, sizeof(ms)); ::GlobalMemoryStatus(&ms); TotalPhysicalMemory = ms.dwTotalPhys; } if (TotalPhysicalMemory == 0) { // Assume 128MB TotalPhysicalMemory = 128 * 1048576; } // Half of total physical memory memorylimit = (size_t)(TotalPhysicalMemory / 1048576 / 2); #else memorylimit = 16; #endif } memorylimit *= 1048576; return true; }
bool CommandLine::Parse(int argc, TCHAR *argv[]) { if (argc<1) { return false; } // Split the program name into path and filename string path, name; DiskFile::SplitFilename(native_char_array_to_utf8_string(argv[0]), path, name); argc--; argv++; // Strip ".exe" from the end if (name.size() > 4 && 0 == stricmp(".exe", name.substr(name.length()-4).c_str())) { name = name.substr(0, name.length()-4); } // Check the resulting program name if (0 == stricmp("par2create", name.c_str())) { operation = opCreate; } else if (0 == stricmp("par2verify", name.c_str())) { operation = opVerify; } else if (0 == stricmp("par2repair", name.c_str())) { operation = opRepair; } // Have we determined what operation we want? if (operation == opNone) { if (argc<2) { cerr << "Not enough command line arguments." << endl; return false; } switch (tolower(argv[0][0])) { case 'c': if (argv[0][1] == 0 || 0 == stricmp(native_char_array_to_utf8_char_array(argv[0]), "create")) operation = opCreate; break; case 'v': if (argv[0][1] == 0 || 0 == stricmp(native_char_array_to_utf8_char_array(argv[0]), "verify")) operation = opVerify; break; case 'r': if (argv[0][1] == 0 || 0 == stricmp(native_char_array_to_utf8_char_array(argv[0]), "repair")) operation = opRepair; break; } if (operation == opNone) { cerr << "Invalid operation specified: " << argv[0] << endl; return false; } argc--; argv++; } bool options = true; #if defined(WIN32) || defined(__APPLE__) std::set<string, less_stri> accepted_filenames; #else std::set<string> accepted_filenames; #endif while (argc>0) { if (argv[0][0]) { if (options && argv[0][0] != '-') options = false; if (options) { switch (tolower(argv[0][1])) { case 'b': // Set the block count { if (operation != opCreate) { cerr << "Cannot specify block count unless creating." << endl; return false; } if (blockcount > 0) { cerr << "Cannot specify block count twice." << endl; return false; } else if (blocksize > 0) { cerr << "Cannot specify both block count and block size." << endl; return false; } TCHAR *p = &argv[0][2]; while (blockcount <= 3276 && *p && isdigit(*p)) { blockcount = blockcount * 10 + (*p - '0'); p++; } if (0 == blockcount || blockcount > 32768 || *p) { cerr << "Invalid block count option: " << argv[0] << endl; return false; } } break; case 's': // Set the block size { if (operation != opCreate) { cerr << "Cannot specify block size unless creating." << endl; return false; } if (blocksize > 0) { cerr << "Cannot specify block size twice." << endl; return false; } else if (blockcount > 0) { cerr << "Cannot specify both block count and block size." << endl; return false; } TCHAR *p = &argv[0][2]; while (blocksize <= 429496729 && *p && isdigit(*p)) { blocksize = blocksize * 10 + (*p - '0'); p++; } if (*p || blocksize == 0) { cerr << "Invalid block size option: " << argv[0] << endl; return false; } if (blocksize & 3) { cerr << "Block size must be a multiple of 4." << endl; return false; } } break; case 'r': // Set the amount of redundancy required { if (operation != opCreate) { cerr << "Cannot specify redundancy unless creating." << endl; return false; } if (redundancyset) { cerr << "Cannot specify redundancy twice." << endl; return false; } else if (recoveryblockcountset) { cerr << "Cannot specify both redundancy and recovery block count." << endl; return false; } TCHAR *p = &argv[0][2]; #if 1 redundancy = (float) _tcstod(p, &p); #else while (redundancy <= 10 && *p && isdigit(*p)) { redundancy = redundancy * 10 + (*p - '0'); p++; } #endif if (redundancy > 100.0f || *p) { cerr << "Invalid redundancy option: " << argv[0] << endl; return false; } if (redundancy == 0.0f && recoveryfilecount > 0) { cerr << "Cannot set redundancy to 0 and file count > 0" << endl; return false; } redundancyset = true; } break; case 'c': // Set the number of recovery blocks to create { if (operation != opCreate) { cerr << "Cannot specify recovery block count unless creating." << endl; return false; } if (recoveryblockcountset) { cerr << "Cannot specify recovery block count twice." << endl; return false; } else if (redundancyset) { cerr << "Cannot specify both recovery block count and redundancy." << endl; return false; } TCHAR *p = &argv[0][2]; while (recoveryblockcount <= 32768 && *p && isdigit(*p)) { recoveryblockcount = recoveryblockcount * 10 + (*p - '0'); p++; } if (recoveryblockcount > 32768 || *p) { cerr << "Invalid recoveryblockcount option: " << argv[0] << endl; return false; } if (recoveryblockcount == 0 && recoveryfilecount > 0) { cerr << "Cannot set recoveryblockcount to 0 and file count > 0" << endl; return false; } recoveryblockcountset = true; } break; case 'f': // Specify the First block recovery number { if (operation != opCreate) { cerr << "Cannot specify first block number unless creating." << endl; return false; } if (firstblock > 0) { cerr << "Cannot specify first block twice." << endl; return false; } TCHAR *p = &argv[0][2]; while (firstblock <= 3276 && *p && isdigit(*p)) { firstblock = firstblock * 10 + (*p - '0'); p++; } if (firstblock > 32768 || *p) { cerr << "Invalid first block option: " << argv[0] << endl; return false; } } break; case 'u': // Specify uniformly sized recovery files { if (operation != opCreate) { cerr << "Cannot specify uniform files unless creating." << endl; return false; } if (argv[0][2]) { cerr << "Invalid option: " << argv[0] << endl; return false; } if (recoveryfilescheme != scUnknown) { cerr << "Cannot specify two recovery file size schemes." << endl; return false; } recoveryfilescheme = scUniform; } break; case 'l': // Limit the size of the recovery files { if (operation != opCreate) { cerr << "Cannot specify limit files unless creating." << endl; return false; } if (argv[0][2]) { cerr << "Invalid option: " << argv[0] << endl; return false; } if (recoveryfilescheme != scUnknown) { cerr << "Cannot specify two recovery file size schemes." << endl; return false; } if (recoveryfilecount > 0) { cerr << "Cannot specify limited size and number of files at the same time." << endl; return false; } recoveryfilescheme = scLimited; } break; case 'n': // Specify the number of recovery files { if (operation != opCreate) { cerr << "Cannot specify recovery file count unless creating." << endl; return false; } if (recoveryfilecount > 0) { cerr << "Cannot specify recovery file count twice." << endl; return false; } if (redundancyset && redundancy == 0) { cerr << "Cannot set file count when redundancy is set to 0." << endl; return false; } if (recoveryblockcountset && recoveryblockcount == 0) { cerr << "Cannot set file count when recovery block count is set to 0." << endl; return false; } if (recoveryfilescheme == scLimited) { cerr << "Cannot specify limited size and number of files at the same time." << endl; return false; } TCHAR *p = &argv[0][2]; while (*p && isdigit(*p)) { recoveryfilecount = recoveryfilecount * 10 + (*p - '0'); p++; } if (recoveryfilecount == 0 || *p) { cerr << "Invalid recovery file count option: " << argv[0] << endl; return false; } } break; case 'm': // Specify how much memory to use for output buffers { if (memorylimit > 0) { cerr << "Cannot specify memory limit twice." << endl; return false; } TCHAR *p = &argv[0][2]; while (*p && isdigit(*p)) { memorylimit = memorylimit * 10 + (*p - '0'); p++; } if (memorylimit == 0 || *p) { cerr << "Invalid memory limit option: " << argv[0] << endl; return false; } } break; case 'v': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'v') noiselevel = nlDebug; else noiselevel = nlNoisy; } break; case nlNoisy: case nlDebug: noiselevel = nlDebug; break; default: cerr << "Cannot use both -v and -q." << endl; return false; break; } } break; case 'q': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'q') noiselevel = nlSilent; else noiselevel = nlQuiet; } break; case nlQuiet: case nlSilent: noiselevel = nlSilent; break; default: cerr << "Cannot use both -v and -q." << endl; return false; break; } } break; case 'd': { base_directory = DiskFile::GetCanonicalPathname(native_char_array_to_utf8_string(2 + argv[0])); if (base_directory.empty()) { cerr << "base directory for hierarchy support must specify a folder" << endl; return false; } else if (operation == opCreate && !is_existing_folder(base_directory)) { cerr << "the base directory (" << base_directory << ") for hierarchy support must specify an accessible and existing folder" << endl; return false; } if (base_directory[base_directory.length()-1] != OS_SEPARATOR) base_directory += OS_SEPARATOR; break; } #if WANT_CONCURRENT case 't': { switch (argv[0][2]) { case '-': concurrent_processing_level = ALL_SERIAL; break; case '0': concurrent_processing_level = CHECKSUM_SERIALLY_BUT_PROCESS_CONCURRENTLY; break; case '+': concurrent_processing_level = ALL_CONCURRENT; break; default: cerr << "Expected -t+ (use multiple cores) or -t0 (checksum serially, process concurrently) or -t- (use single core)." << endl; return false; } } break; case 'p': // Set the number of threads { TCHAR *p = &argv[0][2]; while (numthreads <= 3276 && *p && isdigit(*p)) { numthreads = numthreads * 10 + (*p - '0'); p++; } if (numthreads > 32768 || *p) { cerr << "Invalid number of threads option: " << argv[0] << endl; return false; } } break; #endif case '0': create_dummy_par_files = true; break; case '-': { argc--; argv++; options = false; continue; } break; default: { cerr << "Invalid option specified: " << argv[0] << endl; return false; } } } else { list<string> *filenames; // If the argument includes wildcard characters, // search the disk for matching files if (_tcschr/*strchr*/(argv[0], '*') || _tcschr/*strchr*/(argv[0], '?')) { string path; string name; DiskFile::SplitFilename(native_char_array_to_utf8_string(argv[0]), path, name); filenames = DiskFile::FindFiles(path, name); } else if (is_existing_folder(native_char_array_to_utf8_string(argv[0]))) { filenames = build_file_list_in(native_char_array_to_utf8_string(argv[0]).c_str()); } else { filenames = new list<string>; filenames->push_back(native_char_array_to_utf8_string(argv[0])); } for (list<string>::iterator fn = filenames->begin(); fn != filenames->end(); ++fn) { // Convert filename from command line into a full path + filename string filename = DiskFile::GetCanonicalPathname(*fn); // filename can be empty if the realpath() API in GetCanonicalPathname() returns NULL if (filename.empty()) filename = *fn; // If this is the first file on the command line, then it // is the main PAR2 file. if (parfilename.length() == 0) { // If we are verifying or repairing, the PAR2 file must // already exist if (operation != opCreate) { // Find the last '.' in the filename string::size_type where = filename.find_last_of('.'); if (where != string::npos) { // Get what follows the last '.' string tail = filename.substr(where+1); if (0 == stricmp(tail.c_str(), "par2")) { parfilename = filename; version = verPar2; } else if (0 == stricmp(tail.c_str(), "par") || (tail.size() == 3 && tolower(tail[0]) == 'p' && isdigit(tail[1]) && isdigit(tail[2]))) { parfilename = filename; version = verPar1; } } // If we haven't figured out which version of PAR file we // are using from the file extension, then presumable the // files filename was actually the name of a data file. if (version == verUnknown) { // Check for the existence of a PAR2 of PAR file. if (DiskFile::FileExists(filename + ".par2")) { version = verPar2; parfilename = filename + ".par2"; } else if (DiskFile::FileExists(filename + ".PAR2")) { version = verPar2; parfilename = filename + ".PAR2"; } else if (DiskFile::FileExists(filename + ".par")) { version = verPar1; parfilename = filename + ".par"; } else if (DiskFile::FileExists(filename + ".PAR")) { version = verPar1; parfilename = filename + ".PAR"; } } else { // Does the specified PAR or PAR2 file exist if (!DiskFile::FileExists(filename)) { version = verUnknown; } } if (version == verUnknown) { cerr << "The recovery file does not exist: " << filename << endl; return false; } } else { // We are creating a new file version = verPar2; parfilename = filename; } } else { // Originally, all specified files were supposed to exist, or the program // would stop with an error message. This was not practical, for example in // a directory with files appearing and disappearing (an active download directory). // So the new rule is: when a specified file doesn't exist, it is silently skipped. if (!DiskFile::FileExists(filename)) { cout << "Ignoring non-existent source file: " << filename << endl; } else { u64 filesize = DiskFile::GetFileSize(filename); // Ignore all 0 byte files and duplicate file names if (filesize == 0) cout << "Skipping 0 byte file: " << filename << endl; else if (accepted_filenames.end() != accepted_filenames.find(filename)) cout << "Skipping duplicate filename: " << filename << endl; else /* if (accepted_filenames.end() == accepted_filenames.find(filename)) */ { accepted_filenames.insert(filename); extrafiles.push_back(ExtraFile(filename, filesize)); // track the total size of the source files and how // big the largest one is. totalsourcesize += filesize; if (largestsourcesize < filesize) largestsourcesize = filesize; } // if } // if } // if } // for delete filenames; } } argc--; argv++; } if (parfilename.length() == 0) { cerr << "You must specify a Recovery file." << endl; return false; } // Default noise level if (noiselevel == nlUnknown) { noiselevel = nlNormal; } // If we a creating, check the other parameters if (operation == opCreate) { // If no recovery file size scheme is specified then use Variable if (recoveryfilescheme == scUnknown) { recoveryfilescheme = scVariable; } // If neither block count not block size is specified if (blockcount == 0 && blocksize == 0) { // Use a block count of 2000 blockcount = 2000; } // If we are creating, the source files must be given. if (extrafiles.size() == 0) { // Does the par filename include the ".par2" on the end? if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { // Yes it does. cerr << "You must specify a list of files when creating." << endl; return false; } else { // No it does not. // In that case check to see if the file exists, and if it does // assume that you wish to create par2 files for it. u64 filesize = 0; if (DiskFile::FileExists(parfilename) && (filesize = DiskFile::GetFileSize(parfilename)) > 0) { extrafiles.push_back(ExtraFile(parfilename, filesize)); // track the total size of the source files and how // big the largest one is. totalsourcesize += filesize; if (largestsourcesize < filesize) largestsourcesize = filesize; } else { // The file does not exist or it is empty. cerr << "You must specify a list of files when creating." << endl; return false; } } } // Strip the ".par2" from the end of the filename of the main PAR2 file. if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { parfilename = parfilename.substr(0, parfilename.length()-5); } // Assume a redundancy of 5% if neither redundancy or recoveryblockcount were set. if (!redundancyset && !recoveryblockcountset) { redundancy = 5; } } // Assume a memory limit of 16MB if not specified. if (memorylimit == 0) { #if defined(WIN32) || defined(WIN64) u64 TotalPhysicalMemory = 0; HMODULE hLib = ::LoadLibraryA("kernel32.dll"); if (NULL != hLib) { BOOL (WINAPI *pfn)(LPMEMORYSTATUSEX) = (BOOL (WINAPI*)(LPMEMORYSTATUSEX))::GetProcAddress(hLib, "GlobalMemoryStatusEx"); if (NULL != pfn) { MEMORYSTATUSEX mse; mse.dwLength = sizeof(mse); if (pfn(&mse)) { TotalPhysicalMemory = mse.ullTotalPhys; } } ::FreeLibrary(hLib); } if (TotalPhysicalMemory == 0) { MEMORYSTATUS ms; ::ZeroMemory(&ms, sizeof(ms)); ::GlobalMemoryStatus(&ms); TotalPhysicalMemory = ms.dwTotalPhys; } if (TotalPhysicalMemory == 0) { // Assume 128MB TotalPhysicalMemory = 128 * 1048576; } // Half of total physical memory memorylimit = (size_t)(TotalPhysicalMemory / 1048576 / 2); #elif __APPLE__ int name[2] = {CTL_HW, HW_USERMEM}; int usermem_bytes; size_t size = sizeof(usermem_bytes); sysctl( name, 2, &usermem_bytes, &size, NULL, 0 ); memorylimit = usermem_bytes / (2048 * 1024); #else #if WANT_CONCURRENT // Assume 128MB (otherwise processing is slower) memorylimit = 64; #else memorylimit = 16; #endif #endif } memorylimit *= 1048576; return true; }