/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 |