File indexing completed on 2024-10-06 03:37:43
0001 /* 0002 SPDX-FileCopyrightText: 2000-2001 Dawit Alemayehu <adawit@kde.org> 0003 SPDX-FileCopyrightText: 2001 Rik Hemsley (rikkus) <rik@kde.org> 0004 SPDX-FileCopyrightText: 2001-2002 Marc Mutz <mutz@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-only 0007 0008 The encoding and decoding utilities in KCodecs with the exception of 0009 quoted-printable are based on the java implementation in HTTPClient 0010 package by Ronald Tschalär Copyright (C) 1996-1999. // krazy:exclude=copyright 0011 0012 The quoted-printable codec as described in RFC 2045, section 6.7. is by 0013 Rik Hemsley (C) 2001. 0014 */ 0015 0016 #include "kcodecs.h" 0017 #include "kcharsets.h" 0018 #include "kcharsets_p.h" 0019 #include "kcodecs_debug.h" 0020 #include "kcodecs_p.h" 0021 #include "kcodecsbase64.h" 0022 #include "kcodecsqp.h" 0023 #include "kcodecsuuencode.h" 0024 0025 #include <array> 0026 #include <cassert> 0027 #include <cstring> 0028 #include <stdio.h> 0029 #include <stdlib.h> 0030 #include <string.h> 0031 0032 #include <QDebug> 0033 #include <QStringDecoder> 0034 #include <QStringEncoder> 0035 0036 #if defined(Q_OS_WIN) 0037 #define strncasecmp _strnicmp 0038 #endif 0039 0040 namespace KCodecs 0041 { 0042 static QList<QByteArray> charsetCache; 0043 0044 QByteArray cachedCharset(const QByteArray &name) 0045 { 0046 auto it = std::find_if(charsetCache.cbegin(), charsetCache.cend(), [&name](const QByteArray &charset) { 0047 return qstricmp(name.data(), charset.data()) == 0; 0048 }); 0049 if (it != charsetCache.cend()) { 0050 return *it; 0051 } 0052 0053 charsetCache.append(name.toUpper()); 0054 return charsetCache.last(); 0055 } 0056 0057 namespace CodecNames 0058 { 0059 QByteArray utf8() 0060 { 0061 return QByteArrayLiteral("UTF-8"); 0062 } 0063 } 0064 0065 Q_REQUIRED_RESULT 0066 QByteArray updateEncodingCharset(const QByteArray ¤tCharset, const QByteArray &nextCharset) 0067 { 0068 if (!nextCharset.isEmpty()) { 0069 if (currentCharset.isEmpty()) { 0070 return nextCharset; 0071 } 0072 if (currentCharset != nextCharset) { 0073 // only one charset per string supported, so change to superset charset UTF-8, 0074 // which should cover any possible chars 0075 return CodecNames::utf8(); 0076 } 0077 } 0078 return currentCharset; 0079 } 0080 0081 } // namespace KCodecs 0082 0083 /******************************** KCodecs ********************************/ 0084 0085 QByteArray KCodecs::quotedPrintableEncode(QByteArrayView in, bool useCRLF) 0086 { 0087 Codec *codec = Codec::codecForName("quoted-printable"); 0088 return codec->encode(in, useCRLF ? Codec::NewlineCRLF : Codec::NewlineLF); 0089 } 0090 0091 void KCodecs::quotedPrintableEncode(QByteArrayView in, QByteArray &out, bool useCRLF) 0092 { 0093 out = quotedPrintableEncode(in, useCRLF ? Codec::NewlineCRLF : Codec::NewlineLF); 0094 } 0095 0096 QByteArray KCodecs::quotedPrintableDecode(QByteArrayView in) 0097 { 0098 Codec *codec = Codec::codecForName("quoted-printable"); 0099 return codec->decode(in); 0100 } 0101 0102 void KCodecs::quotedPrintableDecode(QByteArrayView in, QByteArray &out) 0103 { 0104 out = quotedPrintableDecode(in); 0105 } 0106 0107 QByteArray KCodecs::base64Encode(QByteArrayView in) 0108 { 0109 Codec *codec = Codec::codecForName("base64"); 0110 return codec->encode(in); 0111 } 0112 0113 void KCodecs::base64Encode(QByteArrayView in, QByteArray &out, bool insertLFs) 0114 { 0115 Q_UNUSED(insertLFs); 0116 out = base64Encode(in); 0117 } 0118 0119 QByteArray KCodecs::base64Decode(QByteArrayView in) 0120 { 0121 Codec *codec = Codec::codecForName("base64"); 0122 return codec->decode(in); 0123 } 0124 0125 void KCodecs::base64Decode(const QByteArrayView in, QByteArray &out) 0126 { 0127 out = base64Decode(in); 0128 } 0129 0130 QByteArray KCodecs::uudecode(QByteArrayView in) 0131 { 0132 Codec *codec = Codec::codecForName("x-uuencode"); 0133 return codec->decode(in); 0134 } 0135 0136 void KCodecs::uudecode(QByteArrayView in, QByteArray &out) 0137 { 0138 out = uudecode(in); 0139 } 0140 0141 //@cond PRIVATE 0142 0143 namespace KCodecs 0144 { 0145 // parse the encoded-word (scursor points to after the initial '=') 0146 bool parseEncodedWord(const char *&scursor, 0147 const char *const send, 0148 QString *result, 0149 QByteArray *language, 0150 QByteArray *usedCS, 0151 const QByteArray &defaultCS, 0152 CharsetOption charsetOption) 0153 { 0154 assert(result); 0155 assert(language); 0156 0157 // make sure the caller already did a bit of the work. 0158 assert(*(scursor - 1) == '='); 0159 0160 // 0161 // STEP 1: 0162 // scan for the charset/language portion of the encoded-word 0163 // 0164 0165 char ch = *scursor++; 0166 0167 if (ch != '?') { 0168 // qCDebug(KCODECS_LOG) << "first"; 0169 // qCDebug(KCODECS_LOG) << "Premature end of encoded word"; 0170 return false; 0171 } 0172 0173 // remember start of charset (i.e. just after the initial "=?") and 0174 // language (just after the first '*') fields: 0175 const char *charsetStart = scursor; 0176 const char *languageStart = nullptr; 0177 0178 // find delimiting '?' (and the '*' separating charset and language 0179 // tags, if any): 0180 for (; scursor != send; scursor++) { 0181 if (*scursor == '?') { 0182 break; 0183 } else if (*scursor == '*' && languageStart == nullptr) { 0184 languageStart = scursor + 1; 0185 } 0186 } 0187 0188 // not found? can't be an encoded-word! 0189 if (scursor == send || *scursor != '?') { 0190 // qCDebug(KCODECS_LOG) << "second"; 0191 // qCDebug(KCODECS_LOG) << "Premature end of encoded word"; 0192 return false; 0193 } 0194 0195 // extract the language information, if any (if languageStart is 0, 0196 // language will be null, too): 0197 QByteArray maybeLanguage(languageStart, scursor - languageStart); 0198 // extract charset information (keep in mind: the size given to the 0199 // ctor is one off due to the \0 terminator): 0200 QByteArray maybeCharset(charsetStart, (languageStart ? languageStart - 1 : scursor) - charsetStart); 0201 0202 // 0203 // STEP 2: 0204 // scan for the encoding portion of the encoded-word 0205 // 0206 0207 // remember start of encoding (just _after_ the second '?'): 0208 scursor++; 0209 const char *encodingStart = scursor; 0210 0211 // find next '?' (ending the encoding tag): 0212 for (; scursor != send; scursor++) { 0213 if (*scursor == '?') { 0214 break; 0215 } 0216 } 0217 0218 // not found? Can't be an encoded-word! 0219 if (scursor == send || *scursor != '?') { 0220 // qCDebug(KCODECS_LOG) << "third"; 0221 // qCDebug(KCODECS_LOG) << "Premature end of encoded word"; 0222 return false; 0223 } 0224 0225 // extract the encoding information: 0226 QByteArray maybeEncoding(encodingStart, scursor - encodingStart); 0227 0228 // qCDebug(KCODECS_LOG) << "parseEncodedWord: found charset == \"" << maybeCharset 0229 // << "\"; language == \"" << maybeLanguage 0230 // << "\"; encoding == \"" << maybeEncoding << "\""; 0231 0232 // 0233 // STEP 3: 0234 // scan for encoded-text portion of encoded-word 0235 // 0236 0237 // remember start of encoded-text (just after the third '?'): 0238 scursor++; 0239 const char *encodedTextStart = scursor; 0240 0241 // find the '?=' sequence (ending the encoded-text): 0242 for (; scursor != send; scursor++) { 0243 if (*scursor == '?') { 0244 if (scursor + 1 != send) { 0245 if (*(scursor + 1) != '=') { // We expect a '=' after the '?', but we got something else; ignore 0246 // qCDebug(KCODECS_LOG) << "Stray '?' in q-encoded word, ignoring this."; 0247 continue; 0248 } else { // yep, found a '?=' sequence 0249 scursor += 2; 0250 break; 0251 } 0252 } else { // The '?' is the last char, but we need a '=' after it! 0253 // qCDebug(KCODECS_LOG) << "Premature end of encoded word"; 0254 return false; 0255 } 0256 } 0257 } 0258 0259 if (*(scursor - 2) != '?' || *(scursor - 1) != '=' || scursor < encodedTextStart + 2) { 0260 // qCDebug(KCODECS_LOG) << "Premature end of encoded word"; 0261 return false; 0262 } 0263 0264 // set end sentinel for encoded-text: 0265 const char *const encodedTextEnd = scursor - 2; 0266 0267 // 0268 // STEP 4: 0269 // setup decoders for the transfer encoding and the charset 0270 // 0271 0272 // try if there's a codec for the encoding found: 0273 Codec *codec = Codec::codecForName(maybeEncoding); 0274 if (!codec) { 0275 // qCDebug(KCODECS_LOG) << "Unknown encoding" << maybeEncoding; 0276 return false; 0277 } 0278 0279 // get an instance of a corresponding decoder: 0280 Decoder *dec = codec->makeDecoder(); 0281 assert(dec); 0282 0283 // try if there's a (text)codec for the charset found: 0284 QByteArray cs; 0285 QStringDecoder textCodec; 0286 if (charsetOption == KCodecs::ForceDefaultCharset || maybeCharset.isEmpty()) { 0287 textCodec = QStringDecoder(defaultCS.constData()); 0288 cs = cachedCharset(defaultCS); 0289 } else { 0290 textCodec = QStringDecoder(maybeCharset.constData()); 0291 if (!textCodec.isValid()) { // no suitable codec found => use default charset 0292 textCodec = QStringDecoder(defaultCS.constData()); 0293 cs = cachedCharset(defaultCS); 0294 } else { 0295 cs = cachedCharset(maybeCharset); 0296 } 0297 } 0298 if (usedCS) { 0299 *usedCS = updateEncodingCharset(*usedCS, cs); 0300 } 0301 0302 if (!textCodec.isValid()) { 0303 // qCDebug(KCODECS_LOG) << "Unknown charset" << maybeCharset; 0304 delete dec; 0305 return false; 0306 }; 0307 0308 // qCDebug(KCODECS_LOG) << "mimeName(): \"" << textCodec->name() << "\""; 0309 0310 // allocate a temporary buffer to store the 8bit text: 0311 int encodedTextLength = encodedTextEnd - encodedTextStart; 0312 QByteArray buffer; 0313 buffer.resize(codec->maxDecodedSizeFor(encodedTextLength)); 0314 char *bbegin = buffer.data(); 0315 char *bend = bbegin + buffer.length(); 0316 0317 // 0318 // STEP 5: 0319 // do the actual decoding 0320 // 0321 0322 if (!dec->decode(encodedTextStart, encodedTextEnd, bbegin, bend)) { 0323 qWarning() << codec->name() << "codec lies about its maxDecodedSizeFor(" << encodedTextLength << ")\nresult may be truncated"; 0324 } 0325 0326 *result = textCodec.decode(QByteArrayView(buffer.data(), bbegin - buffer.data())); 0327 0328 // qCDebug(KCODECS_LOG) << "result now: \"" << result << "\""; 0329 // cleanup: 0330 delete dec; 0331 *language = maybeLanguage; 0332 0333 return true; 0334 } 0335 0336 } // namespace KCodecs 0337 0338 //@endcond 0339 0340 QString KCodecs::decodeRFC2047String(QStringView msg) 0341 { 0342 QByteArray usedCS; 0343 return decodeRFC2047String(msg.toUtf8(), &usedCS, CodecNames::utf8(), NoOption); 0344 } 0345 0346 QString KCodecs::decodeRFC2047String(QByteArrayView src, QByteArray *usedCS, const QByteArray &defaultCS, CharsetOption charsetOption) 0347 { 0348 QByteArray result; 0349 QByteArray spaceBuffer; 0350 const char *scursor = src.constData(); 0351 const char *send = scursor + src.length(); 0352 bool onlySpacesSinceLastWord = false; 0353 if (usedCS) { 0354 usedCS->clear(); 0355 } 0356 0357 while (scursor != send) { 0358 // space 0359 if (isspace(*scursor) && onlySpacesSinceLastWord) { 0360 spaceBuffer += *scursor++; 0361 continue; 0362 } 0363 0364 // possible start of an encoded word 0365 if (*scursor == '=') { 0366 QByteArray language; 0367 QString decoded; 0368 ++scursor; 0369 const char *start = scursor; 0370 if (parseEncodedWord(scursor, send, &decoded, &language, usedCS, defaultCS, charsetOption)) { 0371 result += decoded.toUtf8(); 0372 onlySpacesSinceLastWord = true; 0373 spaceBuffer.clear(); 0374 } else { 0375 if (onlySpacesSinceLastWord) { 0376 result += spaceBuffer; 0377 onlySpacesSinceLastWord = false; 0378 } 0379 result += '='; 0380 scursor = start; // reset cursor after parsing failure 0381 } 0382 continue; 0383 } else { 0384 // unencoded data 0385 if (onlySpacesSinceLastWord) { 0386 result += spaceBuffer; 0387 onlySpacesSinceLastWord = false; 0388 } 0389 result += *scursor; 0390 ++scursor; 0391 } 0392 } 0393 // If there are any chars that couldn't be decoded in UTF-8, 0394 // fallback to local codec 0395 const QString tryUtf8 = QString::fromUtf8(result); 0396 if (tryUtf8.contains(QChar(0xFFFD))) { 0397 QStringDecoder codec(QStringDecoder::System); 0398 if (usedCS) { 0399 *usedCS = updateEncodingCharset(*usedCS, cachedCharset(codec.name())); 0400 } 0401 return codec.decode(result); 0402 } else { 0403 return tryUtf8; 0404 } 0405 } 0406 0407 QByteArray KCodecs::encodeRFC2047String(QStringView src, const QByteArray &charset) 0408 { 0409 QByteArray result; 0410 int start = 0; 0411 int end = 0; 0412 bool nonAscii = false; 0413 bool useQEncoding = false; 0414 0415 QStringEncoder codec(charset.constData()); 0416 0417 QByteArray usedCS; 0418 if (!codec.isValid()) { 0419 // no codec available => try local8Bit and hope the best ;-) 0420 codec = QStringEncoder(QStringEncoder::System); 0421 usedCS = codec.name(); 0422 } else { 0423 Q_ASSERT(codec.isValid()); 0424 if (charset.isEmpty()) { 0425 usedCS = codec.name(); 0426 } else { 0427 usedCS = charset; 0428 } 0429 } 0430 0431 QByteArray encoded8Bit = codec.encode(src); 0432 if (codec.hasError()) { 0433 usedCS = CodecNames::utf8(); 0434 codec = QStringEncoder(QStringEncoder::Utf8); 0435 encoded8Bit = codec.encode(src); 0436 } 0437 0438 if (usedCS.contains("8859-")) { // use "B"-Encoding for non iso-8859-x charsets 0439 useQEncoding = true; 0440 } 0441 0442 uint encoded8BitLength = encoded8Bit.length(); 0443 for (unsigned int i = 0; i < encoded8BitLength; i++) { 0444 if (encoded8Bit[i] == ' ') { // encoding starts at word boundaries 0445 start = i + 1; 0446 } 0447 0448 // encode escape character, for japanese encodings... 0449 if (((signed char)encoded8Bit[i] < 0) || (encoded8Bit[i] == '\033')) { 0450 end = start; // non us-ascii char found, now we determine where to stop encoding 0451 nonAscii = true; 0452 break; 0453 } 0454 } 0455 0456 if (nonAscii) { 0457 while ((end < encoded8Bit.length()) && (encoded8Bit[end] != ' ')) { 0458 // we encode complete words 0459 end++; 0460 } 0461 0462 for (int x = end; x < encoded8Bit.length(); x++) { 0463 if (((signed char)encoded8Bit[x] < 0) || (encoded8Bit[x] == '\033')) { 0464 end = x; // we found another non-ascii word 0465 0466 while ((end < encoded8Bit.length()) && (encoded8Bit[end] != ' ')) { 0467 // we encode complete words 0468 end++; 0469 } 0470 } 0471 } 0472 0473 result = encoded8Bit.left(start) + "=?" + usedCS; 0474 0475 if (useQEncoding) { 0476 result += "?Q?"; 0477 0478 char hexcode; // "Q"-encoding implementation described in RFC 2047 0479 for (int i = start; i < end; i++) { 0480 const char c = encoded8Bit[i]; 0481 if (c == ' ') { // make the result readable with not MIME-capable readers 0482 result += '_'; 0483 } else { 0484 if (((c >= 'a') && (c <= 'z')) || // paranoid mode, encode *all* special chars to avoid problems 0485 ((c >= 'A') && (c <= 'Z')) || // with "From" & "To" headers 0486 ((c >= '0') && (c <= '9'))) { 0487 result += c; 0488 } else { 0489 result += '='; // "stolen" from KMail ;-) 0490 hexcode = ((c & 0xF0) >> 4) + 48; 0491 if (hexcode >= 58) { 0492 hexcode += 7; 0493 } 0494 result += hexcode; 0495 hexcode = (c & 0x0F) + 48; 0496 if (hexcode >= 58) { 0497 hexcode += 7; 0498 } 0499 result += hexcode; 0500 } 0501 } 0502 } 0503 } else { 0504 result += "?B?" + encoded8Bit.mid(start, end - start).toBase64(); 0505 } 0506 0507 result += "?="; 0508 result += encoded8Bit.right(encoded8Bit.length() - end); 0509 } else { 0510 result = encoded8Bit; 0511 } 0512 0513 return result; 0514 } 0515 0516 /******************************************************************************/ 0517 /* KCodecs::Codec */ 0518 0519 KCodecs::Codec *KCodecs::Codec::codecForName(QByteArrayView name) 0520 { 0521 struct CodecEntry { 0522 const char *name; 0523 std::unique_ptr<KCodecs::Codec> codec; 0524 }; 0525 // ### has to be sorted by name! 0526 static const std::array<CodecEntry, 6> s_codecs{{ 0527 {"b", std::make_unique<KCodecs::Rfc2047BEncodingCodec>()}, 0528 {"base64", std::make_unique<KCodecs::Base64Codec>()}, 0529 {"q", std::make_unique<KCodecs::Rfc2047QEncodingCodec>()}, 0530 {"quoted-printable", std::make_unique<KCodecs::QuotedPrintableCodec>()}, 0531 {"x-kmime-rfc2231", std::make_unique<KCodecs::Rfc2231EncodingCodec>()}, 0532 {"x-uuencode", std::make_unique<KCodecs::UUCodec>()}, 0533 }}; 0534 0535 const auto it = std::lower_bound(s_codecs.begin(), s_codecs.end(), name, [](const auto &lhs, auto rhs) { 0536 return rhs.compare(lhs.name, Qt::CaseInsensitive) > 0; 0537 }); 0538 if (it == s_codecs.end() || name.compare((*it).name, Qt::CaseInsensitive) != 0) { 0539 qWarning() << "Unknown codec \"" << name << "\" requested!"; 0540 } 0541 return (*it).codec.get(); 0542 } 0543 0544 bool KCodecs::Codec::encode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend, NewlineType newline) const 0545 { 0546 // get an encoder: 0547 std::unique_ptr<Encoder> enc(makeEncoder(newline)); 0548 if (!enc) { 0549 qWarning() << "makeEncoder failed for" << name(); 0550 return false; 0551 } 0552 0553 // encode and check for output buffer overflow: 0554 while (!enc->encode(scursor, send, dcursor, dend)) { 0555 if (dcursor == dend) { 0556 return false; // not enough space in output buffer 0557 } 0558 } 0559 0560 // finish and check for output buffer overflow: 0561 while (!enc->finish(dcursor, dend)) { 0562 if (dcursor == dend) { 0563 return false; // not enough space in output buffer 0564 } 0565 } 0566 0567 return true; // successfully encoded. 0568 } 0569 0570 QByteArray KCodecs::Codec::encode(QByteArrayView src, NewlineType newline) const 0571 { 0572 // allocate buffer for the worst case: 0573 QByteArray result; 0574 result.resize(maxEncodedSizeFor(src.size(), newline)); 0575 0576 // set up iterators: 0577 QByteArray::ConstIterator iit = src.begin(); 0578 QByteArray::ConstIterator iend = src.end(); 0579 QByteArray::Iterator oit = result.begin(); 0580 QByteArray::ConstIterator oend = result.end(); 0581 0582 // encode 0583 if (!encode(iit, iend, oit, oend, newline)) { 0584 qCritical() << name() << "codec lies about it's mEncodedSizeFor()"; 0585 } 0586 0587 // shrink result to actual size: 0588 result.truncate(oit - result.begin()); 0589 0590 return result; 0591 } 0592 0593 QByteArray KCodecs::Codec::decode(QByteArrayView src, NewlineType newline) const 0594 { 0595 // allocate buffer for the worst case: 0596 QByteArray result; 0597 result.resize(maxDecodedSizeFor(src.size(), newline)); 0598 0599 // set up iterators: 0600 QByteArray::ConstIterator iit = src.begin(); 0601 QByteArray::ConstIterator iend = src.end(); 0602 QByteArray::Iterator oit = result.begin(); 0603 QByteArray::ConstIterator oend = result.end(); 0604 0605 // decode 0606 if (!decode(iit, iend, oit, oend, newline)) { 0607 qCritical() << name() << "codec lies about it's maxDecodedSizeFor()"; 0608 } 0609 0610 // shrink result to actual size: 0611 result.truncate(oit - result.begin()); 0612 0613 return result; 0614 } 0615 0616 bool KCodecs::Codec::decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend, NewlineType newline) const 0617 { 0618 // get a decoder: 0619 std::unique_ptr<Decoder> dec(makeDecoder(newline)); 0620 assert(dec); 0621 0622 // decode and check for output buffer overflow: 0623 while (!dec->decode(scursor, send, dcursor, dend)) { 0624 if (dcursor == dend) { 0625 return false; // not enough space in output buffer 0626 } 0627 } 0628 0629 // finish and check for output buffer overflow: 0630 while (!dec->finish(dcursor, dend)) { 0631 if (dcursor == dend) { 0632 return false; // not enough space in output buffer 0633 } 0634 } 0635 0636 return true; // successfully encoded. 0637 } 0638 0639 /******************************************************************************/ 0640 /* KCodecs::Encoder */ 0641 0642 KCodecs::EncoderPrivate::EncoderPrivate(Codec::NewlineType newline) 0643 : outputBufferCursor(0) 0644 , newline(newline) 0645 { 0646 } 0647 0648 KCodecs::Encoder::Encoder(Codec::NewlineType newline) 0649 : d(new KCodecs::EncoderPrivate(newline)) 0650 { 0651 } 0652 0653 KCodecs::Encoder::~Encoder() = default; 0654 0655 bool KCodecs::Encoder::write(char ch, char *&dcursor, const char *const dend) 0656 { 0657 if (dcursor != dend) { 0658 // if there's space in the output stream, write there: 0659 *dcursor++ = ch; 0660 return true; 0661 } else { 0662 // else buffer the output: 0663 if (d->outputBufferCursor >= maxBufferedChars) { 0664 qCritical() << "KCodecs::Encoder: internal buffer overflow!"; 0665 } else { 0666 d->outputBuffer[d->outputBufferCursor++] = ch; 0667 } 0668 return false; 0669 } 0670 } 0671 0672 // write as much as possible off the output buffer. Return true if 0673 // flushing was complete, false if some chars could not be flushed. 0674 bool KCodecs::Encoder::flushOutputBuffer(char *&dcursor, const char *const dend) 0675 { 0676 int i; 0677 // copy output buffer to output stream: 0678 for (i = 0; dcursor != dend && i < d->outputBufferCursor; ++i) { 0679 *dcursor++ = d->outputBuffer[i]; 0680 } 0681 0682 // calculate the number of missing chars: 0683 int numCharsLeft = d->outputBufferCursor - i; 0684 // push the remaining chars to the beginning of the buffer: 0685 if (numCharsLeft) { 0686 ::memmove(d->outputBuffer, d->outputBuffer + i, numCharsLeft); 0687 } 0688 // adjust cursor: 0689 d->outputBufferCursor = numCharsLeft; 0690 0691 return !numCharsLeft; 0692 } 0693 0694 bool KCodecs::Encoder::writeCRLF(char *&dcursor, const char *const dend) 0695 { 0696 if (d->newline == Codec::NewlineCRLF) { 0697 write('\r', dcursor, dend); 0698 } 0699 return write('\n', dcursor, dend); 0700 } 0701 0702 /******************************************************************************/ 0703 /* KCodecs::Decoder */ 0704 0705 KCodecs::DecoderPrivate::DecoderPrivate(Codec::NewlineType newline) 0706 : newline(newline) 0707 { 0708 } 0709 0710 KCodecs::Decoder::Decoder(Codec::NewlineType newline) 0711 : d(new KCodecs::DecoderPrivate(newline)) 0712 { 0713 } 0714 0715 KCodecs::Decoder::~Decoder() = default;