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
13using namespace KCodecs;
14
15namespace KCodecs
16{
17class 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
31protected:
32 friend class UUCodec;
33 UUDecoder(Codec::NewlineType newline = Codec::NewlineLF)
34 : Decoder(newline)
35 , mStepNo(0)
36 , mAnnouncedOctetCount(0)
37 , mCurrentOctetCount(0)
38 , mOutbits(0)
39 , mLastWasCRLF(true)
40 , mSawBegin(false)
41 , mIntoBeginLine(0)
42 , mSawEnd(false)
43 , mIntoEndLine(0)
44 {
45 }
46
47public:
48 ~UUDecoder() override
49 {
50 }
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 {
56 Q_UNUSED(dcursor);
57 Q_UNUSED(dend);
58 return true;
59 }
60};
61
62Encoder *UUCodec::makeEncoder(NewlineType newline) const
63{
64 Q_UNUSED(newline)
65 return nullptr; // encoding not supported
66}
67
68Decoder *UUCodec::makeDecoder(NewlineType newline) const
69{
70 return new UUDecoder(newline);
71}
72
73/********************************************************/
74/********************************************************/
75/********************************************************/
76
77void UUDecoder::searchForBegin(const char *&scursor, const char *const send)
78{
79 static const char begin[] = "begin\n";
80 static const uint beginLength = 5; // sic!
81
82 assert(!mSawBegin || mIntoBeginLine > 0);
83
84 while (scursor != send) {
85 uchar ch = *scursor++;
86 if (ch == begin[mIntoBeginLine]) {
87 if (mIntoBeginLine < beginLength) {
88 // found another char
89 ++mIntoBeginLine;
90 if (mIntoBeginLine == beginLength) {
91 mSawBegin = true; // "begin" complete, now search the next \n...
92 }
93 } else { // mIntoBeginLine == beginLength
94 // found '\n': begin line complete
95 mLastWasCRLF = true;
96 mIntoBeginLine = 0;
97 return;
98 }
99 } else if (mSawBegin) {
100 // OK, skip stuff until the next \n
101 } else {
102 // qWarning() << "UUDecoder: garbage before \"begin\", resetting parser";
103 mIntoBeginLine = 0;
104 }
105 }
106}
107
108// uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL,
109// which gets mapped to 0x60
110static inline uchar uuDecode(uchar c)
111{
112 return (c - ' ') // undo shift and
113 & 0x3F; // map 0x40 (0x60-' ') to 0...
114}
115
116bool UUDecoder::decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend)
117{
118 // First, check whether we still need to find the "begin" line:
119 if (!mSawBegin || mIntoBeginLine != 0) {
120 searchForBegin(scursor, send);
121 } else if (mSawEnd) {
122 // or if we are past the end line:
123 scursor = send; // do nothing anymore...
124 return true;
125 }
126
127 while (dcursor != dend && scursor != send) {
128 uchar ch = *scursor++;
129 uchar value;
130
131 // Check whether we need to look for the "end" line:
132 if (mIntoEndLine > 0) {
133 static const char end[] = "end";
134 static const uint endLength = 3;
135
136 if (ch == end[mIntoEndLine]) {
137 ++mIntoEndLine;
138 if (mIntoEndLine == endLength) {
139 mSawEnd = true;
140 scursor = send; // shortcut to the end
141 return true;
142 }
143 continue;
144 } else {
145 // qWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine ="
146 // << mIntoEndLine << ")!";
147 mIntoEndLine = 0;
148 // fall through...
149 }
150 }
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 if (mLastWasCRLF) {
157 // reset char-per-line counter:
158 mLastWasCRLF = false;
159 mCurrentOctetCount = 0;
160
161 // try to decode the chars-on-this-line announcement:
162 if (ch == 'e') { // maybe the beginning of the "end"? ;-)
163 mIntoEndLine = 1;
164 } else if (ch > 0x60) {
165 // ### invalid line length char: what shall we do??
166 } else if (ch > ' ') {
167 mAnnouncedOctetCount = uuDecode(c: ch);
168 } else if (ch == '\n') {
169 mLastWasCRLF = true; // oops, empty line
170 }
171
172 continue;
173 }
174
175 // try converting ch to a 6-bit value:
176 if (ch > 0x60) {
177 continue; // invalid char
178 } else if (ch > ' ') {
179 value = uuDecode(c: ch);
180 } else if (ch == '\n') { // line end
181 mLastWasCRLF = true;
182 continue;
183 } else {
184 continue;
185 }
186
187 // add the new bits to the output stream and flush full octets:
188 switch (mStepNo) {
189 case 0:
190 mOutbits = value << 2;
191 break;
192 case 1:
193 if (mCurrentOctetCount < mAnnouncedOctetCount) {
194 *dcursor++ = (char)(mOutbits | value >> 4);
195 }
196 ++mCurrentOctetCount;
197 mOutbits = value << 4;
198 break;
199 case 2:
200 if (mCurrentOctetCount < mAnnouncedOctetCount) {
201 *dcursor++ = (char)(mOutbits | value >> 2);
202 }
203 ++mCurrentOctetCount;
204 mOutbits = value << 6;
205 break;
206 case 3:
207 if (mCurrentOctetCount < mAnnouncedOctetCount) {
208 *dcursor++ = (char)(mOutbits | value);
209 }
210 ++mCurrentOctetCount;
211 mOutbits = 0;
212 break;
213 default:
214 assert(0);
215 }
216 mStepNo = (mStepNo + 1) % 4;
217
218 // check whether we ran over the announced octet count for this line:
219 if (mCurrentOctetCount == mAnnouncedOctetCount + 1) {
220 // qWarning()
221 // << "UUDecoder: mismatch between announced ("
222 // << mAnnouncedOctetCount << ") and actual line octet count!";
223 }
224 }
225
226 // return false when caller should call us again:
227 return scursor == send;
228} // UUDecoder::decode()
229
230} // namespace KCodecs
231

source code of kcodecs/src/kcodecsuuencode.cpp