/*
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	dkapp.c	The dkapp module.
*/


#line 43 "dkapp.ctr"




#include <stdio.h>

#include "dk.h"
#include "dktypes.h"
#include "dkmem.h"
#include "dksf.h"
#include "dkma.h"
#include "dkstr.h"
#include "dksto.h"
#include "dklogc.h"
#include "dkstt.h"
#include "dkstream.h"
#include "dkcp.h"
#include "dkenc.h"

#if DK_TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if DK_HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#if DK_HAVE_TIME_H
#include <time.h>
#endif
#endif
#endif

#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_UNISTD_H
#include <unistd.h>
#endif

/**	Inside the dkapp.c module.
*/
#define DK_APP_C 1
#include "dkapp.h"
#include "dkrandc.h"

#if DK_HAVE_STRING_H
#include <string.h>
#endif

#if DK_HAVE_WINREG_H
#include "dkwin.h"
#endif

#if DK_HAVE_SYSLOG_H
#include <syslog.h>
#endif

#if DK_HAVE_CTYPE_H
#include <ctype.h>
#endif

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



/**	Default system configuration directory on *x systems.
*/
static char unix_sysconfdir[] = { DK_SYSCONFDIR };


/** Pointer to character buffer. */
typedef char *CHARPTR;


/** String table entry. */
typedef struct {
  char *name;	/**< Name of string table. */
  dk_stt_t *st;	/**< Pointer to string table structure. */
} stt_entry;



/**	Flag: Run silently.
*/
static int set_silent = 0;



/**	Preference key: Logging disabled.
*/
static char key_log_off[]= { "/log/off" };



/**	Encoding: UTF-8.
*/
static char str_utf8_a[] = { "utf-8" };

/**	Variant of encoding: UTF-8
*/
static char str_utf8_b[] = { "utf8" };


/**	File open mode: Read.
*/
static char str_r[] = { "r" };


/* ********************************************************************* */
/* *                                                                   * */
/* *   Check whether or not the command line option                    * */
/* *   --/log/off=yes                                                  * */
/* *   was used to suppress logging to stdout/stderr.                  * */
/* *                                                                   * */
/* ********************************************************************* */

#line 156 "dkapp.ctr"


int
dkapp_silence_check DK_P2(int,argc,char **,argv)
{
  int back = 0;
  int i;
  char *cptr, *vptr;
  for(i = 1; i < argc; i++) {
    cptr = argv[i]; vptr = NULL;
    if(cptr[0] == '-') {
    if(cptr[1] == '-') {
      vptr = dkstr_chr(cptr, '=');
      if(vptr) { *vptr = (char)0; }
      if(strcmp(&(cptr[2]), key_log_off) == 0) {
        if(vptr) {
	  if(dkstr_is_on(&(vptr[1]))) {
	    back = 1;
	  }
	} else {
	  back = 1;
	}
      }
      if(vptr) { *vptr = '='; }
    }
    }
  }
  return back;
}



/**	Release string table.
	@param	s	String table.
*/
static void stt_entry_free DK_P1(stt_entry *,s)
{
  char *cptr;
  cptr = s->name;
  if(cptr) { dk_delete(cptr); }
  s->name = NULL;
  if(s->st) { dkstt_close(s->st); }
  s->st = NULL;
  dk_delete(s) ;
}



/**	Compare string table container entries.
	@param	p1	Left string table.
	@param	p2	Right string table.
	@param	crit	Comparison criteria (0=table/table, 1=table/name).
	@return	Comparison result.
*/
static int
stt_entry_comp DK_P3(void *,p1,void *,p2,int,crit)
{
  int back = 0;
  stt_entry *s1, *s2;
  
  if(p1 && p2) {
    switch(crit) {
      case 1: {
	s1 = (stt_entry *)p1;
	if(s1->name) {
	  back = strcmp((s1->name),((char *)p2));
	} else {
	  back = -1;
	}
      } break;
      default: {
	s1 = (stt_entry *)p1; s2 = (stt_entry *)p2;
	if(s1->name) {
	  if(s2->name) {
	    back = strcmp((s1->name),(s2->name));
	  } else {
	    back = 1;
	  }
	} else {
	  if(s2->name) {
	    back = -1;
	  }
	}
      } break;
    }
  } 
  return back;
}



/**	The "all" scope is used when retrieving preferences from the registry.
*/
static char all_files[] = { "all" };

/**	The general preferences scope.
*/
static char scope_all[] = { "*" };

/**	Dot used to construct file names.
*/
static char dot[] = { "." };

/**	Name "app".
*/
static char app[] = { "app" };

/**	Suffix for log files.
*/
static char suffix_log[] = { "log" };

/**	Suffix for temporary directories.
*/
static char dot_tmp[] = { ".TMP" };



#if DK_HAVE_WINREG_H
/**	Registry key name.
*/
static char hklm[] = { "Software\\DkApp" };

/**	Registry key name.
*/
static char hkcu[] = { "SOFTWARE\\DkApp" };

/**	Backslash.
*/
static char backslash[] = { "\\" };
#endif



/**	Separator between path components.
*/
static char fn_sep[] = {
#if DK_HAVE_FEATURE_BACKSLASH
  "\\"
#else
  "/"
#endif
};



/**	Name of the bin subdirectory.
*/
static char bin_dir[] = { "bin" };

/**	Name of the share subdirectory.
*/
static char share_dir[] = { "share" };



/**	Name of configuration file subdirectory in HOME.
*/
static char defaults_sub[] = {
#if DK_HAVE_DOTFILENAMES
  ".defaults"
#else
  "defaults"
#endif
};


/**	File name for configuration file.
*/
static char appdef[] = { "appdef" };



/* ********************************************************************* */
/* *                                                                   * */
/* *   Comparison of preference entries.                               * */
/* *   cr = 0: pref entry / pref entry                                 * */
/* *   cr = 1: pref entry / entry name as string                       * */
/* *                                                                   * */
/* ********************************************************************* */

#line 331 "dkapp.ctr"



/**	Delete preference entry, release memory.
	@param	p	Preference entry to delete.
*/
void
dkpref_delete DK_P1(dk_preference_t *, p)
{
  char *x;
  
  if(p) {
    
    x = p->v;
    if(x) dk_delete(x);
    x = p->n;
    if(x) dk_delete(x);
    p->p = 0;
    p->v = NULL;
    p->n = NULL;
    dk_delete(p);
  }
  
}



/**	Create preference entry, allocate memory.
	@param	name	Preference name.
	@param	value	Preference value.
	@param	prio	Preference priority.
	@return	Pointer to new preference entry on success, NULL on error.
*/
dk_preference_t *
dkpref_new DK_P3(char *, name, char *, value, int, prio)
{
  dk_preference_t *back = NULL;
  
  if(name && value) {
    back = dk_new(dk_preference_t,1);
    if(back) {
      back->n = dkstr_dup(name);
      back->v = dkstr_dup(value);
      back->p = prio;
      if(!((back->v) && (back->n))) {
	dkpref_delete(back);
	back = NULL;
      }
    }
  } 
  return back;
}



/**	Compare two preference entries.
	@param	p1	Left preference entry.
	@param	p2	Right preference entry.
	@param	cr	Comparison criteria (0=pref/pref, 1=pref/name).
	@return	Comparison result.
*/
int
dkapp_pref_compare DK_P3(void *, p1, void *, p2, int, cr)
{
  int back = 0;
  
  if(p1 && p2) {
    switch(cr) {
      case 1: {  /* search for specific entry */
	
	back = strcmp((((dk_preference_t *)p1)->n),(char *)p2);
      } break;
      default: { /* sort entries in container */
	
	back = strcmp((((dk_preference_t *)p1)->n),(((dk_preference_t *)p2)->n));
      } break;
    }
  } 
  return back;
}



/**	Add a preference to the list.
	@param	s	Preferences storage.
	@param	si	Iterator for \a si.
	@param	n	Preference name.
	@param	v	Preference value.
	@param	p	0=general config file, 2=app-specific file,
			4=file from user home directory.
*/
static void
add_pref_to_list DK_P5(dk_storage_t *,s, dk_storage_iterator_t *,si,char *,n, char *,v, int,p)
{
  dk_preference_t *pptr;
  char *cptr;
  
  if(s && si && n && v) {
    pptr =
    (dk_preference_t *)dksto_it_find_like(si,n,1);
    if(pptr) { 
      if(p >= pptr->p) {
	cptr = pptr->v;
	if(cptr) dk_delete(cptr);
	pptr->v = dkstr_dup(v);
	pptr->p = p;
      }
    } else { 
      pptr = dkpref_new(n,v,p);
      if(pptr) {
	if(!dksto_add(s,pptr)) {
	  dkpref_delete(pptr);
	}
      }
    }
  } 
}




/* ********************************************************************* */
/* *                                                                   * */
/* *   Read configuration from a file.                                 * */
/* *   To select only entries matching current user name,              * */
/* *   application name and host name these names                      * */
/* *   are specified in un, an and hn.                                 * */
/* *                                                                   * */
/* ********************************************************************* */

#line 456 "dkapp.ctr"

/**	Read configuration from a file.
	@param	s	Storage to save settings to.
	@param	si	Iterator for \a s.
	@param	fn	File to read.
	@param	un	user name.
	@param	an	Application name.
	@param	hn	host name.
	@param	prio	0=general config file, 2=app-specific file,
			4=file from user home directory.
*/
static void
read_cfg DK_P7(dk_storage_t *, s, dk_storage_iterator_t *, si, char *, fn, char *, un, char *, an, char *, hn, int, prio)
{
  FILE *inputfile;
  char inputline[256], *cp1, *cp2, *cp3;
  int can_continue;
  int p_to_save;
  int use_entries;
  
  if(fn && s && si) {
    inputfile = dksf_fopen(fn, str_r);
    if(inputfile) { 
      can_continue = 1; use_entries = 1; p_to_save = 0;
      while(can_continue) {
	if(fgets(inputline,sizeof(inputline),inputfile)) {
	  cp2 = cp3 = NULL;
	  cp1 = dkstr_start(inputline, NULL);
	  if(cp1) {
	    cp2 = dkstr_chr(cp1, '#');
	    if(cp2) {
	      *cp2 = '\0';
	    }
	    cp1 = dkstr_start(cp1, NULL);
	    if(cp1) {
	      
	      if(*cp1 == '[') {
		use_entries = 0; p_to_save = 0;
		cp2 = dkstr_rchr(cp1, ']');
		if(cp2) {
		  *cp2 = '\0';
		  cp1++;
		  cp1 = dkstr_start(cp1, NULL);
		  if(cp1) {
		    dkstr_chomp(cp1, NULL);
		    cp2 = dkstr_chr(cp1, '/');
		    if(cp2) {
		      *(cp2++) = '\0';
		      cp3 = dkstr_chr(cp2, '/');
		      if(cp3) {
			*(cp3++) = '\0';
		      }
		    }
                    switch(prio) {
		      case 0: {
			if(cp1) {
			  if(strcmp(cp1, scope_all) == 0) {
			    use_entries = 1;
			  } else {
			    if(un) {
			      if(strcmp(cp1, un) == 0) {
				use_entries = 1; p_to_save = 4;
			      }
			    }
			  }
			  if(use_entries) {
			    use_entries = 0;
			    if(cp2) {
			      if(strcmp(cp2, scope_all) == 0) {
				use_entries = 1;
			      } else {
				if(an) {
				  if(strcmp(cp2, an) == 0) {
				    use_entries = 1; p_to_save |= 2;
				  }
				}
			      }
			      if(use_entries) {
				use_entries = 0;
				if(cp3) {
				  if(strcmp(cp3, scope_all) == 0) {
				    use_entries = 1;
				  } else {
				    if(hn) {
				      if(strcmp(cp3, hn) == 0) {
					use_entries = 1; p_to_save |= 1;
				      }
				    }
				  }
				} else {
				  use_entries = 1;
				}
			      }
			    } else {
			      use_entries = 1;
			    }
			  }
			} else {
			  use_entries = 1;
			}
		      } break;
		      case 2: {
			if(cp1) {
			  if(strcmp(cp1, scope_all) == 0) {
			    use_entries = 1;
			  } else {
			    if(un) {
			      if(strcmp(cp1, un) == 0) {
				use_entries = 1; p_to_save |= 4;
			      }
			    }
			  }
			  if(use_entries) {
			    use_entries = 0;
			    if(cp2) {
			     if(strcmp(cp2, scope_all) == 0) {
			       use_entries = 1;
			     } else {
			       if(hn) {
				 if(strcmp(cp2, hn) == 0) {
				   use_entries = 1; p_to_save |= 1;
				 }
			       }
			     }
			    } else {
			      use_entries = 1;
			    }
			  }
			} else {
			  use_entries = 1;
			}
		      } break;
		      case 4: {
			if(cp1) {
			  if(strcmp(cp1, scope_all) == 0) {
			    use_entries = 1;
			  } else {
			    if(an) {
			      if(strcmp(cp1, an) == 0) {
				use_entries = 1; p_to_save |= 2;
			      }
			    }
			  }
			  if(use_entries) {
			    use_entries = 0;
			    if(cp2) {
			      if(strcmp(cp2, scope_all) == 0) {
				use_entries = 1;
			      } else {
				if(hn) {
				  if(strcmp(cp2, hn) == 0) {
				    use_entries = 1; p_to_save |= 1;
				  }
				}
			      }
			    } else {
			      use_entries = 1;
			    }
			  }
			} else {
			  use_entries = 1;
			}
		      } break;
		      case 6: {
			if(cp1) {
			  if(strcmp(cp1, scope_all) == 0) {
			    use_entries = 1;
			  } else {
			    if(hn) {
			      if(strcmp(cp1, hn) == 0) {
				use_entries = 1; p_to_save |= 1;
			      }
			    }
			  }
			} else {
			  use_entries = 1;
			}
		      } break;
		    }
		  }
		}
	      } else { /* if(*cp1 == '[') */
	        if(use_entries) {
		  cp2 = dkstr_chr(cp1, '=');
		  if(cp2) {
		    *(cp2++) = '\0';
		    cp2 = dkstr_start(cp2, NULL);
		    if(cp2) {
		      cp1 = dkstr_start(cp1, NULL);
		      if(cp1) {
		        dkstr_chomp(cp1, NULL);
		        dkstr_chomp(cp2, NULL);
		        add_pref_to_list(s,si,cp1,cp2,(p_to_save | prio));
		      }
		    }
		  }
	        }
	      }
	    }
	  }
	} else {
	  can_continue = 0;
	}
      }
      fclose(inputfile);
    }
  }
  
}



/**	Initialize application structure.
	@param	a	Application.
*/
static void
app_init_empty DK_P1(dk_app_t *, a)
{
  
  if(a) {
    (a->td).l = 0UL;
    (a->a).o.argc = 0; (a->a).o.argv = NULL;
    (a->a).a.argc = 0; (a->a).a.argv = NULL;
    (a->n).u = NULL;
    (a->n).a = NULL;
    (a->n).h = NULL;
    (a->n).g = NULL;
    (a->d).h = NULL;
    (a->d).t = NULL;
    (a->d).a = NULL;
    (a->d).s = NULL;
    (a->d).pt = NULL;
    (a->d).etc = NULL;
    (a->p).c = NULL;
    (a->p).ci = NULL;
    (a->p).a = NULL;
    (a->p).ai = NULL;
#if DK_HAVE_WINREG_H
    (a->p).what = 0;
#else
    (a->p).u = NULL;
    (a->p).ui = NULL;
    (a->p).s = NULL;
    (a->p).si = NULL;
#endif
    (a->p).unc  = 0;
    (a->p).prf  = 0;
    (a->x).f = NULL;
    (a->x).d = NULL;
    (a->l).max = DK_LOG_LEVEL_IGNORE;
    (a->l).nostdio = 0;
    (a->l).o.m = DK_LOG_LEVEL_NONE;
    (a->l).o.f = 0;
    (a->l).o.ide_type = 0;
    (a->l).e.m = DK_LOG_LEVEL_WARNING;
    (a->l).e.f = 0;
    (a->l).e.ide_type = 0;
    (a->l).f.m = DK_LOG_LEVEL_WARNING;
    (a->l).f.k = DK_LOG_LEVEL_ERROR;
    (a->l).f.f = 3;
    (a->l).f.t = NULL;
    (a->l).f.n = NULL;
    (a->l).f.ide_type = 0;
#if DK_HAVE_SYSLOG
    (a->l).s.m = DK_LOG_LEVEL_NONE;
    (a->l).s.i = NULL;
    (a->l).s.f = 0;
    (a->l).s.o = 0;
#endif
    (a->l).o.c = NULL;
    (a->l).e.c = NULL;
    (a->l).f.c = NULL;
    (a->l).ef.n = NULL;
    (a->l).ef.lineno = 0UL;
    (a->loc).l = NULL;
    (a->loc).r = NULL;
    (a->loc).e = NULL;
    (a->loc).s = NULL;
    (a->loc).si = NULL;
    (a->loc).es = DK_APP_ENCODING_DEFAULT;
    a->relaxed_fopen_check = 0;
    a->keep_temp_dir = DK_LOG_LEVEL_NONE;
    (a->random).prng_type = DK_RAND_TYPE_NONE;
    (a->random).seed_file_name = NULL;
  }
}

#if DK_HAVE_WINREG_H
/**	Colon.
*/
static char colon[] = { ":" };

/**	Slash.
*/
static char slash[] = { "/" };
#endif




/* ********************************************************************* */
/* *                                                                   * */
/* *   Set a preference.                                               * */
/* *   For UNIX/Linux the preference is kept in memory until           * */
/* *   the application is finished, when shutting down the             * */
/* *   application the configuration file HOME/.defaults/app_name      * */
/* *   is rewritten if necessary.                                      * */
/* *   Preference changes on Windows systems go directly to the        * */
/* *   registry.                                                       * */
/* *                                                                   * */
/* ********************************************************************* */

#line 763 "dkapp.ctr"

int dkapp_set_pref DK_P3(dk_app_t *,a,char *,key,char *,value)
{
  int back = 0;
#if DK_HAVE_WINREG_H
  char keybuffer[256];
#endif
  
  if(a && key && value) {
    (a->p).prf = 1;
    if(((a->p).a) && ((a->p).ai)) {
      add_pref_to_list(((a->p).a), ((a->p).ai), key, value, 6);
      back = 1;
    }
#if DK_HAVE_WINREG_H
    if(((a->n).h) && (((a->p).what) & 8)) {
      if(strlen(key) < sizeof(keybuffer)) {
	strcpy(keybuffer, key);
	RegSetValueExA(
	  (a->p).hkcu_app,
	  keybuffer,
	  0,
	  REG_SZ,
	  value,
	  (strlen(value) + 1)
	);
      }
    }
#endif
  } 
  return back;
}



int dkapp_get_pref_env DK_P6(dk_app_t *,a,char *,key,char *,b,size_t,lgt,int,excl, char *,ep)
{
  int back = 0;
  int found;
  int where;
  int can_use_this;
  char *evptr;
#if DK_HAVE_WINREG_H
  char keybuffer[256];
  LONG retval;
  DWORD dwType, sz;
  HKEY thekey;
  int passno;
  char *namea, *nameu, *nameh;
#else
  dk_preference_t *pp1, *pp2;
#endif
  dk_preference_t *pptr;
  
  if((a) && (key) && (b) && (lgt)) {		
    found = 0;
    /* check programs own setting */
    if(!(excl & DK_APP_PREF_EXCL_PROG)) {
      if(((a->p).a) && ((a->p).ai)) {
        pptr = (dk_preference_t *)dksto_it_find_like((a->p).ai, key, 1);
	if(pptr) {
	  found = 1;
	  if(pptr->v) {
	    if(lgt > strlen(pptr->v)) {
	      strcpy(b, pptr->v); back = 1;
	    }
	  }
	}
      }
    }
    /* check command line */
    if((found == 0) && (!(excl & DK_APP_PREF_EXCL_CMD))) {
      if(((a->p).c) && ((a->p).ci)) {
        pptr = (dk_preference_t *)dksto_it_find_like((a->p).ci, key, 1);
	if(pptr) {
	  found = 1;
	  if(pptr->v) {
	    if(lgt > strlen(pptr->v)) {
	      strcpy(b, pptr->v); back = 1;
	    }
	  }
	}
      }
    }
    if((found == 0) && (ep != NULL)) {
      evptr = getenv(ep);
      if(evptr) {
        if(strlen(evptr) < lgt) {
	  strcpy(b, evptr);
	  back = found = 1;
	}
      }
    }
    /* check preferences system */
    if(found == 0) {
      where = 15;
      while((where >= 0) && (found == 0)) {
        can_use_this = 1;
	if(where & 1) {	/* user setting */
	  if(excl & DK_APP_PREF_EXCL_USER) can_use_this = 0;
	} else {	/* system setting */
	  if(excl & DK_APP_PREF_EXCL_SYSTEM) can_use_this = 0;
	  switch(where) {
	    case 7: case 5: case 3: case 1: {
	      can_use_this = 0;
	    } break;
	  }
	}
	if(can_use_this) {
#if DK_HAVE_WINREG_H
	  /* retrieve preferences from registry */
	  nameu = ((where & 8) ? (a->n).u : all_files);
	  namea = ((where & 4) ? (a->n).a : all_files);
	  nameh = ((where & 2) ? (a->n).h : all_files);
	  if(!(nameu)) nameu = all_files;
	  if(!(namea)) namea = all_files;
	  if(!(nameh)) nameh = all_files;
	  passno = 0;
	  while((passno < 2) && (found == 0)) {
	    can_use_this = 0;
	    switch(where) {
	      case 15: {
	        if(passno) {
		  if((a->p).what & 4) {
		    if((strlen(key) + strlen(namea) + strlen(nameh) + 2)
		       < sizeof(keybuffer))
		    {
		      can_use_this = 1;
		      strcpy(keybuffer, namea);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hkcu_all;
		    }
		  }
		} else {
		  if((a->p).what & 8) {
		    if((strlen(key) + strlen(nameh) + 1) < sizeof(keybuffer))
		    {
		      can_use_this = 1;
		      strcpy(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hkcu_app;
		    }
		  }
		}
	      } break;
	      case 14: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if((strlen(key) + strlen(nameu) + strlen(namea)
		       + strlen(nameh) + 3) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, nameu);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, namea);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		} else {
		  if((a->p).what & 2) {
		    if((strlen(key) + strlen(nameu) + strlen(nameh) + 2)
		       < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, nameu);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_app;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 13: {
	        if(passno) {
		  if((a->p).what & 4) {
		    if((strlen(key) + strlen(namea) + 1) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, namea);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hkcu_all;
		      can_use_this = 1;
		    }
		  }
		} else {
		  if((a->p).what & 8) {
		    if(strlen(key) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, key);
		      thekey = (a->p).hkcu_app;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 12: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if((strlen(key) + strlen(nameu) + strlen(namea) + 2)
		       < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, namea);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, namea);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		} else {
		  if((a->p).what & 2) {
		   if((strlen(key) + strlen(nameu) + 1) < sizeof(keybuffer))
		   {
		     strcpy(keybuffer, nameu);
		     strcat(keybuffer, colon);
		     strcat(keybuffer, key);
		     thekey = (a->p).hklm_app;
		     can_use_this = 1;
		   }
		  }
		}
	      } break;
	      case 11: {
	        if(passno) {
		  if((a->p).what & 4) {
		    if((strlen(key) + strlen(all_files) + strlen(nameh) + 2)
		       < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, all_files);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hkcu_all;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 10: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if((strlen(key) + strlen(nameu) + strlen(all_files)
		       + strlen(nameh) + 3) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, nameu);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, all_files);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 9: {
	        if(passno) {
		  if((a->p).what & 4) {
		    if(strlen(key) < sizeof(keybuffer)) {
		      strcpy(keybuffer, key);
		      thekey = (a->p).hkcu_all;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 8: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if((strlen(key) + strlen(nameu) + 1) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, nameu);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 6: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if((strlen(key) + strlen(all_files) + strlen(namea)
		       + strlen(nameh) + 3) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, all_files);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, namea);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		} else {
		  if((a->p).what & 2) {
		    if((strlen(key) + strlen(all_files) + strlen(nameh) + 2)
		       < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, all_files);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_app;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 4: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if((strlen(key) + strlen(all_files) + strlen(namea) + 2)
		       < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, all_files);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, namea);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		} else {
		  if((a->p).what & 2) {
		    if(strlen(key) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, key);
		      thekey = (a->p).hklm_app;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      case 2: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if((strlen(key) + 2 * strlen(all_files) + strlen(nameh)
		        + 3) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, all_files);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, all_files);
		      strcat(keybuffer, slash);
		      strcat(keybuffer, nameh);
		      strcat(keybuffer, colon);
		      strcat(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	      default: {
	        if(passno) {
		  if((a->p).what & 1) {
		    if(strlen(key) < sizeof(keybuffer))
		    {
		      strcpy(keybuffer, key);
		      thekey = (a->p).hklm_all;
		      can_use_this = 1;
		    }
		  }
		}
	      } break;
	    }
	    if(can_use_this) {
	      sz = lgt;
	      retval = RegQueryValueExA(
	        thekey,
		keybuffer,
		NULL,
		&dwType,
		b,
		&sz
	      );
	      if(retval == ERROR_SUCCESS) {
		if(dwType == REG_SZ) {
		  if(sz < lgt) {
		    b[sz] = '\0';
		    back = 1;
		    found = 1;
		  }
		}
	      }
	    }
	    passno++;
	  }
#endif
        }
        where--;
      }
#if !DK_HAVE_WINREG_H
      pp1 = NULL; pp2 = NULL;
      if(!(excl & DK_APP_PREF_EXCL_SYSTEM)) {
        if(((a->p).s) && ((a->p).si)) {
	  pp1 = (dk_preference_t *)dksto_it_find_like((a->p).si, key, 1);
	  if(pp1) {
	    if(pp1->v) {
	      if(strlen(pp1->v) < lgt) {
	        strcpy(b, pp1->v); back = 1; found = pp1->p;
	      }
	    }
	  }
	}
      }
      if(!(excl & DK_APP_PREF_EXCL_USER)) {
        if(((a->p).u) && ((a->p).ui)) {
	  pp1 = (dk_preference_t *)dksto_it_find_like((a->p).ui, key, 1);
	  if(pp1) {
	    if(pp1->p >= found) {
	      if(pp1->v) {
	        if(strlen(pp1->v) < lgt) {
		  strcpy(b, pp1->v); back = 1; found = pp1->p;
		}
	      }
	    }
	  }
	}
      }
#endif
    }
  }
  
  return back;
}



int dkapp_get_pref DK_P5(dk_app_t *,a,char *,key,char *,b,size_t,lgt,int,excl)
{
  int back = 0;
  back = dkapp_get_pref_env(a, key, b, lgt, excl, NULL);
  return back;
}


/**	Log level names.
*/
static char *log_level_strings[] = {
  (char *)"n$one",
  (char *)"pa$nic",
  (char *)"f$atal",
  (char *)"e$rror",
  (char *)"warn$ing",
  (char *)"i$nfo",
  (char *)"pr$ogress",
  (char *)"d$ebug",
  NULL
};



/**	Translate log level from string to numeric value.
	@param	str	Log level name.
	@return	Numeric representation of the log level.
*/
static int
get_log_level DK_P1(char *,str)
{
  int back = -1;
  if(str) {
    back = dkstr_array_abbr(log_level_strings, str, '$', 0);
  }
  return back;
}

/**	Preference key: Use IDE style when logging to file.
*/
static char log_file_ide[] = { "/log/file/ide" };

/**	Preference key: Use IDE style when logging to stdout.
*/
static char log_stdout_ide[] = { "/log/stdout/ide" };

/**	Preference key: Use IDE style when logging to stderr.
*/
static char log_stderr_ide[] = { "/log/stderr/ide" };

/**	Preference key: Log file name.
*/
static char log_file_name[] = { "/log/file/name" };

/**	Preference key: Log level for writing to file.
*/
static char log_file_level[] = { "/log/file/level" };

/**	Preference key: Keep log file when exiting.
*/
static char log_file_keep[] = { "/log/file/keep" };

/**	Preference key: Write timestamps when logging to file.
*/
static char log_file_time[] = { "/log/file/time" };

/**	Preference key: Timestamps in logfile on separate lines.
*/
static char log_file_split[] = { "/log/file/split" };

/**	Preference key: Log level for writing to stdout.
*/
static char log_stdout_level[] = { "/log/stdout/level" };

/**	Preference key: Write timestamps when logging to stdout.
*/
static char log_stdout_time[] = { "/log/stdout/time" };

/**	Preference key: Timestamps on stdout on separate lines.
*/
static char log_stdout_split[] = { "/log/stdout/split" };

/**	Preference key: Log level for writing to stderr.
*/
static char log_stderr_level[] = { "/log/stderr/level" };

/**	Preference key: Write timestamps when logging to stderr.
*/
static char log_stderr_time[] = { "/log/stderr/time" };

/**	Preference key: Timestamps on stderr on separate lines.
*/
static char log_stderr_split[] = { "/log/stderr/split" };

/**	Preference key: Syslog level.
*/
static char log_syslog_level[] = { "/log/syslog/level" };
#if DK_HAVE_CODEPAGES

/**	Preference key: Codepage for logging to file.
*/
static char log_file_codepage[] = { "/log/file/codepage" };

/**	Preference key: Codepage for logging to stdout.
*/
static char log_stdout_codepage[] = { "/log/stdout/codepage" };

/**	Preference key: Codepage for logging to stderr.
*/
static char log_stderr_codepage[] = { "/log/stderr/codepage" };
#endif

/**	Preference key: Application specific directory.
*/
static char app_dir[] = { "/dir/app" };

/**	Preference key: Shared directory.
*/
static char shared_dir[] = { "/dir/shared" };

/**	Preference key:  Base directory for temporary directories.
*/
static char tmp_dir[] = { "/dir/tmp" };

/**	Preference key: Keep temporary directory when exiting.
*/
static char key_keep_temp[] = { "/dir/tmp/keep" };

/**	Preference key: Language/region/encoding.
*/
static char ui_lang[] = { "/ui/lang" };

/**	Preference key: LANG environment variable overwrite
	preference value for language/region/encoding.
*/
static char ui_lang_env[] = { "/ui/lang/env" };

/**	Preference key: Use trees for sorted storage.
*/
static char storage_trees[] = { "/storage/trees" };

/**	Default language/region/encoding.
*/
static char default_language[] = { "en_US" };

#if DK_HAVE_CODEPAGES
/**	Minus sign, used when reading a codepage.
*/
static char minus_sign[] = { "-" };
#endif



/**	Get language/region/encoding settins.
	@param	a	Application.
	@param	str	A copy of the LANG environment variable,
	do not use the result of getenv("LANG") directly.
	The \a str string is modified!
*/
static void
save_lang DK_P2(dk_app_t *, a, char *, str)
{
  char *cptr;
  
  cptr = dkstr_chr(str, '.');
  (a->loc).es = DK_APP_ENCODING_DEFAULT;
  if(cptr) {
    *(cptr++) = '\0';
    if(strlen(cptr) > 0) {
      if((dkstr_casecmp(cptr, str_utf8_a) == 0)
         || (dkstr_casecmp(cptr, str_utf8_b) == 0))
      {
        (a->loc).es = DK_APP_ENCODING_UTF8;
	(a->loc).e = dkstr_dup(str_utf8_a);
      } else {
        (a->loc).e = dkstr_dup(cptr);
      }
    }
  }
  cptr = dkstr_chr(str, '_');
  if(cptr) {
    *(cptr++) = '\0';
    if(strlen(cptr) > 0) {
      (a->loc).r = dkstr_dup(cptr);
    }
  }
  if(strlen(str) > 0) {
    (a->loc).l = dkstr_dup(str);
  } 
}



/**	File open mode: Read binary.
*/
static char rb[] = { "rb" };



/**	Check whether or not a file is a regular file.
	@param	fn	File name.
	@return	1 on success, 0 on error.
*/
static int
file_check DK_P1(char *,fn)
{
  int back = 0;
  dk_stat_t st;
  if(dkstat_get(&st,fn)) {
    if(((st.filetype) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
      back = 1;
    }
  } 
  return back;
}


/**	Debug message texts.
*/
static char *debug_strings[42];



/**	String finder data for debug message texts.
*/
static dk_string_finder_t debug_string_finder[] = {
  { (char *)"/d/00", &(debug_strings[0]), (char *)"Login name:      " },
  { (char *)"/d/01", &(debug_strings[1]), (char *)"Application:     " },
  { (char *)"/d/02", &(debug_strings[2]), (char *)"Host name:       " },
  { (char *)"/d/03", &(debug_strings[3]), (char *)"HOME dir:        " },
  { (char *)"/d/04", &(debug_strings[4]), (char *)"TEMP dir:        " },
  { (char *)"/d/05", &(debug_strings[5]), (char *)"Application dir: " },
  { (char *)"/d/06", &(debug_strings[6]), (char *)"Shared dir:      " },
  { (char *)"/d/07", &(debug_strings[7]), (char *)"Executable file: " },
  { (char *)"/d/08", &(debug_strings[8]), (char *)"Executable dir:  " },
  { (char *)"/d/09", &(debug_strings[9]), (char *)"Log file:        " },
  { (char *)"/d/10", &(debug_strings[10]), (char *)"Language:        " },
  { (char *)"/d/11", &(debug_strings[11]), (char *)"Region:          " },
  { (char *)"/d/12", &(debug_strings[12]), (char *)"Encoding:        " },
  { (char *)"/d/13", &(debug_strings[13]), (char *)"unknown" },
  { (char *)"/d/14", &(debug_strings[14]), (char *)"Proc temp dir:   " },
  { (char *)"/d/15", &(debug_strings[15]), (char *)"Checking file \"" },
  { (char *)"/d/16", &(debug_strings[16]), (char *)"\"." },
  { (char *)"/d/17", &(debug_strings[17]), (char *)"File \"" },
  { (char *)"/d/18", &(debug_strings[18]), (char *)"\" found." },
  { (char *)"/d/19", &(debug_strings[19]), (char *)"Program group:   " },
  { (char *)"/d/20", &(debug_strings[20]), (char *)"System conf dir: " },
  { (char *)"/d/21", &(debug_strings[21]), (char *)"Attempt to seed OpenSSL PRNG." },
  { (char *)"/d/22", &(debug_strings[22]), (char *)"Attempt to seed random() PRNG." },
  { (char *)"/d/23", &(debug_strings[23]), (char *)"Attempt to seed rand48() PRNG." },
  { (char *)"/d/24", &(debug_strings[24]), (char *)"Attempt to seed rand() PRNG." },
  { (char *)"/d/25", &(debug_strings[25]), (char *)"Failed to seed OpenSSL PRNG!" },
  { (char *)"/d/26", &(debug_strings[26]), (char *)"Failed to seed random() PRNG!" },
  { (char *)"/d/27", &(debug_strings[27]), (char *)"Failed to seed rand48() PRNG!" },
  { (char *)"/d/28", &(debug_strings[28]), (char *)"Failed to seed rand() PRNG!" },
  { (char *)"/d/29", &(debug_strings[29]), (char *)"OpenSSL PRNG not available!" },
  { (char *)"/d/30", &(debug_strings[30]), (char *)"random() PRNG not available!" },
  { (char *)"/d/31", &(debug_strings[31]), (char *)"rand48() PRNG not available!" },
  { (char *)"/d/32", &(debug_strings[32]), (char *)"rand() PRNG not available!" },
  { (char *)"/d/33", &(debug_strings[33]), (char *)"Attempt to seed from file \"" },
  { (char *)"/d/34", &(debug_strings[34]), (char *)"\"." },
  { (char *)"/d/35", &(debug_strings[35]), (char *)"Attempt to seed from EGD socket \"" },
  { (char *)"/d/36", &(debug_strings[36]), (char *)"\"." },
  { (char *)"/d/37", &(debug_strings[37]), (char *)"Attempt to seed from device \"" },
  { (char *)"/d/38", &(debug_strings[38]), (char *)"\"." },
  { (char *)"/d/39", &(debug_strings[39]), (char *)"Attempt to seed from keyboard." },
  { (char *)"/d/40", &(debug_strings[40]), (char *)"Attempt to seed from screen." },
  { (char *)"/d/41", &(debug_strings[41]), (char *)"PRNG seeded successfully." },

  {NULL, NULL, NULL}
};



/**	Message textes.
*/
static char *open_and_close_strings[] = {
  NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL, NULL
};



/**	String finder data for message texts.
*/
static dk_string_finder_t open_and_close_finder[] = {
  { (char *)"/msg/01", &(open_and_close_strings[0]), (char *)"Application \"" },
  { (char *)"/msg/02", &(open_and_close_strings[1]), (char *)"\" started." },
  { (char *)"/msg/03", &(open_and_close_strings[2]), (char *)"Application \"" },
  { (char *)"/msg/04", &(open_and_close_strings[3]), (char *)"\" finished." },
  { (char *)"/msg/05", &(open_and_close_strings[4]), (char *)"File \"" },
  { (char *)"/msg/06", &(open_and_close_strings[5]), (char *)"\" found." },
  { (char *)"/msg/07", &(open_and_close_strings[6]), (char *)"File \"" },
  { (char *)"/msg/08", &(open_and_close_strings[7]), (char *)"\" not found!." },
  { (char *)"/msg/09", &(open_and_close_strings[8]), (char *)"File \"" },
  { (char *)"/msg/10", &(open_and_close_strings[9]), (char *)"\" opened." },
  { (char *)"/msg/11", &(open_and_close_strings[10]), (char *)"Failed to read \"" },
  { (char *)"/msg/12", &(open_and_close_strings[11]), (char *)"\"!" },
  { (char *)"/msg/13", &(open_and_close_strings[12]), (char *)"String table \"" },
  { (char *)"/msg/14", &(open_and_close_strings[13]), (char *)"\" not found!." },
  { (char *)"/msg/15", &(open_and_close_strings[14]), (char *)"String table \"" },
  { (char *)"/msg/16", &(open_and_close_strings[15]), (char *)"\" found." },
  { (char *)"/msg/17", &(open_and_close_strings[16]), (char *)"Different owners for symlink \""},
  { (char *)"/msg/18", &(open_and_close_strings[17]), (char *)"\" and target file!" },
  { (char *)"/msg/19", &(open_and_close_strings[18]), (char *)"Symlink \"" },
  { (char *)"/msg/20", &(open_and_close_strings[19]), (char *)"\" resides in a group writable directory!" },
  { (char *)"/msg/21", &(open_and_close_strings[20]), (char *)"Symlink \"" },
  { (char *)"/msg/22", &(open_and_close_strings[21]), (char *)"\" resides in a world writable directory!" },
  { (char *)"/msg/23", &(open_and_close_strings[22]), (char *)"No file name like \"" },
  { (char *)"/msg/24", &(open_and_close_strings[23]), (char *)"\"!" },
  { (char *)"/msg/25", &(open_and_close_strings[24]), (char *)"Too many file names for \"" },
  { (char *)"/msg/26", &(open_and_close_strings[25]), (char *)"\"!" },
  { (char *)"/msg/27", &(open_and_close_strings[26]), (char *)"\"" },
  { (char *)"/msg/28", &(open_and_close_strings[27]), (char *)"\" is a directory!" },
  { (char *)"/msg/29", &(open_and_close_strings[28]), (char *)"The temporary directory \"" },
  { (char *)"/msg/30", &(open_and_close_strings[29]), (char *)"\" will not be deleted!" },
  { (char *)"/msg/31", &(open_and_close_strings[30]), (char *)"Dynamic memory usage: " },
  { (char *)"/msg/32", &(open_and_close_strings[31]), (char *)" bytes." },
  { NULL, NULL, NULL }
};



/**	Flag: Error messages texts were searched.
*/
static int app_err_conf = 0;



/**	Error message texts.
*/
static char *app_err_str[42];


/**	String finder data for error messages.
*/
static dk_string_finder_t app_err_find[] = {
  { (char *)"/e/00", &(app_err_str[0]), (char *)"Cannot traverse directory \"" }, 
  { (char *)"/e/01", &(app_err_str[1]), (char *)"\"!" },
  { (char *)"/e/02", &(app_err_str[2]), (char *)"No information about \"" },
  { (char *)"/e/03", &(app_err_str[3]), (char *)"\"!" },
  { (char *)"/e/04", &(app_err_str[4]), (char *)"Failed to estimate current working directory!" },
  { (char *)"/e/05", &(app_err_str[5]), (char *)"Not enough memory! Failed to allocate " },
  { (char *)"/e/06", &(app_err_str[6]), (char *)" elements, " },
  { (char *)"/e/07", &(app_err_str[7]), (char *)" bytes per element." },
  { (char *)"/e/08", &(app_err_str[8]), (char *)"No file matching \"" },
  { (char *)"/e/09", &(app_err_str[9]), (char *)"\"!" },
  { (char *)"/e/10", &(app_err_str[10]), (char *)"No directory matching \"" },
  { (char *)"/e/11", &(app_err_str[11]), (char *)"\"!" },
  { (char *)"/e/12", &(app_err_str[12]), (char *)"Failed to open file \"" },
  { (char *)"/e/13", &(app_err_str[13]), (char *)"\" for read access!" },
  { (char *)"/e/14", &(app_err_str[14]), (char *)"Failed to open file \"" },
  { (char *)"/e/15", &(app_err_str[15]), (char *)"\" for write access!" },
  { (char *)"/e/16", &(app_err_str[16]), (char *)"Error while writing to file \"" },
  { (char *)"/e/17", &(app_err_str[17]), (char *)"\"!" },
  { (char *)"/e/18", &(app_err_str[18]), (char *)"Error while reading from file \"" },
  { (char *)"/e/19", &(app_err_str[19]), (char *)"\"!" },
  { (char *)"/e/20", &(app_err_str[20]), (char *)"TCP/IP not available!" },
  { (char *)"/e/21", &(app_err_str[21]), (char *)"Failed to open \"" },
  { (char *)"/e/22", &(app_err_str[22]), (char *)"\"! No zlib support available." },
  { (char *)"/e/23", &(app_err_str[23]), (char *)"\"! No bzip2 support available." },
  { (char *)"/e/24", &(app_err_str[24]), (char *)"File \"" },
  { (char *)"/e/25", &(app_err_str[25]), (char *)"\" is not a regular file!" },
  { (char *)"/e/26", &(app_err_str[26]), (char *)"File \"" },
  { (char *)"/e/27", &(app_err_str[27]), (char *)"\" does not exist!" },
  { (char *)"/e/28", &(app_err_str[28]), (char *)"Invalid permissions on file \"" },
  { (char *)"/e/29", &(app_err_str[29]), (char *)"\"!" },
  { (char *)"/e/30", &(app_err_str[30]), (char *)"Invalid owner for file \"" },
  { (char *)"/e/31", &(app_err_str[31]), (char *)"\"!" },
  { (char *)"/e/32", &(app_err_str[32]), (char *)"\"" },
  { (char *)"/e/33", &(app_err_str[33]), (char *)"\" is not a device!" },
  { (char *)"/e/34", &(app_err_str[34]), (char *)"OpenSSL support is not available!" },
  { (char *)"/e/35", &(app_err_str[35]), (char *)"PRNG should not be used for cryptographic purposes\nand/or user authentication!" },
  { (char *)"/e/36", &(app_err_str[36]), (char *)"No PRNG available at all!\nThe requested PRNGs are either not available or cannot be seeded." },
  { (char *)"/e/37", &(app_err_str[37]), (char *)"Some random keyboard input is needed to seed the PRNG.\nPlease hit some keys, press <ENTER> to finish: " },
  { (char *)"/e/38", &(app_err_str[38]), (char *)"Some more random keyboard input is needed: " },
  { (char *)"/e/39", &(app_err_str[39]), (char *)"Warning: Keyboard echo is *not* turned off!" },
  { (char *)"/e/40", &(app_err_str[40]), (char *)"PRNG \"" },
  { (char *)"/e/41", &(app_err_str[41]), (char *)"\" does not exist!" },

  { NULL, NULL, NULL }
};

void dkapp_err_memory DK_P3(dk_app_t *, app, size_t, elsize, size_t, nelem)
{
  char *logmsg[5];
  char buffer1[32], buffer2[32];
  if(app) {
    sprintf(buffer1, "%lu", (unsigned long)nelem);
    sprintf(buffer2, "%lu", (unsigned long)elsize);
    logmsg[0] = app_err_str[5];
    logmsg[1] = buffer1;
    logmsg[2] = app_err_str[6];
    logmsg[3] = buffer2;
    logmsg[4] = app_err_str[7];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 5);
  }
}

void dkapp_err_nowrite DK_P3(dk_app_t *,app,char *,filename,int,reason)
{
  char *logmsg[3];
  if(app && filename) {
    switch(reason) {
      case DK_SF_SEC_WG : {
        logmsg[0] = open_and_close_strings[18];
	logmsg[1] = filename;
	logmsg[2] = open_and_close_strings[19];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
      } break;
      case DK_SF_SEC_WO : {
        logmsg[0] = open_and_close_strings[20];
	logmsg[1] = filename;
	logmsg[2] = open_and_close_strings[21];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
      } break;
      case DK_SF_SEC_OWNER : {
        logmsg[0] = open_and_close_strings[16];
	logmsg[1] = filename;
	logmsg[2] = open_and_close_strings[17];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
      } break;
      case DK_SF_SEC_DIR: {
        logmsg[0] = open_and_close_strings[26];
	logmsg[1] = filename;
	logmsg[2] = open_and_close_strings[27];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
      } break;
    }
  }
}



/**	Data structure used to search for a file.
*/
typedef struct {
  size_t mpl;		/**< Maximum path length. */
  char   *d1;		/**< Buffer for directory to search for. */
  char   *d2;		/**< Buffer for GetWindowsDirectory. */
  char   *d3;		/**< Buffer for entire directory. */
  int    logenab;	/**< Logging enabled. */
  dk_app_t *a;		/**< The application. */
  char   *buffer;	/**< Destination buffer. */
  size_t sz;		/**< Destination buffer size. */
  char   *name;		/**< Name of file to search for. */
  int    index_found;	/**< -1: not found, 0: plain file, >0: use openstream */
  dk_stream_suffix_t *sl;	/**< Suffix list. */
  int    useunsecdir;	/**< Use or not current directory. */
} file_finder_t;



/**	Initialize file finder.
	@param	ff	File finder to initialize.
*/
static void
init_file_finder DK_P1(file_finder_t *,ff)
{
  ff->mpl = 0;
  ff->d1 = ff->d2 = ff->d3 = NULL;
  ff->logenab = 0;
  ff->a = NULL;
  ff->buffer = NULL;
  ff->sz = 0;
  ff->index_found = -1;
  ff->sl = NULL;
  ff->name = NULL;
  ff->useunsecdir = 1;
  
}



/**	Open new file finder.
	@return	Pointer to new file finder on success, NULL on error.
*/
static file_finder_t *
new_file_finder DK_P0()
{
  file_finder_t *back = NULL;
  char *d1, *d2, *d3;
  long l; size_t s;
  
  back = dk_new(file_finder_t,1);
  if(back) {
    init_file_finder(back);
    l = dksf_get_maxpathlen();
    if(l <= 0L) l = 1024L;
    s = (size_t)l;
    back ->mpl = s;
    d1 = dk_new(char,s); d2 = dk_new(char,s); d3 = dk_new(char,s);
    if(d1 && d2 && d3) {
      back->d1 = d1; back->d2 = d2; back->d3 = d3;
    } else {
      if(d1) { dk_delete(d1); }
      if(d2) { dk_delete(d2); }
      if(d3) { dk_delete(d3); }
      dk_delete(back);
      back = NULL;
    }
  } 
  return back;
}



/**	Release file finder.
	@param	ff	File finder to release.
*/
static void
delete_file_finder DK_P1(file_finder_t *,ff)
{
  char *x;
  
  if(ff) {
    x = ff->d1;
    if(x) { dk_delete(x); }
    x = ff->d2;
    if(x) { dk_delete(x); }
    x = ff->d3;
    if(x) { dk_delete(x); }
    ff->mpl = 0; ff->d1 = ff->d2 = NULL;
    ff->logenab = 0; ff->a = NULL;
    dk_delete(ff);
  }
  
}



/**	Create debug messages for file search.
	@param	a	Application.
	@param	fn	File name.
	@param	logenab	Flag: Logging enabled.
	@return	1 on success, 0 on error.
*/
static int
logged_file_check DK_P3(dk_app_t *,a,char *,fn,int,logenab)
{
  int back = 0;
  char *logmsg[3];
  if(fn) {
    if(a && logenab) {
      logmsg[0] = debug_strings[15];
      logmsg[1] = fn;
      logmsg[2] = debug_strings[16];
      dkapp_log_msg(a,DK_LOG_LEVEL_DEBUG,logmsg,3);
    }
    back = file_check(fn);
    if(a && logenab && back) {
      logmsg[0] = debug_strings[17];
      logmsg[1] = fn;
      logmsg[2] = debug_strings[18];
      dkapp_log_msg(a,DK_LOG_LEVEL_DEBUG,logmsg,3);
    }
  }
  return back;
}



/**	Search for file.
	Check in one directory for filename, filename,gz and filename,bz2
	@param	ff	File finder.
	@param	usedir	Combination of DK_APP_PNC_REGION, DK_APP_PNC_LANGUAGE
	and DK_APP_PNC_ENCODING.
*/
static void
ff_dir DK_P2(file_finder_t *,ff,int,usedir)
{
  size_t lgt; int lfd;
  dk_stream_suffix_t *sptr;

  
  lgt = strlen(ff->name);
  if(usedir) {
    lgt += (strlen(ff->d3) + strlen(fn_sep));
  }
  if(lgt < ff->sz) {
    if(usedir) {
      strcpy(ff->buffer, ff->d3);
      strcat(ff->buffer, fn_sep);
      strcat(ff->buffer, ff->name);
    } else {
      strcpy(ff->buffer, ff->name);
    }
    dksf_correct_fnsep(ff->buffer);
    if(logged_file_check(ff->a,ff->buffer,ff->logenab)) {
      ff->index_found = 0;
    }
  }
  if((ff->index_found) == -1) {
  if(ff->sl) {
    sptr = ff->sl; lfd = 1;
    while((sptr->suffix) && ((ff->index_found) == -1)) {
      lgt = strlen(ff->name) + strlen(sptr->suffix);
      if(usedir) {
        lgt += (strlen(ff->d3) + strlen(fn_sep));
      }
      if(lgt < ff->sz) {
        if(usedir) {
	  strcpy(ff->buffer, ff->d3);
	  strcat(ff->buffer, fn_sep);
	  strcat(ff->buffer, ff->name);
        } else {
          strcpy(ff->buffer, ff->name);
        }
	strcat(ff->buffer, sptr->suffix);
	dksf_correct_fnsep(ff->buffer);
	if(logged_file_check(ff->a,ff->buffer,ff->logenab)) {
	  ff->index_found = lfd;
	}
      }
      sptr++; lfd++;
    }
  }
  }
  
}



/**	Search in subdirectories for a file.
	Check for filename, filename.gz and filename.bz2
	in the language, region and encoding subdirectories.
	@param	ff	File finder.
	@param	subdir	Pointer to variable containing a
	combination of DK_APP_PNC_REGION, DK_APP_PNC_LANGUAGE
	and DK_APP_PNC_ENCODING.
*/
static void
ff_subs DK_P2(file_finder_t *,ff, int *,subdir)
{
  int i1, do_the_check;
  size_t lgt;
  
  i1 = 8;
  while((i1 > 0) && ((ff->index_found) == -1)) {
    i1--;
    do_the_check = 1;
    lgt = strlen(ff->d1);
    if(i1 & DK_APP_PNC_LANGUAGE) {
      if(((ff->a)->loc).l) {
        lgt++;
	lgt += strlen(((ff->a)->loc).l);
      } else {
        do_the_check = 0;
      }
    }
    if(i1 & DK_APP_PNC_REGION) {
      if(((ff->a)->loc).r) {
        lgt++;
	lgt += strlen(((ff->a)->loc).r);
      } else {
        do_the_check = 0;
      }
    }
    if(i1 & DK_APP_PNC_ENCODING) {
      if(((ff->a)->loc).e) {
        lgt++;
	lgt += strlen(((ff->a)->loc).e);
      } else {
        do_the_check = 0;
      }
    }
    if(do_the_check) {
      if(lgt < ff->mpl) {
        strcpy(ff->d3, ff->d1);
	if(i1 & DK_APP_PNC_LANGUAGE) {
	  strcat(ff->d3, fn_sep);
	  strcat(ff->d3, ((ff->a)->loc).l);
	}
	if(i1 & DK_APP_PNC_REGION) {
	  strcat(ff->d3, fn_sep);
	  strcat(ff->d3, ((ff->a)->loc).r);
	}
	if(i1 & DK_APP_PNC_ENCODING) {
	  strcat(ff->d3, fn_sep);
	  strcat(ff->d3, ((ff->a)->loc).e);
	}
	ff_dir(ff, 1);
      }
    }
    if(ff->index_found != -1) {
      if(*subdir) {
        *subdir = i1;
      }
    }
  }
  
}



/**	Run file search.
	@param	ff	File finder.
	@param	subdir	Pointer to variable containing a
	combination of DK_APP_PNC_REGION, DK_APP_PNC_LANGUAGE
	and DK_APP_PNC_ENCODING.
*/
static void
ff_run DK_P2(file_finder_t *,ff, int *,subdir)
{
  int   i1; char *my_sysconfdir;
  
  /* nothing found yet */
  my_sysconfdir = unix_sysconfdir;
  if(ff->a) {
    if(((ff->a)->d).etc) {
      my_sysconfdir = ((ff->a)->d).etc;
    }
  }
  ff->index_found = -1;
  i1 = 12;
  while((i1 > 0) && ((ff->index_found) == -1)) {
    switch(i1) {
      case 12: {		/* current directory */
        if(ff->useunsecdir) {
          ff_dir(ff, 0);
	}
      } break;
      case 11: {		/* home directory */
        if(ff->useunsecdir) {
	  if(ff->a) {
	    if(((ff->a)->d).h) {
	      if(strlen(((ff->a)->d).h) < ff->mpl) {
	        strcpy(ff->d3, ((ff->a)->d).h);
	        ff_dir(ff,1);
	      }
	    }
	  }
	}
      } break;
      case 10: {		/* home/.defaults */
        if(ff->useunsecdir) {
	  if(ff->a) {
	    if(((ff->a)->d).h) {
	      if((strlen(((ff->a)->d).h)+strlen(fn_sep)
	         +strlen(defaults_sub))
	         < ff->mpl)
	      {
	        strcpy(ff->d3, ((ff->a)->d).h);
	        strcat(ff->d3, fn_sep);
	        strcat(ff->d3, defaults_sub);
	        ff_dir(ff,1);
	      }
	    }
	  }
	}
      } break;
      case  9: {
        if(ff->useunsecdir) {
          if(((ff->a)->d).a) {
	    if(strlen(((ff->a)->d).a) < ff->mpl) {
	      strcpy(ff->d1, ((ff->a)->d).a);
	      ff_subs(ff, subdir);
	    }
	  }
	}
      } break;
      case  8: {
        if(ff->useunsecdir) {
          if((((ff->a)->d).s) && (((ff->a)->n).g)) {
	    if((1+strlen(((ff->a)->d).s) + strlen(((ff->a)->n).g)) < ff->mpl) {
	      strcpy(ff->d1, ((ff->a)->d).s);
	      strcat(ff->d1, fn_sep);
	      strcat(ff->d1, ((ff->a)->n).g);
	      ff_subs(ff, subdir);
	    }
	  }
	}
      } break;
      case  7: {
        if(ff->useunsecdir) {
          if(((ff->a)->d).s) {
	    if(strlen(((ff->a)->d).s) < ff->mpl) {
	      strcpy(ff->d1, ((ff->a)->d).s);
	      ff_subs(ff, subdir);
	    }
	  }
	}
      } break;
      case  6: {
#if DK_HAVE_GETWINDOWSDIRECTORY
        UINT uSize;
	
	uSize = GetWindowsDirectoryA((LPTSTR)(ff->d2), (UINT)(ff->mpl));
        if((uSize > 0) && (uSize < ((UINT)(ff->mpl)))) {
	  
	  if(((ff->a)->n).a) { 
	  if((1 + strlen(ff->d2) + strlen(((ff->a)->n).a)) < ff->mpl) {
	    
	    strcpy(ff->d1, ff->d2);
	    strcat(ff->d1, fn_sep);
	    strcat(ff->d1, ((ff->a)->n).a);
	    ff_subs(ff, subdir);
	  } else {
	    
	  }
	  } else { 
	  }
	} else {
	  i1 = 4;
	  if(uSize == 0) {
	    
	  }
	  
	}
#else
        i1 = 4;
	
#endif
      } break;
      case  5: {
#if DK_HAVE_GETWINDOWSDIRECTORY
        if(((ff->a)->n).g) {
	if((1 + strlen(ff->d2) + strlen(((ff->a)->n).g)) < ff->mpl) {
	  strcpy(ff->d1, ff->d2);
	  strcat(ff->d1, fn_sep);
	  strcat(ff->d1, ((ff->a)->n).g);
	  ff_subs(ff, subdir);
	}
	}
#else
        i1 = 4;
#endif
      } break;
      case  4: {
#if DK_HAVE_GETWINDOWSDIRECTORY
        if(strlen(ff->d2) < ff->mpl) {
	  strcpy(ff->d1, ff->d2);
	  ff_subs(ff, subdir);
	}
#endif
      } break;
      case  3: {
        if(((ff->a)->n).a) {
	if((1 + strlen(my_sysconfdir) + strlen(((ff->a)->n).a)) < ff->mpl) {
	  strcpy(ff->d1, my_sysconfdir);
	  strcat(ff->d1, fn_sep);
	  strcat(ff->d1, ((ff->a)->n).a);
	  ff_subs(ff, subdir);
	}
	}
      } break;
      case  2: {
        if(((ff->a)->n).g) {
	if((1 + strlen(my_sysconfdir) + strlen(((ff->a)->n).g)) < ff->mpl) {
	  strcpy(ff->d1, my_sysconfdir);
	  strcat(ff->d1, fn_sep);
	  strcat(ff->d1, ((ff->a)->n).g);
	  ff_subs(ff, subdir);
	}
	}
      } break;
      case  1: {
        if(strlen(my_sysconfdir) < ff->mpl) {
	  strcpy(ff->d1, my_sysconfdir);
	  ff_subs(ff, subdir);
	}
      } break;
    }
    i1--;
  }
  if(ff->logenab) {
    char *errmsgs[3];
    if((ff->index_found) == -1) {
      errmsgs[0] = open_and_close_strings[6];
      errmsgs[1] = ff->name;
      errmsgs[2] = open_and_close_strings[7];
      dkapp_log_msg(ff->a,DK_LOG_LEVEL_ERROR,errmsgs,3);
    } else {
      errmsgs[0] = open_and_close_strings[4];
      errmsgs[1] = ff->buffer;
      errmsgs[2] = open_and_close_strings[5];
      dkapp_log_msg(ff->a,DK_LOG_LEVEL_PROGRESS,errmsgs,3);
    }
  }
  
}



/**	Open file for reading.
	@param	a	Application.
	@param	name	File name.
	@param	logenab	Flag: Logging enabled.
	@param	useunsecdir	Flag: Use user-writable directories.
	@param	subdir	Combination of DK_APP_PNC_LANGUAGE,
		DK_APP_PNC_REGION, DK_APP_PNC_ENCODING.
	@return	Stream to read the file, must be closed using dkstream_close().
*/
static dk_stream_t *
my_read_file_ext1 DK_P5(dk_app_t *,a,char *,name,int,logenab,int,useunsecdir, int *,subdir)
{
  dk_stream_t *back = NULL;
  file_finder_t *ff;
  dk_stream_suffix_t *sl;
  dk_stream_open_fct_t *fct;
  char *fnb;

  
  if(a && name) {
    ff = new_file_finder();
    if(ff) {
      fnb = dk_new(char,(ff->mpl));
      if(fnb) {
        ff->logenab = logenab;
        ff->a = a;
        ff->buffer = fnb;
        ff->sz = ff->mpl;
	ff->name = name;
	ff->useunsecdir = useunsecdir;
	sl = ff->sl = dkstream_get_read_suffixes();
	ff_run(ff, subdir);
	if((ff->index_found) > -1) {
	  if((ff->index_found) > 0) {
	    fct = sl[((ff->index_found) - 1)].fct;
	    if(fct) {
	      a->relaxed_fopen_reason = 0;
	      back = ((*fct)(fnb,rb,a->relaxed_fopen_check, &(a->relaxed_fopen_reason)));
	      if(logenab) {
	        if(back) {
		  char *errmsgs[3];
		  errmsgs[0] = open_and_close_strings[8];
		  errmsgs[1] = ff->buffer;
		  errmsgs[2] = open_and_close_strings[9];
		  dkapp_log_msg(a,DK_LOG_LEVEL_PROGRESS,errmsgs,3);
		} else {
	          if(a->relaxed_fopen_reason) {
	            dkapp_err_nowrite(a, fnb, a->relaxed_fopen_reason);
		  } else {
		    char *errmsgs[3];
		    errmsgs[0] = open_and_close_strings[10];
		    errmsgs[1] = ff->buffer;
		    errmsgs[2] = open_and_close_strings[11];
		    dkapp_log_msg(a,DK_LOG_LEVEL_ERROR,errmsgs,3);
		  }
	        }
	      }
	    }
	  } else {
	    back = dkapp_stream_openfile(a, fnb, rb);
	    if(logenab) {
	      if(back) {
		char *errmsgs[3];
		errmsgs[0] = open_and_close_strings[8];
		errmsgs[1] = ff->buffer;
		errmsgs[2] = open_and_close_strings[9];
		dkapp_log_msg(a,DK_LOG_LEVEL_PROGRESS,errmsgs,3);
	      } else {
		char *errmsgs[3];
		errmsgs[0] = open_and_close_strings[10];
		errmsgs[1] = ff->buffer;
		errmsgs[2] = open_and_close_strings[11];
		dkapp_log_msg(a,DK_LOG_LEVEL_ERROR,errmsgs,3);
	      }
	    }
	  }
	}
        dk_delete(fnb);
      } else {
        dkapp_err_memory(a,sizeof(char),(ff->mpl));
      }
      delete_file_finder(ff);
    } else {
      dkapp_err_memory(a,sizeof(file_finder_t),1);
    }
  }
  
  return back;
}



/**	Open a file for reading.
	@param	a	Application.
	@param	name	File name.
	@param	logenab	Flag: Logging enabled.
	@param	useunsecdir	Use user-writable directories.
	@return	Stream to read the file, must be closed using dkstream_close().
*/
static dk_stream_t *
my_read_file DK_P4(dk_app_t *,a,char *,name,int,logenab,int,useunsecdir)
{
  dk_stream_t *back = NULL;
  int subdir;
  subdir = 0;
  back = my_read_file_ext1(a, name, logenab, useunsecdir, &subdir);
  return back;
}



/**	Find file.
	@param	a	Application.
	@param	name	File name (without directory).
	@param	buffer	Result buffer.
	@param	sz	Size of \a buffer.
	@param	uud	Flag: Use user-writable directories.
	@param	subdirtype	Combination of DK_APP_PNC_LANGUAGE,
				DK_APP_PNC_REGION, DK_APP_PNC_ENCODING.
	@return	1 on success, 0 on error.
*/
static int
int_find_file_ext1 DK_P6(dk_app_t *,a,char *,name,char *,buffer,size_t,sz,int,uud,int *,subdirtype)
{
  int back = 0;
  file_finder_t *ff;
  
  if(a && name && buffer && sz) {
    ff = new_file_finder();
    if(ff) {
      ff->logenab = 1;
      ff->a = a;
      ff->buffer = buffer;
      ff->sz = sz;
      ff->name = name;
      ff->sl = NULL;
      ff->useunsecdir = uud;
      ff_run(ff, subdirtype);
      if((ff->index_found) > -1) {
        back = 1;
      }
      delete_file_finder(ff);
    } else {
      dkapp_err_memory(a,sizeof(file_finder_t),1);
    }
  }
  
  return back;
}


/**	Find file.
	@param	a	Application.
	@param	name	File name (without directory).
	@param	buffer	Result buffer.
	@param	sz	Size of \a buffer.
	@param	uud	Flag: Use user-writable directories
	@return	1 on success, 0 on error.
*/
static int
int_find_file DK_P5(dk_app_t *,a,char *,name,char *,buffer,size_t,sz,int,uud)
{
  int back = 0; int i = 0;
  back = int_find_file_ext1(a,name,buffer,sz,uud,&i);
  return back;
}


/**	Find file, create a new file name string.
	Note: On success the function allocates memory for the new string.
	You must release the memory using dk_delete().
	@param	a	Application.
	@param	n	File name (without directory).
	@param	uud	Use user-writable directories.
	@param	subdirtype
	@return	A new string containing the full path name.
*/
static
char *
int_find_file_dup_ext1 DK_P4(dk_app_t *,a,char *,n,int,uud,int *,subdirtype)
{
  char *back = NULL;
  char *buffer = NULL;
  long l; size_t sz;
  if((a) && (n)) {
    l = dksf_get_maxpathlen();
    sz = (size_t)l;
    buffer = dk_new(char,sz);
    if(buffer) {
      if(int_find_file_ext1(a,n,buffer,sz,uud,subdirtype)) {
        back = dkstr_dup(buffer);
	if(!back) {
	  dkapp_err_memory(a,sizeof(char),(1+strlen(buffer)));
	}
      }
      dk_delete(buffer); buffer = NULL;
    } else {
      dkapp_err_memory(a,sizeof(char),sz);
    }
  }
  return back;
}



/**	Find file, create a new file name string.
	Note: On success the function allocates memory for the new string.
	You must release the memory using dk_delete().
	@param	a	Application.
	@param	n	File name (without directory).
	@param	uud	Flag: Use user-writable directories
	@return	A new string containing the full path name.
*/
static
char *
int_find_file_dup DK_P3(dk_app_t *,a,char *,n,int,uud)
{
  char *back = NULL; int i = 0;
  back = int_find_file_dup_ext1(a,n,uud,&i);
  return back;
}



char *
dkapp_find_file_dup_ext1 DK_P3(dk_app_t *,a,char *,n,int *,subdirtype)
{
  char *back = NULL;
  
  back = int_find_file_dup_ext1(a,n,1,subdirtype);
  
  return back;
}



char *
dkapp_find_file_dup DK_P2(dk_app_t *,a,char *,n)
{
  char *back = NULL; int i = 0;
  back = dkapp_find_file_dup_ext1(a,n,&i);
  return back;
}

int
dkapp_find_file_ext1 DK_P5(dk_app_t *,a,char *,name,char *,buffer,size_t,sz, int *,subdirtype)
{
  int back = 0;
  
  back = int_find_file_ext1(a,name,buffer,sz,1,subdirtype);
  
  return back;
}


int
dkapp_find_file DK_P4(dk_app_t *,a,char *,name,char *,buffer,size_t,sz)
{
  int back = 0; int i = 0;
  back = dkapp_find_file_ext1(a, name, buffer, sz, &i);
  return back;
}

char *
dkapp_find_cfg_dup DK_P2(dk_app_t *,a,char *,n)
{
  char *back = NULL;
  
  back = int_find_file_dup(a,n,0);
  
  return back;
}

int
dkapp_find_cfg DK_P4(dk_app_t *,a,char *,name,char *,buffer,size_t,sz)
{
  int back = 0;
  
  back = int_find_file(a,name,buffer,sz,0);
  
  return back;
}

dk_stream_t *
dkapp_read_file_ext1 DK_P3(dk_app_t *, a, char *, filename, int *,subdirtype)
{
  dk_stream_t *back = NULL;
  
  back = my_read_file_ext1(a,filename,1,1,subdirtype);
  
  return back;
}


dk_stream_t *
dkapp_read_file DK_P2(dk_app_t *, a, char *, filename)
{
  dk_stream_t *back = NULL; int i = 0;
  back = dkapp_read_file_ext1(a, filename, &i);
  return back;
}

dk_stream_t *
dkapp_read_cfg DK_P2(dk_app_t *, a, char *, filename)
{
  dk_stream_t *back = NULL;
  
  back = my_read_file(a,filename,1,0);
  
  return back;
}


/**	File name sufix for string tables.
*/
static char suffix_stt[] = { ".stt" };



/**	Find a string table.
	@param	app	Application.
	@param	table	String table name.
	@return	String table object or NULL.
*/
static stt_entry *
find_stt_entry DK_P2(dk_app_t *, app, char *, table)
{
    stt_entry *stteptr = NULL;
    char *buffer, *cptr;
    long maxpathlen;
    dk_stream_t *strm;
    dk_stt_t *sttptr;
    int found;
    
    if(((app->loc).s) && ((app->loc).si)) {
      stteptr =
      (stt_entry *)dksto_it_find_like(((app->loc).si),((void *)table),1);
      if(!stteptr) { 
	maxpathlen = dksf_get_maxpathlen();
	if(maxpathlen < 0L) { maxpathlen = 1024L; }
	buffer = dk_new(char,maxpathlen);
	if(buffer) { 
	  if((strlen(table) + 7) < (size_t)maxpathlen) {
	    strcpy(buffer, table);
	    found = 0;
	    cptr = dksf_get_file_type_dot(buffer);
	    if(cptr) {
	      if(strcmp(cptr, suffix_stt) == 0) {
		found = 1;
	      }
	    }
	    if(!found) {
	      strcat(buffer, suffix_stt);
	    } 
	    strm = my_read_file(app,buffer,0,1);
	    if(strm) { 
		sttptr = dkstt_open(strm);
		if(sttptr) { 
		  stteptr = dk_new(stt_entry,1);
		  if(stteptr) { 
		    stteptr->name = NULL;
		    stteptr->st = NULL;
		    stteptr->name = dkstr_dup(table);
		    if(stteptr->name) { 
		      stteptr->st = sttptr; 
		      if(!dksto_add(((app->loc).s),((void *)stteptr))) {
			
			stt_entry_free(stteptr);
			stteptr = NULL;
			sttptr = NULL;
		      }
		    } else {
		      stteptr->name = NULL;
		      dk_delete(stteptr);
		      stteptr = NULL;
		      dkstt_close(sttptr);
		      sttptr = NULL;
		    }
		  } else {
		    dkstt_close(sttptr);
		    sttptr = NULL;
		  }
		}
		dkstream_close(strm);
	    }
	  }
	}
	if(buffer) { dk_delete(buffer) ; }
      } 
    } 
      return stteptr;
}



/**	Priority names.
*/
static char *log_level_keywords[] = {
  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

/**	String finder data for priority names.
*/
static dk_string_finder_t log_level_string_finder[] = {
  { (char *)"/ll/n"  , (&(log_level_keywords[0])), (char *)"" },
  { (char *)"/ll/pa" , (&(log_level_keywords[1])), (char *)"PANIC:    " },
  { (char *)"/ll/f"  , (&(log_level_keywords[2])), (char *)"FATAL:    " },
  { (char *)"/ll/e"  , (&(log_level_keywords[3])), (char *)"ERROR:    " },
  { (char *)"/ll/w"  , (&(log_level_keywords[4])), (char *)"Warning:  " },
  { (char *)"/ll/i"  , (&(log_level_keywords[5])), (char *)"" },
  { (char *)"/ll/pr" , (&(log_level_keywords[6])), (char *)"" },
  { (char *)"/ll/d"  , (&(log_level_keywords[7])), (char *)"Debug:    " },
  { (char *)"/ll/ign", (&(log_level_keywords[8])), (char *)"" },
  { NULL, NULL, NULL }
} ;



/**	Get priority name.
	@param	i	Priority.
	@return	Priority name or NULL.
*/
static char *
get_prio_string DK_P1(int, i)
{
  int x;
  char *back = NULL;
  x = i;
  if(x < 1) x = 1;
  if(x > 7) x = 7;
  back = log_level_keywords[x];
  return back;
}



/**	Keywords to output messages in an IDE style.
*/
static char *ide_output_words[] = {
  (char *)"line",
  (char *)"error",
  (char *)"warning",
  (char *)"C2000",
  (char *)"C4000",
  NULL
};



/**	Keywords for TASM-style output.
*/
static char *tasm_keywords[] = {
  (char *)"",
  (char *)"*Warning*",
  (char *)"**Error**",
  (char *)"**Fatal**",
  NULL
};



/**	Add log message to log file.
	@param	f		Output file.
	@param	fl		Flags: 1=print timestamp, 2=separate line
	@param	ts		Timestamp
	@param	min		Required priority for this destination.
	@param	p		Message priority.
	@param	msg		Text array.
	@param	az		Number of entries in \a msg.
	@param	cp		Codepage to use.
	@param	ide_type	IDE style to simulate.
	@param	name		Source file name.
	@param	lineno		Source file line number.
	@param	timer		Time of this message.
	@param	time_last	Previously printed time.
*/
static void
file_log DK_P13(FILE *,f,int,fl,char *,ts,int,min,int,p,char **,msg,int,az,unsigned char *,cp,int,ide_type,char *,name,unsigned long,lineno, time_t *,timer, time_t*, time_last)
{
  int i;
  char *x, *kw;
  
  if(f && (p <= min)) {		
    if(fl & 1) {
      if(fl & 2) {
        if(*timer != *time_last) {
	  fputs("# ", f);
	  fputs(ts, f);
	  fputc('\n', f);
	  *time_last = *timer;
	}
      } else {
        fputs(ts, f);
	fputc(' ', f);
      }
    }
    
    if(ide_type && name && lineno) {
      switch(ide_type) {
        case DK_APP_IDE_TASM : {
	  
	  kw = tasm_keywords[0];
	  if(p <= DK_LOG_LEVEL_WARNING) {
	    kw = tasm_keywords[1];
	    if(p <= DK_LOG_LEVEL_ERROR) {
	      kw = tasm_keywords[2];
	      if(p <= DK_LOG_LEVEL_FATAL) {
	        kw = tasm_keywords[3];
	      }
	    }
	  }
	  fprintf(f, "%s %s(%lu) ", kw, name, lineno);
	} break;
        case DK_APP_IDE_WORKSHOP : {
	  
	  if(p <= DK_LOG_LEVEL_ERROR) {
	    fprintf(f, "\"%s\", %s %lu: ",
	      name, ide_output_words[0], lineno
	    );
	  } else {
	    if(p == DK_LOG_LEVEL_WARNING) {
	      fprintf(f, "\"%s\". %s %lu: %s: ",
	        name, ide_output_words[0], lineno, ide_output_words[2]
	      );
	    }
	  }
	} break;
	case DK_APP_IDE_MSVC : {
	  
	  if(p <= DK_LOG_LEVEL_ERROR) {
	    fprintf(f, "%s(%lu) : %s %s: ",
	      name, lineno, ide_output_words[1], ide_output_words[3]
	    );
	  } else {
	    if(p == DK_LOG_LEVEL_WARNING) {
	      fprintf(f, "%s(%lu) : %s %s: ",
	        name, lineno, ide_output_words[2], ide_output_words[4]
	      );
	    }
	  }
	} break;
	default : {
	  
	  if(p <= DK_LOG_LEVEL_ERROR) {
	    fprintf(f, "%s:%lu: ", 
	      name, lineno
	    );
	  } else {
	    if(p == DK_LOG_LEVEL_WARNING) {
	      fprintf(f, "%s:%lu: %s: ",
	        name, lineno, ide_output_words[2]
	      );
	    } else {
	      fputs(name, f); fputs(": ", f);
	    }
	  }
	} break;
      }
      
    } else {
      
      if(name) {
        if(lineno) {
          fprintf(f, "%s:%lu: ",
	    name, lineno
	  );
	} else {
	  fputs(name, f); fputs(": ", f);
	}
      }
      x = get_prio_string(p);	
      if(x) { fputs(x, f); }
    }
    
#if DK_HAVE_CODEPAGES
				
    if(cp) {			
      for(i = 0; i < az; i++) {	
	if(msg[i]) {		
	  dkcp_fputs(f, cp, msg[i]);	
	}
      }
    } else {
#else
				
#endif
      for(i = 0; i < az; i++) {	
        if(msg[i]) {		
	  fputs(msg[i], f);
        }
      }
#if DK_HAVE_CODEPAGES
    }
#endif
    
    fputc('\n', f);
    fflush(f);
  } 
}



int
dkapp_llmax_get DK_P1(dk_app_t *,a)
{
  int back = DK_LOG_LEVEL_IGNORE;
  if(a) {
    back = (a->l).max;
  }
  return back;
}



void
dkapp_llmax_set DK_P2(dk_app_t *,a, int,l)
{
  if(a) {
    (a->l).max = l;
    if((a->l).max < DK_LOG_LEVEL_NONE) {
      (a->l).max = DK_LOG_LEVEL_NONE;
    }
    if((a->l).max > DK_LOG_LEVEL_IGNORE) {
      (a->l).max = DK_LOG_LEVEL_IGNORE;
    }
  }
}



int
dkapp_log_msg DK_P4(dk_app_t *, a, int, p, char **, msg, int, az)
{
  int back = 0;
  char time_buffer[32];
#if DK_HAVE_TIME_H
  static time_t time_last_stdout = (time_t)0;
  static time_t time_last_stderr = (time_t)0;
  static time_t time_last_file = (time_t)0;
  time_t timer;
#endif
#if DK_HAVE_SYSLOG
  size_t lgt;
  int i, xp;
  char *buffer, *ps;
#endif
  
  time_buffer[0] = '\0';
  if(p < ((a->l).max)) { (a->l).max = p; }
#if DK_HAVE_TIME_H
  time(&timer);
  dksf_time_convert(time_buffer,timer);
#endif
  if(a && msg && (az > 0)) {
    
    back = 1;
    
    if(!(((a->l).nostdio) & DK_APP_LOG_NO_STDOUT)) {
      file_log(
        stdout, ((a->l).o.f), time_buffer,((a->l).o.m),
	p,msg,az,((a->l).o.c), ((a->l).o.ide_type),
	((a->l).ef.n), ((a->l).ef.lineno), &timer, &time_last_stdout
      );
    }
    
    if(!(((a->l).nostdio) & DK_APP_LOG_NO_STDERR)) {
      file_log(
        stderr,((a->l).e.f),time_buffer,((a->l).e.m),
        p,msg,az,((a->l).e.c), ((a->l).e.ide_type),
	((a->l).ef.n), ((a->l).ef.lineno), &timer, &time_last_stderr
      );
    }
    
    file_log(
      ((a->l).f.t),((a->l).f.f),time_buffer,((a->l).f.m),
      p,msg,az,((a->l).f.c), ((a->l).e.ide_type),
      ((a->l).ef.n), ((a->l).ef.lineno), &timer, &time_last_file
    );
    
#if DK_HAVE_SYSLOG
    
    if(((a->l).s.o) && (p <= ((a->l).s.m))) {
      
      ps = get_prio_string(p);
      lgt = strlen(ps);
      for(i = 0; i < az; i++) {
        if(msg[i]) { lgt += strlen(msg[i]); }
      }
      lgt++;
      buffer = dk_new(char,lgt);
      if(buffer) {
        strcpy(buffer, ps);
        for(i = 0; i < az; i++) {
	  if(msg[i]) { strcat(buffer, msg[i]); }
        }
        xp = LOG_INFO;
        switch(p) {
	  case DK_LOG_LEVEL_DEBUG: xp = LOG_DEBUG; break;
	  case DK_LOG_LEVEL_PROGRESS: xp = LOG_DEBUG; break;
	  case DK_LOG_LEVEL_INFO: xp = LOG_INFO; break;
	  case DK_LOG_LEVEL_WARNING: xp = LOG_WARNING; break;
	  case DK_LOG_LEVEL_ERROR: xp = LOG_ERR; break;
	  case DK_LOG_LEVEL_FATAL: xp = LOG_CRIT; break;
	  case DK_LOG_LEVEL_PANIC: xp = LOG_EMERG; break;
        }
        syslog(xp,"%s",buffer);
        dk_delete(buffer);
      }
    }
#endif
  } 
  return back;
}



/**	Macro names for replacements.
*/
static char *specials[] = {
  /*	0 */ (char *)"app.name",
  /*	1 */ (char *)"app.dir",
  /*	2 */ (char *)"shared.dir",
  /*	3 */ (char *)"temp.dir",
  /*	4 */ (char *)"user.name",
  /*	5 */ (char *)"user.home",
  /*	6 */ (char *)"user.uid",
  /*	7 */ (char *)"user.gid",
  /*	8 */ (char *)"user.euid",
  /*	9 */ (char *)"user.egid",
  /*	10 */ (char *)"host.name",
  /*	11 */ (char *)"host.domain",
  /*	12 */ (char *)"process.pid",
  /*	13 */ (char *)"process.ppid",
  /*	14 */ (char *)"process.pgid",
  NULL
};



/**	String: Information obtained from preferences.
*/
static char str_pref[] = { "pref:" };

/**	String: Information obtained from environment.
*/
static char str_env[]  = { "env:" };

/**	String for unknown sources.
*/
static char str_unknown[] = { "x" };



/**	Transform a macro.
	@param	app	Application.
	@param	str	In: macro to replace, out: replacement text.
	@param	sz	Size of the \a str buffer.
	@return	1 on success, 0 on error.
*/
static int
transformSpecial DK_P3(dk_app_t *,app,char *,str,size_t,sz)
{
  int back = 0;
  int i;
  char *xbuffer, *ptr;
  size_t lgt1, lgt2;
  long l;
  i = dkstr_array_index(specials, str, 0);
  if(i >= 0) {
    switch(i) {
      case 0: { 
        if((app->n).a) {
	  if(strlen((app->n).a) < sz) {
	    strcpy(str, (app->n).a);
	    back = 1;
	  }
	}
      } break;
      case 1: { 
	if((app->d).a) {
	  if(strlen((app->d).a) < sz) {
	    strcpy(str, (app->d).a);
	    back = 1;
	  }
	}
      } break;
      case 2: {
	if((app->d).s) {
	  if(strlen((app->d).s) < sz) {
	    strcpy(str, (app->d).s);
	    back = 1;
	  }
	}
      } break;
      case 3: {
	if((app->d).t) {
	  if(strlen((app->d).t) < sz) {
	    strcpy(str, (app->d).t);
	    back = 1;
	  }
	}
      } break;
      case 4: {
	if((app->n).u) {
	  if(strlen((app->n).u) < sz) {
	    strcpy(str, (app->n).u);
	    back = 1;
	  }
	}
      } break;
      case 5: {
	if((app->d).h) {
	  if(strlen((app->d).h) < sz) {
	    strcpy(str, (app->d).h);
	    back = 1;
	  }
	}
      } break;
      case 6: {
	if(sz > 11) {
	  back = 1;
	  if(dksf_have_getuid()) {
	    l = dksf_getuid();
	    sprintf(str, "%lu", (unsigned long)l);
	  } else {
	    strcpy(str, str_unknown);
	  }
	}
      } break;
      case 7: {
	if(sz > 11) {
	  back = 1;
	  if(dksf_have_getgid()) {
	    l = dksf_getgid();
	    sprintf(str, "%lu", (unsigned long)l);
	  } else {
	    strcpy(str, str_unknown);
	  }
	}
      } break;
      case 8: {
	if(sz > 11) {
	  back = 1;
	  if(dksf_have_geteuid()) {
	    l = dksf_geteuid();
	    sprintf(str, "%lu", (unsigned long)l);
	  } else {
	    strcpy(str, str_unknown);
	  }
	}
      } break;
      case 9: {
	if(sz > 11) {
	  back = 1;
	  if(dksf_have_getegid()) {
	    l = dksf_getegid();
	    sprintf(str, "%lu", (unsigned long)l);
	  } else {
	    strcpy(str, str_unknown);
	  }
	}
      } break;
      case 10: {
	back = dksf_get_hostname(str,sz);
      } break;
      case 11: {
	back = dksf_get_domainname(str,sz);
      } break;
      case 12: {
	if(sz > 11) {
	  back = 1;
	  if(dksf_have_getpid()) {
	    l = dksf_getpid();
	    sprintf(str, "%lu", (unsigned long)l);
	  } else {
	    strcpy(str, str_unknown);
	  }
	}
      } break;
      case 13: {
	if(sz > 11) {
	  back = 1;
	  if(dksf_have_getppid()) {
	    l = dksf_getppid();
	    sprintf(str, "%lu", (unsigned long)l);
	  } else {
	    strcpy(str, str_unknown);
	  }
	}
      } break;
      case 14: {
	if(sz > 11) {
	  back = 1;
	  if(dksf_have_getpgid()) {
	    l = dksf_getpgid(dksf_getpid());
	    sprintf(str, "%lu", (unsigned long)l);
	  } else {
	    strcpy(str, str_unknown);
	  }
	}
      } break;
    }
  } else {
    lgt1 = strlen(str);
    lgt2 = strlen(str_env);
    if(lgt1 > lgt2) {
      if(strncmp(str,str_env,lgt2) == 0) {
	ptr = getenv(&(str[lgt2]));
	if(ptr) {
	  if(strlen(ptr) < sz) {
	    strcpy(str,ptr);
	    back = 1;
	  }
	}
      }
      if(!back) {
	lgt2 = strlen(str_pref);
	if(lgt1 > lgt2) {
	  if(strncmp(str,str_pref,lgt2) == 0) {
	    xbuffer = dk_new(char,sz);
	    if(xbuffer) {
	      strcpy(xbuffer,&(str[lgt2]));
	      back = dkapp_get_pref(app,xbuffer,str,sz,0);
	      dk_delete(xbuffer);
	    }
	  }
	}
      }
    }
  }
  return back;
}



/**	flag: Use backslash in file names.
*/
#if DK_HAVE_FEATURE_BACKSLASH
#define FNSBS 1
#else
#define FNSBS 0
#endif



/**	Flag: File name parts are separated by backslashes.
*/
static int fnsbs = FNSBS;



int
dkapp_transform_string_ext1 DK_P5(dk_app_t *,app,char *,dest,size_t,sz,char *,src,int,fn)
{
  int back = 1;
  char tmp[256];
  char *dptr, *tptr, *sptr; size_t dsz, tsz;
  int  state;
  
  if(app && dest && src && sz) { 
    dptr = dest; tptr = tmp; sptr = src; state = 0;
    dsz = tsz = 0;
    while(*sptr) {
      switch(state) {
	case 1: { 
	  switch(*sptr) {
	    case '(': {
	      tptr = tmp; tsz = 0; state = 2;
	    } break;
	    default: {
	      if(dsz + 2 < sz) {
		*(dptr++) = '$';
		*(dptr++) = *sptr;
		dsz++; dsz++;
	      } else {
		back = 0;
	      }
	      state = 0;
	    } break;
	  }
	} break;
        case 2: { 
	  switch(*sptr) {
	    case ')': { 
	      *tptr = (char)0; 
	      if(transformSpecial(app, tmp, sizeof(tmp))) {
		
		if((dsz + strlen(tmp)) < sz) {
		  tptr = tmp;
		  while(*tptr) {
		    *(dptr++) = *(tptr++);
		  }
		} else {
		  back = 0;
		}
	      }
	      state = 0;
	    } break;
	    default: {
	      if(tsz + 1 < sizeof(tmp)) {
		*(tptr++) = *sptr;
		tsz++;
	      } else {
		back = 0;
	      }
	    }
	  }
	} break;
	case 3: { 
	  switch(*sptr) {
	    case '\\':
	    case  '$':
	    case  '/': {
	      if(dsz + 1 < sz) {
		*(dptr++) = *sptr; dsz++;
	      } else { back = 0; }
	    } break;
	    default: {
	      if(dsz + 2 < sz) {
		*(dptr++) = '\\';
		*(dptr++) = *sptr;
		dsz++; dsz++;
	      } else { back = 0; }
	    } break;
	  } state = 0;
	} break;
	default: { 
	  switch(*sptr) {
	    case '$': {
	      state = 1;
	    } break;
	    case '\\': {
	      if(fnsbs && fn) {
	        state = 0;
		if(dsz + 1 < sz) {
		  *(dptr++) = *sptr;
		  dsz++;
		} else {
		  back = 0;
		}
	      } else {
	        state = 3;
	      }
	    } break;
	    case '/' : {
	      state = 0;
	      if(dsz + 1 < sz) {
		*(dptr++) = fn_sep[0];
		dsz++;
	      } else {
		back = 0;
	      }
	    } break;
	    default: {
	      state = 0;
	      if(dsz + 1 < sz) {
		*(dptr++) = *sptr;
		dsz++;
	      } else {
		back = 0;
	      }
	    } break;
	  }
	} break;
      }
      sptr++;
    }
    *dptr = (char)0;
  } 
  return back;
}


int
dkapp_transform_string DK_P4(dk_app_t *,app,char *,dest,size_t,sz,char *,src)
{
  int back;
  back = dkapp_transform_string_ext1(app,dest,sz,src,0);
  return back;
}



/**	Find multiple strings.
	@param	app	Application.
	@param	f	String finder data listing key names and default texts.
	@param	table	String table name.
	@param	logenab	Flag: Internal Logging enabled.
*/
static void
my_find_multi DK_P4(dk_app_t *, app, dk_string_finder_t *, f, char *, table,int,logenab)
{
  dk_string_finder_t *ptr;
  stt_entry *stteptr;
  dk_stt_t  *sttptr;
  int found;
  char *errmsgs[3];

  found = 0;
  
  if(app && f && table) {
    stteptr = find_stt_entry(app,table);
    if(stteptr) { 
      sttptr = stteptr->st;
      if(sttptr) { 
	ptr = f; found = 1;
	while((ptr->key) && (ptr->value_pointer) && (ptr->default_value)) {
	  *(ptr->value_pointer) =
	  dkstt_find(sttptr, (ptr->key), (ptr->default_value));
	  ptr++;
	}
      }
    }
  }
  if(!found) { 
    if(f) { 
      ptr = f;
      while((ptr->key) && (ptr->value_pointer) && (ptr->default_value)) {
	*(ptr->value_pointer) = (ptr->default_value);
	ptr++;
      }
    }
    /* ERROR NO STRING TABLE FOUND */
    if(logenab) {
    errmsgs[0] = open_and_close_strings[12];
    errmsgs[1] = table;
    errmsgs[2] = open_and_close_strings[13];
    dkapp_log_msg(app,DK_LOG_LEVEL_WARNING,errmsgs,3);
    }
  } else {
    /* DEBUG STRING TABLE FOUND */
    if(logenab) {
    errmsgs[0] = open_and_close_strings[14];
    errmsgs[1] = table;
    errmsgs[2] = open_and_close_strings[15];
    dkapp_log_msg(app,DK_LOG_LEVEL_DEBUG,errmsgs,3);
    }
  }
  
}


void
dkapp_find_multi DK_P3(dk_app_t *, app, dk_string_finder_t *, f, char *, table)
{
  my_find_multi(app,f,table,1);
}


/** Pointer to character buffer. */
typedef char *PCHR;


void
dkapp_init_key_value DK_P5(dk_app_t *,a,dk_key_value_t *,k,size_t,s,char *,n,char **,b)
{
  dk_string_finder_t *strf, *strfp;
  char **ptr;
  dk_key_value_t *kvp;
  size_t cc;
  
  if (k) {
  if (s) {
  if (b) { 	
    strf = NULL;
    if(a && n) {
      strf = dk_new(dk_string_finder_t,(s+1));
    }
    if(a && n && strf) {	
      strfp = strf; kvp = k; ptr = b; cc = s;
      while (cc--) {
        strfp->key = kvp->key;
	strfp->default_value = kvp->value;
	strfp->value_pointer = ptr;
        strfp++; kvp++; ptr++;
      }
      strf[s].key = NULL;
      strf[s].default_value = NULL;
      strf[s].value_pointer = NULL;
      my_find_multi(a,strf,n,1);
    } else {			
      kvp = k; ptr = b; cc = s;
      while (cc--) {
        *(ptr++) = (kvp++)->value;
      }
    }
    if(strf) {
      dk_delete(strf); strf = NULL;
    }
  }
  }
  }
  
}


char **
dkapp_find_key_value DK_P4(dk_app_t *,a,dk_key_value_t *,k,size_t,s,char *,n)
{
  char **back = NULL;
  
  if (k) {
  if (s) {		
    back = dk_new(PCHR,s);
    if (back) {		
      dkapp_init_key_value(a,k,s,n,back);
    } else {		
      dkapp_err_memory(a,sizeof(PCHR),s);
    }
  }
  }
  
  return back;
}



/**	Find a string from a string table.
	@param	app	Application.
	@param	table	String table name.
	@param	key	Key to search for.
	@param	def	Default text if no matching text is found.
	@param	logenab	Flag: Internal logging enabled.
*/
static char *
my_find_string DK_P5(dk_app_t *,app,char *,table,char *,key,char *,def,int,logenab)
{
  char *back = NULL;
  stt_entry *stteptr;
  char *errmsgs[3];
  
  if(app && table && key) {
    stteptr = find_stt_entry(app,table);
    if(stteptr) {
      if(stteptr->st) {
	back = dkstt_find((stteptr->st),key,def);
      } else {
	/* ERROR NO STRING TABLE FOUND */
	if(logenab) {
	errmsgs[0] = open_and_close_strings[12];
	errmsgs[1] = table;
	errmsgs[2] = open_and_close_strings[13];
	dkapp_log_msg(app,DK_LOG_LEVEL_WARNING,errmsgs,3);
	}
      }
    } else {
        /* ERROR NO STRING TABLE FOUND */
	if(logenab) {
	errmsgs[0] = open_and_close_strings[4];
	errmsgs[1] = table;
	errmsgs[2] = open_and_close_strings[5];
	dkapp_log_msg(app,DK_LOG_LEVEL_DEBUG,errmsgs,3);
	}
    }
  }
  if(!back) {
    back = def;
  } 
  return back;
}


char *
dkapp_find_string DK_P4(dk_app_t *,app,char *,table,char *,key,char *,def)
{
  char *back;
  back = my_find_string(app,table,key,def,1);
  return back;
}


int dkapp_get_argc DK_P1(dk_app_t *,a)
{
  int back = 0;
  if(a) {
    back = (a->a).a.argc;
  }
  return back;
}

char **dkapp_get_argv DK_P1(dk_app_t *,a)
{
  char **back = NULL;
  if(a) {
    back = (a->a).a.argv;
  }
  return back;
}



/**	Write buffer contents to output file (used to show help text).
	@param	a	Application.
	@param	b1	Source buffer.
	@param	b2	Temporary buffer for conversion.
	@param	sz	Size of \a b1 and \a b2.
	@param	mctu	Flag: Conversion to UTF-8 necessary.
	@param	f	Output file.
	@param	cp	Codepage to use.
*/
static
void
buffer_to_file DK_P7(dk_app_t *,a, char *,b1, char *,b2, size_t, sz, int,mctu, FILE *,f, unsigned char *,cp)
{
  dk_udword udw;
  char obuffer[16];
  size_t i, j;
  switch((a->loc).es) {
    case DK_APP_ENCODING_UTF8: {
      if(mctu) {
        for(i = 0; i < sz; i++) {
	  udw = ((dk_udword)((unsigned char)(b1[i]))) & 0x000000FFUL;
	  j = dkenc_uc2utf8(udw, (unsigned char *)obuffer, sizeof(obuffer));
	  if(j > 0) {
	    (void)fwrite((void *)obuffer, 1, j, f);
	  }
	}
      } else {
        (void)fwrite(b1,1,sz,f);
      }
    } break;
    default: {
#if DK_HAVE_CODEPAGES
      if(cp) {
        for(i = 0; i < sz; i++) { b2[i] = dkcp_convert(cp, b1[i]); }
	(void)fwrite(b2,1,sz,f);
      } else {
        (void)fwrite(b1,1,sz,f);
      }
#else
      (void)fwrite(b1,1,sz,f);
#endif
    } break;
  }
}



void
dkapp_stdout DK_P2(dk_app_t *,app, char *,s)
{
  if(s) {
    if(app) {
      switch((app->loc).es) {
        case DK_APP_ENCODING_UTF8: {
	  fputs(s, stdout);
	} break;
	default: {
#if DK_HAVE_CODEPAGES
          dkcp_fputs(stdout, (app->l).o.c, s);
#else
          fputs(s, stdout);
#endif
	} break;
      }
    } else {
      fputs(s, stdout);
    }
  }
}



void
dkapp_stderr DK_P2(dk_app_t *, app, char *,s)
{
  if(s) {
    if(app) {
      switch((app->loc).es) {
        case DK_APP_ENCODING_UTF8: {
	  fputs(s, stderr);
	} break;
	default: {
#if DK_HAVE_CODEPAGES
          dkcp_fputs(stderr, (app->l).e.c, s);
#else
          fputs(s, stderr);
#endif
	} break;
      }
    } else {
      fputs(s, stderr);
    }
  }
}



void
dkapp_fputs DK_P3(dk_app_t *, app, char *,s, FILE *,f)
{
  if(s) {
    if(app) {
      switch((app->loc).es) {
        case DK_APP_ENCODING_UTF8: {
	  fputs(s, f);
	} break;
	default: {
#if DK_HAVE_CODEPAGES
          dkcp_fputs(f, (app->l).f.c, s);
#else
          fputs(s, f);
#endif
	} break;
      }
    } else {
      fputs(s, f);
    }
  }
}



size_t
dkapp_prlen DK_P2(dk_app_t *,app, char *,s)
{
  size_t back = 0;
  size_t used, sl, newlength, summary_used;
  dk_udword uw;
  int cc;
  if(s) {
    if(app) {
      switch((app->loc).es) {
        case DK_APP_ENCODING_UTF8: {
	  /* convert UTF-8 to UC and count */
	  cc = 1;
	  summary_used = 0;
	  sl = strlen(s);
	  while((cc) && (sl > summary_used)) {
	    used = 0; uw = 0UL;
	    if(dkenc_utf82uc(&uw, (dk_ubyte *)(&(s[summary_used])), (sl - summary_used), &used)) {
	      back++;
	      newlength = summary_used + used;
	      if(newlength > summary_used) {
	        summary_used = newlength;
		if(summary_used >= sl) {
		  cc = 0;
		}
	      } else {
	        cc = 0;
	      }
	    } else {
	      cc = 0;
	    }
	  }
	} break;
	default: {
	  back = strlen(s);
	} break;
      }
    } else {
      back = strlen(s);
    }
  }
  return back;
}




void dkapp_help DK_P3(dk_app_t *,a,char *,filename,char **,str)
{
  int found = 0;		/* help file found ? */
  char buffer[128], b2[128];	/* buffers for read/write operations */
  int  bytes;			/* number of bytes read */
  dk_stream_t *strm;		/* stream to read input file */
  char **ptr;			/* pointer running through array str */
  int subdirtype;		/* does subdirectory contain encoding ? */
  int mctu;			/* must convert to UTF-8 */

  subdirtype = 0;
  mctu = 0;
  if((a) && (filename) && (str)) {
    found = 0;
    strm = my_read_file_ext1(a,filename,1,1,&subdirtype);
    /* attempt to read file */
    if(strm) {
      if((a->loc).e) {
        if((a->loc).es == DK_APP_ENCODING_UTF8) {
          if(!(subdirtype & DK_APP_PNC_ENCODING)) {
            mctu = 1;
          }
        }
      }
      while((bytes = dkstream_read(strm,buffer,sizeof(buffer))) > 0) {
        /* write help text to log file */
        buffer_to_file(a, buffer, b2, bytes, mctu, (a->l).f.t, (a->l).f.c);
        /* write help text to standard output */
        buffer_to_file(a, buffer, b2, bytes, mctu, stdout, (a->l).o.c);
        found = 1;
      }
      dkstream_close(strm);
      if(found) {
        fputc('\n', stdout);
        if((a->l).f.t) {
	  fputc('\n', (a->l).f.t);
        }
      }
    }
    /* if file was not found use default text */
    if(!found) {
      ptr = str;
      while(*ptr) {
        dkapp_stdout(a, *ptr);
        fputc('\n', stdout);
        if((a->l).f.t) {
          dkapp_fputs(a, *ptr, (a->l).f.t);
	  fputc('\n', (a->l).f.t);
        }
        ptr++;
      }
    }
  }
}



/**	Open files for binary writing.
*/
static char wb[] = { "wb" };



dk_stream_t *
dkapp_write_file DK_P2(dk_app_t *, app, char *, filename)
{
  dk_stream_t *back = NULL;
  dk_stream_suffix_t *suffix_ptr, *sptr2;
  dk_stream_open_fct_t *fct;
  char *buffer;
  size_t lgt, maxlgt;

  if(app && filename) {
    suffix_ptr = dkstream_get_write_suffixes();
    sptr2 = suffix_ptr;
    maxlgt = 0;
    while((sptr2->suffix) && (sptr2->fct)) {
      lgt = strlen(sptr2->suffix);
      if(lgt > maxlgt) maxlgt = lgt;
      sptr2++;
    }
    maxlgt += strlen(filename);
    maxlgt++;
    buffer = dk_new(char,maxlgt);
    if(buffer) {
      sptr2 = suffix_ptr;
      while((sptr2->suffix) && (sptr2->fct) && (!back)) {
	fct = sptr2->fct;
	strcpy(buffer, filename);
	strcat(buffer, sptr2->suffix);
	app->relaxed_fopen_reason = 0;
	back = ((*fct)(buffer, wb, app->relaxed_fopen_check, &(app->relaxed_fopen_reason)));
	if((!back) && (app->relaxed_fopen_reason)) {
	  dkapp_err_nowrite(app, buffer, app->relaxed_fopen_reason);
	}
	sptr2++;
      }
      dk_delete(buffer);
    }
  }
  return back;
}



/**	Report memory usage.
	@param	a	Application.
*/
static void
memory_usage DK_P1(dk_app_t *,a)
{
  char buffer[64], *ptr;
  unsigned long h, l;
  h = l = 0UL;
  h = dkmem_get_track(1); l = dkmem_get_track(0);
  if(h) {
    sprintf(
      buffer,
      "%32lg",
      (4294967296.0*dkma_l_to_double(h)+dkma_l_to_double(l))
    );
  } else {
    sprintf(buffer, "%lu", l);
  }
  ptr = dkstr_start(buffer, NULL);
  if(ptr) {
    char *logmsgs[4];
    logmsgs[0] = open_and_close_strings[30];
    logmsgs[1] = ptr;
    logmsgs[2] = open_and_close_strings[31];
    dkapp_log_msg(a,DK_LOG_LEVEL_DEBUG,logmsgs,3);
  }
}



/**	Open files for writing.
*/
static char str_w[] = { "w" };



void
dkapp_close DK_P1(dk_app_t *, a)
{
  char *cptr, **argptr; int i;
  dk_preference_t *pptr;
  stt_entry *stteptr;
  char *logmsgs[3];
  long maxpathlen;
  char *filename;
#if !DK_HAVE_WINREG_H
  FILE *outputfile;
  int print_it;
  dk_preference_t *ppt2;
#endif
  
  if(a) {
    if((a->random).seed_file_name) {
      cptr = (a->random).seed_file_name;
      dk_delete(cptr);
      (a->random).seed_file_name = NULL;
    }
    (a->random).prng_type = DK_RAND_TYPE_NONE;
    if((a->n).a) {
      logmsgs[0] = open_and_close_strings[2];
      logmsgs[1] = (a->n).a;
      logmsgs[2] = open_and_close_strings[3];
      dkapp_log_msg(a,DK_LOG_LEVEL_PROGRESS,logmsgs,3);
      memory_usage(a);
    }
    /*
      Free string table space
    */
    if(((a->loc).s) && ((a->loc).si)) {
      dksto_it_reset((a->loc).si);
      while((stteptr = (stt_entry *)dksto_it_next((a->loc).si)) != NULL) {
	stt_entry_free(stteptr);
      }
      dksto_close((a->loc).s);
      (a->loc).s = NULL;
      (a->loc).si = NULL;
    }
    /*
      Write preferences back to file
    */
#if !DK_HAVE_WINREG_H
    if(((a->d).h) && ((a->n).a) && (((a->p).unc) || ((a->p).prf))) {
      maxpathlen = dksf_get_maxpathlen();
      if(maxpathlen < 0L) { maxpathlen = 1024L; }
      filename = dk_new(char,maxpathlen);
      if(filename) {
	if((strlen((a->d).h) + strlen(defaults_sub) + strlen((a->n).a) + 2) < (unsigned long)maxpathlen) {
	  strcpy(filename, (a->d).h);
          strcat(filename, fn_sep);
	  strcat(filename, defaults_sub);
	  strcat(filename, fn_sep);
	  strcat(filename, (a->n).a);
#if NO_FOPEN_CHECKING
#if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64
	  outputfile = fopen64(filename, str_w);
#else
	  outputfile = fopen(filename, str_w);
#endif
#else
	  a->relaxed_fopen_reason = 0;
	  outputfile = dksf_msfo(filename, str_w, a->relaxed_fopen_check, &(a->relaxed_fopen_reason));
	  if((!outputfile) && (a->relaxed_fopen_reason)) {
	    dkapp_err_nowrite(a, filename, a->relaxed_fopen_reason);
	  }
#endif
	  if(outputfile) {
	    if(!((a->p).unc)) {
	      if(((a->p).u) && ((a->p).ui)) {
	        dksto_it_reset((a->p).ui);
                while((pptr = (dk_preference_t *)dksto_it_next((a->p).ui)) != NULL) {
		  if((pptr->n) && (pptr->v)) {
		  if((pptr->p) & 2) {
		    print_it = 1;
		    if(((a->p).a) && ((a->p).ai)) {
		      ppt2 = (dk_preference_t *)dksto_it_find_like((a->p).ai, pptr->n, 1);
		      if(ppt2) {
		        print_it = 0;
		      }
		    }
		    if(print_it) {
		      fputs(pptr->n, outputfile);
		      fputs("\t=\t", outputfile);
		      fputs(pptr->v, outputfile);
		      fputc('\n', outputfile);
		    }
		  }
		  }
	        }
	        dksto_it_reset((a->p).ai);
	        while((pptr = (dk_preference_t *)dksto_it_next((a->p).ai)) != NULL) {
		  if((pptr->n) && (pptr->v)) {
		    fputs(pptr->n, outputfile);
		    fputs("\t=\t", outputfile);
		    fputs(pptr->v, outputfile);
		    fputc('\n', outputfile);
		  }
	        }
	      }
	    }
	    fclose(outputfile);
	  }
	  if((a->p).unc) {
	    dksf_remove_file(filename);
	  }
	}
        dk_delete(filename);
      }
    }
#endif
    if((a->d).pt) {
      if(!(((a->l).max) > (a->keep_temp_dir))) {
        /* INFO temp dir not deleted */
        logmsgs[0] = open_and_close_strings[28];
        logmsgs[1] = (a->d).pt;
        logmsgs[2] = open_and_close_strings[29];
        dkapp_log_msg(a,DK_LOG_LEVEL_WARNING,logmsgs,3);
      }
    }
    /* 
      Close log system
    */
    if((a->l).f.t) {
      fclose((a->l).f.t);
      (a->l).f.t = NULL;
    }
    if((a->l).f.n) {
      if(((a->l).max) > ((a->l).f.k)) {
        dksf_remove_file((a->l).f.n);
      }
      cptr = (a->l).f.n; dk_delete(cptr);
      (a->l).f.n = NULL;
    }
#if DK_HAVE_SYSLOG
    if((a->l).s.o) {
      closelog();
      (a->l).s.o = 0;
    }
#endif
    if((a->l).o.c) {
      cptr = (char *)((a->l).o.c) ; dk_delete(cptr);
    } (a->l).o.c = NULL;
    if((a->l).e.c) {
      cptr = (char *)((a->l).e.c) ; dk_delete(cptr);
    } (a->l).e.c = NULL;
    if((a->l).f.c) {
      cptr = (char *)((a->l).f.c) ; dk_delete(cptr);
    } (a->l).f.c = NULL;
    /*
      Release names
    */
    if((a->loc).l) { cptr = (a->loc).l; dk_delete(cptr); } (a->loc).l = NULL;
    if((a->loc).r) { cptr = (a->loc).r; dk_delete(cptr); } (a->loc).r = NULL;
    if((a->loc).e) { cptr = (a->loc).e; dk_delete(cptr); } (a->loc).e = NULL;
    if((a->x).d) {
      cptr = (a->x).d; dk_delete(cptr);
    }
    if((a->x).f) {
      cptr = (a->x).f; dk_delete(cptr);
    }
    (a->x).d = NULL; (a->x).f = NULL;
    if((a->d).etc) {
      cptr = (a->d).etc; dk_delete(cptr);
      (a->d).etc = NULL;
    }
    if((a->d).pt) {
      if(((a->l).max) > (a->keep_temp_dir)) {
        
        (void)dksf_remove_directory((a->d).pt);
      } else {	
      }
      cptr = (a->d).pt; dk_delete(cptr);
    }
    if((a->d).a) {
      cptr = (a->d).a; dk_delete(cptr);
    }
    if((a->d).s) {
      cptr = (a->d).s; dk_delete(cptr);
    }
    if((a->d).t) {
      cptr = (a->d).t; dk_delete(cptr);
    }
    if((a->d).h) {
      cptr = (a->d).h; dk_delete(cptr);
    }
    /*
    if((a->n).a) {
      cptr = (a->n).a; dk_delete(cptr);
    }
    */
    if((a->n).h) {
      cptr = (a->n).h; dk_delete(cptr);
    }
    if((a->n).u) {
      cptr = (a->n).u; dk_delete(cptr);
    }
    /* (a->n).a = NULL; */ (a->n).h = NULL; (a->n).u = NULL;
    (a->d).s = NULL; (a->d).a = NULL;
    (a->d).h = NULL; (a->d).t = NULL; (a->d).pt = NULL;
    /*
      Release preferences data
    */
    if(((a->p).a) && ((a->p).ai)) {
      dksto_it_reset((a->p).ai);
      while((pptr = (dk_preference_t *)dksto_it_next((a->p).ai)) != NULL) {
	dkpref_delete(pptr);
      }
      dksto_close((a->p).a);
      (a->p).a = NULL; (a->p).ai = NULL;
    }
    if(((a->p).c) && ((a->p).ci)) {
      dksto_it_reset((a->p).ci);
      while((pptr = (dk_preference_t *)dksto_it_next((a->p).ci)) != NULL) {
	dkpref_delete(pptr);
      }
      dksto_close((a->p).c);
      (a->p).c = NULL; (a->p).ci = NULL;
    }
#if DK_HAVE_WINREG_H
    if(((a->p).what) & 1) { RegCloseKey((a->p).hklm_all); }
    if(((a->p).what) & 2) { RegCloseKey((a->p).hklm_app); }
    if(((a->p).what) & 4) { RegCloseKey((a->p).hkcu_all); }
    if(((a->p).what) & 8) { RegCloseKey((a->p).hkcu_app); }
    if((a->p).unc) {
      maxpathlen = 1024L;
      filename = dk_new(char,maxpathlen);
      if(filename) {
        if((strlen((a->n).a) + strlen(hkcu) + 1) < (size_t)maxpathlen) {
	  strcpy(filename, hkcu);
	  strcat(filename, fn_sep);
	  strcat(filename, (a->n).a);
	  RegDeleteKey(HKEY_CURRENT_USER, filename);
        }
        dk_delete(filename);
      }
    }
#else
    if(((a->p).u) && ((a->p).ui)) {
      dksto_it_reset((a->p).ui);
      while((pptr = (dk_preference_t *)dksto_it_next((a->p).ui)) != NULL) {
	dkpref_delete(pptr);
      }
      dksto_close((a->p).u);
      (a->p).u = NULL; (a->p).ui = NULL;
    }
    if(((a->p).s) && ((a->p).si)) {
      dksto_it_reset((a->p).si);
      while((pptr = (dk_preference_t *)dksto_it_next((a->p).si)) != NULL) {
	dkpref_delete(pptr);
      }
      dksto_close((a->p).s);
      (a->p).s = NULL; (a->p).si = NULL;
    }
#endif
    if((a->n).g) {
      cptr = (a->n).g ; dk_delete(cptr);
    }
    (a->n).g = NULL;
    if((a->n).a) {
      cptr = (a->n).a ; dk_delete(cptr);
    }
    (a->n).a = NULL;
    /*
      Release command line args
    */
    if( (a->a).a.argv ) {
      argptr = (a->a).a.argv ;
      for(i = 0; i < (a->a).o.argc; i++) {
	argptr[i] = NULL;
      }
      dk_delete(argptr);
    }
    if( (a->a).o.argv ) {
      argptr = (a->a).o.argv ;
      if(argptr) {
        for(i = 0; i < (a->a).o.argc; i++) {
	  cptr = argptr[i];
	  if(cptr) dk_delete(cptr);
        }
        dk_delete(argptr);
      }
    }

    /*
      Finally delete the application itself
    */
    app_init_empty(a);
    dk_delete(a);
  } 
}


#if !DK_HAVE_WINREG_H
/**	Default configuration file name.
*/
static char local_default_configuration[] = {
#if DK_HAVE_FEATURE_BACKSLASH
  "C:\\ETC\\ALL.DEF"
#else
  DK_SYSCONFDIR "/appdefaults"
#endif
};
#endif


int
dkapp_get_min_loglevel DK_P1(dk_app_t *,a)
{
  int back = DK_LOG_LEVEL_NONE;
  if(a) {
    back = (a->l).o.m;
    if(back < ((a->l).e.m)) { back = (a->l).e.m; }
    if(back < ((a->l).f.m)) { back = (a->l).f.m; }
#if DK_HAVE_SYSLOG
    if(back < ((a->l).s.m)) { back = (a->l).s.m; }
#endif
  }
  return back;
}



/**	Preference key: Ignore different owners for symlink and target.
*/
static char key_sec_ign_file_owner[] = { "/sec/ign/link-owner" };

/**	Preference key: Ignore symlinks group-writable directory.
*/
static char key_sec_ign_dir_group_writable[] = { "/sec/ign/dir-group-wriable" };

/**	Preference key: Ignore symlinks in world-writable directories.
*/
static char key_sec_ign_dir_world_writable[] = { "/sec/ign/dir-world-wriable" };



/**	Keywords to choose an IDE style.
*/
static char *ide_keys[] = {
  (char *)"g$cc",
  (char *)"ms$vc",
  (char *)"w$orkshop",
  (char *)"t$asm",
  NULL
};



/**	String table name for messages from this module.
*/
static char str_dkapp[] = { "dkapp" };

/**	String table name for error messages.
*/
static char str_dkappe[] = { "dkappe" };

/**	String table name for debug messages.
*/
static char str_dkappd[] = { "dkappd" };



int
dkapp_ide_type DK_P1(char *,str)
{
  int back = 0;
  
  if(str) {
    back = 1 + dkstr_array_abbr(ide_keys,str,'$',0);
  }
  
  return back;
}



/**	Open an application.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@param	grname	Application group name.
	@param	etc	System configuration directory (/etc or /usr/local/etc).
	@param	sil	Flag: Run silently
	@param	nostdio	Combination of DK_APP_LOG_NO_STDERR/DK_APP_LOG_NO_STDOUT.
	@return	Pointer to new application on success, NULL on error.
*/
static
dk_app_t *
my_app_open DK_P6(int,argc,char **,argv,char *,grname,char *,etc,int,sil,int,nostdio)
{
  dk_app_t *back = NULL;
  int    i, j, my_set_silent;
  char *cptr, *cptr2, **argptr, **argptr2;
  long maxpathlen, mypid;
  char *buffer, *buffer2, *bptr;
  dk_stat_t st;
  char *logmsgs[3];
#if DK_HAVE_WINREG_H
  HKEY hkTemp;
  PHKEY phkSubkey;
  DWORD disp;
  LONG retval;
#endif
#if DK_HAVE_CODEPAGES
  dk_stream_t *cp_stream;
#endif

  
  maxpathlen = dksf_get_maxpathlen();
  if(maxpathlen < 0L) { maxpathlen = 1024L; }
  buffer = dk_new(char,maxpathlen);
  buffer2 = dk_new(char,maxpathlen);
  if(argc && argv) {
    my_set_silent = dkapp_silence_check(argc,argv);
    if(sil) { my_set_silent = 1; }
    back = dk_new(dk_app_t,1);
    if(back) {
      /*
	Initialize member variables
      */
      app_init_empty(back);

      if(set_silent || my_set_silent || sil) {
        (back->l).f.k = DK_LOG_LEVEL_NONE;
	(back->l).f.m = DK_LOG_LEVEL_NONE;
	(back->l).e.m = DK_LOG_LEVEL_NONE;
	(back->l).o.m = DK_LOG_LEVEL_NONE;
      }
      if(nostdio) {
        (back->l).nostdio = nostdio;
      }
      /*
	Copy command line arguments
      */
      if(argc > 0) {
	argptr = dk_new(CHARPTR,argc);
	if(argptr) {
	  for(i = 0; i < argc; i++) {
	    argptr[i] = NULL;
	    if(argv[i]) {
	      argptr[i] = dkstr_dup(argv[i]);
	    }
	  }
	  (back->a).o.argc = argc;
	  (back->a).o.argv = argptr;
          argptr2 = dk_new(CHARPTR,argc);
	  if(argptr2) {
	    for(i = 0; i < argc; i++) {
	      argptr2[i] = argptr[i];
	    }
	    (back->a).a.argc = argc;
	    (back->a).a.argv = argptr2;
	  }
	}
      }
      /*
	Find names and some directories
      */
      {
        if(grname) {
	  (back->n).g = dkstr_dup(grname);
	}
        if(etc) {
	  (back->d).etc = dkstr_dup(etc);
	  if((back->d).etc) {
	    dksf_correct_fnsep((back->d).etc);
	  }
	}
	if(buffer) {
	  if(dksf_get_uname(buffer,maxpathlen)) {
	    (back->n).u = dkstr_dup(buffer);
	  }
	  if(dksf_get_hostname(buffer,maxpathlen)) {
	    (back->n).h = dkstr_dup(buffer);
	  }
	  if(argc > 0) {
	  if(argv[0]) {
	    bptr = dkstr_rchr(argv[0], fn_sep[0]);
	    if(bptr) bptr++;
	    else bptr = argv[0];
	    (back->n).a = dkstr_dup(bptr);
	    if((back->n).a) {
	      bptr = dkstr_rchr((back->n).a, '.');
	      if(bptr) {
	        *bptr = (char)0;
	      }
	    }
	  }
	  }
	  if(dksf_get_home(buffer,maxpathlen)) {
	    (back->d).h = dkstr_dup(buffer);
	  }
	  if(dksf_get_tempdir(buffer,maxpathlen)) {
	    (back->d).t = dkstr_dup(buffer);
	  }
	  if(argc > 0) {
	  if(argv[0]) {
	    if(buffer2) {
	      if(dksf_getcwd(buffer2,maxpathlen)) {
	        if(dksf_get_executable(buffer,maxpathlen,buffer2,argv[0],1)) {
		  (back->x).f = dkstr_dup(buffer);
		  bptr = dkstr_rchr(buffer, fn_sep[0]);
		  if(bptr) {
		    *bptr = '\0';
		    (back->x).d = dkstr_dup(buffer);
		  }
	        }
	      }
	    }
	  }
	  }
	  if((back->x).d) {
            if((strlen((back->x).d) + 3) < (unsigned long)maxpathlen) {
	      strcpy(buffer, (back->x).d);
	      bptr = dkstr_rchr(buffer, fn_sep[0]);
	      if(bptr) {
	        bptr++;
	        if(strcmp(bptr, bin_dir) == 0) {
		  /* strcpy(bptr, lib_dir); */
		  strcpy(bptr, share_dir);
		  (back->d).s = dkstr_dup(buffer);
                  if((strlen(buffer) + strlen((back->n).a) + 1) < (size_t)maxpathlen) {
		    strcat(buffer, fn_sep);
		    strcat(buffer, (back->n).a);
		    (back->d).a = dkstr_dup(buffer);
		  }
	        } else  {
		  (back->d).a = dkstr_dup(buffer);
		  (back->d).s = dkstr_dup(buffer);
	        }
	      }
	    }

	  }
	}
      }
      /*
	Initialize preferences management
      */
#if DK_HAVE_WINREG_H
	hkTemp = 0;
	phkSubkey = &hkTemp;
	retval = RegCreateKeyExA(
	  HKEY_LOCAL_MACHINE,
          hklm,
	  0,
	  NULL,
	  REG_OPTION_NON_VOLATILE,
	  KEY_QUERY_VALUE,
	  NULL,
	  phkSubkey,
	  &disp
	);
	if(retval == ERROR_SUCCESS) {
	  (back->p).hklm_all = hkTemp;
	  (back->p).what |= 1;
	}
	if(buffer && ((back->n).a)) {
	if((strlen(hklm) + strlen((back->n).a) + 1) < (size_t)maxpathlen) {
	  strcpy(buffer, hklm);
	  strcat(buffer, backslash);
	  strcat(buffer, (back->n).a);
	  hkTemp = 0; phkSubkey = &hkTemp;
	  retval = RegCreateKeyExA(
	    HKEY_LOCAL_MACHINE, buffer,
	    0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE,
	    NULL, phkSubkey, &disp
	  );
	  if(retval == ERROR_SUCCESS) {
	    (back->p).hklm_app = hkTemp;
	    (back->p).what |= 2;
	  }
	}
	}
	hkTemp = 0;
	phkSubkey = &hkTemp;
	retval = RegCreateKeyExA(
	  HKEY_CURRENT_USER,
	  hkcu,
	  0,
	  NULL,
	  REG_OPTION_NON_VOLATILE,
	  KEY_ALL_ACCESS,
	  NULL,
	  phkSubkey,
	  &disp
	);
	if(retval == ERROR_SUCCESS) {
	  (back->p).hkcu_all = hkTemp;
	  (back->p).what |= 4;
	}
	if(buffer && ((back->n).a)) {
	if((strlen(hkcu) + strlen((back->n).a) + 1) < (size_t)maxpathlen) {
	  strcpy(buffer, hkcu);
	  strcat(buffer, backslash);
	  strcat(buffer, (back->n).a);
	  hkTemp = 0; phkSubkey = &hkTemp;
	  retval = RegCreateKeyExA(
	    HKEY_CURRENT_USER, buffer, 0, NULL,
	    REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
	    phkSubkey, &disp
	  );
	  if(retval == ERROR_SUCCESS) {
	    (back->p).hkcu_app = hkTemp;
	    (back->p).what |= 8;
	  }
	}
	}
#else
      (back->p).s = dksto_open(DK_STO_SIZE_TINY);
      if((back->p).s) {
	(back->p).si = dksto_it_open((back->p).s);
	if((back->p).si) {
	  dksto_set_comp((back->p).s, dkapp_pref_compare, 0);
	  dksto_use_trees((back->p).s, 0);
	} else {
	  dksto_close((back->p).s);
	  (back->p).s = NULL;
	}
      }
      (back->p).u = dksto_open(DK_STO_SIZE_TINY);
      if((back->p).u) {
	(back->p).ui = dksto_it_open((back->p).u);
	if((back->p).ui) {
	  dksto_set_comp((back->p).u, dkapp_pref_compare, 0);
	  dksto_use_trees((back->p).u, 0);
	} else {
	  dksto_close((back->p).u);
	  (back->p).u = NULL;
	}
      }
#endif
      (back->p).a = dksto_open(DK_STO_SIZE_TINY);
      if((back->p).a) {
	(back->p).ai = dksto_it_open((back->p).a);
	if((back->p).ai) {
	  dksto_set_comp((back->p).a, dkapp_pref_compare, 0);
	  dksto_use_trees((back->p).a, 0);
	} else {
	  dksto_close((back->p).a);
	  (back->p).a = NULL;
	}
      }
      (back->p).c = dksto_open(DK_STO_SIZE_TINY);
      if((back->p).c) {
	(back->p).ci = dksto_it_open((back->p).c);
	if((back->p).ci) {
	  dksto_set_comp((back->p).c, dkapp_pref_compare, 0);
	  dksto_use_trees((back->p).c, 0);
	} else {
	  dksto_close((back->p).c);
	  (back->p).c = NULL;
	}
      }
      argptr = (back->a).a.argv;
      for(i = 0; i < (back->a).a.argc; i++) {
	cptr = argptr[i];
	if(cptr[0] == '-') {
	if(cptr[1] == '-') {
	if(cptr[2] == '/') {
	  for(j = i; j < ((back->a).a.argc - 1); j++) {
	    argptr[j] = argptr[j+1];
	  }
	  (back->a).a.argc = (back->a).a.argc - 1 ;
	  cptr2 = dkstr_chr(cptr,'=');
	  if(cptr2) {
	    *(cptr2++) = (char)0;
	    add_pref_to_list((back->p).c, (back->p).ci, &(cptr[2]), cptr2, 7);
	  }
	  i--;
	}
	}
	}
      }
      /*
	Read preferences from files
      */
#if !DK_HAVE_WINREG_H
      if(buffer) {
	  if((back->d).s) { 
	    if(((back->p).s) && ((back->p).si)) { 
	      if((strlen((back->d).s) + strlen(appdef) + 1)
	         < (unsigned long)maxpathlen)
	      {
		
		strcpy(buffer, (back->d).s);
		strcat(buffer, fn_sep);
		strcat(buffer, appdef);
		
		read_cfg(
		  (back->p).s, (back->p).si, buffer,
		  (back->n).u, (back->n).a, (back->n).h,
		  0
		);
	      }
	      if((back->n).a) {
if((strlen((back->d).s)+strlen(appdef)+strlen((back->n).a)+2)
   < (unsigned long)maxpathlen)
{
		strcpy(buffer, (back->d).s);
		strcat(buffer, fn_sep);
		strcat(buffer, appdef);
		strcat(buffer, dot);
		strcat(buffer, (back->n).a);
		
		read_cfg(
		  (back->p).s, (back->p).si, buffer,
		  (back->n).u, (back->n).a, (back->n).h,
		  2
		);
}
	      }
	    }
	  }
	  if(((back->p).s) && ((back->p).si)) {
	    
	    read_cfg(
	      (back->p).s, (back->p).si,
	      local_default_configuration,
	      (back->n).u, (back->n).a, (back->n).h,
	      0
	    );
#if DK_HAVE_FEATURE_BACKSLASH
	    if((back->n).a) {
	      if((strlen(local_default_configuration) + strlen((back->n).a) + 4) < maxpathlen) {
		strcpy(buffer, local_default_configuration);
		cptr = dkstr_chr(buffer, fn_sep[0]);
		if(cptr) {
		  cptr++;
		  strcpy(cptr, (back->n).a);
		  strcat(cptr, ".DEF");
		  
		  read_cfg(
		    (back->p).s, (back->p).si, buffer,
		    (back->n).u, (back->n).a, (back->n).h,
		    2
		  );
		}
	      }
	    }
#else
	    if((back->n).a) {
	    if((strlen((back->n).a) + strlen(local_default_configuration) + 2)
	       < (unsigned long)maxpathlen)
	    {
	    }
	      strcpy(buffer, local_default_configuration);
              strcat(buffer, ".");
	      strcat(buffer, (back->n).a);
	      
	      read_cfg(
		(back->p).s, (back->p).si, buffer,
		(back->n).u, (back->n).a, (back->n).h,
		2
	      );
	    }
#endif
	  }
	  if((back->d).h) { 
	    if((strlen((back->d).h) + strlen(defaults_sub) + 1)
	       < (unsigned long)maxpathlen)
	    {
	      strcpy(buffer, (back->d).h);
	      strcat(buffer, fn_sep);
	      strcat(buffer, defaults_sub);
	      dksf_mkdir(buffer, DK_PERM_CREATE_PROTECTED);
	      if((strlen(buffer) + strlen(all_files) + 1)
	         < (unsigned long)maxpathlen)
	      {
		strcat(buffer, fn_sep);
		strcat(buffer, all_files);
                if(((back->p).u) && ((back->p).ui)) {
		  
		  read_cfg(
		    (back->p).u, (back->p).ui, buffer,
                    (back->n).u, (back->n).a, (back->n).h,
		    4
		  );
		}
	      }
	      strcpy(buffer, (back->d).h);
	      strcat(buffer, fn_sep);
	      strcat(buffer, defaults_sub);
	      if((back->n).a) {
		if((strlen(buffer) + strlen((back->n).a) + 1)
		   < (unsigned long)maxpathlen)
		{
		  strcat(buffer, fn_sep);
		  strcat(buffer, (back->n).a);
		  if(((back->p).u) && ((back->p).ui)) {
		    
		    read_cfg(
		      (back->p).u, (back->p).ui, buffer,
		      (back->n).u, (back->n).a, (back->n).h,
		      6
		    );
		  }
		}
	      }
	    }
	  }
      }
#endif
      /*
	Correct file and dir names
      */
      
      if(buffer && buffer2) {
        
	if(dkapp_get_pref(back,key_sec_ign_file_owner,buffer,maxpathlen,(~(DK_APP_PREF_EXCL_CMD)))) {
	  if(dkstr_is_on(buffer)) {
	    back->relaxed_fopen_check |= DK_SF_SEC_OWNER;
	  }
	}
	if(dkapp_get_pref(back,key_sec_ign_dir_world_writable,buffer,maxpathlen,(~(DK_APP_PREF_EXCL_CMD)))) {
	  if(dkstr_is_on(buffer)) {
	    back->relaxed_fopen_check |= DK_SF_SEC_WO;
	    back->relaxed_fopen_check |= DK_SF_SEC_WG;
	  }
	} else {
	  if(dkapp_get_pref(back,key_sec_ign_dir_group_writable,buffer,maxpathlen,(~(DK_APP_PREF_EXCL_CMD)))) {
	    if(dkstr_is_on(buffer)) {
	      back->relaxed_fopen_check |= DK_SF_SEC_WG;
	    }
	  }
	}
	if(dkapp_get_pref(back,app_dir,buffer,maxpathlen,0)) {
	  if(dkapp_transform_string_ext1(back,buffer2,maxpathlen,buffer,1)) {
	    dksf_correct_fnsep(buffer2);
            if(dkstat_get(&st,buffer2)) {
	      if((st.filetype & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
		cptr = dkstr_dup(buffer2);
		if(cptr) {
		  if((back->d).a) {
		    cptr2 = (back->d).a;
		    dk_delete(cptr2); (back->d).a = NULL;
		  }
		  (back->d).a = cptr;
		}
	      }
	    }
	  }
	}
	if(dkapp_get_pref(back,shared_dir,buffer,maxpathlen,0)) {
	  if(dkapp_transform_string_ext1(back,buffer2,maxpathlen,buffer,1)) {
	    dksf_correct_fnsep(buffer2);
            if(dkstat_get(&st,buffer2)) {
	      if((st.filetype & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
		cptr = dkstr_dup(buffer2);
		if(cptr) {
		  if((back->d).s) {
		    cptr2 = (back->d).s;
		    dk_delete(cptr2); (back->d).s = NULL;
		  }
		  (back->d).s = cptr;
		}
	      }
	    }
	  }
	}
	if(dkapp_get_pref(back,tmp_dir,buffer,maxpathlen,0)) {
	  if(dkapp_transform_string_ext1(back,buffer2,maxpathlen,buffer,1)) {
	    dksf_correct_fnsep(buffer2);
	    if(dkstat_get(&st,buffer2)) {
	      if((st.filetype & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
		cptr = dkstr_dup(buffer2);
		if(cptr) {
		  if((back->d).t) {
		    cptr2 = (back->d).t;
		    dk_delete(cptr2); (back->d).t = NULL;
		  }
		  (back->d).t = cptr;
		}
	      }
	    } else {
	      (void)dksf_mkdir(buffer2, DK_PERM_CREATE_DIR);
	      if(dkstat_get(&st,buffer2)) {
		if((st.filetype & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
		  cptr = dkstr_dup(buffer2);
		  if(cptr) {
		    if((back->d).t) {
		      cptr2 = (back->d).t;
		      dk_delete(cptr2); (back->d).t = NULL;
		    }
		    (back->d).t = cptr;
		  }
		}
	      }
	    }
	  }
	}
	if((back->d).t) {
          if(dksf_have_getpid()) {	
	    if((strlen((back->d).t) + 13) < (size_t)maxpathlen) {
	      int cc; unsigned long ul;	
	      mypid = dksf_getpid();
	      cc = 1; ul = 0UL;
	      do {
	        sprintf(
		  buffer,
		  "%s%s%08lX.%03lu",
		  (back->d).t,
		  fn_sep,
		  mypid,
		  ul
		); 
		if(!dkstat_get(&st, buffer)) {	
		  (void)dksf_mkdir(buffer, DK_PERM_CREATE_DIR);
		  if(dkstat_get(&st, buffer)) {	
		    if(((st.filetype) & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
		      (back->d).pt = dkstr_dup(buffer);
		      cc = 0;
		      if(!((back->d).pt)) {
		        dksf_remove_directory(buffer);
		      } else {			
		      }
		    } else {			
		      dksf_remove_directory(buffer);
		    }
		  } else {			
		    /* make sure not to leave artefacts */
		    dksf_remove_directory(buffer);
		  }
		} else {			
		}
	      } while((cc == 1) && (!((back->d).pt)) && (++ul < 1000UL));
	    } else {	
	    }
	  } else {
	    unsigned long myul;		
	    myul = 1UL;
	    while(myul != 0UL) {
	      if((strlen((back->d).t) + 13) < (size_t)maxpathlen) {
		sprintf(buffer2, "%08lX", myul);
		strcpy(buffer, (back->d).t);
		strcat(buffer, fn_sep);
		strcat(buffer, buffer2);
		strcat(buffer, dot_tmp);
		if(!dkstat_get(&st,buffer)) {
		  (void)dksf_mkdir(buffer, DK_PERM_CREATE_DIR);
		  if(dkstat_get(&st,buffer)) {
		    if(((st.filetype) & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
		      cptr = dkstr_dup(buffer);
		      if(cptr) {
			(back->d).pt = cptr;
			myul = 0UL;
		      } else {
			dksf_remove_directory(buffer);
			myul = 0UL;
		      }
		    } else {
		      myul++;
		    }
		  } else {
		    myul++;
		  }
		} else {
		  myul++;
		}
	      } else {
		myul = 0UL;
	      }
	    }
	  }
	}
#if DK_HAVE_CODEPAGES
	if(dkapp_get_pref(back,log_file_codepage,buffer,maxpathlen,0)) {
	  if(strcmp(minus_sign, buffer)) {
	  cp_stream = my_read_file(back, buffer, 0, 1);
	  if(cp_stream) {
	    (back->l).f.c = dkcp_open(cp_stream);
	    dkstream_close(cp_stream);
	  }
	  cp_stream = NULL;
	  }
	}
	if(dkapp_get_pref(back,log_stdout_codepage,buffer,maxpathlen,0)) {
	  if(strcmp(minus_sign, buffer)) {
	  cp_stream = my_read_file(back, buffer, 0, 1);
	  if(cp_stream) {
	    (back->l).o.c = dkcp_open(cp_stream);
	    dkstream_close(cp_stream);
	  }
	  cp_stream = NULL;
	  }
	}
	if(dkapp_get_pref(back,log_stderr_codepage,buffer,maxpathlen,0)) {
	  if(strcmp(minus_sign, buffer)) {
	  cp_stream = my_read_file(back, buffer, 0, 1);
	  if(cp_stream) {
	    (back->l).e.c = dkcp_open(cp_stream);
	    dkstream_close(cp_stream);
	  }
	  cp_stream = NULL;
	  }
	}
#endif
	
	if(dkapp_get_pref(back,log_file_name,buffer,maxpathlen,0)) {
	  if(dkapp_transform_string_ext1(back,buffer2,maxpathlen,buffer,1)) {
	    dksf_correct_fnsep(buffer2);
            (back->l).f.n = dkstr_dup(buffer2);
	  }
	}
      }
      /*
	Set up logging
      */
      if(buffer) {
	char *appname;
	size_t lgt;
	int i;
	if(!(set_silent || my_set_silent || sil)) {
	  if(dkapp_get_pref(back,log_file_level,buffer,maxpathlen,0)) {
	    i = get_log_level(buffer);
	    if(i >= 0) {
	      (back->l).f.m = i;
	    }
	  }
	  if(dkapp_get_pref(back,log_file_keep,buffer,maxpathlen,0)) {
	    i = get_log_level(buffer);
	    if(i >= 0) {
	      (back->l).f.k = i;
	    }
	  }
	}
	i = 0;
	if(dkapp_get_pref(back,log_file_time,buffer,maxpathlen,0)) {
	  if(dkstr_is_on(buffer)) {
	    i = 1;
	  }
	}
	if(dkapp_get_pref(back,log_file_split,buffer,maxpathlen,0)) {
	  if(dkstr_is_on(buffer)) {
	    i |= 2;
	  }
	}
	(back->l).f.f = i;
	if(dkapp_get_pref(back,log_file_ide,buffer,maxpathlen,0)) {
	  (back->l).f.ide_type = dkapp_ide_type(buffer);
	  /* 1 + dkstr_array_abbr(ide_keys,buffer,'$',0); */
	  
	}
	if(!(set_silent || my_set_silent || sil)) {
	  if(dkapp_get_pref(back,log_stdout_level,buffer,maxpathlen,0)) {
	    i = get_log_level(buffer);
	    if(i >= 0) {
	      (back->l).o.m = i;
	    }
	  }
	}
	i = 0;
	if(dkapp_get_pref(back,log_stdout_time,buffer,maxpathlen,0)) {
	  if(dkstr_is_on(buffer)) {
	    i = 1;
	  }
	}
	if(dkapp_get_pref(back,log_stdout_split,buffer,maxpathlen,0)) {
	  if(dkstr_is_on(buffer)) {
	    i |= 2;
	  }
	}
	(back->l).o.f = i;
	if(dkapp_get_pref(back,log_stdout_ide,buffer,maxpathlen,0)) {
	  (back->l).o.ide_type =
	  1 + dkstr_array_abbr(ide_keys, buffer, '$', 0);
	  
	}
	if(!(set_silent || my_set_silent || sil)) {
	  if(dkapp_get_pref(back,log_stderr_level,buffer,maxpathlen,0)) {
	    i = get_log_level(buffer);
	    if(i >= 0) {
	      (back->l).e.m = i;
	    }
	  }
	}
	i = 0;
	if(dkapp_get_pref(back,log_stderr_time,buffer,maxpathlen,0)) {
	  if(dkstr_is_on(buffer)) {
	    i = 1;
	  }
	}
	if(dkapp_get_pref(back,log_stderr_split,buffer,maxpathlen,0)) {
	  if(dkstr_is_on(buffer)) {
	    i |= 2;
	  }
	}
	(back->l).e.f = i;
	if(dkapp_get_pref(back,log_stderr_ide,buffer,maxpathlen,0)) {
	  (back->l).e.ide_type =
	  1 + dkstr_array_abbr(ide_keys, buffer, '$', 0);
	  
	}
#if DK_HAVE_SYSLOG
	i = DK_APP_PREF_EXCL_CMD;
	i |= DK_APP_PREF_EXCL_PROG;
	i |= DK_APP_PREF_EXCL_USER;
        if(dkapp_get_pref(back,log_syslog_level,buffer,maxpathlen,i)) {
	  i = get_log_level(buffer);
	  if(i >= 0) {
	    (back->l).s.m = i;
	  }
	}
#endif
	appname = (back->n).a;
	appname = (appname ? appname : app);
	lgt = strlen(appname) + strlen(suffix_log) + 1;
        if(dksf_have_getpid()) {
	  lgt += 16;
        }
	if(lgt < (size_t)maxpathlen) {
	  strcpy(buffer, appname);
	  if(dksf_have_getpid()) {
	    strcat(buffer, dot);
	    appname = buffer;
	    while(*appname) appname++;
	    sprintf(appname, "%lu", (unsigned long)dksf_getpid());
	  }
	  strcat(buffer, dot);
	  strcat(buffer, suffix_log);
	}
	if(!((back->l).f.n)) {
	  (back->l).f.n = dkstr_dup(buffer);
	}
	
	if((back->l).f.n) {
#if NO_FOPEN_CHECKING
#if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64
          (back->l).f.t = fopen64((back->l).f.n, str_w);
#else
	  (back->l).f.t = fopen((back->l).f.n, str_w);
#endif
#else
	  back->relaxed_fopen_reason = 0;
	  (back->l).f.t = dksf_msfo((back->l).f.n, str_w, back->relaxed_fopen_check, &(back->relaxed_fopen_reason));
	  if((!((back->l).f.t)) && (back->relaxed_fopen_reason)) {
	    dkapp_err_nowrite(back, (back->l).f.n, back->relaxed_fopen_reason);
	  }
#endif
	}
#if DK_HAVE_SYSLOG
	appname = (back->n).a;
	appname = (appname ? appname : app);
	if((back->l).s.m > DK_LOG_LEVEL_NONE) {
	  (back->l).s.o = 1;
	  openlog(appname, (LOG_PID|LOG_CONS), LOG_USER);
	}
#endif
	i = DK_APP_PREF_EXCL_USER | DK_APP_PREF_EXCL_SYSTEM;
	if(dkapp_get_pref(back,ui_lang,buffer,maxpathlen,i)) {
	  save_lang(back,buffer);
	  /* ##### es */
	} else {
	  int use_env_lang;
	  use_env_lang = 1;
	  if(dkapp_get_pref(back,ui_lang_env,buffer,maxpathlen,0)) {
	    if(dkstr_is_on(buffer)) {
	      use_env_lang = 1;
	    } else {
	      use_env_lang = 0;
	    }
	  }
	  if(use_env_lang) {
	    cptr = getenv("LANG");
	    if(cptr) {
	      if(strlen(cptr) < (size_t)maxpathlen) {
	        strcpy(buffer,cptr);
	        save_lang(back,buffer);
	        /* ##### es */
	      }
	    } else {	/* check registry if available */
#if DK_HAVE_WINREG_H
	      HKEY intkey;
	      LONG intres;
	      DWORD intType;
	      DWORD intlen;
	      char intchar[64];
	      char *intptr;
	      char intc;
	      intres = RegOpenKeyExA(
	        HKEY_CURRENT_USER,
		"Control Panel\\International",
		(DWORD)0,
		KEY_READ,
		&intkey
	      );
	      if(intres == ERROR_SUCCESS) {
	        intType = REG_SZ;
		intlen = (DWORD)sizeof(intchar);
	        intres = RegQueryValueExA(
		  intkey,
		  "sLanguage",
		  NULL,
		  &intType,
		  (LPBYTE)intchar,
		  &intlen
		);
		if(intres == ERROR_SUCCESS) {
		  if((intType == REG_SZ) || (intType == REG_EXPAND_SZ)) {
		    if(intlen >= 2) {
		      intchar[2] = '\0';
		      strcpy(buffer, intchar);
		      intptr = buffer;
		      while(*intptr) {
		        intc = *intptr;
			if(isascii(intc)) {
			  if(isupper(intc)) {
			    *intptr = tolower(intc);
			  }
			}
		        intptr++;
		      }
		      save_lang(back,buffer);
		    }
		  }
		}
	        RegCloseKey(intkey);
	      }
#endif
	    }
	  } else {
	    if(dkapp_get_pref(back,ui_lang,buffer,maxpathlen,0)) {
	      save_lang(back,buffer);
	      /* ##### es */
	    } else {
	      strcpy(buffer, default_language);
	      save_lang(back,buffer);
	    }
	  }
	}
	if(dkapp_get_pref(back,storage_trees,buffer,maxpathlen,0)) {
	  if(dkstr_is_on(buffer)) {
	    dksto_use_trees(NULL,1);
	  } else {
	    dksto_use_trees(NULL,0);
	  }
	}
      }
      cptr = (back->loc).l;
      if(cptr) {
	while(*cptr) {
	  if(isupper(*cptr)) { *cptr = tolower(*cptr); }
	  cptr++;
	}
      }
      cptr = (back->loc).r;
      if(cptr) {
	while(*cptr) {
	  if(isupper(*cptr)) { *cptr = tolower(*cptr); }
	  cptr++;
	}
      }
      cptr = (back->loc).e;
      if(cptr) {
	while(*cptr) {
	  if(isupper(*cptr)) { *cptr = tolower(*cptr); }
	  cptr++;
	}
      }

      /*
	Set up string table handling
      */
      (back->loc).s = dksto_open(DK_STO_SIZE_TINY);
      if((back->loc).s) {
	(back->loc).si = dksto_it_open((back->loc).s);
	if((back->loc).si) {
	  dksto_set_comp((back->loc).s, stt_entry_comp, 0);
	  dksto_use_trees((back->loc).s, 0);
	} else {
	  dksto_close((back->loc).s);
	  (back->loc).s = NULL;
	}
      }
      my_find_multi(back,log_level_string_finder,str_dkapp,0);
      my_find_multi(back,open_and_close_finder,str_dkapp,0);
      if(!app_err_conf) {
	my_find_multi(back,app_err_find,str_dkappe,0);
	app_err_conf = 1;
      }
      /*
	Debug output
      */
      if(grname && (!((back->n).g))) {
        dkapp_err_memory(back, sizeof(char), (1+strlen(grname)));
      }
      if(etc && (!((back->d).etc))) {
        dkapp_err_memory(back, sizeof(char), (1+strlen(etc)));
      }
      if(dkapp_get_min_loglevel(back) >= DK_LOG_LEVEL_DEBUG) {
	my_find_multi(back,debug_string_finder,str_dkappd,0);
	logmsgs[0] = debug_strings[0];
	logmsgs[1] = (((back->n).u) ? ((back->n).u) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[1];
	logmsgs[1] = (((back->n).a) ? ((back->n).a) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	if((back->n).g) {
	  logmsgs[0] = debug_strings[19];
	  logmsgs[1] = (back->n).g;
	  dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	}
	logmsgs[0] = debug_strings[2];
	logmsgs[1] = (((back->n).h) ? ((back->n).h) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[3];
	logmsgs[1] = (((back->d).h) ? ((back->d).h) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[4];
	logmsgs[1] = (((back->d).t) ? ((back->d).t) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[20];
	logmsgs[1] = (((back->d).etc) ? ((back->d).etc) : unix_sysconfdir);
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[14];
	logmsgs[1] = (((back->d).pt) ? ((back->d).pt) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[5];
	logmsgs[1] = (((back->d).a) ? ((back->d).a) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[6];
	logmsgs[1] = (((back->d).s) ? ((back->d).s) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[7];
	logmsgs[1] = (((back->x).f) ? ((back->x).f) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[8];
	logmsgs[1] = (((back->x).d) ? ((back->x).d) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[9];
	logmsgs[1] = (((back->l).f.n) ? ((back->l).f.n) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[10];
	logmsgs[1] = (((back->loc).l) ? ((back->loc).l) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[11];
	logmsgs[1] = (((back->loc).r) ? ((back->loc).r) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
	logmsgs[0] = debug_strings[12];
	logmsgs[1] = (((back->loc).e) ? ((back->loc).e) : (debug_strings[13]));
	dkapp_log_msg(back,DK_LOG_LEVEL_DEBUG,logmsgs,2);
      } else {
        my_find_multi(back,debug_string_finder,NULL,0);
      }
      
      
      
      
      
      
      
      
      
      
#if DK_HAVE_WINREG_H
      
#endif
      
      
      
      
      if((back->n).a) {
        logmsgs[0] = open_and_close_strings[0];
        logmsgs[1] = (back->n).a;
        logmsgs[2] = open_and_close_strings[1];
	dkapp_log_msg(back,DK_LOG_LEVEL_PROGRESS,logmsgs,3);
      }
    }
  }
  if(buffer2) {
    dk_delete(buffer2);
  }
  if(buffer) {
    if(dkapp_get_pref(back, key_keep_temp, buffer, maxpathlen, 0)) {
      char *ptr;
      ptr = dkstr_start(buffer, NULL);
      if(ptr) {
        dkstr_chomp(ptr, NULL);
	if(dkstr_is_bool(buffer)) {
	  if(dkstr_is_on(buffer)) {
	    back->keep_temp_dir = DK_LOG_LEVEL_IGNORE;
	  } else {
	    back->keep_temp_dir = DK_LOG_LEVEL_NONE;
	  }
	} else {
	  back->keep_temp_dir = dkstr_array_abbr(
	    log_level_strings,
	    ptr, '$', 0
	  );
	  if((back->keep_temp_dir) < 0) {
	    back->keep_temp_dir = DK_LOG_LEVEL_NONE;
	  }
	}
      }
    }
    dk_delete(buffer);
  }
  
  return back;
}

dk_app_t *
dkapp_open DK_P2(int, argc, char **, argv)
{
  dk_app_t *back = NULL;
  back = my_app_open(argc, argv, NULL, NULL, 0, 0);
  return back;
}

dk_app_t *
dkapp_open_ext1 DK_P6(int,argc,char **,argv,char *,grname,char *,etc,int,sil,int,nostd)
{
  dk_app_t *back = NULL;
  back = my_app_open(argc, argv, grname, etc, sil, nostd);
  return back;
}

int dkapp_tmpnam DK_P3(dk_app_t *, a, char *, buffer, size_t, sz)
{
  int back = 0;
  char number_buffer[16];
  if(a && buffer && sz) {
    if((a->d).pt) {
      if((strlen((a->d).pt) + 13) < sz) {
	sprintf(number_buffer, "%08lX", (a->td).l);
	(a->td).l += 1UL;
	strcpy(buffer, (a->d).pt);
	strcat(buffer, fn_sep);
	strcat(buffer, number_buffer);
	strcat(buffer, dot_tmp);
	back = 1;
      }
    }
    if(!back) {
      if((a->d).t) {
	if((strlen((a->d).t) + 13) < sz) {
	  sprintf(number_buffer, "%08lX", (a->td).l);
	  (a->td).l += 1UL;
	  strcpy(buffer, (a->d).t);
	  strcat(buffer, fn_sep);
	  strcat(buffer, number_buffer);
	  strcat(buffer, dot_tmp);
	  back = 1;
	}
      }
    }
  }
  return back;
}

void
dkapp_unconfigure DK_P1(dk_app_t *,a)
{
  if(a) {
    (a->p).unc = 1;
  }
}

void dkapp_err_traverse_dir DK_P2(dk_app_t *, app, char *, dirname)
{
  char *logmsg[3];
  if(app && dirname) {
    logmsg[0] = app_err_str[0];
    logmsg[1] = dirname;
    logmsg[2] = app_err_str[1];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}

void dkapp_err_stat_failed DK_P2(dk_app_t *, app, char *, name)
{
  char *logmsg[3];
  if(app && name) {
    logmsg[0] = app_err_str[2];
    logmsg[1] = name;
    logmsg[2] = app_err_str[3];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}

void dkapp_err_cwd DK_P1(dk_app_t *, app)
{
  char *logmsg[3];
  if(app) {
    logmsg[0] = app_err_str[4];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
  }
}

void dkapp_err_matchfile DK_P2(dk_app_t *, app, char *, name)
{
  char *logmsg[3];
  if(app && name) {
    logmsg[0] = app_err_str[8];
    logmsg[1] = name;
    logmsg[2] = app_err_str[9];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}

void dkapp_err_matchdir DK_P2(dk_app_t *, app, char *, name)
{
  char *logmsg[3];
  if(app && name) {
    logmsg[0] = app_err_str[10];
    logmsg[1] = name;
    logmsg[2] = app_err_str[11];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}

void dkapp_err_fopenr DK_P2(dk_app_t *, app, char *, name)
{
  char *logmsg[3];
  if(app && name) {
    logmsg[0] = app_err_str[12];
    logmsg[1] = name;
    logmsg[2] = app_err_str[13];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}

void dkapp_err_fopenw DK_P2(dk_app_t *, app, char *, name)
{
  char *logmsg[3];
  if(app && name) {
    logmsg[0] = app_err_str[14];
    logmsg[1] = name;
    logmsg[2] = app_err_str[15];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}

void dkapp_err_fwrite DK_P2(dk_app_t *, app, char *, name)
{
  char *logmsg[3];
  if(app && name) {
    logmsg[0] = app_err_str[16];
    logmsg[1] = name;
    logmsg[2] = app_err_str[17];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}


int
dkapp_log_level_in_use DK_P2(dk_app_t *, app, int, loglevel)
{
  int back = 0;
  if(app) {
    if(((app->l).o.m) >= loglevel) {
      back = 1;
    }
    if(((app->l).e.m) >= loglevel) {
      back = 1;
    }
    if(((app->l).f.m) >= loglevel) {
      back = 1;
    }
#if DK_HAVE_SYSLOG
    if(((app->l).s.m) >= loglevel) {
      back = 1;
    }
#endif
  }
  return back;
}

void dkapp_err_fread DK_P2(dk_app_t *, app, char *, name)
{
  char *logmsg[3];
  if(app && name) {
    logmsg[0] = app_err_str[18];
    logmsg[1] = name;
    logmsg[2] = app_err_str[19];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
  }
}

int
dkapp_get_relaxed_fopen_check DK_P1(dk_app_t *,app)
{
  int back = 0;
  if(app) {
    back = app->relaxed_fopen_check;
  }
  return back;
}

dk_stream_t *
dkapp_stream_openfile DK_P3(dk_app_t *,a,char *,n,char *,m)
{
  dk_stream_t *back = NULL;
  int ign, reason;
  if(n && m) {
    reason = ign = 0;
    if(a) { ign = a->relaxed_fopen_check; }
    back = dkstream_openfile(n,m,ign,&reason);
    if(a && (!back) && reason) {
      dkapp_err_nowrite(a,n,reason);
    }
  }
  return back;
}


void
dkapp_err_no_zlib_support_for DK_P2(dk_app_t *,a, char *,n)
{
  char *messages[5];
  if((a) && (n)) {
    messages[0] = app_err_str[21];
    messages[1] = n;
    messages[2] = app_err_str[22];
    dkapp_log_msg(a, DK_LOG_LEVEL_ERROR, messages, 3);
  }
}



void
dkapp_err_no_bzlib_support_for DK_P2(dk_app_t *,a, char *,n)
{
  char *messages[5];
  if((a) && (n)) {
    messages[0] = app_err_str[21];
    messages[1] = n;
    messages[2] = app_err_str[23];
    dkapp_log_msg(a, DK_LOG_LEVEL_ERROR, messages, 3);
  }
}



dk_stream_t *
dkapp_stream_opengz DK_P3(dk_app_t *,a,char *,n,char *,m)
{
  dk_stream_t *back = NULL;
  int ign, reason;
  if(n && m) {
#if DK_HAVE_ZLIB_H
    reason = ign = 0;
    if(a) { ign = a->relaxed_fopen_check; }
    back = dkstream_opengz(n,m,ign,&reason);
    if(a && (!back) && reason) {
      dkapp_err_nowrite(a,n,reason);
    }
#else
    dkapp_err_no_zlib_support_for(a, n);
#endif
  }
  return back;
}

dk_stream_t *
dkapp_stream_openbz2 DK_P3(dk_app_t *,a,char *,n,char *,m)
{
  dk_stream_t *back = NULL;
  int ign, reason;
  if(n && m) {
#if DK_HAVE_BZLIB_H
    reason = ign = 0;
    if(a) { ign = a->relaxed_fopen_check; }
    back = dkstream_openbz2(n,m,ign,&reason);
    if(a && (!back) && reason) {
      dkapp_err_nowrite(a,n,reason);
    }
#else
    dkapp_err_no_bzlib_support_for(a, n);
#endif
  }
  return back;
}

FILE *
dkapp_fopen DK_P3(dk_app_t *,a,char *,n,char *,m)
{
  FILE *back = NULL;
  int ign, reason;
  if(n && m) {
    ign = reason = 0;
    if(a) { ign = a->relaxed_fopen_check; }
    back = dksf_msfo(n,m,ign,&reason);
    if(a && (!back) && reason) {
      dkapp_err_nowrite(a,n,reason);
    }
  }
  return back;
}

char *
dkapp_get_appname DK_P1(dk_app_t *,a)
{
  char *back = NULL;
  if(a) {
    back = (a->n).a;
  }
  return back;
}

void
dkapp_set_silent DK_P2(dk_app_t *,a,int,v)
{
  char buffer[32];
  size_t maxpathlen;
  int i;
  maxpathlen = sizeof(buffer);
  if(a) {
    if(v) {
      (a->l).f.m = DK_LOG_LEVEL_NONE;
      (a->l).f.k = DK_LOG_LEVEL_NONE;
      (a->l).e.m = DK_LOG_LEVEL_NONE;
      (a->l).o.m = DK_LOG_LEVEL_NONE;
    } else {
      if(dkapp_get_pref(a,log_file_level,buffer,maxpathlen,0)) {
        i = get_log_level(buffer);
        if(i >= 0) {
          (a->l).f.m = i;
        }
      }
      if(dkapp_get_pref(a,log_file_keep,buffer,maxpathlen,0)) {
        i = get_log_level(buffer);
        if(i >= 0) {
          (a->l).f.k = i;
        }
      }
      if(dkapp_get_pref(a,log_stdout_level,buffer,maxpathlen,0)) {
        i = get_log_level(buffer);
        if(i >= 0) {
          (a->l).o.m = i;
        }
      }
      if(dkapp_get_pref(a,log_stderr_level,buffer,maxpathlen,0)) {
        i = get_log_level(buffer);
        if(i >= 0) {
          (a->l).e.m = i;
        }
      }
    }
  } else {
    set_silent = (v ? 1 : 0);
  }
}



EXTERN void
dkapp_set_nostdio DK_P2(dk_app_t *,a, int,v)
{
  char buffer[32];
  size_t maxpathlen;
  int i;
  maxpathlen = sizeof(buffer);
  if(a) {
    (a->l).nostdio = v;
    if(v & DK_APP_LOG_NO_STDOUT) {
      (a->l).o.m = DK_LOG_LEVEL_NONE;
    } else {
      if(dkapp_get_pref(a,log_stdout_level,buffer,maxpathlen,0)) {
        i = get_log_level(buffer);
        if(i >= 0) {
          (a->l).o.m = i;
        }
      }
    }
    if(v & DK_APP_LOG_NO_STDERR) {
      (a->l).e.m = DK_LOG_LEVEL_NONE;
    } else {
      if(dkapp_get_pref(a,log_stderr_level,buffer,maxpathlen,0)) {
        i = get_log_level(buffer);
        if(i >= 0) {
          (a->l).e.m = i;
        }
      }
    }
  }
}



int
dkapp_set_groupname DK_P2(dk_app_t *,a,char *,gn)
{
  int back = 0;
  char *ptr, *cptr;
  if(a && gn) {
    ptr = dkstr_dup(gn);
    if(ptr) {
      if((a->n).g) {
        cptr = (a->n).g ; dk_delete(cptr);
	(a->n).g = NULL;
      }
      (a->n).g = ptr; back = 1;
    } else {
      dkapp_err_memory(a, sizeof(char), (1+strlen(gn)));
    }
  }
  return back;
}

void
dkapp_err_tcpip DK_P1(dk_app_t *,app)
{
  if(app) dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, &(app_err_str[20]), 1);
}


void
dkapp_err_no_such_file DK_P2(dk_app_t *, app, char *,pat)
{
  char *msgptr[3];
  if(app && pat) {
    msgptr[0] = open_and_close_strings[22];
    msgptr[1] = pat;
    msgptr[2] = open_and_close_strings[23];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, msgptr, 3);
  }
}


void
dkapp_err_multiple_files DK_P2(dk_app_t *, app, char *,pat)
{
  char *msgptr[3];
  if(app && pat) {
    msgptr[0] = open_and_close_strings[24];
    msgptr[1] = pat;
    msgptr[2] = open_and_close_strings[25];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, msgptr, 3);
  }
}


void
dkapp_set_source_filename DK_P2(dk_app_t *,a,char *,n)
{
  if(a) {
    (a->l).ef.n = n;
  }
}


char *
dkapp_get_source_filename DK_P1(dk_app_t *,a)
{
  char *back = NULL;
  if(a) { back = (a->l).ef.n; }
  return back;
}


void
dkapp_set_source_lineno DK_P2(dk_app_t *,a,unsigned long,lineno)
{
  if(a) {
    (a->l).ef.lineno = lineno;
  }
}


unsigned long
dkapp_get_source_lineno DK_P1(dk_app_t *,a)
{
  unsigned long back = 0UL;
  if(a) { back = (a->l).ef.lineno; }
  return back;
}


unsigned char *
dkapp_get_stdout_codepage DK_P1(dk_app_t *,a)
{
  unsigned char *back = NULL;
  if(a) { back = (a->l).o.c; }
  return back;
}

char *
dkapp_fne_one DK_P3(dk_app_t *,a,dk_fne_t *,f,char *,p)
{
  char *back = NULL;
  char *ptr;
  if(a && f && p) {
    if(dkfne_next(f)) {
      ptr = dkfne_get_fullname(f);
      if(ptr) {
        back = dkstr_dup(ptr);
	if(back) {
	  if(dkfne_next(f)) {
	    dk_delete(back);
	    back = NULL;
	    dkapp_err_multiple_files(a,p);
	  }
	} else {
	  dkapp_err_memory(a,1,strlen(ptr));
	}
      } else {
        dkapp_err_no_such_file(a,p);
      }
    } else {
      dkapp_err_no_such_file(a,p);
    }
  }
  return back;
}



char *
dkapp_get_str_pref DK_P2(dk_app_t *,app,char *,key)
{
  char *back = NULL;
  char buffer[256];
  
  if(app && key) {
    if (dkapp_get_pref(app,key,buffer,sizeof(buffer),0)) {
      back = dkstr_dup(buffer);
      if(!back) {
        dkapp_err_memory(app, sizeof(char), (1+strlen(buffer)));
      }
    }
  }
  
  return back;
}



int
dkapp_get_bool_pref DK_P2(dk_app_t *,app,char *,key)
{
  int back = -1;
  char buffer[256];
  
  if (app && key) {
    if (dkapp_get_pref(app,key,buffer,sizeof(buffer),0)) {
      if (dkstr_is_bool(buffer)) {
        if (dkstr_is_on(buffer)) {
	  back = 1;
	} else {
	  back = 0;
	}
      }
    }
  }
  
  return back;
}



void
dkapp_set_keep_temp DK_P2(dk_app_t *,app, int,l)
{
  if(app) {
    app->keep_temp_dir = l;
  }
}



unsigned long
dkapp_get_ul_pref DK_P2(dk_app_t *,app,char *,key)
{
  unsigned long back = 0UL;
  unsigned long ul;
  char buffer[32];
  
  if(dkapp_get_pref(app, key, buffer, sizeof(buffer), 0)) {
    if(sscanf(buffer, "%lu", &ul) == 1) {
      back = ul;
    }
  }
  
  return back;
}



/**	Message made of 1 part.
	@param	a	Application.
	@param	l	Log level.
	@param	s	Message text.
*/
static
void
msg_1 DK_P3(dk_app_t *,a, int,l, char *,s)
{
  char *logmsg[2];
  logmsg[0] = s;
  logmsg[1] = NULL;
  dkapp_log_msg(a, l, logmsg, 1);
}


/**	Message made of 3 parts.
	@param	a	Application.
	@param	l	Log level.
	@param	s1	First part.
	@param	s2	Second part.
	@param	s3	Third part.
*/
static
void
msg_3 DK_P5(dk_app_t *,a, int,l, char *,s1, char *,s2, char *,s3)
{
  char *logmsg[4];
  logmsg[0] = s1;
  logmsg[1] = s2;
  logmsg[2] = s3;
  logmsg[3] = NULL;
  dkapp_log_msg(a, l, logmsg, 3);
}


void
dkapp_err_not_a_regular_file DK_P2(dk_app_t *, app, char *,pat)
{
  msg_3(app, DK_LOG_LEVEL_ERROR, app_err_str[24], pat, app_err_str[25]);
}


void
dkapp_info_file_does_not_exist DK_P2(dk_app_t *, app, char *,pat)
{
  msg_3(app, DK_LOG_LEVEL_INFO, app_err_str[26], pat, app_err_str[27]);
}


void
dkapp_err_invalid_permissions DK_P2(dk_app_t *, app, char *,pat)
{
  msg_3(app, DK_LOG_LEVEL_ERROR, app_err_str[28], pat, app_err_str[29]);
}


void
dkapp_err_invalid_owner DK_P2(dk_app_t *, app, char *,pat)
{
  msg_3(app, DK_LOG_LEVEL_ERROR, app_err_str[30], pat, app_err_str[31]);
}



void
dkapp_err_not_a_device DK_P2(dk_app_t *, app, char *,pat)
{
  msg_3(app, DK_LOG_LEVEL_ERROR, app_err_str[32], pat, app_err_str[33]);
}


void
dkapp_info_no_openssl_support DK_P1(dk_app_t *, app)
{
  msg_1(app, DK_LOG_LEVEL_INFO, app_err_str[34]);
}


void
dkapp_debug_attempt_seed_openssl DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[21]);
}


void
dkapp_debug_attempt_seed_random DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[22]);
}



void
dkapp_debug_attempt_seed_rand48 DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[23]);
}



void
dkapp_debug_attempt_seed_rand DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[24]);
}



void
dkapp_debug_failed_seed_openssl DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[25]);
}



void
dkapp_debug_failed_seed_random DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[26]);
}



void
dkapp_debug_failed_seed_rand48 DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[27]);
}



void
dkapp_debug_failed_seed_rand DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[28]);
}



void
dkapp_debug_unavailable_openssl DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[29]);
}



void
dkapp_debug_unavailable_random DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[30]);
}



void
dkapp_debug_unavailable_rand48 DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[31]);
}



void
dkapp_debug_unavailable_rand DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[32]);
}



void
dkapp_debug_attempt_seed_file DK_P2(dk_app_t *,a, char *,fn)
{
  msg_3(a, DK_LOG_LEVEL_DEBUG, debug_strings[33], fn, debug_strings[34]);
}



void
dkapp_debug_attempt_seed_egd DK_P2(dk_app_t *,a, char *,fn)
{
  msg_3(a, DK_LOG_LEVEL_DEBUG, debug_strings[35], fn, debug_strings[36]);
}



void
dkapp_debug_attempt_seed_device DK_P2(dk_app_t *,a, char *,fn)
{
   msg_3(a, DK_LOG_LEVEL_DEBUG, debug_strings[37], fn, debug_strings[38]);
}



void
dkapp_debug_attempt_seed_kbd DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[39]);
}



void
dkapp_debug_attempt_seed_screen DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[40]);
}



void
dkapp_debug_prng_seeded_successfully DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_DEBUG, debug_strings[41]);
}



void
dkapp_warn_prng_not_crypto DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_WARNING, app_err_str[35]);
}



void
dkapp_err_no_prng_found DK_P1(dk_app_t *,a)
{
  msg_1(a, DK_LOG_LEVEL_WARNING, app_err_str[36]);
}



/**	Write a string to standard error output.
	@param	a	Application.
	@param	s	String to write.
*/
static
void
stderr_1 DK_P2(dk_app_t *,a, char *,s)
{
#if DK_HAVE_CODEPAGES
  dkcp_fputs(stderr, (a->l).e.c, s);
#else
  fputs(s, stderr); fputc('\n', stderr);
#endif
}



void
dkapp_stderr_msg_need_random_input DK_P1(dk_app_t *,a)
{
  stderr_1(a, app_err_str[37]);
}



void
dkapp_stderr_msg_need_more_random_input DK_P1(dk_app_t *,a)
{
  stderr_1(a, app_err_str[38]);
}



void
dkapp_stderr_msg_echo_not_off DK_P1(dk_app_t *,a)
{
  stderr_1(a, app_err_str[39]);
}



void
dkapp_warn_unknown_prng DK_P2(dk_app_t *,a, char *,prng)
{
  msg_3(a, DK_LOG_LEVEL_WARNING, app_err_str[40], prng, app_err_str[41]);
}




