blob: c3db7b38613b3b07340e137eb63b5ede889c477c [file]
/*
* 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("&quot;"));
break;
case 0x26:
buf.append(LOG4CXX_STR("&amp;"));
break;
case 0x3C:
buf.append(LOG4CXX_STR("&lt;"));
break;
case 0x3E:
buf.append(LOG4CXX_STR("&gt;"));
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("]]&gt;<![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);
}