]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/xml/xmltextags.cpp
gcc: appease the hardening warnings
[xonotic/netradiant.git] / libs / xml / xmltextags.cpp
1 /*
2    Copyright (C) 2006, Stefan Greven.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "xmltextags.h"
23
24 #include <string>
25 #include <cassert>
26
27 #include "qerplugin.h"
28 #include "stream/stringstream.h"
29
30 XmlTagBuilder::XmlTagBuilder(){
31 }
32
33 XmlTagBuilder::~XmlTagBuilder(){
34         // clean up
35         xmlFreeDoc( doc );
36         xmlXPathFreeContext( context );
37 }
38
39 bool XmlTagBuilder::CreateXmlDocument(){
40         /* Creates an XML file
41
42            returns TRUE if the file was created successfully or FALSE when failed
43          */
44
45         xmlTextWriterPtr writer;
46
47         writer = xmlNewTextWriterDoc( &doc, 0 );
48
49         // begin a new UTF-8 formatted xml document
50         xmlTextWriterStartDocument( writer, NULL, "UTF-8", NULL );
51
52         // create the root node with stock and custom elements
53         xmlTextWriterStartElement( writer, (xmlChar*)"root" );
54         xmlTextWriterWriteString( writer, (xmlChar*)"\n  " );
55         xmlTextWriterStartElement( writer, (xmlChar*)"stock" );
56         xmlTextWriterWriteString( writer, (xmlChar*)"\n  " );
57         xmlTextWriterEndElement( writer );
58         xmlTextWriterWriteString( writer, (xmlChar*)"\n  " );
59         xmlTextWriterStartElement( writer, (xmlChar*)"custom" );
60         xmlTextWriterWriteString( writer, (xmlChar*)"\n  " );
61         xmlTextWriterEndElement( writer );
62         xmlTextWriterWriteString( writer, (xmlChar*)"\n" );
63         xmlTextWriterEndElement( writer );
64
65         // end of the xml document
66         xmlTextWriterEndDocument( writer );
67         xmlFreeTextWriter( writer );
68
69         if ( !doc ) {
70                 return false;
71         }
72         else {
73                 context = xmlXPathNewContext( doc );
74                 return true;
75         }
76 }
77
78 bool XmlTagBuilder::OpenXmlDoc( const char* file, const char* savefile ){
79         /* Reads a XML document from a file
80
81            returns TRUE if the document was read successfully or FALSE when failed
82          */
83
84         if ( savefile ) {
85                 m_savefilename = savefile;
86         }
87         else{
88                 m_savefilename = file;
89         }
90
91         doc = xmlParseFile( file ); // TODO error checking!
92
93         if ( !doc ) {
94                 return false;
95         }
96         else {
97                 context = xmlXPathNewContext( doc );
98                 return true;
99         }
100 }
101
102 bool XmlTagBuilder::SaveXmlDoc( void ){
103         return SaveXmlDoc( m_savefilename.c_str() );
104 }
105
106 bool XmlTagBuilder::SaveXmlDoc( const char* file ){
107         /* Writes the XML document
108
109            returns TRUE if the document was saved successfully or FALSE when saving failed
110          */
111
112         xmlSaveNoEmptyTags = 1;
113
114         if ( xmlSaveFile( file, doc ) != -1 ) {
115                 return true;
116         }
117         return false;
118 }
119
120 bool XmlTagBuilder::AddShaderNode( const char* shader, TextureType textureType, NodeShaderType nodeShaderType ){
121         /* Adds a shader node
122
123            char* shader - the name of the shader or texture (without trailing .tga or something)
124
125            returns TRUE if the node was added successfully or FALSE when failed
126          */
127
128         xmlNodeSetPtr nodePtr = NULL;
129         xmlXPathObjectPtr xpathPtr = NULL;
130
131         switch ( textureType )
132         {
133         case STOCK:
134                 xpathPtr = XpathEval( "/root/stock" );
135                 break;
136         case CUSTOM:
137                 xpathPtr = XpathEval( "/root/custom" );
138         };
139
140         if ( xpathPtr ) {
141                 nodePtr = xpathPtr->nodesetval;
142         }
143         else{
144                 return false;
145         }
146
147         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
148                 xmlNodePtr newnode, newtext;
149                 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
150
151                 // create a new node and set the node attribute (shader path)
152                 switch ( nodeShaderType )
153                 {
154                 case SHADER:
155                         newnode = xmlNewNode( NULL, (xmlChar*)"shader" );
156                         break;
157                 case TEXTURE:
158                         newnode = xmlNewNode( NULL, (xmlChar*)"texture" );
159                         break;
160                 default:
161                         assert(false);
162                         break;
163                 };
164
165                 newnode = xmlDocCopyNode( newnode, doc, 1 );
166                 xmlSetProp( newnode, (xmlChar*)"path", (xmlChar*)shader );
167                 xmlNodeSetContent( newnode, (xmlChar*)"\n    " );
168
169                 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // there are no shaders yet
170                         // add spaces
171                         newtext = xmlNewText( (xmlChar*)"  " );
172                         xmlAddChild( nodeParent->children, newtext );
173
174                         // add the new node
175                         xmlAddNextSibling( nodeParent->children, newnode );
176
177                         // append a new line
178                         newtext = xmlNewText( (xmlChar*)"\n  " );
179                         xmlAddNextSibling( nodeParent->children->next, newtext );
180                 }
181                 else {
182                         // add the node
183                         xmlAddNextSibling( nodeParent->children, newnode );
184
185                         // append a new line and spaces
186                         newtext = xmlNewText( (xmlChar*)"\n    " );
187                         xmlAddNextSibling( nodeParent->children->next, newtext );
188                 }
189
190                 xmlXPathFreeObject( xpathPtr );
191                 return true;
192         }
193         else {
194                 xmlXPathFreeObject( xpathPtr );
195                 return false;
196         }
197 }
198
199 bool XmlTagBuilder::DeleteShaderNode( const char* shader ){
200         /* Deletes a shader node
201
202            char* shader - the name of the shader or texture (without trailing .tga or something)
203
204            returns TRUE if the node was deleted successfully or FALSE when failed
205          */
206
207         char buffer[256];
208         char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
209         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
210
211         xmlNodeSetPtr nodePtr;
212         if ( xpathPtr ) {
213                 nodePtr = xpathPtr->nodesetval;
214         }
215         else{
216                 return false;
217         }
218
219         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
220                 xmlNodePtr ptrContent = nodePtr->nodeTab[0];
221                 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[0]->prev;
222
223                 // delete the node
224                 xmlUnlinkNode( ptrContent );
225                 xmlFreeNode( ptrContent );
226
227                 // delete leading whitespace node
228                 xmlUnlinkNode( ptrWhitespace );
229                 xmlFreeNode( ptrWhitespace );
230                 xmlXPathFreeObject( xpathPtr );
231                 return true;
232         }
233         xmlXPathFreeObject( xpathPtr );
234         return false;
235 }
236
237 bool XmlTagBuilder::CheckShaderTag( const char* shader ){
238         /* Checks whether there exists an entry for a shader/texture with at least one tag
239
240            char* shader  - the name of the shader or texture (without trailing .tga or something)
241
242            returns TRUE if the shader is already stored in the XML tag file and has at least one tag
243          */
244
245         // build the XPath expression to search for
246         char buffer[256];
247         strcpy( buffer, "/root/*/*[@path='" );
248         strcat( buffer, shader );
249         strcat( buffer, "']" );
250
251         char* expression = buffer;
252
253         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
254         xmlNodeSetPtr nodePtr;
255         if ( xpathPtr ) {
256                 nodePtr = xpathPtr->nodesetval;
257         }
258         else{
259                 return false;
260         }
261
262         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
263                 xmlXPathFreeObject( xpathPtr );
264                 return true;
265         }
266         else {
267                 xmlXPathFreeObject( xpathPtr );
268                 return false;
269         }
270 }
271
272 bool XmlTagBuilder::CheckShaderTag( const char* shader, const char* content ){
273         /* Checks whether a tag with content already exists
274
275            char* shader  - the name of the shader or texture (without trailing .tga or something)
276            char* content - the node content (a tag name)
277
278            returns TRUE if the tag with content already exists or FALSE if not
279          */
280
281         // build the XPath expression to search for
282         // example expression: "/stock/*[@path='textures/alpha/barb_wire'][child::tag='Alpha']";
283
284         char buffer[256];
285         strcpy( buffer, "/root/*/*[@path='" );
286         strcat( buffer, shader );
287         strcat( buffer, "'][child::tag='" );
288         strcat( buffer, content );
289         strcat( buffer, "']" );
290
291         char* expression = buffer;
292
293         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
294         xmlNodeSetPtr nodePtr;
295         if ( xpathPtr ) {
296                 nodePtr = xpathPtr->nodesetval;
297         }
298         else{
299                 return false;
300         }
301
302         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
303                 xmlXPathFreeObject( xpathPtr );
304                 return true;
305         }
306         else {
307                 xmlXPathFreeObject( xpathPtr );
308                 return false;
309         }
310 }
311
312 bool XmlTagBuilder::AddShaderTag( const char* shader, const char* content, NodeTagType nodeTagType ){
313         /* Adds a tag node to an existing shader/texture node if there's no tag with the same content yet
314
315            char* shader  - the name of the shader or texture (without trailing .tga or something)
316            char* content - the node content (a tag name)
317
318            returns TRUE if the node was added successfully or FALSE when failed
319          */
320
321         // build the XPath expression
322         char buffer[256];
323         char* expression = GetTagsXpathExpression( buffer, shader, EMPTY );
324
325         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
326         xmlNodeSetPtr nodePtr;
327         if ( xpathPtr ) {
328                 nodePtr = xpathPtr->nodesetval;
329         }
330         else{
331                 return false;
332         }
333
334         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) { // node was found
335                 xmlNodePtr newnode = xmlNewNode( NULL, (xmlChar*)"tag" );
336                 xmlNodePtr nodeParent = nodePtr->nodeTab[0];
337                 newnode = xmlDocCopyNode( newnode, doc, 1 );
338                 xmlNodeSetContent( newnode, (xmlChar*)content );
339
340                 if ( nodePtr->nodeTab[0]->children->next == NULL ) { // shader node has NO children
341                         // add spaces
342                         xmlNodePtr newtext = xmlNewText( (xmlChar*)"  " );
343                         xmlAddChild( nodeParent->children, newtext );
344
345                         // add new node
346                         xmlAddNextSibling( nodeParent->children, newnode );
347
348                         // append a new line + spaces
349                         newtext = xmlNewText( (xmlChar*)"\n    " );
350                         xmlAddNextSibling( nodeParent->children->next, newtext );
351                 }
352                 else { // shader node has children already - the new node will be the first sibling
353                         xmlAddNextSibling( nodeParent->children, newnode );
354                         xmlNodePtr newtext = xmlNewText( (xmlChar*)"\n      " );
355                         xmlAddNextSibling( nodeParent->children->next, newtext );
356                 }
357                 xmlXPathFreeObject( xpathPtr );
358                 return true;
359         }
360         else {
361                 xmlXPathFreeObject( xpathPtr );
362                 return false;
363         }
364 }
365
366 //int XmlTagBuilder::RenameShaderTag(const char* oldtag, const char* newtag)
367 int XmlTagBuilder::RenameShaderTag( const char* oldtag, CopiedString newtag ){
368         /* Replaces tag node contents
369
370            char* oldtag - the <tag></tag> node content that sould be changed
371            char* newtag - the new <tag></tag> node content
372
373            returns the number of renamed shaders
374          */
375
376         int num = 0;
377
378         // build the XPath expression
379         char expression[256];
380         strcpy( expression, "/root/*/*[child::tag='" );
381         strcat( expression, oldtag );
382         strcat( expression, "']/*" );
383
384         xmlXPathObjectPtr result = xmlXPathEvalExpression( (xmlChar*)expression, context );
385         if ( !result ) {
386                 return 0;
387         }
388         xmlNodeSetPtr nodePtr = result->nodesetval;
389
390         for ( int i = 0; i < nodePtr->nodeNr; i++ )
391         {
392                 xmlNodePtr ptrContent = nodePtr->nodeTab[i];
393                 char* content = (char*)xmlNodeGetContent( ptrContent );
394
395                 if ( strcmp( content, oldtag ) == 0 ) { // found a node with old content?
396                         xmlNodeSetContent( ptrContent, (xmlChar*)newtag.c_str() );
397                         num++;
398                 }
399         }
400
401         SaveXmlDoc();
402         xmlXPathFreeObject( result ); // CHANGED
403         return num;
404 }
405
406 bool XmlTagBuilder::DeleteShaderTag( const char* shader, const char* tag ){
407         /* Deletes a child node of a shader
408
409            char* shader  - the name of the shader or texture (without trailing .tga or something)
410            char* tag     - the tag being deleted
411
412            returns TRUE if the node was deleted successfully or FALSE when failed
413          */
414
415         char buffer[256];
416         char* expression = GetTagsXpathExpression( buffer, shader, TAG );
417         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
418         xmlNodeSetPtr nodePtr;
419         if ( xpathPtr ) {
420                 nodePtr = xpathPtr->nodesetval;
421         }
422         else{
423                 return false;
424         }
425
426         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
427                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
428                 {
429                         xmlNodePtr ptrContent = nodePtr->nodeTab[i];
430                         char* content = (char*)(xmlChar*)xmlNodeGetContent( ptrContent );
431
432                         if ( strcmp( content, tag ) == 0 ) { // find the node
433                                 xmlNodePtr ptrWhitespace = nodePtr->nodeTab[i]->prev;
434                                 // delete the node
435                                 xmlUnlinkNode( ptrContent );
436                                 xmlFreeNode( ptrContent );
437
438                                 // delete leading whitespace node
439                                 xmlUnlinkNode( ptrWhitespace );
440                                 xmlFreeNode( ptrWhitespace );
441                                 xmlXPathFreeObject( xpathPtr );
442                                 return true;
443                         }
444                 }
445         }
446         xmlXPathFreeObject( xpathPtr );
447         return false;
448 }
449
450 bool XmlTagBuilder::DeleteTag( const char* tag ){
451         /* Deletes a tag from all shaders
452
453            char* tag - the tag being deleted from all shaders
454
455            returns TRUE if the tag was deleted successfully or FALSE when failed
456          */
457
458         char expression[256];
459         strcpy( expression, "/root/*/*[child::tag='" );
460         strcat( expression, tag );
461         strcat( expression, "']" );
462
463         std::set<CopiedString> dellist;
464         TagSearch( expression, dellist );
465         std::set<CopiedString>::iterator iter;
466
467         for ( iter = dellist.begin(); iter != dellist.end(); iter++ )
468         {
469                 DeleteShaderTag( iter->c_str(), tag );
470         }
471         SaveXmlDoc();
472
473         return true;
474 }
475
476 void XmlTagBuilder::GetShaderTags( const char* shader, std::vector<CopiedString>& tags ){
477         /* Gets the tags from a shader
478
479            char* shader - the name of the shader
480
481            returns a vector containing the tags
482          */
483
484         char const *expression;
485
486         if ( shader == NULL ) { // get all tags from all shaders
487                 expression = "/root/*/*/tag";
488         }
489         else {
490                 char buffer[256];
491                 expression = GetTagsXpathExpression( buffer, shader, TAG );
492         }
493
494         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
495         xmlNodeSetPtr nodePtr;
496         if ( xpathPtr ) {
497                 nodePtr = xpathPtr->nodesetval;
498         }
499         else{
500                 return;
501         }
502
503         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
504                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
505                 {
506                         tags.push_back( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
507                 }
508         }
509         xmlXPathFreeObject( xpathPtr );
510 }
511
512 void XmlTagBuilder::GetUntagged( std::set<CopiedString>& shaders ){
513         /* Gets all textures and shaders listed in the xml file that don't have any tag
514
515            returns a set containing the shaders (with path)
516          */
517
518         char const *expression = "/root/*/*[not(child::tag)]";
519
520         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
521         xmlNodeSetPtr nodePtr;
522         if ( xpathPtr ) {
523                 nodePtr = xpathPtr->nodesetval;
524         }
525         else{
526                 return;
527         }
528
529         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
530                 xmlNodePtr ptr;
531
532                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
533                 {
534                         ptr = nodePtr->nodeTab[i];
535                         shaders.insert( (char*)xmlGetProp( ptr, (xmlChar*)"path" ) );
536                 }
537         }
538
539         xmlXPathFreeObject( xpathPtr );
540 }
541
542 void XmlTagBuilder::GetAllTags( std::set<CopiedString>& tags ){
543         /* Gets a list of all tags that are used (assigned to any shader)
544
545            returns a set containing all used tags
546          */
547
548         char const *expression = "/root/*/*/tag";
549
550         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
551         xmlNodeSetPtr nodePtr;
552         if ( xpathPtr ) {
553                 nodePtr = xpathPtr->nodesetval;
554         }
555         else{
556                 return;
557         }
558
559         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
560                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
561                 {
562                         tags.insert( (CopiedString)(char*)xmlNodeGetContent( nodePtr->nodeTab[i] ) );
563                 }
564         }
565
566         xmlXPathFreeObject( xpathPtr );
567 }
568
569 void XmlTagBuilder::TagSearch( const char* expression, std::set<CopiedString>& paths ){
570         /* Searches shaders by tags
571
572            char* expression - the XPath expression to search
573
574            returns a set containing the found shaders
575          */
576
577         xmlXPathObjectPtr xpathPtr = XpathEval( expression );
578         xmlNodeSetPtr nodePtr;
579         if ( xpathPtr ) {
580                 nodePtr = xpathPtr->nodesetval;
581         }
582         else{
583                 return;
584         }
585
586         if ( !xmlXPathNodeSetIsEmpty( nodePtr ) ) {
587                 xmlNodePtr ptr;
588                 xmlChar* xmlattrib;
589                 for ( int i = 0; i < nodePtr->nodeNr; i++ )
590                 {
591                         ptr = nodePtr->nodeTab[i];
592                         xmlattrib = xmlGetProp( ptr, (xmlChar*)"path" );
593                         paths.insert( (CopiedString)(char*)xmlattrib );
594                 }
595         }
596         xmlXPathFreeObject( xpathPtr );
597 }