From 16615a96ba636df93118d43a593a4fe034e2883c Mon Sep 17 00:00:00 2001 From: Andreas Adelmann Date: Sat, 5 Jan 2008 13:52:05 +0000 Subject: [PATCH] Initial commit of Christof's H5PartMerge --- .gitattributes | 4 + tools/H5PartMerge/H5merge.cpp | 1076 ++++++++++++++++++++++++++++++++ tools/H5PartMerge/make.sh | 3 + tools/H5PartMerge/optparse.cpp | 309 +++++++++ tools/H5PartMerge/optparse.h | 180 ++++++ 5 files changed, 1572 insertions(+) create mode 100644 tools/H5PartMerge/H5merge.cpp create mode 100755 tools/H5PartMerge/make.sh create mode 100644 tools/H5PartMerge/optparse.cpp create mode 100644 tools/H5PartMerge/optparse.h diff --git a/.gitattributes b/.gitattributes index 2fd175a..9df51a7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -78,6 +78,10 @@ test/H5test.cc -text test/H5testF.f -text test/H5testFpar.f90 -text test/Makefile.am -text +tools/H5PartMerge/H5merge.cpp -text +tools/H5PartMerge/make.sh -text +tools/H5PartMerge/optparse.cpp -text +tools/H5PartMerge/optparse.h -text tools/Makefile.am -text tools/README -text tools/h5pAttrib.cc -text diff --git a/tools/H5PartMerge/H5merge.cpp b/tools/H5PartMerge/H5merge.cpp new file mode 100644 index 0000000..45aad9b --- /dev/null +++ b/tools/H5PartMerge/H5merge.cpp @@ -0,0 +1,1076 @@ +/* Description + * + * This application merges and slices one, two or more H5Part files + * into a single one. The range of each file which should be merged/ + * sliced can be specified on the command line where each input file + * is handled similar to a python array. Negative indices are counted + * from the end of the file i.e. -1 signifies the last step. + * + * Copyright by Christof Kraus, 2007-2008, all rights reserved. + * + * \author Christof Kraus + * + * \date 2007 dec 27 + * + * \warning none + * + * \attention none required + * + * \bug this code is under permanent development. + * + * \todo ?? + */ +#include +#include +#include +#include +#include +#include "H5Part.h" +#include "optparse.h" + +#ifdef USE_BOOST +#include +#endif + +#define MAX_LEN 128 + +using namespace std; +struct strCmp { + bool operator()( const char* s1, const char* s2 ) const { + return strcmp( s1, s2 ) < 0; + } +}; + +struct StringCmp { + bool operator()( const string s1, const string s2 ) const { + return s1 < s2; + } +}; + +#ifdef USE_BOOST +using boost::any_cast; +typedef map StringAnyValueMap; +#endif + +typedef map StringIntMap; +typedef map StringInt64Map; + +h5part_int64_t copyFileAttribute(H5PartFile* In, H5PartFile* Out, string AttrName, h5part_int64_t Type, h5part_int64_t Num); +h5part_int64_t copyFileToStepAttribute(H5PartFile* In, H5PartFile* Out, string AttrName, h5part_int64_t Type, h5part_int64_t Num); + +#ifdef USE_BOOST +h5part_int64_t copyFileAttribute(H5PartFile* In, H5PartFile* Out, StringAnyValueMap &FileAttributes, string AttrName, h5part_int64_t Type, h5part_int64_t Num); +h5part_int64_t copyFileToStepAttribute(H5PartFile* In, H5PartFile* Out, StringAnyValueMap &FileAttributes, string AttrName, h5part_int64_t Type, h5part_int64_t Num); +#endif + +h5part_int64_t copyStepAttribute(H5PartFile* In, H5PartFile* Out, string AttrName, h5part_int64_t Type, h5part_int64_t Num); +h5part_int64_t copyDataset(H5PartFile* In, H5PartFile* Out, string SetName, h5part_int64_t Type, h5part_float64_t* FloatArray, h5part_int64_t* IntArray); + +int main(int argc, char **argv){ + + using optparse::STORE; + using optparse::STORE_TRUE; + using optparse::STORE_FALSE; + using optparse::STRING; + using optparse::BOOL; + using optparse::INT; + using optparse::DOUBLE; + + H5PartFile *H5OutFile, *H5InFile; + ifstream testopen; + + int NumInput = 0; + int effNumInput = 0; + string *InputFilenames = NULL; + string Input; + string Range; + string from_str; + int *from = NULL; + string to_str; + int *to = NULL; + + + string OutputFilename; + bool isOutputInput = false; + int OutputIsInput = 0; + + char AttrName[MAX_LEN]; + int NumFileAttr; + StringIntMap FileAttribName; + StringInt64Map FileAttribType; + StringInt64Map FileAttribNumType; + +#ifdef USE_BOOST + StringAnyValueMap FileAttributes; +#endif + + char SetName[MAX_LEN]; + int NumDataSets; + StringIntMap DataSetName; + StringInt64Map DataSetType; + + char StepAttrName[MAX_LEN]; + int NumStepAttr; + StringIntMap StepAttribName; + StringInt64Map StepAttribType; + StringInt64Map StepAttribNumType; + + StringIntMap::iterator itName; + StringInt64Map::iterator itType; + StringInt64Map::iterator itNumType; + + h5part_int64_t NumParticles = 0; + h5part_int64_t maxNumParticles = 0; + + h5part_int64_t NumSteps; + h5part_int64_t effStep = 0; + bool firstStepInput = true; + + h5part_float64_t* FloatValues = NULL; + h5part_int64_t* IntegerValues = NULL; + + h5part_int64_t Type; + h5part_int64_t SetType; + h5part_int64_t NumType; + h5part_int64_t SetNum; + + int verbosity = 0; + h5part_int64_t rc; + + string usage("H5merge\n\nmerges two or more H5Part files, but can also be used to slice a H5Part file\n\nUsage: H5merge [[options]] input1.h5[from1:to1] [input2.h5[from2:to2] ... inputN.h5[fromN:toN]] output.h5,\n where fromX and toX are the step numbers which determine the range to be merged.\n Negative values are counted from the end of the file\n\noptions:"); + optparse::OptionParser parser(usage); + + parser.add_option("-h", "--help", "help","Show this message",STORE_TRUE, BOOL, "0"); + parser.add_option("-v", "--verbose", "verbosity", "increase verbosity", STORE, INT, "0"); + parser.add_option("-r", "--replace", "replace", "If output file name is the same as input file replace the input file at the end", STORE_TRUE, BOOL, "0"); + parser.add_option("-f", "--intersect-file-attributes", "intersect-file-attributes", "merge only the intersection of file attributes from all input files", STORE_TRUE, BOOL, "0"); + parser.add_option("-s", "--intersect-step-attributes", "intersect-step-attributes", "merge only the intersection of step attributes from all steps in all input files", STORE_TRUE, BOOL, "0"); + parser.add_option("-d", "--intersect-datasets", "intersect-datasets", "merge only the intersection of datasets of all input files", STORE_TRUE, BOOL, "0"); + try { + parser.parse_args(argc, argv); + if (parser.get("help")) { + parser.help(cerr); + exit(0); + } + } + catch(optparse::OptionError e){ + cerr << e.what() << endl << endl; + parser.help(cerr); + exit(1); + } + + if (parser.arguments.size() < 2){ + cerr << "No output file found." << endl << endl; + parser.help(cerr); + exit(1); + } + + NumInput = parser.arguments.size(); //argc - 2; + InputFilenames = new string[NumInput]; + from = new int[NumInput]; + to = new int[NumInput]; + + verbosity = parser.get("verbosity"); + H5PartSetVerbosityLevel(verbosity); + + /**************************************************************\ + * Parse input file names and ranges to be merged + \**************************************************************/ + + for (int i = 0; i < NumInput - 1; ++i){ + Input = string(parser.arguments[i]); + + if (Input.find("[") != string::npos && Input.find("]") != string::npos){ + Range = Input; + Range.erase(0,Range.find_first_of("[")+1); + Range.erase(Range.find_first_of("]")); + if (Range.find(":") != string::npos){ + from_str = Range; + from_str.erase(from_str.find_first_of(":")); + if (from_str.length() == 0) + from[effNumInput] = 0; + else + from[effNumInput] = atoi(from_str.c_str()); + + to_str = Range; + to_str.erase(0,to_str.find_first_of(":")+1); + if (to_str.length() == 0) + to[effNumInput] = -1; + else + to[effNumInput] = atoi(to_str.c_str()); + }else{ + if (Range.length() > 0){ + from[effNumInput] = atoi(Range.c_str()); + to[effNumInput] = from[effNumInput]; + }else{ + from[effNumInput] = -1; + to[effNumInput] = 0; + } + } + Input.erase(Input.find_first_of("[")); + + }else{ + if (Input.find("[") != string::npos){ + Input.erase(Input.find_first_of("[")); + cerr << "error when assigning a range for file " << Input << endl; + exit(1); + } + if (Input.find("]") != string::npos){ + Input.erase(Input.find_first_of("]")); + cerr << "error when assigning a range for file " << Input << endl; + exit(1); + } + from[effNumInput] = 0; + to[effNumInput] = -1; + } + testopen.open(Input.c_str(),ios::in); + if (testopen.good()) + { + InputFilenames[effNumInput] = Input; + effNumInput++; + } + else + { + cerr << "Can't open " << Input << endl; + exit(1); + } + testopen.close(); + } + + OutputFilename = string(parser.arguments[NumInput - 1]); + + /**************************************************************\ + * check whether output file name is also an input filename; + * if true then append '_tmp' to the base name; + * rename it in the end + \**************************************************************/ + for (int i = 0; i < effNumInput; ++i){ + if (InputFilenames[i] == OutputFilename){ + if (!parser.get("replace")) + { + cout << "Do you really want to overwrite the input file " << InputFilenames[i] << "? [y/N] "; + char answer = '0'; + cin.get(answer); + + if (answer != 'y' && answer != 'Y') + { + cout << "Aborting..." << endl; + exit(1); + } + } + ostringstream tmp; + isOutputInput = true; + OutputIsInput = i; + OutputFilename.erase(OutputFilename.find_last_of(".")); + tmp << OutputFilename << "_tmp.h5"; + OutputFilename = tmp.str(); + break; + } + } + + /**************************************************************\ + * check whether output file allready exists + \**************************************************************/ + if (!isOutputInput) + { + testopen.open(OutputFilename.c_str(), ios::in); + if (testopen.good()) + { + testopen.close(); + if (!parser.get("replace")) + { + cout << "Do you really want to overwrite the file " << OutputFilename << "? [y/N] "; + char answer = '0'; + cin.get(answer); + + if (answer != 'y' && answer != 'Y') + { + cout << "Aborting..." << endl; + exit(1); + } + } + } + else if (parser.get("replace")) + { + cerr << "WARNING: option 'replace' chosen but output filename is not the same" << endl + << " as any of the input filenames" << endl; + } + } + + for (int i = 0; i < effNumInput; ++i) + { + H5InFile = H5PartOpenFile(InputFilenames[i].c_str(), H5PART_READ); + + /**************************************************************\ + * fix range if values are negative + \**************************************************************/ + int n_steps = H5PartGetNumSteps(H5InFile); + + if (from[i] >= n_steps) + from[i] = n_steps - 1; + else + while (from[i] < 0) + from[i] += n_steps; + + if (to[i] >= n_steps) + to[i] = n_steps - 1; + else + while (to[i] < 0) + to[i] += n_steps; + if (to[i] < from[i]) + { + int tmp; + tmp = to[i]; + to[i] = from[i]; + from[i] = tmp; + } + + NumSteps = to[i] - from[i] + 1; + + // cout << InputFilenames[i] << "[" << from[i] << ":" << to[i] << "]" << endl; + + for (int k = from[i]; k <= to[i]; ++k) + { + H5PartSetStep(H5InFile,k); + // cout << "Step set to " << k << endl; + + /**************************************************************\ + * determine maximum number of particles in all input files + * and all steps + \**************************************************************/ + h5part_int64_t localNumParticles = H5PartGetNumParticles(H5InFile); + if (localNumParticles > maxNumParticles) + maxNumParticles = localNumParticles; + } + + + /**************************************************************\ + * get the *intersection* of names of all file attributes + * --------------- " ------------------- step attributes + * --------------- " ------------------- datasets + * from all files which are to be merged + \**************************************************************/ + if (parser.get("intersect-file-attributes") || + parser.get("intersect-datasets") || + parser.get("intersect-step-attributes") ) + { + StringIntMap localStepAttribName; + StringInt64Map localStepAttribType; + StringInt64Map localStepAttribNumType; + StringIntMap localDataSetName; + StringInt64Map localDataSetType; + StringIntMap::iterator itlocalName; + StringInt64Map::iterator itlocalType; + StringInt64Map::iterator itlocalNumType; + + + if (parser.get("intersect-file-attributes")) + { + NumFileAttr = H5PartGetNumFileAttribs(H5InFile); + // cout << NumFileAttr << " file attributes" << endl; + for (int l = 0; l < NumFileAttr; ++l) + { + + H5PartGetFileAttribInfo(H5InFile, l, AttrName, MAX_LEN, &Type, &NumType); + // cout << "Found file attribute '" << AttrName << "'" << endl; + itName = FileAttribName.find(string(AttrName)); + if (itName == FileAttribName.end()) + { + FileAttribName.insert(make_pair(string(AttrName), 1)); + FileAttribType.insert(make_pair(string(AttrName), Type)); + FileAttribNumType.insert(make_pair(string(AttrName), NumType)); + // cout << "Added file attribute " << AttrName << endl; + } + else + { + if (FileAttribType[string(AttrName)] == Type) + { + if (FileAttribNumType[string(AttrName)] == NumType) + { + FileAttribName[string(AttrName)] += 1; + } + else + { + cerr << "WARNING: found same file attribute (" << AttrName << ") " << endl; + cerr << " with same type but different length! Will be skipped!" << endl; + } + } + else + { + cerr << "WARNING: found same file attribute (" << AttrName << ") " << endl; + cerr << " with different type! Will be skipped!" << endl; + } + } + } + } + + + for (int k = from[i]; k <= to[i]; ++k) + { + H5PartSetStep(H5InFile,k); + + if (parser.get("intersect-datasets")) + { + NumDataSets = H5PartGetNumDatasets(H5InFile); + for (h5part_int64_t m = 0; m < NumDataSets; ++m) + { + H5PartGetDatasetInfo(H5InFile, m, SetName, MAX_LEN, &SetType, &SetNum); + // cout << "Found dataset '" << SetName << "' " << endl; + itName = localDataSetName.find(string(SetName)); + if (itName == localDataSetName.end()) + { + localDataSetName.insert(make_pair(string(SetName), 1)); + localDataSetType.insert(make_pair(string(SetName), SetType)); + // cout << "Added dataset " << SetName << endl; + } + else + { + if (localDataSetType[string(SetName)] == SetType) + { + localDataSetName[string(SetName)] += 1; + } + else + { + cerr << "WARNING: found same dataset (" << SetName << ")" << endl; + cerr << " with different type! Will be skipped!" << endl; + } + } + } + } + + if (parser.get("intersect-step-attributes")) + { + NumStepAttr = H5PartGetNumStepAttribs(H5InFile); + for (int m = 0; m < NumStepAttr; ++m) + { + H5PartGetStepAttribInfo(H5InFile, m, StepAttrName, MAX_LEN, &Type, &NumType); + // cout << "Found Step Attribute '" << StepAttrName << "' " << endl; + itlocalName = localStepAttribName.find(string(StepAttrName)); + if (itlocalName == localStepAttribName.end()) + { + localStepAttribName.insert(make_pair(string(StepAttrName), 1)); + localStepAttribType.insert(make_pair(string(StepAttrName), Type)); + localStepAttribNumType.insert(make_pair(string(StepAttrName), NumType)); + } + else + { + if (localStepAttribType[string(StepAttrName)] == Type) + { + if (localStepAttribNumType[string(StepAttrName)] == NumType) + { + localStepAttribName[string(StepAttrName)] += 1; + } + else + { + cerr << "WARNING: found same step attribute (" << StepAttrName << ") " << endl; + cerr << " with same type but different length! Will be skipped!" << endl; + } + } + else + { + cerr << "WARNING: found same step attribute (" << StepAttrName << ")" << endl; + cerr << " with different type! Will be skipped!" << endl; + } + } + } + } + } + + if (parser.get("intersect-datasets")) + { + + // exclude those datasets which do not occur in all steps in file i + for (itlocalName = localDataSetName.begin(), itlocalType = localDataSetType.begin(); itlocalName != localDataSetName.end(); itlocalName++, itlocalType++) + { + if (itlocalName->second != NumSteps) + { + cerr << "WARNING: dataset '" << itlocalName->first << "' in file " << InputFilenames[i] << " only found in " << itlocalName->second << " of " << NumSteps << " steps;" << endl; + cerr << " Skipping this dataset;" << endl; + localDataSetName.erase(itlocalName); + } + else + { + itName = DataSetName.find(itlocalName->first); + if (itName == DataSetName.end()) + { + DataSetName.insert(make_pair(itlocalName->first, 1)); + DataSetType.insert(make_pair(itlocalName->first, itlocalType->second)); + } + else + { + if (DataSetType[itlocalName->first] == itlocalType->second) + { + DataSetName[itlocalName->first] += 1; + } + else + { + cerr << "WARNING: found same dataset (" << itlocalName->first << ")" << endl; + cerr << " with different type! Will be skipped!" << endl; + } + } + } + } + } + + if (parser.get("intersect-step-attributes")) + { + // exclude those step attributes which do not occur in all steps in file i + itlocalType = localStepAttribType.begin(); + itlocalNumType = localStepAttribNumType.begin(); + for (itlocalName = localStepAttribName.begin(); itlocalName != localStepAttribName.end(); itlocalName++, itlocalType++, itlocalNumType++) + { + if (itlocalName->second != NumSteps){ + cerr << "WARNING: step attribute '" << itlocalName->first << "' in file " << InputFilenames[i] << " only found in " << itlocalName->second << " of " << NumSteps << " steps;" << endl; + cerr << " Skipping this step attribute;" << endl; + // localStepAttribName.erase(itlocalName); + // localStepAttribType.erase(itlocalType); + // localStepAttribNumType.erase(itlocalNumType); + } + else + { + itName = StepAttribName.find(itlocalName->first); + if (itName == StepAttribName.end()) + { + StepAttribName.insert( make_pair(itlocalName->first, 1)); + StepAttribType.insert( make_pair(itlocalName->first, itlocalType->second)); + StepAttribNumType.insert( make_pair(itlocalName->first, itlocalNumType->second)); + } + else + { + if (StepAttribType[itlocalName->first] == itlocalType->second) + { + if (StepAttribNumType[itlocalName->first] == itlocalNumType->second) + { + StepAttribName[itlocalName->first] += 1; + } + else + { + cerr << "WARNING: found same step attribute (" << itlocalName->first << ") " << endl; + cerr << " with same type but different length! Will be skipped!" << endl; + } + } + else + { + cerr << "WARNING: found same step attribute (" << itlocalName->first << ")" << endl; + cerr << " with different type! Will be skipped!" << endl; + } + } + } + } + } + + H5PartCloseFile(H5InFile); + } + } + + + if (parser.get("intersect-file-attributes")) + { + //erase all those file attributes which do not occur in all files + itType = FileAttribType.begin(); + itNumType = FileAttribNumType.begin(); + for (itName = FileAttribName.begin(); itName != FileAttribName.end(); itName++, itType++, itNumType++) + { + if (itName->second != effNumInput) + { + cerr << "WARNING: file attribute '" << itName->first << "' only found in " << itName->second << " of " << effNumInput << " input files;" << endl; + cerr << " skipping this attribute!" << endl; + FileAttribName.erase(itName); + FileAttribType.erase(itType); + FileAttribNumType.erase(itNumType); + } + } + + if (verbosity > 0) + { + cout << "found the following file attributes (intersection of all file attributes):" << endl; + for (itName = FileAttribName.begin(); itName != FileAttribName.end(); ++itName) + { + cout << "file attribute '" << itName->first << "'" << endl; + } + cout << endl; + } + } + + if (parser.get("intersect-datasets")) + { + //erase all those datasets which do not occur in all steps of all files + for (itName = DataSetName.begin(), itType = DataSetType.begin(); itName != DataSetName.end(); itName++, itType++) + { + if (itName->second != effNumInput) + { + cerr << "WARNING: dataset '" << itName->first << "' only found in " << itName->second << " of " << effNumInput << " input files;" << endl; + cerr << " skipping this data set!" << endl; + DataSetName.erase(itName); + DataSetType.erase(itType); + } + } + + if (verbosity > 0) + { + cout << "found the following data sets (intersection of all datasets):" << endl; + for (itName = DataSetName.begin(); itName != DataSetName.end(); itName++) + cout << "dataset '" << itName->first << "'" << endl; + cout << endl; + } + } + + if (parser.get("intersect-step-attributes")) + { + //erase all those step attributes which do not occur in all steps of all fiels + itType = StepAttribType.begin(); + itNumType = StepAttribNumType.begin(); + for (itName = StepAttribName.begin(); itName != StepAttribName.end(); itName++, itType++, itNumType++) + { + if (itName->second != effNumInput) + { + cerr << "WARNING: step attribute '" << itName->first << "' only found in " << itName->second << " of " << effNumInput << " input files;" << endl; + cerr << " skipping this attribute!" << endl; + StepAttribName.erase(itName); + StepAttribType.erase(itType); + StepAttribNumType.erase(itNumType); + } + } + + if (verbosity > 0) + { + cout << "found the following step attributes (intersection of all step attributes):" << endl; + for (itName = StepAttribName.begin(); itName != StepAttribName.end(); ++itName) + { + cout << "step attribute '" << itName->first << "'" << endl; + } + cout << endl; + } + } + + + H5OutFile = H5PartOpenFile(OutputFilename.c_str(), H5PART_WRITE); + if (H5OutFile == NULL) + { + cerr << "ABORT: could not open '" << OutputFilename << "' for writing!" << endl; + exit(1); + } + + FloatValues = new h5part_float64_t[maxNumParticles]; + IntegerValues = new h5part_int64_t[maxNumParticles]; + + // cout << "ready to write data " << maxNumParticles << endl; + for (int i = 0; i < effNumInput; ++i) + { + H5InFile = H5PartOpenFile(InputFilenames[i].c_str(), H5PART_READ); + + H5PartSetStep(H5InFile,from[i]); + H5PartSetStep(H5OutFile,effStep); + + if (parser.get("intersect-file-attributes")) + { + itType = FileAttribType.begin(); + itNumType = FileAttribNumType.begin(); + for (itName = FileAttribName.begin(); itName != FileAttribName.end(); itName++, itType++, itNumType++) + { + // cout << "writing file attribute " << itName->first << endl; + if (i == 0) + { +#ifdef USE_BOOST + rc = copyFileAttribute(H5InFile, H5OutFile, FileAttributes, itName->first, itType->second, itNumType->second); +#else + rc = copyFileAttribute(H5InFile, H5OutFile, itName->first, itType->second, itNumType->second); +#endif + } + else + { +#ifdef USE_BOOST + rc = copyFileToStepAttribute(H5InFile, H5OutFile, FileAttributes, itName->first, itType->second, itNumType->second); +#else + rc = copyFileToStepAttribute(H5InFile, H5OutFile, itName->first, itType->second, itNumType->second); +#endif + } + if (rc != H5PART_SUCCESS) + cerr << "WARNING: could not write file attribute '" << AttrName << "'" << endl; + } + // cout << "wrote file attributes" << endl; + } + else + { + h5part_int64_t NumFileAttr = H5PartGetNumFileAttribs(H5InFile); + // cout << NumFileAttr << " file attributes" << endl; + for (h5part_int64_t l = 0; l < NumFileAttr; ++l) + { + + H5PartGetFileAttribInfo(H5InFile, l, AttrName, MAX_LEN, &Type, &NumType); + // cout << "Found file attribute '" << AttrName << "'" << endl; + if (i == 0) + { +#ifdef USE_BOOST + rc = copyFileAttribute(H5InFile, H5OutFile, FileAttributes, string(AttrName), Type, NumType); +#else + rc = copyFileAttribute(H5InFile, H5OutFile, string(AttrName), Type, NumType); +#endif + } + else + { +#ifdef USE_BOOST + rc = copyFileToStepAttribute(H5InFile, H5OutFile, FileAttributes, string(AttrName), Type, NumType); +#else + rc = copyFileToStepAttribute(H5InFile, H5OutFile, string(AttrName), Type, NumType); +#endif + } + if (rc != H5PART_SUCCESS) + cerr << "WARNING: could not write file attribute '" << AttrName << "'" << endl; + } + } + + firstStepInput = true; + for (int l = from[i]; l <= to[i]; ++l, ++effStep) + { + if(!firstStepInput) + { + rc = H5PartSetStep(H5InFile, l); + if (rc != H5PART_SUCCESS) + { + cerr << "ABORT: could not change to step #" << l << " in input file '" << InputFilenames[i] << "'" << endl; + rc = H5PartCloseFile(H5InFile); + rc = H5PartCloseFile(H5OutFile); + exit(1); + } + rc = H5PartSetStep(H5OutFile, effStep); + if (rc != H5PART_SUCCESS) + { + cerr << "ABORT: could not change to step #" << effStep << " in output file '" << OutputFilename << "'" << endl; + rc = H5PartCloseFile(H5InFile); + rc = H5PartCloseFile(H5OutFile); + exit(1); + } + } + else + { + firstStepInput = false; + } + NumParticles = H5PartGetNumParticles(H5InFile); + rc = H5PartSetNumParticles(H5OutFile, NumParticles); + if (rc != H5PART_SUCCESS) + { + cerr << "ABORT: could not set the number of particles in output file '" << OutputFilename << "'" << endl; + rc = H5PartCloseFile(H5InFile); + rc = H5PartCloseFile(H5OutFile); + exit(1); + } + if (parser.get("intersect-step-attributes")) + { + itType = StepAttribType.begin(); + itNumType = StepAttribNumType.begin(); + for (itName = StepAttribName.begin(); itName != StepAttribName.end(); ++itName, ++itType, ++itNumType) + { + rc = copyStepAttribute(H5InFile, H5OutFile, itName->first, itType->second, itNumType->second); + if (rc != H5PART_SUCCESS) + cerr << "WARNING: could not write step attribute '" << itName->first << "'" << endl; + } + } + else + { + h5part_int64_t NumStepAttr = H5PartGetNumStepAttribs(H5InFile); + for (h5part_int64_t m = 0; m < NumStepAttr; ++m) + { + H5PartGetStepAttribInfo(H5InFile, m, StepAttrName, MAX_LEN, &Type, &NumType); + rc = copyStepAttribute(H5InFile, H5OutFile, string(StepAttrName), Type, NumType); + if (rc != H5PART_SUCCESS) + cerr << "WARNING: could not write step attribute '" << StepAttrName << "'" << endl; + } + } + + if (parser.get("intersect-datasets")) + { + + for (itType = DataSetType.begin(); itType != DataSetType.end(); ++itType) + { + rc = copyDataset(H5InFile, H5OutFile, itType->first, itType->second, FloatValues, IntegerValues); + if (rc != H5PART_SUCCESS) + cerr << "WARNING: could not write dataset '" << itType->first << "'" << endl; + } + } + else + { + h5part_int64_t NumDataSets = H5PartGetNumDatasets(H5InFile); + for (h5part_int64_t m = 0; m < NumDataSets; ++m) + { + H5PartGetDatasetInfo(H5InFile, m, SetName, MAX_LEN, &SetType, &SetNum); + rc = copyDataset(H5InFile, H5OutFile, string(SetName), SetType, FloatValues, IntegerValues); + if (rc != H5PART_SUCCESS) + cerr << "WARNING: could not write dataset '" << SetName << "'" << endl; + } + } + + } + rc = H5PartCloseFile(H5InFile); + } + rc = H5PartCloseFile(H5OutFile); + + if (isOutputInput) + { + rename(OutputFilename.c_str(), InputFilenames[OutputIsInput].c_str()); + } + + delete[] FloatValues; + delete[] IntegerValues; + delete[] InputFilenames; + delete[] from; + delete[] to; +} + +h5part_int64_t copyFileAttribute(H5PartFile* In, H5PartFile* Out, string AttrName, h5part_int64_t Type, h5part_int64_t Num) +{ + h5part_int64_t rc; + if (Type == H5T_NATIVE_DOUBLE) + { + void *Attrib = (double*)malloc(sizeof(double)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (double*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteFileAttrib(Out, AttrName.c_str(), Type, (double*)Attrib, Num); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_CHAR) + { + void *Attrib = (char*)malloc(sizeof(char)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (char*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteFileAttrib(Out, AttrName.c_str(), Type, (char*)Attrib, Num); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_INT64) + { + void *Attrib = (h5part_int64_t*)malloc(sizeof(h5part_int64_t)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (h5part_int64_t*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteFileAttrib(Out, AttrName.c_str(), Type, (h5part_int64_t*)Attrib, Num); + free(Attrib); + return rc; + } + else + { + cerr << "WARNING: unknown type for file attribue " << AttrName << "! Skipping it. " << endl; + return H5PART_ERR_INVAL; + } +} + + +h5part_int64_t copyFileToStepAttribute(H5PartFile* In, H5PartFile* Out, string AttrName, h5part_int64_t Type, h5part_int64_t Num) +{ + h5part_int64_t rc; + if (Type == H5T_NATIVE_DOUBLE) + { + void *Attrib = (double*)malloc(sizeof(double)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (double*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (double*)Attrib, Num); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_CHAR) + { + void *Attrib = (char*)malloc(sizeof(char)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (char*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (char*)Attrib, Num); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_INT64) + { + void *Attrib = (h5part_int64_t*)malloc(sizeof(h5part_int64_t)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (h5part_int64_t*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (h5part_int64_t*)Attrib, Num); + free(Attrib); + return rc; + } + else + { + cerr << "WARNING: unknown type for file attribue " << AttrName << "! Skipping it. " << endl; + return H5PART_ERR_INVAL; + } +} + +h5part_int64_t copyStepAttribute(H5PartFile* In, H5PartFile* Out, string AttrName, h5part_int64_t Type, h5part_int64_t Num) +{ + h5part_int64_t rc; + if (Type == H5T_NATIVE_DOUBLE) + { + void* Attrib = (double*)malloc(sizeof(double)*Num);; + rc = H5PartReadStepAttrib(In, AttrName.c_str(), (double*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (double*)Attrib, Num); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_CHAR) + { + void* Attrib = (char*)malloc(sizeof(char)*Num);; + rc = H5PartReadStepAttrib(In, AttrName.c_str(), (char*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (char*)Attrib, Num); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_INT64) + { + void* Attrib = (h5part_int64_t*)malloc(sizeof(h5part_int64_t)*Num); + rc = H5PartReadStepAttrib(In, AttrName.c_str(), (h5part_int64_t*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (h5part_int64_t*)Attrib, Num); + free(Attrib); + return rc; + } + else + { + cerr << "WARNING: unknown type for step attribute " << AttrName << "! Skipping it." << endl; + return H5PART_ERR_INVAL; + } +} + + +h5part_int64_t copyDataset(H5PartFile* In, H5PartFile* Out, string SetName, h5part_int64_t Type, h5part_float64_t* FloatArray, h5part_int64_t* IntegerArray) +{ + h5part_int64_t rc; + if (Type == H5T_NATIVE_DOUBLE) + { + rc = H5PartReadDataFloat64(In, SetName.c_str(), FloatArray); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteDataFloat64(Out, SetName.c_str(), FloatArray); + return rc; + } + else if (Type == H5T_NATIVE_INT64) + { + rc = H5PartReadDataInt64(In, SetName.c_str(), IntegerArray); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteDataInt64(Out, SetName.c_str(), IntegerArray); + return rc; + } + else + { + cerr << "WARNING: unknown type for dataset " << SetName << "! Skipping it." << endl; + return H5PART_ERR_INVAL; + } +} + +#ifdef USE_BOOST +h5part_int64_t copyFileAttribute(H5PartFile* In, H5PartFile* Out, StringAnyValueMap &FileAttributes, string AttrName, h5part_int64_t Type, h5part_int64_t Num) +{ + h5part_int64_t rc; + if (Type == H5T_NATIVE_DOUBLE) + { + void *Attrib = (double*)malloc(sizeof(double)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (double*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteFileAttrib(Out, AttrName.c_str(), Type, (double*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes.insert(make_pair(AttrName,boost::any(*(double*)Attrib))); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_CHAR) + { + void *Attrib = (char*)malloc(sizeof(char)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (char*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteFileAttrib(Out, AttrName.c_str(), Type, (char*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes.insert(make_pair(AttrName,boost::any(string((char*)Attrib)))); + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_INT64) + { + void *Attrib = (h5part_int64_t*)malloc(sizeof(h5part_int64_t)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (h5part_int64_t*)Attrib); + if (rc == H5PART_SUCCESS) + rc = H5PartWriteFileAttrib(Out, AttrName.c_str(), Type, (h5part_int64_t*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes.insert(make_pair(AttrName,boost::any(*(h5part_int64_t*)Attrib))); + free(Attrib); + return rc; + } + else + { + cerr << "WARNING: unknown type for file attribue " << AttrName << "! Skipping it. " << endl; + return H5PART_ERR_INVAL; + } +} + + +h5part_int64_t copyFileToStepAttribute(H5PartFile* In, H5PartFile* Out, StringAnyValueMap &FileAttributes, string AttrName, h5part_int64_t Type, h5part_int64_t Num) +{ + h5part_int64_t rc; + if (Type == H5T_NATIVE_DOUBLE) + { + void *Attrib = (double*)malloc(sizeof(double)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (double*)Attrib); + if (rc == H5PART_SUCCESS) + { + if (FileAttributes.find(AttrName) == FileAttributes.end()) + { + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (double*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes.insert(make_pair(AttrName,boost::any(*(double*)Attrib))); + } + else if(any_cast(FileAttributes[AttrName]) != *(double*)Attrib) + { + rc = H5PartWriteStepAttrib(Out, (AttrName + " changed to").c_str(), Type, (double*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes[AttrName] = boost::any(*(double*)Attrib); + } + } + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_CHAR) + { + void *Attrib = (char*)malloc(sizeof(char)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (char*)Attrib); + if (rc == H5PART_SUCCESS) + { + if (FileAttributes.find(AttrName) == FileAttributes.end()) + { + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (char*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes.insert(make_pair(AttrName,boost::any(string((char*)Attrib)))); + } + else if(strcmp((any_cast(FileAttributes[AttrName])).c_str(),(char*)Attrib) != 0) + { + rc = H5PartWriteStepAttrib(Out, (AttrName + " changed to").c_str(), Type, (char*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes[AttrName] = boost::any(string((char*)Attrib)); + } + } + free(Attrib); + return rc; + } + else if (Type == H5T_NATIVE_INT64) + { + void *Attrib = (h5part_int64_t*)malloc(sizeof(h5part_int64_t)*Num); + rc = H5PartReadFileAttrib(In, AttrName.c_str(), (h5part_int64_t*)Attrib); + if (rc == H5PART_SUCCESS) + { + if (FileAttributes.find(AttrName) == FileAttributes.end()) + { + rc = H5PartWriteStepAttrib(Out, AttrName.c_str(), Type, (h5part_int64_t*)Attrib, Num); + FileAttributes.insert(make_pair(AttrName,boost::any((h5part_int64_t*)Attrib))); + } + else if(any_cast(FileAttributes[AttrName]) != *(h5part_int64_t*)Attrib) + { + rc = H5PartWriteStepAttrib(Out, (AttrName + " changed to").c_str(), Type, (h5part_int64_t*)Attrib, Num); + if (rc == H5PART_SUCCESS) + FileAttributes[AttrName] = boost::any((h5part_int64_t*)Attrib); + } + } + free(Attrib); + return rc; + } + else + { + cerr << "WARNING: unknown type for file attribue " << AttrName << "! Skipping it. " << endl; + return H5PART_ERR_INVAL; + } +} +#endif diff --git a/tools/H5PartMerge/make.sh b/tools/H5PartMerge/make.sh new file mode 100755 index 0000000..43fbf4a --- /dev/null +++ b/tools/H5PartMerge/make.sh @@ -0,0 +1,3 @@ +g++ -c -g optparse.cpp +g++ -c -g H5merge.cpp -I$HOME/include -DUSE_BOOST +g++ -g -o H5merge0.1 H5merge.o optparse.o -L$HOME/lib -lH5Part -lhdf5 -lz -lm diff --git a/tools/H5PartMerge/optparse.cpp b/tools/H5PartMerge/optparse.cpp new file mode 100644 index 0000000..5ab156e --- /dev/null +++ b/tools/H5PartMerge/optparse.cpp @@ -0,0 +1,309 @@ +/* + * Implementation of optparse.h + * + * Roman Geus, 2005 + * + */ + +#include +#include +#include "optparse.h" + +using namespace std; + +namespace optparse { + +/********************** Option **********************/ + +Option::Option (string shrt, string lng, string dest, + string hlp, action_t act, string dfault, type_t type, string allowed_args) + : action (act), + shrt_flag (shrt), + lng_flag (lng), + help (hlp), + destination (dest), + dfault_(dfault), + type_(type), + allowed_args_(allowed_args) +{} + +Option::~Option () {} + +bool +Option::is_allowed(std::string argument) { + // Test type + // FIXME: implement that + + // Test allowed values + if (allowed_args_ == "") + return true; + + size_t pos_begin = 0; + do { + size_t pos_comma = allowed_args_.find(',', pos_begin); + if (pos_comma == string::npos) + pos_comma = allowed_args_.size(); + if (allowed_args_.substr(pos_begin, pos_comma-pos_begin) == argument) + return true; + pos_begin = pos_comma + 1; + } while(pos_begin < allowed_args_.size()); + return false; +} + +/******************** OptionParser *******************/ + +OptionParser::OptionParser (string usage) + : use_msg (usage) +{ } + +OptionParser::~OptionParser () {} + +void +OptionParser::add_option(string shrt_flag, string lng_flag, string destination, + string help, action_t act, type_t type, string dfault, + string allowed_values) +{ + Option option(shrt_flag, lng_flag, destination, help, act, dfault, type, allowed_values); + + /* Add the option to our list of options. */ + opts.push_back(option); + + /* Set the default value for this option, this not only allows you to + * set default values but insures that every option's destination will + * be in our dictionary, even if the value is only "". + */ + set_option(option, dfault); +} + +string OptionParser::get_option(std::string option_name) { + string arg(options[option_name]); + size_t colon_pos = arg.find(':'); + return arg.substr(0, colon_pos); +} + +void +OptionParser::parse_args (int argc, char **argv) { + /* Walk through the arguments and sort them into options + * or arguments. + */ + for (int i = 1; i < argc; i++) { + /* If we start with a '-' we're probably a pair + * so we need to figure out which option it is. find_opt() is + * where the real work is done. + */ + if (argv[i][0] == '-') + if (argv[i][1] == '-') + find_opt_long(argc, argv, i); + else + find_opt_short(argc, argv, i); + + /* If we're looking at an argument (i.e. a value with no flag who's + * meaning is determined by position) just append it into the + * arguments list. + */ + else + arguments.insert(arguments.end(), argv[i]); + } +} + +void +OptionParser::help (ostream& os) { + const size_t WORD_WRAP = 50; // max width of last column + + // Determine column width for short options + size_t shrt_flag_max_len = 0; + for (size_t i=0; i < opts.size(); ++ i) { + stringstream buf; + buf << opts[i].shrt_flag; + if (opts[i].shrt_flag != "" && opts[i].action == STORE) + buf << " " << type2string(opts[i].type_); + if (buf.str().size() > shrt_flag_max_len) + shrt_flag_max_len = buf.str().size(); + } + + // Determine column width for long options + size_t lng_flag_max_len = 0; + for (size_t i=0; i < opts.size(); ++ i) { + stringstream buf; + buf << opts[i].lng_flag; + if (opts[i].action == STORE) + buf << "=" << type2string(opts[i].type_); + if (buf.str().size() > lng_flag_max_len) + lng_flag_max_len = buf.str().size(); + } + + os << use_msg << endl; + for (size_t i=0; i < opts.size(); ++ i) { + stringstream line; + line << " "; + + // short option column + stringstream shrt_buf; + shrt_buf << opts[i].shrt_flag; + if (opts[i].shrt_flag != "" && opts[i].action == STORE) + shrt_buf << " " << type2string(opts[i].type_); + line << ' ' << shrt_buf.str(); + for (size_t k = 0; k < shrt_flag_max_len-shrt_buf.str().size()+2; ++ k) + line << ' '; + + // long option column + stringstream buf; + buf << opts[i].lng_flag; + if (opts[i].action == STORE) + buf << "=" << type2string(opts[i].type_); + line << buf.str(); + for (size_t k = 0; k < lng_flag_max_len-buf.str().size()+4; ++ k) + line << ' '; + + // help column + os << line.str(); + size_t help_col = line.str().size(); + line.str(""); + + line << opts[i].help; + bool is_allowed = opts[i].allowed_args_ != ""; + bool is_default = opts[i].action == STORE && opts[i].dfault_ != ""; + if (is_allowed || is_default) { + line << " ("; + if (is_allowed) { + line << "possible values="; + size_t pos_begin = 0; + size_t pos_comma = opts[i].allowed_args_.find(',', pos_begin); + while (pos_comma != string::npos) { + line << '\'' << opts[i].allowed_args_.substr(pos_begin, pos_comma-pos_begin) << "\',"; + pos_begin = pos_comma + 1; + pos_comma = opts[i].allowed_args_.find(',', pos_begin); + } + line << '\'' << opts[i].allowed_args_.substr(pos_begin) << '\''; + if (is_default) + line << ", "; + } + if (is_default) + line << "default=\'" << opts[i].dfault_ << "\'"; + line << ')'; + } + + // split over several lines + size_t begin_pos = 0; + size_t last_pos = 0; + size_t next_pos; + + do { + next_pos = line.str().find_first_of(" ,", last_pos+1); + if (next_pos == string::npos) + next_pos = line.str().size()-1; + if (last_pos == line.str().size()-1 || (next_pos+1 - begin_pos > WORD_WRAP && last_pos > begin_pos)) { + if (begin_pos != 0) + for (size_t k = 0; k < help_col+2; ++ k) + os << ' '; + os << line.str().substr(begin_pos, last_pos+1 - begin_pos) << endl; + begin_pos = last_pos+1; + last_pos = begin_pos; + } else + last_pos = next_pos; + } while (begin_pos != line.str().size()); + } + } + +void +OptionParser::find_opt_short(int argc, char **argv, int &index) { + /* Step through our list of known options. */ + for (size_t i = 0; i < opts.size(); i++) { + /* Uses the overridden == operator for the Options class + * to compare argv[index] to the flags of each Option. + */ + if (opts[i].shrt_flag == (string)argv[index]) { + switch (opts[i].action) { + case STORE_FALSE: + set_option(opts[i], "0"); + break; + case STORE_TRUE: + set_option(opts[i], "1"); + break; + case STORE: + /* Set the value and return if we've found a match. */ + if (index >= argc-1) + throw OptionError(argv[index], "Missing option argument"); + set_option(opts[i], argv[index+1]); + index++; + break; + default: + break; + }; + return; + } + } + + /* If we haven't found a match this is not a known argument. */ + throw OptionError(argv[index], "Unknown option"); +} + +void +OptionParser::find_opt_long(int argc, char **argv, int &index) { + // Split option and argument at "=" + string option; + string argument; + string arg_str(argv[index]); + size_t equal_pos = arg_str.find('='); + if (equal_pos != string::npos) { + option = arg_str.substr(0, equal_pos); + argument = arg_str.substr(equal_pos+1, string::npos); + } else + option = arg_str; + + /* Step through our list of known options. */ + for (size_t i = 0; i < opts.size(); i++) { + if (opts[i].lng_flag == option) { + switch (opts[i].action) { + case STORE_FALSE: + if (argument != "") + throw OptionError(option, "No argument expected for this option"); + set_option(opts[i], "0"); + break; + case STORE_TRUE: + if (argument != "") + throw OptionError(option, "No argument expected for this option"); + set_option(opts[i], "1"); + break; + case STORE: + if (argument == "") + throw OptionError(option, "Missing option argument"); + set_option(opts[i], argument); + break; + default: + break; + }; + return; + } + } + + /* If we haven't found a match this is not a known argument. */ + throw OptionError(option, "Unknown option"); +} + +void OptionParser::set_option(Option& option, std::string argument) { + if (option.is_allowed(argument)) + options[option.destination] = argument; + else + throw OptionError("", "Invalid argument for option"); +} + +string OptionParser::type2string(type_t type) { + if (type == STRING) + return string("STRING"); + else if (type == DOUBLE) + return string("DOUBLE"); + else if (type == INT) + return string("INT"); + else if (type == BOOL) + return string("BOOL"); + else + return ""; +} + +template<> bool OptionParser::get(std::string option_name) +{ + return get_option(option_name) == "1"; +} + +} // namespace optparse diff --git a/tools/H5PartMerge/optparse.h b/tools/H5PartMerge/optparse.h new file mode 100644 index 0000000..7183673 --- /dev/null +++ b/tools/H5PartMerge/optparse.h @@ -0,0 +1,180 @@ +/* + * optparse.h + * + * An option parser for C++ modeled after the optparse Python + * module. Create an instance of OptionParser in your program + * and you can add options to it then call the method + * parse_args with argc and argv and it will parse them. The + * '-h' and '--help' options are built in, they print the + * usage message. + * + * W. Evan Sheehan + * + */ + +#ifndef OPTPARSE_H +#define OPTPARSE_H + +#include +#include +#include +#include +#include + +namespace optparse { + +/** + * Action type. + * Use this type to determine what we're storing, naming + * convention is the same as the optparse python module. + * Even with STORE_FALSE and STORE_TRUE the values are + * going to be stored as strings, false is "0" true is "1" + * so you can just atoi() the value and use that for tests. + */ +typedef enum {STORE_FALSE=0, STORE_TRUE, STORE} action_t; + +/** + * Option argument type. + * Defines expected option argument type. + */ +typedef enum {STRING=0, BOOL, INT, DOUBLE} type_t; + +/** Option for OptionParser */ +class Option { +public: + Option (std::string shrt, std::string lng, std::string dest, + std::string hlp, action_t act, std::string dfault, type_t type, + std::string allowed_args); + ~Option (); + + /** + * Validate option argument. + * Test if argument is valid for the option. The argument must be valid w.r.t. + * to type_ and allowed_values_. + * @param argument Argument to be tested. + * @return true if argument is valid. + */ + bool is_allowed(std::string argument); + + action_t action; // define what we store + std::string shrt_flag; // short flag, something like -o + std::string lng_flag; // long flag, something like --outfile + std::string help; // help message about the option + std::string destination; // the key used to store this option in the parser. + /** + * Default value. An empty string means no default value exist. + */ + std::string dfault_; + /** + * Expected type the argument of this option should have. + * Only relevant for STORE action. + * Used for generating the usage string. + */ + type_t type_; + std::string allowed_args_; //!< Comma-separated list of allowed option arguments. +}; + +class OptionError : public std::runtime_error { +public: + OptionError(std::string option, std::string error_msg) + : std::runtime_error("") { + std::ostringstream str; + str << "OptionParser error: option: " << option << ", cause: " << error_msg; + msg_ = str.str(); + } + virtual ~OptionError() throw() {} + /** + * Human-readable error message. + * @return Pointer to error message. + */ + virtual const char* what() const throw() { return msg_.c_str(); } +protected: + std::string msg_; +}; + +/** Option parser. */ +class OptionParser { +public: + OptionParser (std::string usage=""); + virtual ~OptionParser (); + + /** + * Add an option to the parser. + * @param shrt_flag Short option name, like e.g. "-q". + * @param lng_flag Long option name, like e.g. "--quiet" + * @param destination Key under which the option argument is stored in the dictionary. + * @param help Help string for generating the usage info. + * @param act Action, one of STORE, STORE_TRUE, STORE_FALSE. + * @param type Type info of the expected option argument. One of INT, DOUBLE, STRING, BOOL. + * @param dfault Default value. Value stored in the dictionary if the option is not given. + * @param allowed_values List of possible option values. + * A string of comma-separated allowed values. An empty string means + * that any value is allowed. + */ + void add_option (std::string shrt_flag, std::string lng_flag, + std::string destination, std::string help="", action_t act=STORE, + type_t type=STRING, std::string dfault="", + std::string allowed_values=""); + + /** + * Get option argument. + * @param option_name Name of option, as given in "destination" parameter of "add_option". + * @return Option argument with type appendix removed. + */ + std::string get_option(std::string option_name); + template T get(std::string option_name); + /* Parse the commandline args. */ + void parse_args (int argc, char **argv); + /** + * Write usage info to stream. + * The usage info includes a formatted list of all options the parser knows about, + * including the help string, expected argument type and default value. + * @param os Output stream the usage info is written to. + */ + void help (std::ostream& os); + /** + * Convert type_t type to human-readable string. + * This is used for generating the usage string and also in + * TypeOptionParser to encode the argument type in the options dictionary. + * @see help + * @param type Type. + * @return Converted string. + */ + std::string type2string(type_t type); + /** + * Type of "options" dictionary. + */ + typedef std::map options_type; + options_type options; //!< Dictionary of options. Use options[key] to access. + std::vector arguments; //!< List of positional arguments. +protected: + /** + * Set option in dictionary. + * Function the parser uses to store an option argument or a default value in + * options dictionary. Can be overridden in a subclass to store additional info. + * @param option Option object. + * @param argument Option argument to store. + */ + virtual void set_option(Option& option, std::string argument); + +private: + std::string use_msg; //!< Usage message. + std::vector