26b25c38998e760fee7b3d22909a09e5c10e4321
4 #pragma warning ( disable : 4786 ) // identifier was truncated to '255' characters in the debug information
17 #define getcwd _getcwd
20 static const char* WS
= " \t\r\n";
21 static const char* WSEQ
= " =\t\r\n";
23 string working_directory
;
26 InitWorkingDirectory()
28 // store the current directory for path calculations
29 working_directory
.resize ( _MAX_PATH
);
30 working_directory
[0] = 0;
31 getcwd ( &working_directory
[0], working_directory
.size() );
32 working_directory
.resize ( strlen ( working_directory
.c_str() ) );
43 return _filelengthi64 ( _fileno(f
) );
45 struct stat64 file_stat
;
46 if ( fstat64(fileno(f
), &file_stat
) != 0 )
48 return file_stat
.st_size
;
54 string
s ( working_directory
);
55 const char* p
= strtok ( &s
[0], "/\\" );
60 p
= strtok ( NULL
, "/\\" );
64 Path::Path ( const Path
& cwd
, const string
& file
)
66 string
s ( cwd
.Fixup ( file
, false ) );
67 const char* p
= strtok ( &s
[0], "/\\" );
72 p
= strtok ( NULL
, "/\\" );
77 Path::Fixup ( const string
& file
, bool include_filename
) const
79 if ( strchr ( "/\\", file
[0] )
81 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
88 vector
<string
> pathtmp ( path
);
90 const char* prev
= strtok ( &tmp
[0], "/\\" );
91 const char* p
= strtok ( NULL
, "/\\" );
94 if ( !strcmp ( prev
, "." ) )
96 else if ( !strcmp ( prev
, ".." ) )
98 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
100 if ( pathtmp
.size() > 1 )
102 if ( pathtmp
.size() )
104 pathtmp
.resize ( pathtmp
.size() - 1 );
107 pathtmp
.push_back ( prev
);
109 p
= strtok ( NULL
, "/\\" );
111 if ( include_filename
)
112 pathtmp
.push_back ( prev
);
114 // reuse tmp variable to return recombined path
116 for ( size_t i
= 0; i
< pathtmp
.size(); i
++ )
118 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
130 Path::RelativeFromWorkingDirectory ( const string
& path
)
132 vector
<string
> vwork
, vpath
, vout
;
133 Path::Split ( vwork
, working_directory
, true );
134 Path::Split ( vpath
, path
, true );
136 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
137 // not possible to do relative across different drive letters
138 if ( vwork
[0] != vpath
[0] )
142 while ( i
< vwork
.size() && i
< vpath
.size() && vwork
[i
] == vpath
[i
] )
144 if ( i
< vwork
.size() )
146 // path goes above our working directory, we will need some ..'s
147 for ( size_t j
= 0; j
< i
; j
++ )
148 vout
.push_back ( ".." );
150 while ( i
< vpath
.size() )
151 vout
.push_back ( vpath
[i
++] );
153 // now merge vout into a string again
155 for ( i
= 0; i
< vout
.size(); i
++ )
157 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
169 Path::Split ( vector
<string
>& out
,
174 const char* prev
= strtok ( &s
[0], "/\\" );
175 const char* p
= strtok ( NULL
, "/\\" );
179 out
.push_back ( prev
);
181 p
= strtok ( NULL
, "/\\" );
184 out
.push_back ( prev
);
196 fclose ( _f
.back() );
204 XMLFile::open(const string
& filename
)
207 FILE* f
= fopen ( filename
.c_str(), "rb" );
210 unsigned long len
= (unsigned long)filelen(f
);
212 fread ( &_buf
[0], 1, len
, f
);
220 // next_token() moves the pointer to next token, which may be
221 // an xml element or a text element, basically it's a glorified
222 // skipspace, normally the user of this class won't need to call
225 XMLFile::next_token()
227 _p
+= strspn ( _p
, WS
);
231 XMLFile::next_is_text()
237 XMLFile::more_tokens()
242 // get_token() is used to return a token, and move the pointer
245 XMLFile::get_token(string
& token
)
248 if ( !strncmp ( _p
, "<!--", 4 ) )
250 tokend
= strstr ( _p
, "-->" );
256 else if ( *_p
== '<' )
258 tokend
= strchr ( _p
, '>' );
266 tokend
= strchr ( _p
, '<' );
269 while ( tokend
> _p
&& isspace(tokend
[-1]) )
274 token
= string ( _p
, tokend
-_p
);
280 XMLAttribute::XMLAttribute()
284 XMLAttribute::XMLAttribute(const string
& name_
,
285 const string
& value_
)
286 : name(name_
), value(value_
)
290 XMLElement::XMLElement()
291 : parentElement(NULL
)
295 XMLElement::~XMLElement()
298 for ( i
= 0; i
< attributes
.size(); i
++ )
299 delete attributes
[i
];
300 for ( i
= 0; i
< subElements
.size(); i
++ )
301 delete subElements
[i
];
305 XMLElement::AddSubElement ( XMLElement
* e
)
307 subElements
.push_back ( e
);
308 e
->parentElement
= this;
312 // This function takes a single xml tag ( i.e. beginning with '<' and
313 // ending with '>', and parses out it's tag name and constituent
315 // Return Value: returns true if you need to look for a </tag> for
316 // the one it just parsed...
318 XMLElement::Parse(const string
& token
,
321 const char* p
= token
.c_str();
322 assert ( *p
== '<' );
324 p
+= strspn ( p
, WS
);
326 // check if this is a comment
327 if ( !strncmp ( p
, "!--", 3 ) )
331 return false; // never look for end tag to a comment
334 end_tag
= ( *p
== '/' );
338 p
+= strspn ( p
, WS
);
340 const char* end
= strpbrk ( p
, WS
);
343 end
= strpbrk ( p
, "/>" );
346 name
= string ( p
, end
-p
);
348 p
+= strspn ( p
, WS
);
349 while ( *p
!= '>' && *p
!= '/' )
351 end
= strpbrk ( p
, WSEQ
);
354 end
= strpbrk ( p
, "/>" );
357 string
attribute ( p
, end
-p
), value
;
359 p
+= strspn ( p
, WS
);
363 p
+= strspn ( p
, WS
);
365 if ( strchr ( "\"'", *p
) )
368 end
= strchr ( p
, quote
);
372 end
= strpbrk ( p
, WS
);
376 end
= strchr ( p
, '>' );
378 if ( end
[-1] == '/' )
381 value
= string ( p
, end
-p
);
383 if ( quote
&& *p
== quote
)
385 p
+= strspn ( p
, WS
);
387 attributes
.push_back ( new XMLAttribute ( attribute
, value
) );
389 return !( *p
== '/' ) && !end_tag
;
393 XMLElement::GetAttribute ( const string
& attribute
,
396 // this would be faster with a tree-based container, but our attribute
397 // lists are likely to stay so short as to not be an issue.
398 for ( size_t i
= 0; i
< attributes
.size(); i
++ )
400 if ( attribute
== attributes
[i
]->name
)
401 return attributes
[i
];
405 printf ( "syntax error: attribute '%s' required for <%s>\n",
406 attribute
.c_str(), name
.c_str() );
412 XMLElement::GetAttribute ( const string
& attribute
,
413 bool required
) const
415 // this would be faster with a tree-based container, but our attribute
416 // lists are likely to stay so short as to not be an issue.
417 for ( size_t i
= 0; i
< attributes
.size(); i
++ )
419 if ( attribute
== attributes
[i
]->name
)
420 return attributes
[i
];
424 printf ( "syntax error: attribute '%s' required for <%s>\n",
425 attribute
.c_str(), name
.c_str() );
431 // This function reads a "token" from the file loaded in XMLFile
432 // REM TODO FIXME: At the moment it can't handle comments or non-xml tags.
433 // if it finds a tag that is non-singular, it parses sub-elements and/or
434 // inner text into the XMLElement that it is building to return.
435 // Return Value: an XMLElement allocated via the new operator that contains
436 // it's parsed data. Keep calling this function until it returns NULL
441 bool* pend_tag
/*= NULL*/)
444 if ( !f
.get_token(token
) )
448 while ( token
[0] != '<' )
450 printf ( "syntax error: expecting xml tag, not '%s'\n", token
.c_str() );
451 if ( !f
.get_token(token
) )
455 XMLElement
* e
= new XMLElement
;
456 bool bNeedEnd
= e
->Parse ( token
, end_tag
);
458 if ( e
->name
== "xi:include" )
461 att
= e
->GetAttribute("href",true);
464 string
file ( path
.Fixup(att
->value
,true) );
465 string
top_file ( Path::RelativeFromWorkingDirectory ( file
) );
466 e
->attributes
.push_back ( new XMLAttribute ( "top_href", top_file
) );
468 if ( !fInc
.open ( file
) )
469 printf ( "xi:include error, couldn't find file '%s'\n", file
.c_str() );
472 Path
path2 ( path
, att
->value
);
475 XMLElement
* e2
= XMLParse ( fInc
, path2
);
478 e
->AddSubElement ( e2
);
491 printf ( "syntax error: end tag '%s' not expected\n", token
.c_str() );
496 bool bThisMixingErrorReported
= false;
497 while ( f
.more_tokens() )
499 if ( f
.next_is_text() )
501 if ( !f
.get_token ( token
) || !token
.size() )
503 printf ( "internal tool error - get_token() failed when more_tokens() returned true\n" );
506 if ( e
->subElements
.size() && !bThisMixingErrorReported
)
508 printf ( "syntax error: mixing of inner text with sub elements\n" );
509 bThisMixingErrorReported
= true;
511 if ( e
->value
.size() )
513 printf ( "syntax error: multiple instances of inner text\n" );
514 e
->value
+= " " + token
;
521 XMLElement
* e2
= XMLParse ( f
, path
, &end_tag
);
524 if ( e
->name
!= e2
->name
)
525 printf ( "end tag name mismatch\n" );
529 if ( e
->value
.size() && !bThisMixingErrorReported
)
531 printf ( "syntax error: mixing of inner text with sub elements\n" );
532 bThisMixingErrorReported
= true;
534 e
->AddSubElement ( e2
);