diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 4472a9a07..24a13d36e 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -228,7 +228,7 @@ bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNo bool LLUICtrlFactory::getLayeredXMLNodeFromBuffer(const std::string &buffer, LLXMLNodePtr& root) { - if (!LLXMLNode::parseBuffer(buffer.data(), buffer.size(), root, 0)) { + if (!LLXMLNode::parseBuffer((U8*)buffer.data(), buffer.size(), root, 0)) { llwarns << "Error reading UI description from buffer." << llendl; return false; } diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index db9caf8c0..b35585527 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -64,6 +64,7 @@ LLXMLNode::LLXMLNode() : mPrecision(64), mType(TYPE_CONTAINER), mEncoding(ENCODING_DEFAULT), + mLineNumber(-1), mParent(NULL), mChildren(NULL), mAttributes(), @@ -85,6 +86,7 @@ LLXMLNode::LLXMLNode(const char* name, BOOL is_attribute) : mPrecision(64), mType(TYPE_CONTAINER), mEncoding(ENCODING_DEFAULT), + mLineNumber(-1), mParent(NULL), mChildren(NULL), mAttributes(), @@ -106,6 +108,7 @@ LLXMLNode::LLXMLNode(LLStringTableEntry* name, BOOL is_attribute) : mPrecision(64), mType(TYPE_CONTAINER), mEncoding(ENCODING_DEFAULT), + mLineNumber(-1), mParent(NULL), mChildren(NULL), mAttributes(), @@ -127,6 +130,8 @@ LLXMLNode::LLXMLNode(const LLXMLNode& rhs) : mPrecision(rhs.mPrecision), mType(rhs.mType), mEncoding(rhs.mEncoding), + mLineNumber(0), + mParser(NULL), mParent(NULL), mChildren(NULL), mAttributes(), @@ -226,6 +231,10 @@ BOOL LLXMLNode::removeChild(LLXMLNode *target_child) { mChildren->head = target_child->mNext; } + if (target_child == mChildren->tail) + { + mChildren->tail = target_child->mPrev; + } LLXMLNodePtr prev = target_child->mPrev; LLXMLNodePtr next = target_child->mNext; @@ -294,6 +303,22 @@ void LLXMLNode::addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child) mChildren->tail = new_child; } } + // if after_child == parent, then put new_child at beginning + else if (after_child == this) + { + // add to front of list + new_child->mNext = mChildren->head; + if (mChildren->head) + { + mChildren->head->mPrev = new_child; + mChildren->head = new_child; + } + else // no children + { + mChildren->head = new_child; + mChildren->tail = new_child; + } + } else { if (after_child->mNext.notNull()) @@ -401,7 +426,8 @@ void XMLCALL StartXMLNode(void *userData, } new_node_ptr->mParser = parent->mParser; - + new_node_ptr->setLineNumber(XML_GetCurrentLineNumber(*new_node_ptr->mParser)); + // Set the current active node to the new node XML_Parser *parser = parent->mParser; XML_SetUserData(*parser, (void *)new_node_ptr); @@ -492,6 +518,7 @@ void XMLCALL StartXMLNode(void *userData, if (!new_node->getAttribute(attr_name.c_str(), attr_node, FALSE)) { attr_node = new LLXMLNode(attr_name.c_str(), TRUE); + attr_node->setLineNumber(XML_GetCurrentLineNumber(*new_node_ptr->mParser)); } attr_node->setValue(attr_value); new_node->addChild(attr_node); @@ -608,13 +635,14 @@ bool LLXMLNode::updateNode( } //update all of node's children with updateNodes children that match name - LLXMLNodePtr child; + LLXMLNodePtr child = node->getFirstChild(); + LLXMLNodePtr last_child = child; LLXMLNodePtr updateChild; for (updateChild = update_node->getFirstChild(); updateChild.notNull(); updateChild = updateChild->getNextSibling()) { - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + while(child.notNull()) { std::string nodeName; std::string updateName; @@ -633,6 +661,22 @@ bool LLXMLNode::updateNode( if ((nodeName != "") && (updateName == nodeName)) { updateNode(child, updateChild); + last_child = child; + child = child->getNextSibling(); + if (child.isNull()) + { + child = node->getFirstChild(); + } + break; + } + + child = child->getNextSibling(); + if (child.isNull()) + { + child = node->getFirstChild(); + } + if (child == last_child) + { break; } } @@ -670,14 +714,14 @@ bool LLXMLNode::parseFile(const std::string& filename, LLXMLNodePtr& node, LLXML LLFILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ if (fp == NULL) { - node = new LLXMLNode(); + node = NULL ; return false; } fseek(fp, 0, SEEK_END); U32 length = ftell(fp); fseek(fp, 0, SEEK_SET); - char *buffer = new char[length+1]; + U8* buffer = new U8[length+1]; size_t nread = fread(buffer, 1, length, fp); buffer[nread] = 0; fclose(fp); @@ -689,7 +733,7 @@ bool LLXMLNode::parseFile(const std::string& filename, LLXMLNodePtr& node, LLXML // static bool LLXMLNode::parseBuffer( - const char *buffer, + U8* buffer, U32 length, LLXMLNodePtr& node, LLXMLNode* defaults) @@ -708,7 +752,7 @@ bool LLXMLNode::parseBuffer( XML_SetUserData(my_parser, (void *)file_node_ptr); // Do the parsing - if (XML_Parse(my_parser, buffer, length, TRUE) != XML_STATUS_OK) + if (XML_Parse(my_parser, (const char *)buffer, length, TRUE) != XML_STATUS_OK) { llwarns << "Error parsing xml error code: " << XML_ErrorString(XML_GetErrorCode(my_parser)) @@ -723,7 +767,7 @@ bool LLXMLNode::parseBuffer( { llwarns << "Parse failure - wrong number of top-level nodes xml." << llendl; - node = new LLXMLNode(); + node = NULL ; return false; } @@ -761,7 +805,7 @@ bool LLXMLNode::parseStream( while(str.good()) { str.read((char*)buffer, BUFSIZE); - int count = str.gcount(); + int count = (int)str.gcount(); if (XML_Parse(my_parser, (const char *)buffer, count, !str.good()) != XML_STATUS_OK) { @@ -782,7 +826,7 @@ bool LLXMLNode::parseStream( { llwarns << "Parse failure - wrong number of top-level nodes xml." << llendl; - node = new LLXMLNode(); + node = NULL; return false; } @@ -836,12 +880,64 @@ BOOL LLXMLNode::isFullyDefault() } // static -void LLXMLNode::writeHeaderToFile(LLFILE *fOut) +bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root, + const std::vector& paths) { - fprintf(fOut, "\n"); + if (paths.empty()) return false; + + std::string filename = paths.front(); + if (filename.empty()) + { + return false; + } + + if (!LLXMLNode::parseFile(filename, root, NULL)) + { + llwarns << "Problem reading UI description file: " << filename << llendl; + return false; + } + + LLXMLNodePtr updateRoot; + + std::vector::const_iterator itor; + + for (itor = paths.begin(), ++itor; itor != paths.end(); ++itor) + { + std::string layer_filename = *itor; + if(layer_filename.empty() || layer_filename == filename) + { + // no localized version of this file, that's ok, keep looking + continue; + } + + if (!LLXMLNode::parseFile(layer_filename, updateRoot, NULL)) + { + llwarns << "Problem reading localized UI description file: " << layer_filename << llendl; + return false; + } + + std::string nodeName; + std::string updateName; + + updateRoot->getAttributeString("name", updateName); + root->getAttributeString("name", nodeName); + + if (updateName == nodeName) + { + LLXMLNode::updateNode(root, updateRoot); + } + } + + return true; } -void LLXMLNode::writeToFile(LLFILE *fOut, const std::string& indent) +// static +void LLXMLNode::writeHeaderToFile(LLFILE *out_file) +{ + fprintf(out_file, "\n"); +} + +void LLXMLNode::writeToFile(LLFILE *out_file, const std::string& indent, bool use_type_decorations) { if (isFullyDefault()) { @@ -850,15 +946,16 @@ void LLXMLNode::writeToFile(LLFILE *fOut, const std::string& indent) } std::ostringstream ostream; - writeToOstream(ostream, indent); + writeToOstream(ostream, indent, use_type_decorations); std::string outstring = ostream.str(); - if (fwrite(outstring.c_str(), 1, outstring.length(), fOut) != outstring.length()) + size_t written = fwrite(outstring.c_str(), 1, outstring.length(), out_file); + if (written != outstring.length()) { llwarns << "Short write" << llendl; } } -void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& indent) +void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& indent, bool use_type_decorations) { if (isFullyDefault()) { @@ -872,84 +969,86 @@ void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& i BOOL has_default_length = mDefault.isNull()?FALSE:(mLength == mDefault->mLength); // stream the name - output_stream << indent << "<" << mName->mString; + output_stream << indent << "<" << mName->mString << "\n"; - // ID - if (mID != "") + if (use_type_decorations) { - output_stream << " id=\"" << mID << "\""; - } - - // Type - if (!has_default_type) - { - switch (mType) + // ID + if (mID != "") { - case TYPE_BOOLEAN: - output_stream << " type=\"boolean\""; - break; - case TYPE_INTEGER: - output_stream << " type=\"integer\""; - break; - case TYPE_FLOAT: - output_stream << " type=\"float\""; - break; - case TYPE_STRING: - output_stream << " type=\"string\""; - break; - case TYPE_UUID: - output_stream << " type=\"uuid\""; - break; - case TYPE_NODEREF: - output_stream << " type=\"noderef\""; - break; - default: - // default on switch(enum) eliminates a warning on linux - break; - }; - } + output_stream << indent << " id=\"" << mID << "\"\n"; + } - // Encoding - if (!has_default_encoding) - { - switch (mEncoding) + // Type + if (!has_default_type) { - case ENCODING_DECIMAL: - output_stream << " encoding=\"decimal\""; - break; - case ENCODING_HEX: - output_stream << " encoding=\"hex\""; - break; - /*case ENCODING_BASE32: - output_stream << " encoding=\"base32\""; - break;*/ - default: - // default on switch(enum) eliminates a warning on linux - break; - }; - } + switch (mType) + { + case TYPE_BOOLEAN: + output_stream << indent << " type=\"boolean\"\n"; + break; + case TYPE_INTEGER: + output_stream << indent << " type=\"integer\"\n"; + break; + case TYPE_FLOAT: + output_stream << indent << " type=\"float\"\n"; + break; + case TYPE_STRING: + output_stream << indent << " type=\"string\"\n"; + break; + case TYPE_UUID: + output_stream << indent << " type=\"uuid\"\n"; + break; + case TYPE_NODEREF: + output_stream << indent << " type=\"noderef\"\n"; + break; + default: + // default on switch(enum) eliminates a warning on linux + break; + }; + } - // Precision - if (!has_default_precision && (mType == TYPE_INTEGER || mType == TYPE_FLOAT)) - { - output_stream << " precision=\"" << mPrecision << "\""; - } + // Encoding + if (!has_default_encoding) + { + switch (mEncoding) + { + case ENCODING_DECIMAL: + output_stream << indent << " encoding=\"decimal\"\n"; + break; + case ENCODING_HEX: + output_stream << indent << " encoding=\"hex\"\n"; + break; + /*case ENCODING_BASE32: + output_stream << indent << " encoding=\"base32\"\n"; + break;*/ + default: + // default on switch(enum) eliminates a warning on linux + break; + }; + } - // Version - if (mVersionMajor > 0 || mVersionMinor > 0) - { - output_stream << " version=\"" << mVersionMajor << "." << mVersionMinor << "\""; - } + // Precision + if (!has_default_precision && (mType == TYPE_INTEGER || mType == TYPE_FLOAT)) + { + output_stream << indent << " precision=\"" << mPrecision << "\"\n"; + } - // Array length - if (!has_default_length && mLength > 0) - { - output_stream << " length=\"" << mLength << "\""; + // Version + if (mVersionMajor > 0 || mVersionMinor > 0) + { + output_stream << indent << " version=\"" << mVersionMajor << "." << mVersionMinor << "\"\n"; + } + + // Array length + if (!has_default_length && mLength > 0) + { + output_stream << indent << " length=\"" << mLength << "\"\n"; + } } { // Write out attributes - S32 col_pos = 0; LLXMLAttribList::const_iterator attr_itr; LLXMLAttribList::const_iterator attr_end = mAttributes.end(); for (attr_itr = mAttributes.begin(); attr_itr != attr_end; ++attr_itr) @@ -958,12 +1057,13 @@ void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& i if (child->mDefault.isNull() || child->mDefault->mValue != child->mValue) { std::string attr = child->mName->mString; - if (attr == "id" || - attr == "type" || - attr == "encoding" || - attr == "precision" || - attr == "version" || - attr == "length") + if (use_type_decorations + && (attr == "id" || + attr == "type" || + attr == "encoding" || + attr == "precision" || + attr == "version" || + attr == "length")) { continue; // skip built-in attributes } @@ -971,17 +1071,14 @@ void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& i std::string attr_str = llformat(" %s=\"%s\"", attr.c_str(), escapeXML(child->mValue).c_str()); - if (col_pos + (S32)attr_str.length() > MAX_COLUMN_WIDTH) - { - output_stream << "\n" << indent << " "; - col_pos = 4; - } - col_pos += attr_str.length(); - output_stream << attr_str; + output_stream << indent << attr_str << "\n"; } } } + // erase last \n before attaching final > or /> + output_stream.seekp(-1, std::ios::cur); + if (mChildren.isNull() && mValue == "") { output_stream << " />\n"; @@ -993,16 +1090,16 @@ void LLXMLNode::writeToOstream(std::ostream& output_stream, const std::string& i if (mChildren.notNull()) { // stream non-attributes - std::string next_indent = indent + "\t"; + std::string next_indent = indent + " "; for (LLXMLNode* child = getFirstChild(); child; child = child->getNextSibling()) { - child->writeToOstream(output_stream, next_indent); + child->writeToOstream(output_stream, next_indent, use_type_decorations); } } if (!mValue.empty()) { std::string contents = getTextContents(); - output_stream << indent << "\t" << escapeXML(contents) << "\n"; + output_stream << indent << " " << escapeXML(contents) << "\n"; } output_stream << indent << "mString << ">\n"; } @@ -1130,7 +1227,7 @@ bool LLXMLNode::getChild(const LLStringTableEntry* name, LLXMLNodePtr& node, BOO { return mDefault->getChild(name, node, FALSE); } - node = new LLXMLNode(); + node = NULL; return false; } @@ -1201,7 +1298,7 @@ bool LLXMLNode::getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, { return mDefault->getAttribute(name, node, FALSE); } - node = new LLXMLNode(); + return false; } @@ -2475,14 +2572,15 @@ std::string LLXMLNode::escapeXML(const std::string& xml) return out; } -void LLXMLNode::setStringValue(U32 length, const std::string *array) +void LLXMLNode::setStringValue(U32 length, const std::string *strings) { if (length == 0) return; std::string new_value; for (U32 pos=0; pos @@ -44,10 +44,11 @@ #include #include "indra_constants.h" -#include "llmemory.h" -#include "llthread.h" +#include "llpointer.h" +#include "llthread.h" // LLThreadSafeRefCount #include "llstring.h" #include "llstringtable.h" +#include "llfile.h" class LLVector3; @@ -141,7 +142,7 @@ public: LLXMLNodePtr& node, LLXMLNode* defaults_tree); static bool parseBuffer( - const char *buffer, + U8* buffer, U32 length, LLXMLNodePtr& node, LLXMLNode* defaults); @@ -153,9 +154,18 @@ public: LLXMLNodePtr& node, LLXMLNodePtr& update_node); static LLXMLNodePtr replaceNode(LLXMLNodePtr node, LLXMLNodePtr replacement_node); - static void writeHeaderToFile(LLFILE *fOut); - void writeToFile(LLFILE *fOut, const std::string& indent = std::string()); - void writeToOstream(std::ostream& output_stream, const std::string& indent = std::string()); + + static bool getLayeredXMLNode(LLXMLNodePtr& root, const std::vector& paths); + + + // Write standard XML file header: + // + static void writeHeaderToFile(LLFILE *out_file); + + // Write XML to file with one attribute per line. + // XML escapes values as they are written. + void writeToFile(LLFILE *out_file, const std::string& indent = std::string(), bool use_type_decorations=true); + void writeToOstream(std::ostream& output_stream, const std::string& indent = std::string(), bool use_type_decorations=true); // Utility void findName(const std::string& name, LLXMLNodeList &results); @@ -207,6 +217,7 @@ public: U32 getLength() const { return mLength; } U32 getPrecision() const { return mPrecision; } const std::string& getValue() const { return mValue; } + std::string getSanitizedValue() const; std::string getTextContents() const; const LLStringTableEntry* getName() const { return mName; } BOOL hasName(const char* name) const { return mName == gStringTable.checkStringEntry(name); } @@ -227,6 +238,8 @@ public: bool getAttribute(const char* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); bool getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE); + S32 getLineNumber(); + // The following skip over attributes LLXMLNodePtr getFirstChild() const; LLXMLNodePtr getNextSibling() const; @@ -262,6 +275,8 @@ public: void setName(const std::string& name); void setName(LLStringTableEntry* name); + void setLineNumber(S32 line_number); + // Escapes " (quot) ' (apos) & (amp) < (lt) > (gt) static std::string escapeXML(const std::string& xml); @@ -300,6 +315,7 @@ public: U32 mPrecision; // The number of BITS per array item ValueType mType; // The value type Encoding mEncoding; // The value encoding + S32 mLineNumber; // line number in source file, if applicable LLXMLNode* mParent; // The parent node LLXMLChildrenPtr mChildren; // The child nodes