1/*
2 * bz2.c --
3 *
4 *	Implements and registers compressor based on bzip2.
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: bz2.c,v 1.8 2009/05/07 04:57:27 andreas_kupries Exp $
28 */
29
30#include "transformInt.h"
31
32/*
33 * Declarations of internal procedures.
34 */
35
36static Trf_ControlBlock CreateEncoder  _ANSI_ARGS_ ((ClientData writeClientData,
37						     Trf_WriteProc *fun,
38						     Trf_Options optInfo,
39						     Tcl_Interp*   interp,
40						     ClientData clientData));
41static void             DeleteEncoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
42						     ClientData clientData));
43static int              Encode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
44						     unsigned int character,
45						     Tcl_Interp* interp,
46						     ClientData clientData));
47static int              EncodeBuffer   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
48						     unsigned char* buffer, int bufLen,
49						     Tcl_Interp* interp,
50						     ClientData clientData));
51static int              FlushEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
52						     Tcl_Interp* interp,
53						     ClientData clientData));
54static void             ClearEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
55						     ClientData clientData));
56
57static Trf_ControlBlock CreateDecoder  _ANSI_ARGS_ ((ClientData writeClientData,
58						     Trf_WriteProc *fun,
59						     Trf_Options optInfo,
60						     Tcl_Interp*   interp,
61						     ClientData clientData));
62static void             DeleteDecoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
63						     ClientData clientData));
64static int              Decode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
65						     unsigned int character,
66						     Tcl_Interp* interp,
67						     ClientData clientData));
68static int              DecodeBuffer   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
69						     unsigned char* buffer, int bufLen,
70						     Tcl_Interp* interp,
71						     ClientData clientData));
72static int              FlushDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
73						     Tcl_Interp* interp,
74						     ClientData clientData));
75static void             ClearDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
76						     ClientData clientData));
77
78static void Bz2libError _ANSI_ARGS_ ((Tcl_Interp* interp,
79				    bz_stream*  state,
80				    int         errcode,
81				    CONST char* prefix));
82
83
84/*
85 * Converter definition.
86 */
87
88static Trf_TypeDefinition convDefinition =
89{
90  "bz2",
91  NULL, /* client data not used       */
92  NULL, /* filled by TrfInit_ZB2, THREADING: serialize initialization */
93  {
94    CreateEncoder,
95    DeleteEncoder,
96    Encode,
97    EncodeBuffer,
98    FlushEncoder,
99    ClearEncoder,
100    NULL /* no MaxRead */
101  }, {
102    CreateDecoder,
103    DeleteDecoder,
104    Decode,
105    DecodeBuffer,
106    FlushDecoder,
107    ClearDecoder,
108    NULL /* no MaxRead */
109  },
110  TRF_UNSEEKABLE
111};
112
113/*
114 * Definition of the control blocks for en- and decoder.
115 */
116
117typedef struct _EncoderControl_ {
118  Trf_WriteProc* write;
119  ClientData     writeClientData;
120
121  /* add conversion specific items here (BZ2) */
122
123  bz_stream state;	/* compressor state */
124
125  char* output_buffer;
126
127} EncoderControl;
128
129
130typedef struct _DecoderControl_ {
131  Trf_WriteProc* write;
132  ClientData     writeClientData;
133
134  /* add conversion specific items here (BZ2) */
135
136  bz_stream state;	/* decompressor state */
137
138  char* output_buffer;
139
140  int lastRes;
141
142} DecoderControl;
143
144#define KILO     (1024)
145#define OUT_SIZE (32 * KILO)
146
147
148/*
149 *------------------------------------------------------*
150 *
151 *	TrfInit_BZ2 --
152 *
153 *	------------------------------------------------*
154 *	Register the compressor implemented in this file.
155 *	------------------------------------------------*
156 *
157 *	Sideeffects:
158 *		As of 'Trf_Register'.
159 *
160 *	Result:
161 *		A standard Tcl error code.
162 *
163 *------------------------------------------------------*
164 */
165
166int
167TrfInit_BZ2 (interp)
168Tcl_Interp* interp;
169{
170  TrfLock; /* THREADING: serialize initialization */
171  convDefinition.options = TrfBZ2Options ();
172  TrfUnlock;
173
174  return Trf_Register (interp, &convDefinition);
175}
176
177/*
178 *------------------------------------------------------*
179 *
180 *	CreateEncoder --
181 *
182 *	------------------------------------------------*
183 *	Allocate and initialize the control block of a
184 *	data encoder.
185 *	------------------------------------------------*
186 *
187 *	Sideeffects:
188 *		Allocates memory.
189 *
190 *	Result:
191 *		An opaque reference to the control block.
192 *
193 *------------------------------------------------------*
194 */
195
196static Trf_ControlBlock
197CreateEncoder (writeClientData, fun, optInfo, interp, clientData)
198ClientData     writeClientData;
199Trf_WriteProc* fun;
200Trf_Options    optInfo;
201Tcl_Interp*    interp;
202ClientData     clientData;
203{
204  EncoderControl*    c;
205  TrfBz2OptionBlock* o = (TrfBz2OptionBlock*) optInfo;
206  int res;
207
208  c = (EncoderControl*) ckalloc (sizeof (EncoderControl));
209  c->write           = fun;
210  c->writeClientData = writeClientData;
211
212  /* initialize conversion specific items here (BZ2) */
213
214  c->state.bzalloc = NULL;
215  c->state.bzfree  = NULL;
216  c->state.opaque = NULL;
217
218  c->output_buffer = (char*) ckalloc (OUT_SIZE);
219
220  if (c->output_buffer == (char*) NULL) {
221    ckfree ((VOID*) c);
222    return (ClientData) NULL;
223  }
224
225  res = bz.bcompressInit (&c->state, o->level, 0, 0);
226
227  if (res != BZ_OK) {
228    if (interp) {
229      Bz2libError (interp, &c->state, res, "compressor/init");
230    }
231
232    ckfree ((VOID*) c->output_buffer);
233    ckfree ((VOID*) c);
234    return (ClientData) NULL;
235  }
236
237  return (ClientData) c;
238}
239
240/*
241 *------------------------------------------------------*
242 *
243 *	DeleteEncoder --
244 *
245 *	------------------------------------------------*
246 *	Destroy the control block of an encoder.
247 *	------------------------------------------------*
248 *
249 *	Sideeffects:
250 *		Releases the memory allocated by 'CreateEncoder'
251 *
252 *	Result:
253 *		None.
254 *
255 *------------------------------------------------------*
256 */
257
258static void
259DeleteEncoder (ctrlBlock, clientData)
260Trf_ControlBlock ctrlBlock;
261ClientData clientData;
262{
263  EncoderControl* c = (EncoderControl*) ctrlBlock;
264
265  /* release conversion specific items here (BZ2) */
266
267  bz.bcompressEnd (&c->state);
268  ckfree ((char*) c->output_buffer);
269  ckfree ((char*) c);
270}
271
272/*
273 *------------------------------------------------------*
274 *
275 *	Encode --
276 *
277 *	------------------------------------------------*
278 *	Encode the given character and write the result.
279 *	------------------------------------------------*
280 *
281 *	Sideeffects:
282 *		As of the called WriteFun.
283 *
284 *	Result:
285 *		Generated bytes implicitly via WriteFun.
286 *		A standard Tcl error code.
287 *
288 *------------------------------------------------------*
289 */
290
291static int
292Encode (ctrlBlock, character, interp, clientData)
293Trf_ControlBlock ctrlBlock;
294unsigned int character;
295Tcl_Interp* interp;
296ClientData clientData;
297{
298  EncoderControl* c = (EncoderControl*) ctrlBlock;
299
300  /* execute conversion specific code here (BZ2) */
301
302  char in;
303  int res;
304
305  in = character;
306
307  c->state.next_in   = (unsigned char*) (Bytef*) ∈
308  c->state.avail_in  = 1;
309
310  for (;;) {
311    c->state.next_out  = (unsigned char*) (Bytef*) c->output_buffer;
312    c->state.avail_out = OUT_SIZE;
313
314    res = bz.bcompress (&c->state, BZ_RUN);
315
316    if (res < BZ_OK) {
317      if (interp) {
318	Bz2libError (interp, &c->state, res, "compressor");
319      }
320      return TCL_ERROR;
321    }
322
323    if (c->state.avail_out < OUT_SIZE) {
324      res = c->write (c->writeClientData, (unsigned char*) c->output_buffer,
325		      OUT_SIZE - c->state.avail_out, interp);
326      if (res != TCL_OK) {
327	return res;
328      }
329    }
330
331    if (c->state.avail_in > 0)
332      continue;
333
334    if ((c->state.avail_out == 0) && (res == BZ_OK))
335      continue;
336
337    break;
338  }
339
340  return TCL_OK;
341}
342
343/*
344 *------------------------------------------------------*
345 *
346 *	EncodeBuffer --
347 *
348 *	------------------------------------------------*
349 *	Encode the given buffer and write the result.
350 *	------------------------------------------------*
351 *
352 *	Sideeffects:
353 *		As of the called WriteFun.
354 *
355 *	Result:
356 *		Generated bytes implicitly via WriteFun.
357 *		A standard Tcl error code.
358 *
359 *------------------------------------------------------*
360 */
361
362static int
363EncodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData)
364Trf_ControlBlock ctrlBlock;
365unsigned char*   buffer;
366int         bufLen;
367Tcl_Interp* interp;
368ClientData clientData;
369{
370  EncoderControl* c = (EncoderControl*) ctrlBlock;
371
372  /* execute conversion specific code here (BZ2) */
373  int res;
374
375  c->state.next_in   = (unsigned char*) (Bytef*) buffer;
376  c->state.avail_in  = bufLen;
377
378  for (;;) {
379    c->state.next_out  = (unsigned char*) (Bytef*) c->output_buffer;
380    c->state.avail_out = OUT_SIZE;
381
382    res = bz.bcompress (&c->state, BZ_RUN);
383
384    if (res < BZ_OK) {
385      if (interp) {
386	Bz2libError (interp, &c->state, res, "compressor");
387      }
388      return TCL_ERROR;
389    }
390
391    if (c->state.avail_out < OUT_SIZE) {
392      res = c->write (c->writeClientData, (unsigned char*) c->output_buffer,
393		      OUT_SIZE - c->state.avail_out, interp);
394      if (res != TCL_OK) {
395	return res;
396      }
397    }
398
399    if (c->state.avail_in > 0)
400      continue;
401
402    if ((c->state.avail_out == 0) && (res == BZ_OK))
403      continue;
404
405    break;
406  }
407
408  return TCL_OK;
409}
410
411/*
412 *------------------------------------------------------*
413 *
414 *	FlushEncoder --
415 *
416 *	------------------------------------------------*
417 *	Encode an incomplete character sequence (if possible).
418 *	------------------------------------------------*
419 *
420 *	Sideeffects:
421 *		As of the called WriteFun.
422 *
423 *	Result:
424 *		Generated bytes implicitly via WriteFun.
425 *		A standard Tcl error code.
426 *
427 *------------------------------------------------------*
428 */
429
430static int
431FlushEncoder (ctrlBlock, interp, clientData)
432Trf_ControlBlock ctrlBlock;
433Tcl_Interp* interp;
434ClientData clientData;
435{
436  EncoderControl* c = (EncoderControl*) ctrlBlock;
437
438  /* execute conversion specific code here (BZ2) */
439  int res;
440
441  c->state.next_in   = (unsigned char*) (Bytef*) NULL;
442  c->state.avail_in  = 0;
443
444  for (;;) {
445    c->state.next_out  = (unsigned char*) (Bytef*) c->output_buffer;
446    c->state.avail_out = OUT_SIZE;
447
448    res = bz.bcompress (&c->state, BZ_FINISH);
449
450    if (res < BZ_OK) {
451      if (interp) {
452	Bz2libError (interp, &c->state, res, "compressor/flush");
453      }
454      return TCL_ERROR;
455    }
456
457    if (c->state.avail_out < OUT_SIZE) {
458      res = c->write (c->writeClientData, (unsigned char*) c->output_buffer,
459		      OUT_SIZE - c->state.avail_out, interp);
460      if (res != TCL_OK) {
461	return res;
462      }
463    }
464
465    if ((c->state.avail_out == 0) && (res == BZ_OK))
466      continue;
467
468    break;
469  }
470
471  return TCL_OK;
472}
473
474/*
475 *------------------------------------------------------*
476 *
477 *	ClearEncoder --
478 *
479 *	------------------------------------------------*
480 *	Discard an incomplete character sequence.
481 *	------------------------------------------------*
482 *
483 *	Sideeffects:
484 *		See above.
485 *
486 *	Result:
487 *		None.
488 *
489 *------------------------------------------------------*
490 */
491
492static void
493ClearEncoder (ctrlBlock, clientData)
494Trf_ControlBlock ctrlBlock;
495ClientData clientData;
496{
497  /* EncoderControl* c = (EncoderControl*) ctrlBlock; */
498
499  /* execute conversion specific code here (BZ2) */
500
501  /* bz.bcompressReset (&c->state); */
502}
503
504/*
505 *------------------------------------------------------*
506 *
507 *	CreateDecoder --
508 *
509 *	------------------------------------------------*
510 *	Allocate and initialize the control block of a
511 *	data decoder.
512 *	------------------------------------------------*
513 *
514 *	Sideeffects:
515 *		Allocates memory.
516 *
517 *	Result:
518 *		An opaque reference to the control block.
519 *
520 *------------------------------------------------------*
521 */
522
523static Trf_ControlBlock
524CreateDecoder (writeClientData, fun, optInfo, interp, clientData)
525ClientData     writeClientData;
526Trf_WriteProc* fun;
527Trf_Options    optInfo;
528Tcl_Interp*    interp;
529ClientData     clientData;
530{
531  DecoderControl*    c;
532  int res;
533
534  c = (DecoderControl*) ckalloc (sizeof (DecoderControl));
535  c->write           = fun;
536  c->writeClientData = writeClientData;
537
538  /* initialize conversion specific items here (BZ2) */
539
540  c->state.bzalloc = NULL;
541  c->state.bzfree  = NULL;
542  c->state.opaque = NULL;
543
544  c->output_buffer = (char*) ckalloc (OUT_SIZE);
545
546  if (c->output_buffer == (char*) NULL) {
547    ckfree ((VOID*) c);
548    return (ClientData) NULL;
549  }
550
551  res = bz.bdecompressInit (&c->state, 0, 0);
552
553  if (res != BZ_OK) {
554    if (interp) {
555      Bz2libError (interp, &c->state, res, "decompressor/init");
556    }
557
558    ckfree ((VOID*) c->output_buffer);
559    ckfree ((VOID*) c);
560    return (ClientData) NULL;
561  }
562
563  c->lastRes = res;
564
565  return (ClientData) c;
566}
567
568/*
569 *------------------------------------------------------*
570 *
571 *	DeleteDecoder --
572 *
573 *	------------------------------------------------*
574 *	Destroy the control block of an decoder.
575 *	------------------------------------------------*
576 *
577 *	Sideeffects:
578 *		Releases the memory allocated by 'CreateDecoder'
579 *
580 *	Result:
581 *		None.
582 *
583 *------------------------------------------------------*
584 */
585
586static void
587DeleteDecoder (ctrlBlock, clientData)
588Trf_ControlBlock ctrlBlock;
589ClientData clientData;
590{
591  DecoderControl* c = (DecoderControl*) ctrlBlock;
592
593  /* release conversion specific items here (BZ2) */
594
595  bz.bdecompressEnd (&c->state);
596
597  ckfree ((char*) c->output_buffer);
598  ckfree ((char*) c);
599}
600
601/*
602 *------------------------------------------------------*
603 *
604 *	Decode --
605 *
606 *	------------------------------------------------*
607 *	Decode the given character and write the result.
608 *	------------------------------------------------*
609 *
610 *	Sideeffects:
611 *		As of the called WriteFun.
612 *
613 *	Result:
614 *		Generated bytes implicitly via WriteFun.
615 *		A standard Tcl error code.
616 *
617 *------------------------------------------------------*
618 */
619
620static int
621Decode (ctrlBlock, character, interp, clientData)
622Trf_ControlBlock ctrlBlock;
623unsigned int character;
624Tcl_Interp* interp;
625ClientData clientData;
626{
627  DecoderControl* c = (DecoderControl*) ctrlBlock;
628
629  /* execute conversion specific code here (BZ2) */
630  char in;
631  int res;
632
633  in = character;
634
635  c->state.next_in   = (unsigned char*) (Bytef*) &in;
636  c->state.avail_in  = 1;
637
638  for (;;) {
639    c->state.next_out  = (unsigned char*) (Bytef*) c->output_buffer;
640    c->state.avail_out = OUT_SIZE;
641
642    res = bz.bdecompress (&c->state);
643    c->lastRes = res;
644
645    if ((res < BZ_OK) && (res != BZ_STREAM_END)) {
646      if (interp) {
647	Bz2libError (interp, &c->state, res, "decompressor");
648      }
649      return TCL_ERROR;
650    }
651
652    if (c->state.avail_out < OUT_SIZE) {
653      res = c->write (c->writeClientData, (unsigned char*) c->output_buffer,
654		      OUT_SIZE - c->state.avail_out, interp);
655      if (res != TCL_OK) {
656	return res;
657      }
658    }
659
660    if (c->state.avail_in > 0)
661      continue;
662
663    if ((c->state.avail_out == 0) && (res == BZ_OK))
664      continue;
665
666    break;
667  }
668
669  return TCL_OK;
670}
671
672/*
673 *------------------------------------------------------*
674 *
675 *	DecodeBuffer --
676 *
677 *	------------------------------------------------*
678 *	Decode the given buffer and write the result.
679 *	------------------------------------------------*
680 *
681 *	Sideeffects:
682 *		As of the called WriteFun.
683 *
684 *	Result:
685 *		Generated bytes implicitly via WriteFun.
686 *		A standard Tcl error code.
687 *
688 *------------------------------------------------------*
689 */
690
691static int
692DecodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData)
693Trf_ControlBlock ctrlBlock;
694unsigned char* buffer;
695int bufLen;
696Tcl_Interp* interp;
697ClientData clientData;
698{
699  DecoderControl* c = (DecoderControl*) ctrlBlock;
700
701  /* execute conversion specific code here (BZ2) */
702  int res;
703
704  c->state.next_in   = (unsigned char*) (Bytef*) buffer;
705  c->state.avail_in  = bufLen;
706
707  for (;;) {
708    c->state.next_out  = (unsigned char*) (Bytef*) c->output_buffer;
709    c->state.avail_out = OUT_SIZE;
710
711    res = bz.bdecompress (&c->state);
712    c->lastRes = res;
713
714    if ((res < BZ_OK) && (res != BZ_STREAM_END)) {
715      if (interp) {
716	Bz2libError (interp, &c->state, res, "decompressor");
717      }
718      return TCL_ERROR;
719    }
720
721    if (c->state.avail_out < OUT_SIZE) {
722      res = c->write (c->writeClientData, (unsigned char*) c->output_buffer,
723		      OUT_SIZE - c->state.avail_out, interp);
724      if (res != TCL_OK) {
725	return res;
726      }
727    }
728
729    if (c->state.avail_in > 0)
730      continue;
731
732    if ((c->state.avail_out == 0) && (res == BZ_OK))
733      continue;
734
735    break;
736  }
737
738  return TCL_OK;
739}
740
741/*
742 *------------------------------------------------------*
743 *
744 *	FlushDecoder --
745 *
746 *	------------------------------------------------*
747 *	Decode an incomplete character sequence (if possible).
748 *	------------------------------------------------*
749 *
750 *	Sideeffects:
751 *		As of the called WriteFun.
752 *
753 *	Result:
754 *		Generated bytes implicitly via WriteFun.
755 *		A standard Tcl error code.
756 *
757 *------------------------------------------------------*
758 */
759
760static int
761FlushDecoder (ctrlBlock, interp, clientData)
762Trf_ControlBlock ctrlBlock;
763Tcl_Interp* interp;
764ClientData clientData;
765{
766  DecoderControl* c = (DecoderControl*) ctrlBlock;
767
768  /* execute conversion specific code here (BZ2) */
769  int res;
770
771  if (c->lastRes == BZ_STREAM_END) {
772    /* Essentially already flushed ! */
773    return TCL_OK;
774  }
775
776  c->state.next_in  = (unsigned char*) (Bytef*) c->output_buffer; /* fake out
777								   * 'inflate'
778								   */
779  c->state.avail_in = 0;
780
781  for (;;) {
782    c->state.next_out  = (unsigned char*) (Bytef*) c->output_buffer;
783    c->state.avail_out = OUT_SIZE;
784
785    res = bz.bdecompress (&c->state);
786
787    if ((res < BZ_OK) && (res != BZ_STREAM_END)) {
788      if (interp) {
789	Bz2libError (interp, &c->state, res, "decompressor/flush");
790      }
791      return TCL_ERROR;
792    }
793
794    if (c->state.avail_out < OUT_SIZE) {
795      res = c->write (c->writeClientData, (unsigned char*) c->output_buffer,
796		      OUT_SIZE - c->state.avail_out, interp);
797      if (res != TCL_OK) {
798	return res;
799      }
800    }
801
802    if ((c->state.avail_out == 0) && (res == BZ_OK))
803      continue;
804
805    break;
806  }
807
808  return TCL_OK;
809}
810
811/*
812 *------------------------------------------------------*
813 *
814 *	ClearDecoder --
815 *
816 *	------------------------------------------------*
817 *	Discard an incomplete character sequence.
818 *	------------------------------------------------*
819 *
820 *	Sideeffects:
821 *		See above.
822 *
823 *	Result:
824 *		None.
825 *
826 *------------------------------------------------------*
827 */
828
829static void
830ClearDecoder (ctrlBlock, clientData)
831Trf_ControlBlock ctrlBlock;
832ClientData clientData;
833{
834  /*  DecoderControl* c = (DecoderControl*) ctrlBlock; */
835
836  /* execute conversion specific code here (BZ2) */
837
838  /* bz.bdecompressReset (&c->state); */
839}
840
841/*
842 *------------------------------------------------------*
843 *
844 *	Bz2libError --
845 *
846 *	------------------------------------------------*
847 *	Append error message from bzlib-state to interpreter
848 *	------------------------------------------------*
849 *
850 *	Sideeffects:
851 *		See above.
852 *
853 *	Result:
854 *		None.
855 *
856 *------------------------------------------------------*
857 */
858
859static void
860Bz2libError (interp, state, errcode, prefix)
861Tcl_Interp* interp;
862bz_stream*   state;
863int         errcode;
864CONST char* prefix;
865{
866    CONST char* msg;
867
868    /*
869     * A table-lookup might have been nicer, but this
870     * is more secure against changes of the codevalues
871     * used by bzlib.
872     */
873    switch (errcode) {
874    case BZ_MEM_ERROR:
875      msg =  "not enough memory available";
876      break;
877
878    case BZ_SEQUENCE_ERROR:
879      msg =  "sequence error";
880      break;
881
882    case BZ_PARAM_ERROR:
883      msg =  "param error";
884      break;
885
886    case BZ_DATA_ERROR:
887      msg = "incoming data corrupted";
888      break;
889
890    case BZ_DATA_ERROR_MAGIC:
891      msg = "magic number corrupted";
892      break;
893
894    case BZ_IO_ERROR:
895      msg = "io error";
896      break;
897
898    case BZ_UNEXPECTED_EOF:
899      msg = "unexpected eof";
900      break;
901
902    case BZ_OUTBUFF_FULL:
903      msg = "output buffer full";
904      break;
905
906    default:
907      msg = "?";
908      break;
909    }
910
911  Tcl_AppendResult (interp, "bz2lib error (", (char*) NULL);
912  Tcl_AppendResult (interp, prefix, (char*) NULL);
913  Tcl_AppendResult (interp, "): ", (char*) NULL);
914  Tcl_AppendResult (interp, msg, (char*) NULL);
915}
916