]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/common/inout.c
Fix possible security vulnerability and fatal error
[xonotic/netradiant.git] / tools / quake2 / common / inout.c
index ef21edea165d9688fa3879473b99ef7fe174d661..b6d272ffdf694588b7c572b0f425cae2d5e87890 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-*/\r
-\r
-//-----------------------------------------------------------------------------\r
-//\r
-//\r
-// DESCRIPTION:\r
-// deal with in/out tasks, for either stdin/stdout or network/XML stream\r
-// \r
-\r
-#include "cmdlib.h"\r
-#include "mathlib.h"\r
-#include "polylib.h"\r
-#include "inout.h"\r
-#include <sys/types.h>\r
-#include <sys/stat.h>\r
-\r
-#ifdef _WIN32\r
-#include <direct.h>\r
-#include <windows.h>\r
-#endif\r
-\r
-// network broadcasting\r
-#include "l_net/l_net.h"\r
-#include "libxml/tree.h"\r
-\r
-#ifdef _WIN32\r
-HWND hwndOut = NULL;\r
-qboolean lookedForServer = false;\r
-UINT wm_BroadcastCommand = -1;\r
-#endif\r
-\r
-socket_t *brdcst_socket;\r
-netmessage_t msg;\r
-\r
-qboolean verbose = false;\r
-\r
-// our main document\r
-// is streamed through the network to Radiant\r
-// possibly written to disk at the end of the run\r
-//++timo FIXME: need to be global, required when creating nodes?\r
-xmlDocPtr doc;\r
-xmlNodePtr tree;\r
-\r
-// some useful stuff\r
-xmlNodePtr xml_NodeForVec( vec3_t v )\r
-{\r
-  xmlNodePtr ret;\r
-  char buf[1024];\r
-  \r
-  sprintf (buf, "%f %f %f", v[0], v[1], v[2]);\r
-  ret = xmlNewNode (NULL, "point");\r
-  xmlNodeSetContent (ret, buf);\r
-  return ret;\r
-}\r
-\r
-// send a node down the stream, add it to the document\r
-void xml_SendNode (xmlNodePtr node)\r
-{\r
-  xmlBufferPtr xml_buf;\r
-  char xmlbuf[MAX_NETMESSAGE]; // we have to copy content from the xmlBufferPtr into an aux buffer .. that sucks ..\r
-  // this index loops through the node buffer\r
-  int pos = 0;\r
-  int size;\r
-\r
-  xmlAddChild( doc->children, node );\r
-\r
-  if (brdcst_socket)\r
-  {\r
-    xml_buf = xmlBufferCreate();\r
-    xmlNodeDump( xml_buf, doc, node, 0, 0 );\r
-\r
-    // the XML node might be too big to fit in a single network message\r
-    // l_net library defines an upper limit of MAX_NETMESSAGE\r
-    // there are some size check errors, so we use MAX_NETMESSAGE-10 to be safe\r
-    // if the size of the buffer exceeds MAX_NETMESSAGE-10 we'll send in several network messages\r
-    while (pos < xml_buf->use)\r
-    {\r
-      // what size are we gonna send now?\r
-      (xml_buf->use - pos < MAX_NETMESSAGE - 10) ? (size = xml_buf->use - pos) : (size = MAX_NETMESSAGE - 10);\r
-      //++timo just a debug thing\r
-      if (size == MAX_NETMESSAGE - 10)\r
-        Sys_FPrintf (SYS_NOXML, "Got to split the buffer\n");\r
-      memcpy( xmlbuf, xml_buf->content+pos, size);\r
-      xmlbuf[size] = '\0';\r
-      NMSG_Clear( &msg );\r
-      NMSG_WriteString (&msg, xmlbuf );\r
-      Net_Send(brdcst_socket, &msg );\r
-      // now that the thing is sent prepare to loop again\r
-      pos += size;\r
-    }\r
-\r
-#if 0\r
-    // NOTE: the NMSG_WriteString is limited to MAX_NETMESSAGE\r
-    // we will need to split into chunks\r
-    // (we could also go lower level, in the end it's using send and receiv which are not size limited)\r
-    //++timo FIXME: MAX_NETMESSAGE is not exactly the max size we can stick in the message\r
-    //  there's some tweaking to do in l_net for that .. so let's give us a margin for now\r
-\r
-    //++timo we need to handle the case of a buffer too big to fit in a single message\r
-    // try without checks for now\r
-    if (xml_buf->use > MAX_NETMESSAGE-10 )\r
-    {\r
-      // if we send that we are probably gonna break the stream at the other end..\r
-      // and Error will call right there\r
-      //Error( "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use);\r
-      Sys_FPrintf (SYS_NOXML, "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use);\r
-      xml_buf->content[xml_buf->use]='\0'; //++timo this corrupts the buffer but we don't care it's for printing\r
-      Sys_FPrintf (SYS_NOXML, xml_buf->content);\r
-\r
-    }\r
-\r
-    size = xml_buf->use;\r
-    memcpy( xmlbuf, xml_buf->content, size );\r
-    xmlbuf[size] = '\0';\r
-    NMSG_Clear( &msg );\r
-    NMSG_WriteString (&msg, xmlbuf );\r
-    Net_Send(brdcst_socket, &msg );\r
-#endif\r
-\r
-    xmlBufferFree( xml_buf );\r
-  }  \r
-}\r
-\r
-void xml_Select (char *msg, int entitynum, int brushnum, qboolean bError)\r
-{\r
-  xmlNodePtr node, select;\r
-  char buf[1024];\r
-  char level[2];\r
-\r
-  // now build a proper "select" XML node\r
-  sprintf (buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg);\r
-  node = xmlNewNode (NULL, "select");\r
-  xmlNodeSetContent (node, buf);\r
-  level[0] = (int)'0' + (bError ? SYS_ERR : SYS_WRN)  ;\r
-  level[1] = 0;\r
-  xmlSetProp (node, "level", (char *)&level);\r
-  // a 'select' information\r
-  sprintf (buf, "%i %i", entitynum, brushnum);\r
-  select = xmlNewNode (NULL, "brush");\r
-  xmlNodeSetContent (select, buf);\r
-  xmlAddChild (node, select);\r
-  xml_SendNode (node);\r
-\r
-  sprintf (buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg);\r
-  if (bError)\r
-    Error(buf);\r
-  else\r
-    Sys_FPrintf (SYS_NOXML, "%s\n", buf);\r
-\r
-}\r
-\r
-void xml_Point (char *msg, vec3_t pt)\r
-{\r
-  xmlNodePtr node, point;\r
-  char buf[1024];\r
-  char level[2];\r
-\r
-  node = xmlNewNode (NULL, "pointmsg");\r
-  xmlNodeSetContent (node, msg);\r
-  level[0] = (int)'0' + SYS_ERR;\r
-  level[1] = 0;\r
-  xmlSetProp (node, "level", (char *)&level);\r
-  // a 'point' node\r
-  sprintf (buf, "%g %g %g", pt[0], pt[1], pt[2]);\r
-  point = xmlNewNode (NULL, "point");\r
-  xmlNodeSetContent (point, buf);\r
-  xmlAddChild (node, point);\r
-  xml_SendNode (node);\r
-\r
-  sprintf (buf, "%s (%g %g %g)", msg, pt[0], pt[1], pt[2]);\r
-  Error (buf);\r
-}\r
-\r
-#define WINDING_BUFSIZE 2048\r
-void xml_Winding (char *msg, vec3_t p[], int numpoints, qboolean die)\r
-{\r
-  xmlNodePtr node, winding;\r
-  char buf[WINDING_BUFSIZE];\r
-  char smlbuf[128];\r
-  char level[2];\r
-  int i;\r
-\r
-  node = xmlNewNode (NULL, "windingmsg");\r
-  xmlNodeSetContent (node, msg);\r
-  level[0] = (int)'0' + SYS_ERR;\r
-  level[1] = 0;\r
-  xmlSetProp (node, "level", (char *)&level);\r
-  // a 'winding' node\r
-  sprintf( buf, "%i ", numpoints);\r
-  for(i = 0; i < numpoints; i++)\r
-  {\r
-         sprintf (smlbuf, "(%g %g %g)", p[i][0], p[i][1], p[i][2]);\r
-    // don't overflow\r
-    if (strlen(buf)+strlen(smlbuf)>WINDING_BUFSIZE)\r
-      break;\r
-         strcat( buf, smlbuf);\r
-  }\r
-\r
-  winding = xmlNewNode (NULL, "winding");\r
-  xmlNodeSetContent (winding, buf);\r
-  xmlAddChild (node, winding);\r
-  xml_SendNode (node);\r
-\r
-  if(die)\r
-    Error (msg);\r
-  else\r
-  {\r
-    Sys_Printf(msg);\r
-    Sys_Printf("\n");\r
-  }\r
-}\r
-\r
-// in include\r
-#include "stream_version.h"\r
-\r
-void Broadcast_Setup( const char *dest )\r
-{\r
-       address_t address;\r
-  char sMsg[1024];\r
-\r
-       Net_Setup();\r
-       Net_StringToAddress((char *)dest, &address);\r
-  brdcst_socket = Net_Connect(&address, 0);\r
-  if (brdcst_socket)\r
-  {\r
-    // send in a header\r
-    sprintf (sMsg, "<?xml version=\"1.0\"?><q3map_feedback version=\"" Q3MAP_STREAM_VERSION "\">");\r
-    NMSG_Clear( &msg );\r
-    NMSG_WriteString(&msg, sMsg );\r
-    Net_Send(brdcst_socket, &msg );\r
-  }\r
-}\r
-\r
-void Broadcast_Shutdown()\r
-{\r
-  if (brdcst_socket)\r
-  {    \r
-    Sys_Printf("Disconnecting\n");\r
-    Net_Disconnect(brdcst_socket);\r
-    brdcst_socket = NULL;\r
-  }\r
-}\r
-\r
-// all output ends up through here\r
-void FPrintf (int flag, char *buf)\r
-{\r
-  xmlNodePtr node;\r
-  static qboolean bGotXML = false;\r
-  char level[2];\r
-\r
-  printf(buf);\r
-\r
-  // the following part is XML stuff only.. but maybe we don't want that message to go down the XML pipe?\r
-  if (flag == SYS_NOXML)\r
-    return;\r
-\r
-  // ouput an XML file of the run\r
-  // use the DOM interface to build a tree\r
-  /*\r
-  <message level='flag'>\r
-    message string\r
-    .. various nodes to describe corresponding geometry ..\r
-  </message>\r
-  */\r
-  if (!bGotXML)\r
-  {\r
-    // initialize\r
-    doc = xmlNewDoc("1.0");\r
-    doc->children = xmlNewDocRawNode(doc, NULL, "q3map_feedback", NULL);\r
-    bGotXML = true;\r
-  }\r
-  node = xmlNewNode (NULL, "message");\r
-  xmlNodeSetContent (node, buf);\r
-  level[0] = (int)'0' + flag;\r
-  level[1] = 0;\r
-  xmlSetProp (node, "level", (char *)&level );\r
-  \r
-  xml_SendNode (node);\r
-}\r
-\r
-#ifdef DBG_XML\r
-void DumpXML()\r
-{\r
-  xmlSaveFile( "XMLDump.xml", doc );\r
-}\r
-#endif\r
-\r
-void Sys_FPrintf (int flag, const char *format, ...)\r
-{\r
-  char out_buffer[4096];\r
-       va_list argptr;\r
-  \r
-  if ((flag == SYS_VRB) && (verbose == false))\r
-    return;\r
-\r
-  va_start (argptr, format);\r
-       vsprintf (out_buffer, format, argptr);\r
-       va_end (argptr);\r
-\r
-  FPrintf (flag, out_buffer);\r
-}\r
-\r
-void Sys_Printf (const char *format, ...)\r
-{\r
-  char out_buffer[4096];\r
-       va_list argptr;\r
-\r
-  va_start (argptr, format);\r
-       vsprintf (out_buffer, format, argptr);\r
-       va_end (argptr);\r
-  \r
-  FPrintf (SYS_STD, out_buffer);\r
-}\r
-\r
-/*\r
-=================\r
-Error\r
-\r
-For abnormal program terminations\r
-=================\r
-*/\r
-void Error( const char *error, ...)\r
-{\r
-  char out_buffer[4096];\r
-  char tmp[4096];\r
-       va_list argptr;\r
-\r
-       va_start (argptr,error);\r
-       vsprintf (tmp, error, argptr);\r
-       va_end (argptr);\r
-\r
-  sprintf( out_buffer, "************ ERROR ************\n%s\n", tmp );\r
-\r
-  FPrintf( SYS_ERR, out_buffer );\r
-\r
-#ifdef DBG_XML  \r
-  DumpXML();\r
-#endif\r
-\r
-  //++timo HACK ALERT .. if we shut down too fast the xml stream won't reach the listener.\r
-  // a clean solution is to send a sync request node in the stream and wait for an answer before exiting\r
-  Sys_Sleep( 1000 );\r
-  \r
-  Broadcast_Shutdown();\r
-\r
-       exit (1);\r
-}\r
-\r
+/*
+   Copyright (C) 1999-2007 id Software, Inc. and contributors.
+   For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+   This file is part of GtkRadiant.
+
+   GtkRadiant is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   GtkRadiant is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GtkRadiant; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+//-----------------------------------------------------------------------------
+//
+//
+// DESCRIPTION:
+// deal with in/out tasks, for either stdin/stdout or network/XML stream
+//
+
+#include "globaldefs.h"
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "polylib.h"
+#include "inout.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if GDEF_OS_WINDOWS
+#include <direct.h>
+#include <windows.h>
+#endif
+
+// network broadcasting
+#include "l_net/l_net.h"
+#include "libxml/tree.h"
+
+#if GDEF_OS_WINDOWS
+HWND hwndOut = NULL;
+qboolean lookedForServer = false;
+UINT wm_BroadcastCommand = -1;
+#endif
+
+socket_t *brdcst_socket;
+netmessage_t msg;
+
+qboolean verbose = false;
+
+// our main document
+// is streamed through the network to Radiant
+// possibly written to disk at the end of the run
+//++timo FIXME: need to be global, required when creating nodes?
+xmlDocPtr doc;
+xmlNodePtr tree;
+
+// some useful stuff
+xmlNodePtr xml_NodeForVec( vec3_t v ){
+       xmlNodePtr ret;
+       char buf[1024];
+
+       sprintf( buf, "%f %f %f", v[0], v[1], v[2] );
+       ret = xmlNewNode( NULL, "point" );
+       xmlNodeAddContent( ret, buf );
+       return ret;
+}
+
+// send a node down the stream, add it to the document
+void xml_SendNode( xmlNodePtr node ){
+       xmlBufferPtr xml_buf;
+       char xmlbuf[MAX_NETMESSAGE]; // we have to copy content from the xmlBufferPtr into an aux buffer .. that sucks ..
+       // this index loops through the node buffer
+       int pos = 0;
+       int size;
+
+       xmlAddChild( doc->children, node );
+
+       if ( brdcst_socket ) {
+               xml_buf = xmlBufferCreate();
+               xmlNodeDump( xml_buf, doc, node, 0, 0 );
+
+               // the XML node might be too big to fit in a single network message
+               // l_net library defines an upper limit of MAX_NETMESSAGE
+               // there are some size check errors, so we use MAX_NETMESSAGE-10 to be safe
+               // if the size of the buffer exceeds MAX_NETMESSAGE-10 we'll send in several network messages
+               while ( pos < xml_buf->use )
+               {
+                       // what size are we gonna send now?
+                       ( xml_buf->use - pos < MAX_NETMESSAGE - 10 ) ? ( size = xml_buf->use - pos ) : ( size = MAX_NETMESSAGE - 10 );
+                       //++timo just a debug thing
+                       if ( size == MAX_NETMESSAGE - 10 ) {
+                               Sys_FPrintf( SYS_NOXML, "Got to split the buffer\n" );
+                       }
+                       memcpy( xmlbuf, xml_buf->content + pos, size );
+                       xmlbuf[size] = '\0';
+                       NMSG_Clear( &msg );
+                       NMSG_WriteString( &msg, xmlbuf );
+                       Net_Send( brdcst_socket, &msg );
+                       // now that the thing is sent prepare to loop again
+                       pos += size;
+               }
+
+#if 0
+               // NOTE: the NMSG_WriteString is limited to MAX_NETMESSAGE
+               // we will need to split into chunks
+               // (we could also go lower level, in the end it's using send and receiv which are not size limited)
+               //++timo FIXME: MAX_NETMESSAGE is not exactly the max size we can stick in the message
+               //  there's some tweaking to do in l_net for that .. so let's give us a margin for now
+
+               //++timo we need to handle the case of a buffer too big to fit in a single message
+               // try without checks for now
+               if ( xml_buf->use > MAX_NETMESSAGE - 10 ) {
+                       // if we send that we are probably gonna break the stream at the other end..
+                       // and Error will call right there
+                       //Error( "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use);
+                       Sys_FPrintf( SYS_NOXML, "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use );
+                       xml_buf->content[xml_buf->use] = '\0'; //++timo this corrupts the buffer but we don't care it's for printing
+                       Sys_FPrintf( SYS_NOXML, xml_buf->content );
+
+               }
+
+               size = xml_buf->use;
+               memcpy( xmlbuf, xml_buf->content, size );
+               xmlbuf[size] = '\0';
+               NMSG_Clear( &msg );
+               NMSG_WriteString( &msg, xmlbuf );
+               Net_Send( brdcst_socket, &msg );
+#endif
+
+               xmlBufferFree( xml_buf );
+       }
+}
+
+void xml_Select( char *msg, int entitynum, int brushnum, qboolean bError ){
+       xmlNodePtr node, select;
+       char buf[1024];
+       char level[2];
+
+       // now build a proper "select" XML node
+       sprintf( buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg );
+       node = xmlNewNode( NULL, "select" );
+       xmlNodeAddContent( node, buf );
+       level[0] = (int)'0' + ( bError ? SYS_ERR : SYS_WRN )  ;
+       level[1] = 0;
+       xmlSetProp( node, "level", (char *)&level );
+       // a 'select' information
+       sprintf( buf, "%i %i", entitynum, brushnum );
+       select = xmlNewNode( NULL, "brush" );
+       xmlNodeAddContent( select, buf );
+       xmlAddChild( node, select );
+       xml_SendNode( node );
+
+       sprintf( buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg );
+       if ( bError ) {
+               Error( buf );
+       }
+       else{
+               Sys_FPrintf( SYS_NOXML, "%s\n", buf );
+       }
+
+}
+
+void xml_Point( char *msg, vec3_t pt ){
+       xmlNodePtr node, point;
+       char buf[1024];
+       char level[2];
+
+       node = xmlNewNode( NULL, "pointmsg" );
+       xmlNodeAddContent( node, msg );
+       level[0] = (int)'0' + SYS_ERR;
+       level[1] = 0;
+       xmlSetProp( node, "level", (char *)&level );
+       // a 'point' node
+       sprintf( buf, "%g %g %g", pt[0], pt[1], pt[2] );
+       point = xmlNewNode( NULL, "point" );
+       xmlNodeAddContent( point, buf );
+       xmlAddChild( node, point );
+       xml_SendNode( node );
+
+       sprintf( buf, "%s (%g %g %g)", msg, pt[0], pt[1], pt[2] );
+       Error( buf );
+}
+
+#define WINDING_BUFSIZE 2048
+void xml_Winding( char *msg, vec3_t p[], int numpoints, qboolean die ){
+       xmlNodePtr node, winding;
+       char buf[WINDING_BUFSIZE];
+       char smlbuf[128];
+       char level[2];
+       int i;
+
+       node = xmlNewNode( NULL, "windingmsg" );
+       xmlNodeAddContent( node, msg );
+       level[0] = (int)'0' + SYS_ERR;
+       level[1] = 0;
+       xmlSetProp( node, "level", (char *)&level );
+       // a 'winding' node
+       sprintf( buf, "%i ", numpoints );
+       for ( i = 0; i < numpoints; i++ )
+       {
+               sprintf( smlbuf, "(%g %g %g)", p[i][0], p[i][1], p[i][2] );
+               // don't overflow
+               if ( strlen( buf ) + strlen( smlbuf ) > WINDING_BUFSIZE ) {
+                       break;
+               }
+               strcat( buf, smlbuf );
+       }
+
+       winding = xmlNewNode( NULL, "winding" );
+       xmlNodeAddContent( winding, buf );
+       xmlAddChild( node, winding );
+       xml_SendNode( node );
+
+       if ( die ) {
+               Error( msg );
+       }
+       else
+       {
+               Sys_Printf( msg );
+               Sys_Printf( "\n" );
+       }
+}
+
+// in include
+#include "stream_version.h"
+
+void Broadcast_Setup( const char *dest ){
+       address_t address;
+       char sMsg[1024];
+
+       Net_Setup();
+       Net_StringToAddress( (char *)dest, &address );
+       brdcst_socket = Net_Connect( &address, 0 );
+       if ( brdcst_socket ) {
+               // send in a header
+               sprintf( sMsg, "<?xml version=\"1.0\"?><q3map_feedback version=\"" Q3MAP_STREAM_VERSION "\">" );
+               NMSG_Clear( &msg );
+               NMSG_WriteString( &msg, sMsg );
+               Net_Send( brdcst_socket, &msg );
+       }
+}
+
+void Broadcast_Shutdown(){
+       if ( brdcst_socket ) {
+               Sys_Printf( "Disconnecting\n" );
+               Net_Disconnect( brdcst_socket );
+               brdcst_socket = NULL;
+       }
+}
+
+// all output ends up through here
+void FPrintf( int flag, char *buf ){
+       xmlNodePtr node;
+       static qboolean bGotXML = false;
+       char level[2];
+
+       printf( "%s", buf );
+
+       // the following part is XML stuff only.. but maybe we don't want that message to go down the XML pipe?
+       if ( flag == SYS_NOXML ) {
+               return;
+       }
+
+       // ouput an XML file of the run
+       // use the DOM interface to build a tree
+       /*
+          <message level='flag'>
+          message string
+          .. various nodes to describe corresponding geometry ..
+          </message>
+        */
+       if ( !bGotXML ) {
+               // initialize
+               doc = xmlNewDoc( "1.0" );
+               doc->children = xmlNewDocRawNode( doc, NULL, "q3map_feedback", NULL );
+               bGotXML = true;
+       }
+       node = xmlNewNode( NULL, "message" );
+       xmlNodeAddContent( node, buf );
+       level[0] = (int)'0' + flag;
+       level[1] = 0;
+       xmlSetProp( node, "level", (char *)&level );
+
+       xml_SendNode( node );
+}
+
+#ifdef DBG_XML
+void DumpXML(){
+       xmlSaveFile( "XMLDump.xml", doc );
+}
+#endif
+
+void Sys_FPrintf( int flag, const char *format, ... ){
+       char out_buffer[4096];
+       va_list argptr;
+
+       if ( ( flag == SYS_VRB ) && ( verbose == false ) ) {
+               return;
+       }
+
+       va_start( argptr, format );
+       vsprintf( out_buffer, format, argptr );
+       va_end( argptr );
+
+       FPrintf( flag, out_buffer );
+}
+
+void Sys_Printf( const char *format, ... ){
+       char out_buffer[4096];
+       va_list argptr;
+
+       va_start( argptr, format );
+       vsprintf( out_buffer, format, argptr );
+       va_end( argptr );
+
+       FPrintf( SYS_STD, out_buffer );
+}
+
+/*
+   =================
+   Error
+
+   For abnormal program terminations
+   =================
+ */
+void Error( const char *error, ... ){
+       char out_buffer[4096];
+       char tmp[4096];
+       va_list argptr;
+
+       va_start( argptr,error );
+       vsprintf( tmp, error, argptr );
+       va_end( argptr );
+
+       sprintf( out_buffer, "************ ERROR ************\n%s\n", tmp );
+
+       FPrintf( SYS_ERR, out_buffer );
+
+#ifdef DBG_XML
+       DumpXML();
+#endif
+
+       //++timo HACK ALERT .. if we shut down too fast the xml stream won't reach the listener.
+       // a clean solution is to send a sync request node in the stream and wait for an answer before exiting
+       Sys_Sleep( 1000 );
+
+       Broadcast_Shutdown();
+
+       exit( 1 );
+}