/*
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	dkstream.c	Stream I/O module.
*/



#include "dk.h"
#include "dkmem.h"
#include "dktypes.h"
#include "dkstr.h"
#include "dksf.h"
#include "dkenc.h"

#if DK_HAVE_ZLIB_H
#include <zlib.h>
#endif
#if DK_HAVE_BZLIB_H
#include <bzlib.h>
#endif



/**	Inside the dkstream module.
*/
#define DK_STREAM_C 1

#include "dkstream.h"




#line 69 "dkstream.ctr"




#include <stdio.h>



/**	Newline.
*/
static char str_nl[] = { "\n" };



dk_stream_t *
dkstream_new DK_P2(void *, data, dk_stream_fct_t *, fct)
{
  dk_stream_t *back = NULL;
  
  if(data && fct) { 
    back = dk_new(dk_stream_t,1); 
    if(back) {
      back->data = data; back->fct = fct;
      (back->api).strm = (void *)back;
      back->bytes = 0UL;
      back->flags = 0;
      back->opt = 0;
    }
  } 
  return back;
}



size_t
dkstream_write DK_P3(dk_stream_t *, st, char *, b, size_t, l)
{
  size_t back = 0;
  unsigned long size_to_add;
  dk_stream_fct_t *f; void *d; dk_stream_api_t *a;
  
  if(st && b && l) { 
    d = st->data; f = st->fct; a = &(st->api);
    if(d && f) { 
      a->cmd = DK_STREAM_CMD_WRBUF;
      (a->params).buffer = b;
      (a->params).length = l;
      (a->results).buffer = b;
      (a->results).length = l;
      (*f)(a); 
      if(a->return_value) { 
	back = (a->results).used;
	size_to_add = (unsigned long)back;
	st->bytes = st->bytes + size_to_add;
      }
    }
  } 
  return back;
}



size_t
dkstream_read  DK_P3(dk_stream_t *, st, char *, b, size_t, l)
{
  size_t back = 0;
  dk_stream_fct_t *f; void *d; dk_stream_api_t *a;
  
  if(st && b && l) { 
    d = st->data; f = st->fct; a = &(st->api);
    if(d && f) { 
      a->cmd = DK_STREAM_CMD_RDBUF;
      (a->params).buffer = b;
      (a->params).length = l;
      (a->results).buffer = b;
      (a->results).length = l;
      (*f)(a); 
      if(a->return_value) { 
	back = (a->results).used;
      } else {
	a->cmd = DK_STREAM_CMD_TEST;
	(a->params).cmd = DK_STREAM_CMD_AT_END;
	(*f)(a);
	if(a->return_value) {
	  a->cmd = DK_STREAM_CMD_AT_END;
	  (*f)(a);
	  if(a->return_value) {
	    st->flags |= 1;
	  }
	}
      }
    }
  } 
  return back;
}



void
dkstream_delete DK_P1(dk_stream_t *, st)
{
  dk_stream_api_t *a; dk_stream_fct_t *f;
  
  if(st) {
    if((st->data) && (st->fct)) {
      a = &(st->api); f = st->fct;
      a->cmd = DK_STREAM_CMD_FINAL;
      (*f)(a);
    }
    st->data = NULL; st->fct = NULL; (st->api).strm = NULL;
    
    dk_delete(st);
  }
  
}



/**	Low-level function for uncompressed streams, closing.
	@param	a	Stream API structure.
*/
void
file_stream_fct DK_P1(dk_stream_api_t *, a)
{
  dk_stream_t *s;
  void *d;
  FILE *f;
  char *b;
  size_t l, x;
  
  if(a) { 
    a->return_value = 0;
    s = (dk_stream_t *)(a->strm);
    if(s) { 
      d = s->data;
      f = (FILE *)d;
      if(f) { 
	switch(a->cmd) {
	  case DK_STREAM_CMD_TEST: {
	    switch( (a->params).cmd ) {
	      case DK_STREAM_CMD_TEST: a->return_value = 1; break;
	      case DK_STREAM_CMD_AT_END: a->return_value = 1; break;
	      case DK_STREAM_CMD_RDBUF: a->return_value = 1; break;
	      case DK_STREAM_CMD_WRBUF: a->return_value = 1; break;
	      case DK_STREAM_CMD_FINAL: a->return_value = 1; break;
	      case DK_STREAM_CMD_FINISH: a->return_value = 1; break;
	      case DK_STREAM_CMD_REWIND: a->return_value = 1; break;
	      case DK_STREAM_CMD_FLUSH: a->return_value = 1; break;
	      case DK_STREAM_CMD_GETS: a->return_value = 1; break;
	      /*
	        Bugfix 2005/01/05
		On some systems the fputs() function returns the
		number of bytes successfully written, on other
		systems it returns 0 to indicate success.
		So we can not use it.
	      */
	      case DK_STREAM_CMD_PUTS: a->return_value = 0; break;
	    }
	  } break;
	  case DK_STREAM_CMD_GETS: {
	    b = (a->params).buffer; l = (a->params).length;
	    if(b && l) { 
              if(fgets(b,l,f)) {
		a->return_value = 1;
	      }
	    }
	  } break;
	  case DK_STREAM_CMD_PUTS: {
/*
  Bugfix 2005/01/05
  Do not use the fputs() function as it has different return
  values on different systems.
  On some systems it returns the number of bytes successfully
  written, on other systems it returns 0 to indicate success.
*/
#if USE_BROKEN_FPUTS
	    int i;
	    b = (a->params).buffer;
	    if(b) { 
	      i = fputs(b,f);
	      if(i >= 0) {
		a->return_value = 1; (a->results).length = i;
	      }
	    }
#else
	    b = (a->params).buffer;
	    /* l = (a->params).length; */
	    l = strlen((a->params).buffer);
	    if(b && l) { 
	      x = fwrite((void *)b, 1, l, f); 
	      if(x) { 
		(a->results).used = (a->results).length = x;
		a->return_value = 1;
	      }
	    }
#endif
	  } break;
	  case DK_STREAM_CMD_AT_END: {		
	    if(feof(f)) {
	      a->return_value = 1;
	    } else {
	      a->return_value = 0;
	    }
	  } break;
	  case DK_STREAM_CMD_RDBUF: {		
	    b = (a->params).buffer; l = (a->params).length;
	    if(b && l) { 
	      x = fread((void *)b, 1, l, f); 
	      if(x) { 
		(a->results).used = x;
		a->return_value = 1;
	      }
	    }
	  } break;
	  case DK_STREAM_CMD_WRBUF: {		
	    b = (a->params).buffer; l = (a->params).length;
	    if(b && l) { 
	      x = fwrite((void *)b, 1, l, f); 
	      if(x) { 
		(a->results).used = x;
		a->return_value = 1;
	      }
	    }
	  } break;
	  case DK_STREAM_CMD_FINAL: {		
	    if(fflush(f) == 0) {
	      a->return_value = 1;
	    }
	  } break;
	  case DK_STREAM_CMD_FINISH: {		
	    if(fclose(f) == 0) {
	      a->return_value = 1;
	    }
	    s->data = NULL; /* file is closed now */
	    s->fct  = NULL; /* nothing more to do */
	  } break;
	  case DK_STREAM_CMD_REWIND: {		
	    rewind(f); a->return_value = 1;
	  } break;
	  case DK_STREAM_CMD_FLUSH: {		
	    if(fflush(f) == 0) {
	      a->return_value = 1;
	    }
	  } break;
	}
      } else {	/* NO FILE */
	
      }
    } else {	/* INVALID POINTER TO STREAM */
      
    }
    
  } else {	/* INVALID API POINTER */
    
    
  }
}



/**	Low-level function for uncompressed streams, non-closing.
	@param	a	Stream API structure.
*/
void
file_stream_function DK_P1(dk_stream_api_t *, a)
{
  if(a) {
    if(a->cmd == DK_STREAM_CMD_FINISH) {
      a->cmd = DK_STREAM_CMD_FINAL;
      file_stream_fct(a);
      a->cmd = DK_STREAM_CMD_FINISH;
    } else {
      file_stream_fct(a);
    }
  }
}



/**	Low-level function for bzip2 compressed streams, closing.
	@param	a	Stream API structure.
*/
void
bz2_stream_fct DK_P1(dk_stream_api_t *, a)
{
#if DK_HAVE_BZLIB_H
  BZFILE *bzf;
  dk_stream_t *st;
  void *d;
  void *b;
  int   l;
  int   x;
#endif
  if(a) {
    a->return_value = 0;
#if DK_HAVE_BZLIB_H
    st = (dk_stream_t *)(a->strm);
    if(st) {
      d = st->data;
      bzf = (BZFILE *)d;
      switch(a->cmd) {
	case DK_STREAM_CMD_TEST : {
	  switch((a->params).cmd) {
	    case DK_STREAM_CMD_TEST : {
	      a->return_value = 1;
	    } break;
	    case DK_STREAM_CMD_RDBUF : {
	      a->return_value = 1;
	    } break;
	    case DK_STREAM_CMD_WRBUF : {
	      a->return_value = 1;
	    } break;
	    case DK_STREAM_CMD_FINAL : {
	      a->return_value = 1;
	    } break;
	    case DK_STREAM_CMD_FINISH : {
	      a->return_value = 1;
	    } break;
	    case DK_STREAM_CMD_REWIND : {
	    } break;
	    case DK_STREAM_CMD_FLUSH : {
	      a->return_value = 1;
	    } break;
	    case DK_STREAM_CMD_AT_END : {
	    } break;
	    case DK_STREAM_CMD_GETS : {
	    } break;
	    case DK_STREAM_CMD_PUTS : {
	    } break;
	  }
	} break;
	case DK_STREAM_CMD_RDBUF : {
	  if(!((st->flags) & 1)) {
	    b = ((void *)((a->params).buffer));
	    l = ((int)((a->params).length));
	    if(b && (l > 0)) {
	      x = BZ2_bzread(bzf,b,l);
	      if(x > 0) {
	        (a->results).used = x;
	        a->return_value = 1;
	      } else {
	        st->flags |= 1;
	      }
	    }
	  }
	} break;
	case DK_STREAM_CMD_WRBUF : {
	  b = ((void *)((a->params).buffer));
	  l = ((int)((a->params).length));
	  if(b && (l > 0)) {
	    x = BZ2_bzwrite(bzf,b,l);
	    if(x > 0) {
	      (a->results).used = x;
	      a->return_value = 1;
	    }
	  }
	} break;
	case DK_STREAM_CMD_FINAL : {
	  BZ2_bzflush(bzf);
	  a->return_value = 1;
	} break;
	case DK_STREAM_CMD_FINISH : {
	  BZ2_bzclose(bzf);
	  a->return_value = 1;
	  st->data = NULL;
	  st->fct  = NULL;
	} break;
	case DK_STREAM_CMD_REWIND : {
	} break;
	case DK_STREAM_CMD_FLUSH : {
	  BZ2_bzflush(bzf);
	  a->return_value = 1;
	} break;
	case DK_STREAM_CMD_AT_END : {
	} break;
	case DK_STREAM_CMD_GETS : {
	} break;
	case DK_STREAM_CMD_PUTS : {
	} break;
      }
    }
#endif
  }
}



/**	Low-level function for bzip2 compressed streams, non-closing.
	@param	a	Stream API structure.
*/
void bz2_stream_function DK_P1(dk_stream_api_t *, a)
{
#if DK_HAVE_BZLIB_H
  if(a) {
    if(a->cmd == DK_STREAM_CMD_FINISH) {
      a->cmd = DK_STREAM_CMD_FINAL;
      bz2_stream_fct(a);
      a->cmd = DK_STREAM_CMD_FINISH;
    } else {
      bz2_stream_fct(a);
    }
  }
#endif
}



/**	Low-level function for gzip compressed streams, closing.
	@param	a	Stream API structure.
*/
void
gz_stream_fct DK_P1(dk_stream_api_t *, a)
{
#if DK_HAVE_ZLIB_H
  gzFile gzf;
  dk_stream_t *st;
  char        *b;
  size_t       l;
  void        *d;
  int x;
#endif
  
  if(a) {
    a->return_value = 0;
#if DK_HAVE_ZLIB_H
    st = (dk_stream_t *)(a->strm);
    if(st) {					
      d = st->data;
      if(d) {					
	gzf = (gzFile)d;
	switch(a->cmd) {
	  case DK_STREAM_CMD_TEST: {		
	    switch( (a->params).cmd) {
	      case DK_STREAM_CMD_TEST: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_RDBUF: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_WRBUF: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_FINAL: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_FINISH: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_REWIND: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_FLUSH: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_AT_END: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_GETS: {
		a->return_value = 1;
	      } break;
	      case DK_STREAM_CMD_PUTS: {
		a->return_value = 1;
	      } break;
	    }
	  } break;
	  case DK_STREAM_CMD_RDBUF: {		
	    b = (a->params).buffer; l = (a->params).length;
	    if(b && l) {			
	      x = gzread(gzf,b,l);
	      if(x > 0) {			
		(a->results).used = x;
		a->return_value = 1;
	      }
	    }
	  } break;
	  case DK_STREAM_CMD_WRBUF: {		
	    b = (a->params).buffer; l = (a->params).length;
	    if(b && l) {			
	      x = gzwrite(gzf,b,l);
	      if(x > 0) {			
		(a->results).used = x;
		a->return_value = 1;
	      }
	    }
	  } break;
	  case DK_STREAM_CMD_FINAL: {		
	    if(gzflush(gzf, Z_FULL_FLUSH) == Z_OK) {		
	      a->return_value = 1;
	    }
	  } break;
	  case DK_STREAM_CMD_FINISH: {		
	    if(gzclose(gzf) == Z_OK) {		
	      a->return_value = 1;
	    }
	    st->data = NULL;
	    st->fct  = NULL;
	  } break;
	  case DK_STREAM_CMD_REWIND: {		
	    if(gzrewind(gzf) == Z_OK) {		
	      a->return_value = 1;
	    }
	  } break;
	  case DK_STREAM_CMD_FLUSH: {		
	    if(gzflush(gzf, Z_FULL_FLUSH) == Z_OK) {		
	      a->return_value = 1;
	    }
	  } break;
	  case DK_STREAM_CMD_AT_END: {		
	    if(gzeof(gzf) == 1) {		
	      a->return_value = 1;
	    }
	  } break;
	  case DK_STREAM_CMD_GETS: {		
	    b = (a->params).buffer; l = (a->params).length;
            (a->results).buffer = b; (a->results).length = l;
            if(b && l) {
	      if(gzgets(gzf,b,l)) {		
		a->return_value = 1;
	      }
	    }
	  } break;
	  case DK_STREAM_CMD_PUTS: {
	    int i;
	    
	    b = (a->params).buffer;
	    if(b) {
	      i = gzputs(gzf,b);
	      if(i > 0) {
		a->return_value = 1; (a->results).length = i;
	      }
	    }
	  } break;
	}
      } else {				
      }
    } else {				
    }
#endif
    
  } else { 
  }
}



/**	Low-level function for gzip compressed streams, non-closing.
	@param	a	Stream API structure.
*/
void
gz_stream_function DK_P1(dk_stream_api_t *, a)
{
#if DK_HAVE_ZLIB_H
  if(a) {
    if(a->cmd == DK_STREAM_CMD_FINISH) {
      a->cmd = DK_STREAM_CMD_FINAL;
      gz_stream_fct(a);
      a->cmd = DK_STREAM_CMD_FINISH;
    } else {
      gz_stream_fct(a);
    }
  }
#endif
}



int
dkstream_puts	DK_P2(dk_stream_t *, st, char *, str)
{
  int back = 0;
  dk_stream_api_t *a;
  dk_stream_fct_t *fct;

  if(st && str) {
    a = (dk_stream_api_t *)(&(st->api));
    a->cmd = DK_STREAM_CMD_TEST; (a->params).cmd = DK_STREAM_CMD_PUTS;
    fct = st->fct;
    (*fct)(a);
    if(a->return_value) {
      a->cmd = DK_STREAM_CMD_PUTS;
      (a->params).buffer = str;
      (*fct)(a);
      back = a->return_value;
      if(back) {
        st->bytes += strlen(str);
      }
    } else {
      size_t sz;
      sz = strlen(str);
      a->cmd = DK_STREAM_CMD_WRBUF;
      (a->params).buffer = str;
      (a->params).length = sz;
      (a->results).buffer = str;
      (a->results).length = sz;
      (*fct)(a);
      back = a->return_value;
      if(back) {
        st->bytes += (a->results).used;
      }
      if(((a->results).length) != sz) {
	back = 0;
      }
    }
  }
  return back;
}



int
dkstream_puts_double_use_exp DK_P2(dk_stream_t *, st, double, d)
{
  int back;
  char buffer[64];
  sprintf(buffer, "%lg", d);
  back = dkstream_puts(st, buffer);
  return back;
}



int
dkstream_puts_double DK_P2(dk_stream_t *, st, double, d)
{
  int back;
  if((st->opt) & DK_STREAM_OPT_DOUBLE_NO_EXPONENT) {
    back = dkstream_puts_double_no_exp(st, d);
  } else {
    back = dkstream_puts_double_use_exp(st, d);
  }
  return back;
}



int
dkstream_puts_ul DK_P2(dk_stream_t *, st, unsigned long, ul)
{
  int back;
  char buffer[64];
  sprintf(buffer, "%lu", ul);
  back = dkstream_puts(st, buffer);
  return back;
}



int
dkstream_puts_long DK_P2(dk_stream_t *, st, long, l)
{
  int back;
  char buffer[64];
  sprintf(buffer, "%ld", l);
  back = dkstream_puts(st, buffer);
  return back;
}



char *
dkstream_gets DK_P3(dk_stream_t *, st, char *, b, size_t, l)
{
  char *back = NULL;
  dk_stream_api_t *a;
  void *d;
  dk_stream_fct_t *fct;
  int goon;
  char c;
  size_t used;
  
  if(st && b && l) { 
    a = &(st->api);
    d = st->data; fct = st->fct;
    if(d && fct) { 
      a->cmd = DK_STREAM_CMD_TEST;
      (a->params).cmd = DK_STREAM_CMD_GETS;
      (*fct)(a);
      if(a->return_value) {	
	(a->params).buffer = b;
	(a->params).length = l;
	a->cmd = DK_STREAM_CMD_GETS;
	(*fct)(a);
	if(a->return_value) {
	  back = b;
	}
      } else {			
	goon = 1; used = 0;
	while(goon) {
	  (a->params).buffer = &c;
	  (a->params).length = 1;
	  (a->results).buffer = &c;
	  (a->results).length = 1;
	  a->cmd = DK_STREAM_CMD_RDBUF;
	  (*fct)(a);
	  if(a->return_value) {
            if(((a->results).used) == 1) {
	      switch(c) {
		case '\0': {
		  b[used++] = '\0'; goon = 0;
		} break;
		case '\r':
		case '\n': {
		  b[used++] = c; b[used] = '\0'; goon = 0;
		} break;
		default: {
		  b[used++] = c;
		  if(used >= (l - 1)) {
		    goon = 0; b[used] = '\0';
		  }
		} break;
	      }
	    } else {
	      b[used] = '\0'; goon = 0;
	    }
	  } else {
	    b[used] = '\0'; goon = 0;
	  }
	}
	if(used) {
	  back = b;
	}
      }
    }
  }
  
  return back;
}



dk_stream_t *
dkstream_openfile DK_P4(char *, name, char *, mode, int, ign, int *, reason)
{
  FILE *fipo;
  dk_stream_t *back = NULL;
  if(name && mode) {
#if NO_FOPEN_CHECKING
#if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64
    fipo = fopen64(name, mode);
#else
    fipo = fopen(name, mode);
#endif
#else
    fipo = dksf_msfo(name,mode,ign,reason);
#endif
    if(fipo) {
      back = dkstream_new((void *)fipo, file_stream_fct);
      if(!back) {
	fclose(fipo);
      }
    }
  }
  return back;
}



dk_stream_t *
dkstream_for_file DK_P1(FILE *, fipo)
{
  dk_stream_t *back = NULL;
  if(fipo) {
    back = dkstream_new((void *)fipo, file_stream_function);
  }
  return back;
}



#if DK_HAVE_ZLIB_H
/**	Correct the open mode for gzip compressed files.
	@param	m	Mode suggested by the application.
	@param	l	Length of buffer used by \a m.
*/
static
void
correct_gz_mode(char *m, size_t l)
{
  int want_to_write;
  int have_compression_level;
  char *ptr;
  size_t lgt;
  
  want_to_write = 0; have_compression_level = 0;
  lgt = strlen(m);
  ptr = m;
  while(*ptr) {
    switch(*ptr) {
      case 'w': {
	want_to_write = 1;
      } break;
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      {
	have_compression_level = 1;
      } break;
    }
    ptr++;
  }
  if(want_to_write && (!have_compression_level)) {
    if(lgt < (l - 1)) {
      m[lgt] = '9'; m[lgt+1] = '\0';
    }
  } 
}
#endif



/**	Check whether the open mode indicates write operations.
	@param	mode	File open mode string.
	@return	1 for write operations, 0 for no write operations.
*/
static int
is_open_mode_write DK_P1(char *,mode)
{
  int back = 0;
  char *ptr;
  if(mode) {
    ptr = mode;
    while(*ptr) {
      switch(*ptr) {
        case 'w':
	case 'a':
	case '+':
	{
	  back = 1;
	} break;
      }
      ptr++;
    }
  }
  
  return back;
}



dk_stream_t *
dkstream_opengz DK_P4(char *, name, char *, mode, int, ign, int *, reason)
{
  dk_stream_t *back = NULL;
#if DK_HAVE_ZLIB_H
  size_t my_mode_used;
  char my_mode[8];
  gzFile gzf;
  if(name && mode) {
    my_mode_used = strlen(mode);
    if(my_mode_used < sizeof(my_mode)) {
      strcpy(my_mode, mode);
      correct_gz_mode(my_mode, sizeof(my_mode));
#if NO_FOPEN_CHECKING
      gzf = gzopen(name, my_mode);
#else
      gzf = NULL;
      if(is_open_mode_write(my_mode)) {
      if(dksf_allowed_to_write(name,ign,reason)) {
        gzf = gzopen(name, my_mode);
      }
      } else {
        gzf = gzopen(name, my_mode);
      }
#endif
      if(gzf) {
	back = dkstream_new((void *)gzf, gz_stream_fct);
	if(!back) {
	  gzclose(gzf);
	}
      }
    }
  }
#endif
  return back;
}



#if DK_HAVE_ZLIB_H
dk_stream_t *
dkstream_for_gz DK_P1(gzFile, gzf)
{
  dk_stream_t *back = NULL;
  if(gzf) {
    back = dkstream_new((void *)gzf, gz_stream_function);
  }
  return back;
}
#endif



dk_stream_t *
dkstream_openbz2 DK_P4(char *, name, char *, mode, int, ign, int *, reason)
{
  dk_stream_t *back = NULL;
#if DK_HAVE_BZLIB_H
  BZFILE *bzf;
  if(name && mode) {
#if NO_FOPEN_CHECKING
    bzf = BZ2_bzopen(name,mode);
#else
    bzf = NULL;
    if(is_open_mode_write(mode)) {
    if(dksf_allowed_to_write(name,ign,reason)) {
      bzf = BZ2_bzopen(name,mode);
    }
    } else {
      bzf = BZ2_bzopen(name,mode);
    }
#endif
    if(bzf) {
      back = dkstream_new((void *)bzf, bz2_stream_fct);
      if(!back) {
	BZ2_bzclose(bzf);
      }
    }
  }
#endif
  return back;
}



#if DK_HAVE_BZLIB_H
dk_stream_t *
dkstream_for_bz2 DK_P1(BZFILE *, bzf)
{
  dk_stream_t *back = NULL;
  if(bzf) {
    back = dkstream_new((void *)bzf, bz2_stream_function);
  }
  return back;
}
#endif



void
dkstream_close DK_P1(dk_stream_t *, st)
{
  dk_stream_api_t *a;
  dk_stream_fct_t *f;
  void *d;
  if(st) {
    f = st->fct; d = st->data;
    if(f && d) {
      a = &(st->api);
      a->cmd = DK_STREAM_CMD_FINISH;
      (*f)(a);
      /* ##### 2001/07/04 - D. Krause */
      /* prevent FINAL from dkstream_delte() after FINISH */
      st->fct = NULL;
      st->data = NULL;
      /* ##### */
    }
    dkstream_delete(st);
  }
}




/* ********************************************************************* */
/* *                                                                   * */
/* *         The following functions do not depend on low              * */
/* *         level functionality directly.                             * */
/* *                                                                   * */
/* ********************************************************************* */

#line 1041 "dkstream.ctr"




int
dkstream_wb_word DK_P2(dk_stream_t *, st, dk_word, w)
{
  int back = 0;
  dk_word x;
  if(st) {
    x = dkenc_htons(w);
    if(dkstream_write(st, ((char *)(&x)), sizeof(dk_word)) == sizeof(dk_word)) {
      back = 1;
    }
  }
  return back;
}



int
dkstream_wb_uword DK_P2(dk_stream_t *, st, dk_uword, w)
{
  int back = 0;
  dk_uword x;
  if(st) {
    x = dkenc_htons(w);
if(dkstream_write(st, ((char *)(&x)), sizeof(dk_uword)) == sizeof(dk_uword)) {
      back = 1;
}
  }
  return back;
}



int
dkstream_wb_dword DK_P2(dk_stream_t *, st, dk_dword, w)
{
  int back = 0;
  dk_dword x;
  if(st) {
    x = dkenc_htonl(w);
if(dkstream_write(st, ((char *)(&x)), sizeof(dk_dword)) == sizeof(dk_dword)) {
      back = 1;
}
  }
  return back;
}



int
dkstream_wb_udword DK_P2(dk_stream_t *, st, dk_udword, w)
{
  int back = 0;
  dk_udword x;
  if(st) {
    x = dkenc_htonl(w);
if(dkstream_write(st, ((char *)(&x)), sizeof(dk_udword)) == sizeof(dk_udword)) {
    back = 1;
}
  }
  return back;
}



int dkstream_rb_word DK_P2(dk_stream_t *, st, dk_word *, w)
{
  int back = 0;
  dk_word x;
  if(st && w) {
if(dkstream_read(st, ((char *)(&x)), sizeof(dk_word)) == sizeof(dk_word)) {
    *w = dkenc_ntohs(x);
    back = 1;
}
  }
  return back;
}



int dkstream_rb_uword DK_P2(dk_stream_t *, st, dk_uword *, w)
{
  int back = 0;
  dk_uword x;
  if(st && w) {
if(dkstream_read(st, ((char *)(&x)), sizeof(dk_uword)) == sizeof(dk_uword)) {
    *w = dkenc_ntohs(x);
    back = 1;
}
  }
  return back;
}



int
dkstream_rb_dword DK_P2(dk_stream_t *, st, dk_dword *, w)
{
  int back = 0;
  dk_dword x;
  if(st && w) {
if(dkstream_read(st, ((char *)(&x)), sizeof(dk_dword)) == sizeof(dk_dword)) {
    *w = dkenc_ntohl(x);
    back = 1;
}
  }
  return back;
}



int
dkstream_rb_udword DK_P2(dk_stream_t *, st, dk_udword *, w)
{
  int back = 0;
  dk_udword x;
  if(st && w) {
if(dkstream_read(st, ((char *)(&x)), sizeof(dk_udword)) == sizeof(dk_udword)) {
    *w = dkenc_ntohl(x);
    back = 1;
}
  }
  return back;
}



int
dkstream_wb_string DK_P2(dk_stream_t *,st, char *,str) {
  int		back = 0;	/**< return value */
  size_t	sl;		/**< string length from strlen() */
  dk_uword	lgt;		/**< string length saved to stream */
  if(st) {
    if(str) {
      sl = strlen(str); sl++;
      if(sl) {
        if(sl <= (size_t)0xFFFFU) {
	  lgt = (dk_uword)sl;
	  if(dkstream_wb_uword(st,lgt)) {
	    if(dkstream_write(st, str, lgt) == lgt) {
	      back = 1;	/* success */
	    } else {	/* number of bytes differs */
	    }
	  } else {	/* failed to write length */
	  }
	} else {	/* numeric overflow */
	}
      } else {		/* numeric overflow */
      }
    }
  }
  return back;
}



char *
dkstream_rb_string DK_P1(dk_stream_t *, st)
{
  char *back = NULL;
  dk_uword lgt;
  if(st) {
    if(dkstream_rb_uword(st,&lgt)) {
      if(lgt > 0) {
        back = dk_new(char,lgt);
        if(back) {
	  if(dkstream_read(st, back, lgt) == lgt) {
	    back[lgt-1] = '\0';
	  } else {
	    dk_delete(back); back = NULL;
	  }
        }
      }
    }
  }
  return back;
}



/**	No suffix for plain (uncompressed) files.
*/
static char suffix_none[] = { "" };

#if DK_HAVE_ZLIB_H
/**	Suffix for gzip compressed files.
*/
static char suffix_gz[] = { ".gz" };
#endif

#if DK_HAVE_BZLIB_H
/**	Suffix for bzip2 compressed files.
*/
static char suffix_bz2[] = { ".bz2" };
#endif



/**	Table for file types to read.
*/
dk_stream_suffix_t dkstream_read_suffix_list[] = {
  { suffix_none, (dk_stream_open_fct_t *)dkstream_openfile },
#if DK_HAVE_ZLIB_H
  { suffix_gz,   (dk_stream_open_fct_t *)dkstream_opengz },
#endif
#if DK_HAVE_BZLIB_H
  { suffix_bz2,  (dk_stream_open_fct_t *)dkstream_openbz2 },
#endif
  { NULL, (dk_stream_open_fct_t *)NULL }
};



/**	Table for file types to write.
*/
dk_stream_suffix_t dkstream_write_suffix_list[] = {
#if DK_HAVE_BZLIB_H
  { suffix_bz2,  (dk_stream_open_fct_t *)dkstream_openbz2 },
#endif
#if DK_HAVE_ZLIB_H
  { suffix_gz,   (dk_stream_open_fct_t *)dkstream_opengz },
#endif
  { suffix_none, (dk_stream_open_fct_t *)dkstream_openfile },
  { NULL, (dk_stream_open_fct_t *)NULL }
};



dk_stream_suffix_t *dkstream_get_read_suffixes DK_P0()
{
  return dkstream_read_suffix_list;
}



dk_stream_suffix_t *dkstream_get_write_suffixes DK_P0()
{
  return dkstream_write_suffix_list;
}



unsigned long
dkstream_get_bytes_written DK_P1(dk_stream_t *,st)
{
  unsigned long back = 0UL;
  if(st) { back = st->bytes; }
  return back;
}



int
dkstream_puts_array DK_P2(dk_stream_t *,st, char **,array)
{
  int back = 0;
  char **ptr = NULL;
  
  if(st && array) {
    back = 1; ptr = array;
    while(*ptr) {
      if(!dkstream_puts(st, *ptr)) {
        back = 0;
      }
      if(!dkstream_puts(st, str_nl)) {
        back = 0;
      }
      ptr++;
    }
  }
  
  return back;
}



/**	Check whether a character is a digit 1-9.
	@param	c	Character to check.
	@return	1 on true, 0 on false.
*/
static int
is_non_zero DK_P1(char,c) {
  int back = 0;
  switch(c) {
    case '1': case '2': case '3': case '4': case '5':
    case '6': case '7': case '8': case '9': {
      back = 1;
    } break;
  }
  return back;
}



/**	Index of first non-zero digit in text.
	@param	s	Text to search.
	@return	Position index of the first non-zero digit in \a s.
*/
static
int
first_non_zero DK_P1(char *,s) {
  int back = -1, i = 0;
  char *p;
  p = s; i = 0;
  while((back == -1) && (*p)) {
    if(is_non_zero(*p)) { back = i; }
    p++; i++;
  }
  return back;
}



/**	Used to print a double value as text.
*/
static char str_0[] = { "0" };

/**	Used to print a double value as text.
*/
static char str_d[] = { "." };

/**	Used to print a double value as text.
*/
static char str_e[] = { "e" };


int
dkstream_puts_double_str_no_exp DK_P2(dk_stream_t *,stream, char *,s) {
  int back = 0;
  char buffer[64];
  char *p1, *p2, *pe, *sptr, *dptr;
  int evalue, itest, dotpos, fnz, max, i, sl;
  size_t sz;
  p1 = dkstr_start(s, NULL);
  if(p1) {
    back = 1;
    if(*p1 == '-') {
      if(dkstream_write(stream, p1++, 1) < 1) {
        back = 0;
      }
    }
    pe = strchr(p1, 'e');
    if(!pe) { pe = strchr(p1, 'E'); }
    if(pe) {
      *(pe++) = '\0';
      if(sscanf(pe, "%d", &itest) == 1) {
        evalue = itest;
	if(evalue != 0) {
	  p2 = strchr(p1, '.');
	  if(p2) { *(p2++) = '\0'; }
	  sz = 0;
	  if(p1) { sz += strlen(p1); }
	  if(p2) { sz += strlen(p2); }
	  if(sz < sizeof(buffer)) {
	    buffer[0] = '\0';
	    if(p1) { strcat(buffer, p1); }
	    if(p2) { strcat(buffer, p2); }
	    dotpos = 0;
	    if(p1) { dotpos = strlen(p1); }
	    fnz = first_non_zero(buffer);
	    if(fnz > -1) {
	      if(fnz > 0) {
	        dptr = buffer; sptr = &(buffer[fnz]);
		while(*sptr) { *(dptr++) = *(sptr++); }
		*dptr = '\0';
		dotpos -= fnz;
	      }
	      dotpos += evalue;
	      if(dotpos <= 0) {	
		if(!dkstream_puts(stream, str_0)) { back = 0; }
		if(!dkstream_puts(stream, str_d)) { back = 0; }
		max = 0 - dotpos;
		for(i = 0; i < max; i++) {
		  if(!dkstream_puts(stream, str_0)) { back = 0; }
		}
		if(!dkstream_puts(stream, buffer)) { back = 0; }
	      } else {		
	        sl = max = strlen(buffer);
		if(dotpos > max) { max = dotpos; }
		for(i = 0; i < max; i++) {
		  if(i < sl) {
		    if(dkstream_write(stream, &(buffer[i]), 1) < 1) {
		      back = 0;
		    }
		  } else {
		    if(!dkstream_puts(stream, str_0)) { back = 0; }
		  }
		  if(((i + 1) == dotpos) && (dotpos < sl)) {
		    if(!dkstream_puts(stream, str_d)) { back = 0; }
		  }
		}
#if 0
		if(dotpos >= sl) {
		  if(!dkstream_puts(stream, str_0)) { back = 0; }
		}
#endif
	      }
	    } else {		
	      back = dkstream_puts(stream, str_0);
	    }
	  } else {		
	    back = 0;
	    if(p1) { dkstream_puts(stream, p1); }
	    if(p2) { dkstream_puts(stream, str_d); dkstream_puts(stream, p2); }
	    dkstream_puts(stream, str_e);
	    dkstream_puts_long(stream, (long)evalue);
	  }
	} else {		
	  back = dkstream_puts(stream, p1);
	}
      } else {			
        dkstream_puts(stream, p1);
	back = 0;
      }
    } else {			
      back = dkstream_puts(stream, p1);
    }
  } else {			
    dkstream_puts(stream, str_0);
  }
  return back;
}



int
dkstream_puts_double_no_exp DK_P2(dk_stream_t *,stream, double,d) {
  int back = 0;
  char buffer[64];
  sprintf(buffer, "%lg", d);
  dkstream_puts_double_str_no_exp(stream, buffer);
  return back;
}



void
dkstream_set_double_no_exponent DK_P2(dk_stream_t *,s, int,f) {
  if(s) {
    if(f) {
      s->opt |= DK_STREAM_OPT_DOUBLE_NO_EXPONENT;
    } else {
      s->opt &= (~(DK_STREAM_OPT_DOUBLE_NO_EXPONENT));
    }
  }
}



int
dkstream_get_double_no_exponent DK_P1(dk_stream_t *,s) {
  int back = 0;
  if(s) {
    if((s->opt) & DK_STREAM_OPT_DOUBLE_NO_EXPONENT) {
      back = 1;
    }
  }
  return back;
}



