/*
Copyright (c) 2000-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file	dkof.c	Output filtering module.
*/



/**	Inside the dkof module.
*/
#define DK_OF_C	1

#include "dkof.h"
#include "dkstream.h"
#include "dkmem.h"
#include "dkerror.h"
#include "dksto.h"




#line 57 "dkof.ctr"




/**	Convert size_t to unsigned long.
*/
#define TR_UL(x) ((unsigned long)x)



/**	LZW flag: Have a table entry.
*/
#define LZW_FLAG_HAVE_STRING	1

/**	LZW flag: Error occured.
*/
#define LZW_FLAG_ERROR_OCCURED	2



/**	LZW compression table entry.
*/
typedef struct {
  dk_uword	r;	/**< Result of combintation \a s+c. */
  dk_uword 	s;	/**< Previous combination. */
  unsigned char c;	/**< Current input. */
} lzw_entry;



static size_t
add_to_cell DK_PR((dk_of_t *o,dk_of_cell_t *c,size_t n,char *b,size_t l));



/**	Coefficients used for ASCII-85 encoding.
*/
static unsigned long a85[] = {
  (85UL * 85UL * 85UL * 85UL),
  (85UL * 85UL * 85UL),
  (85UL * 85UL),
  85UL,
  1UL
};



/**	Characters to show hexadecimal numbers.
*/
static char hex_digits[] = { "0123456789ABCDEF" };


/**	Line end with carriage return.
*/
static char eol1[] = { "\r\n" };

/**	Line end without carriage return.
*/
static char eol2[] = { "\n" };

/**	Finalizer for ASCII-85 encoded data.
*/
static char a85end[] = { "~>" };

/**	Finalizer for ASCII-Hex encoded data.
*/
static char ahend[] = { ">" };


#if 0
static unsigned short bits[] = {
  0x0001,
  0x0002,
  0x0004,
  0x0008,
  0x0010,
  0x0020,
  0x0040,
  0x0080,
  0x0100,
  0x0200,
  0x0400,
  0x0800,
  0x1000
};
#endif



/**	Bit masks to mask trailing bits.
*/
static unsigned char mask_trailing_bits[] = {
/* 1111 1111 */	0xFF,
/* 1111 1110 */	0xFE,
/* 1111 1100 */ 0xFC,
/* 1111 1000 */ 0xF8,
/* 1111 0000 */ 0xF0,
/* 1110 0000 */ 0xE0,
/* 1100 0000 */ 0xC0,
/* 1000 0000 */ 0x80,
/* 0000 0000 */ 0x00
};



/**	Bit masks to mask leading bits.
*/
static unsigned char mask_leading_bits[] = {
/* 0000 0000 */ 0x00,
/* 0000 0001 */ 0x01,
/* 0000 0011 */ 0x03,
/* 0000 0111 */ 0x07,
/* 0000 1111 */ 0x0f,
/* 0001 1111 */ 0x1f,
/* 0011 1111 */ 0x3f,
/* 0111 1111 */ 0x7f,
/* 1111 1111 */ 0xff
};



/**	Shift \a c \a n bits to the left and reset the trailing bits.
*/
#define SHIFTBITS(c,n) (n ? (\
(c << n) & mask_trailing_bits[n]\
) : c)


/**	Get the last \a r bits from \a c shifted \a (n-r) to the right.
*/
#define GETBITS(c,n,r) (n ? (r ? (\
(unsigned char)(((n > r) ? (c >> (n - r)) : c) & mask_leading_bits[r])\
) : 0) : 0)




/**	Write data from a cell downwards into the filter chain.
	@param	o	Output filter.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static size_t
add_from_cell DK_P4(dk_of_t *,o,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  
  if(n) {	
    back = add_to_cell(o,&((o->cells)[(n-1)]),(n-1),b,l);
    
  } else {	
    if(o->target) {
      back = dkstream_write(o->target, b, l);
      
    } else {	
    }
  }
  
  return back;
}



#if DK_HAVE_ZLIB_H

/**	Write data into a filter cell (not final write operation).
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	1 on success, 0 on error.
*/
static int
write_flate_not_final DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  int err, cc;
  uLong odiff;
  size_t sz;
  
  cc = 1;
  ((c->c).flate.zs)->next_in = (c->c).flate.ibuffer;
  ((c->c).flate.zs)->avail_in = (c->c).flate.used;
  back = 1;
  while(cc) {
    cc = 0;
    ((c->c).flate.zs)->next_out = (c->c).flate.obuffer;
    ((c->c).flate.zs)->avail_out = (c->c).flate.o_length;
    err = deflate((c->c).flate.zs, 0);
    switch(err) {
      case Z_OK: {
        if(((c->c).flate.zs)->avail_in) {
	  cc = 1;
	}
	odiff = (c->c).flate.o_length - ((c->c).flate.zs)->avail_out;
	sz = (size_t)odiff;
	if(add_from_cell(o,n,(char *)((c->c).flate.obuffer),sz) != sz) {
	  back = 0;
	}
      } break;
      default: {
        back = 0; (c->c).flate.stillok = -1;
      } break;
    }
  }
  
  return back;
}



/**	Write data into a flate compressing cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static size_t
add_to_flate_cell DK_P5(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  char *ptr; size_t i;
  
  if(((c->c).flate.zs) && ((c->c).flate.ibuffer) && ((c->c).flate.obuffer)) {
    if(((c->c).flate.i_length) && ((c->c).flate.o_length)) {
      if(((c->c).flate.stillok) == 1) {
        ptr = b;
	for(i = 0; i < l; i++) {
	  ((c->c).flate.ibuffer)[(c->c).flate.used] = (Bytef)(*(ptr++));
	  (c->c).flate.used += (uLong)1UL;
	  if(((c->c).flate.used) >= ((c->c).flate.i_length)) {
	    if(write_flate_not_final(o,c,n)) {
	      back++;
	    }
	    (c->c).flate.used = (uLong)0UL;
	  } else {
	    back++;
	  }
	}
      } else {	
      }
    } else {	
    }
  } else {	
  }
  
  return back;
}
#endif



/**	Write data into an ASCII-85 encoding cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error
*/
static int
write_a85_buffer DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  char buffer[6];
  unsigned long v, ul;
  unsigned char uc;
  size_t i;
  
  v = (c->c).a85.a85val;
  for(i = 0; i < 5; i++) {
    ul = 33UL + v / a85[i];
    v  = v % a85[i];
    uc = (unsigned char)((unsigned)ul);
    buffer[i] = uc;
  }
  if(add_from_cell(o,n,buffer,(1 + (c->c).a85.used)) == (1 + (c->c).a85.used)) {
    back = 1;
  }
  (c->c).a85.linepos += (1 + (c->c).a85.used);
  if(((c->c).a85.maxlinepos) > 0) {
    if(((c->c).a85.linepos) >= (c->c).a85.maxlinepos) {
      if((o->flags) & 1) {
        if(add_from_cell(o,n,eol1,2) != 2) {
          back = 0;
        }
      } else {
        if(add_from_cell(o,n,eol2,1) != 1) {
          back = 0;
        }
      }
      (c->c).a85.linepos = 0;
    }
  }
  (c->c).a85.a85val = 0UL;
  (c->c).a85.used = 0;
  
  return back;
}



#if DK_HAVE_LZW

/**	Clear LZW compression table of LZW compressing cell.
	@param	c	Filter cell.
*/
static
void
clear_lzw_table DK_P1(dk_of_cell_t *,c)
{
  lzw_entry *ep;
  
  if((c->c).lzw.table) {
    if((c->c).lzw.ti) {
      dksto_it_reset((c->c).lzw.ti);
      while((ep = (lzw_entry *)dksto_it_next((c->c).lzw.ti)) != NULL) {
        dk_delete(ep);
      }
      dksto_remove_all((c->c).lzw.table);
    }
  }
  (c->c).lzw.ob = 9; (c->c).lzw.lte = 257;
  
}



/**	Add output bit to an LZW compressing cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	i	Bit value.
	@return	1 on success, 0 on error.
*/
static
int
lzw_output_bit DK_P4(dk_of_t *,o, dk_of_cell_t *,c, size_t,n, int, i)
{
  int back = 1;
  char buffer[2];
  
  (c->c).lzw.oc = (c->c).lzw.oc << 1;
  if(i) {
    (c->c).lzw.oc |= 0x01;
  } else {
    (c->c).lzw.oc &= 0xFE;
  }
  
  (c->c).lzw.bitsused += 1;
  if((c->c).lzw.bitsused >= 8) {
    
    buffer[0] = (char)((c->c).lzw.oc);
    buffer[1] = 0x00; 
    if(add_from_cell(o, n, buffer, 1) != 1) {
      
      back = 0;
    }
    (c->c).lzw.bitsused = 0;
  }
  
  return back;
}



/**	Output a string produced by LZW compression.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	chr	9-to-12 bit character to add.
	@return	1 on success, 0 on error.
*/
static
int
lzw_output_string DK_P4(dk_of_t *,o, dk_of_cell_t *,c, size_t,n, dk_uword,chr)
{
  int back = 1;		/* 1 indicates success, 0 error */
  unsigned char	buffer;	/* buffer character */
  unsigned short bu;
  unsigned short br;	/* number of output bits remaining */
  unsigned short itl;	/* number of bits to handle in current loop */

  br = (c->c).lzw.ob;
  buffer = (c->c).lzw.oc; bu = (c->c).lzw.bitsused;
  do {
    itl = 8 - bu;
    itl = ((itl > br) ? br : itl);
    /* change buffer */
    buffer = SHIFTBITS(buffer,itl) | GETBITS(chr,br,itl);
    br -= itl; bu += itl;
    if(bu >= 8) {
      bu = 0;
      if(add_from_cell(o, n, (char *)(&buffer), 1) != 1) {
        back = 0;
      }
      buffer = 0x00;
    }
  } while((br > 0) && (back));
  (c->c).lzw.oc = buffer; (c->c).lzw.bitsused = bu;

  return back;
}



/**	Add one byte to an LZW compressing cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	chr	Character to add.
	@return	1 on success, 0 on error.
*/
static
int
add_byte_to_lzw DK_P4(dk_of_t *,o, dk_of_cell_t *,c, size_t,n, unsigned char,chr)
{
  int back = 0;
  lzw_entry lzwe, *lzwptr;
  
  if(((c->c).lzw.table) && ((c->c).lzw.ti)) {
      if(((c->c).lzw.fl) & LZW_FLAG_HAVE_STRING) {
        lzwe.s = (c->c).lzw.s;
        lzwe.c = chr;
        lzwptr = dksto_it_find_like((c->c).lzw.ti, (void *)(&lzwe), 0);
        if(lzwptr) {		
          (c->c).lzw.s = lzwptr->r;
	  (c->c).lzw.fl |= LZW_FLAG_HAVE_STRING;
	  back = 1;
        } else {		
          lzwptr = dk_new(lzw_entry,1);
	  if(lzwptr) {
	    lzwptr->s = (c->c).lzw.s;
	    lzwptr->c = chr;
	    (c->c).lzw.lte = (c->c).lzw.lte + 1;
	    lzwptr->r = (c->c).lzw.lte;
	    if(dksto_add((c->c).lzw.table, (void *)lzwptr)) {
	      if(lzw_output_string(o, c, n, (c->c).lzw.s)) {
	        (c->c).lzw.s = (dk_uword)chr;
	        if((c->c).lzw.lte >= 4095) {
	          if(lzw_output_string(o, c, n, (dk_uword)256)) {
		    clear_lzw_table(c);
		    back = 1;
		  } else {
		    back = 0;
		    (c->c).lzw.fl |= LZW_FLAG_ERROR_OCCURED;
		  }
	        } else {
	          if(((c->c).lzw.lte) == 2047) {
		    (c->c).lzw.ob = 12; back = 1;
		  } else {
		    if((c->c).lzw.lte == 1023) {
		      (c->c).lzw.ob = 11; back = 1;
		    } else {
		      if((c->c).lzw.lte == 511) {
		        (c->c).lzw.ob = 10; back = 1;
		      } else {
		        back = 1;
		      }
		    }
		  }
	        }
	      } else {
	        back = 0;
	      }
	    } else {
	      dk_delete(lzwptr);
	      back = 0; 
	      (c->c).lzw.fl |= LZW_FLAG_ERROR_OCCURED;
	    }
	  } else {
	    back = 0; 
	    (c->c).lzw.fl |= LZW_FLAG_ERROR_OCCURED;
	  }
        }
      } else {
        (c->c).lzw.s = (dk_uword)chr;
        (c->c).lzw.fl |= LZW_FLAG_HAVE_STRING;
        back = 1;
      }
  } 
  return back;
}



/**	Write data into an LZW compressing cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static
size_t
add_to_lzw_cell DK_P5(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  char chr, *ptr; size_t s; int err;
  
  ptr = b; err = 0; s = 0;
  while((s < l) && (err == 0)) {
    chr = *(ptr++);
    if(add_byte_to_lzw(o, c, n, (unsigned char)chr)) {
      back++;
    } else {
      err = 1;	
    }
    s++;
  }
  if(err) {
    (c->c).lzw.fl |= LZW_FLAG_ERROR_OCCURED;
  }
  
  return back;
}


#endif
/* if DK_HAVE_LZW */



/**	Write data into an ASCII-85 encoding cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static size_t
add_to_a85_cell DK_P5(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  unsigned long ul; char *ptr; size_t i;
  
  ptr = b;
  for(i = 0; i < l; i++) {
    ul = (unsigned long)((unsigned)((unsigned char)(*(ptr++))));
    (c->c).a85.a85val = ul + 256UL * (c->c).a85.a85val;
    (c->c).a85.used += 1;
    if(((c->c).a85.used) >= 4) {
      if(write_a85_buffer(o,c,n)) {
        back++;
      }
    } else {
      back++;
    }
  }
  
  return back;
}



/**	Write data into an ASCII-Hex encoding cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static
size_t
add_to_ah_cell DK_P5(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  int ok = 0;
  unsigned u; char *ptr; size_t i;
  char buffer[3];
  
  ptr = b; buffer[2] = '\0';
  for(i = 0; i < l; i++) {
    ok = 0;
    u = (unsigned)((unsigned char)(*ptr));
    buffer[0] = hex_digits[((u >> 4) & 15)];
    buffer[1] = hex_digits[(u  & 15)];
    if(add_from_cell(o,n,buffer,2) == 2) {
      ok = 1;
    }
    (c->c).ah.linepos += 2;
    if((c->c).ah.maxlinepos > 0) {
      if((c->c).ah.linepos >= (c->c).ah.maxlinepos) {
        if((o->flags) & 1) {
          if(add_from_cell(o,n,eol1,2) != 2) {
            ok = 0;
          }
        } else {
          if(add_from_cell(o,n,eol2,1) != 1) {
             ok = 0;
          }
        }
        (c->c).ah.linepos = 0;
      }
    }
    ptr++;
    if(ok) { back++; }
  }
  
  return back;
}



/**	Copy byte "to the left".
	As \a d and \a s are overlapping we will not
	use the memcpy() or bcopy() functions.
	@param	d	Destination pointer.
	@param	s	Source pointer.
	@param	l	Number of bytes to copy.
*/
static void
copy_forward DK_P3(char *,d, char *,s, size_t,l)
{
  char *dptr, *sptr; size_t lgt;
  lgt = l; sptr = s; dptr = d;
  while(lgt--) {
    *(dptr++) = *(sptr++);
  }
}



/**	Remove leading bytes from cell buffer.
	@param	c	Cell.
	@param	l	Number of leading bytes to remove.
*/
static void
preserve_buffer_contents DK_P2(dk_of_cell_t *,c, size_t,l)
{
  size_t newused;
  
  if((c->c).rl.used > l) {	
    newused = (c->c).rl.used - l;
    copy_forward((c->c).rl.buffer, &(((c->c).rl.buffer)[l]), newused);
    (c->c).rl.used = newused;
  } else {			
    (c->c).rl.used = 0;
    (c->c).rl.lc = '\0';
    (c->c).rl.st = 0;
  }
  
}




/**	Write data into a filter cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	l	Buffer length.
	@return	1 on success, 0 on error.
*/
static int
write_psrl_data DK_P4(dk_of_t *,o, dk_of_cell_t *,c, size_t,n, size_t,l)
{
  int back = 0;
  char ch;
  size_t wr;
  
  if((c->c).rl.buffer) {
    if(l > 0) {		
      ch = (char)((unsigned char)((l - 1) & 255));
      if(add_from_cell(o,n,&ch,1) == 1) {
        
        if((wr = add_from_cell(o,n,(c->c).rl.buffer,l)) == l) {
	  
          preserve_buffer_contents(c, l);
          back = 1;
        } else {	
	}
      } else {		
      }
    } else {		
      back = 1;
    }
  }
  
  return back;
}



/**	Write one run.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	l	Size of the run.
	@return	1 on success, 0 on error.
*/
static int
write_psrl_run DK_P4(dk_of_t *,o, dk_of_cell_t *,c, size_t,n, size_t,l)
{
  int back = 0; size_t b;
  char buffer[3];
  
  if(((c->c).rl.buffer) && (l > 0)) {
    b = 257 - l;
    buffer[0] = (char)((unsigned char)(b & 255));
    buffer[1] = (c->c).rl.lc;
    if(add_from_cell(o,n,buffer,2) == 2) {
      preserve_buffer_contents(c, l);
      back = 1;
    }
  }
  
  return back;
}



/**	Add one character to PS run-length compressing cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	ch	Character to add.
	@return	1 on success, 0 on error.
*/
static int
add_psrl_char DK_P4(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char,ch)
{
  int back = 0;
  
  if((c->c).rl.used) {
    if(ch == (c->c).rl.lc) {	
      switch((c->c).rl.st) {
        case 2: {		
	  back = 1;
	  ((c->c).rl.buffer)[(c->c).rl.used] = ch;
	  (c->c).rl.used += 1;
	  if((c->c).rl.used >= 127) {	
	    back = write_psrl_run(o,c,n,(c->c).rl.used);
	    (c->c).rl.used = 0;
	    (c->c).rl.st = 0;
	    (c->c).rl.lc = ch;
	  }
	} break;
	case 1: {		
	  back = write_psrl_data(o,c,n,((c->c).rl.used - 2));
	  ((c->c).rl.buffer)[2] = ch;
	  (c->c).rl.lc = ch;
	  (c->c).rl.st = 2;
	  (c->c).rl.used = 3;
	} break;
	default: {		
	  back = 1;
	  ((c->c).rl.buffer)[(c->c).rl.used] = ch;
	  (c->c).rl.used += 1;
	  (c->c).rl.lc = ch;
	  (c->c).rl.st = 1;
	  if((c->c).rl.used >= 127) {
	    back = write_psrl_data(o,c,n,((c->c).rl.used - 2));
	    (c->c).rl.st = 1;
	    (c->c).rl.lc = ch;
	    (c->c).rl.used = 2;
	  }
	} break;
      }
    } else {			
      switch((c->c).rl.st) {
        case 2: {		
	  back = write_psrl_run(o,c,n,(c->c).rl.used);
	  ((c->c).rl.buffer)[0] = ch;
	  (c->c).rl.lc = ch;
	  (c->c).rl.st = 0;
	  (c->c).rl.used = 1;
	} break;
	default: {		
	  back = 1;
	  ((c->c).rl.buffer)[(c->c).rl.used] = ch;
	  (c->c).rl.used += 1;
	  (c->c).rl.lc = ch;
	  (c->c).rl.st = 0;
	  if((c->c).rl.used >= 127) {
	    back = write_psrl_data(o,c,n,(c->c).rl.used);
	  }
	} break;
      }
    }
  } else {
    
    ((c->c).rl.buffer)[0] = ch;
    (c->c).rl.lc = ch;
    (c->c).rl.used = 1;
    (c->c).rl.st = 0;
    back = 1;
  }
  
  return back;
}



/**	Write data into a PS run-length compressing cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static size_t
add_to_psrl_cell DK_P5(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  char *ptr; size_t lgt;
  
  if((c->c).rl.buffer) {
    ptr = b; lgt = l;
    while(lgt--) {
      if(add_psrl_char(o,c,n,*(ptr++))) {
        back++;
      }
    }
  }
  
  return back;
}



/**	Write data into a buffering cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static size_t
add_to_buf_cell DK_P5(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  char *ptr; size_t i;
  
  if(((c->c).b.buffer) && ((c->c).b.lgt)) {
    ptr = b;
    for(i = 0; i < l; i++) {
      ((c->c).b.buffer)[(c->c).b.used] = *(ptr++);
      (c->c).b.used += 1;
      if(((c->c).b.used) >= ((c->c).b.lgt)) {
        
	
#line 907 "dkof.ctr"
	
#line 908 "dkof.ctr"
	
#line 909 "dkof.ctr"
	
#line 910 "dkof.ctr"
	
        if(add_from_cell(o,n,(c->c).b.buffer,(c->c).b.used)==((c->c).b.used)) {
	  back++;
	}
	(c->c).b.used = 0;
      } else {
        back++;
      }
    }
  } else {		
  }
  
  return back;
}



/**	Write data into a filter cell.
	@param	o	Output filter.
	@param	c	Filter cell.
	@param	n	Index of \a c in filter chain.
	@param	b	Data buffer pointer.
	@param	l	Size of \a b.
	@return	Number of bytes written.
*/
static size_t
add_to_cell DK_P5(dk_of_t *,o,dk_of_cell_t *,c,size_t,n,char *,b,size_t,l)
{
  size_t back = 0;
  
  switch(c->what) {
#if DK_HAVE_ZLIB_H
    case DK_OF_TYPE_FLATE: {
      back = add_to_flate_cell(o,c,n,b,l);
    } break;
#endif
    case DK_OF_TYPE_ASCII85: {
      back = add_to_a85_cell(o,c,n,b,l);
    } break;
    case DK_OF_TYPE_ASCIIHEX: {
      back = add_to_ah_cell(o,c,n,b,l);
    } break;
    case DK_OF_TYPE_PSRL: {
      back = add_to_psrl_cell(o,c,n,b,l);
    } break;
    case DK_OF_TYPE_BUFFERED: {
      back = add_to_buf_cell(o,c,n,b,l);
    } break;
#if DK_HAVE_LZW
    case DK_OF_TYPE_LZW: {
      back = add_to_lzw_cell(o,c,n,b,l);
    } break;
#endif
    /* further cell types here */
    default: {		
    } break;
  } 
  return back;
}



#if DK_HAVE_ZLIB_H

/**	Initialize a flate compressing cell.
	@param	c	Cell to prepare.
	@return	1 on success, 0 on error.
*/
static int
cell_flate_init DK_P1(dk_of_cell_t *,c)
{
  int back = 0;
  
  (c->c).flate.zs = dk_new(z_stream,1);
  (c->c).flate.ibuffer = dk_new(Bytef,1024);
  (c->c).flate.obuffer = dk_new(Bytef,1057);
  if(((c->c).flate.zs) && ((c->c).flate.ibuffer) && ((c->c).flate.obuffer)) {
    (c->c).flate.i_length = (uLong)1024UL;
    (c->c).flate.o_length = (uLong)1057UL;
    (c->c).flate.used     = (uLong)0UL;
    (c->c).flate.stillok  = 0;
    c->what = DK_OF_TYPE_FLATE;
    back = 1;
  } else {	
    if((c->c).flate.zs) { dk_delete((c->c).flate.zs); }
    if((c->c).flate.ibuffer) { dk_delete((c->c).flate.ibuffer); }
    if((c->c).flate.obuffer) { dk_delete((c->c).flate.obuffer); }
    (c->c).flate.zs = NULL;
    (c->c).flate.ibuffer = NULL;
    (c->c).flate.obuffer = NULL;
  }
  
  return back;
}
#endif



/**	Initialize an ASCII-85 encoding cell.
	@param	c	Cell to prepare.
	@return	1 on success, 0 on error.
*/
static int
cell_a85_init DK_P1(dk_of_cell_t *,c)
{
  int back = 1;
  
  (c->c).a85.a85val = 0UL;
  (c->c).a85.linepos = 0;
  (c->c).a85.maxlinepos = 75;
  (c->c).a85.used = 0;
  (c->c).a85.fl = 1;
  c->what = DK_OF_TYPE_ASCII85;
  
  return back;
}



#if DK_HAVE_LZW

/**	Compare two LZW table entries.
	@param	l	Left entry.
	@param	r	Right entry.
	@param	cr	Comparison criteria (ignored).
	@return	Comparison result.
*/
static
int
compare_lzw_entry DK_P3(void *,l, void *,r, int,cr)
{
  int back = 0;
  lzw_entry *pl, *pr;
  pl = (lzw_entry *)l; pr = (lzw_entry *)r;
  if(l) {
    if(r) {
      if(pl->s > pr->s) {
        back = 1;
      } else {
        if(pl->s < pr->s) {
	  back = -1;
	} else {
	  if(pl->c > pr->c) {
	    back = 1;
	  } else {
	    if(pl->c < pr->c) {
	      back = -1;
	    }
	  }
	}
      }
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  }
  return back;
}



/**	Initialize an LZW compressing cell.
	@param	c	Cell to prepare.
	@return	1 on success, 0 on error.
*/
static int
cell_lzw_init DK_P1(dk_of_cell_t *,c)
{
  int back = 0;
  
  c->what = DK_OF_TYPE_LZW;
  (c->c).lzw.fl = 0;
  (c->c).lzw.s = 0;
  (c->c).lzw.ic = 0x00;
  (c->c).lzw.oc = 0x00;
  (c->c).lzw.bitsused = 0;
  (c->c).lzw.table = dksto_open(0);
  (c->c).lzw.lte = 257;
  if((c->c).lzw.table) {
    dksto_set_comp((c->c).lzw.table, compare_lzw_entry, 0);
    (c->c).lzw.ti = dksto_it_open((c->c).lzw.table);
    if((c->c).lzw.ti) {
      back = 1;
    }
  }
  
  return back;
}

#endif

/* if DK_HAVE_LZW */



/**	Initialize an ASCII-Hex encoding cell.
	@param	c	Cell to prepare.
	@return	1 on success, 0 on error.
*/
static int
cell_ah_init DK_P1(dk_of_cell_t *,c)
{
  int back = 1;
  
  (c->c).ah.linepos = 0;
  (c->c).ah.maxlinepos = 78;
  (c->c).ah.fl = 1;
  c->what = DK_OF_TYPE_ASCIIHEX;
  
  return back;
}



/**	Initialize a PS run-length compressing cell.
	@param	c	Cell to prepare.
	@return	1 on success, 0 on error.
*/
static int
cell_psrl_init DK_P1(dk_of_cell_t *,c)
{
  int back = 0;
  
  c->what = DK_OF_TYPE_PSRL;
  (c->c).rl.buffer = NULL; (c->c).rl.lc = '\0';
  (c->c).rl.used = 0; (c->c).rl.st = 0;
  (c->c).rl.buffer = dk_new(char,128);
  if((c->c).rl.buffer) {
    back = 1;
  }
  
  return back;
}



/**	Initialize a buffering cell.
	@param	c	Cell to prepare.
	@return	1 on success, 0 on error.
*/
static int
cell_buffered_init DK_P1(dk_of_cell_t *,c)
{
  int back = 0;
  
  (c->c).b.buffer = dk_new(char,512);
  if((c->c).b.buffer) {
    (c->c).b.lgt = 512;
    (c->c).b.used = 0;
    c->what = DK_OF_TYPE_BUFFERED;
    back = 1;
  }
  
  return back;
}



/**	Initialize a filtering cell.
	@param	c	Cell to prepare.
	@param	w	Cell type.
	@return	1 on success, 0 on error.
*/
static int
cell_init DK_P2(dk_of_cell_t *,c,int,w)
{
  int back = 0;
  
  if((c->what) == DK_OF_TYPE_NONE) {
    switch(w) {
#if DK_HAVE_ZLIB_H
      case DK_OF_TYPE_FLATE: {
        back = cell_flate_init(c);
      } break;
#endif
      case DK_OF_TYPE_ASCII85: {
        back = cell_a85_init(c);
      } break;
      case DK_OF_TYPE_ASCIIHEX: {
        back = cell_ah_init(c);
      } break;
      case DK_OF_TYPE_PSRL: {
        back = cell_psrl_init(c);
      } break;
      case DK_OF_TYPE_BUFFERED: {
        back = cell_buffered_init(c);
      } break;
#if DK_HAVE_LZW
      case DK_OF_TYPE_LZW: {
        back = cell_lzw_init(c);
      } break;
#endif
      /* further cell types here */
      default: {	
      } break;
    }
  } else {		
  }
  
  return back;
}



#if DK_HAVE_ZLIB_H

/**	Prepare flate compressing cell at start of filtered chunk.
	@param	o	Output filter.
	@param	c	Cell to prepare.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_flate_start DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  if(((c->c).flate.zs) && ((c->c).flate.ibuffer) && ((c->c).flate.obuffer)) {
    if(((c->c).flate.i_length) && ((c->c).flate.o_length)) {
      (c->c).flate.used = (uLong)0UL;
      (c->c).flate.stillok = 0;
      ((c->c).flate.zs)->zfree = (free_func)0;
      ((c->c).flate.zs)->zalloc = (alloc_func)0;
      ((c->c).flate.zs)->opaque = (voidpf)0;
      
      if(deflateInit((c->c).flate.zs, 9) == Z_OK) {
        
        back = 1; (c->c).flate.stillok = 1;
      } else {		
      }
    } else {	
    }
  } else {	
  }
  
  return back;
}
#endif



/**	Prepare ASCII-85-encoding cell at start of filtered chunk.
	@param	o	Output filter.
	@param	c	Cell to prepare.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_a85_start DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 1;
  
  (c->c).a85.a85val = 0UL;
  (c->c).a85.linepos = 0;
  (c->c).a85.used = 0;
  
  return back;
}



#if DK_HAVE_LZW

/**	Prepare LZW compressing cell at start of filtered chunk.
	@param	o	Output filter.
	@param	c	Cell to prepare.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_lzw_start DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  if((c->c).lzw.table) {
    if((c->c).lzw.ti) {
      (c->c).lzw.fl = 0;
      (c->c).lzw.s =  0;
      (c->c).lzw.ic = 0x00;
      (c->c).lzw.oc = 0x00;
      (c->c).lzw.bitsused = 0;
      clear_lzw_table(c);
      (c->c).lzw.ob = 9;
      (c->c).lzw.lte = 257;
      if(lzw_output_string(o, c, n, 256)) {
        back = 1;
      }
    }
  }
  if(!back) {
    (c->c).lzw.fl |= LZW_FLAG_ERROR_OCCURED;
  }
  
  return back;
}

#endif
/* if DK_HAVE_LZW */


/**	Prepare ASCII-Hex encoding cell at start of filtered chunk.
	@param	o	Output filter.
	@param	c	Cell to prepare.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_ah_start DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 1;
  
  (c->c).ah.linepos = 0;
  
  return back;
}



/**	Prepare PS run-length compressing cell at start of filtered chunk.
	@param	o	Output filter.
	@param	c	Cell to prepare.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_psrl_start DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  if((c->c).rl.buffer) {
    back = 1;
    (c->c).rl.lc = '\0'; (c->c).rl.used = 0;
    (c->c).rl.st = 0;
  }
  
  return back;
}



/**	Prepare buffering cell at start of filtered chunk.
	@param	o	Output filter.
	@param	c	Cell to prepare.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_buf_start DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  if(((c->c).b.lgt) && ((c->c).b.buffer)) {
    (c->c).b.used = 0; back = 1;
  } else {	
  }
  
  return back;
}



/**	Prepare filtering cell at start of filtered chunk.
	@param	o	Output filter.
	@param	c	Cell to prepare.
	@param	n	Index of \a c in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_start DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  switch(c->what) {
#if DK_HAVE_ZLIB_H
    case DK_OF_TYPE_FLATE: {
      back = cell_flate_start(o,c,n);
    } break;
#endif
    case DK_OF_TYPE_ASCII85: {
      back = cell_a85_start(o,c,n);
    } break;
    case DK_OF_TYPE_ASCIIHEX: {
      back = cell_ah_start(o,c,n);
    } break;
    case DK_OF_TYPE_PSRL: {
      back = cell_psrl_start(o,c,n);
    } break;
    case DK_OF_TYPE_BUFFERED: {
      back = cell_buf_start(o,c,n);
    } break;
#if DK_HAVE_LZW
    case DK_OF_TYPE_LZW: {
      back = cell_lzw_start(o,c,n);
    } break;
#endif
    default: {		
    } break;
  }
  
  return back;
}



#if DK_HAVE_ZLIB_H

/**	Final write operation to flate compressing cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
write_flate_final DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  int cc, err;
  size_t sz;
  
  cc = 1;
  ((c->c).flate.zs)->next_in = (c->c).flate.ibuffer;
  ((c->c).flate.zs)->avail_in = (c->c).flate.used;
  back = 1;
  
  while(cc) {
    
    cc = 0;
    ((c->c).flate.zs)->next_out = (c->c).flate.obuffer;
    ((c->c).flate.zs)->avail_out = (c->c).flate.o_length;
    err = deflate((c->c).flate.zs, Z_FINISH);
    
    
    switch(err) {
      case Z_STREAM_END: {	
        sz = (size_t)(((c->c).flate.o_length) - (((c->c).flate.zs)->avail_out));
	
	if(sz) {
	  if(add_from_cell(o,n,(char *)((c->c).flate.obuffer),sz) != sz) {
	    back = 0;
	  }
	}
      } break;
      case Z_OK: {	
        cc = 1;
        sz = (size_t)(((c->c).flate.o_length) - (((c->c).flate.zs)->avail_out));
	if(sz) {
	  if(add_from_cell(o,n,(char *)((c->c).flate.obuffer),sz) != sz) {
	    back = 0;
	  }
	}
      } break;
      default: {	
        back = 0;
      } break;
    }
    
  }
  if(deflateEnd((c->c).flate.zs) != Z_OK) {
    back = 0;
  }
  (c->c).flate.used = (uLong)0UL;
  (c->c).flate.stillok = 0;
  
  
  return back;
}



/**	Flush deflate compressing cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_flate_end DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  if(((c->c).flate.zs) && ((c->c).flate.ibuffer) && ((c->c).flate.obuffer)) {
    if(((c->c).flate.o_length) && ((c->c).flate.i_length)) {
      if((c->c).flate.stillok) {
        back = write_flate_final(o,c,n);
      } else {	
      }
    } else {	
    }
  } else {	
  }
  
  return back;
}
#endif




/**	Flush ASCII-85 encoding cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_a85_end DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 1;
  size_t i;
  
  if((c->c).a85.used) {
    back = 0;
    for(i = (c->c).a85.used; i < 4; i++) {
      (c->c).a85.a85val = 256UL * (c->c).a85.a85val;
    }
    if(write_a85_buffer(o,c,n)) {
      back = 1;
    }
  }
  if(((c->c).a85.maxlinepos) > 0) {
    if(((c->c).a85.linepos) >= ((c->c).a85.maxlinepos + 3)) {
      if((o->flags) & 1) {
        if(add_from_cell(o,n,eol1,2) != 2) {
          back = 0;
        }
      } else {
        if(add_from_cell(o,n,eol2,1) != 1) {
          back = 0;
        }
      }
      (c->c).a85.linepos = 0;
    }
  }
  if(((c->c).a85.fl) & 1) {
    if(add_from_cell(o,n,a85end,2) != 2) {
      back = 0;
    }
  }
  if((o->flags) & 1) {
    if(add_from_cell(o,n,eol1,2) != 2) {
      back = 0;
    }
  } else {
    if(add_from_cell(o,n,eol2,1) != 1) {
      back = 0;
    }
  }
  (c->c).a85.a85val = 0UL;
  (c->c).a85.used = 0;
  (c->c).a85.linepos = 0;
  
  return back;
}



/**	Flush buffering cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_buf_end DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  size_t sz;
  
  if(((c->c).b.lgt) && ((c->c).b.buffer)) {
    if((c->c).b.used) {
      sz = (c->c).b.used;
      if(add_from_cell(o,n,(c->c).b.buffer,sz) == sz) {
        back = 1;
      }
    } else {
      back = 1;	
    }
  } else {	
  }
  (c->c).b.used = 0;
  
  return back;
}



#if DK_HAVE_LZW

/**	Flush LZW compressing cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_lzw_end DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  /* flush pending output, send EOD */
  if(((c->c).lzw.fl) & LZW_FLAG_HAVE_STRING) {
    
    if(!((c->c).lzw.fl & LZW_FLAG_ERROR_OCCURED)) {
      if(lzw_output_string(o, c, n, (c->c).lzw.s)) {
        back = 1;
      } else {
        back = 0;
	(c->c).lzw.fl |= LZW_FLAG_ERROR_OCCURED;
      }
    } else {
      back = 0;
    }
  } else {
    back = 1;
  }
  if(!((c->c).lzw.fl & LZW_FLAG_ERROR_OCCURED)) {
    if(!lzw_output_string(o, c, n, 257)) {
      back = 0;
      (c->c).lzw.fl |= LZW_FLAG_ERROR_OCCURED;
    }
  }
  if(!((c->c).lzw.fl & LZW_FLAG_ERROR_OCCURED)) {
    while((c->c).lzw.bitsused > 0) {
      
      if(!lzw_output_bit(o, c, n, 0)) {
        back = 0;
      }
    }
  }
  clear_lzw_table(c);
  
  return back;
}

#endif
/* if DK_HAVE_LZW */



/**	Flush ASCII-Hex filtering cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_ah_end DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 1;
  
  if(((c->c).ah.maxlinepos) > 0) {
    if(((c->c).ah.linepos) >= (c->c).ah.maxlinepos) {
      if((o->flags) & 1) {
        if(add_from_cell(o,n,eol1,2) != 2) {
          back = 0;
        }
      } else {
        if(add_from_cell(o,n,eol2,1) != 1) {
          back = 0;
        }
      }
      (c->c).ah.linepos = 0;
    }
  }
  if(((c->c).ah.fl) & 1) {
    if(add_from_cell(o,n,ahend,strlen(ahend)) != 2) {
      back = 0;
    }
  }
  if((o->flags) & 1) {
    if(add_from_cell(o,n,eol1,2) != 2) {
      back = 0;
    }
  } else {
    if(add_from_cell(o,n,eol2,1) != 1) {
      back = 0;
    }
  }
  
  return back;
}



/**	Flush PS run-length filtering cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_psrl_end DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  char buffer[2];
  
  if((c->c).rl.buffer) {
    if((c->c).rl.used) {
      switch((c->c).rl.st) {
        case 2: {
	  back = write_psrl_run(o,c,n,(c->c).rl.used);
	} break;
	default: {
	  back = write_psrl_data(o,c,n,(c->c).rl.used);
	} break;
      }
    } else {
      back = 1;	/* no bytes in buffer */
    }
    if(back) {
      buffer[0] = (char)0x80;
      buffer[1] = (char)0x00;
      back = add_from_cell(o,n,buffer,1);
    }
  }
  
  return back;
}



/**	Flush filtering cell at end of chunk.
	@param	o	output filter.
	@param	c	Filtering cell.
	@param	n	Index of \a in filter chain.
	@return	1 on success, 0 on error.
*/
static int
cell_end DK_P3(dk_of_t *,o,dk_of_cell_t *,c,size_t,n)
{
  int back = 0;
  
  switch(c->what) {
#if DK_HAVE_ZLIB_H
    case DK_OF_TYPE_FLATE: {
      back = cell_flate_end(o,c,n);
    } break;
#endif
    case DK_OF_TYPE_ASCII85: {
      back = cell_a85_end(o,c,n);
    } break;
    case DK_OF_TYPE_BUFFERED: {
      back = cell_buf_end(o,c,n);
    } break;
    case DK_OF_TYPE_ASCIIHEX: {
      back = cell_ah_end(o,c,n);
    } break;
    case DK_OF_TYPE_PSRL: {
      back = cell_psrl_end(o,c,n);
    } break;
#if DK_HAVE_LZW
    case DK_OF_TYPE_LZW: {
      back = cell_lzw_end(o,c,n);
    } break;
#endif
    /* further cell types here */
    default: {		
    } break;
  }
  
  return back;
}



/**	Start a filtered chunk.
	@param	o	Output filter.
	@return	1 on success, 0 on error.
*/
static int
start_chunk DK_P1(dk_of_t *,o)
{
  int back = 0;
  size_t n;
  
  if((o->have_data) == 0) {
    if((o->cells) && (o->n_of_cells)) {
      back = 1;
      for(n = 0; n < (size_t)(o->n_of_cells); n++) {
        if(!cell_start(o,&((o->cells)[n]),n)) {
	  back = 0;
	}
      }
      if(back) {
        o->have_data = 1;
      }
    } else {		
    }
  } else {		
  }
  
  return back;
}



/**	End chunk of filtered data.
	@param	o	Output filter.
	@return	1 on success, 0 on error.
*/
static int
end_chunk DK_P1(dk_of_t *,o)
{
  int back = 0;
  size_t n;
  
  if((o->have_data) == 1) {
    if((o->cells) && (o->n_of_cells)) {
      back = 1;
      n = o->n_of_cells;
      while(n--) {
        if(!cell_end(o,&((o->cells)[n]),n)) {
	  back = 0;
	}
      }
      o->have_data = (back ? 0 : -1);
    } else {		
    }
  } else {		
  }
  
  return back;
}



#if DK_HAVE_ZLIB_H
/**	Close a deflate compressing filtering cell.
	@param	c	Filtering cell to close.
*/
static void
delete_flate_cell DK_P1(dk_of_cell_t *,c)
{
  void *ptr;
  
  if((c->c).flate.zs) {
    ptr = (c->c).flate.zs; dk_delete(ptr);
  }
  if((c->c).flate.ibuffer) {
    ptr = (c->c).flate.ibuffer; dk_delete(ptr);
  }
  if((c->c).flate.obuffer) {
    ptr = (c->c).flate.obuffer; dk_delete(ptr);
  }
  
}
#endif



#if DK_HAVE_LZW


/**	Close an LZW compressing filtering cell.
	@param	c	Filtering cell to close.
*/
static void
delete_lzw_cell DK_P1(dk_of_cell_t *,c)
{
  
  if((c->c).lzw.table) {
    if((c->c).lzw.ti) {
      clear_lzw_table(c);
      dksto_it_close((c->c).lzw.ti);
    }
    dksto_close((c->c).lzw.table);
  }
  (c->c).lzw.table = NULL; (c->c).lzw.ti = NULL;
  (c->c).lzw.fl = 0; (c->c).lzw.s = 0;
  (c->c).lzw.ic = 0x00; (c->c).lzw.oc = 0x00;
  (c->c).lzw.bitsused = 0; (c->c).lzw.lte = 0;
  
}

#endif
/* if DK_HAVE_LZW */



/**	Close an ASCII-85 filtering cell.
	@param	c	Filtering cell to close.
*/
static void
delete_a85_cell DK_P1(dk_of_cell_t *,c)
{
  
  (c->c).a85.a85val = 0UL;
  (c->c).a85.linepos = 0;
  (c->c).a85.used = 0;
  
}



/**	Close a buffering filter cell.
	@param	c	Filtering cell to close.
*/
static void
delete_buf_cell DK_P1(dk_of_cell_t *,c)
{
  void *ptr;
  
  if((c->c).b.buffer) {
    ptr = (c->c).b.buffer; dk_delete(ptr);
  }
  
}



/**	Close an ASCII-Hex filtering cell.
	@param	c	Filtering cell to close.
*/
static void
delete_ascii_hex_cell DK_P1(dk_of_cell_t *,c)
{
  
}



/**	Close a PS run-length filtering cell.
	@param	c	Cell to close.
*/
static void
delete_ps_runlength_cell DK_P1(dk_of_cell_t *,c)
{
  char *ptr;
  
  ptr = (c->c).rl.buffer;
  if(ptr) {
    dk_delete(ptr);
  }
  (c->c).rl.buffer = NULL; (c->c).rl.lc = '\0';
  (c->c).rl.used = 0; (c->c).rl.st = 0;
  
}



/**	Close one filter cell.
	@param	c	Filter cell to close.
*/
static void
delete_cell DK_P1(dk_of_cell_t *,c)
{
  switch(c->what) {
#if DK_HAVE_ZLIB_H
    case DK_OF_TYPE_FLATE: {
      delete_flate_cell(c);
    } break;
#endif
    case DK_OF_TYPE_ASCII85: {
      delete_a85_cell(c);
    } break;
    case DK_OF_TYPE_BUFFERED: {
      delete_buf_cell(c);
    } break;
    case DK_OF_TYPE_ASCIIHEX: {
      delete_ascii_hex_cell(c);
    } break;
    case DK_OF_TYPE_PSRL: {
      delete_ps_runlength_cell(c);
    } break;
#if DK_HAVE_LZW
    case DK_OF_TYPE_LZW: {
      delete_lzw_cell(c);
    } break;
#endif
    /* further cell types here */
    default: {	
    } break;
  }
  DK_MEMRES(c,sizeof(dk_of_cell_t));
}



/**	Close output filter.
	@param	o	Output filter to close.
*/
static int
delete_of DK_P1(dk_of_t *,o)
{
  int back = 1;
  dk_of_cell_t *c; size_t i;
  void *ptr;
  
  if(o->have_data == 1) {
    if(!end_chunk(o)) { back = 0; }
  }
  c = o->cells;
  if(c && (o->n_of_cells)) {
    for(i = 0; i < (size_t)(o->n_of_cells); i++) {
      delete_cell(c++);
    }
    ptr = (void *)(o->cells);
    dk_delete(ptr);
  }
  o->target = NULL;
  o->cells = NULL;
  o->n_of_cells = 0;
  o->have_data = 0;
  dk_delete(o);
  
  return back;
}



void
dkof_stream_fct DK_P1(dk_stream_api_t *, a)
{
  dk_stream_t *s;
  dk_of_t *o;
  dk_of_cell_t *c;
  if(a) {
    a->return_value = 0;
    a->error_code = DK_ERR_FUNCTION_UNSUPPORTED;
    s = (dk_stream_t *)(a->strm);
    if(s) {
      o = (dk_of_t *)(s->data);
      if(o) {
        c = o->cells;
	if(c) {
	  switch(a->cmd) {
	    case DK_STREAM_CMD_TEST: {
	      switch( (a->params).cmd ) {
	        case DK_STREAM_CMD_TEST:
		case DK_STREAM_CMD_FINISH:
		case DK_STREAM_CMD_WRBUF: {
		  a->return_value= 1;
		} break;
	      }
	    } break;
	    case DK_STREAM_CMD_FINISH: {
	      a->return_value = delete_of(o);
	      s->data = NULL; s->fct = NULL;
	    } break;
	    case DK_STREAM_CMD_WRBUF: {
	      void *b; size_t sz, res, n;
	      if(o->n_of_cells) {
	        sz = (a->params).length;
		b  = (a->params).buffer;
		n  = o->n_of_cells - 1;
		if(b && sz) {
		  res = add_to_cell(o, &(c[n]), n, (char *)b, sz);
		  if(res) {
		    a->return_value = 1;
		    (a->results).used = res;
		  } else {	
		    a->error_code = DK_ERR_IO;
		  }
		} else {	
		}
	      } else {		
	      }
	    } break;
	  }
	} else {	
	}
      } else {	
      }
    } else {	
    }
  } else {	
  }
}



dk_stream_t *
dkof_open DK_P2(dk_stream_t *,t,size_t,nof)
{
  dk_stream_t *back = NULL;
  dk_of_t *o = NULL;
  dk_of_cell_t *c = NULL, *cptr = NULL;
  size_t n;
  
  if(t && nof) {	
    o = dk_new(dk_of_t,1); c = dk_new(dk_of_cell_t,nof);
    if(o && c) {
      back = dkstream_new((void *)o, (dk_stream_fct_t *)dkof_stream_fct);
      if(back) {	
        o->flags = 0;
	o->target = t;
	o->cells = c;
	o->n_of_cells = nof;
	o->have_data = 0;
	cptr = c; n = nof;
	while(nof--) {
	  DK_MEMRES(cptr,sizeof(dk_of_cell_t));
	  cptr->what = DK_OF_TYPE_NONE;
	  cptr++;
	}
      } else {		
      }
    } else {		
    }
    if(!back) {
      if(o) dk_delete(o);
      if(c) dk_delete(c);
    }
  } else {		
  }
  
  return back;
}



void
dkof_close DK_P1(dk_stream_t *,s)
{
  
  if(s) {
    dkstream_delete(s);
  }
  
}



int
dkof_set DK_P3(dk_stream_t *,s,size_t,n,int,w)
{
  int back = 0;
  dk_of_t *o; dk_of_cell_t *c;
  
  if(s) {
    o = (dk_of_t *)(s->data);
    if(o) {
      c = o->cells;
      if(c) {
        if(n < (size_t)(o->n_of_cells)) {
	  back = cell_init(&((o->cells)[n]), w);
	} else {	
	}
      } else {	
      }
    } else {	
    }
  } else {	
  }
  
  return back;
}



int
dkof_start_chunk DK_P1(dk_stream_t *,s)
{
  int back = 0;
  dk_of_t *o;
  
  if(s) {
    o = (dk_of_t *)(s->data);
    if(o) {
      back = start_chunk(o);
    } else {		
    }
  } else {		
  }
  
  return back;
}



int
dkof_end_chunk DK_P1(dk_stream_t *,s)
{
  int back = 0;
  dk_of_t *o;
  
  if(s) {
    o = (dk_of_t *)(s->data);
    if(o) {
      back = end_chunk(o);
    } else {		
    }
  } else {		
  }
  
  return back;
}




void
dkof_set_crnl DK_P2(dk_stream_t *,s,int,v)
{
  dk_of_t *o;
  
  if(s) {
    o = (dk_of_t *)(s->data);
    if(o) {
      if(v) {
        o->flags |= 1;
      } else {
        o->flags &= (~1);
      }
    } else {	
    }
  }
}



void
dkof_set_finalizing DK_P2(dk_stream_t *,s,int,v)
{
  dk_of_t *o;
  size_t n;
  if(s) {
    o = (dk_of_t *)(s->data);
    if(o) {
      if((o->cells) && (o->n_of_cells)) {
        for(n = 0; n < (size_t)(o->n_of_cells); n++) {
	  switch(((o->cells)[n]).what) {
	    case DK_OF_TYPE_ASCII85: {
	      if(v) {
	        ((o->cells)[n]).c.a85.fl |= 1;
	      } else {
	        ((o->cells)[n]).c.a85.fl &= (~1);
	      }
	    } break;
	    case DK_OF_TYPE_ASCIIHEX: {
	      if(v) {
	        ((o->cells)[n]).c.ah.fl |= 1;
	      } else {
	        ((o->cells)[n]).c.ah.fl &= (~1);
	      }
	    } break;
	  }
        }
      }
    }
  }
}



dk_bitshift_t *
dkof_bs_open DK_P1(dk_stream_t *,t)
{
  dk_bitshift_t *back = NULL;
  if(t) {
    back = dk_new(dk_bitshift_t,1);
    if(back) {
      back->t = t;
      back->c = 0x00;
      back->b = 0;
    }
  }
  return back;
}



void
dkof_bs_close DK_P1(dk_bitshift_t *,p)
{
  if(p) {
    p->t = NULL;
    p->c = 0x00;
    p->b = 0;
    dk_delete(p);
  }
}



int
dkof_bs_put DK_P3(dk_bitshift_t *,bsp, unsigned short,c, unsigned short,nbits)
{
  int back = 0;
  unsigned short br;	/* bits remaining for next loops */
  unsigned short itl;	/* bits in this loop */
  
  if(bsp) {
    back = 1;
    br = nbits;
    do {
      itl = 8 - bsp->b;
      itl = ((itl > br) ? br : itl);
      bsp->c = SHIFTBITS((bsp->c),itl) | GETBITS(c,br,itl);
      br -= itl; bsp->b += itl;
      if((bsp->b) >= 8) {
        bsp->b = 0;
	if(dkstream_write(bsp->t, (char *)(&(bsp->c)), 1) != 1) {
	  back = 0;
	}
      }
    } while((br > 0) && (back));
    br = nbits;
  } else {
  } 
  return back;
}



int
dkof_bs_flush DK_P1(dk_bitshift_t *,bsp)
{
  int back = 0;
  if(bsp) {
    back = 1;
    if(bsp->b) {
      back = dkof_bs_put(bsp, (unsigned short)0, (unsigned short)(8 - bsp->b));
    }
  }
  return back;
}



int
dkof_lzw_available DK_P0()
{
  int back = 0;
#if DK_HAVE_LZW
  back = 1;
#endif
  return back;
}



void
dkof_set_max_line_length DK_P2(dk_stream_t *,s, size_t,ll)
{
  dk_of_t *o;
  size_t i;
  if(s) {
    o = (dk_of_t *)(s->data);
    if(o) {
      if(o->cells) {
        for(i = 0; i < (size_t)(o->n_of_cells); i++) {
	  switch(((o->cells)[i]).what) {
	    case DK_OF_TYPE_ASCII85: {
	      if(ll > 5) {
	        ((o->cells)[i]).c.a85.maxlinepos = ll - 5;
	      } else {
	        ((o->cells)[i]).c.a85.maxlinepos = 0;
	      }
	    } break;
	    case DK_OF_TYPE_ASCIIHEX: {
	      if(ll > 2) {
	        ((o->cells)[i]).c.ah.maxlinepos = ll - 2;
	      } else {
	        ((o->cells)[i]).c.ah.maxlinepos = 0;
	      }
	    } break;
	  }
	}
      }
    }
  }
}



