Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-02-14 13:32:23

0001 // -*- C++ -*-
0002 #include <iostream>
0003 #include <fstream>
0004 #include <iomanip>
0005 #include <cstdlib>
0006 #include <cassert>
0007 
0008 #include "TString.h"
0009 
0010 #include "PhysicsTools/FWLite/interface/CommandLineParser.h"
0011 #include "PhysicsTools/FWLite/interface/dout.h"
0012 #include "PhysicsTools/FWLite/interface/dumpSTL.icc"
0013 
0014 using namespace std;
0015 using namespace optutl;
0016 
0017 const std::string CommandLineParser::kSpaces = " \t";
0018 
0019 CommandLineParser::CommandLineParser(const string &usage, unsigned int optionsType)
0020     : m_argv0(""), m_usageString(usage), m_printOptions(true), m_optionsType(optionsType) {
0021   if (m_optionsType & kEventContOpt) {
0022     // Integer options
0023     addOption("totalSections", kInteger, "Total number of sections", 0);
0024     addOption("section", kInteger, "This section (from 1..totalSections inclusive)", 0);
0025     addOption("maxEvents", kInteger, "Maximum number of events to run over (0 for whole file)", 0);
0026     addOption("jobID", kInteger, "jobID given by CRAB,etc. (-1 means append nothing)", -1);
0027     addOption("outputEvery", kInteger, "Output something once every N events (0 for never)", 0);
0028     // String options
0029     addOption("outputFile", kString, "Output filename", "output.root");
0030     addOption("storePrepend", kString, "Prepend location on files starting with '/store/'");
0031     addOption("tag", kString, "A 'tag' to append to output file (e.g., 'v2', etc.)");
0032     // Bool options
0033     addOption("logName", kBool, "Print log name and exit");
0034     // String vector options
0035     addOption("inputFiles", kStringVector, "List of input files");
0036     addOption("secondaryInputFiles", kStringVector, "List of secondary input files (a.k.a. two-file-solution");
0037     addOption("orderedSecondaryFiles", kBool, "Are the secondary files ordered?", false);
0038     return;
0039   }
0040   // If we're still here, we have a type I don't understand.
0041   cerr << "CommandLineParser() Error: type '" << optionsType << "' is not understood.  Aborting." << endl;
0042   assert(0);
0043 }
0044 
0045 void CommandLineParser::parseArguments(int argc, char **argv, bool returnArgs) {
0046   bool callHelp = false;
0047   SVec argsVec;
0048   m_argv0 = argv[0];
0049   m_fullArgVec.push_back(argv[0]);
0050   for (int loop = 1; loop < argc; ++loop) {
0051     string arg = argv[loop];
0052     m_fullArgVec.push_back(arg);
0053     string::size_type where = arg.find_first_of('=');
0054     if (string::npos != where) {
0055       if (_setVariableFromString(arg)) {
0056         continue;
0057       }
0058       if (_runVariableCommandFromString(arg)) {
0059         continue;
0060       }
0061       cerr << "Don't understand: " << arg << endl;
0062       exit(0);
0063     }  // tag=value strings
0064     else if (arg.at(0) == '-') {
0065       string::size_type where = arg.find_first_not_of('-');
0066       if (string::npos == where) {
0067         // a poorly formed option
0068         cerr << "Don't understand: " << arg << endl;
0069         exit(0);
0070         continue;
0071       }
0072       lowercaseString(arg);
0073       char first = arg.at(where);
0074       // Print the values
0075       if ('p' == first) {
0076         m_printOptions = true;
0077         continue;
0078       }
0079       if ('n' == first) {
0080         m_printOptions = false;
0081         continue;
0082       }
0083       // Exit after printing values
0084       if ('h' == first) {
0085         callHelp = true;
0086         continue;
0087       }
0088       // if we're still here, then we've got a problem.
0089       cerr << "Don't understand: " << arg << endl;
0090       exit(0);
0091     }  // -arg strings
0092     if (returnArgs) {
0093       argsVec.push_back(arg);
0094     } else {
0095       cerr << "Don't understand: " << arg << endl;
0096       exit(0);
0097     }
0098   }  // for loop
0099   if (callHelp) {
0100     help();
0101   }
0102 }
0103 
0104 void CommandLineParser::help() {
0105   if (m_usageString.length()) {
0106     cout << m_argv0 << " - " << m_usageString << endl;
0107   }
0108   cout << "--help    - This screen" << endl
0109        << "--noPrint - Do not print out all settings" << endl
0110        << "--print   - Print out all settings" << endl;
0111   printOptionValues();
0112   exit(0);
0113 }
0114 
0115 void CommandLineParser::split(SVec &retval, string line, string match, bool ignoreComments) {
0116   if (ignoreComments) {
0117     removeComment(line);
0118   }  // if ignoreComments
0119   retval.clear();
0120   // find the first non-space
0121   string::size_type start1 = line.find_first_not_of(kSpaces);
0122   // Is the first non-space character a '#'
0123   char firstCh = line[start1];
0124   if ('#' == firstCh) {
0125     // this line is a comment
0126     return;
0127   }
0128 
0129   line += match;  // get last word of line
0130   string::size_type last = string::npos;
0131   string::size_type current = line.find_first_of(match);
0132   while (string::npos != current) {
0133     string::size_type pos;
0134     if (string::npos != last) {
0135       pos = last + 1;
0136     } else {
0137       pos = 0;
0138     }
0139     string part = line.substr(pos, current - last - 1);
0140     // don't bother adding 0 length strings
0141     if (part.length()) {
0142       retval.push_back(part);
0143     }
0144     last = current;
0145     current = line.find_first_of(match, current + 1);
0146   }  // while we're finding spaces
0147 }
0148 
0149 void CommandLineParser::removeComment(string &line) {
0150   string::size_type location = line.find('#');
0151   if (string::npos != location) {
0152     // we've got a comment.  Strip it out
0153     line = line.substr(0, location - 1);
0154   }  // if found
0155 }
0156 
0157 void CommandLineParser::removeLeadingAndTrailingSpaces(std::string &line) {
0158   string::size_type pos = line.find_first_not_of(kSpaces);
0159   if (string::npos == pos) {
0160     // we don't have anything here at all.  Just quit now
0161     return;
0162   }
0163   if (pos) {
0164     // We have spaces at the beginning.
0165     line = line.substr(pos);
0166   }
0167   pos = line.find_last_not_of(kSpaces);
0168   if (pos + 1 != line.length()) {
0169     // we've got spaces at the end
0170     line = line.substr(0, pos + 1);
0171   }
0172 }
0173 
0174 string CommandLineParser::removeEnding(const string &input, const string &ending) {
0175   string::size_type position = input.rfind(ending);
0176   if (input.length() - ending.length() == position) {
0177     // we've got it
0178     return input.substr(0, position);
0179   }
0180   // If we're still here, it wasn't there
0181   return input;
0182 }
0183 
0184 void CommandLineParser::findCommand(const string &line, string &command, string &rest) {
0185   command = rest = "";
0186   string::size_type nonspace = line.find_first_not_of(kSpaces);
0187   if (string::npos == nonspace) {
0188     // we don't have anything here at all.  Just quit now
0189     return;
0190   }
0191   string::size_type space = line.find_first_of(kSpaces, nonspace);
0192   if (string::npos == space) {
0193     // we only have a command and nothing else
0194     command = line.substr(nonspace);
0195     return;
0196   }
0197   command = line.substr(nonspace, space - nonspace);
0198   rest = line.substr(space + 1);
0199   removeLeadingAndTrailingSpaces(rest);
0200 }
0201 
0202 void CommandLineParser::printOptionValues() {
0203   cout << "------------------------------------------------------------------" << left << endl;
0204   // Print the integers next
0205   if (!m_integerMap.empty()) {
0206     cout << endl << "Integer options:" << endl;
0207   }
0208   for (SIMapConstIter iter = m_integerMap.begin(); m_integerMap.end() != iter; ++iter) {
0209     const string &description = m_variableDescriptionMap[iter->first];
0210     cout << "    " << setw(14) << iter->first << " = " << setw(14) << iter->second;
0211     if (description.length()) {
0212       cout << " - " << description;
0213     }
0214     cout << endl;
0215   }  // for iter
0216 
0217   // Print the doubles next
0218   if (!m_doubleMap.empty()) {
0219     cout << endl << "Double options:" << endl;
0220   }
0221   for (SDMapConstIter iter = m_doubleMap.begin(); m_doubleMap.end() != iter; ++iter) {
0222     const string &description = m_variableDescriptionMap[iter->first];
0223     cout << "    " << setw(14) << iter->first << " = " << setw(14) << iter->second;
0224     if (description.length()) {
0225       cout << " - " << description;
0226     }
0227     cout << endl;
0228   }  // for iter
0229 
0230   // Print the bools first
0231   if (!m_boolMap.empty()) {
0232     cout << endl << "Bool options:" << endl;
0233   }
0234   for (SBMapConstIter iter = m_boolMap.begin(); m_boolMap.end() != iter; ++iter) {
0235     const string &description = m_variableDescriptionMap[iter->first];
0236     cout << "    " << setw(14) << iter->first << " = " << setw(14);
0237     if (iter->second) {
0238       cout << "true";
0239     } else {
0240       cout << "false";
0241     }
0242     if (description.length()) {
0243       cout << " - " << description;
0244     }
0245     cout << endl;
0246   }  // for iter
0247 
0248   // Print the strings next
0249   if (!m_stringMap.empty()) {
0250     cout << endl << "String options:" << endl;
0251   }
0252   for (SSMapConstIter iter = m_stringMap.begin(); m_stringMap.end() != iter; ++iter) {
0253     const string &description = m_variableDescriptionMap[iter->first];
0254     cout << "    " << setw(14) << iter->first << " = ";
0255     const string value = "'" + iter->second + "'";
0256     cout << setw(14) << "";
0257     if (description.length()) {
0258       cout << " - " << description;
0259     }
0260     cout << endl << "        " << value << endl;
0261   }  // for iter
0262 
0263   // Integer Vec
0264   if (!m_integerVecMap.empty()) {
0265     cout << endl << "Integer Vector options:" << endl;
0266   }
0267   for (SIVecMapConstIter iter = m_integerVecMap.begin(); m_integerVecMap.end() != iter; ++iter) {
0268     const string &description = m_variableDescriptionMap[iter->first];
0269     cout << "    " << setw(14) << iter->first << " = ";
0270     dumpSTL(iter->second);
0271     cout << endl;
0272     if (description.length()) {
0273       cout << "      - " << description;
0274     }
0275     cout << endl;
0276   }  // for iter
0277 
0278   // Double Vec
0279   if (!m_doubleVecMap.empty()) {
0280     cout << endl << "Double Vector options:" << endl;
0281   }
0282   for (SDVecMapConstIter iter = m_doubleVecMap.begin(); m_doubleVecMap.end() != iter; ++iter) {
0283     const string &description = m_variableDescriptionMap[iter->first];
0284     cout << "    " << setw(14) << iter->first << " = ";
0285     dumpSTL(iter->second);
0286     cout << endl;
0287     if (description.length()) {
0288       cout << "      - " << description;
0289     }
0290     cout << endl;
0291   }  // for iter
0292 
0293   // String Vec
0294   if (!m_stringVecMap.empty()) {
0295     cout << endl << "String Vector options:" << endl;
0296   } else {
0297     cout << endl;
0298   }
0299   for (SSVecMapConstIter iter = m_stringVecMap.begin(); m_stringVecMap.end() != iter; ++iter) {
0300     const string &description = m_variableDescriptionMap[iter->first];
0301     cout << "    " << setw(14) << iter->first << " = ";
0302     if (description.length()) {
0303       cout << setw(14) << ""
0304            << " - " << description;
0305     }
0306     cout << endl;
0307     dumpSTLeachEndl(iter->second, 8);
0308   }  // for iter
0309 
0310   cout << "------------------------------------------------------------------" << right << endl;
0311 }
0312 
0313 bool CommandLineParser::_setVariableFromString(const string &arg, bool dontOverrideChange, int offset) {
0314   string::size_type where = arg.find_first_of('=', offset + 1);
0315   string varname = arg.substr(offset, where - offset);
0316   string value = arg.substr(where + 1);
0317   lowercaseString(varname);
0318   // check to make sure this is a valid option
0319   SBMapConstIter sbiter = m_variableModifiedMap.find(varname);
0320   if (m_variableModifiedMap.end() == sbiter) {
0321     // Not found.  Not a valid option
0322     return false;
0323   }
0324   // if 'dontOverrideChange' is set, then we are being asked to NOT
0325   // change any variables that have already been changed.
0326   if (dontOverrideChange && _valueHasBeenModified(varname)) {
0327     // don't go any further
0328     return true;
0329   }
0330   // integers
0331   SIMapIter integerIter = m_integerMap.find(varname);
0332   if (m_integerMap.end() != integerIter) {
0333     // we found it
0334     // use 'atof' instead of 'atoi' to get scientific notation
0335     integerIter->second = (int)atof(value.c_str());
0336     m_variableModifiedMap[varname] = true;
0337     return true;
0338   }
0339   // double
0340   SDMapIter doubleIter = m_doubleMap.find(varname);
0341   if (m_doubleMap.end() != doubleIter) {
0342     // we found it
0343     doubleIter->second = atof(value.c_str());
0344     m_variableModifiedMap[varname] = true;
0345     return true;
0346   }
0347   // string
0348   SSMapIter stringIter = m_stringMap.find(varname);
0349   if (m_stringMap.end() != stringIter) {
0350     // we found it
0351     stringIter->second = value;
0352     m_variableModifiedMap[varname] = true;
0353     return true;
0354   }
0355   // bool
0356   SBMapIter boolIter = m_boolMap.find(varname);
0357   if (m_boolMap.end() != boolIter) {
0358     // we found it
0359     boolIter->second = 0 != atoi(value.c_str());
0360     m_variableModifiedMap[varname] = true;
0361     return true;
0362   }
0363   // IntegerVec
0364   SIVecMapIter integerVecIter = m_integerVecMap.find(varname);
0365   if (m_integerVecMap.end() != integerVecIter) {
0366     // we found it
0367     SVec words;
0368     split(words, value, ",");
0369     for (SVecConstIter wordIter = words.begin(); words.end() != wordIter; ++wordIter) {
0370       integerVecIter->second.push_back((int)atof(wordIter->c_str()));
0371     }
0372     // we don't want to mark this as modified because we can add
0373     // many values to this
0374     // m_variableModifiedMap[varname] = true;
0375     return true;
0376   }
0377   // DoubleVec
0378   SDVecMapIter doubleVecIter = m_doubleVecMap.find(varname);
0379   if (m_doubleVecMap.end() != doubleVecIter) {
0380     // we found it
0381     SVec words;
0382     split(words, value, ",");
0383     for (SVecConstIter wordIter = words.begin(); words.end() != wordIter; ++wordIter) {
0384       doubleVecIter->second.push_back(atof(wordIter->c_str()));
0385     }
0386     // we don't want to mark this as modified because we can add
0387     // many values to this
0388     // m_variableModifiedMap[varname] = true;
0389     return true;
0390   }
0391   // StringVec
0392   SSVecMapIter stringVecIter = m_stringVecMap.find(varname);
0393   if (m_stringVecMap.end() != stringVecIter) {
0394     // we found it
0395     SVec words;
0396     split(words, value, ",");
0397     for (SVecConstIter wordIter = words.begin(); words.end() != wordIter; ++wordIter) {
0398       stringVecIter->second.push_back(*wordIter);
0399     }
0400     // we don't want to mark this as modified because we can add
0401     // many values to this
0402     // m_variableModifiedMap[varname] = true;
0403     return true;
0404   }
0405   // We didn't find your variable.  And we really shouldn't be here
0406   // because we should have know that we didn't find your variable.
0407   cerr << "CommandLineParser::SetVeriableFromString() Error: "
0408        << "Unknown variable and internal fault.  Aborting." << endl;
0409   assert(0);
0410   return false;
0411 }
0412 
0413 bool CommandLineParser::_setVariablesFromFile(const string &filename) {
0414   ifstream source(filename.c_str(), ios::in);
0415   if (!source) {
0416     cerr << "file " << filename << "could not be opened" << endl;
0417     return false;
0418   }
0419   string line;
0420   while (getline(source, line)) {
0421     // find the first nonspace
0422     string::size_type where = line.find_first_not_of(kSpaces);
0423     if (string::npos == where) {
0424       // no non-spaces
0425       continue;
0426     }
0427     char first = line.at(where);
0428     if ('-' != first) {
0429       continue;
0430     }
0431     where = line.find_first_not_of(kSpaces, where + 1);
0432     if (string::npos == where) {
0433       // no non-spaces
0434       continue;
0435     }
0436     // Get string starting at first nonspace after '-'.  Copy it to
0437     // another string without copying any spaces and stopping at the
0438     // first '#'.
0439     string withspaces = line.substr(where);
0440     string nospaces;
0441     for (int position = 0; position < (int)withspaces.length(); ++position) {
0442       char ch = withspaces[position];
0443       if ('#' == ch) {
0444         // start of a comment
0445         break;
0446       } else if (' ' == ch || '\t' == ch) {
0447         continue;
0448       }
0449       nospaces += ch;
0450     }  // for position
0451     if (!_setVariableFromString(nospaces, true)) {
0452       cerr << "Don't understand line" << endl
0453            << line << endl
0454            << "in options file '" << filename << "'.  Aborting." << endl;
0455       exit(0);
0456     }  // if setting variable failed
0457   }    // while getline
0458   return true;
0459 }
0460 
0461 bool CommandLineParser::_runVariableCommandFromString(const string &arg) {
0462   SVec equalWords;
0463   split(equalWords, arg, "=");
0464   if (2 != equalWords.size()) {
0465     return false;
0466   }
0467   SVec commandWords;
0468   split(commandWords, equalWords.at(0), "_");
0469   if (2 != commandWords.size()) {
0470     return false;
0471   }
0472   string &command = commandWords.at(1);
0473   lowercaseString(command);
0474   if (command != "load" && command != "clear") {
0475     return false;
0476   }
0477   const string &key = commandWords.at(0);
0478   OptionType type = hasOption(key);
0479   if (type < kIntegerVector || type > kStringVector) {
0480     cerr << "Command '" << command << "' only works on vectors." << endl;
0481     return false;
0482   }
0483 
0484   ///////////
0485   // Clear //
0486   ///////////
0487   if ("clear" == command) {
0488     if (kIntegerVector == type) {
0489       integerVector(key).clear();
0490     } else if (kDoubleVector == type) {
0491       doubleVector(key).clear();
0492     } else if (kStringVector == type) {
0493       stringVector(key).clear();
0494     } else {
0495       // If we're here, then I made a coding mistake and want to
0496       // know about it.
0497       assert(0);
0498     }
0499     return true;
0500   }
0501 
0502   //////////
0503   // Load //
0504   //////////
0505   const string &filename = equalWords.at(1);
0506   ifstream source(filename.c_str(), ios::in);
0507   if (!source) {
0508     cerr << "file " << filename << "could not be opened" << endl;
0509     return false;
0510   }
0511   string line;
0512   while (getline(source, line)) {
0513     // find the first nonspace
0514     string::size_type where = line.find_first_not_of(kSpaces);
0515     if (string::npos == where) {
0516       // no non-spaces
0517       continue;
0518     }
0519     // get rid of leading spaces
0520     line = line.substr(where);
0521     // get rid of trailing spaces
0522     where = line.find_last_not_of(kSpaces);
0523     if (line.length() - 1 != where) {
0524       line = line.substr(0, where + 1);
0525     }
0526     if ('#' == line.at(0)) {
0527       // this is a comment line, ignore it
0528       continue;
0529     }
0530     if (kIntegerVector == type) {
0531       integerVector(key).push_back((int)atof(line.c_str()));
0532     } else if (kDoubleVector == type) {
0533       doubleVector(key).push_back(atof(line.c_str()));
0534     } else if (kStringVector == type) {
0535       stringVector(key).push_back(line);
0536     } else {
0537       // If we're here, then I made a coding mistake and want to
0538       // know about it.
0539       assert(0);
0540     }
0541   }  // while getline
0542   return true;
0543 }
0544 
0545 void CommandLineParser::_getSectionFiles(const SVec &inputList, SVec &outputList, int section, int totalSections) {
0546   // Make sure the segment numbers make sense
0547   assert(section > 0 && section <= totalSections);
0548 
0549   // The Perl code:
0550   // my $entries    = @list;
0551   // my $perSection = int ($entries / $totalSections);
0552   // my $extra      = $entries % $totalSections;
0553   // --$section; # we want 0..n-1 not 1..n
0554   // my $start = $perSection * $section;
0555   // my $num   = $perSection - 1;
0556   // if ($section < $extra) {
0557   //    $start += $section;
0558   //    ++$num;
0559   // } else {
0560   //    $start += $extra;
0561   // };
0562   // my $end = $start + $num;
0563   int entries = (int)inputList.size();
0564   int perSection = entries / totalSections;
0565   int extra = entries % totalSections;
0566   --section;  // we want 0..n-1, not 1..n.
0567   int current = perSection * section;
0568   int num = perSection - 1;
0569   if (section < extra) {
0570     current += section;
0571     ++num;
0572   } else {
0573     current += extra;
0574   }
0575   outputList.clear();
0576   // we want to go from 0 to num inclusive, so make sure we have '<='
0577   // and not '='
0578   for (int loop = current; loop <= current + num; ++loop) {
0579     outputList.push_back(inputList.at(loop));
0580   }  // for loop
0581 }
0582 
0583 void CommandLineParser::_finishDefaultOptions(std::string tag) {
0584   if (!(m_optionsType & kEventContOpt)) {
0585     // nothing to see here, folks
0586     return;
0587   }
0588   ////////////////////////
0589   // Deal with sections //
0590   ////////////////////////
0591   if (integerValue("totalSections")) {
0592     // we have a request to break this into sections.  First, note
0593     // this in the output file
0594     tag += Form("_sec%03d", integerValue("section"));
0595     SVec tempVec;
0596     _getSectionFiles(stringVector("inputFiles"), tempVec, integerValue("section"), integerValue("totalSections"));
0597     stringVector("inputFiles") = tempVec;
0598   }  // if section requested
0599 
0600   //////////////////////
0601   // Store lfn to pfn //
0602   //////////////////////
0603   const string &kStorePrepend = stringValue("storePrepend");
0604   if (kStorePrepend.length()) {
0605     string match = "/store/";
0606     int matchLen = match.length();
0607     SVec tempVec;
0608     SVec &currentFiles = stringVector("inputFiles");
0609     for (SVecConstIter iter = currentFiles.begin(); currentFiles.end() != iter; ++iter) {
0610       const string &filename = *iter;
0611       if ((int)filename.length() > matchLen && filename.substr(0, matchLen) == match) {
0612         tempVec.push_back(kStorePrepend + filename);
0613       } else {
0614         tempVec.push_back(filename);
0615       }
0616     }
0617     currentFiles = tempVec;
0618   }  // if storePrepend.
0619 
0620   //////////////////////////////////
0621   // //////////////////////////// //
0622   // // Modify output filename // //
0623   // //////////////////////////// //
0624   //////////////////////////////////
0625   string outputFile = stringValue("outputFile");
0626   bool modifyOutputFile = (outputFile.length());
0627   outputFile = removeEnding(outputFile, ".root");
0628   outputFile += tag;
0629   if (integerValue("maxEvents")) {
0630     outputFile += Form("_maxevt%03d", integerValue("maxEvents"));
0631   }
0632   if (integerValue("jobID") >= 0) {
0633     outputFile += Form("_jobID%03d", integerValue("jobID"));
0634   }
0635   if (stringValue("tag").length()) {
0636     outputFile += "_" + stringValue("tag");
0637   }
0638 
0639   /////////////////////////////////
0640   // Log File Name, if requested //
0641   /////////////////////////////////
0642   if (boolValue("logName")) {
0643     cout << outputFile << ".log" << endl;
0644     exit(0);
0645   }
0646   outputFile += ".root";
0647   if (modifyOutputFile) {
0648     stringValue("outputFile") = outputFile;
0649   }
0650 
0651   // finally, if they asked us to print all options, let's do so
0652   // after we've modified all variables appropriately
0653   if (m_printOptions) {
0654     printOptionValues();
0655   }  // if printOptions
0656 }
0657 
0658 // friends
0659 ostream &operator<<(ostream &o_stream, const CommandLineParser &rhs) { return o_stream; }