1 /*
2 * uucode.c --
3 *
4 * Implements and registers conversion from and to uuencoded representation.
5 *
6 *
7 * Copyright (c) 1996 Andreas Kupries (andreas_kupries@users.sourceforge.net)
8 * All rights reserved.
9 *
10 * Permission is hereby granted, without written agreement and without
11 * license or royalty fees, to use, copy, modify, and distribute this
12 * software and its documentation for any purpose, provided that the
13 * above copyright notice and the following two paragraphs appear in
14 * all copies of this software.
15 *
16 * IN NO EVENT SHALL I LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
17 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
18 * SOFTWARE AND ITS DOCUMENTATION, EVEN IF I HAVE BEEN ADVISED OF THE
19 * POSSIBILITY OF SUCH DAMAGE.
20 *
21 * I SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND
24 * I HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
25 * ENHANCEMENTS, OR MODIFICATIONS.
26 *
27 * CVS: $Id: uucode.c,v 1.8 2009/05/07 04:57:27 andreas_kupries Exp $
28 */
29
30 #include "transformInt.h"
31
32 /*
33 * Converter description
34 * ---------------------
35 *
36 * Encoding:
37 * Each sequence of 3 bytes is expanded into 4 printable characters
38 * using the 4 6bit-sequences contained in the 3 bytes. The mapping
39 * from 6bit value to printable characters is done with the UU map.
40 * Special processing is done for incomplete byte sequences at the
41 * end of the input (1,2 bytes).
42 *
43 * Decoding:
44 * Each sequence of 4 characters is mapped into 4 6bit values using
45 * the reverse UU map and then concatenated to form 3 8bit bytes.
46 * Special processing is done for incomplete character sequences at
47 * the end of the input (1,2,3 bytes).
48 */
49
50
51 /*
52 * Declarations of internal procedures.
53 */
54
55 static Trf_ControlBlock CreateEncoder _ANSI_ARGS_ ((ClientData writeClientData, Trf_WriteProc *fun,
56 Trf_Options optInfo, Tcl_Interp* interp,
57 ClientData clientData));
58 static void DeleteEncoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
59 ClientData clientData));
60 static int Encode _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
61 unsigned int character,
62 Tcl_Interp* interp,
63 ClientData clientData));
64 static int FlushEncoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
65 Tcl_Interp* interp,
66 ClientData clientData));
67 static void ClearEncoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
68 ClientData clientData));
69
70
71 static Trf_ControlBlock CreateDecoder _ANSI_ARGS_ ((ClientData writeClientData, Trf_WriteProc *fun,
72 Trf_Options optInfo, Tcl_Interp* interp,
73 ClientData clientData));
74 static void DeleteDecoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
75 ClientData clientData));
76 static int Decode _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
77 unsigned int character,
78 Tcl_Interp* interp,
79 ClientData clientData));
80 static int FlushDecoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
81 Tcl_Interp* interp,
82 ClientData clientData));
83 static void ClearDecoder _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
84 ClientData clientData));
85
86
87 /*
88 * Converter definition.
89 */
90
91 static Trf_TypeDefinition convDefinition =
92 {
93 "uuencode",
94 NULL, /* clientData not used by converters */
95 NULL, /* set later by Trf_InitUU, THREADING: serialize initialization */
96 {
97 CreateEncoder,
98 DeleteEncoder,
99 Encode,
100 NULL,
101 FlushEncoder,
102 ClearEncoder,
103 NULL /* no MaxRead */
104 }, {
105 CreateDecoder,
106 DeleteDecoder,
107 Decode,
108 NULL,
109 FlushDecoder,
110 ClearDecoder,
111 NULL /* no MaxRead */
112 },
113 TRF_RATIO (3, 4)
114 };
115
116 /*
117 * Definition of the control blocks for en- and decoder.
118 */
119
120 typedef struct _EncoderControl_ {
121 Trf_WriteProc* write;
122 ClientData writeClientData;
123
124 /* add conversion specific items here (uuencode) */
125
126 unsigned char charCount;
127 unsigned char buf [3];
128
129 } EncoderControl;
130
131
132 typedef struct _DecoderControl_ {
133 Trf_WriteProc* write;
134 ClientData writeClientData;
135
136 /* add conversion specific items here (uudecode) */
137
138 unsigned char charCount;
139 unsigned char buf [4];
140 unsigned char expectFlush;
141
142 } DecoderControl;
143
144
145 /*
146 * Character mapping for uuencode (bin -> ascii)
147 *
148 * Index this array by a 6-bit value to obtain the corresponding
149 * 8-bit character. The last character (index 64) is the pad char (~)
150 * |
151 * 1 2 3 4 5 6 6
152 * 01 2345678901234567890123456789012345678901234567890123456789 01234 */
153 static CONST char* uuMap = "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_~";
154 /* uuMap: THREADING: constant, read-only => safe */
155
156 #define PAD '~'
157
158 /*
159 * Character mappings for uudecode (ascii -> bin)
160 *
161 * Index this array by a 8 bit value to get the 6-bit binary field
162 * corresponding to that value. Any illegal characters have high bit set.
163 */
164
165 #define Ccc (CONST char) /* Ccc = CONST char cast */
166 static CONST char uuMapReverse [] = { /* THREADING: constant, read-only => safe */
167 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
168 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
169 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
170 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
171 Ccc 0200, Ccc 0001, Ccc 0002, Ccc 0003, Ccc 0004, Ccc 0005, Ccc 0006, Ccc 0007,
172 Ccc 0010, Ccc 0011, Ccc 0012, Ccc 0013, Ccc 0014, Ccc 0015, Ccc 0016, Ccc 0017,
173 Ccc 0020, Ccc 0021, Ccc 0022, Ccc 0023, Ccc 0024, Ccc 0025, Ccc 0026, Ccc 0027,
174 Ccc 0030, Ccc 0031, Ccc 0032, Ccc 0033, Ccc 0034, Ccc 0035, Ccc 0036, Ccc 0037,
175 Ccc 0040, Ccc 0041, Ccc 0042, Ccc 0043, Ccc 0044, Ccc 0045, Ccc 0046, Ccc 0047,
176 Ccc 0050, Ccc 0051, Ccc 0052, Ccc 0053, Ccc 0054, Ccc 0055, Ccc 0056, Ccc 0057,
177 Ccc 0060, Ccc 0061, Ccc 0062, Ccc 0063, Ccc 0064, Ccc 0065, Ccc 0066, Ccc 0067,
178 Ccc 0070, Ccc 0071, Ccc 0072, Ccc 0073, Ccc 0074, Ccc 0075, Ccc 0076, Ccc 0077,
179 Ccc 0000, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
180 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
181 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
182 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
183 /* */
184 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
185 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
186 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
187 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
188 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
189 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
190 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
191 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
192 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
193 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
194 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
195 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
196 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
197 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
198 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200,
199 Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200, Ccc 0200
200 };
201 #undef Ccc
202
203
204 /*
205 *------------------------------------------------------*
206 *
207 * TrfInit_UU --
208 *
209 * ------------------------------------------------*
210 * Register the conversion implemented in this file.
211 * ------------------------------------------------*
212 *
213 * Sideeffects:
214 * As of 'Trf_Register'.
215 *
216 * Result:
217 * A standard Tcl error code.
218 *
219 *------------------------------------------------------*
220 */
221
222 int
TrfInit_UU(interp)223 TrfInit_UU (interp)
224 Tcl_Interp* interp;
225 {
226 TrfLock; /* THREADING: serialize initialization */
227 convDefinition.options = Trf_ConverterOptions ();
228 TrfUnlock;
229
230 return Trf_Register (interp, &convDefinition);
231 }
232
233 /*
234 *------------------------------------------------------*
235 *
236 * CreateEncoder --
237 *
238 * ------------------------------------------------*
239 * Allocate and initialize the control block of a
240 * data encoder.
241 * ------------------------------------------------*
242 *
243 * Sideeffects:
244 * Allocates memory.
245 *
246 * Result:
247 * An opaque reference to the control block.
248 *
249 *------------------------------------------------------*
250 */
251
252 static Trf_ControlBlock
CreateEncoder(writeClientData,fun,optInfo,interp,clientData)253 CreateEncoder (writeClientData, fun, optInfo, interp, clientData)
254 ClientData writeClientData;
255 Trf_WriteProc *fun;
256 Trf_Options optInfo;
257 Tcl_Interp* interp;
258 ClientData clientData;
259 {
260 EncoderControl* c;
261
262 c = (EncoderControl*) ckalloc (sizeof (EncoderControl));
263 c->write = fun;
264 c->writeClientData = writeClientData;
265
266 /* initialize conversion specific items here (uuencode) */
267
268 c->charCount = 0;
269 memset (c->buf, '\0', 3);
270
271 return (ClientData) c;
272 }
273
274 /*
275 *------------------------------------------------------*
276 *
277 * DeleteEncoder --
278 *
279 * ------------------------------------------------*
280 * Destroy the control block of an encoder.
281 * ------------------------------------------------*
282 *
283 * Sideeffects:
284 * Releases the memory allocated by 'CreateEncoder'
285 *
286 * Result:
287 * None.
288 *
289 *------------------------------------------------------*
290 */
291
292 static void
DeleteEncoder(ctrlBlock,clientData)293 DeleteEncoder (ctrlBlock, clientData)
294 Trf_ControlBlock ctrlBlock;
295 ClientData clientData;
296 {
297 EncoderControl* c = (EncoderControl*) ctrlBlock;
298
299 /* release conversion specific items here (uuencode) */
300
301 ckfree ((char*) c);
302 }
303
304 /*
305 *------------------------------------------------------*
306 *
307 * Encode --
308 *
309 * ------------------------------------------------*
310 * Encode the given character and write the result.
311 * ------------------------------------------------*
312 *
313 * Sideeffects:
314 * As of the called WriteFun.
315 *
316 * Result:
317 * Generated bytes implicitly via WriteFun.
318 * A standard Tcl error code.
319 *
320 *------------------------------------------------------*
321 */
322
323 static int
Encode(ctrlBlock,character,interp,clientData)324 Encode (ctrlBlock, character, interp, clientData)
325 Trf_ControlBlock ctrlBlock;
326 unsigned int character;
327 Tcl_Interp* interp;
328 ClientData clientData;
329 {
330 EncoderControl* c = (EncoderControl*) ctrlBlock;
331
332 /* execute conversion specific code here (uuencode) */
333
334 c->buf [c->charCount] = character;
335 c->charCount ++;
336
337 if (c->charCount == 3) {
338 unsigned char buf [4];
339
340 TrfSplit3to4 (c->buf, buf, 3);
341 TrfApplyEncoding (buf, 4, uuMap);
342
343 c->charCount = 0;
344 memset (c->buf, '\0', 3);
345
346 return c->write (c->writeClientData, buf, 4, interp);
347 }
348
349 return TCL_OK;
350 }
351
352 /*
353 *------------------------------------------------------*
354 *
355 * FlushEncoder --
356 *
357 * ------------------------------------------------*
358 * Encode an incomplete character sequence (if possible).
359 * ------------------------------------------------*
360 *
361 * Sideeffects:
362 * As of the called WriteFun.
363 *
364 * Result:
365 * Generated bytes implicitly via WriteFun.
366 * A standard Tcl error code.
367 *
368 *------------------------------------------------------*
369 */
370
371 static int
FlushEncoder(ctrlBlock,interp,clientData)372 FlushEncoder (ctrlBlock, interp, clientData)
373 Trf_ControlBlock ctrlBlock;
374 Tcl_Interp* interp;
375 ClientData clientData;
376 {
377 EncoderControl* c = (EncoderControl*) ctrlBlock;
378
379 /* execute conversion specific code here (uuencode) */
380
381 if (c->charCount > 0) {
382 unsigned char buf [4];
383
384 TrfSplit3to4 (c->buf, buf, c->charCount);
385 TrfApplyEncoding (buf, 4, uuMap);
386
387 c->charCount = 0;
388 memset (c->buf, '\0', 3);
389
390 return c->write (c->writeClientData, buf, 4, interp);
391 }
392
393 return TCL_OK;
394 }
395
396 /*
397 *------------------------------------------------------*
398 *
399 * ClearEncoder --
400 *
401 * ------------------------------------------------*
402 * Discard an incomplete character sequence.
403 * ------------------------------------------------*
404 *
405 * Sideeffects:
406 * See above.
407 *
408 * Result:
409 * None.
410 *
411 *------------------------------------------------------*
412 */
413
414 static void
ClearEncoder(ctrlBlock,clientData)415 ClearEncoder (ctrlBlock, clientData)
416 Trf_ControlBlock ctrlBlock;
417 ClientData clientData;
418 {
419 EncoderControl* c = (EncoderControl*) ctrlBlock;
420
421 /* execute conversion specific code here (uuencode) */
422
423 c->charCount = 0;
424 memset (c->buf, '\0', 3);
425 }
426
427 /*
428 *------------------------------------------------------*
429 *
430 * CreateDecoder --
431 *
432 * ------------------------------------------------*
433 * Allocate and initialize the control block of a
434 * data decoder.
435 * ------------------------------------------------*
436 *
437 * Sideeffects:
438 * Allocates memory.
439 *
440 * Result:
441 * An opaque reference to the control block.
442 *
443 *------------------------------------------------------*
444 */
445
446 static Trf_ControlBlock
CreateDecoder(writeClientData,fun,optInfo,interp,clientData)447 CreateDecoder (writeClientData, fun, optInfo, interp, clientData)
448 ClientData writeClientData;
449 Trf_WriteProc *fun;
450 Trf_Options optInfo;
451 Tcl_Interp* interp;
452 ClientData clientData;
453 {
454 DecoderControl* c;
455
456 c = (DecoderControl*) ckalloc (sizeof (DecoderControl));
457 c->write = fun;
458 c->writeClientData = writeClientData;
459
460 /* initialize conversion specific items here (uudecode) */
461
462 c->charCount = 0;
463 memset (c->buf, '\0', 4);
464 c->expectFlush = 0;
465
466 return (ClientData) c;
467 }
468
469 /*
470 *------------------------------------------------------*
471 *
472 * DeleteDecoder --
473 *
474 * ------------------------------------------------*
475 * Destroy the control block of an decoder.
476 * ------------------------------------------------*
477 *
478 * Sideeffects:
479 * Releases the memory allocated by 'CreateDecoder'
480 *
481 * Result:
482 * None.
483 *
484 *------------------------------------------------------*
485 */
486
487 static void
DeleteDecoder(ctrlBlock,clientData)488 DeleteDecoder (ctrlBlock, clientData)
489 Trf_ControlBlock ctrlBlock;
490 ClientData clientData;
491 {
492 DecoderControl* c = (DecoderControl*) ctrlBlock;
493
494 /* release conversion specific items here (uudecode) */
495
496 ckfree ((char*) c);
497 }
498
499 /*
500 *------------------------------------------------------*
501 *
502 * Decode --
503 *
504 * ------------------------------------------------*
505 * Decode the given character and write the result.
506 * ------------------------------------------------*
507 *
508 * Sideeffects:
509 * As of the called WriteFun.
510 *
511 * Result:
512 * Generated bytes implicitly via WriteFun.
513 * A standard Tcl error code.
514 *
515 *------------------------------------------------------*
516 */
517
518 static int
Decode(ctrlBlock,character,interp,clientData)519 Decode (ctrlBlock, character, interp, clientData)
520 Trf_ControlBlock ctrlBlock;
521 unsigned int character;
522 Tcl_Interp* interp;
523 ClientData clientData;
524 {
525 DecoderControl* c = (DecoderControl*) ctrlBlock;
526
527 /* execute conversion specific code here (uudecode) */
528
529 if (c->expectFlush) {
530 /*
531 * We had a quadruple with pad characters at the last call,
532 * this had to be the last characters in input! coming here
533 * now indicates, that the padding characters were in the
534 * middle of the string, therefore illegal.
535 */
536
537 if (interp) {
538 Tcl_ResetResult (interp);
539 Tcl_AppendResult (interp, "illegal padding inside the string", (char*) NULL);
540 }
541 return TCL_ERROR;
542 }
543
544
545 c->buf [c->charCount] = character;
546 c->charCount ++;
547
548 if (c->charCount == 4) {
549 int res, hasPadding;
550 unsigned char buf [3];
551
552 hasPadding = 0;
553 res = TrfReverseEncoding (c->buf, 4, uuMapReverse,
554 PAD, &hasPadding);
555
556 if (res != TCL_OK) {
557 if (interp) {
558 Tcl_ResetResult (interp);
559 Tcl_AppendResult (interp, "illegal character found in input", (char*) NULL);
560 }
561 return res;
562 }
563
564 if (hasPadding)
565 c->expectFlush = 1;
566
567 TrfMerge4to3 (c->buf, buf);
568
569 c->charCount = 0;
570 memset (c->buf, '\0', 4);
571
572 return c->write (c->writeClientData, buf, 3-hasPadding, interp);
573 }
574
575 return TCL_OK;
576 }
577
578 /*
579 *------------------------------------------------------*
580 *
581 * FlushDecoder --
582 *
583 * ------------------------------------------------*
584 * Decode an incomplete character sequence (if possible).
585 * ------------------------------------------------*
586 *
587 * Sideeffects:
588 * As of the called WriteFun.
589 *
590 * Result:
591 * Generated bytes implicitly via WriteFun.
592 * A standard Tcl error code.
593 *
594 *------------------------------------------------------*
595 */
596
597 static int
FlushDecoder(ctrlBlock,interp,clientData)598 FlushDecoder (ctrlBlock, interp, clientData)
599 Trf_ControlBlock ctrlBlock;
600 Tcl_Interp* interp;
601 ClientData clientData;
602 {
603 DecoderControl* c = (DecoderControl*) ctrlBlock;
604
605 /* execute conversion specific code here (uudecode) */
606
607 /*
608 * expectFlush && c->charcount > 0 impossible, catched
609 * in 'Decode' already.
610 */
611
612 if (c->charCount > 0) {
613 /*
614 * Convert, as if padded with the pad-character.
615 */
616
617 int res, hasPadding;
618 unsigned char buf [3];
619
620 hasPadding = 0;
621 res = TrfReverseEncoding (c->buf, c->charCount, uuMapReverse,
622 PAD, &hasPadding);
623
624 if (res != TCL_OK) {
625 if (interp) {
626 Tcl_ResetResult (interp);
627 Tcl_AppendResult (interp, "illegal character found in input", (char*) NULL);
628 }
629 return res;
630 }
631
632 TrfMerge4to3 (c->buf, buf);
633
634 c->charCount = 0;
635 memset (c->buf, '\0', 4);
636
637 return c->write (c->writeClientData, buf, 3-hasPadding, interp);
638 }
639
640 return TCL_OK;
641 }
642
643 /*
644 *------------------------------------------------------*
645 *
646 * ClearDecoder --
647 *
648 * ------------------------------------------------*
649 * Discard an incomplete character sequence.
650 * ------------------------------------------------*
651 *
652 * Sideeffects:
653 * See above.
654 *
655 * Result:
656 * None.
657 *
658 *------------------------------------------------------*
659 */
660
661 static void
ClearDecoder(ctrlBlock,clientData)662 ClearDecoder (ctrlBlock, clientData)
663 Trf_ControlBlock ctrlBlock;
664 ClientData clientData;
665 {
666 DecoderControl* c = (DecoderControl*) ctrlBlock;
667
668 /* execute conversion specific code here (uudecode) */
669
670 c->charCount = 0;
671 memset (c->buf, '\0', 4);
672 c->expectFlush = 0;
673 }
674