From 8b92039b5b5e5bd184451a5ecae5475bf5034c47 Mon Sep 17 00:00:00 2001 From: arjen Date: Tue, 5 Aug 2003 08:15:00 +0000 Subject: [PATCH] Debug output to the log stream instead of cerr. Fixed namespace problems in XPath searches of the DOM. Moved string utility functions to a separate file. --- src/gcm_input/message.cpp | 251 ++++++++++++++++++++++------------------- src/gcm_input/string_utils.cpp | 115 +++++++++++++++++++ 2 files changed, 247 insertions(+), 119 deletions(-) create mode 100644 src/gcm_input/string_utils.cpp diff --git a/src/gcm_input/message.cpp b/src/gcm_input/message.cpp index 5951f21..363aaa2 100644 --- a/src/gcm_input/message.cpp +++ b/src/gcm_input/message.cpp @@ -8,7 +8,7 @@ *********************** ** FILE NAME : message.cpp ** SYSTEM NAME : Gnucomo - Gnu Computer Monitoring -** VERSION NUMBER : $Revision: 1.10 $ +** VERSION NUMBER : $Revision: 1.11 $ ** ** DESCRIPTION : Implementation of the message handling classes ** @@ -20,13 +20,18 @@ ******************************** ** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl ** CREATION DATE : Sep 16, 2002 -** LAST UPDATE : Apr 29, 2003 +** LAST UPDATE : Jul 24, 2003 ** MODIFICATIONS : **************************************************************************/ /***************************** $Log: message.cpp,v $ - Revision 1.10 2003-04-29 09:16:44 arjen + Revision 1.11 2003-08-05 08:15:00 arjen + Debug output to the log stream instead of cerr. + Fixed namespace problems in XPath searches of the DOM. + Moved string utility functions to a separate file. + + Revision 1.10 2003/04/29 09:16:44 arjen Read XML input, Only cooked log entries for now. @@ -65,7 +70,7 @@ *****************************/ -static const char *RCSID = "$Id: message.cpp,v 1.10 2003-04-29 09:16:44 arjen Exp $"; +static const char *RCSID = "$Id: message.cpp,v 1.11 2003-08-05 08:15:00 arjen Exp $"; #include #include @@ -74,10 +79,12 @@ static const char *RCSID = "$Id: message.cpp,v 1.10 2003-04-29 09:16:44 arjen Ex extern bool verbose; /* Defined in the main application */ extern bool testmode; +extern bool incremental; +extern std::ostream *log; /* Utility functions */ -String SQL_Escape(String s); +extern String SQL_Escape(String s); /*========================================================================= ** NAME : operator >> @@ -155,7 +162,7 @@ static const regex re_mail_Date("^Date:[[:blank:]]+" + mail_date_re); static const regex re_mail_MsId("^Message-Id:[[:blank:]]+"); static const regex re_email_address("[[:alnum:]_.-]+@[[:alnum:]_.-]+"); static const regex re_email_user("[[:alnum:]_.-]+@"); -static const regex re_xml_header("\?xml .*\?>$"); +static const regex re_xml_header("xml .*\?>$"); /*========================================================================= ** NAME : readXMLinput @@ -169,7 +176,7 @@ static const regex re_xml_header("\?xml .*\?>$"); ** VARS CHANGED : ** FUNCTIONS USED : ** SEE ALSO : -** LAST MODIFIED : Apr 28, 2003 +** LAST MODIFIED : Jul 24, 2003 **========================================================================= */ @@ -178,6 +185,7 @@ int client_message::readXMLinput(String first_line) xmlParserCtxtPtr ctxt; String line; xmlNodePtr root, item; + xmlNsPtr namespaces[1]; xmlXPathObjectPtr res; xmlXPathContextPtr pathcontext; @@ -193,29 +201,53 @@ int client_message::readXMLinput(String first_line) xmlFreeParserCtxt(ctxt); root = xmlDocGetRootElement(xmlDom); + namespaces[0] = root->ns; + //TODO Ought to check root->name and root->ns->href pathcontext = xmlXPathNewContext(xmlDom); pathcontext->node = xmlDocGetRootElement(xmlDom); + pathcontext->namespaces = namespaces; + pathcontext->nsNr = 1; - res = xmlXPathEval((const xmlChar *)"header/messagetype/text()", pathcontext); +#ifdef DEBUG + xmlDebugDumpNodeList(stdout, pathcontext->node, 0); +#endif + + res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:messagetype/text()", pathcontext); if (res->nodesetval != NULL) { +#ifdef DEBUG + xmlDebugDumpNodeList(stdout, *res->nodesetval->nodeTab, 0); +#endif item = *res->nodesetval->nodeTab; } - res = xmlXPathEval((const xmlChar *)"header/hostname/text()", pathcontext); + else + { + *log << "Message type not found in XML header.\n"; + } + + res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:hostname/text()", pathcontext); if (res->nodesetval != NULL) { +#ifdef DEBUG + xmlDebugDumpNodeList(stdout, *res->nodesetval->nodeTab, 0); +#endif item = *res->nodesetval->nodeTab; hostname = (const char *)item->content; } - res = xmlXPathEval((const xmlChar *)"header/service/text()", pathcontext); + else + { + *log << "Hostname not found in XML header.\n"; + } + + res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:service/text()", pathcontext); if (res->nodesetval != NULL) { item = *res->nodesetval->nodeTab; service = (const char *)item->content; } - res = xmlXPathEval((const xmlChar *)"header/time/text()", pathcontext); + res = xmlXPathEval((const xmlChar *)"gcmt:header/gcmt:time/text()", pathcontext); if (res->nodesetval != NULL) { item = *res->nodesetval->nodeTab; @@ -266,8 +298,9 @@ double client_message::classify(String host, UTC arriv, String serv) { from_address = line(re_email_address); from_address(re_email_user) = ""; // Remove the user part; - if (from_address != "") + if (from_address != "" && ~hostname < ~from_address) { + *log << "Detected hostname " << from_address << "\n"; hostname = from_address; } } @@ -275,9 +308,10 @@ double client_message::classify(String host, UTC arriv, String serv) { from_address = line(re_email_address); from_address(re_email_user) = ""; // Remove the user part; - if (from_address != "") + if (from_address != "" && ~hostname < ~from_address) { - hostname = from_address; + *log << "Detected hostname " << from_address << "\n"; + hostname = from_address; } } if (line == re_mail_Date) @@ -303,7 +337,7 @@ double client_message::classify(String host, UTC arriv, String serv) { if (verbose) { - std::cout << " testing: " << line << "\n"; + *log << " testing: " << line << "\n"; } if (line == re_xml_header) @@ -312,7 +346,7 @@ double client_message::classify(String host, UTC arriv, String serv) classification = XML; if (verbose) { - std::cout << "XML input detected.\n"; + *log << "XML input detected.\n"; } readXMLinput(line); } @@ -322,7 +356,7 @@ double client_message::classify(String host, UTC arriv, String serv) classification = SYSLOG; if (verbose) { - std::cout << "Syslog detected.\n"; + *log << "Syslog detected.\n"; } } else if (line == re_syslog_irix) @@ -331,21 +365,21 @@ double client_message::classify(String host, UTC arriv, String serv) classification = SYSLOG_IRIX; if (verbose) { - std::cout << "IRIX Syslog detected.\n"; + *log << "IRIX Syslog detected.\n"; } } else if (line == re_PGP) { certainty = 1.0; gpg_encrypted = true; - std::cerr << "The message is PGP/GnuPG encrypted.\n"; + *log << "The message is PGP/GnuPG encrypted.\n"; } else if (line == re_dump) { certainty = 1.0; if (verbose) { - std::cout << "DUMP output detected.\n"; + *log << "DUMP output detected.\n"; } } else if (line == re_accesslog) @@ -355,7 +389,7 @@ double client_message::classify(String host, UTC arriv, String serv) service = "httpd"; if (verbose) { - std::cout << "HTTP access log detected.\n"; + *log << "HTTP access log detected.\n"; } } else if (line == re_errorlog) @@ -365,7 +399,7 @@ double client_message::classify(String host, UTC arriv, String serv) service = "httpd"; if (verbose) { - std::cout << "HTTP error log detected.\n"; + *log << "HTTP error log detected.\n"; } } else if (line == re_rpm) @@ -375,7 +409,7 @@ double client_message::classify(String host, UTC arriv, String serv) service = ""; if (verbose) { - std::cout << "RPM package list detected.\n"; + *log << "RPM package list detected.\n"; } } } @@ -383,12 +417,12 @@ double client_message::classify(String host, UTC arriv, String serv) if (hostname == "") { - std::cerr << "Can not determine the hostname where the message came from.\n"; + *log << "Can not determine the hostname where the message came from.\n"; certainty = 0.0; } else if (!arrival.proper()) { - std::cerr << "Arrival time is not knwon.\n"; + *log << "Arrival time is not knwon.\n"; certainty = 0.0; } else @@ -412,7 +446,7 @@ double client_message::classify(String host, UTC arriv, String serv) ** VARS CHANGED : ** FUNCTIONS USED : ** SEE ALSO : -** LAST MODIFIED : Apr 29, 2003 +** LAST MODIFIED : Jul 24, 2003 **========================================================================= */ @@ -420,6 +454,7 @@ void client_message::enterXML() { xmlXPathObjectPtr res; xmlXPathContextPtr pathcontext; + xmlNsPtr namespaces[1]; /* Try to find the host in the database */ @@ -428,17 +463,21 @@ void client_message::enterXML() objectid = database.find_host(hostname); if (objectid == "") { - std::cerr << "Please define the host " << hostname << " in the database.\n"; + *log << "Please define the host " << hostname << " in the database.\n"; return; } if (verbose) { - std::cout << "Object id for " << hostname << " is " << objectid << "\n"; + *log << "Object id for " << hostname << " is " << objectid << "\n"; } pathcontext = xmlXPathNewContext(xmlDom); pathcontext->node = xmlDocGetRootElement(xmlDom); - res = xmlXPathEval((const xmlChar *)"data/node()", pathcontext); + namespaces[0] = pathcontext->node->ns; + pathcontext->namespaces = namespaces; + pathcontext->nsNr = 1; + + res = xmlXPathEval((const xmlChar *)"gcmt:data/node()", pathcontext); if (res->nodesetval != NULL) { @@ -460,7 +499,7 @@ void client_message::enterXML() { if (strcmp((char *)node->name, "raw") == 0) { - std::cerr << "Can not cook log elements yet.\n"; + *log << "Can not cook log elements yet.\n"; } else if (strcmp((char *)node->name, "cooked") == 0) { @@ -474,7 +513,7 @@ void client_message::enterXML() if (verbose) { - std::cout << "Analyzing cooked element.\n"; + *log << "Analyzing cooked element.\n"; } pathcontext->node = node; @@ -485,7 +524,7 @@ void client_message::enterXML() log_hostname = (const char *)item->content; if (log_hostname != hostname(0, ~log_hostname)) { - std::cerr << "Hostname " << log_hostname << " does not match.\n"; + *log << "Hostname " << log_hostname << " does not match.\n"; log_hostname = ""; } } @@ -513,7 +552,7 @@ void client_message::enterXML() } else { - std::cerr << " missing from cooked log element.\n"; + *log << " missing from cooked log element.\n"; } res = xmlXPathEval((const xmlChar *)"raw/text()", pathcontext); @@ -524,7 +563,7 @@ void client_message::enterXML() } else { - std::cerr << " missing from cooked log element.\n"; + *log << " missing from cooked log element.\n"; } if (raw != "" && log_hostname != "" && log_date.proper()) @@ -543,7 +582,7 @@ void client_message::enterXML() if (testmode) { - std::cout << insertion << "\n"; + *log << insertion << "\n"; } else { @@ -552,7 +591,7 @@ void client_message::enterXML() if (verbose) { - std::cout << "\n\n"; + *log << "\n\n"; } } } @@ -562,12 +601,12 @@ void client_message::enterXML() } else { - std::cerr << "Data element " << node->name << " is not supported.\n"; + *log << "Data element " << node->name << " is not supported.\n"; } } else { - std::cerr << "Data node not found.\n"; + *log << "Data node not found.\n"; } } @@ -583,7 +622,7 @@ void client_message::enterXML() ** VARS CHANGED : ** FUNCTIONS USED : ** SEE ALSO : -** LAST MODIFIED : Mar 28, 2003 +** LAST MODIFIED : Jul 24, 2003 **========================================================================= */ @@ -627,12 +666,12 @@ int client_message::enter() objectid = database.find_host(hostname); if (objectid == "") { - std::cerr << "Please define the host " << hostname << " in the database.\n"; + *log << "Please define the host " << hostname << " in the database.\n"; return 0; } if (verbose) { - std::cout << "Object id for " << hostname << " is " << objectid << "\n"; + *log << "Object id for " << hostname << " is " << objectid << "\n"; } if (classification == RPMLIST) @@ -648,12 +687,16 @@ int client_message::enter() n_packages = database.Query(qry); initial_entry = n_packages == 0; - std::cout << n_packages << " packages in database.\n"; +#ifdef DEBUG + *log << n_packages << " packages in database.\n"; +#endif for (int t = 0; t < n_packages; t++) { packages.push_back(database.Field(t, "name")); } - std::cout << "Package list built: " << packages.size() << ".\n"; +#ifdef DEBUG + *log << "Package list built: " << packages.size() << ".\n"; +#endif } /* Scan the input line by line, entring records into the database */ @@ -664,7 +707,7 @@ int client_message::enter() { if (verbose) { - std::cout << line << "\n"; + *log << line << "\n"; } @@ -704,8 +747,9 @@ int client_message::enter() switch (classification) { case SYSLOG: - log_date = line; - log_time = line; + datestring = line(0,16); + log_date = datestring; + log_time = datestring; if (log_date.Year() < 0 || log_date.Year() > 2500) { // The year is not in the log file. Assume the year of arrival, @@ -721,7 +765,7 @@ int client_message::enter() if (verbose) { - std::cout << " Log timestamp = " << log_date << " " << log_time << "\n"; + *log << " Log timestamp = " << log_date << " " << log_time << "\n"; } rest = line << 16; i = rest.index(' '); @@ -730,13 +774,13 @@ int client_message::enter() rest <<= i + 1; if (verbose) { - std::cout << " Hostname matches.\n"; - std::cout << " rest = " << rest << "\n"; + *log << " Hostname matches.\n"; + *log << " rest = " << rest << "\n"; } for (i = 0; isalpha(rest[i]) && i < ~rest; i++); if (verbose) { - std::cout << " Service name = " << rest(0,i) << "\n"; + *log << " Service name = " << rest(0,i) << "\n"; } /* Insert a new record into the log table */ @@ -750,7 +794,7 @@ int client_message::enter() if (testmode) { - std::cout << insertion << "\n"; + *log << insertion << "\n"; } else { @@ -759,20 +803,21 @@ int client_message::enter() if (verbose) { - std::cout << "\n\n"; + *log << "\n\n"; } nr_lines++; } else { - std::cerr << " Hostname " << rest(0,i) << " does not match.\n"; + *log << " Hostname " << rest(0,i) << " does not match.\n"; } break; case SYSLOG_IRIX: - log_date = line; - log_time = line; + datestring = line(0,16); + log_date = datestring; + log_time = datestring; if (log_date.Year() < 0 || log_date.Year() > 2500) { // The year is not in the log file. Assume the year of arrival, @@ -788,7 +833,7 @@ int client_message::enter() if (verbose) { - std::cout << " Log timestamp = " << log_date << " " << log_time << "\n"; + *log << " Log timestamp = " << log_date << " " << log_time << "\n"; } rest = line << 19; i = rest.index(' '); @@ -797,13 +842,13 @@ int client_message::enter() rest <<= i + 1; if (verbose) { - std::cout << " Hostname matches.\n"; - std::cout << " rest = " << rest << "\n"; + *log << " Hostname matches.\n"; + *log << " rest = " << rest << "\n"; } for (i = 0; isalpha(rest[i]) && i < ~rest; i++); if (verbose) { - std::cout << " Service name = " << rest(0,i) << "\n"; + *log << " Service name = " << rest(0,i) << "\n"; } /* Insert a new record into the log table */ @@ -817,7 +862,7 @@ int client_message::enter() if (testmode) { - std::cout << insertion << "\n"; + *log << insertion << "\n"; } else { @@ -826,14 +871,14 @@ int client_message::enter() if (verbose) { - std::cout << "\n\n"; + *log << "\n\n"; } nr_lines++; } else { - std::cerr << " Hostname " << rest(0,i) << " does not match.\n"; + *log << " Hostname " << rest(0,i) << " does not match.\n"; } break; @@ -846,7 +891,7 @@ int client_message::enter() log_time = datestring; if (verbose) { - std::cout << " Log timestamp = " << log_date << " " << log_time << "\n"; + *log << " Log timestamp = " << log_date << " " << log_time << "\n"; } insertion += "'" + objectid + "',"; insertion += "'" + service + "',"; @@ -857,7 +902,7 @@ int client_message::enter() if (testmode) { - std::cout << insertion << "\n"; + *log << insertion << "\n"; } else { @@ -866,7 +911,7 @@ int client_message::enter() if (verbose) { - std::cout << "\n\n"; + *log << "\n\n"; } nr_lines++; @@ -880,7 +925,7 @@ int client_message::enter() log_time = datestring; if (verbose) { - std::cout << " Log timestamp = " << log_date << " " << log_time << "\n"; + *log << " Log timestamp = " << log_date << " " << log_time << "\n"; } insertion += "'" + objectid + "',"; insertion += "'" + service + "',"; @@ -891,7 +936,7 @@ int client_message::enter() if (testmode) { - std::cout << insertion << "\n"; + *log << insertion << "\n"; } else { @@ -900,7 +945,7 @@ int client_message::enter() if (verbose) { - std::cout << "\n\n"; + *log << "\n\n"; } nr_lines++; @@ -951,8 +996,8 @@ int client_message::enter() if (verbose) { - std::cout << "Package is " << package; - std::cout << ", version is " << version << "\n"; + *log << "Package is " << package; + *log << ", version is " << version << "\n"; } // Construct a qry to check the package's existance @@ -972,7 +1017,7 @@ int client_message::enter() } else { - std::cerr << "Could NOT find " << package << " in list.\n"; + *log << "Could NOT find " << package << " in list.\n"; } paramid = database.Field(0, "paramid"); @@ -980,14 +1025,14 @@ int client_message::enter() qry += paramid + "' and name='version'"; if (database.Query(qry) == 0) { - std::cerr << "Database corruption: Package " << package; - std::cerr << " does not have a 'version' property.\n"; + *log << "Database corruption: Package " << package; + *log << " does not have a 'version' property.\n"; } else if (database.Field(0, "value") != version) { if (verbose) { - std::cout << " Parameter " << package << " has different version\n"; + *log << " Parameter " << package << " has different version\n"; } insertion = "update property set value='"; insertion += version + "' where paramid='"; @@ -1017,14 +1062,14 @@ int client_message::enter() } else { - std::cerr << "gcm_input ERROR: Cannot create 'property modified' notification.\n"; + *log << "gcm_input ERROR: Cannot create 'property modified' notification.\n"; } } else { if (verbose) { - std::cout << " Parameter " << package << " has not changed.\n"; + *log << " Parameter " << package << " has not changed.\n"; } } } @@ -1033,7 +1078,7 @@ int client_message::enter() if (verbose) { - std::cout << " Parameter " << package << " does not exist.\n"; + *log << " Parameter " << package << " does not exist.\n"; } // Create a new package parameter, including version property and history record @@ -1042,7 +1087,7 @@ int client_message::enter() if (testmode) { paramid = "0"; - std::cout << insertion << "\n"; + *log << insertion << "\n"; } else { @@ -1064,7 +1109,7 @@ int client_message::enter() if (testmode) { - std::cout << insertion << "\n" << insert_h << "\n"; + *log << insertion << "\n" << insert_h << "\n"; } else { @@ -1087,7 +1132,7 @@ int client_message::enter() } else { - std::cerr << "gcm_input ERROR: Cannot create 'parameter created' notification.\n"; + *log << "gcm_input ERROR: Cannot create 'parameter created' notification.\n"; } } } @@ -1095,7 +1140,7 @@ int client_message::enter() if (verbose) { - std::cout << "\n"; + *log << "\n"; } nr_lines++; @@ -1105,11 +1150,11 @@ int client_message::enter() } else { - std::cerr << "gcm_input WARNING: Not a valid line: " << line << "\n"; + *log << "gcm_input WARNING: Not a valid line: " << line << "\n"; } } - if (classification == RPMLIST) + if (classification == RPMLIST && !incremental) { std::list::iterator lp; String remove_notification(""); @@ -1138,13 +1183,13 @@ int client_message::enter() qry += paramid + "' order by modified desc"; if (database.Query(qry) <= 0) { - std::cerr << "Database ERROR: no history record for parameter " << *lp << ".\n"; + *log << "Database ERROR: no history record for parameter " << *lp << ".\n"; } else if (database.Field(0, "change_nature") != "REMOVED") { if (verbose) { - std::cout << "Removing parameter " << *lp << ".\n"; + *log << "Removing parameter " << *lp << ".\n"; } insert = "insert into history (paramid, modified, change_nature)"; @@ -1169,7 +1214,7 @@ int client_message::enter() } else { - std::cerr << "gcm_input ERROR: Cannot create 'parameter removed' notification.\n"; + *log << "gcm_input ERROR: Cannot create 'parameter removed' notification.\n"; } } } @@ -1178,39 +1223,7 @@ int client_message::enter() if (verbose) { - std::cout << nr_lines << " lines parsed from the log file.\n"; + *log << nr_lines << " lines parsed from the log file.\n"; } return nr_lines; } - -/*========================================================================= -** NAME : SQL_Escape -** SYNOPSIS : String SQL_Escape(String) -** PARAMETERS : -** RETURN VALUE : -** -** DESCRIPTION : Insert backslashes before single quotes. -** -** VARS USED : -** VARS CHANGED : -** FUNCTIONS USED : -** SEE ALSO : -** LAST MODIFIED : -**========================================================================= -*/ - -String SQL_Escape(String s) -{ - int i; - - for (i = 0; i < ~s; i++) - { - if (s[i] == '\'') - { - s(i,0) = "\\"; - i++; - } - } - - return s; -} diff --git a/src/gcm_input/string_utils.cpp b/src/gcm_input/string_utils.cpp new file mode 100644 index 0000000..56010a1 --- /dev/null +++ b/src/gcm_input/string_utils.cpp @@ -0,0 +1,115 @@ + +/************************************************************************** +** (c) Copyright 2003, Andromeda Technology & Automation +** This is free software; you can redistribute it and/or modify it under the +** terms of the GNU General Public License, see the file COPYING. +*************************************************************************** +** MODULE INFORMATION * +*********************** +** FILE NAME : string_utils.cpp +** SYSTEM NAME : Gnucomo - Gnu Computer Monitoring +** VERSION NUMBER : $Revision: 1.1 $ +** +** DESCRIPTION : Utility functions for Strings +** +** EXPORTED OBJECTS : +** LOCAL OBJECTS : +** MODULES USED : +*************************************************************************** +** ADMINISTRATIVE INFORMATION * +******************************** +** ORIGINAL AUTHOR : Arjen Baart - arjen@andromeda.nl +** CREATION DATE : Jul 31, 2003 +** LAST UPDATE : Jul 31, 2003 +** MODIFICATIONS : +**************************************************************************/ + +/***************************** + $Log: string_utils.cpp,v $ + Revision 1.1 2003-08-05 08:15:01 arjen + Debug output to the log stream instead of cerr. + Fixed namespace problems in XPath searches of the DOM. + Moved string utility functions to a separate file. + +*****************************/ + +static const char *RCSID = "$Id: string_utils.cpp,v 1.1 2003-08-05 08:15:01 arjen Exp $"; + +#include + +/*========================================================================= +** NAME : SQL_Escape +** SYNOPSIS : String SQL_Escape(String) +** PARAMETERS : +** RETURN VALUE : +** +** DESCRIPTION : Insert backslashes before single quotes. +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : +**========================================================================= +*/ + +String SQL_Escape(String s) +{ + int i; + + for (i = 0; i < ~s; i++) + { + if (s[i] == '\'') + { + s(i,0) = "\\"; + i++; + } + } + + return s; +} + +/*========================================================================= +** NAME : XML_Entities +** SYNOPSIS : String XML_Entities(String) +** PARAMETERS : +** RETURN VALUE : +** +** DESCRIPTION : Replace special characters for XML with their entity codes: +** "<" => "<" +** ">" => ">" +** "&" => "&" +** +** VARS USED : +** VARS CHANGED : +** FUNCTIONS USED : +** SEE ALSO : +** LAST MODIFIED : +**========================================================================= +*/ + +String XML_Entities(String s) +{ + int i; + + for (i = 0; i < ~s; i++) + { + switch (s[i]) + { + case '&': + s(i,1) = "&"; + i++; + break; + case '<': + s(i,1) = "<"; + i++; + break; + case '>': + s(i,1) = ">"; + i++; + break; + } + } + + return s; +} -- 2.11.0