worldfile.cc
Go to the documentation of this file.
00001 /*
00002  *  Stage : a multi-robot simulator.
00003  *  Copyright (C) 2001-2011 Richard Vaughan, Andrew Howard and Brian Gerkey.
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  *
00019  */
00020 /*
00021  * Desc: A class for reading in the world file.
00022  * Authors: Andrew Howard <ahoward@usc.edu>
00023  *          Richard Vaughan (rtv) <vaughan@sfu.ca>
00024  *          Douglas S. Blank <dblank@brynmawr.edu>
00025  *
00026  * Date: 15 Nov 2001
00027  * CVS info: $Id$
00028  */
00029 
00030 #include <assert.h>
00031 #include <ctype.h>
00032 #include <errno.h>
00033 #include <limits.h> // for PATH_MAX
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <unistd.h>
00038 #include <math.h>
00039 #include <stdarg.h>
00040 
00041 //#define DEBUG
00042 
00043 #include "replace.h" // for dirname(3)
00044 #include "stage.hh"
00045 #include "worldfile.hh"
00046 using namespace Stg;
00047 
00048 // the isblank() macro is not standard - it's a GNU extension
00049 // and it doesn't work for me, so here's an implementation - rtv
00050 #ifndef isblank
00051 #define isblank(a) (a == ' ' || a == '\t')
00052 #endif
00053 
00055 // Useful macros for dumping parser errors
00056 #define TOKEN_ERR(z, l)                         \
00057   PRINT_ERR2("%s:%d : " z, this->filename.c_str(), l)
00058 #define PARSE_ERR(z, l)                         \
00059   PRINT_ERR2("%s:%d : " z, this->filename.c_str(), l)
00060 
00061 
00063 // Default constructor
00064 Worldfile::Worldfile() :
00065   tokens(),
00066   macros(),
00067   entities(),
00068         properties(),
00069   filename(),
00070   unit_length( 1.0 ),
00071   unit_angle( M_PI / 180.0 )
00072 {
00073 }
00074 
00075 
00077 // Destructor
00078 Worldfile::~Worldfile()
00079 {
00080   ClearProperties();
00081   ClearMacros();
00082   ClearEntities();
00083   ClearTokens();
00084 }
00085 
00086 FILE *Worldfile::FileOpen(const std::string& filename, const char* method)
00087 {
00088   FILE *fp = fopen(filename.c_str(), method);
00089   // if this opens, then we will go with it:
00090   if (fp) {
00091     PRINT_DEBUG1( "Loading: %s", filename.c_str());
00092     return fp;
00093   }
00094   // else, search other places, and set this->filename
00095   // accordingly if found:
00096   char *stagepath = getenv("STAGEPATH");
00097   char *token = strtok(stagepath, ":");
00098   char* fullpath = new char[PATH_MAX];
00099   char *tmp = strdup(filename.c_str());
00100   const char *base = basename(tmp);
00101   while (token != NULL) {
00102     // for each part of the path, try it:
00103     memset( fullpath, 0, PATH_MAX);
00104     strcat( fullpath, token);
00105     strcat( fullpath, "/" );
00106     strcat( fullpath, base);
00107     assert(strlen(fullpath) + 1 < PATH_MAX);
00108     fp = fopen(fullpath, method);
00109     if (fp) {
00110       this->filename = std::string(fullpath);
00111       PRINT_DEBUG1( "Loading: %s", filename.c_str());
00112       free(tmp);
00113       return fp;
00114     }
00115     token = strtok(NULL, ":");
00116   }
00117   if( tmp ) free(tmp);
00118   if( fullpath ) delete[] fullpath;
00119   return NULL;
00120 }
00121 
00122 
00124 // Load world from file
00125 bool Worldfile::Load(const std::string& filename )
00126 {
00127   this->filename = filename;
00128 
00129   // Open the file
00130   //FILE *file = fopen(this->filename, "r");
00131   FILE *file = FileOpen(this->filename.c_str(), "r");
00132   if (!file)
00133     {
00134       PRINT_ERR2("unable to open world file %s : %s",
00135                                           this->filename.c_str(), strerror(errno));
00136       return false;
00137     }
00138 
00139   ClearTokens();
00140 
00141   // Read tokens from the file
00142   if (!LoadTokens(file, 0))
00143     {
00144       //DumpTokens();
00145       fclose(file);
00146       return false;
00147     }
00148 
00149   fclose(file);
00150 
00151   // Parse the tokens to identify entities
00152   if (!ParseTokens())
00153     {
00154       //DumpTokens();
00155       return false;
00156     }
00157 
00158   // Dump contents and exit if this file is meant for debugging only.
00159   if (ReadInt(0, "test", 0) != 0)
00160     {
00161       PRINT_ERR("this is a test file; quitting");
00162       DumpTokens();
00163       DumpMacros();
00164       DumpEntities();
00165       DumpProperties();
00166       return false;
00167     }
00168   
00169   // Work out what the length units are
00170   const std::string& unitl = ReadString(0, "unit_length", "m");
00171   if(  unitl == "m")
00172     this->unit_length = 1.0;
00173   else if( unitl == "cm")
00174     this->unit_length = 0.01;
00175   else if( unitl == "mm") 
00176     this->unit_length = 0.001;
00177   
00178   // Work out what the angle units are
00179   const std::string& unita = ReadString(0, "unit_angle", "degrees");
00180   if(  unita == "degrees")
00181     this->unit_angle = M_PI / 180;
00182   else if( unita == "radians")
00183     this->unit_angle = 1;
00184   
00185   //printf( "f: %s\n", this->filename.c_str() );
00186 
00187   return true;
00188 }
00189 
00190 
00192 // Save world to file
00193 bool Worldfile::Save(const std::string& filename )
00194 {
00195   // Debugging
00196   //DumpProperties();
00197 
00198   // Open file
00199   FILE *file = fopen(filename.c_str(), "w+");
00200   //FILE *file = FileOpen(filename, "w+");
00201   if (!file)
00202     {
00203       PRINT_ERR2("unable to open world file %s : %s",
00204                                                                  filename.c_str(), strerror(errno));
00205       return false;
00206     }
00207         
00208   // TODO - make a backup before saving the file
00209         
00210   // Write the current set of tokens to the file
00211   if (!SaveTokens(file))
00212     {
00213       fclose(file);
00214       return false;
00215     }
00216         
00217   fclose(file);
00218   return true;
00219 }
00220 
00221 
00223 // Check for unused properties and print warnings
00224 bool Worldfile::WarnUnused()
00225 {
00226   bool unused = false;
00227   
00228   FOR_EACH( it, properties )
00229     {
00230       if( ! it->second->used )
00231         {
00232           PRINT_WARN3("worldfile %s:%d : property [%s] is defined but not used",
00233                       this->filename.c_str(), it->second->line, it->second->name.c_str());
00234           unused = true;
00235         }
00236     }
00237   
00238   return unused;
00239 }
00240 
00241 
00243 // Load tokens from a file.
00244 bool Worldfile::LoadTokens(FILE *file, int include)
00245 {
00246   int ch;
00247   int line;
00248   char token[256];
00249 
00250   line = 1;
00251 
00252   while (true)
00253     {
00254       ch = fgetc(file);
00255       if (ch == EOF)
00256         break;
00257 
00258       if ((char) ch == '#')
00259         {
00260           ungetc(ch, file);
00261           if (!LoadTokenComment(file, &line, include))
00262             return false;
00263         }
00264       else if (isalpha(ch))
00265         {
00266           ungetc(ch, file);
00267           if (!LoadTokenWord(file, &line, include))
00268             return false;
00269         }
00270       else if (strchr("+-.0123456789", ch))
00271         {
00272           ungetc(ch, file);
00273           if (!LoadTokenNum(file, &line, include))
00274             return false;
00275         }
00276       else if (isblank(ch))
00277         {
00278           ungetc(ch, file);
00279           if (!LoadTokenSpace(file, &line, include))
00280             return false;
00281         }
00282       else if (ch == '"')
00283         {
00284           ungetc(ch, file);
00285           if (!LoadTokenString(file, &line, include))
00286             return false;
00287         }
00288       else if (strchr("(", ch))
00289         {
00290           token[0] = ch;
00291           token[1] = 0;
00292           AddToken(TokenOpenEntity, token, include);
00293         }
00294       else if (strchr(")", ch))
00295         {
00296           token[0] = ch;
00297           token[1] = 0;
00298           AddToken(TokenCloseEntity, token, include);
00299         }
00300       else if (strchr("[", ch))
00301         {
00302           token[0] = ch;
00303           token[1] = 0;
00304           AddToken(TokenOpenTuple, token, include);
00305         }
00306       else if (strchr("]", ch))
00307         {
00308           token[0] = ch;
00309           token[1] = 0;
00310           AddToken(TokenCloseTuple, token, include);
00311         }
00312       else if ( 0x0d == ch )
00313         {
00314           ch = fgetc(file);
00315           if ( 0x0a != ch )
00316             ungetc(ch, file);
00317           line++;
00318           AddToken(TokenEOL, "\n", include);
00319         }
00320       else if ( 0x0a == ch )
00321         {
00322           ch = fgetc(file);
00323           if ( 0x0d != ch )
00324             ungetc(ch, file);
00325           line++;
00326           AddToken(TokenEOL, "\n", include);
00327         }
00328       else
00329         {
00330           TOKEN_ERR("syntax error", line);
00331           return false;
00332         }
00333     }
00334 
00335   return true;
00336 }
00337 
00338 
00340 // Read in a comment token
00341 bool Worldfile::LoadTokenComment(FILE *file, int *line, int include)
00342 {
00343   char token[256];
00344   int len;
00345   int ch;
00346 
00347   len = 0;
00348   memset(token, 0, sizeof(token));
00349 
00350   while (true)
00351     {
00352       ch = fgetc(file);
00353 
00354       if (ch == EOF)
00355         {
00356           AddToken(TokenComment, token, include);
00357           return true;
00358         }
00359       else if ( 0x0a == ch || 0x0d == ch )
00360         {
00361           ungetc(ch, file);
00362           AddToken(TokenComment, token, include);
00363           return true;
00364         }
00365       else
00366         token[len++] = ch;
00367     }
00368   return true;
00369 }
00370 
00371 
00373 // Read in a word token
00374 bool Worldfile::LoadTokenWord(FILE *file, int *line, int include)
00375 {
00376   char token[256];
00377   int len;
00378   int ch;
00379 
00380   len = 0;
00381   memset(token, 0, sizeof(token));
00382 
00383   while (true)
00384     {
00385       ch = fgetc(file);
00386 
00387       if (ch == EOF)
00388         {
00389           AddToken(TokenWord, token, include);
00390           return true;
00391         }
00392       else if (isalpha(ch) || isdigit(ch) || strchr(".-_[]", ch))
00393         {
00394           token[len++] = ch;
00395         }
00396       else
00397         {
00398           if (strcmp(token, "include") == 0)
00399             {
00400               ungetc(ch, file);
00401               AddToken(TokenWord, token, include);
00402               if (!LoadTokenInclude(file, line, include))
00403                 return false;
00404             }
00405           else
00406             {
00407               ungetc(ch, file);
00408               AddToken(TokenWord, token, include);
00409             }
00410           return true;
00411         }
00412     }
00413   assert(false);
00414   return false;
00415 }
00416 
00417 
00419 // Load an include token; this will load the include file.
00420 bool Worldfile::LoadTokenInclude(FILE *file, int *line, int include)
00421 {
00422   int ch;
00423   const char *filename;
00424   char *fullpath;
00425 
00426   ch = fgetc(file);
00427 
00428   if (ch == EOF)
00429     {
00430       TOKEN_ERR("incomplete include statement", *line);
00431       return false;
00432     }
00433   else if (!isblank(ch))
00434     {
00435       TOKEN_ERR("syntax error in include statement", *line);
00436       return false;
00437     }
00438 
00439   ungetc(ch, file);
00440   if (!LoadTokenSpace(file, line, include))
00441     return false;
00442 
00443   ch = fgetc(file);
00444 
00445   if (ch == EOF)
00446     {
00447       TOKEN_ERR("incomplete include statement", *line);
00448       return false;
00449     }
00450   else if (ch != '"')
00451     {
00452       TOKEN_ERR("syntax error in include statement", *line);
00453       return false;
00454     }
00455 
00456   ungetc(ch, file);
00457   if (!LoadTokenString(file, line, include))
00458     return false;
00459 
00460   // This is the basic filename
00461   filename = GetTokenValue(this->tokens.size() - 1);
00462 
00463   // Now do some manipulation.  If its a relative path,
00464   // we append the path of the world file.
00465   if (filename[0] == '/' || filename[0] == '~')
00466     {
00467       fullpath = strdup(filename);
00468     }
00469   else if (this->filename[0] == '/' || this->filename[0] == '~')
00470     {
00471       // Note that dirname() modifies the contents, so
00472       // we need to make a copy of the filename.
00473       // There's no bounds-checking, but what the heck.
00474       char *tmp = strdup(this->filename.c_str());
00475       fullpath = new char[PATH_MAX];
00476       memset(fullpath, 0, PATH_MAX);
00477       strcat( fullpath, dirname(tmp));
00478       strcat( fullpath, "/" );
00479       strcat( fullpath, filename );
00480       assert(strlen(fullpath) + 1 < PATH_MAX);
00481       free(tmp);
00482     }
00483   else
00484     {
00485       // Note that dirname() modifies the contents, so
00486       // we need to make a copy of the filename.
00487       // There's no bounds-checking, but what the heck.
00488       char *tmp = strdup(this->filename.c_str());
00489       fullpath = new char[PATH_MAX];
00490       char* dummy = getcwd(fullpath, PATH_MAX);
00491       if (!dummy)
00492                   {
00493                          PRINT_ERR2("unable to get cwd %d: %s", errno, strerror(errno));
00494                          if(tmp) free(tmp);
00495                          delete[] fullpath;
00496                          return false;
00497       }
00498       strcat( fullpath, "/" );
00499       strcat( fullpath, dirname(tmp));
00500       strcat( fullpath, "/" );
00501       strcat( fullpath, filename );
00502       assert(strlen(fullpath) + 1 < PATH_MAX);
00503       free(tmp);
00504     }
00505 
00506   printf( "[Include %s]", filename );
00507   fflush( stdout );
00508 
00509   // Open the include file
00510   FILE *infile = FileOpen(fullpath, "r");
00511   if (!infile)
00512     {
00513       PRINT_ERR2("unable to open include file %s : %s",
00514                  fullpath, strerror(errno));
00515       //free(fullpath);
00516                 delete[] fullpath;
00517       return false;
00518     }
00519 
00520   // Terminate the include line
00521   AddToken(TokenEOL, "\n", include);
00522 
00523   //DumpTokens();
00524 
00525   // Read tokens from the file
00526   if (!LoadTokens(infile, include + 1))
00527     {
00528                 fclose( infile );
00529       //DumpTokens();
00530       //free(fullpath);
00531                 delete[] fullpath;
00532       return false;
00533     }
00534 
00535   // done with the include file
00536   fclose( infile );
00537 
00538   // consume the rest of the include line XX a bit of a hack - assumes
00539   // that an include is the last thing on a line
00540   while ( ch != '\n' )
00541     ch = fgetc(file);
00542 
00543   delete[] fullpath;
00544   return true;
00545 }
00546 
00547 
00549 // Read in a number token
00550 bool Worldfile::LoadTokenNum(FILE *file, int *line, int include)
00551 {
00552   char token[256];
00553   int len;
00554   int ch;
00555 
00556   len = 0;
00557   memset(token, 0, sizeof(token));
00558 
00559   while (true)
00560     {
00561       ch = fgetc(file);
00562 
00563       if (ch == EOF)
00564         {
00565           AddToken(TokenNum, token, include);
00566           return true;
00567         }
00568       else if (strchr("+-.0123456789", ch))
00569         {
00570           token[len++] = ch;
00571         }
00572       else
00573         {
00574           AddToken(TokenNum, token, include);
00575           ungetc(ch, file);
00576           return true;
00577         }
00578     }
00579   assert(false);
00580   return false;
00581 }
00582 
00583 
00585 // Read in a string token
00586 bool Worldfile::LoadTokenString(FILE *file, int *line, int include)
00587 {
00588   int ch;
00589   int len;
00590   char token[256];
00591 
00592   len = 0;
00593   memset(token, 0, sizeof(token));
00594 
00595   ch = fgetc(file);
00596 
00597   while (true)
00598     {
00599       ch = fgetc(file);
00600 
00601       if ( EOF == ch || 0x0a == ch || 0x0d == ch )
00602         {
00603           TOKEN_ERR("unterminated string constant", *line);
00604           return false;
00605         }
00606       else if (ch == '"')
00607         {
00608           AddToken(TokenString, token, include);
00609           return true;
00610         }
00611       else
00612         {
00613           token[len++] = ch;
00614         }
00615     }
00616   assert(false);
00617   return false;
00618 }
00619 
00620 
00622 // Read in a whitespace token
00623 bool Worldfile::LoadTokenSpace(FILE *file, int *line, int include)
00624 {
00625   int ch;
00626   int len;
00627   char token[256];
00628 
00629   len = 0;
00630   memset(token, 0, sizeof(token));
00631 
00632   while (true)
00633     {
00634       ch = fgetc(file);
00635 
00636       if (ch == EOF)
00637         {
00638           AddToken(TokenSpace, token, include);
00639           return true;
00640         }
00641       else if (isblank(ch))
00642         {
00643           token[len++] = ch;
00644         }
00645       else
00646         {
00647           AddToken(TokenSpace, token, include);
00648           ungetc(ch, file);
00649           return true;
00650         }
00651     }
00652   assert(false);
00653   return false;
00654 }
00655 
00656 
00658 // Save tokens to a file.
00659 bool Worldfile::SaveTokens(FILE *file)
00660 {
00661   unsigned int i;
00662   CToken *token;
00663 
00664   for (i = 0; i < this->tokens.size(); i++)
00665     {
00666       token = &this->tokens[i];
00667 
00668       if (token->include > 0)
00669         continue;
00670       if (token->type == TokenString)
00671                                 fprintf(file, "\"%s\"", token->value.c_str());
00672       else
00673                                 fprintf(file, "%s", token->value.c_str());
00674     }
00675   return true;
00676 }
00677 
00678 
00680 // Clear the token list
00681 void Worldfile::ClearTokens()
00682 {
00683         tokens.clear();
00684 }
00685 
00686 
00688 // Add a token to the token list
00689 bool Worldfile::AddToken(int type, const char *value, int include)
00690 {
00691         tokens.push_back( CToken( include, type, value ));
00692   return true;
00693 }
00694 
00695 
00697 // Set a token value in the token list
00698 bool Worldfile::SetTokenValue(int index, const char *value)
00699 {
00700   assert(index >= 0 && index < (int)this->tokens.size() );
00701         tokens[index].value = value;
00702   return true;
00703 }
00704 
00705 
00707 // Get the value of a token
00708 const char *Worldfile::GetTokenValue(int index)
00709 {
00710   assert(index >= 0 && index < (int)this->tokens.size());
00711   return this->tokens[index].value.c_str();
00712 }
00713 
00714 
00716 // Dump the token list (for debugging).
00717 void Worldfile::DumpTokens()
00718 {
00719   int line;
00720 
00721   line = 1;
00722   printf("\n## begin tokens\n");
00723   printf("## %4d : ", line);
00724 
00725         FOR_EACH( it, tokens )
00726   //for (int i = 0; i < this->token_count; i++)
00727     {
00728       if ( it->value[0] == '\n')
00729                                 printf("[\\n]\n## %4d : %02d ", ++line, it->include);
00730       else
00731                                 printf("[%s] ", it->value.c_str());
00732     }
00733   printf("\n");
00734   printf("## end tokens\n");
00735 }
00736 
00737 
00739 // Parse tokens into entities and properties.
00740 bool Worldfile::ParseTokens()
00741 {
00742   int i;
00743   int entity;
00744   int line;
00745   CToken *token;
00746 
00747   ClearEntities();
00748   ClearProperties();
00749 
00750   // Add in the "global" entity.
00751   entity = AddEntity(-1, "");
00752   line = 1;
00753 
00754   for (i = 0; i < (int)this->tokens.size(); i++)
00755     {
00756       token = &this->tokens[0] + i;
00757                         
00758       switch (token->type)
00759                                 {
00760                                 case TokenWord:
00761                                         if ( token->value == "include") 
00762                                                 {
00763                                                         if (!ParseTokenInclude(&i, &line))
00764                                                                 return false;
00765                                                 }
00766                                         else if ( token->value == "define" )
00767                                                 {
00768                                                         if (!ParseTokenDefine(&i, &line))
00769                                                                 return false;
00770                                                 }
00771                                         else
00772                                                 {
00773                                                         if (!ParseTokenWord(entity, &i, &line))
00774                                                                 return false;
00775                                                 }
00776                                         break;
00777                                 case TokenComment:
00778                                         break;
00779                                 case TokenSpace:
00780                                         break;
00781                                 case TokenEOL:
00782                                         line++;
00783                                         break;
00784                                 default:
00785                                         PARSE_ERR("syntax error 1", line);
00786                                         return false;
00787                                 }
00788     }
00789   return true;
00790 }
00791 
00792 
00794 // Parse an include statement
00795 bool Worldfile::ParseTokenInclude(int *index, int *line)
00796 {
00797   int i;
00798   CToken *token;
00799         
00800   for (i = *index + 1; i < (int)this->tokens.size(); i++)
00801     {
00802       token = &this->tokens[i];
00803                         
00804       switch (token->type)
00805                                 {
00806                                 case TokenString:
00807                                         break;
00808                                 case TokenSpace:
00809                                         break;
00810                                 case TokenEOL:
00811                                         *index = i;
00812                                         (*line)++;
00813                                         return true;
00814                                 default:
00815                                         PARSE_ERR("syntax error in include statement", *line);
00816                                         return false;
00817                                 }
00818     }
00819   PARSE_ERR("incomplete include statement", *line);
00820   return false;
00821 }
00822 
00823 
00825 // Parse a macro definition
00826 bool Worldfile::ParseTokenDefine(int *index, int *line)
00827 {
00828   int i;
00829   int count;
00830   const char *macroname, *entityname;
00831   int starttoken;
00832   CToken *token;
00833 
00834   count = 0;
00835   macroname = NULL;
00836   entityname = NULL;
00837   starttoken = -1;
00838 
00839   for (i = *index + 1; i < (int)this->tokens.size(); i++)
00840     {
00841       token = &this->tokens[i];
00842 
00843       switch (token->type)
00844         {
00845         case TokenWord:
00846           if (count == 0)
00847             {
00848               if (macroname == NULL)
00849                 macroname = GetTokenValue(i);
00850               else if (entityname == NULL)
00851                 {
00852                   entityname = GetTokenValue(i);
00853                   starttoken = i;
00854                 }
00855               else
00856                 {
00857                   PARSE_ERR("extra tokens in macro definition", *line);
00858                   return false;
00859                 }
00860             }
00861           else
00862             {
00863               if (macroname == NULL)
00864                 {
00865                   PARSE_ERR("missing name in macro definition", *line);
00866                   return false;
00867                 }
00868               if (entityname == NULL)
00869                 {
00870                   PARSE_ERR("missing name in macro definition", *line);
00871                   return false;
00872                 }
00873             }
00874           break;
00875         case TokenOpenEntity:
00876           count++;
00877           break;
00878         case TokenCloseEntity:
00879           count--;
00880           if (count == 0)
00881             {
00882               AddMacro(macroname, entityname, *line, starttoken, i);
00883               *index = i;
00884               return true;
00885             }
00886           if (count < 0)
00887             {
00888               PARSE_ERR("misplaced ')'", *line);
00889               return false;
00890             }
00891           break;
00892         default:
00893           break;
00894         }
00895     }
00896   PARSE_ERR("missing ')'", *line);
00897   return false;
00898 }
00899 
00900 
00902 // Parse something starting with a word; could be a entity or an property.
00903 bool Worldfile::ParseTokenWord(int entity, int *index, int *line)
00904 {
00905   int i;
00906   CToken *token;
00907 
00908   for (i = *index + 1; i < (int)this->tokens.size(); i++)
00909     {
00910       token = &this->tokens[i];
00911 
00912       switch (token->type)
00913         {
00914         case TokenComment:
00915           break;
00916         case TokenSpace:
00917           break;
00918         case TokenEOL:
00919           (*line)++;
00920           break;
00921         case TokenOpenEntity:
00922           return ParseTokenEntity(entity, index, line);
00923         case TokenNum:
00924         case TokenString:
00925         case TokenOpenTuple:
00926           return ParseTokenProperty(entity, index, line);
00927         default:
00928           PARSE_ERR("syntax error 2", *line);
00929           return false;
00930         }
00931     }
00932 
00933   return false;
00934 }
00935 
00936 
00938 // Parse a entity from the token list.
00939 bool Worldfile::ParseTokenEntity(int entity, int *index, int *line)
00940 {
00941   int i;
00942   //int macro;
00943   int name;
00944   CToken *token;
00945 
00946   name = *index;
00947   CMacro* macro = LookupMacro(GetTokenValue(name));
00948 
00949   // If the entity name is a macro...
00950   if (macro )
00951     {
00952       int nentity = this->entities.size();
00953       int mindex = macro->starttoken;
00954       int mline = macro->line;
00955       if (!ParseTokenEntity(entity, &mindex, &mline))
00956                                 return false;
00957       entity = nentity;
00958 
00959       for (i = *index + 1; i < (int)this->tokens.size(); i++)
00960                                 {
00961                                         token = &this->tokens[i];
00962 
00963           switch (token->type)
00964             {
00965             case TokenOpenEntity:
00966               break;
00967             case TokenWord:
00968               if (!ParseTokenWord(entity, &i, line))
00969                 return false;
00970               break;
00971             case TokenCloseEntity:
00972               *index = i;
00973               return true;
00974             case TokenComment:
00975               break;
00976             case TokenSpace:
00977               break;
00978             case TokenEOL:
00979               (*line)++;
00980               break;
00981             default:
00982               PARSE_ERR("syntax error 3", *line);
00983               return false;
00984             }
00985         }
00986       PARSE_ERR("missing ')'", *line);
00987     }
00988 
00989   // If the entity name is not a macro...
00990   else
00991     {
00992       for (i = *index + 1; i < (int)this->tokens.size(); i++)
00993         {
00994           token = &this->tokens[i];
00995 
00996           switch (token->type)
00997             {
00998             case TokenOpenEntity:
00999               entity = AddEntity(entity, GetTokenValue(name));
01000               break;
01001             case TokenWord:
01002               if (!ParseTokenWord(entity, &i, line))
01003                 return false;
01004               break;
01005             case TokenCloseEntity:
01006               *index = i;
01007               return true;
01008             case TokenComment:
01009               break;
01010             case TokenSpace:
01011               break;
01012             case TokenEOL:
01013               (*line)++;
01014               break;
01015             default:
01016               PARSE_ERR("syntax error 3", *line);
01017               return false;
01018             }
01019         }
01020       PARSE_ERR("missing ')'", *line);
01021     }
01022   return false;
01023 }
01024 
01025 
01027 // Parse an property from the token list.
01028 bool Worldfile::ParseTokenProperty(int entity, int *index, int *line)
01029 {
01030   CProperty* property(NULL);
01031   int name( *index );
01032   CToken *token(NULL);
01033   
01034   for(int i = *index + 1; i < (int)this->tokens.size(); i++)
01035     {
01036       token = &this->tokens[i];
01037 
01038       switch (token->type)
01039         {
01040         case TokenNum:
01041           property = AddProperty(entity, GetTokenValue(name), *line);
01042           AddPropertyValue(property, 0, i);
01043           *index = i;
01044           return true;
01045         case TokenString:
01046           property = AddProperty(entity, GetTokenValue(name), *line);
01047           AddPropertyValue(property, 0, i);
01048           *index = i;
01049           return true;
01050         case TokenOpenTuple:
01051           property = AddProperty(entity, GetTokenValue(name), *line);
01052           if (!ParseTokenTuple( property, &i, line))
01053             return false;
01054           *index = i;
01055           return true;
01056         case TokenSpace:
01057           break;
01058         default:
01059           PARSE_ERR("syntax error 4", *line);
01060           return false;
01061         }
01062     }
01063   return true;
01064 }
01065 
01066 
01068 // Parse a tuple.
01069 bool Worldfile::ParseTokenTuple( CProperty* property, int *index, int *line)
01070 {
01071   unsigned int i, count;
01072   CToken *token;
01073 
01074   count = 0;
01075 
01076   for (i = *index + 1; i < this->tokens.size(); i++)
01077     {
01078       token = &this->tokens[i];
01079 
01080       switch (token->type)
01081         {
01082         case TokenNum:
01083           AddPropertyValue(property, count++, i);
01084           *index = i;
01085           break;
01086         case TokenString:
01087           AddPropertyValue(property, count++, i);
01088           *index = i;
01089           break;
01090         case TokenCloseTuple:
01091           *index = i;
01092           return true;
01093         case TokenSpace:
01094           break;
01095         default:
01096           PARSE_ERR("syntax error 5", *line);
01097           return false;
01098         }
01099     }
01100   return true;
01101 }
01102 
01103 
01105 // Clear the macro list
01106 void Worldfile::ClearMacros()
01107 {
01108         macros.clear();
01109 }
01110 
01111 
01113 // Add a macro
01114 void Worldfile::AddMacro(const char *macroname, const char *entityname,
01115                                                                                                  int line, int starttoken, int endtoken)
01116 {
01117         macros.insert( std::pair<std::string,CMacro>( macroname, CMacro( macroname, entityname, line, starttoken, endtoken )));
01118 }
01119 
01120 
01122 // Lookup a macro by name
01123 // Returns -1 if there is no macro with this name.
01124 Worldfile::CMacro* Worldfile::LookupMacro(const char *macroname)
01125 {
01126         std::map<std::string,CMacro>::iterator it = macros.find( macroname );
01127         
01128         if( it == macros.end() )
01129                 return NULL;
01130         else
01131                 return &it->second;
01132 }
01133 
01134 
01136 // Dump the macro list for debugging
01137 void Worldfile::DumpMacros()
01138 {
01139   printf("\n## begin macros\n");
01140 
01141         FOR_EACH( it, macros )
01142                 //for (int i = 0; i < this->macro_count; i++)
01143     {
01144       CMacro *macro = &(it->second);//this->macros + i;
01145 
01146                                 printf("## [%s][%s]", macro->macroname.c_str(), macro->entityname.c_str());
01147       for (int j = macro->starttoken; j <= macro->endtoken; j++)
01148         {
01149           if (this->tokens[j].type == TokenEOL)
01150             printf("[\\n]");
01151           else
01152             printf("[%s]", GetTokenValue(j));
01153         }
01154       printf("\n");
01155     }
01156   printf("## end macros\n");
01157 }
01158 
01159 
01161 // Clear the entity list
01162 void Worldfile::ClearEntities()
01163 {
01164   // free(this->entities);
01165   // this->entities = NULL;
01166   // this->entity_size = 0;
01167   // this->entity_count = 0;
01168 
01169         this->entities.clear();
01170 }
01171 
01172 
01174 // Add a entity
01175 int Worldfile::AddEntity(int parent, const char *type)
01176 {
01177   // if (this->entity_count >= this->entity_size)
01178   //   {
01179   //     this->entity_size += 100;
01180   //     this->entities = (CEntity*)
01181         // realloc(this->entities, this->entity_size * sizeof(this->entities[0]));
01182   //   }
01183 
01184   // int entity = this->entity_count;
01185   // this->entities[entity].parent = parent;
01186   // this->entities[entity].type = type;
01187   // this->entity_count++;
01188 
01189         this->entities.push_back( CEntity( parent, type ));
01190   return( entities.size()-1); // index of the new entity
01191 }
01192 
01193 
01195 // Get the number of entities
01196 int Worldfile::GetEntityCount()
01197 {
01198   return this->entities.size();
01199 }
01200 
01201 
01203 // Get a entity's parent entity
01204 int Worldfile::GetEntityParent(int entity)
01205 {
01206   if (entity < 0 || entity >= (int)this->entities.size())
01207     return -1;
01208   return this->entities[entity].parent;
01209 }
01210 
01211 
01213 // Get a entity (returns the entity type value)
01214 const char *Worldfile::GetEntityType(int entity)
01215 {
01216   if (entity < 0 || entity >= (int)this->entities.size())
01217     return NULL;
01218   return this->entities[entity].type.c_str();
01219 }
01220 
01221 
01223 // Lookup a entity number by type name
01224 // Returns -1 if there is no entity with this type
01225 int Worldfile::LookupEntity(const char *type)
01226 {
01227   for (int entity = 0; entity < GetEntityCount(); entity++)
01228     {
01229       if (strcmp(GetEntityType(entity), type) == 0)
01230         return entity;
01231     }
01232   return -1;
01233 }
01234 
01235 
01236 void PrintProp( const char* key, CProperty* prop )
01237 {
01238   if( prop )
01239     printf( "Print key %s prop ent %d name %s\n", key, prop->entity, prop->name.c_str() );
01240 }
01241 
01243 // Dump the entity list for debugging
01244 void Worldfile::DumpEntities()
01245 {
01246   printf("\n## begin entities\n");
01247 
01248         FOR_EACH( it, properties )
01249                 PrintProp( it->first.c_str(), it->second );
01250 
01251   printf("## end entities\n");
01252 }
01253 
01254 
01256 // Clear the property list
01257 void Worldfile::ClearProperties()
01258 {
01259         FOR_EACH( it, properties )
01260                 delete it->second;      
01261         properties.clear();
01262 }
01263 
01264 
01266 // Add an property
01267 CProperty* Worldfile::AddProperty(int entity, const char *name, int line)
01268 {
01269   char key[128];
01270   snprintf( key, 127, "%d%s", entity, name );
01271 
01272   CProperty *property = new CProperty( entity, name, line );
01273 
01274         properties[ key ] = property;
01275 
01276         return property;
01277 }
01278 
01279 
01281 // Add an property value
01282 void Worldfile::AddPropertyValue( CProperty* property, int index, int value_token)
01283 {
01284   assert(property);
01285 
01286   // Set the relevant value
01287         
01288         if( index >= (int)property->values.size() )
01289                 property->values.resize( index+1 );
01290 
01291   property->values[index] = value_token;
01292 }
01293 
01294 
01295 
01297 // Get an property
01298 CProperty* Worldfile::GetProperty(int entity, const char *name)
01299 {
01300   char key[128];
01301   snprintf( key, 127, "%d%s", entity, name );
01302   
01303   //printf( "looking up key %s for entity %d name %s\n", key, entity, name );
01304   
01305   static char cache_key[128] = { 0 }; 
01306   static CProperty* cache_property = NULL;
01307   
01308   if( strncmp( key, cache_key, 128 ) != 0 ) // different to last time
01309          {              
01310                 strncpy( cache_key, key, 128 ); // remember for next time               
01311                 
01312                 std::map<std::string,CProperty*>::iterator it = properties.find( key ); 
01313                 if( it == properties.end() ) // not found
01314                   cache_property = NULL;
01315                 else
01316                   cache_property = it->second;          
01317          }
01318   //else
01319   // printf( "cache hit with %s\n", cache_key );
01320         
01321   return cache_property;
01322 }
01323 
01324 bool Worldfile::PropertyExists( int section, const char* token )
01325 {
01326   return (bool)GetProperty( section, token );
01327 }
01328 
01329 
01331 // Set the value of an property
01332 void Worldfile::SetPropertyValue( CProperty* property, int index, const char *value)
01333 {
01334   assert( property );
01335   //printf( "property %s index %d values %d \n",
01336   //      property->name.c_str(), index, (int)property->values.size() );
01337 
01338   assert(index >= 0 && index < (int)property->values.size() );
01339   // Set the relevant value
01340   SetTokenValue( property->values[index], value);
01341 }
01342 
01344 // Get the value of an property
01345 const char *Worldfile::GetPropertyValue(CProperty* property, int index)
01346 {
01347   assert(property);
01348   property->used = true;
01349   return GetTokenValue(property->values[index]);
01350 }
01351 
01352 
01354 // Dump the property list for debugging
01355 void Worldfile::DumpProperties()
01356 {
01357   printf("\n## begin properties\n");
01358   //   for (int i = 0; i < this->property_count; i++)
01359   //   {
01360   //     CProperty *property = this->properties + i;
01361   //     CEntity *entity = this->entities + property->entity;
01362 
01363   //     printf("## [%d]", property->entity);
01364   //     printf("[%s]", entity->type);
01365   //     printf("[%s]", property->name);
01366   //     for (int j = 0; j < property->value_count; j++)
01367   //       printf("[%s]", GetTokenValue(property->values[j]));
01368   //     printf("\n");
01369   //   }
01370   printf("## end properties\n");
01371 }
01372 
01373 
01375 // Read a string
01376 const std::string Worldfile::ReadString(int entity, const char *name, const std::string& value)
01377 {
01378   CProperty* property = GetProperty(entity, name);
01379   if (property == NULL )
01380     return value;
01381   return GetPropertyValue(property, 0);
01382 }
01383 
01384 
01386 // Write a string
01387 void Worldfile::WriteString(int entity, const char *name, const std::string &value)
01388 {
01389   CProperty* property = GetProperty(entity, name);
01390   if( property == NULL )
01391     return;
01392   SetPropertyValue(property, 0, value.c_str());
01393 }
01394 
01395 
01397 // Read an int
01398 int Worldfile::ReadInt(int entity, const char *name, int value)
01399 {
01400   CProperty* property = GetProperty(entity, name);
01401   if (property == NULL )
01402     return value;
01403   return atoi(GetPropertyValue(property, 0));
01404 }
01405 
01406 
01408 // Write an int
01409 void Worldfile::WriteInt(int entity, const char *name, int value)
01410 {
01411   char default_str[64];
01412   snprintf(default_str, sizeof(default_str), "%d", value);
01413   WriteString(entity, name, default_str);
01414 }
01415 
01417 // Write a float
01418 void Worldfile::WriteFloat(int entity, const char *name, double value)
01419 {
01420   // compact zeros make the file more readable
01421   if( fabs(value) < 0.001 ) // nearly 0
01422     WriteString(entity, name, "0" ); 
01423   else
01424     {
01425       char default_str[64];
01426       snprintf(default_str, sizeof(default_str), "%.3f", value);
01427       WriteString(entity, name, default_str);
01428     }
01429 }
01430 
01431 
01433 // Read a float
01434 double Worldfile::ReadFloat(int entity, const char *name, double value)
01435 {
01436   CProperty* property = GetProperty(entity, name);
01437   if (property == NULL )
01438     return value;
01439   return atof(GetPropertyValue(property, 0));
01440 }
01441 
01442 
01444 // Read a file name
01445 // Always returns an absolute path.
01446 // If the filename is entered as a relative path, we prepend
01447 // the world files path to it.
01448 // Known bug: will leak memory everytime it is called (but its not called often,
01449 // so I cant be bothered fixing it).
01450 const char *Worldfile::ReadFilename(int entity, const char *name, const char *value)
01451 {
01452   CProperty* property = GetProperty(entity, name);
01453   if (property == NULL )
01454     return value;
01455   const char *filename = GetPropertyValue(property, 0);
01456 
01457   if( filename[0] == '/' || filename[0] == '~' )
01458     return filename;
01459 
01460   else if (this->filename[0] == '/' || this->filename[0] == '~')
01461     {
01462       // Note that dirname() modifies the contents, so
01463       // we need to make a copy of the filename.
01464       // There's no bounds-checking, but what the heck.
01465       char *tmp = strdup(this->filename.c_str());
01466                 char* fullpath = new char[PATH_MAX];
01467       memset(fullpath, 0, PATH_MAX);
01468       strcat( fullpath, dirname(tmp));
01469       strcat( fullpath, "/" );
01470       strcat( fullpath, filename );
01471       assert(strlen(fullpath) + 1 < PATH_MAX);
01472       if(tmp) free(tmp);
01473       return fullpath;
01474     }
01475   else
01476     {
01477       // Prepend the path
01478       // Note that dirname() modifies the contents, so
01479       // we need to make a copy of the filename.
01480       // There's no bounds-checking, but what the heck.
01481       char *tmp = strdup(this->filename.c_str());
01482                 char* fullpath = new char[PATH_MAX];
01483       char* dummy = getcwd(fullpath, PATH_MAX);
01484       if (!dummy)
01485       {
01486         PRINT_ERR2("unable to get cwd %d: %s", errno, strerror(errno));
01487                   if( fullpath ) delete[] fullpath;
01488                   if( tmp ) free(tmp);
01489         return value;
01490       }
01491 
01492       strcat( fullpath, "/" );
01493       strcat( fullpath, dirname(tmp));
01494       strcat( fullpath, "/" );
01495       strcat( fullpath, filename );
01496       assert(strlen(fullpath) + 1 < PATH_MAX);
01497       free(tmp);
01498 
01499       return fullpath;
01500     }
01501 }
01502 
01503 
01505 // Read a series of floats from a tuple (experimental)
01506 int Worldfile::ReadTuple( const int entity, const char *name,
01507                          const unsigned int first, const unsigned int count, const char* format, ... )
01508 {
01509   CProperty* property = GetProperty(entity, name);
01510   if (property == NULL )
01511     return 0;
01512   
01513   if( property->values.size() < first+count )
01514     {
01515       PRINT_ERR4( "Worldfile: reading tuple \"%s\" index %u to %u - tuple has length %u\n",
01516                   name, first, first+count-1, (unsigned int)property->values.size() );
01517       exit(-1);
01518     }
01519     
01520   if( strlen(format) != count )
01521     {
01522       PRINT_ERR2( "format string length %u does not match argument count %u", 
01523                   (unsigned int)strlen(format), count );
01524       exit(-1);
01525     }
01526   
01527   va_list args;
01528   va_start( args, format );
01529 
01530   for( unsigned int i=0; i<count; i++ )
01531     {
01532       const char* val = GetPropertyValue(property, first+i);
01533       
01534       switch( format[i] )
01535         {
01536         case 'i': // signed integer
01537           *va_arg( args, int* ) = atoi(val);
01538           break;
01539 
01540         case 'u': // unsigned integer
01541           *va_arg( args, unsigned int* ) = (unsigned int)atoi(val);
01542           break;
01543 
01544         case 'f': // float
01545           *va_arg( args, double* ) = atof(val);
01546           break;
01547           
01548         case 'l': //length
01549           *va_arg( args, double* ) = atof(val) * unit_length;
01550           break;
01551           
01552         case 'a': // angle
01553           *va_arg( args, double* ) = atof(val) * unit_angle;
01554           break;
01555           
01556         case 's': // string
01557           *va_arg( args, char** ) = strdup(val);
01558           break;
01559           
01560         default:
01561           PRINT_ERR3( "Unknown format character %c in string %s loading %s",
01562                       format[i], format, name );
01563         }
01564     }
01565 
01566   va_end( args );
01567   
01568   return count;
01569 }
01570 
01572 // Write a series of floats to a tuple (experimental)
01573 void Worldfile::WriteTuple( const int entity, const char *name,
01574                          const unsigned int first, const unsigned int count, const char* format, ... )
01575 {
01576   CProperty* property = GetProperty(entity, name);
01577   if (property == NULL )
01578     return;
01579   
01580   if( property->values.size() < first+count )
01581     {
01582       PRINT_ERR4( "Worldfile: reading tuple \"%s\" index %d to %d - tuple has length %d\n",
01583                   name, first, first+count-1, (int)property->values.size() );
01584       exit(-1);
01585     }
01586   
01587   if( strlen(format) != count )
01588     {
01589       PRINT_ERR2( "format string length %u does not match argument count %u", 
01590                   (unsigned int)strlen(format), count );
01591       exit(-1);
01592     }
01593   
01594   char buf[2048];
01595   buf[0] = 0;
01596   
01597   va_list args;
01598   va_start( args, format );
01599   
01600   for( unsigned int i=0; i<count; i++ )
01601     {      
01602       switch( format[i] )
01603         {
01604         case 'i': // integer
01605           snprintf( buf, sizeof(buf), "%d", va_arg( args, int ) );
01606           break;
01607 
01608         case 'u': // unsigned integer
01609           snprintf( buf, sizeof(buf), "%u", va_arg( args, unsigned int ) );
01610           break;
01611 
01612         case 'f': // float
01613           snprintf( buf, sizeof(buf), "%.3f", va_arg( args, double ) );
01614           break;
01615           
01616         case 'l': //length
01617           snprintf( buf, sizeof(buf), "%.3f",  va_arg( args, double ) / unit_length );
01618           break;
01619           
01620         case 'a': // angle
01621           snprintf( buf, sizeof(buf), "%.3f", va_arg( args, double ) / unit_angle );
01622           break;
01623           
01624         case 's': // string
01625           strncpy( buf, va_arg( args, char* ), sizeof(buf) );
01626           buf[sizeof(buf)-1] = 0; // force zero terminator
01627           break;
01628           
01629         default:
01630           PRINT_ERR3( "Unknown format character %c in string %s loading %s",
01631                       format[i], format, name );
01632           exit(-1);
01633         }
01634       
01635       //printf( "writing %s %d %s\n", name, first+i, buf );
01636       SetPropertyValue(property, first+i, buf );
01637     }
01638   
01639   va_end( args );
01640 }
01641 


stage
Author(s): Richard Vaughan , Brian Gerkey , Reed Hedges , Andrew Howard , Toby Collett , Pooya Karimian , Jeremy Asher , Alex Couture-Beil , Geoff Biggs , Rich Mattes , Abbas Sadat
autogenerated on Thu Aug 27 2015 15:20:57