File indexing completed on 2025-01-05 05:23:53

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2010 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "bytearrayxxencodingstreamencoder.hpp"
0010 
0011 // Okteta core
0012 #include <Okteta/AbstractByteArrayModel>
0013 // KF
0014 #include <KConfigGroup>
0015 #include <KSharedConfig>
0016 #include <KLocalizedString>
0017 // Qt
0018 #include <QTextStream>
0019 
0020 namespace Kasten {
0021 
0022 const QString XxencodingStreamEncoderSettings::DefaultFileName;
0023 
0024 static constexpr char xxencodeMap[64] = {
0025     '+', '-', '0', '1', '2', '3', '4', '5',
0026     '6', '7', '8', '9', 'A', 'B', 'C', 'D',
0027     'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
0028     'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
0029     'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
0030     'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
0031     'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
0032     's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
0033 };
0034 static constexpr const char* paddingData[2] = {"++", "+"};
0035 
0036 static inline constexpr char xxmapByte(char byte) { return xxencodeMap[static_cast<int>(byte)]; }
0037 
0038 static inline constexpr const char* xxpadding(ByteArrayXxencodingStreamEncoder::InputByteIndex index)
0039 {
0040     return paddingData[static_cast<int>(index) - 1];
0041 }
0042 
0043 XxencodingStreamEncoderSettings::XxencodingStreamEncoderSettings() = default;
0044 
0045 bool XxencodingStreamEncoderSettings::operator==(const XxencodingStreamEncoderSettings& other) const
0046 {
0047     return (fileName == other.fileName);
0048 }
0049 
0050 void XxencodingStreamEncoderSettings::loadConfig(const KConfigGroup& configGroup)
0051 {
0052     fileName = configGroup.readEntry(FileNameConfigKey, DefaultFileName);
0053 }
0054 
0055 void XxencodingStreamEncoderSettings::saveConfig(KConfigGroup& configGroup) const
0056 {
0057     configGroup.writeEntry(FileNameConfigKey, fileName);
0058 }
0059 
0060 
0061 ByteArrayXxencodingStreamEncoder::ByteArrayXxencodingStreamEncoder()
0062     : AbstractByteArrayStreamEncoder(i18nc("name of the encoding target", "Xxencoding"), QStringLiteral("text/x-xxencode"))
0063 {
0064     const KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0065     mSettings.loadConfig(configGroup);
0066 }
0067 
0068 ByteArrayXxencodingStreamEncoder::~ByteArrayXxencodingStreamEncoder() = default;
0069 
0070 void ByteArrayXxencodingStreamEncoder::setSettings(const XxencodingStreamEncoderSettings& settings)
0071 {
0072     if (mSettings == settings) {
0073         return;
0074     }
0075 
0076     mSettings = settings;
0077     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0078     mSettings.saveConfig(configGroup);
0079     Q_EMIT settingsChanged();
0080 }
0081 
0082 // TODO: make this algorithm shared with ByteArrayUuencodingStreamEncoder again
0083 bool ByteArrayXxencodingStreamEncoder::encodeDataToStream(QIODevice* device,
0084                                                           const ByteArrayView* byteArrayView,
0085                                                           const Okteta::AbstractByteArrayModel* byteArrayModel,
0086                                                           const Okteta::AddressRange& range)
0087 {
0088     Q_UNUSED(byteArrayView);
0089 
0090     const char header[] = "begin";
0091     const char footer[] = "\n+\nend\n";
0092 
0093     bool success = true;
0094 
0095     // encode
0096     QTextStream textStream(device);
0097 
0098     // prepare
0099     InputByteIndex inputByteIndex = InputByteIndex::First;
0100     int inputGroupsPerLine = 0;
0101     unsigned char bitsFromLastByte;
0102 
0103     // header
0104     textStream << header << " 644 " << mSettings.fileName.toLatin1();
0105 
0106     const int firstLineLength = qMin(range.width(), xxInputLineLength);
0107     if (firstLineLength > 0) {
0108         textStream << '\n';
0109         textStream << xxmapByte(firstLineLength);
0110     }
0111 
0112     for (Okteta::Address i = range.start(); i <= range.end(); ++i) {
0113         const Okteta::Byte byte = byteArrayModel->byte(i);
0114 
0115         switch (inputByteIndex)
0116         {
0117         case InputByteIndex::First:
0118             // bits 7..2
0119             textStream << xxmapByte(byte >> 2);
0120             // bits 1..0 -> 5..4 for next
0121             bitsFromLastByte = (byte & 0x3) << 4;
0122             inputByteIndex = InputByteIndex::Second;
0123             break;
0124         case InputByteIndex::Second:
0125             // from last and bits 7..4 as 3..0 from this
0126             textStream << xxmapByte(bitsFromLastByte | byte >> 4);
0127             // bits 3..0 -> 5..2 for next
0128             bitsFromLastByte = (byte & 0xf) << 2;
0129             inputByteIndex = InputByteIndex::Third;
0130             break;
0131         case InputByteIndex::Third:
0132             // from last and bits 7..6 as 1..0 from this
0133             textStream << xxmapByte(bitsFromLastByte | byte >> 6);
0134             // bits 5..0
0135             textStream << xxmapByte(byte & 0x3F);
0136             inputByteIndex = InputByteIndex::First;
0137             ++inputGroupsPerLine;
0138             if (inputGroupsPerLine >= maxXxInputGroupsPerLine && i < range.end()) {
0139                 const int remainsCount = range.end() - i;
0140                 const int nextLineLength = qMin(remainsCount, xxInputLineLength);
0141                 textStream << '\n';
0142                 textStream << xxmapByte(nextLineLength);
0143                 inputGroupsPerLine = 0;
0144             }
0145             break;
0146         }
0147     }
0148 
0149     const bool hasBitsLeft = (inputByteIndex != InputByteIndex::First);
0150     if (hasBitsLeft) {
0151         textStream << xxmapByte(bitsFromLastByte)
0152                    << xxpadding(inputByteIndex);
0153     }
0154     // footer
0155     textStream << footer;
0156 
0157     return success;
0158 }
0159 
0160 }
0161 
0162 #include "moc_bytearrayxxencodingstreamencoder.cpp"