00001 00002 // $Id: JobCommand.cxx,v 1.24 2009/07/24 07:52:19 nwest Exp $ 00003 // 00004 // Convert text strings to command paths and options and provide easy 00005 // access to the command path and options to clients 00006 // 00007 // messier@huhepl.harvard.edu 00009 #include <cstdlib> // atoi(), atof(), strtod() 00010 00011 #include "JobControl/JobCommand.h" 00012 #include "MessageService/MsgService.h" 00013 #include "Registry/Registry.h" 00014 00015 CVSID("$Id: JobCommand.cxx,v 1.24 2009/07/24 07:52:19 nwest Exp $"); 00016 00017 //...................................................................... 00018 00019 ostream& operator<<(ostream& os, const JobCommand& jc) 00020 { 00021 vector<string>::const_iterator itrend(jc.fCmdList.end()); 00022 vector<string>::const_iterator itr; 00023 bool ifirst = true; 00024 for (itr = jc.fCmdList.begin(); itr!= itrend; ++itr) { 00025 if (ifirst) { os << (*itr); ifirst = false; } 00026 else os << "/" << (*itr); 00027 } 00028 itrend = jc.fOptList.end(); 00029 for (itr = jc.fOptList.begin(); itr!= itrend; ++itr) { 00030 os << " " << (*itr); 00031 } 00032 return os; 00033 } 00034 00035 //...................................................................... 00036 00037 JobCommand::JobCommand() 00038 { 00039 //====================================================================== 00040 // Purpose: Create an empty command 00041 //====================================================================== 00042 // Flag these as empty 00043 fItrCmdList = fCmdList.end(); 00044 fItrOptList = fOptList.end(); 00045 } 00046 00047 //...................................................................... 00048 00049 JobCommand::JobCommand(const char *cmd) 00050 { 00051 this->Parse(cmd); 00052 } 00053 00054 //...................................................................... 00055 00056 void JobCommand::Parse(const char *cmd) 00057 { 00058 //====================================================================== 00059 // Purpose: Parse a command line 00060 // 00061 // For example the command: 00062 // 00063 // /Path/Module/Set a 10 00064 // 00065 // Has fCmdList = {"Path","Module","Set"} and 00066 // fOptList = {"a", "10"} 00067 // 00068 // Any command starting as "#" is taken to be a comment. Also, 00069 // standard C and C++ commenting styles are supported. However, 00070 // multi-line comments are *not* supported. 00071 //====================================================================== 00072 int len; // Number of characters in the command 00073 bool procCmd; // Are we working on the command? 00074 bool procOpt; // Are we handling options? 00075 bool inQuotes; // Are we inside a quoted string? 00076 bool expectSpecial; // Expect a special character? 00077 string word; // The current word in the command line 00078 // MsgStream& msgDebug = MSGSTREAM("JobC",Msg::kDebug); // Debug stream 00079 00080 word = ""; 00081 len = strlen(cmd); 00082 procCmd = true; 00083 procOpt = false; 00084 inQuotes = false; 00085 expectSpecial = false; 00086 // msgDebug << "Parse: "; 00087 for (int i=0; i<len; ++i) { 00088 // If we've been told not to treat this character as a token, just 00089 // and it to the word 00090 if (expectSpecial) { 00091 word += cmd[i]; 00092 expectSpecial = false; 00093 continue; // next character 00094 } 00095 00096 // See if we have a \ which flags that the next character is not a token 00097 if (cmd[i] == '\\') { 00098 expectSpecial = true; 00099 continue; // next character; 00100 } 00101 00102 // Check if we are entering/leaving a quoted passage 00103 if (cmd[i] == '"' || cmd[i] == '\'') { 00104 inQuotes = !inQuotes; 00105 continue; // next character 00106 } 00107 if (inQuotes) { 00108 // Use every character inside quotes 00109 word += cmd[i]; 00110 continue; // next character 00111 } 00112 00113 // Skip extra white space 00114 if (i>0 && cmd[i]==' ' && cmd[i-1]==' ') { 00115 continue; // next character 00116 } 00117 00118 // Check for command separator 00119 if (procCmd && cmd[i] == '/') { 00120 // Place the previous word (if there is one) in the command list 00121 if (word.length()>0) fCmdList.push_back(word); 00122 // msgDebug << '/' << word; 00123 word = ""; 00124 continue; // next character 00125 } 00126 00127 // White space is used to separate commands from options and 00128 // options from each other 00129 if (cmd[i] == ' ' || cmd[i] == '=') { 00130 if (procCmd) { 00131 // Place the previous work in the command list 00132 // msgDebug << '/' << word; 00133 if (word.length()>0) fCmdList.push_back(word); 00134 word = ""; 00135 // Switch from processing commands to options 00136 procCmd = false; 00137 procOpt = true; 00138 } 00139 else if (procOpt) { 00140 // msgDebug << '|' << word; 00141 if (word.length()>0) fOptList.push_back(word); 00142 word = ""; 00143 } 00144 continue; // next character 00145 } 00146 00147 // Character triggered no special action -- add it to word 00148 word += cmd[i]; 00149 } // End loop over characters 00150 00151 00152 if (procCmd) { 00153 // msgDebug << '/' << word; 00154 if (word.length()) fCmdList.push_back(word); 00155 } 00156 if (procOpt) { 00157 // msgDebug << '|' << word; 00158 if (word.length()) fOptList.push_back(word); 00159 } 00160 // msgDebug << "\n"; 00161 00162 // Point the iterators at the begining of the lists 00163 fItrCmdList = fCmdList.begin(); 00164 fItrOptList = fOptList.begin(); 00165 } 00166 00167 //...................................................................... 00168 00169 JobCommand::~JobCommand() {} 00170 00171 //...................................................................... 00172 00173 bool JobCommand::HaveCmd() const 00174 { 00175 //====================================================================== 00176 // Purpose: Are there parts of the command that need processing? 00177 // 00178 // Returns: true - there are more parts of the command to parse 00179 // false - there are no more parts of the command to parse 00180 //====================================================================== 00181 if (fItrCmdList == fCmdList.end()) return false; 00182 return true; 00183 } 00184 00185 //...................................................................... 00186 00187 bool JobCommand::HaveOpt() const 00188 { 00189 //====================================================================== 00190 // Purpose: Are there options that need processing? 00191 // 00192 // Returns: true - options remain, 00193 // false - no more options left 00194 //====================================================================== 00195 if (fItrOptList == fOptList.end()) return false; 00196 return true; 00197 } 00198 00199 //...................................................................... 00200 00201 const char *JobCommand::PopCmd() 00202 { 00203 //====================================================================== 00204 // Purpose: Return the current option parameter as a character string 00205 // 00206 // Returns: A pointer to the start of the option character string 00207 // If the list of options has been exhausted this returns "" 00208 //====================================================================== 00209 if (this->HaveCmd()) { 00210 const char *val = 0; 00211 val = (*fItrCmdList).c_str(); 00212 ++fItrCmdList; 00213 return val; 00214 } 00215 return 0; 00216 } 00217 00218 //...................................................................... 00219 00220 const char *JobCommand::PushCmd() 00221 { 00222 //====================================================================== 00223 // Purpose: Return the current option parameter as a character string 00224 // 00225 // Returns: A pointer to the start of the option character string 00226 // If the list of options has been exhausted this returns "" 00227 //====================================================================== 00228 if (fItrCmdList != fCmdList.begin()) { 00229 const char *val = 0; 00230 --fItrCmdList; 00231 val = (*fItrCmdList).c_str(); 00232 return val; 00233 } 00234 return 0; 00235 } 00236 00237 const char *JobCommand::PushOpt() 00238 { 00239 //====================================================================== 00240 // Purpose: Return the current option parameter as a character string 00241 // 00242 // Returns: A pointer to the start of the option character string 00243 // If the list of options has been exhausted this returns "" 00244 //====================================================================== 00245 if (fItrOptList != fOptList.begin()) { 00246 const char *val = 0; 00247 --fItrOptList; 00248 val = (*fItrOptList).c_str(); 00249 return val; 00250 } 00251 return 0; 00252 } 00253 00254 //...................................................................... 00255 00256 const char *JobCommand::PopOpt() 00257 { 00258 //====================================================================== 00259 // Purpose: Return the current option parameter as a character string 00260 // 00261 // Returns: A pointer to the start of the option character string 00262 // If the list of options has been exhausted this returns "" 00263 //====================================================================== 00264 if (this->HaveOpt()) { 00265 const char *val = 0; 00266 val = (*fItrOptList).c_str(); 00267 ++fItrOptList; 00268 return val; 00269 } 00270 return 0; 00271 } 00272 00273 //...................................................................... 00274 00275 int JobCommand::PopIntOpt() 00276 { 00277 //====================================================================== 00278 // Purpose: Return the current option parameter as an integer number 00279 // and advance the counter. 00280 // 00281 // Returns: The current option interpreted as an integer number 00282 // If the list of options has been exhausted this returns 0 00283 //====================================================================== 00284 if (this->HaveOpt()) { 00285 int val; 00286 val = atoi((*fItrOptList).c_str()); 00287 ++fItrOptList; 00288 return val; 00289 } 00290 return 0; 00291 } 00292 00293 //...................................................................... 00294 00295 double JobCommand::PopFloatOpt() 00296 { 00297 //====================================================================== 00298 // Purpose: Return the current option parameter as a floating point 00299 // number and advance the counter. 00300 // 00301 // Returns: The current option interpreted as a floating point number 00302 // If the list of options has been exhausted this returns 0.0 00303 //====================================================================== 00304 if (this->HaveOpt()) { 00305 double val = 0.0; 00306 val = atof((*fItrOptList).c_str()); 00307 ++fItrOptList; 00308 return val; 00309 } 00310 return 0.0; 00311 } 00312 00313 //...................................................................... 00314 00315 void JobCommand::SplitLine(const char *line, char sep, string& a, string& b) 00316 { 00317 //====================================================================== 00318 // Purpose: Split a character string into two pieces given a 00319 // separator. Example peas::carots into peas and carrots 00320 // 00321 // Inputs: line - the text to split 00322 // sep - the separation character (':' in the example above) 00323 // a - text before the separator 00324 // b - text after separator 00325 //====================================================================== 00326 // Clear a and b strings 00327 a = ""; 00328 b = ""; 00329 bool doFront = true; 00330 for (const char* c = line; *c != '\0'; ++c) { 00331 if (*c == ' ') continue; 00332 if (*c == sep) { 00333 doFront = false; 00334 continue; 00335 } 00336 if (doFront) a += (*c); 00337 else b += (*c); 00338 } 00339 00340 MSG("JobC",Msg::kVerbose) << 00341 "Split " << line << " into " << a << " " << b << ".\n"; 00342 } 00343 00344 00345 //...................................................................... 00346 00347 void JobCommand::StringTok(std::vector<std::string>& ls, 00348 const std::string& str, 00349 const std::string& tok) 00350 { 00351 //====================================================================== 00352 // Split a long string into a set of shorter strings spliting along 00353 // divisions makers by the characters listed in the token string 00354 //====================================================================== 00355 const string::size_type S = str.size(); 00356 const string::size_type toksz = tok.size(); 00357 string::size_type i = 0; 00358 00359 while (i < S) { 00360 // eat leading whitespace 00361 while ( (i<S) && (tok.find(str[i])<=toksz) ) { 00362 ++i; 00363 } 00364 if (i == S) return; // nothing left but WS 00365 00366 // find end of word 00367 string::size_type j = i+1; 00368 while ( (j<S) && !(tok.find(str[j])<=toksz) ) { 00369 ++j; 00370 } 00371 00372 // add word 00373 ls.push_back(str.substr(i,j-i)); 00374 00375 // set up for next loop 00376 i = j+1; 00377 } 00378 } 00379 00380 //...................................................................... 00381 00382 bool JobCommand::IsInt(const char* s) 00383 { 00384 //====================================================================== 00385 // Does the string s represent an integer? 00386 //====================================================================== 00387 char* endptr; 00388 strtol(s, &endptr,0); 00389 // Consider this a failure if the entire string does not convert to a long 00390 if (endptr != (s + strlen(s))) return false; 00391 00392 // All checks for "intness" passed 00393 return true; 00394 } 00395 00396 00397 //...................................................................... 00398 00399 bool JobCommand::IsFloat(const char* s) 00400 { 00401 //====================================================================== 00402 // Does the string s represent a float? 00403 //====================================================================== 00404 char* endptr; 00405 strtod(s, &endptr); 00406 // Consider this a failure if the entire string does not convert to a double 00407 if (endptr != (s + strlen(s))) return false; 00408 00409 // All checks for "floatness" passed 00410 return true; 00411 } 00412 00413 //...................................................................... 00414 00415 void JobCommand::StringToRegistry(Registry& r, const char* s) 00416 { 00417 //====================================================================== 00418 // Convert a string like "a=1 b=2.0 name=Mark" to a registry 00419 //====================================================================== 00420 // JobCommand's expect format Command option1 option2... so cast 00421 // the string into this form by prepending a bogus command string 00422 string cmd("Set "); // Set is as good a command name as any... 00423 cmd += s; // Append the registry string 00424 JobCommand c(cmd.c_str()); 00425 00426 // Use the command to build a registry 00427 r.UnLockValues(); 00428 while (c.HaveOpt()) { 00429 const char* key; 00430 const char* value; 00431 00432 // Extract key/value pair 00433 key = c.PopOpt(); 00434 if (c.HaveOpt() == false) { 00435 MSG("JobC",Msg::kWarning) << "Missing value for key \"" 00436 << key << "\"!" << endl; 00437 return; 00438 } 00439 value = c.PopOpt(); 00440 00441 // Guess type of value and build registry 00442 if (JobCommand::IsInt(value)) { 00443 // Intergers are floats so check them next 00444 int i = atoi(value); 00445 r.Set(key, i); 00446 } 00447 else if (JobCommand::IsFloat(value)) { 00448 // Check float as it is the smallest set 00449 double d = atof(value); 00450 r.Set(key, d); 00451 } 00452 else if (JobCommand::IsBool(value)) { 00453 bool b = JobCommand::atob(value); 00454 r.Set(key, b); // Which Set is this?? char? int? Dunno... 00455 } 00456 else { 00457 // Everything is a string so check that last 00458 r.Set(key, value); 00459 } 00460 } 00461 00462 r.LockValues(); 00463 } 00464 00465 //...................................................................... 00466 00467 bool JobCommand::IsBool(const char* value) 00468 { 00469 //====================================================================== 00470 // Can the string value be interpreted as a bool value? 00471 //====================================================================== 00472 std::string v(value); 00473 if (v == "true") return true; // C++ style 00474 if (v == "false") return true; 00475 if (v == "kTRUE") return true; // ROOT style 00476 if (v == "kFALSE") return true; 00477 if (v == "TRUE") return true; // Some other reasonable variations... 00478 if (v == "FALSE") return true; 00479 if (v == "True") return true; 00480 if (v == "False") return true; 00481 if (v == "on") return true; 00482 if (v == "off") return true; 00483 if (v == "On") return true; 00484 if (v == "Off") return true; 00485 if (v == "ON") return true; 00486 if (v == "OFF") return true; 00487 return false; 00488 } 00489 00490 //...................................................................... 00491 00492 bool JobCommand::atob(const char* value) 00493 { 00494 //====================================================================== 00495 // Convert the text string to its bool equivalent No error checking is 00496 // done. Returns "false" if the contents of value are not regognized 00497 //====================================================================== 00498 std::string v(value); 00499 if (v == "true") return true; // C++ style 00500 if (v == "false") return false; 00501 if (v == "kTRUE") return true; // ROOT style 00502 if (v == "kFALSE") return false; 00503 if (v == "TRUE") return true; // Some other reasonable variations... 00504 if (v == "FALSE") return false; 00505 if (v == "True") return true; 00506 if (v == "False") return false; 00507 if (v == "on") return true; 00508 if (v == "off") return false; 00509 if (v == "On") return true; 00510 if (v == "Off") return false; 00511 if (v == "ON") return true; 00512 if (v == "OFF") return false; 00513 00514 // Oops, what have we here? 00515 MSG("JobC",Msg::kWarning) << 00516 "Attmept to convert string '" << value << "' to bool. Result is 'false'\n"; 00517 return false; 00518 } 00519 00521
1.3.9.1