1/*
2 * hexcode.c --
3 *
4 *	Implements and registers conversion from and to hexadecimal 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: hexcode.c,v 1.12 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 *	Every byte is converted into 2 characters, using elements
38 *	in the set {0-9,a-z} only. Thus a hexadecimal representation is
39 *	generated. The MSBit is output first.
40 *
41 * Decoding:
42 *	Only characters in the set {0-9a-zA-Z} are allowed as input.
43 *	Each 2-tuple is converted into a single byte. A single character
44 *	at the end of input is converted as if padded with "0".
45 */
46
47
48/*
49 * Declarations of internal procedures.
50 */
51
52static Trf_ControlBlock
53CreateEncoder  _ANSI_ARGS_ ((ClientData writeClientData, Trf_WriteProc *fun,
54			     Trf_Options optInfo, Tcl_Interp*   interp,
55			     ClientData clientData));
56static void
57DeleteEncoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
58			     ClientData clientData));
59static int
60Encode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
61			     unsigned int character,
62			     Tcl_Interp* interp,
63			     ClientData clientData));
64static int
65EncodeBuffer   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
66			     unsigned char* buffer,
67			     int bufLen,
68			     Tcl_Interp* interp,
69			     ClientData clientData));
70static int
71FlushEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
72			     Tcl_Interp* interp,
73			     ClientData clientData));
74static void
75ClearEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
76			     ClientData clientData));
77
78
79static Trf_ControlBlock
80CreateDecoder  _ANSI_ARGS_ ((ClientData writeClientData, Trf_WriteProc *fun,
81			     Trf_Options optInfo, Tcl_Interp*   interp,
82			     ClientData clientData));
83static void
84DeleteDecoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
85			     ClientData clientData));
86static int
87Decode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
88			     unsigned int character,
89			     Tcl_Interp* interp,
90			     ClientData clientData));
91static int
92DecodeBuffer   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
93			     unsigned char* buffer,
94			     int bufLen,
95			     Tcl_Interp* interp,
96			     ClientData clientData));
97static int
98FlushDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
99			     Tcl_Interp* interp,
100			     ClientData clientData));
101static void
102ClearDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
103			     ClientData clientData));
104
105
106/*
107 * Converter definition.
108 */
109
110static Trf_TypeDefinition convDefinition =
111{
112  "hex",
113  NULL, /* clientData not sed by converters */
114  NULL, /* set by 'TrfInit_Hex'. THREAD: serialize initialization */
115  {
116    CreateEncoder,
117    DeleteEncoder,
118    Encode,
119    EncodeBuffer,
120    FlushEncoder,
121    ClearEncoder,
122    NULL /* no MaxRead */
123  }, {
124    CreateDecoder,
125    DeleteDecoder,
126    Decode,
127    DecodeBuffer,
128    FlushDecoder,
129    ClearDecoder,
130    NULL /* no MaxRead */
131  },
132  TRF_RATIO (1, 2)
133};
134
135/*
136 * Definition of the control blocks for en- and decoder.
137 */
138
139typedef struct _EncoderControl_ {
140  Trf_WriteProc* write;
141  ClientData     writeClientData;
142
143} EncoderControl;
144
145
146typedef struct _DecoderControl_ {
147  Trf_WriteProc* write;
148  ClientData     writeClientData;
149
150  unsigned char charCount;  /* number of characters assembled so far (0..1) */
151  unsigned char bench;      /* buffer for assembled byte */
152
153} DecoderControl;
154
155
156/*
157 * Use table lookup
158 */
159
160static const char* code [] = { /* THREADING: constant, read-only => safe */
161  "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
162  "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
163  "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
164  "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
165  "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
166  "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
167  "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
168  "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
169  "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
170  "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
171  "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
172  "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
173  "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
174  "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
175  "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
176  "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF",
177};
178
179
180/*
181 *------------------------------------------------------*
182 *
183 *	TrfInit_Hex --
184 *
185 *	------------------------------------------------*
186 *	Register the conversion implemented in this file.
187 *	------------------------------------------------*
188 *
189 *	Sideeffects:
190 *		As of 'Trf_Register'.
191 *
192 *	Result:
193 *		A standard Tcl error code.
194 *
195 *------------------------------------------------------*
196 */
197
198int
199TrfInit_Hex (interp)
200Tcl_Interp* interp;
201{
202  TrfLock; /* THREADING: serialize initialization */
203  convDefinition.options = Trf_ConverterOptions ();
204  TrfUnlock;
205
206  return Trf_Register (interp, &convDefinition);
207}
208
209/*
210 *------------------------------------------------------*
211 *
212 *	CreateEncoder --
213 *
214 *	------------------------------------------------*
215 *	Allocate and initialize the control block of a
216 *	data encoder.
217 *	------------------------------------------------*
218 *
219 *	Sideeffects:
220 *		Allocates memory.
221 *
222 *	Result:
223 *		An opaque reference to the control block.
224 *
225 *------------------------------------------------------*
226 */
227
228static Trf_ControlBlock
229CreateEncoder (writeClientData, fun, optInfo, interp, clientData)
230ClientData    writeClientData;
231Trf_WriteProc *fun;
232Trf_Options   optInfo;
233Tcl_Interp*   interp;
234ClientData clientData;
235{
236  EncoderControl* c;
237
238  c = (EncoderControl*) ckalloc (sizeof (EncoderControl));
239  c->write           = fun;
240  c->writeClientData = writeClientData;
241
242  return (ClientData) c;
243}
244
245/*
246 *------------------------------------------------------*
247 *
248 *	DeleteEncoder --
249 *
250 *	------------------------------------------------*
251 *	Destroy the control block of an encoder.
252 *	------------------------------------------------*
253 *
254 *	Sideeffects:
255 *		Releases the memory allocated by 'CreateEncoder'
256 *
257 *	Result:
258 *		None.
259 *
260 *------------------------------------------------------*
261 */
262
263static void
264DeleteEncoder (ctrlBlock, clientData)
265Trf_ControlBlock ctrlBlock;
266ClientData clientData;
267{
268  EncoderControl* c = (EncoderControl*) ctrlBlock;
269
270  ckfree ((char*) c);
271}
272
273/*
274 *------------------------------------------------------*
275 *
276 *	Encode --
277 *
278 *	------------------------------------------------*
279 *	Encode the given character and write the result.
280 *	------------------------------------------------*
281 *
282 *	Sideeffects:
283 *		As of the called WriteFun.
284 *
285 *	Result:
286 *		Generated bytes implicitly via WriteFun.
287 *		A standard Tcl error code.
288 *
289 *------------------------------------------------------*
290 */
291
292static int
293Encode (ctrlBlock, character, interp, clientData)
294Trf_ControlBlock ctrlBlock;
295unsigned int character;
296Tcl_Interp* interp;
297ClientData clientData;
298{
299  EncoderControl* c = (EncoderControl*) ctrlBlock;
300
301#if 0
302  unsigned char   buffer [2];
303
304  char high = (character >> 4) & 0x0F;
305  char low  =  character       & 0x0F;
306
307  high += ((high < 10) ? '0' : 'A'-10);
308  low  += ((low  < 10) ? '0' : 'A'-10);
309
310  buffer [0] = high;
311  buffer [1] = low;
312
313  return c->write (c->writeClientData, buffer, 2, interp);
314#endif
315
316  return c->write (c->writeClientData, (unsigned char*) code [character & 0x00ff], 2, interp);
317}
318
319/*
320 *------------------------------------------------------*
321 *
322 *	EncodeBuffer --
323 *
324 *	------------------------------------------------*
325 *	Encode the given buffer and write the result.
326 *	------------------------------------------------*
327 *
328 *	Sideeffects:
329 *		As of the called WriteFun.
330 *
331 *	Result:
332 *		Generated bytes implicitly via WriteFun.
333 *		A standard Tcl error code.
334 *
335 *------------------------------------------------------*
336 */
337
338static int
339EncodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData)
340Trf_ControlBlock ctrlBlock;
341unsigned char*   buffer;
342int         bufLen;
343Tcl_Interp* interp;
344ClientData clientData;
345{
346  EncoderControl* c   = (EncoderControl*) ctrlBlock;
347  char*  out = (char*) ckalloc (2*bufLen+1);
348  int    res, i, j;
349  CONST char*  ch;
350
351  for (i=0, j=0; i < bufLen; i++) {
352    ch = code [buffer [i] & 0x00ff];
353    out [j] = ch [0]; j++;
354    out [j] = ch [1]; j++;
355  }
356  out [j] = '\0';
357
358  res = c->write (c->writeClientData, (unsigned char*) out, 2*bufLen, interp);
359
360  ckfree ((char*) out);
361  return res;
362}
363
364/*
365 *------------------------------------------------------*
366 *
367 *	FlushEncoder --
368 *
369 *	------------------------------------------------*
370 *	Encode an incomplete character sequence (if possible).
371 *	------------------------------------------------*
372 *
373 *	Sideeffects:
374 *		As of the called WriteFun.
375 *
376 *	Result:
377 *		Generated bytes implicitly via WriteFun.
378 *		A standard Tcl error code.
379 *
380 *------------------------------------------------------*
381 */
382
383static int
384FlushEncoder (ctrlBlock, interp, clientData)
385Trf_ControlBlock ctrlBlock;
386Tcl_Interp* interp;
387ClientData clientData;
388{
389  /* nothing to to */
390  return TCL_OK;
391}
392
393/*
394 *------------------------------------------------------*
395 *
396 *	ClearEncoder --
397 *
398 *	------------------------------------------------*
399 *	Discard an incomplete character sequence.
400 *	------------------------------------------------*
401 *
402 *	Sideeffects:
403 *		See above.
404 *
405 *	Result:
406 *		None.
407 *
408 *------------------------------------------------------*
409 */
410
411static void
412ClearEncoder (ctrlBlock, clientData)
413Trf_ControlBlock ctrlBlock;
414ClientData clientData;
415{
416  /* nothing to do */
417}
418
419/*
420 *------------------------------------------------------*
421 *
422 *	CreateDecoder --
423 *
424 *	------------------------------------------------*
425 *	Allocate and initialize the control block of a
426 *	data decoder.
427 *	------------------------------------------------*
428 *
429 *	Sideeffects:
430 *		Allocates memory.
431 *
432 *	Result:
433 *		An opaque reference to the control block.
434 *
435 *------------------------------------------------------*
436 */
437
438static Trf_ControlBlock
439CreateDecoder (writeClientData, fun, optInfo, interp, clientData)
440ClientData    writeClientData;
441Trf_WriteProc *fun;
442Trf_Options   optInfo;
443Tcl_Interp*   interp;
444ClientData clientData;
445{
446  DecoderControl* c;
447
448  c = (DecoderControl*) ckalloc (sizeof (DecoderControl));
449  c->write           = fun;
450  c->writeClientData = writeClientData;
451
452  c->charCount = 0;
453  c->bench     = '\0';
454
455  return (ClientData) c;
456}
457
458/*
459 *------------------------------------------------------*
460 *
461 *	DeleteDecoder --
462 *
463 *	------------------------------------------------*
464 *	Destroy the control block of an decoder.
465 *	------------------------------------------------*
466 *
467 *	Sideeffects:
468 *		Releases the memory allocated by 'CreateDecoder'
469 *
470 *	Result:
471 *		None.
472 *
473 *------------------------------------------------------*
474 */
475
476static void
477DeleteDecoder (ctrlBlock, clientData)
478Trf_ControlBlock ctrlBlock;
479ClientData clientData;
480{
481  DecoderControl* c = (DecoderControl*) ctrlBlock;
482
483  ckfree ((char*) c);
484}
485
486/*
487 *------------------------------------------------------*
488 *
489 *	Decode --
490 *
491 *	------------------------------------------------*
492 *	Decode the given character and write the result.
493 *	------------------------------------------------*
494 *
495 *	Sideeffects:
496 *		As of the called WriteFun.
497 *
498 *	Result:
499 *		Generated bytes implicitly via WriteFun.
500 *		A standard Tcl error code.
501 *
502 *------------------------------------------------------*
503 */
504
505static int
506Decode (ctrlBlock, character, interp, clientData)
507Trf_ControlBlock ctrlBlock;
508unsigned int character;
509Tcl_Interp* interp;
510ClientData clientData;
511{
512  DecoderControl* c      = (DecoderControl*) ctrlBlock;
513  unsigned char   nibble = character;
514
515#define IN_RANGE(low,x,high) (((low) <= (x)) && ((x) <= (high)))
516
517  if (IN_RANGE ('0', nibble, '9'))
518    nibble -= '0';
519  else if (IN_RANGE ('a', nibble, 'f'))
520    nibble -= 'a'-10;
521  else if (IN_RANGE ('A', nibble, 'F'))
522    nibble -= 'A'-10;
523  else {
524    if (interp) {
525      char buf [10];
526
527      if (character < ' ' || character > 127) {
528	sprintf (buf, "0x%02x", character);
529      } else {
530	buf [0] = '\'';
531	buf [1] = character;
532	buf [2] = '\'';
533	buf [3] = '\0';
534      }
535
536      Tcl_ResetResult  (interp);
537      Tcl_AppendResult (interp, "illegal character ", buf,
538			" found in input", (char*) NULL);
539    }
540    return TCL_ERROR;
541  }
542
543  c->bench |= (nibble << (4 * (1- c->charCount)));
544  c->charCount ++;
545
546  if (c->charCount >= 2) {
547    int res = c->write (c->writeClientData, &c->bench, 1, interp);
548
549    c->bench     = '\0';
550    c->charCount = 0;
551
552    return res;
553  }
554
555  return TCL_OK;
556
557#undef IN_RANGE
558}
559
560/*
561 *------------------------------------------------------*
562 *
563 *	DecodeBuffer --
564 *
565 *	------------------------------------------------*
566 *	Decode the given buffer and write the result.
567 *	------------------------------------------------*
568 *
569 *	Sideeffects:
570 *		As of the called WriteFun.
571 *
572 *	Result:
573 *		Generated bytes implicitly via WriteFun.
574 *		A standard Tcl error code.
575 *
576 *------------------------------------------------------*
577 */
578
579static int
580DecodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData)
581Trf_ControlBlock ctrlBlock;
582unsigned char*   buffer;
583int              bufLen;
584Tcl_Interp*      interp;
585ClientData       clientData;
586{
587#define IN_RANGE(low,x,high) (((low) <= (x)) && ((x) <= (high)))
588
589  DecoderControl* c   = (DecoderControl*) ctrlBlock;
590  char*  out = (char*) ckalloc (1+bufLen/2);
591  int    res, i, j;
592  unsigned char nibble;
593
594  for (i=0, j=0; i < bufLen; i++) {
595    nibble = buffer [i];
596
597    if (IN_RANGE ('0', nibble, '9'))
598      nibble -= '0';
599    else if (IN_RANGE ('a', nibble, 'f'))
600      nibble -= 'a' - 10;
601    else if (IN_RANGE ('A', nibble, 'F'))
602      nibble -= 'A' - 10;
603    else {
604      if (interp) {
605	char buf [10];
606
607	if (nibble < ' ' || nibble > 127) {
608	  sprintf (buf, "0x%02x", nibble);
609	} else {
610	  buf [0] = '\'';
611	  buf [1] = nibble;
612	  buf [2] = '\'';
613	  buf [3] = '\0';
614	}
615
616	Tcl_ResetResult  (interp);
617	Tcl_AppendResult (interp, "illegal character ", buf,
618			  " found in input", (char*) NULL);
619      }
620
621      ckfree (out);
622      return TCL_ERROR;
623    }
624
625    c->bench |= (nibble << (4 * (1 - c->charCount)));
626    c->charCount ++;
627
628    if (c->charCount >= 2) {
629      out [j] = c->bench;
630
631      c->bench     = '\0';
632      c->charCount = 0;
633
634      j ++;
635    }
636  }
637
638  res = c->write (c->writeClientData, (unsigned char*) out, j, interp);
639  return res;
640
641#undef IN_RANGE
642}
643
644/*
645 *------------------------------------------------------*
646 *
647 *	FlushDecoder --
648 *
649 *	------------------------------------------------*
650 *	Decode an incomplete character sequence (if possible).
651 *	------------------------------------------------*
652 *
653 *	Sideeffects:
654 *		As of the called WriteFun.
655 *
656 *	Result:
657 *		Generated bytes implicitly via WriteFun.
658 *		A standard Tcl error code.
659 *
660 *------------------------------------------------------*
661 */
662
663static int
664FlushDecoder (ctrlBlock, interp, clientData)
665Trf_ControlBlock ctrlBlock;
666Tcl_Interp* interp;
667ClientData clientData;
668{
669  DecoderControl* c = (DecoderControl*) ctrlBlock;
670  int           res = TCL_OK;
671
672  if (c->charCount > 0) {
673    res = c->write (c->writeClientData, &c->bench, 1, interp);
674
675    c->bench     = '\0';
676    c->charCount = 0;
677  }
678
679  return res;
680}
681
682/*
683 *------------------------------------------------------*
684 *
685 *	ClearDecoder --
686 *
687 *	------------------------------------------------*
688 *	Discard an incomplete character sequence.
689 *	------------------------------------------------*
690 *
691 *	Sideeffects:
692 *		See above.
693 *
694 *	Result:
695 *		None.
696 *
697 *------------------------------------------------------*
698 */
699
700static void
701ClearDecoder (ctrlBlock, clientData)
702Trf_ControlBlock ctrlBlock;
703ClientData clientData;
704{
705  DecoderControl* c = (DecoderControl*) ctrlBlock;
706
707  c->bench     = '\0';
708  c->charCount = 0;
709}
710