Coverage Report

Created: 2026-11-12 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kcodecs/src/kcodecsuuencode.cpp
Line
Count
Source
1
/*  -*- c++ -*-
2
    SPDX-FileCopyrightText: 2002 Marc Mutz <mutz@kde.org>
3
4
    SPDX-License-Identifier: LGPL-2.0-or-later
5
*/
6
7
#include "kcodecsuuencode.h"
8
9
#include <QDebug>
10
11
#include <cassert>
12
13
using namespace KCodecs;
14
15
namespace KCodecs
16
{
17
class UUDecoder : public Decoder
18
{
19
    uint mStepNo;
20
    uchar mAnnouncedOctetCount; // (on current line)
21
    uchar mCurrentOctetCount; // (on current line)
22
    uchar mOutbits;
23
    bool mLastWasCRLF : 1;
24
    bool mSawBegin : 1; // whether we already saw ^begin...
25
    uint mIntoBeginLine : 3; // count #chars we compared against "begin" 0..5
26
    bool mSawEnd : 1; // whether we already saw ^end...
27
    uint mIntoEndLine : 2; // count #chars we compared against "end" 0..3
28
29
    void searchForBegin(const char *&scursor, const char *const send);
30
31
protected:
32
    friend class UUCodec;
33
    UUDecoder(Codec::NewlineType newline = Codec::NewlineLF)
34
127k
        : Decoder(newline)
35
127k
        , mStepNo(0)
36
127k
        , mAnnouncedOctetCount(0)
37
127k
        , mCurrentOctetCount(0)
38
127k
        , mOutbits(0)
39
127k
        , mLastWasCRLF(true)
40
127k
        , mSawBegin(false)
41
127k
        , mIntoBeginLine(0)
42
127k
        , mSawEnd(false)
43
127k
        , mIntoEndLine(0)
44
127k
    {
45
127k
    }
46
47
public:
48
    ~UUDecoder() override
49
0
    {
50
0
    }
51
52
    bool decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) override;
53
    // ### really needs no finishing???
54
    bool finish(char *&dcursor, const char *const dend) override
55
127k
    {
56
127k
        Q_UNUSED(dcursor);
57
127k
        Q_UNUSED(dend);
58
127k
        return true;
59
127k
    }
60
};
61
62
Encoder *UUCodec::makeEncoder(NewlineType newline) const
63
0
{
64
0
    Q_UNUSED(newline)
65
0
    return nullptr; // encoding not supported
66
0
}
67
68
Decoder *UUCodec::makeDecoder(NewlineType newline) const
69
127k
{
70
127k
    return new UUDecoder(newline);
71
127k
}
72
73
/********************************************************/
74
/********************************************************/
75
/********************************************************/
76
77
void UUDecoder::searchForBegin(const char *&scursor, const char *const send)
78
127k
{
79
127k
    static const char begin[] = "begin\n";
80
127k
    static const uint beginLength = 5; // sic!
81
82
127k
    assert(!mSawBegin || mIntoBeginLine > 0);
83
84
40.1M
    while (scursor != send) {
85
40.1M
        uchar ch = *scursor++;
86
40.1M
        if (ch == begin[mIntoBeginLine]) {
87
736k
            if (mIntoBeginLine < beginLength) {
88
                // found another char
89
614k
                ++mIntoBeginLine;
90
614k
                if (mIntoBeginLine == beginLength) {
91
122k
                    mSawBegin = true; // "begin" complete, now search the next \n...
92
122k
                }
93
614k
            } else { // mIntoBeginLine == beginLength
94
                // found '\n': begin line complete
95
122k
                mLastWasCRLF = true;
96
122k
                mIntoBeginLine = 0;
97
122k
                return;
98
122k
            }
99
39.3M
        } else if (mSawBegin) {
100
            // OK, skip stuff until the next \n
101
37.6M
        } else {
102
            // qWarning() << "UUDecoder: garbage before \"begin\", resetting parser";
103
1.78M
            mIntoBeginLine = 0;
104
1.78M
        }
105
40.1M
    }
106
127k
}
107
108
// uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL,
109
// which gets mapped to 0x60
110
static inline uchar uuDecode(uchar c)
111
1.75M
{
112
1.75M
    return (c - ' ') // undo shift and
113
1.75M
        & 0x3F; // map 0x40 (0x60-' ') to 0...
114
1.75M
}
115
116
bool UUDecoder::decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend)
117
127k
{
118
    // First, check whether we still need to find the "begin" line:
119
127k
    if (!mSawBegin || mIntoBeginLine != 0) {
120
127k
        searchForBegin(scursor, send);
121
127k
    } else if (mSawEnd) {
122
        // or if we are past the end line:
123
0
        scursor = send; // do nothing anymore...
124
0
        return true;
125
0
    }
126
127
4.13M
    while (dcursor != dend && scursor != send) {
128
4.13M
        uchar ch = *scursor++;
129
4.13M
        uchar value;
130
131
        // Check whether we need to look for the "end" line:
132
4.13M
        if (mIntoEndLine > 0) {
133
252k
            static const char end[] = "end";
134
252k
            static const uint endLength = 3;
135
136
252k
            if (ch == end[mIntoEndLine]) {
137
246k
                ++mIntoEndLine;
138
246k
                if (mIntoEndLine == endLength) {
139
122k
                    mSawEnd = true;
140
122k
                    scursor = send; // shortcut to the end
141
122k
                    return true;
142
122k
                }
143
124k
                continue;
144
246k
            } else {
145
                // qWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine ="
146
                //           << mIntoEndLine << ")!";
147
6.32k
                mIntoEndLine = 0;
148
                // fall through...
149
6.32k
            }
150
252k
        }
151
152
        // Normal parsing:
153
154
        // The first char of a line is an encoding of the length of the
155
        // current line. We simply ignore it:
156
3.88M
        if (mLastWasCRLF) {
157
            // reset char-per-line counter:
158
407k
            mLastWasCRLF = false;
159
407k
            mCurrentOctetCount = 0;
160
161
            // try to decode the chars-on-this-line announcement:
162
407k
            if (ch == 'e') { // maybe the beginning of the "end"? ;-)
163
128k
                mIntoEndLine = 1;
164
279k
            } else if (ch > 0x60) {
165
                // ### invalid line length char: what shall we do??
166
232k
            } else if (ch > ' ') {
167
188k
                mAnnouncedOctetCount = uuDecode(ch);
168
188k
            } else if (ch == '\n') {
169
41.2k
                mLastWasCRLF = true; // oops, empty line
170
41.2k
            }
171
172
407k
            continue;
173
407k
        }
174
175
        // try converting ch to a 6-bit value:
176
3.47M
        if (ch > 0x60) {
177
855k
            continue; // invalid char
178
2.62M
        } else if (ch > ' ') {
179
1.57M
            value = uuDecode(ch);
180
1.57M
        } else if (ch == '\n') { // line end
181
244k
            mLastWasCRLF = true;
182
244k
            continue;
183
808k
        } else {
184
808k
            continue;
185
808k
        }
186
187
        // add the new bits to the output stream and flush full octets:
188
1.57M
        switch (mStepNo) {
189
418k
        case 0:
190
418k
            mOutbits = value << 2;
191
418k
            break;
192
403k
        case 1:
193
403k
            if (mCurrentOctetCount < mAnnouncedOctetCount) {
194
145k
                *dcursor++ = (char)(mOutbits | value >> 4);
195
145k
            }
196
403k
            ++mCurrentOctetCount;
197
403k
            mOutbits = value << 4;
198
403k
            break;
199
379k
        case 2:
200
379k
            if (mCurrentOctetCount < mAnnouncedOctetCount) {
201
123k
                *dcursor++ = (char)(mOutbits | value >> 2);
202
123k
            }
203
379k
            ++mCurrentOctetCount;
204
379k
            mOutbits = value << 6;
205
379k
            break;
206
369k
        case 3:
207
369k
            if (mCurrentOctetCount < mAnnouncedOctetCount) {
208
116k
                *dcursor++ = (char)(mOutbits | value);
209
116k
            }
210
369k
            ++mCurrentOctetCount;
211
369k
            mOutbits = 0;
212
369k
            break;
213
0
        default:
214
0
            assert(0);
215
1.57M
        }
216
1.57M
        mStepNo = (mStepNo + 1) % 4;
217
218
        // check whether we ran over the announced octet count for this line:
219
1.57M
        if (mCurrentOctetCount == mAnnouncedOctetCount + 1) {
220
            // qWarning()
221
            //         << "UUDecoder: mismatch between announced ("
222
            //         << mAnnouncedOctetCount << ") and actual line octet count!";
223
21.8k
        }
224
1.57M
    }
225
226
    // return false when caller should call us again:
227
5.72k
    return scursor == send;
228
127k
} // UUDecoder::decode()
229
230
} // namespace KCodecs