4 #pragma warning ( disable : 4786 ) // identifier was truncated to '255' characters in the debug information
17 #define getcwd _getcwd
19 string working_directory
;
29 return _filelengthi64 ( _fileno(f
) );
31 struct stat64 file_stat
;
32 if ( fstat64(fileno(f
), &file_stat
) != 0 )
34 return file_stat
.st_size
;
38 static const char* WS
= " \t\r\n";
39 static const char* WSEQ
= " =\t\r\n";
43 string
s ( working_directory
);
44 const char* p
= strtok ( &s
[0], "/\\" );
49 p
= strtok ( NULL
, "/\\" );
53 Path::Path ( const Path
& cwd
, const string
& file
)
55 string
s ( cwd
.Fixup ( file
, false ) );
56 const char* p
= strtok ( &s
[0], "/\\" );
61 p
= strtok ( NULL
, "/\\" );
66 Path::Fixup ( const string
& file
, bool include_filename
) const
68 if ( strchr ( "/\\", file
[0] )
70 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
77 vector
<string
> pathtmp ( path
);
79 const char* prev
= strtok ( &tmp
[0], "/\\" );
80 const char* p
= strtok ( NULL
, "/\\" );
83 if ( !strcmp ( prev
, "." ) )
85 else if ( !strcmp ( prev
, ".." ) )
87 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
89 if ( pathtmp
.size() > 1 )
93 pathtmp
.resize ( pathtmp
.size() - 1 );
96 pathtmp
.push_back ( prev
);
98 p
= strtok ( NULL
, "/\\" );
100 if ( include_filename
)
101 pathtmp
.push_back ( prev
);
103 // reuse tmp variable to return recombined path
105 for ( size_t i
= 0; i
< pathtmp
.size(); i
++ )
107 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
119 Path::RelativeFromWorkingDirectory ( const string
& path
)
121 vector
<string
> vwork
, vpath
, vout
;
122 Path::Split ( vwork
, working_directory
, true );
123 Path::Split ( vpath
, path
, true );
125 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
126 // not possible to do relative across different drive letters
127 if ( vwork
[0] != vpath
[0] )
131 while ( i
< vwork
.size() && i
< vpath
.size() && vwork
[i
] == vpath
[i
] )
133 if ( i
< vwork
.size() )
135 // path goes above our working directory, we will need some ..'s
136 for ( int j
= 0; j
< i
; j
++ )
137 vout
.push_back ( ".." );
139 while ( i
< vpath
.size() )
140 vout
.push_back ( vpath
[i
++] );
142 // now merge vout into a string again
144 for ( i
= 0; i
< vout
.size(); i
++ )
146 // this squirreliness is b/c win32 has drive letters and *nix doesn't...
158 Path::Split ( vector
<string
>& out
,
163 const char* prev
= strtok ( &s
[0], "/\\" );
164 const char* p
= strtok ( NULL
, "/\\" );
168 out
.push_back ( prev
);
170 p
= strtok ( NULL
, "/\\" );
173 out
.push_back ( prev
);
185 fclose ( _f
.back() );
193 XMLFile::open(const string
& filename
)
196 FILE* f
= fopen ( filename
.c_str(), "rb" );
199 unsigned long len
= (unsigned long)filelen(f
);
201 fread ( &_buf
[0], 1, len
, f
);
209 // next_token() moves the pointer to next token, which may be
210 // an xml element or a text element, basically it's a glorified
211 // skipspace, normally the user of this class won't need to call
214 XMLFile::next_token()
216 _p
+= strspn ( _p
, WS
);
220 XMLFile::next_is_text()
226 XMLFile::more_tokens()
231 // get_token() is used to return a token, and move the pointer
234 XMLFile::get_token(string
& token
)
237 if ( !strncmp ( _p
, "<!--", 4 ) )
239 tokend
= strstr ( _p
, "-->" );
245 else if ( *_p
== '<' )
247 tokend
= strchr ( _p
, '>' );
255 tokend
= strchr ( _p
, '<' );
258 while ( tokend
> _p
&& isspace(tokend
[-1]) )
263 token
= string ( _p
, tokend
-_p
);
269 XMLAttribute::XMLAttribute()
273 XMLAttribute::XMLAttribute(const string
& name_
,
274 const string
& value_
)
275 : name(name_
), value(value_
)
279 XMLElement::XMLElement()
280 : parentElement(NULL
)
284 XMLElement::~XMLElement()
287 for ( i
= 0; i
< attributes
.size(); i
++ )
288 delete attributes
[i
];
289 for ( i
= 0; i
< subElements
.size(); i
++ )
290 delete subElements
[i
];
294 XMLElement::AddSubElement ( XMLElement
* e
)
296 subElements
.push_back ( e
);
297 e
->parentElement
= this;
301 // This function takes a single xml tag ( i.e. beginning with '<' and
302 // ending with '>', and parses out it's tag name and constituent
304 // Return Value: returns true if you need to look for a </tag> for
305 // the one it just parsed...
307 XMLElement::Parse(const string
& token
,
310 const char* p
= token
.c_str();
311 assert ( *p
== '<' );
313 p
+= strspn ( p
, WS
);
315 // check if this is a comment
316 if ( !strncmp ( p
, "!--", 3 ) )
320 return false; // never look for end tag to a comment
323 end_tag
= ( *p
== '/' );
327 p
+= strspn ( p
, WS
);
329 const char* end
= strpbrk ( p
, WS
);
332 end
= strpbrk ( p
, "/>" );
335 name
= string ( p
, end
-p
);
337 p
+= strspn ( p
, WS
);
338 while ( *p
!= '>' && *p
!= '/' )
340 end
= strpbrk ( p
, WSEQ
);
343 end
= strpbrk ( p
, "/>" );
346 string
attribute ( p
, end
-p
), value
;
348 p
+= strspn ( p
, WS
);
352 p
+= strspn ( p
, WS
);
354 if ( strchr ( "\"'", *p
) )
357 end
= strchr ( p
, quote
);
361 end
= strpbrk ( p
, WS
);
365 end
= strchr ( p
, '>' );
367 if ( end
[-1] == '/' )
370 value
= string ( p
, end
-p
);
372 if ( quote
&& *p
== quote
)
374 p
+= strspn ( p
, WS
);
376 attributes
.push_back ( new XMLAttribute ( attribute
, value
) );
378 return !( *p
== '/' ) && !end_tag
;
382 XMLElement::GetAttribute ( const string
& attribute
,
385 // this would be faster with a tree-based container, but our attribute
386 // lists are likely to stay so short as to not be an issue.
387 for ( int i
= 0; i
< attributes
.size(); i
++ )
389 if ( attribute
== attributes
[i
]->name
)
390 return attributes
[i
];
394 printf ( "syntax error: attribute '%s' required for <%s>\n",
395 attribute
.c_str(), name
.c_str() );
401 XMLElement::GetAttribute ( const string
& attribute
,
402 bool required
) const
404 // this would be faster with a tree-based container, but our attribute
405 // lists are likely to stay so short as to not be an issue.
406 for ( int i
= 0; i
< attributes
.size(); i
++ )
408 if ( attribute
== attributes
[i
]->name
)
409 return attributes
[i
];
413 printf ( "syntax error: attribute '%s' required for <%s>\n",
414 attribute
.c_str(), name
.c_str() );
420 // This function reads a "token" from the file loaded in XMLFile
421 // REM TODO FIXME: At the moment it can't handle comments or non-xml tags.
422 // if it finds a tag that is non-singular, it parses sub-elements and/or
423 // inner text into the XMLElement that it is building to return.
424 // Return Value: an XMLElement allocated via the new operator that contains
425 // it's parsed data. Keep calling this function until it returns NULL
430 bool* pend_tag
= NULL
)
433 if ( !f
.get_token(token
) )
437 while ( token
[0] != '<' )
439 printf ( "syntax error: expecting xml tag, not '%s'\n", token
.c_str() );
440 if ( !f
.get_token(token
) )
444 XMLElement
* e
= new XMLElement
;
445 bool bNeedEnd
= e
->Parse ( token
, end_tag
);
447 if ( e
->name
== "xi:include" )
450 att
= e
->GetAttribute("href",true);
453 string
file ( path
.Fixup(att
->value
,true) );
454 string
top_file ( Path::RelativeFromWorkingDirectory ( file
) );
455 e
->attributes
.push_back ( new XMLAttribute ( "top_href", top_file
) );
457 if ( !fInc
.open ( file
) )
458 printf ( "xi:include error, couldn't find file '%s'\n", file
.c_str() );
461 Path
path2 ( path
, att
->value
);
464 XMLElement
* e2
= XMLParse ( fInc
, path2
);
467 e
->AddSubElement ( e2
);
480 printf ( "syntax error: end tag '%s' not expected\n", token
.c_str() );
485 bool bThisMixingErrorReported
= false;
486 while ( f
.more_tokens() )
488 if ( f
.next_is_text() )
490 if ( !f
.get_token ( token
) || !token
.size() )
492 printf ( "internal tool error - get_token() failed when more_tokens() returned true\n" );
495 if ( e
->subElements
.size() && !bThisMixingErrorReported
)
497 printf ( "syntax error: mixing of inner text with sub elements\n" );
498 bThisMixingErrorReported
= true;
500 if ( e
->value
.size() )
502 printf ( "syntax error: multiple instances of inner text\n" );
503 e
->value
+= " " + token
;
510 XMLElement
* e2
= XMLParse ( f
, path
, &end_tag
);
513 if ( e
->name
!= e2
->name
)
514 printf ( "end tag name mismatch\n" );
518 if ( e
->value
.size() && !bThisMixingErrorReported
)
520 printf ( "syntax error: mixing of inner text with sub elements\n" );
521 bThisMixingErrorReported
= true;
523 e
->AddSubElement ( e2
);
531 for ( size_t i
= 0; i
< modules
.size(); i
++ )
536 Project::ProcessXML ( const XMLElement
& e
, const string
& path
)
538 const XMLAttribute
*att
;
539 string
subpath(path
);
540 if ( e
.name
== "project" )
542 att
= e
.GetAttribute ( "name", false );
548 else if ( e
.name
== "module" )
550 att
= e
.GetAttribute ( "name", true );
553 Module
* module
= new Module ( e
, att
->value
, path
);
554 modules
.push_back ( module
);
555 return; // REM TODO FIXME no processing of modules... yet
557 else if ( e
.name
== "directory" )
559 const XMLAttribute
* att
= e
.GetAttribute ( "name", true );
562 subpath
= path
+ "/" + att
->value
;
564 for ( size_t i
= 0; i
< e
.subElements
.size(); i
++ )
565 ProcessXML ( *e
.subElements
[i
], subpath
);
569 main ( int argc
, char** argv
)
571 // store the current directory for path calculations
572 working_directory
.resize ( _MAX_PATH
);
573 working_directory
[0] = 0;
574 getcwd ( &working_directory
[0], working_directory
.size() );
575 working_directory
.resize ( strlen ( working_directory
.c_str() ) );
579 string
xml_file ( "ReactOS.xml" );
580 if ( !f
.open ( xml_file
) )
582 printf ( "couldn't open ReactOS.xml!\n" );
586 vector
<string
> xml_dependencies
;
587 xml_dependencies
.push_back ( xml_file
);
590 XMLElement
* head
= XMLParse ( f
, path
);
592 break; // end of file
594 if ( head
->name
== "!--" )
595 continue; // ignore comments
597 if ( head
->name
!= "project" )
599 printf ( "error: expecting 'project', got '%s'\n", head
->name
.c_str() );
603 Project
* proj
= new Project
;
604 proj
->ProcessXML ( *head
, "." );
606 // REM TODO FIXME actually do something with Project object...
607 printf ( "Found %lu modules:\n", proj
->modules
.size() );
608 for ( size_t i
= 0; i
< proj
->modules
.size(); i
++ )
610 Module
& m
= *proj
->modules
[i
];
611 printf ( "\t%s in folder: %s\n",