Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages | Examples

ArFileParser.cpp

00001 /*
00002 MobileRobots Advanced Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004, 2005 ActivMedia Robotics LLC
00004 Copyright (C) 2006, 2007 MobileRobots Inc.
00005 
00006      This program is free software; you can redistribute it and/or modify
00007      it under the terms of the GNU General Public License as published by
00008      the Free Software Foundation; either version 2 of the License, or
00009      (at your option) any later version.
00010 
00011      This program is distributed in the hope that it will be useful,
00012      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014      GNU General Public License for more details.
00015 
00016      You should have received a copy of the GNU General Public License
00017      along with this program; if not, write to the Free Software
00018      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 If you wish to redistribute ARIA under different terms, contact 
00021 MobileRobots for information about a commercial version of ARIA at 
00022 robots@mobilerobots.com or 
00023 MobileRobots Inc, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
00024 */
00025 
00026 #include "ArExport.h"
00027 #include "ariaOSDef.h"
00028 #include "ArFileParser.h"
00029 #include "ArLog.h"
00030 #include "ariaUtil.h"
00031 #include <ctype.h>
00032 
00033 AREXPORT ArFileParser::ArFileParser(const char *baseDirectory)
00034 {
00035   myRemainderHandler = NULL;
00036   setBaseDirectory(baseDirectory);
00037   resetCounters();
00038 }
00039 
00040 AREXPORT ArFileParser::~ArFileParser(void)
00041 {
00042 
00043 }
00044 
00045 AREXPORT bool ArFileParser::addHandler(
00046     const char *keyword, ArRetFunctor1<bool, ArArgumentBuilder *> *functor)
00047 {
00048   std::map<std::string, HandlerCBType *, ArStrCaseCmpOp>::iterator it;
00049   if (keyword == NULL)
00050   {
00051     if (myRemainderHandler != NULL)
00052     {
00053       ArLog::log(ArLog::Verbose, "There is already a functor to handle unhandled lines");
00054       return false;
00055     }
00056     else
00057     {
00058       delete myRemainderHandler;
00059       myRemainderHandler = new HandlerCBType(functor);
00060       return true;
00061     }
00062   }
00063 
00064   if ((it = myMap.find(keyword)) != myMap.end())
00065   {
00066     ArLog::log(ArLog::Verbose, "There is already a functor to handle keyword '%s'", keyword);
00067     return false;
00068   }
00069   ArLog::log(ArLog::Verbose, "keyword '%s' handler added", keyword);
00070   myMap[keyword] = new HandlerCBType(functor);
00071   return true;
00072 }
00073 
00080 AREXPORT bool ArFileParser::addHandlerWithError(
00081     const char *keyword, 
00082     ArRetFunctor3<bool, ArArgumentBuilder *, char *, size_t> *functor)
00083 {
00084   std::map<std::string, HandlerCBType *, ArStrCaseCmpOp>::iterator it;
00085   if (keyword == NULL)
00086   {
00087     if (myRemainderHandler != NULL)
00088     {
00089       ArLog::log(ArLog::Verbose, "There is already a functor to handle unhandled lines");
00090       return false;
00091     }
00092     else
00093     {
00094       delete myRemainderHandler;
00095       myRemainderHandler = new HandlerCBType(functor);
00096       return true;
00097     }
00098   }
00099 
00100   if ((it = myMap.find(keyword)) != myMap.end())
00101   {
00102     ArLog::log(ArLog::Verbose, "There is already a functor to handle keyword '%s'", keyword);
00103     return false;
00104   }
00105   ArLog::log(ArLog::Verbose, "keyword '%s' handler added", keyword);
00106   myMap[keyword] = new HandlerCBType(functor);
00107   return true;
00108 }
00109 
00110 AREXPORT bool ArFileParser::remHandler(const char *keyword, 
00111                        bool logIfCannotFind)
00112 {
00113   std::map<std::string, HandlerCBType *, ArStrCaseCmpOp>::iterator it;
00114   HandlerCBType *handler;
00115 
00116   if (myRemainderHandler != NULL && keyword == NULL)
00117   {
00118     delete myRemainderHandler;
00119     myRemainderHandler = NULL;
00120     ArLog::log(ArLog::Verbose, "Functor for remainder handler removed");
00121     return true;
00122   }
00123 
00124   if ((it = myMap.find(keyword)) == myMap.end())
00125   {
00126     if (logIfCannotFind)
00127       ArLog::log(ArLog::Normal, "There is no keyword '%s' to remove.", 
00128          keyword);
00129     return false;
00130   }
00131   ArLog::log(ArLog::Verbose, "keyword '%s' removed", keyword);
00132   handler = (*it).second;
00133   myMap.erase(it);
00134   delete handler;
00135   remHandler(keyword, false);
00136   return true;
00137 
00138 }
00139 
00140 AREXPORT bool ArFileParser::remHandler(
00141     ArRetFunctor1<bool, ArArgumentBuilder *> *functor)
00142 {
00143   std::map<std::string, HandlerCBType *, ArStrCaseCmpOp>::iterator it;
00144   HandlerCBType *handler;
00145 
00146   if (myRemainderHandler != NULL && myRemainderHandler->haveFunctor(functor))
00147   {
00148     delete myRemainderHandler;
00149     myRemainderHandler = NULL;
00150     ArLog::log(ArLog::Verbose, "Functor for remainder handler removed");
00151     return true;
00152   }
00153 
00154   for (it = myMap.begin(); it != myMap.end(); it++)
00155   {
00156     if ((*it).second->haveFunctor(functor))
00157     {
00158       ArLog::log(ArLog::Verbose, "Functor for keyword '%s' removed.", 
00159          (*it).first.c_str());
00160       handler = (*it).second;
00161       myMap.erase(it);
00162       delete handler;
00163       remHandler(functor);
00164       return true;
00165     }
00166   }
00167   return false;
00168 
00169 }
00170 
00171 AREXPORT bool ArFileParser::remHandler(
00172     ArRetFunctor3<bool, ArArgumentBuilder *, char *, size_t> *functor)
00173 {
00174   std::map<std::string, HandlerCBType *, ArStrCaseCmpOp>::iterator it;
00175   HandlerCBType *handler;
00176 
00177   if (myRemainderHandler != NULL && myRemainderHandler->haveFunctor(functor))
00178   {
00179     delete myRemainderHandler;
00180     myRemainderHandler = NULL;
00181     ArLog::log(ArLog::Verbose, "Functor for remainder handler removed");
00182     return true;
00183   }
00184 
00185   for (it = myMap.begin(); it != myMap.end(); it++)
00186   {
00187     if ((*it).second->haveFunctor(functor))
00188     {
00189       ArLog::log(ArLog::Verbose, "Functor for keyword '%s' removed.", 
00190          (*it).first.c_str());
00191       handler = (*it).second;
00192       myMap.erase(it);
00193       delete handler;
00194       remHandler(functor);
00195       return true;
00196     }
00197   }
00198   return false;
00199 
00200 }
00201 
00202 /*
00203 AREXPORT ArRetFunctor1<bool, ArArgumentBuilder *> *ArFileParser::getHandler(const char *keyword)
00204 {
00205   std::map<std::string, ArRetFunctor1<bool, ArArgumentBuilder *> *, ArStrCaseCmpOp>::iterator it;
00206 
00207   if ((it = myMap.find(keyword)) == myMap.end())
00208   {
00209     ArLog::log(ArLog::Normal, "There is no keyword handler for '%s'", keyword);
00210     return NULL;
00211   }
00212   
00213   return (*it).second;
00214 }
00215 */
00216 AREXPORT void ArFileParser::setBaseDirectory(const char *baseDirectory)
00217 {
00218   if (baseDirectory != NULL && strlen(baseDirectory) > 0)
00219     myBaseDir = baseDirectory;
00220   else
00221     myBaseDir = "";
00222 }
00223 
00224 AREXPORT const char *ArFileParser::getBaseDirectory(void) const
00225 {
00226   return myBaseDir.c_str();
00227 }
00228 
00229 AREXPORT void ArFileParser::resetCounters(void)
00230 {
00231   myLineNumber = 0;
00232 }
00233 
00234 AREXPORT bool ArFileParser::parseLine(char *line, 
00235                       char *errorBuffer, size_t errorBufferLen)
00236 {
00237   char keyword[512];
00238   char *choppingPos;
00239   char *valueStart;
00240   size_t textStart;
00241   size_t len;
00242   size_t i;
00243   bool noArgs;
00244   std::map<std::string, HandlerCBType *, ArStrCaseCmpOp>::iterator it;
00245   HandlerCBType *handler;
00246 
00247   myLineNumber++;
00248   noArgs = false;
00249   
00250   // chop out the comments
00251   if ((choppingPos = strstr(line, ";")) != NULL)
00252     line[choppingPos-line] = '\0';
00253   if ((choppingPos = strstr(line, "#")) != NULL)
00254     line[choppingPos-line] = '\0';
00255   
00256   
00257   
00258   // chop out the new line if its there
00259   if ((choppingPos = strstr(line, "\n")) != NULL)
00260     line[choppingPos-line] = '\0';
00261   // chop out the windows new line if its there
00262   while ((choppingPos = strstr(line, "\r")) != NULL)
00263     memmove(choppingPos, choppingPos + 1, strlen(line));
00264   
00265   // see how long the line is
00266   len = strlen(line);
00267   
00268   // find the keyword
00269   // if this is 0 then we have an empty line so we continue
00270   if (len == 0)
00271   {
00272     ArLog::log(ArLog::Verbose, "line %d: empty line", myLineNumber);
00273     return true;
00274   }
00275   // first find the start of the text
00276   for (i = 0; i < len; i++)
00277   {
00278     // if its not a space we're done
00279     if (!isspace(line[i]))
00280     {
00281       textStart = i;
00282       break;
00283     };
00284   }
00285   // if we reached the end of the line then continue
00286   if (i == len)
00287   {
00288     ArLog::log(ArLog::Verbose, "line %d: just white space at start of line", myLineNumber);
00289     return true;
00290   }
00291   // now we chisel out the keyword 
00292   // adding it so that if the text is quoted it pulls the whole keyword
00293   bool quoted = false;
00294   for (i = textStart; 
00295        i < len && i < sizeof(keyword) + textStart - 3;
00296        i++)
00297   {
00298     // if we're on the start and its a quote just note that and continue
00299     if (!quoted && i == textStart && line[i] == '"')
00300     {
00301       // set quoted to true since we're going to move textStart ahead
00302       // and don't want to loop this
00303       quoted = true;
00304       // note that our text starts on the next char really
00305       textStart++;
00306       continue;
00307     }
00308     // if we're not looking for the end quote and its a space we're done
00309     if (!quoted && isspace(line[i]))
00310     {
00311       break;
00312     }
00313     // if we are looking for the end quote and its a quote we're done
00314     // (so put the null terminator in the keyword and advance the line
00315     // iterator beyond the end quote
00316     else if (quoted && line[i] == '"')
00317     {
00318       keyword[i-textStart] = '\0';
00319       i++;
00320       break;
00321     }
00322     // if not its part of the keyword
00323     else
00324       keyword[i-textStart] = line[i];
00325   }
00326 
00327   keyword[i-textStart] = '\0';
00328   //ArLog::log(ArLog::Verbose, "line %d: keyword %s", lineNumber, keyword);
00329   // now find the start of the value (first non whitespace)
00330   for (; i < len; i++)
00331   {
00332     // if its not a space we're done
00333     if (!isspace(line[i]))
00334     {
00335       valueStart = &line[i];
00336       break;
00337     };
00338   }
00339   // lower that keyword
00340   ArUtil::lower(keyword, keyword, 512);
00341 
00342   // a variable for if we're using the remainder handler or not (don't
00343   // do a test just because someone could set the remainder handler to
00344   // some other handler they're using)
00345   bool usingRemainder = false;
00346   // see if we have a handler for the keyword
00347   if ((it = myMap.find(keyword)) != myMap.end())
00348   {
00349     //printf("have handler for keyword %s\n", keyword);
00350     // we have a handler, so pull that out
00351     handler = (*it).second;
00352     // valueStart was set above but make sure there's an argument
00353     if (i == len)
00354       noArgs = true;
00355   }
00356   // if we don't then check for a remainder handler
00357   else
00358   {
00359     //printf("no handler for keyword %s\n", keyword);
00360     // if we have one set it
00361     if (myRemainderHandler != NULL)
00362     {
00363       usingRemainder = true;
00364       handler = myRemainderHandler;
00365       // reset the value to the start of the text
00366       valueStart = &line[textStart];
00367     }
00368     // if we don't just keep going
00369     else
00370     {
00371       ArLog::log(ArLog::Verbose, 
00372          "line %d: unknown keyword '%s' line '%s', continuing", 
00373          myLineNumber, keyword, &line[textStart]);
00374       return true;
00375     }
00376   }
00377   /*
00378   if (noArgs)
00379     ArLog::log(ArLog::Verbose, "line %d: firstword '%s' no argument", 
00380            myLineNumber, keyword);
00381   else
00382     ArLog::log(ArLog::Verbose, "line %d: firstword '%s' argument '%s'", 
00383            myLineNumber, keyword, valueStart);
00384   */
00385   // now toss the rest of the argument into an argument builder then
00386   // form it up to send to the functor
00387   
00388   ArArgumentBuilder builder;
00389   // if we have arguments add them
00390   if (!noArgs)
00391     builder.add(valueStart);
00392   // if not we still set the name of whatever we parsed (unless we
00393   // didn't have a param of course)
00394   if (!usingRemainder)
00395     builder.setExtraString(keyword);
00396 
00397   // make sure we don't overwrite any errors
00398   if (errorBuffer != NULL && errorBuffer[0] != '\0')
00399   {
00400     errorBuffer = NULL;
00401     errorBufferLen = 0;
00402   }
00403     
00404   // call the functor and see if there are errors;
00405   // if we had an error and aren't continuing on errors then we keep going
00406   if (!handler->call(&builder, errorBuffer, errorBufferLen))
00407   {
00408     // put the line number in the error message (this won't overwrite
00409     // anything because of the check above
00410     if (errorBuffer != NULL)
00411     {
00412       std::string errorString = errorBuffer;
00413       snprintf(errorBuffer, errorBufferLen, "Line %d: %s", myLineNumber, 
00414            errorString.c_str());
00415       
00416     }
00417     return false;
00418   }
00419   return true;
00420 }
00421 
00437 AREXPORT bool ArFileParser::parseFile(const char *fileName, 
00438                       bool continueOnErrors, 
00439                       bool noFileNotFoundMessage,
00440                       char *errorBuffer,
00441                       size_t errorBufferLen)
00442 {
00443   FILE *file;
00444 
00445   char line[10000];
00446   bool ret = true;
00447 
00448   if (errorBuffer)
00449     errorBuffer[0] = '\0';
00450 
00451   std::string realFileName;
00452   if (fileName[0] == '/' || fileName[0] == '\\')
00453   {
00454     realFileName = fileName;
00455   }
00456   else
00457   {
00458     realFileName = myBaseDir;
00459     realFileName += fileName;
00460   }
00461 
00462   ArLog::log(ArLog::Verbose, "Opening file %s from fileName given %s and base directory %s", realFileName.c_str(), fileName, myBaseDir.c_str());
00463     
00464   //char *buf = new char[4096];
00465 
00466   if ((file = fopen(realFileName.c_str(), "r")) == NULL)
00467   {
00468     if (errorBuffer != NULL)
00469       snprintf(errorBuffer, errorBufferLen, "cannot open file %s", fileName);
00470     if (!noFileNotFoundMessage)
00471       ArLog::log(ArLog::Terse, "ArFileParser::parseFile: Could not open file %s to parse file.", realFileName.c_str());
00472     return false;
00473   }
00481   resetCounters();
00482   // read until the end of the file
00483   while (fgets(line, sizeof(line), file) != NULL)
00484   {
00485     if (!parseLine(line, errorBuffer, errorBufferLen))
00486     {
00487       ArLog::log(ArLog::Terse, "## Last error on line %d of file '%s'", 
00488          myLineNumber, realFileName.c_str());
00489       ret = false;
00490       if (!continueOnErrors)
00491     break;
00492     }
00493   }
00494   
00495   fclose(file);
00496   return ret;
00497 }
00498 
00499 
00512 AREXPORT bool ArFileParser::parseFile(FILE *file, char *buffer, 
00513                       int bufferLength, 
00514                       bool continueOnErrors,
00515                       char *errorBuffer,
00516                       size_t errorBufferLen)
00517 {
00518   if (errorBuffer)
00519     errorBuffer[0] = '\0';
00520 
00521   if ((file == NULL) || (buffer == NULL) || (bufferLength <= 0)) 
00522   {
00523     if (errorBuffer != NULL)
00524       snprintf(errorBuffer, errorBufferLen, "parseFile: bad setup");
00525     return false;
00526   }
00527 
00528   bool ret = true;
00529   resetCounters();
00530 
00531   // read until the end of the file
00532   while (fgets(buffer, bufferLength, file) != NULL)
00533   {
00534     if (!parseLine(buffer, errorBuffer, errorBufferLen))
00535     {
00536       ret = false;
00537       if (!continueOnErrors)
00538         break;
00539     }
00540   }
00541   return ret;
00542 }

Generated on Tue Feb 20 10:51:39 2007 for Aria by  doxygen 1.4.0