| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| * (the "License"); you may not use this file except in compliance with | |
| * the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include <log4cxx/logstring.h> | |
| #include <log4cxx/helpers/transform.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include <functional> | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::helpers; | |
| namespace | |
| { | |
| using CharProcessor = std::function<bool(LogString&, int)>; | |
| // Allowable XML 1.0 characters are: | |
| // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] | |
| void appendValidCharacters(LogString& buf, const LogString& input, CharProcessor handler = {}, bool handleValidCharacters = false) | |
| { | |
| static const unsigned int specials[] = | |
| { 0x22 /* " */ | |
| , 0x26 /* & */ | |
| , 0x3C /* < */ | |
| , 0x3E /* > */ | |
| , 0x00 | |
| }; | |
| auto start = input.begin(); | |
| for (auto nextCodePoint = start; input.end() != nextCodePoint; ) | |
| { | |
| auto lastCodePoint = nextCodePoint; | |
| auto ch = static_cast<unsigned int>(*nextCodePoint); | |
| if (ch <= 0x7f) | |
| ++nextCodePoint; | |
| else | |
| ch = Transcoder::getCodePoint(input, nextCodePoint); | |
| if (((0x20 <= ch && ch <= 0xD7FF) && | |
| specials[0] != ch && | |
| specials[1] != ch && | |
| specials[2] != ch && | |
| specials[3] != ch) || | |
| (0x9 == ch || 0xA == ch || 0xD == ch) || | |
| (0xE000 <= ch && ch < 0xFFFD) || | |
| (0x10000 <= ch && ch <= 0x10FFFF)) | |
| { | |
| LogString escaped; | |
| if (handleValidCharacters && handler && handler(escaped, ch)) | |
| { | |
| if (start != lastCodePoint) | |
| buf.append(start, lastCodePoint); | |
| buf.append(escaped); | |
| start = nextCodePoint; | |
| } | |
| continue; | |
| } | |
| if (start != lastCodePoint) | |
| buf.append(start, lastCodePoint); | |
| start = nextCodePoint; | |
| switch (ch) | |
| { | |
| case 0: // Do not output a NUL character | |
| break; | |
| case 0x22: | |
| buf.append(LOG4CXX_STR(""")); | |
| break; | |
| case 0x26: | |
| buf.append(LOG4CXX_STR("&")); | |
| break; | |
| case 0x3C: | |
| buf.append(LOG4CXX_STR("<")); | |
| break; | |
| case 0x3E: | |
| buf.append(LOG4CXX_STR(">")); | |
| break; | |
| case 0xFFFD: // The Unicode replacement character | |
| Transform::appendCharacterReference(buf, 0xFFFD); | |
| break; | |
| default: | |
| if (handler && !handler(buf, ch)) | |
| Transform::appendCharacterReference(buf, ch); | |
| break; | |
| } | |
| } | |
| buf.append(start, input.end()); | |
| } | |
| bool appendCharacterReferenceHandler(LogString& buf, int ch) | |
| { | |
| Transform::appendCharacterReference(buf, ch); | |
| return true; | |
| } | |
| bool appendAttributeCharacterReference(LogString& buf, int ch) | |
| { | |
| if (0x27 == ch || 0x9 == ch || 0xA == ch || 0xD == ch) | |
| { | |
| Transform::appendCharacterReference(buf, ch); | |
| return true; | |
| } | |
| return false; | |
| } | |
| } // namespace | |
| void Transform::appendEscapingCDATA( | |
| LogString& buf, const LogString& input) | |
| { | |
| static const LogString CDATA_END(LOG4CXX_STR("]]>")); | |
| const LogString::size_type CDATA_END_LEN = 3; | |
| static const LogString CDATA_EMBEDED_END(LOG4CXX_STR("]]><![CDATA[")); | |
| auto start = input.begin(); | |
| for (auto nextCodePoint = start; input.end() != nextCodePoint; ) | |
| { | |
| bool cdataEnd = false; | |
| auto lastCodePoint = nextCodePoint; | |
| auto ch = Transcoder::getCodePoint(input, nextCodePoint); | |
| if (CDATA_END[0] == ch && input.end() != nextCodePoint) | |
| { | |
| lastCodePoint = nextCodePoint; | |
| if (CDATA_END[1] != Transcoder::decode(input, nextCodePoint) || | |
| input.end() == nextCodePoint || | |
| CDATA_END[2] != Transcoder::decode(input, nextCodePoint)) | |
| { | |
| nextCodePoint = lastCodePoint; | |
| continue; | |
| } | |
| lastCodePoint = nextCodePoint; | |
| cdataEnd = true; | |
| } | |
| else if ((0x20 <= ch && ch <= 0xD7FF) || | |
| (0x9 == ch || 0xA == ch || 0xD == ch) || | |
| (0xE000 <= ch && ch < 0xFFFD) || | |
| (0x10000 <= ch && ch <= 0x10FFFF)) | |
| { | |
| continue; | |
| } | |
| if (start != lastCodePoint) | |
| buf.append(start, lastCodePoint); | |
| if (cdataEnd) | |
| buf.append(CDATA_EMBEDED_END); | |
| else if (0 != ch) | |
| appendCharacterReference(buf, ch); | |
| start = nextCodePoint; | |
| } | |
| buf.append(start, input.end()); | |
| } | |
| void Transform::appendCharacterReference(LogString& buf, unsigned int ch) | |
| { | |
| auto toHexDigit = [](int ch) -> int | |
| { | |
| return (10 <= ch ? (0x61 - 10) : 0x30) + ch; | |
| }; | |
| buf.push_back('&'); | |
| buf.push_back('#'); | |
| buf.push_back('x'); | |
| if (0xFFFFFFF < ch) | |
| buf.push_back(toHexDigit((ch & 0xF0000000) >> 28)); | |
| if (0xFFFFFF < ch) | |
| buf.push_back(toHexDigit((ch & 0xF000000) >> 24)); | |
| if (0xFFFFF < ch) | |
| buf.push_back(toHexDigit((ch & 0xF00000) >> 20)); | |
| if (0xFFFF < ch) | |
| buf.push_back(toHexDigit((ch & 0xF0000) >> 16)); | |
| if (0xFFF < ch) | |
| buf.push_back(toHexDigit((ch & 0xF000) >> 12)); | |
| if (0xFF < ch) | |
| buf.push_back(toHexDigit((ch & 0xF00) >> 8)); | |
| if (0xF < ch) | |
| buf.push_back(toHexDigit((ch & 0xF0) >> 4)); | |
| buf.push_back(toHexDigit(ch & 0xF)); | |
| buf.push_back(';'); | |
| } | |
| void Transform::appendEscapingTags(LogString& buf, const LogString& input) | |
| { | |
| appendValidCharacters(buf, input, appendCharacterReferenceHandler); | |
| } | |
| void Transform::appendEscapingAttribute(LogString& buf, const LogString& input) | |
| { | |
| appendValidCharacters(buf, input, appendAttributeCharacterReference, true); | |
| } | |
| void Transform::appendLegalCharacters(LogString& buf, const LogString& input) | |
| { | |
| appendValidCharacters(buf, input); | |
| } |