void CXMLparser::ProcessEntity (void) { CString strName; CString strValue; CString strTemp; strName = GetName ("entity"); strTemp = strName.Left (3); strTemp.MakeUpper (); if (strTemp == "XML") ThrowErrorException ("Names starting with \"XML\" are reserved"); strValue = GetValue ("entity", strName); // entities might have entities in them, so replace them m_CustomEntityMap.SetAt (strName, ReplaceEntities (strValue)); if (m_xmlBuff [m_xmlPos] != '>') ThrowErrorException ("Expected '>', got %c", m_xmlBuff [m_xmlPos]); m_xmlPos++; // skip > // bypass spaces and comments SkipComments (true); } // end of CXMLparser::ProcessEntity
void csWrappedDocumentNode::ProcessSingleWrappedNode ( NodeProcessingState* state, iDocumentNode* node) { CS_ASSERT(globalState); if (ProcessTemplate (node, state)) return; if (ProcessStaticIf (state, node)) { // Template invokation has precedence over dropping nodes... TempString<> tokenStr, args; if (SplitNodeValue (node->GetValue(), tokenStr, args)) { Template::Params params; if (!args.IsEmpty()) { ParseTemplateArguments (args, params, false); } InvokeTemplate (tokenStr, node, state, params); } return; } csRefArray<iDocumentNode>& currentWrapper = state->currentWrapper; bool handled = false; if (node->GetType() == CS_NODE_UNKNOWN) { TempString<> replaceScratch; const char* nodeValue = ReplaceEntities (node->GetValue(), replaceScratch); if ((nodeValue != 0) && (*nodeValue == '?') && (*(nodeValue + strlen (nodeValue) - 1) == '?')) { const char* valStart = nodeValue + 1; if ((*valStart == '!') || (*valStart == '#')) { /* Discard PIs beginning with ! and #. This allows comments, e.g. * <?! some comment ?> * The difference to XML comments is that the PI comments do not * appear in the final document after processing, hence are useful * if some PIs themselves are to be commented, but it is undesireable * to have an XML comment in the result. */ return; } while (*valStart == ' ') valStart++; CS_ASSERT (*valStart != 0); size_t valLen = strlen (valStart) - 1; if (valLen == 0) { Report (syntaxErrorSeverity, node, "Empty processing instruction"); } else { while (*(valStart + valLen - 1) == ' ') valLen--; const char* space = strchr (valStart, ' '); /* The rightmost spaces were skipped and don't interest us any more. */ if (space >= valStart + valLen) space = 0; size_t cmdLen; if (space != 0) { cmdLen = space - valStart; } else { cmdLen = valLen; } TempString<> tokenStr; tokenStr.Replace (valStart, cmdLen); csStringID tokenID = shared->pitokens.Request (tokenStr); switch (tokenID) { case csWrappedDocumentNodeFactory::PITOKEN_INCLUDE: if (shared->plugin->do_verbose) { Report (CS_REPORTER_SEVERITY_WARNING, node, "Deprecated syntax, please use %s", CS::Quote::Single ("Include")); } // Fall through case csWrappedDocumentNodeFactory::PITOKEN_INCLUDE_NEW: { bool okay = true; TempString<> filename; const char* space = strchr (valStart, ' '); /* The rightmost spaces were skipped and don't interest us * any more. */ if (space != 0) { filename.Replace (space + 1, valLen - cmdLen - 1); filename.Trim (); } if ((space == 0) || (filename.IsEmpty ())) { Report (syntaxErrorSeverity, node, "%s without filename", CS::Quote::Single ("Include")); okay = false; } if (okay) { ProcessInclude (filename, state, node); } handled = true; } break; case csWrappedDocumentNodeFactory::PITOKEN_TEMPLATE: if (shared->plugin->do_verbose) { Report (CS_REPORTER_SEVERITY_WARNING, node, "Deprecated syntax, please use %s", CS::Quote::Single ("Template")); } // Fall through case csWrappedDocumentNodeFactory::PITOKEN_TEMPLATE_NEW: case csWrappedDocumentNodeFactory::PITOKEN_TEMPLATEWEAK: { TempString<> args (valStart + cmdLen, valLen - cmdLen); args.LTrim(); ProcessInstrTemplate (state, node, args, tokenID == csWrappedDocumentNodeFactory::PITOKEN_TEMPLATEWEAK); handled = true; } break; case csWrappedDocumentNodeFactory::PITOKEN_ENDTEMPLATE: if (shared->plugin->do_verbose) { Report (CS_REPORTER_SEVERITY_WARNING, node, "Deprecated syntax, please use %s", CS::Quote::Single ("Endtemplate")); handled = true; } // Fall through case csWrappedDocumentNodeFactory::PITOKEN_ENDTEMPLATE_NEW: { Report (syntaxErrorSeverity, node, "%s without %s", CS::Quote::Single ("Endtemplate"), CS::Quote::Single ("Template")); // ProcessTemplate() would've handled it otherwise handled = true; } break; case csWrappedDocumentNodeFactory::PITOKEN_GENERATE: { bool okay = true; Template::Params args; if (space != 0) { TempString<> pStr (space + 1, valLen - cmdLen - 1); ParseTemplateArguments (pStr, args, false); } if ((args.GetSize() < 3) || (args.GetSize() > 4)) { okay = false; Report (syntaxErrorSeverity, node, "%s expects 3 or 4 arguments, got %zu", CS::Quote::Single ("Generate"), args.GetSize()); } if (okay) { state->generateVar = args[0]; int start, end, step; char dummy; if (sscanf (args[1], "%d%c", &start, &dummy) != 1) { Report (syntaxErrorSeverity, node, "Argument %s is not an integer", CS::Quote::Single (args[1].GetData())); okay = false; } if (okay && sscanf (args[2], "%d%c", &end, &dummy) != 1) { Report (syntaxErrorSeverity, node, "Argument %s is not an integer", CS::Quote::Single (args[2].GetData())); okay = false; } if (okay) { if (args.GetSize() == 4) { if (sscanf (args[3], "%d%c", &step, &dummy) != 1) { Report (syntaxErrorSeverity, node, "Argument %s is not an integer", CS::Quote::Single (args[3].GetData())); okay = false; } } else { step = (end < start) ? -1 : 1; } } if (okay) { state->generateActive = true; state->generateNestLevel = 1; if (((start > end) && (step >= 0)) || ((end >= start) && (step <= 0))) { Report (syntaxErrorSeverity, node, "Can't reach end value %d starting from %d with step %d", end, start, step); state->generateValid = false; } else { state->generateValid = true; state->generateStart = start; state->generateEnd = end; state->generateStep = step; state->templ = Template(); } } } handled = true; } break; case csWrappedDocumentNodeFactory::PITOKEN_ENDGENERATE: { Report (syntaxErrorSeverity, node, "%s without %s", CS::Quote::Single ("Endgenerate"), CS::Quote::Single ("Generate")); // ProcessTemplate() would've handled it otherwise } break; case csWrappedDocumentNodeFactory::PITOKEN_DEFINE: { TempString<> args (valStart + cmdLen, valLen - cmdLen); args.LTrim(); ProcessDefine (state, node, args); handled = true; } break; case csWrappedDocumentNodeFactory::PITOKEN_UNDEF: { TempString<> args (valStart + cmdLen, valLen - cmdLen); args.LTrim(); ProcessUndef (state, node, args); handled = true; } break; case csWrappedDocumentNodeFactory::PITOKEN_STATIC_IFDEF: case csWrappedDocumentNodeFactory::PITOKEN_STATIC_IFNDEF: { TempString<> args (valStart + cmdLen, valLen - cmdLen); args.LTrim(); ProcessStaticIfDef (state, node, args, tokenID == csWrappedDocumentNodeFactory::PITOKEN_STATIC_IFNDEF); handled = true; } break; case csWrappedDocumentNodeFactory::PITOKEN_STATIC_ELSIFDEF: case csWrappedDocumentNodeFactory::PITOKEN_STATIC_ELSIFNDEF: case csWrappedDocumentNodeFactory::PITOKEN_STATIC_ELSE: case csWrappedDocumentNodeFactory::PITOKEN_STATIC_ENDIF: { Report (syntaxErrorSeverity, node, "%s without %s", CS::Quote::Single (shared->pitokens.Request (tokenID)), CS::Quote::Single ("SIfDef")); // ProcessStaticIf() would've handled it otherwise handled = true; } break; default: { Template::Params params; if (space != 0) { TempString<> pStr (space + 1, valLen - cmdLen - 1); ParseTemplateArguments (pStr, params, false); } if (InvokeTemplate (tokenStr, node, state, params)) { handled = true; } // If it's neither a template nor a recognized command, pass through } } } } } if (!handled) { csRef<iDocumentNode> newWrapper; newWrapper.AttachNew (new csWrappedDocumentNode (this, node, shared, globalState)); currentWrapper.Push (newWrapper); } }
void CXMLparser::ProcessAttributes (CXMLelement & node) { CAttribute * pAttribute; CString strName; CString strValue; while (m_xmlBuff [m_xmlPos] != '>') { // look for "empty element" terminator: /> if (m_xmlBuff [m_xmlPos] == '/' && m_xmlBuff [m_xmlPos + 1] == '>') { node.bEmpty = true; m_xmlPos++; // skip '/' symbol break; } // look for terminator ?> at end of <?xml tag if (m_xmlBuff [m_xmlPos] == '?' && m_xmlBuff [m_xmlPos + 1] == '>' && node.strName [0] == '?' ) { node.bEmpty = true; m_xmlPos++; // skip '?' symbol break; } // shouldn't have end-of-file before the > if (m_xmlBuff [m_xmlPos] == 0) ThrowErrorException ("Unexpected end-of-file inside element definition for %s", (LPCTSTR) node.strName); // we really should have a name now ... strName = GetName ("Attribute"); // already there? error if (node.AttributeMap.Lookup (strName, pAttribute)) ThrowErrorException ("Duplicate attribute name '%s'", (LPCTSTR) strName); // in case spaces before next thing SkipSpaces (); if (m_xmlBuff [m_xmlPos] != '=') ThrowErrorException ("Attribute name '%s' not followed by '='", (LPCTSTR) strName); m_xmlPos++; // skip the = sign int iLine = m_xmlLine; // line that = sign is on strValue = GetValue ("attribute", strName); pAttribute = new CAttribute; if (!pAttribute) ThrowErrorException ("Could not allocate memory for XML parsing of attribute %s", (LPCTSTR) strName); pAttribute->strName = strName; pAttribute->strValue = ReplaceEntities (strValue); pAttribute->iLine = iLine; // add to map node.AttributeMap.SetAt (strName, pAttribute); } // end of looking for attributes m_xmlPos++; // skip '>' symbol } // end of CXMLparser::ProcessAttributes
void CXMLparser::AssembleContent (CXMLelement & node, const UINT iFirstLine, const UINT iNodeStart) { /* work out content - it will be non-nested text belonging to this element eg. <a>xxx<b>yyy</b>zzz</a> "a" will have content xxxzzz "b" will have content yyy whitespace is preserved You can reassemble an entire element (including nested ones) by concatenating the contents of its children. For cross-platform consistency, carriage returns are dropped, and linefeeds become carriage-return/linefeed */ int iContentLen = m_xmlPos - iNodeStart; int iLines = m_xmlLine - iFirstLine; // lines of content const char * pi = &m_xmlBuff [iNodeStart]; // copy from const char * pl = &m_xmlBuff [m_xmlPos]; // limit of copy char * po = node.strContent.GetBuffer (iContentLen + 1 + iLines); // copy to int iDepth = 0; bool bInside = false; char cQuote = ' '; while (pi < pl) // copy, dropping nested elements { // they might have <blah value=">"> // so we have to skip special characters inside quotes if (bInside && (*pi == '\'' || *pi == '\"')) { cQuote = *pi; // for terminator pi++; while (pi < pl && *pi != cQuote) pi++; pi++; // skip terminating quote continue; // back to start of loop } // end of quotes inside a definition // <![CDATA[ is special - just copy contents if (pi [0] == '<' && pi [1] == '!' && pi [2] == '[' && pi [3] == 'C' && pi [4] == 'D' && pi [5] == 'A' && pi [6] == 'T' && pi [7] == 'A' && pi [8] == '[') { pi += 9; // skip <![CDATA[ // this shouldn't happen if (bInside) ThrowErrorException ("Unexpected '<![CDATA[' inside element definition"); // should be terminated as we checked earlier, but may as well check again while (pi < pl && (pi [0] != ']' || pi [1] != ']' || pi [2] != '>')) { // copy if not nested, and not inside an element definition // -- omit carriage returns if (iDepth == 0 && *pi != '\r') { // make linefeeds into carriage return/linefeed if (*pi == '\n') *po++ = '\r'; // special treatment for ampersands, so they don't get converted // into entities ... if (*pi == '&') *po++ = '\x01'; else *po++ = *pi; // copy it } pi++; } // end of inside <![CDATA[ pi += 3; // skip ]]> continue; // back to start of loop } // end of <![CDATA[ section if (*pi == '<') // going into an element { bInside = true; if (pi [1] == '/') // </tag> drops a level { iDepth--; if (iDepth < 0) iDepth = 0; // cannot have negative depth } else if (pi [1] != '!' && pi [1] != '?') // excepting special ones iDepth++; // <tag> goes up a level } // copy if not nested, and not inside an element definition // -- omit carriage returns if (iDepth == 0 && !bInside && *pi != '\r') { // make linefeeds into carriage return/linefeed if (*pi == '\n') *po++ = '\r'; *po++ = *pi; // copy if not inside an element } // leaving an element (only occurs 'inside' a <... sequence ) if (bInside && pi [0] == '/' && pi [1] == '>') { iDepth--; // cancel level gain - this tag is empty if (iDepth < 0) iDepth = 0; // cannot have negative depth } else if (*pi == '>') // leaving an element bInside = false; pi++; // onto next } *po = 0; // terminating null node.strContent.ReleaseBuffer (-1); // get rid of entities (such as <) in the content node.strContent = ReplaceEntities (node.strContent); // now, in case we had <![CDATA[ sections, replace 0x01 by & node.strContent.Replace ('\x01', '&'); } // end of CXMLparser::AssembleContent