worldfile.cc
Go to the documentation of this file.
1 /*
2  * Stage : a multi-robot simulator.
3  * Copyright (C) 2001-2011 Richard Vaughan, Andrew Howard and Brian Gerkey.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  */
20 /*
21  * Desc: A class for reading in the world file.
22  * Authors: Andrew Howard <ahoward@usc.edu>
23  * Richard Vaughan (rtv) <vaughan@sfu.ca>
24  * Douglas S. Blank <dblank@brynmawr.edu>
25  *
26  * Date: 15 Nov 2001
27  * CVS info: $Id$
28  */
29 
30 #include <assert.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <limits.h> // for PATH_MAX
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <math.h>
39 #include <stdarg.h>
40 
41 //#define DEBUG
42 
43 #include "replace.h" // for dirname(3)
44 #include "stage.hh"
45 #include "worldfile.hh"
46 using namespace Stg;
47 
48 // the isblank() macro is not standard - it's a GNU extension
49 // and it doesn't work for me, so here's an implementation - rtv
50 #ifndef isblank
51 #define isblank(a) (a == ' ' || a == '\t')
52 #endif
53 
55 // Useful macros for dumping parser errors
56 #define TOKEN_ERR(z, l) \
57  PRINT_ERR2("%s:%d : " z, this->filename.c_str(), l)
58 #define PARSE_ERR(z, l) \
59  PRINT_ERR2("%s:%d : " z, this->filename.c_str(), l)
60 
61 
63 // Default constructor
65  tokens(),
66  macros(),
67  entities(),
68  properties(),
69  filename(),
70  unit_length( 1.0 ),
71  unit_angle( M_PI / 180.0 )
72 {
73 }
74 
75 
77 // Destructor
79 {
81  ClearMacros();
82  ClearEntities();
83  ClearTokens();
84 }
85 
86 FILE *Worldfile::FileOpen(const std::string& filename, const char* method)
87 {
88  FILE *fp = fopen(filename.c_str(), method);
89  // if this opens, then we will go with it:
90  if (fp) {
91  PRINT_DEBUG1( "Loading: %s", filename.c_str());
92  return fp;
93  }
94  // else, search other places, and set this->filename
95  // accordingly if found:
96  char *stagepath = getenv("STAGEPATH");
97  char *token = strtok(stagepath, ":");
98  char* fullpath = new char[PATH_MAX];
99  char *tmp = strdup(filename.c_str());
100  const char *base = basename(tmp);
101  while (token != NULL) {
102  // for each part of the path, try it:
103  memset( fullpath, 0, PATH_MAX);
104  strcat( fullpath, token);
105  strcat( fullpath, "/" );
106  strcat( fullpath, base);
107  assert(strlen(fullpath) + 1 < PATH_MAX);
108  fp = fopen(fullpath, method);
109  if (fp) {
110  this->filename = std::string(fullpath);
111  PRINT_DEBUG1( "Loading: %s", filename.c_str());
112  free(tmp);
113  return fp;
114  }
115  token = strtok(NULL, ":");
116  }
117  if( tmp ) free(tmp);
118  if( fullpath ) delete[] fullpath;
119  return NULL;
120 }
121 
122 
124 // Load world from file
125 bool Worldfile::Load(const std::string& filename )
126 {
127  this->filename = filename;
128 
129  // Open the file
130  //FILE *file = fopen(this->filename, "r");
131  FILE *file = FileOpen(this->filename.c_str(), "r");
132  if (!file)
133  {
134  PRINT_ERR2("unable to open world file %s : %s",
135  this->filename.c_str(), strerror(errno));
136  return false;
137  }
138 
139  ClearTokens();
140 
141  // Read tokens from the file
142  if (!LoadTokens(file, 0))
143  {
144  //DumpTokens();
145  fclose(file);
146  return false;
147  }
148 
149  fclose(file);
150 
151  // Parse the tokens to identify entities
152  if (!ParseTokens())
153  {
154  //DumpTokens();
155  return false;
156  }
157 
158  // Dump contents and exit if this file is meant for debugging only.
159  if (ReadInt(0, "test", 0) != 0)
160  {
161  PRINT_ERR("this is a test file; quitting");
162  DumpTokens();
163  DumpMacros();
164  DumpEntities();
165  DumpProperties();
166  return false;
167  }
168 
169  // Work out what the length units are
170  const std::string& unitl = ReadString(0, "unit_length", "m");
171  if( unitl == "m")
172  this->unit_length = 1.0;
173  else if( unitl == "cm")
174  this->unit_length = 0.01;
175  else if( unitl == "mm")
176  this->unit_length = 0.001;
177 
178  // Work out what the angle units are
179  const std::string& unita = ReadString(0, "unit_angle", "degrees");
180  if( unita == "degrees")
181  this->unit_angle = M_PI / 180;
182  else if( unita == "radians")
183  this->unit_angle = 1;
184 
185  //printf( "f: %s\n", this->filename.c_str() );
186 
187  return true;
188 }
189 
190 
192 // Save world to file
193 bool Worldfile::Save(const std::string& filename )
194 {
195  // Debugging
196  //DumpProperties();
197 
198  // Open file
199  FILE *file = fopen(filename.c_str(), "w+");
200  //FILE *file = FileOpen(filename, "w+");
201  if (!file)
202  {
203  PRINT_ERR2("unable to open world file %s : %s",
204  filename.c_str(), strerror(errno));
205  return false;
206  }
207 
208  // TODO - make a backup before saving the file
209 
210  // Write the current set of tokens to the file
211  if (!SaveTokens(file))
212  {
213  fclose(file);
214  return false;
215  }
216 
217  fclose(file);
218  return true;
219 }
220 
221 
223 // Check for unused properties and print warnings
225 {
226  bool unused = false;
227 
228  FOR_EACH( it, properties )
229  {
230  if( ! it->second->used )
231  {
232  PRINT_WARN3("worldfile %s:%d : property [%s] is defined but not used",
233  this->filename.c_str(), it->second->line, it->second->name.c_str());
234  unused = true;
235  }
236  }
237 
238  return unused;
239 }
240 
241 
243 // Load tokens from a file.
244 bool Worldfile::LoadTokens(FILE *file, int include)
245 {
246  int ch;
247  int line;
248  char token[256];
249 
250  line = 1;
251 
252  while (true)
253  {
254  ch = fgetc(file);
255  if (ch == EOF)
256  break;
257 
258  if ((char) ch == '#')
259  {
260  ungetc(ch, file);
261  if (!LoadTokenComment(file, &line, include))
262  return false;
263  }
264  else if (isalpha(ch))
265  {
266  ungetc(ch, file);
267  if (!LoadTokenWord(file, &line, include))
268  return false;
269  }
270  else if (strchr("+-.0123456789", ch))
271  {
272  ungetc(ch, file);
273  if (!LoadTokenNum(file, &line, include))
274  return false;
275  }
276  else if (isblank(ch))
277  {
278  ungetc(ch, file);
279  if (!LoadTokenSpace(file, &line, include))
280  return false;
281  }
282  else if (ch == '"')
283  {
284  ungetc(ch, file);
285  if (!LoadTokenString(file, &line, include))
286  return false;
287  }
288  else if (strchr("(", ch))
289  {
290  token[0] = ch;
291  token[1] = 0;
292  AddToken(TokenOpenEntity, token, include);
293  }
294  else if (strchr(")", ch))
295  {
296  token[0] = ch;
297  token[1] = 0;
298  AddToken(TokenCloseEntity, token, include);
299  }
300  else if (strchr("[", ch))
301  {
302  token[0] = ch;
303  token[1] = 0;
304  AddToken(TokenOpenTuple, token, include);
305  }
306  else if (strchr("]", ch))
307  {
308  token[0] = ch;
309  token[1] = 0;
310  AddToken(TokenCloseTuple, token, include);
311  }
312  else if ( 0x0d == ch )
313  {
314  ch = fgetc(file);
315  if ( 0x0a != ch )
316  ungetc(ch, file);
317  line++;
318  AddToken(TokenEOL, "\n", include);
319  }
320  else if ( 0x0a == ch )
321  {
322  ch = fgetc(file);
323  if ( 0x0d != ch )
324  ungetc(ch, file);
325  line++;
326  AddToken(TokenEOL, "\n", include);
327  }
328  else
329  {
330  TOKEN_ERR("syntax error", line);
331  return false;
332  }
333  }
334 
335  return true;
336 }
337 
338 
340 // Read in a comment token
341 bool Worldfile::LoadTokenComment(FILE *file, int *line, int include)
342 {
343  char token[256];
344  int len;
345  int ch;
346 
347  len = 0;
348  memset(token, 0, sizeof(token));
349 
350  while (true)
351  {
352  ch = fgetc(file);
353 
354  if (ch == EOF)
355  {
356  AddToken(TokenComment, token, include);
357  return true;
358  }
359  else if ( 0x0a == ch || 0x0d == ch )
360  {
361  ungetc(ch, file);
362  AddToken(TokenComment, token, include);
363  return true;
364  }
365  else
366  token[len++] = ch;
367  }
368  return true;
369 }
370 
371 
373 // Read in a word token
374 bool Worldfile::LoadTokenWord(FILE *file, int *line, int include)
375 {
376  char token[256];
377  int len;
378  int ch;
379 
380  len = 0;
381  memset(token, 0, sizeof(token));
382 
383  while (true)
384  {
385  ch = fgetc(file);
386 
387  if (ch == EOF)
388  {
389  AddToken(TokenWord, token, include);
390  return true;
391  }
392  else if (isalpha(ch) || isdigit(ch) || strchr(".-_[]", ch))
393  {
394  token[len++] = ch;
395  }
396  else
397  {
398  if (strcmp(token, "include") == 0)
399  {
400  ungetc(ch, file);
401  AddToken(TokenWord, token, include);
402  if (!LoadTokenInclude(file, line, include))
403  return false;
404  }
405  else
406  {
407  ungetc(ch, file);
408  AddToken(TokenWord, token, include);
409  }
410  return true;
411  }
412  }
413  assert(false);
414  return false;
415 }
416 
417 
419 // Load an include token; this will load the include file.
420 bool Worldfile::LoadTokenInclude(FILE *file, int *line, int include)
421 {
422  int ch;
423  const char *filename;
424  char *fullpath;
425 
426  ch = fgetc(file);
427 
428  if (ch == EOF)
429  {
430  TOKEN_ERR("incomplete include statement", *line);
431  return false;
432  }
433  else if (!isblank(ch))
434  {
435  TOKEN_ERR("syntax error in include statement", *line);
436  return false;
437  }
438 
439  ungetc(ch, file);
440  if (!LoadTokenSpace(file, line, include))
441  return false;
442 
443  ch = fgetc(file);
444 
445  if (ch == EOF)
446  {
447  TOKEN_ERR("incomplete include statement", *line);
448  return false;
449  }
450  else if (ch != '"')
451  {
452  TOKEN_ERR("syntax error in include statement", *line);
453  return false;
454  }
455 
456  ungetc(ch, file);
457  if (!LoadTokenString(file, line, include))
458  return false;
459 
460  // This is the basic filename
461  filename = GetTokenValue(this->tokens.size() - 1);
462 
463  // Now do some manipulation. If its a relative path,
464  // we append the path of the world file.
465  if (filename[0] == '/' || filename[0] == '~')
466  {
467  fullpath = strdup(filename);
468  }
469  else if (this->filename[0] == '/' || this->filename[0] == '~')
470  {
471  // Note that dirname() modifies the contents, so
472  // we need to make a copy of the filename.
473  // There's no bounds-checking, but what the heck.
474  char *tmp = strdup(this->filename.c_str());
475  fullpath = new char[PATH_MAX];
476  memset(fullpath, 0, PATH_MAX);
477  strcat( fullpath, dirname(tmp));
478  strcat( fullpath, "/" );
479  strcat( fullpath, filename );
480  assert(strlen(fullpath) + 1 < PATH_MAX);
481  free(tmp);
482  }
483  else
484  {
485  // Note that dirname() modifies the contents, so
486  // we need to make a copy of the filename.
487  // There's no bounds-checking, but what the heck.
488  char *tmp = strdup(this->filename.c_str());
489  fullpath = new char[PATH_MAX];
490  char* dummy = getcwd(fullpath, PATH_MAX);
491  if (!dummy)
492  {
493  PRINT_ERR2("unable to get cwd %d: %s", errno, strerror(errno));
494  if(tmp) free(tmp);
495  delete[] fullpath;
496  return false;
497  }
498  strcat( fullpath, "/" );
499  strcat( fullpath, dirname(tmp));
500  strcat( fullpath, "/" );
501  strcat( fullpath, filename );
502  assert(strlen(fullpath) + 1 < PATH_MAX);
503  free(tmp);
504  }
505 
506  printf( "[Include %s]", filename );
507  fflush( stdout );
508 
509  // Open the include file
510  FILE *infile = FileOpen(fullpath, "r");
511  if (!infile)
512  {
513  PRINT_ERR2("unable to open include file %s : %s",
514  fullpath, strerror(errno));
515  //free(fullpath);
516  delete[] fullpath;
517  return false;
518  }
519 
520  // Terminate the include line
521  AddToken(TokenEOL, "\n", include);
522 
523  //DumpTokens();
524 
525  // Read tokens from the file
526  if (!LoadTokens(infile, include + 1))
527  {
528  fclose( infile );
529  //DumpTokens();
530  //free(fullpath);
531  delete[] fullpath;
532  return false;
533  }
534 
535  // done with the include file
536  fclose( infile );
537 
538  // consume the rest of the include line XX a bit of a hack - assumes
539  // that an include is the last thing on a line
540  while ( ch != '\n' )
541  ch = fgetc(file);
542 
543  delete[] fullpath;
544  return true;
545 }
546 
547 
549 // Read in a number token
550 bool Worldfile::LoadTokenNum(FILE *file, int *line, int include)
551 {
552  char token[256];
553  int len;
554  int ch;
555 
556  len = 0;
557  memset(token, 0, sizeof(token));
558 
559  while (true)
560  {
561  ch = fgetc(file);
562 
563  if (ch == EOF)
564  {
565  AddToken(TokenNum, token, include);
566  return true;
567  }
568  else if (strchr("+-.0123456789", ch))
569  {
570  token[len++] = ch;
571  }
572  else
573  {
574  AddToken(TokenNum, token, include);
575  ungetc(ch, file);
576  return true;
577  }
578  }
579  assert(false);
580  return false;
581 }
582 
583 
585 // Read in a string token
586 bool Worldfile::LoadTokenString(FILE *file, int *line, int include)
587 {
588  int ch;
589  int len;
590  char token[256];
591 
592  len = 0;
593  memset(token, 0, sizeof(token));
594 
595  ch = fgetc(file);
596 
597  while (true)
598  {
599  ch = fgetc(file);
600 
601  if ( EOF == ch || 0x0a == ch || 0x0d == ch )
602  {
603  TOKEN_ERR("unterminated string constant", *line);
604  return false;
605  }
606  else if (ch == '"')
607  {
608  AddToken(TokenString, token, include);
609  return true;
610  }
611  else
612  {
613  token[len++] = ch;
614  }
615  }
616  assert(false);
617  return false;
618 }
619 
620 
622 // Read in a whitespace token
623 bool Worldfile::LoadTokenSpace(FILE *file, int *line, int include)
624 {
625  int ch;
626  int len;
627  char token[256];
628 
629  len = 0;
630  memset(token, 0, sizeof(token));
631 
632  while (true)
633  {
634  ch = fgetc(file);
635 
636  if (ch == EOF)
637  {
638  AddToken(TokenSpace, token, include);
639  return true;
640  }
641  else if (isblank(ch))
642  {
643  token[len++] = ch;
644  }
645  else
646  {
647  AddToken(TokenSpace, token, include);
648  ungetc(ch, file);
649  return true;
650  }
651  }
652  assert(false);
653  return false;
654 }
655 
656 
658 // Save tokens to a file.
659 bool Worldfile::SaveTokens(FILE *file)
660 {
661  unsigned int i;
662  CToken *token;
663 
664  for (i = 0; i < this->tokens.size(); i++)
665  {
666  token = &this->tokens[i];
667 
668  if (token->include > 0)
669  continue;
670  if (token->type == TokenString)
671  fprintf(file, "\"%s\"", token->value.c_str());
672  else
673  fprintf(file, "%s", token->value.c_str());
674  }
675  return true;
676 }
677 
678 
680 // Clear the token list
682 {
683  tokens.clear();
684 }
685 
686 
688 // Add a token to the token list
689 bool Worldfile::AddToken(int type, const char *value, int include)
690 {
691  tokens.push_back( CToken( include, type, value ));
692  return true;
693 }
694 
695 
697 // Set a token value in the token list
698 bool Worldfile::SetTokenValue(int index, const char *value)
699 {
700  assert(index >= 0 && index < (int)this->tokens.size() );
701  tokens[index].value = value;
702  return true;
703 }
704 
705 
707 // Get the value of a token
708 const char *Worldfile::GetTokenValue(int index)
709 {
710  assert(index >= 0 && index < (int)this->tokens.size());
711  return this->tokens[index].value.c_str();
712 }
713 
714 
716 // Dump the token list (for debugging).
718 {
719  int line;
720 
721  line = 1;
722  printf("\n## begin tokens\n");
723  printf("## %4d : ", line);
724 
725  FOR_EACH( it, tokens )
726  //for (int i = 0; i < this->token_count; i++)
727  {
728  if ( it->value[0] == '\n')
729  printf("[\\n]\n## %4d : %02d ", ++line, it->include);
730  else
731  printf("[%s] ", it->value.c_str());
732  }
733  printf("\n");
734  printf("## end tokens\n");
735 }
736 
737 
739 // Parse tokens into entities and properties.
741 {
742  int i;
743  int entity;
744  int line;
745  CToken *token;
746 
747  ClearEntities();
748  ClearProperties();
749 
750  // Add in the "global" entity.
751  entity = AddEntity(-1, "");
752  line = 1;
753 
754  for (i = 0; i < (int)this->tokens.size(); i++)
755  {
756  token = &this->tokens[0] + i;
757 
758  switch (token->type)
759  {
760  case TokenWord:
761  if ( token->value == "include")
762  {
763  if (!ParseTokenInclude(&i, &line))
764  return false;
765  }
766  else if ( token->value == "define" )
767  {
768  if (!ParseTokenDefine(&i, &line))
769  return false;
770  }
771  else
772  {
773  if (!ParseTokenWord(entity, &i, &line))
774  return false;
775  }
776  break;
777  case TokenComment:
778  break;
779  case TokenSpace:
780  break;
781  case TokenEOL:
782  line++;
783  break;
784  default:
785  PARSE_ERR("syntax error 1", line);
786  return false;
787  }
788  }
789  return true;
790 }
791 
792 
794 // Parse an include statement
795 bool Worldfile::ParseTokenInclude(int *index, int *line)
796 {
797  int i;
798  CToken *token;
799 
800  for (i = *index + 1; i < (int)this->tokens.size(); i++)
801  {
802  token = &this->tokens[i];
803 
804  switch (token->type)
805  {
806  case TokenString:
807  break;
808  case TokenSpace:
809  break;
810  case TokenEOL:
811  *index = i;
812  (*line)++;
813  return true;
814  default:
815  PARSE_ERR("syntax error in include statement", *line);
816  return false;
817  }
818  }
819  PARSE_ERR("incomplete include statement", *line);
820  return false;
821 }
822 
823 
825 // Parse a macro definition
826 bool Worldfile::ParseTokenDefine(int *index, int *line)
827 {
828  int i;
829  int count;
830  const char *macroname, *entityname;
831  int starttoken;
832  CToken *token;
833 
834  count = 0;
835  macroname = NULL;
836  entityname = NULL;
837  starttoken = -1;
838 
839  for (i = *index + 1; i < (int)this->tokens.size(); i++)
840  {
841  token = &this->tokens[i];
842 
843  switch (token->type)
844  {
845  case TokenWord:
846  if (count == 0)
847  {
848  if (macroname == NULL)
849  macroname = GetTokenValue(i);
850  else if (entityname == NULL)
851  {
852  entityname = GetTokenValue(i);
853  starttoken = i;
854  }
855  else
856  {
857  PARSE_ERR("extra tokens in macro definition", *line);
858  return false;
859  }
860  }
861  else
862  {
863  if (macroname == NULL)
864  {
865  PARSE_ERR("missing name in macro definition", *line);
866  return false;
867  }
868  if (entityname == NULL)
869  {
870  PARSE_ERR("missing name in macro definition", *line);
871  return false;
872  }
873  }
874  break;
875  case TokenOpenEntity:
876  count++;
877  break;
878  case TokenCloseEntity:
879  count--;
880  if (count == 0)
881  {
882  AddMacro(macroname, entityname, *line, starttoken, i);
883  *index = i;
884  return true;
885  }
886  if (count < 0)
887  {
888  PARSE_ERR("misplaced ')'", *line);
889  return false;
890  }
891  break;
892  default:
893  break;
894  }
895  }
896  PARSE_ERR("missing ')'", *line);
897  return false;
898 }
899 
900 
902 // Parse something starting with a word; could be a entity or an property.
903 bool Worldfile::ParseTokenWord(int entity, int *index, int *line)
904 {
905  int i;
906  CToken *token;
907 
908  for (i = *index + 1; i < (int)this->tokens.size(); i++)
909  {
910  token = &this->tokens[i];
911 
912  switch (token->type)
913  {
914  case TokenComment:
915  break;
916  case TokenSpace:
917  break;
918  case TokenEOL:
919  (*line)++;
920  break;
921  case TokenOpenEntity:
922  return ParseTokenEntity(entity, index, line);
923  case TokenNum:
924  case TokenString:
925  case TokenOpenTuple:
926  return ParseTokenProperty(entity, index, line);
927  default:
928  PARSE_ERR("syntax error 2", *line);
929  return false;
930  }
931  }
932 
933  return false;
934 }
935 
936 
938 // Parse a entity from the token list.
939 bool Worldfile::ParseTokenEntity(int entity, int *index, int *line)
940 {
941  int i;
942  //int macro;
943  int name;
944  CToken *token;
945 
946  name = *index;
947  CMacro* macro = LookupMacro(GetTokenValue(name));
948 
949  // If the entity name is a macro...
950  if (macro )
951  {
952  int nentity = this->entities.size();
953  int mindex = macro->starttoken;
954  int mline = macro->line;
955  if (!ParseTokenEntity(entity, &mindex, &mline))
956  return false;
957  entity = nentity;
958 
959  for (i = *index + 1; i < (int)this->tokens.size(); i++)
960  {
961  token = &this->tokens[i];
962 
963  switch (token->type)
964  {
965  case TokenOpenEntity:
966  break;
967  case TokenWord:
968  if (!ParseTokenWord(entity, &i, line))
969  return false;
970  break;
971  case TokenCloseEntity:
972  *index = i;
973  return true;
974  case TokenComment:
975  break;
976  case TokenSpace:
977  break;
978  case TokenEOL:
979  (*line)++;
980  break;
981  default:
982  PARSE_ERR("syntax error 3", *line);
983  return false;
984  }
985  }
986  PARSE_ERR("missing ')'", *line);
987  }
988 
989  // If the entity name is not a macro...
990  else
991  {
992  for (i = *index + 1; i < (int)this->tokens.size(); i++)
993  {
994  token = &this->tokens[i];
995 
996  switch (token->type)
997  {
998  case TokenOpenEntity:
999  entity = AddEntity(entity, GetTokenValue(name));
1000  break;
1001  case TokenWord:
1002  if (!ParseTokenWord(entity, &i, line))
1003  return false;
1004  break;
1005  case TokenCloseEntity:
1006  *index = i;
1007  return true;
1008  case TokenComment:
1009  break;
1010  case TokenSpace:
1011  break;
1012  case TokenEOL:
1013  (*line)++;
1014  break;
1015  default:
1016  PARSE_ERR("syntax error 3", *line);
1017  return false;
1018  }
1019  }
1020  PARSE_ERR("missing ')'", *line);
1021  }
1022  return false;
1023 }
1024 
1025 
1027 // Parse an property from the token list.
1028 bool Worldfile::ParseTokenProperty(int entity, int *index, int *line)
1029 {
1030  CProperty* property(NULL);
1031  int name( *index );
1032  CToken *token(NULL);
1033 
1034  for(int i = *index + 1; i < (int)this->tokens.size(); i++)
1035  {
1036  token = &this->tokens[i];
1037 
1038  switch (token->type)
1039  {
1040  case TokenNum:
1041  property = AddProperty(entity, GetTokenValue(name), *line);
1042  AddPropertyValue(property, 0, i);
1043  *index = i;
1044  return true;
1045  case TokenString:
1046  property = AddProperty(entity, GetTokenValue(name), *line);
1047  AddPropertyValue(property, 0, i);
1048  *index = i;
1049  return true;
1050  case TokenOpenTuple:
1051  property = AddProperty(entity, GetTokenValue(name), *line);
1052  if (!ParseTokenTuple( property, &i, line))
1053  return false;
1054  *index = i;
1055  return true;
1056  case TokenSpace:
1057  break;
1058  default:
1059  PARSE_ERR("syntax error 4", *line);
1060  return false;
1061  }
1062  }
1063  return true;
1064 }
1065 
1066 
1068 // Parse a tuple.
1069 bool Worldfile::ParseTokenTuple( CProperty* property, int *index, int *line)
1070 {
1071  unsigned int i, count;
1072  CToken *token;
1073 
1074  count = 0;
1075 
1076  for (i = *index + 1; i < this->tokens.size(); i++)
1077  {
1078  token = &this->tokens[i];
1079 
1080  switch (token->type)
1081  {
1082  case TokenNum:
1083  AddPropertyValue(property, count++, i);
1084  *index = i;
1085  break;
1086  case TokenString:
1087  AddPropertyValue(property, count++, i);
1088  *index = i;
1089  break;
1090  case TokenCloseTuple:
1091  *index = i;
1092  return true;
1093  case TokenSpace:
1094  break;
1095  default:
1096  PARSE_ERR("syntax error 5", *line);
1097  return false;
1098  }
1099  }
1100  return true;
1101 }
1102 
1103 
1105 // Clear the macro list
1107 {
1108  macros.clear();
1109 }
1110 
1111 
1113 // Add a macro
1114 void Worldfile::AddMacro(const char *macroname, const char *entityname,
1115  int line, int starttoken, int endtoken)
1116 {
1117  macros.insert( std::pair<std::string,CMacro>( macroname, CMacro( macroname, entityname, line, starttoken, endtoken )));
1118 }
1119 
1120 
1122 // Lookup a macro by name
1123 // Returns -1 if there is no macro with this name.
1125 {
1126  std::map<std::string,CMacro>::iterator it = macros.find( macroname );
1127 
1128  if( it == macros.end() )
1129  return NULL;
1130  else
1131  return &it->second;
1132 }
1133 
1134 
1136 // Dump the macro list for debugging
1138 {
1139  printf("\n## begin macros\n");
1140 
1141  FOR_EACH( it, macros )
1142  //for (int i = 0; i < this->macro_count; i++)
1143  {
1144  CMacro *macro = &(it->second);//this->macros + i;
1145 
1146  printf("## [%s][%s]", macro->macroname.c_str(), macro->entityname.c_str());
1147  for (int j = macro->starttoken; j <= macro->endtoken; j++)
1148  {
1149  if (this->tokens[j].type == TokenEOL)
1150  printf("[\\n]");
1151  else
1152  printf("[%s]", GetTokenValue(j));
1153  }
1154  printf("\n");
1155  }
1156  printf("## end macros\n");
1157 }
1158 
1159 
1161 // Clear the entity list
1163 {
1164  // free(this->entities);
1165  // this->entities = NULL;
1166  // this->entity_size = 0;
1167  // this->entity_count = 0;
1168 
1169  this->entities.clear();
1170 }
1171 
1172 
1174 // Add a entity
1175 int Worldfile::AddEntity(int parent, const char *type)
1176 {
1177  // if (this->entity_count >= this->entity_size)
1178  // {
1179  // this->entity_size += 100;
1180  // this->entities = (CEntity*)
1181  // realloc(this->entities, this->entity_size * sizeof(this->entities[0]));
1182  // }
1183 
1184  // int entity = this->entity_count;
1185  // this->entities[entity].parent = parent;
1186  // this->entities[entity].type = type;
1187  // this->entity_count++;
1188 
1189  this->entities.push_back( CEntity( parent, type ));
1190  return( entities.size()-1); // index of the new entity
1191 }
1192 
1193 
1195 // Get the number of entities
1197 {
1198  return this->entities.size();
1199 }
1200 
1201 
1203 // Get a entity's parent entity
1205 {
1206  if (entity < 0 || entity >= (int)this->entities.size())
1207  return -1;
1208  return this->entities[entity].parent;
1209 }
1210 
1211 
1213 // Get a entity (returns the entity type value)
1214 const char *Worldfile::GetEntityType(int entity)
1215 {
1216  if (entity < 0 || entity >= (int)this->entities.size())
1217  return NULL;
1218  return this->entities[entity].type.c_str();
1219 }
1220 
1221 
1223 // Lookup a entity number by type name
1224 // Returns -1 if there is no entity with this type
1225 int Worldfile::LookupEntity(const char *type)
1226 {
1227  for (int entity = 0; entity < GetEntityCount(); entity++)
1228  {
1229  if (strcmp(GetEntityType(entity), type) == 0)
1230  return entity;
1231  }
1232  return -1;
1233 }
1234 
1235 
1236 void PrintProp( const char* key, CProperty* prop )
1237 {
1238  if( prop )
1239  printf( "Print key %s prop ent %d name %s\n", key, prop->entity, prop->name.c_str() );
1240 }
1241 
1243 // Dump the entity list for debugging
1245 {
1246  printf("\n## begin entities\n");
1247 
1248  FOR_EACH( it, properties )
1249  PrintProp( it->first.c_str(), it->second );
1250 
1251  printf("## end entities\n");
1252 }
1253 
1254 
1256 // Clear the property list
1258 {
1259  FOR_EACH( it, properties )
1260  delete it->second;
1261  properties.clear();
1262 }
1263 
1264 
1266 // Add an property
1267 CProperty* Worldfile::AddProperty(int entity, const char *name, int line)
1268 {
1269  char key[128];
1270  snprintf( key, 127, "%d%s", entity, name );
1271 
1272  CProperty *property = new CProperty( entity, name, line );
1273 
1274  properties[ key ] = property;
1275 
1276  return property;
1277 }
1278 
1279 
1281 // Add an property value
1282 void Worldfile::AddPropertyValue( CProperty* property, int index, int value_token)
1283 {
1284  assert(property);
1285 
1286  // Set the relevant value
1287 
1288  if( index >= (int)property->values.size() )
1289  property->values.resize( index+1 );
1290 
1291  property->values[index] = value_token;
1292 }
1293 
1294 
1295 
1297 // Get an property
1298 CProperty* Worldfile::GetProperty(int entity, const char *name)
1299 {
1300  char key[128];
1301  snprintf( key, 127, "%d%s", entity, name );
1302 
1303  //printf( "looking up key %s for entity %d name %s\n", key, entity, name );
1304 
1305  static char cache_key[128] = { 0 };
1306  static CProperty* cache_property = NULL;
1307 
1308  if( strncmp( key, cache_key, 128 ) != 0 ) // different to last time
1309  {
1310  strncpy( cache_key, key, 128 ); // remember for next time
1311 
1312  std::map<std::string,CProperty*>::iterator it = properties.find( key );
1313  if( it == properties.end() ) // not found
1314  cache_property = NULL;
1315  else
1316  cache_property = it->second;
1317  }
1318  //else
1319  // printf( "cache hit with %s\n", cache_key );
1320 
1321  return cache_property;
1322 }
1323 
1324 bool Worldfile::PropertyExists( int section, const char* token )
1325 {
1326  return (bool)GetProperty( section, token );
1327 }
1328 
1329 
1331 // Set the value of an property
1332 void Worldfile::SetPropertyValue( CProperty* property, int index, const char *value)
1333 {
1334  assert( property );
1335  //printf( "property %s index %d values %d \n",
1336  // property->name.c_str(), index, (int)property->values.size() );
1337 
1338  assert(index >= 0 && index < (int)property->values.size() );
1339  // Set the relevant value
1340  SetTokenValue( property->values[index], value);
1341 }
1342 
1344 // Get the value of an property
1345 const char *Worldfile::GetPropertyValue(CProperty* property, int index)
1346 {
1347  assert(property);
1348  property->used = true;
1349  return GetTokenValue(property->values[index]);
1350 }
1351 
1352 
1354 // Dump the property list for debugging
1356 {
1357  printf("\n## begin properties\n");
1358  // for (int i = 0; i < this->property_count; i++)
1359  // {
1360  // CProperty *property = this->properties + i;
1361  // CEntity *entity = this->entities + property->entity;
1362 
1363  // printf("## [%d]", property->entity);
1364  // printf("[%s]", entity->type);
1365  // printf("[%s]", property->name);
1366  // for (int j = 0; j < property->value_count; j++)
1367  // printf("[%s]", GetTokenValue(property->values[j]));
1368  // printf("\n");
1369  // }
1370  printf("## end properties\n");
1371 }
1372 
1373 
1375 // Read a string
1376 const std::string Worldfile::ReadString(int entity, const char *name, const std::string& value)
1377 {
1378  CProperty* property = GetProperty(entity, name);
1379  if (property == NULL )
1380  return value;
1381  return GetPropertyValue(property, 0);
1382 }
1383 
1384 
1386 // Write a string
1387 void Worldfile::WriteString(int entity, const char *name, const std::string &value)
1388 {
1389  CProperty* property = GetProperty(entity, name);
1390  if( property == NULL )
1391  return;
1392  SetPropertyValue(property, 0, value.c_str());
1393 }
1394 
1395 
1397 // Read an int
1398 int Worldfile::ReadInt(int entity, const char *name, int value)
1399 {
1400  CProperty* property = GetProperty(entity, name);
1401  if (property == NULL )
1402  return value;
1403  return atoi(GetPropertyValue(property, 0));
1404 }
1405 
1406 
1408 // Write an int
1409 void Worldfile::WriteInt(int entity, const char *name, int value)
1410 {
1411  char default_str[64];
1412  snprintf(default_str, sizeof(default_str), "%d", value);
1413  WriteString(entity, name, default_str);
1414 }
1415 
1417 // Write a float
1418 void Worldfile::WriteFloat(int entity, const char *name, double value)
1419 {
1420  // compact zeros make the file more readable
1421  if( fabs(value) < 0.001 ) // nearly 0
1422  WriteString(entity, name, "0" );
1423  else
1424  {
1425  char default_str[64];
1426  snprintf(default_str, sizeof(default_str), "%.3f", value);
1427  WriteString(entity, name, default_str);
1428  }
1429 }
1430 
1431 
1433 // Read a float
1434 double Worldfile::ReadFloat(int entity, const char *name, double value)
1435 {
1436  CProperty* property = GetProperty(entity, name);
1437  if (property == NULL )
1438  return value;
1439  return atof(GetPropertyValue(property, 0));
1440 }
1441 
1442 
1444 // Read a file name
1445 // Always returns an absolute path.
1446 // If the filename is entered as a relative path, we prepend
1447 // the world files path to it.
1448 // Known bug: will leak memory everytime it is called (but its not called often,
1449 // so I cant be bothered fixing it).
1450 const char *Worldfile::ReadFilename(int entity, const char *name, const char *value)
1451 {
1452  CProperty* property = GetProperty(entity, name);
1453  if (property == NULL )
1454  return value;
1455  const char *filename = GetPropertyValue(property, 0);
1456 
1457  if( filename[0] == '/' || filename[0] == '~' )
1458  return filename;
1459 
1460  else if (this->filename[0] == '/' || this->filename[0] == '~')
1461  {
1462  // Note that dirname() modifies the contents, so
1463  // we need to make a copy of the filename.
1464  // There's no bounds-checking, but what the heck.
1465  char *tmp = strdup(this->filename.c_str());
1466  char* fullpath = new char[PATH_MAX];
1467  memset(fullpath, 0, PATH_MAX);
1468  strcat( fullpath, dirname(tmp));
1469  strcat( fullpath, "/" );
1470  strcat( fullpath, filename );
1471  assert(strlen(fullpath) + 1 < PATH_MAX);
1472  if(tmp) free(tmp);
1473  return fullpath;
1474  }
1475  else
1476  {
1477  // Prepend the path
1478  // Note that dirname() modifies the contents, so
1479  // we need to make a copy of the filename.
1480  // There's no bounds-checking, but what the heck.
1481  char *tmp = strdup(this->filename.c_str());
1482  char* fullpath = new char[PATH_MAX];
1483  char* dummy = getcwd(fullpath, PATH_MAX);
1484  if (!dummy)
1485  {
1486  PRINT_ERR2("unable to get cwd %d: %s", errno, strerror(errno));
1487  if( fullpath ) delete[] fullpath;
1488  if( tmp ) free(tmp);
1489  return value;
1490  }
1491 
1492  strcat( fullpath, "/" );
1493  strcat( fullpath, dirname(tmp));
1494  strcat( fullpath, "/" );
1495  strcat( fullpath, filename );
1496  assert(strlen(fullpath) + 1 < PATH_MAX);
1497  free(tmp);
1498 
1499  return fullpath;
1500  }
1501 }
1502 
1503 
1505 // Read a series of floats from a tuple (experimental)
1506 int Worldfile::ReadTuple( const int entity, const char *name,
1507  const unsigned int first, const unsigned int count, const char* format, ... )
1508 {
1509  CProperty* property = GetProperty(entity, name);
1510  if (property == NULL )
1511  return 0;
1512 
1513  if( property->values.size() < first+count )
1514  {
1515  PRINT_ERR4( "Worldfile: reading tuple \"%s\" index %u to %u - tuple has length %u\n",
1516  name, first, first+count-1, (unsigned int)property->values.size() );
1517  exit(-1);
1518  }
1519 
1520  if( strlen(format) != count )
1521  {
1522  PRINT_ERR2( "format string length %u does not match argument count %u",
1523  (unsigned int)strlen(format), count );
1524  exit(-1);
1525  }
1526 
1527  va_list args;
1528  va_start( args, format );
1529 
1530  for( unsigned int i=0; i<count; i++ )
1531  {
1532  const char* val = GetPropertyValue(property, first+i);
1533 
1534  switch( format[i] )
1535  {
1536  case 'i': // signed integer
1537  *va_arg( args, int* ) = atoi(val);
1538  break;
1539 
1540  case 'u': // unsigned integer
1541  *va_arg( args, unsigned int* ) = (unsigned int)atoi(val);
1542  break;
1543 
1544  case 'f': // float
1545  *va_arg( args, double* ) = atof(val);
1546  break;
1547 
1548  case 'l': //length
1549  *va_arg( args, double* ) = atof(val) * unit_length;
1550  break;
1551 
1552  case 'a': // angle
1553  *va_arg( args, double* ) = atof(val) * unit_angle;
1554  break;
1555 
1556  case 's': // string
1557  *va_arg( args, char** ) = strdup(val);
1558  break;
1559 
1560  default:
1561  PRINT_ERR3( "Unknown format character %c in string %s loading %s",
1562  format[i], format, name );
1563  }
1564  }
1565 
1566  va_end( args );
1567 
1568  return count;
1569 }
1570 
1572 // Write a series of floats to a tuple (experimental)
1573 void Worldfile::WriteTuple( const int entity, const char *name,
1574  const unsigned int first, const unsigned int count, const char* format, ... )
1575 {
1576  CProperty* property = GetProperty(entity, name);
1577  if (property == NULL )
1578  return;
1579 
1580  if( property->values.size() < first+count )
1581  {
1582  PRINT_ERR4( "Worldfile: reading tuple \"%s\" index %d to %d - tuple has length %d\n",
1583  name, first, first+count-1, (int)property->values.size() );
1584  exit(-1);
1585  }
1586 
1587  if( strlen(format) != count )
1588  {
1589  PRINT_ERR2( "format string length %u does not match argument count %u",
1590  (unsigned int)strlen(format), count );
1591  exit(-1);
1592  }
1593 
1594  char buf[2048];
1595  buf[0] = 0;
1596 
1597  va_list args;
1598  va_start( args, format );
1599 
1600  for( unsigned int i=0; i<count; i++ )
1601  {
1602  switch( format[i] )
1603  {
1604  case 'i': // integer
1605  snprintf( buf, sizeof(buf), "%d", va_arg( args, int ) );
1606  break;
1607 
1608  case 'u': // unsigned integer
1609  snprintf( buf, sizeof(buf), "%u", va_arg( args, unsigned int ) );
1610  break;
1611 
1612  case 'f': // float
1613  snprintf( buf, sizeof(buf), "%.3f", va_arg( args, double ) );
1614  break;
1615 
1616  case 'l': //length
1617  snprintf( buf, sizeof(buf), "%.3f", va_arg( args, double ) / unit_length );
1618  break;
1619 
1620  case 'a': // angle
1621  snprintf( buf, sizeof(buf), "%.3f", va_arg( args, double ) / unit_angle );
1622  break;
1623 
1624  case 's': // string
1625  strncpy( buf, va_arg( args, char* ), sizeof(buf) );
1626  buf[sizeof(buf)-1] = 0; // force zero terminator
1627  break;
1628 
1629  default:
1630  PRINT_ERR3( "Unknown format character %c in string %s loading %s",
1631  format[i], format, name );
1632  exit(-1);
1633  }
1634 
1635  //printf( "writing %s %d %s\n", name, first+i, buf );
1636  SetPropertyValue(property, first+i, buf );
1637  }
1638 
1639  va_end( args );
1640 }
1641 
bool SetTokenValue(int index, const char *value)
Definition: worldfile.cc:698
std::string name
Name of property.
Definition: worldfile.hh:44
bool ParseTokenInclude(int *index, int *line)
Definition: worldfile.cc:795
std::map< std::string, CMacro > macros
Definition: worldfile.hh:327
void DumpTokens()
Definition: worldfile.cc:717
bool LoadTokenSpace(FILE *file, int *line, int include)
Definition: worldfile.cc:623
bool ParseTokenEntity(int entity, int *index, int *line)
Definition: worldfile.cc:939
void ClearProperties()
Definition: worldfile.cc:1257
int GetEntityParent(int entity)
Definition: worldfile.cc:1204
int LookupEntity(const char *type)
Definition: worldfile.cc:1225
double unit_angle
Definition: worldfile.hh:354
The Stage library uses its own namespace.
Definition: canvas.hh:8
const char * GetEntityType(int entity)
Definition: worldfile.cc:1214
bool LoadTokenWord(FILE *file, int *line, int include)
Definition: worldfile.cc:374
const char * basename(const char *filename)
Definition: basename.c:35
bool LoadTokenInclude(FILE *file, int *line, int include)
Definition: worldfile.cc:420
void DumpEntities()
Definition: worldfile.cc:1244
const std::string ReadString(int entity, const char *name, const std::string &value)
Definition: worldfile.cc:1376
CProperty * GetProperty(int entity, const char *name)
Definition: worldfile.cc:1298
const char * GetPropertyValue(CProperty *property, int index)
Definition: worldfile.cc:1345
const char * ReadFilename(int entity, const char *name, const char *value)
Definition: worldfile.cc:1450
#define PRINT_WARN3(m, a, b, c)
Definition: stage.hh:636
int ReadTuple(const int entity, const char *name, const unsigned int first, const unsigned int num, const char *format,...)
Definition: worldfile.cc:1506
bool ParseTokens()
Definition: worldfile.cc:740
bool LoadTokenString(FILE *file, int *line, int include)
Definition: worldfile.cc:586
void SetPropertyValue(CProperty *property, int index, const char *value)
Definition: worldfile.cc:1332
#define PARSE_ERR(z, l)
Definition: worldfile.cc:58
void DumpProperties()
Definition: worldfile.cc:1355
bool ParseTokenTuple(CProperty *property, int *index, int *line)
Definition: worldfile.cc:1069
void WriteInt(int entity, const char *name, int value)
Definition: worldfile.cc:1409
void WriteTuple(const int entity, const char *name, const unsigned int first, const unsigned int count, const char *format,...)
Definition: worldfile.cc:1573
std::string entityname
Definition: worldfile.hh:309
CMacro * LookupMacro(const char *macroname)
Definition: worldfile.cc:1124
bool Save(const std::string &filename)
Definition: worldfile.cc:193
char * dirname(char *path)
Definition: dirname.c:31
bool PropertyExists(int section, const char *token)
Definition: worldfile.cc:1324
void DumpMacros()
Definition: worldfile.cc:1137
std::string filename
Definition: worldfile.hh:350
void ClearMacros()
Definition: worldfile.cc:1106
int entity
Index of entity this property belongs to.
Definition: worldfile.hh:41
#define PRINT_ERR(m)
Definition: stage.hh:625
bool LoadTokenNum(FILE *file, int *line, int include)
Definition: worldfile.cc:550
void AddPropertyValue(CProperty *property, int index, int value_token)
Definition: worldfile.cc:1282
void WriteString(int entity, const char *name, const std::string &value)
Definition: worldfile.cc:1387
void WriteFloat(int entity, const char *name, double value)
Definition: worldfile.cc:1418
bool LoadTokens(FILE *file, int include)
Definition: worldfile.cc:244
std::string macroname
Definition: worldfile.hh:306
double unit_length
Definition: worldfile.hh:353
bool SaveTokens(FILE *file)
Definition: worldfile.cc:659
const char * GetTokenValue(int index)
Definition: worldfile.cc:708
bool WarnUnused()
Definition: worldfile.cc:224
std::vector< CToken > tokens
Definition: worldfile.hh:298
#define PRINT_ERR3(m, a, b, c)
Definition: stage.hh:628
#define isblank(a)
Definition: worldfile.cc:51
void ClearEntities()
Definition: worldfile.cc:1162
void PrintProp(const char *key, CProperty *prop)
Definition: worldfile.cc:1236
bool ParseTokenWord(int entity, int *index, int *line)
Definition: worldfile.cc:903
int GetEntityCount()
Definition: worldfile.cc:1196
#define TOKEN_ERR(z, l)
Definition: worldfile.cc:56
void AddMacro(const char *macroname, const char *entityname, int line, int starttoken, int endtoken)
Definition: worldfile.cc:1114
bool AddToken(int type, const char *value, int include)
Definition: worldfile.cc:689
#define PRINT_DEBUG1(m, a)
Definition: stage.hh:667
double ReadFloat(int entity, const char *name, double value)
Definition: worldfile.cc:1434
bool ParseTokenProperty(int entity, int *index, int *line)
Definition: worldfile.cc:1028
#define PRINT_ERR2(m, a, b)
Definition: stage.hh:627
int ReadInt(int entity, const char *name, int value)
Definition: worldfile.cc:1398
int AddEntity(int parent, const char *type)
Definition: worldfile.cc:1175
#define FOR_EACH(I, C)
Definition: stage.hh:616
#define PRINT_ERR4(m, a, b, c, d)
Definition: stage.hh:629
bool Load(const std::string &filename)
Definition: worldfile.cc:125
std::vector< CEntity > entities
Definition: worldfile.hh:344
CProperty * AddProperty(int entity, const char *name, int line)
Definition: worldfile.cc:1267
void ClearTokens()
Definition: worldfile.cc:681
FILE * FileOpen(const std::string &filename, const char *method)
Definition: worldfile.cc:86
Property class.
Definition: worldfile.hh:37
std::vector< int > values
A list of token indexes.
Definition: worldfile.hh:47
bool ParseTokenDefine(int *index, int *line)
Definition: worldfile.cc:826
bool LoadTokenComment(FILE *file, int *line, int include)
Definition: worldfile.cc:341
std::map< std::string, CProperty * > properties
Definition: worldfile.hh:347


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 Mon Jun 10 2019 15:06:10