1/*
2 * uucode.c --
3 *
4 *	Implements and registers conversion from and to uuencoded representation.
5 *
6 *
7 * Copyright (c) 1996 Andreas Kupries (a.kupries@westend.com)
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
55static Trf_ControlBlock CreateEncoder  _ANSI_ARGS_ ((ClientData writeClientData, Trf_WriteProc *fun,
56						     Trf_Options optInfo, Tcl_Interp*   interp,
57						     ClientData clientData));
58static void             DeleteEncoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
59						     ClientData clientData));
60static int              Encode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
61						     unsigned int character,
62						     Tcl_Interp* interp,
63						     ClientData clientData));
64static int              FlushEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
65						     Tcl_Interp* interp,
66						     ClientData clientData));
67static void             ClearEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
68						     ClientData clientData));
69
70
71static Trf_ControlBlock CreateDecoder  _ANSI_ARGS_ ((ClientData writeClientData, Trf_WriteProc *fun,
72						     Trf_Options optInfo, Tcl_Interp*   interp,
73						     ClientData clientData));
74static void             DeleteDecoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
75						     ClientData clientData));
76static int              Decode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
77						     unsigned int character,
78						     Tcl_Interp* interp,
79						     ClientData clientData));
80static int              FlushDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
81						     Tcl_Interp* interp,
82						     ClientData clientData));
83static void             ClearDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
84						     ClientData clientData));
85
86
87/*
88 * Converter definition.
89 */
90
91static 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
120typedef 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
132typedef 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 */
153static 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 */
166static 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
222int
223TrfInit_UU (interp)
224Tcl_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
252static Trf_ControlBlock
253CreateEncoder (writeClientData, fun, optInfo, interp, clientData)
254ClientData    writeClientData;
255Trf_WriteProc *fun;
256Trf_Options   optInfo;
257Tcl_Interp*   interp;
258ClientData 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
292static void
293DeleteEncoder (ctrlBlock, clientData)
294Trf_ControlBlock ctrlBlock;
295ClientData 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
323static int
324Encode (ctrlBlock, character, interp, clientData)
325Trf_ControlBlock ctrlBlock;
326unsigned int character;
327Tcl_Interp* interp;
328ClientData 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
371static int
372FlushEncoder (ctrlBlock, interp, clientData)
373Trf_ControlBlock ctrlBlock;
374Tcl_Interp* interp;
375ClientData 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
414static void
415ClearEncoder (ctrlBlock, clientData)
416Trf_ControlBlock ctrlBlock;
417ClientData 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
446static Trf_ControlBlock
447CreateDecoder (writeClientData, fun, optInfo, interp, clientData)
448ClientData    writeClientData;
449Trf_WriteProc *fun;
450Trf_Options   optInfo;
451Tcl_Interp*   interp;
452ClientData 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
487static void
488DeleteDecoder (ctrlBlock, clientData)
489Trf_ControlBlock ctrlBlock;
490ClientData 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
518static int
519Decode (ctrlBlock, character, interp, clientData)
520Trf_ControlBlock ctrlBlock;
521unsigned int character;
522Tcl_Interp* interp;
523ClientData 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
597static int
598FlushDecoder (ctrlBlock, interp, clientData)
599Trf_ControlBlock ctrlBlock;
600Tcl_Interp* interp;
601ClientData 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
661static void
662ClearDecoder (ctrlBlock, clientData)
663Trf_ControlBlock ctrlBlock;
664ClientData 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