/*
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	kls.c	The kls program.
*/



#include <dk.h>

#include <stdio.h>

#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_PROCESS_H
#include <process.h>
#endif

#include <dksf.h>
#include <dkma.h>
#include <dkenc.h>
#include <dkstr.h>
#include <dksto.h>
#include <dkapp.h>
#include <dkmem.h>
#include <dklogc.h>
#include <dkerror.h>

#if DK_HAVE_OPENSSL_MD5_H
#include <openssl/md5.h>
#endif

#if DK_HAVE_OPENSSL_SHA_H
#include <openssl/sha.h>
#endif

#if DK_HAVE_OPENSSL_RIPEMD_H
#include <openssl/ripemd.h>
#endif



#include <dklic.h>

#include "dktools-version.h"




#line 86 "kls.ctr"




/**	System configuration directory.
*/
static char sysconfdir[] = { DK_SYSCONFDIR };



/**	Program group name.
*/
static char packagename[] = { "dktools" };



/**	Type definition needed for sizeof() operator.
*/
typedef char *PCHAR;


/**	Kls job.
*/
typedef struct {
  PCHAR *filenames;			/**< File names to process. */
  int    names_used;			/**< Flag: Names used. */
  int    symlinks_limited;		/**< Flag: Symlink depth limited. */
  unsigned long symlinks_allowed;	/**< Limit for symlink depth. */
  int    symlinks_configured;		/**< Flag: Symlink limit configured. */
  FILE  *output_file;			/**< Output file to write. */
  char  *print_list;			/**< Output column order. */
  int    rec;				/**< Flag: Recursive listing. */
  int    rec_configured;		/**< Flag: Recursive l configured. */
  char  *file_types;			/**< File types to list. */
  int    ft_reg;			/**< Flag: List regular files. */
  int    ft_dir;			/**< Flag: List directories. */
  int    ft_pipe;			/**< Flag: List pipes. */
  int    ft_chr;			/**< Flag: List character devices. */
  int    ft_blk;			/**< Flag: List block devices. */
  int    ft_sock;			/**< Flag: List sockets. */
  int    ft_oth;			/**< Flag: List other file types. */
  int    md5_wanted;			/**< Flag: Message digest wanted. */
  int    sz_wanted;			/**< Flag: Size wanted. */
  int    configure_only;		/**< Flag: Save conf settings only. */
  int	 unconf_only;			/**< Flag: Unconfigure only. */
  int    show_summary;			/**< Flag: Show summary. */
  int    summary_configured;		/**< Flag: Summary flag configured. */
  int    show_version;			/**< Flag: Show version only. */
  int    show_help;			/**< Flag: Show help only. */
  int	 show_configuration;		/**< Flag: Show configuration. */
  int    mdtype_configured;		/**< Flag: Message digest configured. */
  int	 mdenc_configured;		/**< Flag: Digest encoding configured.*/
  dk_long_long_unsigned_t ull_bytes_found;	/**< Size found. */
  dk_long_long_unsigned_t ull_bytes_in_files;	/**< Size of files found. */
  dk_long_long_unsigned_t ull_files_found;	/**< Numer of files. */
  dk_long_long_unsigned_t ull_dirs_found;	/**< Number of directories. */
  int	 me_ull_bytes_found;		/**< Flag: Overflow in size. */
  int	 me_ull_bytes_in_files;		/**< Flag: Overflow in file size. */
  int	 me_ull_files_found;		/**< Flag: Overflow in number files. */
  int	 me_ull_dirs_found;		/**< Flag: Overflow in number dirs. */
  double bytes_found;			/**< Size found. */
  double bytes_in_files;		/**< Bytes in files. */
  double files_found;			/**< Number of files. */
  double dirs_found;			/**< Number of directories. */
  /* used by run_for_current() */
  char   *current_name;			/**< Name of current object. */
  unsigned long current_depth;		/**< Depth of current object. */
  unsigned long current_symlinks;	/**< Symlink depth of object. */
  int    math_error;			/**< Flag: Mathematical error. */
  double math_border;		/**< Max unsigned long expressed as double. */
  int    dc_dir;			/**< Flag: Directory. */
  int    dc_contents;			/**< Directory contents. */
  int    dc_configured;			/**< Flag: Dir contents configured. */
  int    remove_cwd_from_name;		/**< Flag: Remove cwd from output. */
  int    remove_cwd_length;		/**< Cwd length. */
  int	 size_output_width;		/**< Output width. */
  int    size_output_configured;	/**< Flag: Output width configured. */
  int	 stay_on_filesystem;		/**< Flag: Stay on filesystem. */
  int	 stay_configured;	/**< Flag: stay on filesystem configured. */
  int	 message_digest_type;		/**< Message digest type. */
  int	 message_digest_encoding;	/**< Message digest encoding. */
} KlsCmd;




/**	Message digest type: MD5.
*/
#define MD_TYPE_MD5		0

/**	Message digest type: SHA-1.
*/
#define MD_TYPE_SHA1		1

/**	Message digest type: SHA-224.
*/
#define MD_TYPE_SHA224		2

/**	Message digest type: SHA-256.
*/
#define MD_TYPE_SHA256		3

/**	Message digest type: SHA-384.
*/
#define MD_TYPE_SHA384		4

/**	Message digest type: SHA-512.
*/
#define MD_TYPE_SHA512		5

/**	Message digest type: RIPE-MD 160.
*/
#define MD_TYPE_RIPE160		6




/**	Message digest encoding: ASCII-Hex.
*/
#define MD_ENC_HEX		0

/**	Message digest encoding: ASCII-85.
*/
#define MD_ENC_ASCII85		1

/**	Message digest encoding: Reverse ASCII-85.
*/
#define MD_ENC_RA85		2



/**	Output column order.
*/
static char *allocated_print_list = NULL;

/**	Directory contents handling.
*/
static char *allocated_dc = NULL;



#if DK_HAVE_FEATURE_BACKSLASH
/**	File name separator.
*/
static char fn_sep[] = { "\\" };
#else
/**	File name separator.
*/
static char fn_sep[] = { "/" };
#endif



/**	Messages issued by the program, filled using string finder.
*/
static char *kls_str[62];



/**	String finder data.
*/
static dk_string_finder_t kls_mesgs[] = {
  { 
    "/m/00",
    &(kls_str[0]),
    "Can not traverse directory \""
  },
  { 
    "/m/01",
    &(kls_str[1]),
    "\"!"
  },
  { 
    "/m/02",
    &(kls_str[2]),
    "No information about file \""
  },
  { 
    "/m/03",
    &(kls_str[3]),
    "\" accessable!"
  },
  { 
    "/m/04",
    &(kls_str[4]),
    "Failed to estimate current working directory!"
  },
  { 
    "/m/05",
    &(kls_str[5]),
    "Not enough memory (RAM or swap space)!"
  },
  { 
    "/m/06",
    &(kls_str[6]),
    "Internal error!"
  },
  { 
    "/m/07",
    &(kls_str[7]),
    "File \""
  },
  { 
    "/m/08",
    &(kls_str[8]),
    "\" not found!"
  },
  { 
    "/m/09",
    &(kls_str[9]),
    "Directories:      "
  },
  { 
    "/m/10",
    &(kls_str[10]),
    "Files:            "
  },
  { 
    "/m/11",
    &(kls_str[11]),
    "Bytes in files:   "
  },
  { 
    "/m/12",
    &(kls_str[12]),
    "Bytes in summary: "
  },
  { 
    "/m/13",
    &(kls_str[13]),
    "Directory \""
  },
  { 
    "/m/14",
    &(kls_str[14]),
    "\" exceeds the maximum symlink depth!"
  },
  { 
    "/m/15",
    &(kls_str[15]),
    "Support for RSA Data Security, Inc. MD5 Message-Digest not available here!"
  },
  { 
    "/m/16",
    &(kls_str[16]),
    "Support for SHA-... message digests not available here!"
  },
  { 
    "/m/17",
    &(kls_str[17]),
    "information output order" 
  },
  { 
    "/m/18",
    &(kls_str[18]),
    "name" 
  },
  { 
    "/m/19",
    &(kls_str[19]),
    "size" 
  },
  { 
    "/m/20",
    &(kls_str[20]),
    "type" 
  },
  { 
    "/m/21",
    &(kls_str[21]),
    "permissions" 
  },
  { 
    "/m/22",
    &(kls_str[22]),
    "check sum" 
  },
  { 
    "/m/23",
    &(kls_str[23]),
    "link number" 
  },
  { 
    "/m/24",
    &(kls_str[24]),
    "creation" 
  },
  { 
    "/m/25",
    &(kls_str[25]),
    "modification" 
  },
  { 
    "/m/26",
    &(kls_str[26]),
    "access" 
  },
  { 
    "/m/27",
    &(kls_str[27]),
    "owner UID" 
  },
  { 
    "/m/28",
    &(kls_str[28]),
    "owner GID" 
  },
  { 
    "/m/29",
    &(kls_str[29]),
    "device number" 
  },
  { 
    "/m/30",
    &(kls_str[30]),
    "relative device number" 
  },
  { 
    "/m/31",
    &(kls_str[31]),
    "inode number" 
  },
  { 
    "/m/32",
    &(kls_str[32]),
    "message digest type" 
  },
  { 
    "/m/33",
    &(kls_str[33]),
    "traverse subdirectories" 
  },
  { 
    "/m/34",
    &(kls_str[34]),
    "file types" 
  },
  { 
    "/m/35",
    &(kls_str[35]),
    "regular files" 
  },
  { 
    "/m/36",
    &(kls_str[36]),
    "directories" 
  },
  { 
    "/m/37",
    &(kls_str[37]),
    "pipes" 
  },
  { 
    "/m/38",
    &(kls_str[38]),
    "byte specials" 
  },
  { 
    "/m/39",
    &(kls_str[39]),
    "block specials" 
  },
  { 
    "/m/40",
    &(kls_str[40]),
    "sockets" 
  },
  { 
    "/m/41",
    &(kls_str[41]),
    "others" 
  },
  { 
    "/m/42",
    &(kls_str[42]),
    "max symlink depth" 
  },
  { 
    "/m/43",
    &(kls_str[43]),
    "summary" 
  },
  { 
    "/m/44",
    &(kls_str[44]),
    "on" 
  },
  { 
    "/m/45",
    &(kls_str[45]),
    "off" 
  },
  { 
    "/m/46",
    &(kls_str[46]),
    "unlimited" 
  },
  { 
    "/m/47",
    &(kls_str[47]),
    "directory behaviour" 
  },
  { 
    "/m/48",
    &(kls_str[48]),
    "directory itself" 
  },
  { 
    "/m/49",
    &(kls_str[49]),
    "directory contents" 
  },
  { 
    "/m/50",
    &(kls_str[50]),
    "none" 
  },
  { 
    "/m/51",
    &(kls_str[51]),
    "Current configuration:" 
  },
  { 
    "/m/52",
    &(kls_str[52]),
    "stay on file system" 
  },
  { 
    "/m/53",
    &(kls_str[53]),
    "column width for file size" 
  },
  { 
    "/m/54",
    &(kls_str[54]),
    "auto" 
  },
  { 
    "/m/55",
    &(kls_str[55]),
    "message digest encoding" 
  },
  { 
    "/m/56",
    &(kls_str[56]),
    "Support for RIPEMD-... message digests not available here!" 
  },
  { 
    "/m/57",
    &(kls_str[57]),
    "Support for SHA-1 message digests not available here!" 
  },
  { 
    "/m/58",
    &(kls_str[58]),
    "Support for SHA-224 message digests not available here!" 
  },
  { 
    "/m/59",
    &(kls_str[59]),
    "Support for SHA-256 message digests not available here!" 
  },
  { 
    "/m/60",
    &(kls_str[60]),
    "Support for SHA-384 message digests not available here!" 
  },
  { 
    "/m/61",
    &(kls_str[61]),
    "Support for SHA-512 message digests not available here!" 
  },
  { NULL, NULL, NULL},
};



#if DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H || DK_HAVE_OPENSSL_RIPEMD_H



/**	OpenSSL library version.
*/
static char *openssl_lib_version[] = {
  "OpenSSL      cryptography toolkit implementing the Secure Sockets Layer",
  "             (SSL v2/v3) and Transport Layer Security (TLS v1) network",
  "             protocols and related cryptography standards required by them.",
  "             See http://www.openssl.org for more information.",
  "             The kls program uses the message digest functions provided",
  "             by the OpenSSL library to create checksums (message digests)",
  "             for files using the following algorithms:",
#if DK_HAVE_OPENSSL_MD5_H
  "             * MD5:",
  "               RSA Data Security, Inc. MD5 Message-Digest Algorithm",
  "               as published in RFC 1321 (see RFC 1321 for detailed",
  "               copyright and license information,",
#endif
#if DK_HAVE_OPENSSL_RIPEMD_H
  "             * RIPEMD-160:",
  "               Cryptographic hash function designed by Hans Dobbertin,",
  "               Antoon Bosselaers and Bart Preneel.",
#endif
#if DK_HAVE_OPENSSL_SHA_H
  "             * SHA-1"
#if DK_HAVE_SHA224
  " SHA-224"
#endif
#if DK_HAVE_SHA256
  " SHA-256"
#endif
#if DK_HAVE_SHA384
  " SHA-384"
#endif
#if DK_HAVE_SHA512
  " SHA-512"
#endif
  ":",
  "               SHA-... messages digests as published in the FIPS",
  "               publications.",
#endif
  NULL
};

#endif



/**	Flag: Error already printed.
*/
#if !(DK_HAVE_OPENSSL_MD5_H && DK_HAVE_OPENSSL_SHA_H && DK_HAVE_OPENSSL_RIPEMD_H && DK_HAVE_SHA224 && DK_HAVE_SHA256 && DK_HAVE_SHA384 && DK_HAVE_SHA512)
static int error_type_printed = 0;
#endif



#if DK_HAVE_LONG_LONG_INT
/**	Convert unsigned long long to string.
	Use MS specific format string when using MS C compiler.
	@param	buffer	Result buffer.
	@param	ull	Value to convert.
*/
static void
ull_to_string DK_P2(char *,buffer, long long unsigned, ull)
{
#if _MSC_VER >= 1100
  sprintf(buffer, "%I64u", ull);
#else
  sprintf(buffer, "%llu", ull);
#endif
}
#else
/**	Convert unsigned long to string.
	@param	buffer	Result buffer.
	@param	ull	Value to convert.
*/
static void
ull_to_string DK_P2(char *,buffer, long unsigned, ull)
{
  sprintf(buffer, "%lu", ull);
}
#endif



/**	Print size as unsigned long long or double value for size.
	@param	cmd	Kls job.
	@param	ull	Unsigned long long value.
	@param	d	Double value.
	@param	ma	Print double instead of ull.
*/
static
void
pr_ull_or_double DK_P4(KlsCmd *,cmd, dk_long_long_unsigned_t,ull, double,d, int,ma)
{
  char buffer[32], *stptr;
  size_t in_use = 0, field_length = 0, i = 0;
#if DK_HAVE_LONG_LONG_INT
  field_length = 21;
  
#else
  field_length = 11;
  
#endif
  if(ma) {
    sprintf(buffer, "%20.0lf", d);
    stptr = dkstr_start(buffer, NULL);
  } else {
    ull_to_string(buffer, ull);
    stptr = buffer;
  }
  if(cmd->size_output_width > 0) {
    field_length = cmd->size_output_width;
  }
  in_use = 0;
  if(stptr) { in_use = strlen(stptr); }
  if(ma)    { in_use++; }
  if((in_use <= field_length) && stptr) {
    if(cmd->size_output_width != -1) {
      for(i = in_use; i < field_length; i++) {
	fputc(' ', cmd->output_file);
      }
    }
    if(ma) {
      fputc('~', cmd->output_file);
    }
    fputs(stptr, cmd->output_file);
  } else {
    for(i = 0; i < field_length; i++) {
      fputc('~', cmd->output_file);
    }
  } 
}



/**	Initialize kls job.
	@param	cmd	Kls job.
*/
static
void
cmd_init DK_P1(KlsCmd *,cmd)
{
  cmd->remove_cwd_from_name = 0;
  cmd->remove_cwd_length = 0;
  cmd->math_error = 0;
  cmd->filenames = NULL;
  cmd->names_used = 0;
  cmd->symlinks_limited = 0;
  cmd->symlinks_allowed = 0UL;
  cmd->symlinks_configured = 0;
  cmd->mdtype_configured = 0;
  cmd->mdenc_configured = 0;
  cmd->output_file = NULL;
  cmd->print_list = NULL;
  cmd->rec = 0;
  cmd->rec_configured = 0;
  cmd->file_types = NULL;
  cmd->configure_only = 0;
  cmd->unconf_only = 0;
  cmd->show_summary = 0;
  cmd->summary_configured = 0;
  cmd->show_version = 0;
  cmd->show_help = 0;
  cmd->show_configuration = 0;
  cmd->bytes_found = 0.0;
  cmd->files_found = 0.0;
  cmd->dirs_found = 0.0;
  cmd->bytes_in_files = 0.0;
  cmd->ull_bytes_found = DK_ZERO_LONG_LONG_UNSIGNED;
  cmd->ull_bytes_in_files = DK_ZERO_LONG_LONG_UNSIGNED;
  cmd->ull_files_found = DK_ZERO_LONG_LONG_UNSIGNED;
  cmd->ull_dirs_found = DK_ZERO_LONG_LONG_UNSIGNED;
  cmd->me_ull_bytes_found = 0;
  cmd->me_ull_bytes_in_files = 0;
  cmd->me_ull_files_found = 0;
  cmd->me_ull_dirs_found = 0;
  cmd->ft_reg = 0;
  cmd->ft_dir = 0;
  cmd->ft_pipe = 0;
  cmd->ft_chr = 0;
  cmd->ft_blk = 0;
  cmd->ft_sock = 0;
  cmd->ft_oth = 0;
  cmd->md5_wanted = 0;
  cmd->sz_wanted = 0;
  cmd->math_border = dkma_ul_to_double(0xFFFFFFFFUL);
  cmd->dc_dir = 0; cmd->dc_contents = 0; cmd->dc_configured = 0;
  cmd->size_output_width = 0;
  cmd->size_output_configured = 0;
  cmd->stay_on_filesystem = 0;
  cmd->stay_configured = 0;
  cmd->message_digest_type = MD_TYPE_SHA1;
  cmd->message_digest_encoding = MD_ENC_HEX;
}



/**	Default column order.
*/
static char default_print_list[] = { "tn" };

/**	Default list of file types to show.
*/
static char default_file_types[] = { "fd" };



/**	Preference key for column order.
*/
static char pn_print_list[] = { "/print" };

/**	Preference key for recursive listing.
*/
static char pn_rec[] = { "/recursive" };

/**	Preference key to choose file types to show.
*/
static char pn_type[] = { "/type" };

/**	Preference key for writing summary information.
*/
static char pn_summary[] = { "/summary" };

/**	Preference key for symlink depth.
*/
static char pn_links[] = { "/links" };

/**	Preference key for width.
*/
static char pn_width[] = { "/width" };

/**	Preference key how to handle directories.
*/
static char pn_dir[] =  { "/directory" };

/**	Preference key for "stay-on-filesystem" flag.
*/
static char pn_stay[] = { "/stay-on-filesystem" };

/**	Preference key to choose message digest.
*/
static char pn_digest[] = { "/message-digest" };

/**	Preference key for message digest encoding.
*/
static char pn_mdenc[]  = { "/message-digest-encoding" };



/**	Preference value "on".
*/
static char pv_on[] = { "on" };

/**	Preference value "off".
*/
static char pv_off[] = { "off" };



/**	Preference value "unlimited" for symlink depth.
*/
static char pv_unlimited[] = { "unlimited" };



/**	Message digest types.
*/
static char *digest_types[] = {
  "MD5",
  "SHA-1",
  "SHA-224",
  "SHA-256",
  "SHA-384",
  "SHA-512",
  "RIPEMD-160",
  NULL
};



/**	Encodings for messages digests.
*/
static char *digest_encodings[] = {
  "HEX",
  "ASCII-85",
  "R-ASCII-85",
  NULL
};



/**	Application.
*/
static dk_app_t *app = NULL;



#ifndef MIN
/**	Minimum of two values.
*/
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif



/**	Retrieve settings from preferences.
	The settings from here are overwritten by command line options.
	@param	cmd	Kls job.
	@param	my_info	Address of pointer to column order string.
	@param	my_types	Address of pointer to file type string.
*/
static
void
complete_from_prefs DK_P3(KlsCmd *,cmd, char **,my_info, char **,my_types)
{
  char line[256], *cptr;
  unsigned long x;
  if(!(cmd->mdtype_configured)) {
    if(dkapp_get_pref(app, pn_digest, line, sizeof(line), 0)) {
      cmd->message_digest_type = dkstr_array_index(
	digest_types, line, 0
      );
      if(cmd->message_digest_type < 0) {
	cmd->message_digest_type = MD_TYPE_SHA1;
      }
    }
  }
  if(!(cmd->mdenc_configured)) {
    if(dkapp_get_pref(app, pn_mdenc, line, sizeof(line), 0)) {
      cmd->message_digest_encoding = dkstr_array_index(
        digest_encodings, line, 0
      );
      if(cmd->message_digest_encoding < 0) {
        cmd->message_digest_encoding = MD_ENC_HEX;
      }
    }
  }
  if(!(cmd->print_list)) {
    if(dkapp_get_pref(app, pn_print_list, line, sizeof(line), 0)) {
      cptr = dkstr_dup(line);
      if(cptr) {
	*my_info = cptr;
	cmd->print_list = cptr;
      }
    }
  }
  if(!(cmd->file_types)) {
    if(dkapp_get_pref(app, pn_type, line, sizeof(line), 0)) {
      cptr = dkstr_dup(line);
      if(cptr) {
	*my_types = cptr;
	cmd->file_types = cptr;
      }
    }
  }
  if(!(cmd->rec_configured)) {
    if(dkapp_get_pref(app, pn_rec, line, sizeof(line), 0)) {
      if(dkstr_is_on(line)) {
	cmd->rec = 1;
      } else {
	cmd->rec = 0;
      }
      cmd->rec_configured = 1;
    }
  }
  if(!(cmd->symlinks_configured)) {
    if(dkapp_get_pref(app, pn_links, line, sizeof(line), 0)) {
      cmd->symlinks_configured = 1;
      if(sscanf(line, "%lu", &x) == 1) {
	cmd->symlinks_limited = 1;
	cmd->symlinks_allowed = x;
      } else {
	cmd->symlinks_limited = 0;
      }
    }
  }
  if(!(cmd->size_output_configured)) {
    if(dkapp_get_pref(app, pn_width, line, sizeof(line), 0)) {
      int i;
      if(sscanf(line, "%d", &i) == 1) {
        if(i < -1) i = -1;
	if(i > 20) i = 20;
	cmd->size_output_width = i;
	cmd->size_output_configured = 1;
      }
    }
  }
  if(!(cmd->summary_configured)) {
    if(dkapp_get_pref(app, pn_summary, line, sizeof(line), 0)) {
      cmd->summary_configured = 1;
      if(dkstr_is_on(line)) {
	cmd->show_summary = 1;
      } else {
	cmd->show_summary = 0;
      }
    }
  }
  if(!(cmd->dc_configured)) {
    if(dkapp_get_pref(app, pn_dir, line, sizeof(line), 0)) {
      cmd->dc_configured= 1;
      cptr = line;
      while(*cptr) {
	switch(*cptr) {
	  case 'd' : cmd->dc_dir = 1; break;
	  case 'c' : cmd->dc_contents = 1; break;
	}
	cptr++;
      }
    }
  }
  if(!(cmd->stay_configured)) {
    if(dkapp_get_pref(app, pn_stay, line, sizeof(line), 0)) {
      cmd->stay_configured = 1;
      cmd->stay_on_filesystem = dkstr_is_on(line);
    }
  }
}



/**	Version number string.
*/
static char the_version_number[] = { VERSNUMB } ;



/**	License terms.
*/
static char *license_terms[] = {
"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 copyright 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 other 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.",
#if (DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H || DK_HAVE_OPENSSL_RIPEMD_H)
  "",
  "This program uses the OpenSSL library.",
  "This product includes cryptographic software written by",
  "Eric Young (eay@cryptsoft.com).",
#endif
NULL
};



/**	Print version number.
*/
static
void
print_version DK_P0()
{
  char **ptr;
  printf("\n");
  printf(
    "kls (part of the dktools collection, version %s)\n",
    the_version_number
  );
  printf("Copyright (C) 2002-2010 Dipl.-Ing. D. Krause\n");
  printf("http://dktools.sourceforge.net/\n");
  printf(
    "File sizes reported by the OS use %lu bits\n",
    (8UL * dksf_filesize_bytes())
  );
  printf(
    "File size calculations in kls use %lu bits\n",
    (8UL * dksf_long_long_bytes())
  );
  printf("Message digest types: MD5 RIPEMD-160 SHA-1");
#if DK_HAVE_SHA224
  printf(" SHA-224");
#endif
#if DK_HAVE_SHA256
  printf(" SHA-256");
#endif
#if DK_HAVE_SHA384
  printf(" SHA-384");
#endif
#if DK_HAVE_SHA512
  printf(" SHA-512");
#endif
  printf("\n\n");
  ptr = license_terms;
  while(*ptr) {
    fputs(*(ptr++), stdout); fputc('\n', stdout);
  }
  printf("\nLibraries used:\n\n");
  ptr = dklic_get();
  while(*ptr) {
    fputs(*(ptr++), stdout); fputc('\n', stdout);
  }
  fputc('\n', stdout);
}



/**	Default help text, printed if help text file is not found.
*/
static char *help_text[] = {
"kls - ls-replacement",
"====================",
"",
"Usage:",
"------",
"",
"kls [-o <outputfile>] <options> [<file(s)>]",
"",
"  lists the selected files as specified in the options. Command line",
"  options overwrite permanent options.",
"  Option -o redirects output to the given file.",
"",
"kls -c <options>",
"  ",
"  establishes the given options as permanent options.",
"",
"kls -u",
"",
"  removes all permanent options.",
"",
"kls -C",
"",
"  shows all permanent options.",
"",
"Options:",
"---------",
"",
"-p <info>        --print=<info>",
"                 issues the following information for each file",
"                 n ... file name",
"                 s ... file size in bytes",
"                 t ... file type",
"                 p ... permissions",
"                 x ... check sum",
"                 l ... link number",
"                 c ... creation timestamp",
"                 m ... last modification timestamp",
"                 a ... last access timestamp",
"                 u ... owner UID",
"                 g ... owner GID",
"                 d ... device number",
"                 r ... relative device number",
"                 i ... inode number",
"-m <mdtype>      --message-digest=<mdtype>",
"                 chooses a message digest type",
"   MD5           RSA Data Security, Inc. MD5 message-digest",
"   SHA-1         SHA-1 message-digest",
"   SHA-224	  SHA-224 message digest",
"   SHA-256	  SHA-256 message digest",
"   SHA-384	  SHA-384 message digest",
"   SHA-512	  SHA-512 message digest",
"   RIPEMD-160	  RIPEMD-160 message digest",
"-e <encoding>    --encoding=<encoding>",
"		  chooses the encoding of the message digest",
"   HEX		  hexadecimal encoding (default)",
"   ASCII-85	  ASCII-85 encoding",
"   R-ASCII-85	  reverse ASCII-85 encoding",
"-r               --recursive",
"                 traverses subdirectories too",
"-t <filetypes>   --type=<filetypes>",
"                 restricts files to inspect to a set of given types:",
"                 f ... regular files",
"                 d ... directories",
"                 p ... pipes",
"                 c ... character special devices",
"                 b ... block special devices",
"                 s ... sockets",
"                 o ... all other files",
"-l <symlinks>    --links=<symlinks>",
"                 restricts the depth of symbolic links to follow",
"		 (specify a number or \"unlimited\")",
"-f               --stay-on-filesystem",
"                 stops at filesystem borders",
"-s               --summary",
"                 gives a summary before quitting.",
"-d <how>         specifies how to handle directory names from the",
"                 command line. If the string contains a \"d\" the",
"		 program issues information about the directory",
"		 itself, if it contains a \"c\" it lists the contents.",
"		 One can combine \"d\" and \"c\".",
"",
"kls combines features from both ls and du. Output is configurable,",
"you can specify the order of columns.",
"",
"The program uses the dkapp library and it's preferences mechanism",
"to provide permanent options.",
"",
"The following preferences are recognized:",
"",
"/directory  a string telling how to handle directory names given on",
"            the command line. See the \"-d\" option.",
"",
"/recursive  is a boolean flag specifying whether or not to traverse",
"            subdirectories. See \"-r\".",
"",
"/links      is a string specifying the maximum depth of",
"            symbolic links to follow. See \"-l\".",
"",
"/print      specifies the order of output columns. See \"-p\".",
"",
"/summary    chooses whether or not to print a summary. See \"-s\".",
"",
"/type       selects the file types to inspect. See \"-t\".",
"",
"One can set permanent options using \"-c\", i.e. when running",
"",
"  kls -c -ptsn -ddc -s",
"",
"you get no output, the options \"-ptsn -ddc -s\" are stored as",
"permanent options instead.",
"If you run",
"",
"  kls <directory>",
"",
"the program behaves as if the options \"-ptsn -ddc -s\" were specified.",
"",
"To remove all permanent options, use",
"",
"  kls -u",
"",
  NULL
};



/**	Directory entry.
*/
struct _kls_dir_entry_ {
  struct _kls_dir_entry_ *prev;		/**< Parent entry. */
  dk_dir_t               *dir;		/**< Directory traversal. */
  dk_stat_t              *st;		/**< Information about entry. */
  char                   *name;		/**< File/directory name. */
  double bytes_found;			/**< Size (including all contents). */
  dk_long_long_unsigned_t ull_bytes_found;	/**< Size as unsigned long. */
  int                     ull_math_error;	/**< Flag: Numeric overflow. */
  unsigned long ll;				/**< Symlink level. */
  /* ---- for traversal ---- */
  dk_stat_t              *testst;	/**< Information about entry. */
  char                   *fn;		/**< Entry name. */
  char                   *sn;		/**< Short entry name. */
  int                    ft;		/**< Entry type. */
};

/**	Directory entry.
*/
typedef struct _kls_dir_entry_ DirEntry;

/**	Create new structure for directory entry.
	@param	name	Directory entry name.
	@param	parent	Entry for the parent directory.
	@return	Pointer to new entry on success, NULL on error.
*/
static
DirEntry *
dir_entry_new DK_P2(char *,name, DirEntry *,parent)
{
  DirEntry  *back = NULL;
  dk_dir_t  *newdir;
  dk_stat_t *newstat;
  char      *newname;
  back = dk_new(DirEntry,1);
  newdir = dkdir_open(name);
  newstat = dkstat_open(name);
  newname = dkstr_dup(name);
  if(back && newdir && newstat && newname) {
    back->prev = parent; back->dir = newdir;
    back->st = newstat; back->name = newname;
    /* back->bytes_found = dkma_ull_to_double(dkstat_size(newstat)); */
    back->bytes_found = 0.0;
    back->ull_bytes_found = DK_ZERO_LONG_LONG_UNSIGNED;
    back->ull_math_error = 0;
    back->ll = 0UL;
    if(parent) {
      back->ll = parent->ll;
    }
  } else {
    if(back) { dk_delete(back); back = NULL; }
    if(newdir) { dkdir_close(newdir); }
    if(newstat) { dkstat_close(newstat); }
    if(newname) { dk_delete(newname); }
    back = NULL;
  }
  return back;
}



/**	Convert file type to character.
	@param	ft	File type.
	@return	Character representing the file type in listing.
*/
static
char
ft_to_char DK_P1(int, ft)
{
  char back;
  back = '-';
  if(ft & DK_FT_SYMLINK) {
    switch(ft & (~(DK_FT_SYMLINK))) {
      case DK_FT_REG : back = 'l' ; break;
      case DK_FT_DIR : back = 'D' ; break;
      case DK_FT_FIFO : back = 'P' ; break;
      case DK_FT_CHR : back = 'C' ; break;
      case DK_FT_BLK : back = 'B' ; break;
      case DK_FT_SOCKET : back = 'S' ; break;
      default : back = 'O' ; break;
    }
  } else {
    switch(ft) {
      case DK_FT_REG : back = '-' ; break;
      case DK_FT_DIR : back = 'd' ; break;
      case DK_FT_FIFO : back = 'p' ; break;
      case DK_FT_CHR : back = 'c' ; break;
      case DK_FT_BLK : back = 'b' ; break;
      case DK_FT_SOCKET : back = 's' ; break;
      default : back = 'o' ; break;
    }
  }
  return back;
}



/**	Check whether or not we need to traverse the subdirectory.
	@param	cmd	Kls job.
	@param	de	Directory entry.
	@param	st	Information about the directory entry.
	@return	1 for traverse, 0 for no traverse.
*/
static
int
must_go_sub DK_P3(KlsCmd *,cmd, DirEntry *,de, dk_stat_t *,st)
{
  int back = 0;
  int rec;
  char *logmsg[3];
#if !DK_HAVE_NO_INODES
  DirEntry *current;
  unsigned long st_device, st_inode, current_device, current_inode;
#endif
  
  if(de) {
    
    
    if((cmd->rec) || (cmd->sz_wanted)) {
      back = 1;
    }
  } else {
    
    if((cmd->dc_contents) || (cmd->rec) || (cmd->sz_wanted)) {
      back = 1;
    }
  }
  rec = 0;
  if(back) {
    if(dkstat_filetype(st) & DK_FT_SYMLINK) {
      
      if(cmd->symlinks_limited) {
	back = 0;
	
	if(de) {
	  
          if((de->ll) < (cmd->symlinks_allowed)) {
	    back = 1;
	  }
	} else {
	  if(cmd->symlinks_allowed) {
	    back = 1;
	  }
	}
      } else {
	
      }
#if !DK_HAVE_NO_INODES
      if(back) {
	/* Rekursivitaets-Test */
	st_device = dkstat_device(st);
	st_inode  = dkstat_inode(st);
	current = de;
	while(current && back) {
	  current_inode  = dkstat_inode(current->st);
	  if((current_inode == st_inode) && current_inode) {
	    current_device = dkstat_device(current->st);
	    if(current_device == st_device) {
	      back = 0; rec = 1;
	      
	    }
	  }
	  current = current->prev;
	}
      }
#endif
    }
    if(!back) {
      if(!rec) {
	logmsg[0] = kls_str[13];
	logmsg[1] = dkdir_get_fullname(de->dir);
	logmsg[2] = kls_str[14];
	dkapp_log_msg(app, DK_LOG_LEVEL_WARNING, logmsg, 3);
      }
    }
  }
  if(back && (cmd->stay_on_filesystem)) {
    
    if(de && st) { 
      if(dkstat_device(de->st) != dkstat_device(st)) {
        
	back = 0; 
      }
    }
  }
  
  return back;
}



/**	Check directory depth.
	Printing is restricted to a directory depth.
	@param	cmd	Kls job.
	@param	de	Current directory entry.
	@param	st	Information about the directory entry.
	@param	name	File/directory name.
	@return	1 on success, 0 on error.
*/
static
int
can_print DK_P4(KlsCmd *,cmd, DirEntry *,de, dk_stat_t *,st, char *,name)
{
  int back = 1;
  if(cmd->remove_cwd_from_name) {
    if(cmd->remove_cwd_length >= 0) {
    if(strlen(name) <= (size_t)(cmd->remove_cwd_length)) {
      back = 0;
    }
    }
  }
  return back;
}



#ifdef NEED_LOGMSG
#undef NEED_LOGMSG
#endif
/**	Compile condition: Variable logmsg is needed.
*/
#define NEED_LOGMSG	1

#if DK_HAVE_OPENSSL_MD5_H
#if DK_HAVE_OPENSSL_SHA_H
#if DK_HAVE_OPENSSL_RIPEMD_H
#if DK_HAVE_SHA224
#if DK_HAVE_SHA256
#if DK_HAVE_SHA384
#if DK_HAVE_SHA512
#undef NEED_LOGMSG
#endif
#endif
#endif
#endif
#endif
#endif
#endif



/**	Create digest (file checksum).
	@param	cmd	Kls job.
	@param	b	Result buffer.
	@param	sz	Size of \a b in bytes.
	@param	de	Directory entry.
	@param	st	Information about the directory entry.
	@param	name	File/directory name.
*/
static
void
create_digest DK_P6(KlsCmd *,cmd, char *,b, size_t,sz, DirEntry *,de, dk_stat_t *,st, char *,name)
{
#if NEED_LOGMSG
  char *logmsg[16];
#endif
#if (DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H || DK_HAVE_OPENSSL_RIPEMD_H)
  unsigned char	buffer[512];	/**< Binary message digest. */
  size_t	u_buffer = 0;	/**< Length of binary message digest. */
  FILE		*f = NULL;	/**< File to read. */
  unsigned char	not_regular;	/**< Flag: not a regular file. */
  int		bytes_read;	/**< Number of bytes read from file. */
  size_t	i;		/**< Used in loop to set '-'. */
#endif
#if DK_HAVE_OPENSSL_MD5_H
  MD5_CTX	md5_ctx;
#endif
#if DK_HAVE_OPENSSL_SHA_H
  SHA_CTX	sha_ctx;
#if DK_HAVE_SHA256 || DK_HAVE_SHA224
  SHA256_CTX	sha256_ctx;
#endif
#if DK_HAVE_SHA512 || DK_HAVE_SHA384
  SHA512_CTX	sha512_ctx;
#endif
#endif
#if DK_HAVE_OPENSSL_RIPEMD_H
  RIPEMD160_CTX	ripemd160_ctx;
#endif
  
  *b = '\0';
#if (DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H || DK_HAVE_OPENSSL_RIPEMD_H) 
  u_buffer = 0; not_regular = 0x00;
  switch(cmd->message_digest_type) {
    case MD_TYPE_MD5: {		/* 16 bytes */
    
#if DK_HAVE_OPENSSL_MD5_H
      
      if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
        
        f = dkapp_fopen(app, name, "rb");
        if(f) {				
          MD5_Init(&md5_ctx);
	  do {
	    bytes_read = (int)fread(buffer,1,sizeof(buffer),f);
	    if(bytes_read > 0) {
	      MD5_Update(&md5_ctx, buffer, (size_t)bytes_read);
	    }
	  } while(bytes_read > 0);
	  MD5_Final(buffer, &md5_ctx);
	  u_buffer = 16;
          fclose(f);
        } else {			
	  dkapp_err_fopenr(app, name);
        }
      } else {				
        not_regular = 0x01;
      }
#else
      if(!(error_type_printed & 1)) {
        error_type_printed |= 1;
	logmsg[0] = kls_str[15];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
#endif
    } break;
    case MD_TYPE_SHA1: {	/* 20 bytes */
#if DK_HAVE_OPENSSL_SHA_H
      
      if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
        
        f = dkapp_fopen(app, name, "rb");
        if(f) {				
          SHA1_Init(&sha_ctx);
	  do {
	    bytes_read = (int)fread(buffer,1,sizeof(buffer),f);
	    if(bytes_read > 0) {
	      SHA1_Update(&sha_ctx, buffer, (size_t)bytes_read);
	    }
	  } while(bytes_read > 0);
	  SHA1_Final(buffer, &sha_ctx);
	  u_buffer = 20;
          fclose(f);
        } else {			
	  dkapp_err_fopenr(app, name);
        }
      } else {				
        not_regular = 0x01;
      }
#else
      if(!(error_type_printed & 2)) {
        error_type_printed |= 2;
	logmsg[0] = kls_str[57];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
#endif
    } break;
    case MD_TYPE_SHA224: {	/* 28 bytes */
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA224
      
      if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
        
        f = dkapp_fopen(app, name, "rb");
        if(f) {				
          SHA224_Init(&sha256_ctx);
	  do {
	    bytes_read = (int)fread(buffer,1,sizeof(buffer),f);
	    if(bytes_read > 0) {
	      SHA224_Update(&sha256_ctx, buffer, (size_t)bytes_read);
	    }
	  } while(bytes_read > 0);
	  SHA224_Final(buffer, &sha256_ctx);
	  u_buffer = 28;
          fclose(f);
        } else {			
	  dkapp_err_fopenr(app, name);
        }
      } else {				
        not_regular = 0x01;
      }
#else
      if(!(error_type_printed & 2)) {
        error_type_printed |= 2;
	logmsg[0] = kls_str[58];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
#endif
    } break;
    case MD_TYPE_SHA256: {	/* 32 bytes */
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA256
      
      if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
        
        f = dkapp_fopen(app, name, "rb");
        if(f) {				
          SHA256_Init(&sha256_ctx);
	  do {
	    bytes_read = (int)fread(buffer,1,sizeof(buffer),f);
	    if(bytes_read > 0) {
	      SHA256_Update(&sha256_ctx, buffer, (size_t)bytes_read);
	    }
	  } while(bytes_read > 0);
	  SHA256_Final(buffer, &sha256_ctx);
	  u_buffer = 32;
          fclose(f);
        } else {			
	  dkapp_err_fopenr(app, name);
        }
      } else {				
        not_regular = 0x01;
      }
#else
      if(!(error_type_printed & 2)) {
        error_type_printed |= 2;
	logmsg[0] = kls_str[59];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
#endif
    } break;
    case MD_TYPE_SHA384: {	/* 48 bytes */
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA384
      
      if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
        
        f = dkapp_fopen(app, name, "rb");
        if(f) {				
          SHA384_Init(&sha512_ctx);
	  do {
	    bytes_read = (int)fread(buffer,1,sizeof(buffer),f);
	    if(bytes_read > 0) {
	      SHA384_Update(&sha512_ctx, buffer, (size_t)bytes_read);
	    }
	  } while(bytes_read > 0);
	  SHA384_Final(buffer, &sha512_ctx);
	  u_buffer = 48;
          fclose(f);
        } else {			
	  dkapp_err_fopenr(app, name);
        }
      } else {				
        not_regular = 0x01;
      }
#else
      if(!(error_type_printed & 2)) {
        error_type_printed |= 2;
	logmsg[0] = kls_str[60];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
#endif
    } break;
    case MD_TYPE_SHA512: {	/* 64 bytes */
#if DK_HAVE_OPENSSL_SHA_H && DK_HAVE_SHA512
      
      if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
        
        f = dkapp_fopen(app, name, "rb");
        if(f) {				
          SHA512_Init(&sha512_ctx);
	  do {
	    bytes_read = (int)fread(buffer,1,sizeof(buffer),f);
	    if(bytes_read > 0) {
	      SHA512_Update(&sha512_ctx, buffer, (size_t)bytes_read);
	    }
	  } while(bytes_read > 0);
	  SHA512_Final(buffer, &sha512_ctx);
	  u_buffer = 64;
          fclose(f);
        } else {			
	  dkapp_err_fopenr(app, name);
        }
      } else {				
        not_regular = 0x01;
      }
#else
      if(!(error_type_printed & 2)) {
        error_type_printed |= 2;
	logmsg[0] = kls_str[61];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
#endif
    } break;
    case MD_TYPE_RIPE160: {	/* 20 bytes */
#if DK_HAVE_OPENSSL_RIPEMD_H
      
      if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_REG) {
        
        f = dkapp_fopen(app, name, "rb");
        if(f) {				
          RIPEMD160_Init(&ripemd160_ctx);
	  do {
	    bytes_read = (int)fread(buffer,1,sizeof(buffer),f);
	    if(bytes_read > 0) {
	      RIPEMD160_Update(&ripemd160_ctx, buffer, (size_t)bytes_read);
	    }
	  } while(bytes_read > 0);
	  RIPEMD160_Final(buffer, &ripemd160_ctx);
	  u_buffer = 20;
          fclose(f);
        } else {			
	  dkapp_err_fopenr(app, name);
        }
      } else {				
        not_regular = 0x01;
      }
#else
      if(!(error_type_printed & 4)) {
        error_type_printed |= 4;
	logmsg[0] = kls_str[56];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
#endif
    } break;
  }
  if(u_buffer) {		
    switch(cmd->message_digest_encoding) {
      case MD_ENC_ASCII85: {	
        dkenc_bin_to_a85(b, sz, (char *)buffer, u_buffer);
      } break;
      case MD_ENC_RA85: {	
        dkenc_bin_to_ra85(b, sz, (char *)buffer, u_buffer);
      } break;
      default: {		
        dkenc_bin_to_hex(b, sz, (char *)buffer, u_buffer);
      } break;
    }
  } else {			
    if(not_regular) {
      if(not_regular == 0x01) {
        u_buffer = 0;
        switch(cmd->message_digest_type) {
          case MD_TYPE_MD5: {		/* 16 bytes */
#if DK_HAVE_OPENSSL_MD5_H
	    u_buffer = 16;
#endif
          } break;
          case MD_TYPE_SHA1: {	/* 20 bytes */
#if DK_HAVE_OPENSSL_SHA_H
	    u_buffer = 20;
#endif
          } break;
          case MD_TYPE_SHA224: {	/* 28 bytes */
#if DK_HAVE_OPENSSL_SHA_H
	    u_buffer = 28;
#endif
          } break;
          case MD_TYPE_SHA256: {	/* 32 bytes */
#if DK_HAVE_OPENSSL_SHA_H
	    u_buffer = 32;
#endif
          } break;
          case MD_TYPE_SHA384: {	/* 48 bytes */
#if DK_HAVE_OPENSSL_SHA_H
	    u_buffer = 48;
#endif
          } break;
          case MD_TYPE_SHA512: {	/* 64 bytes */
#if DK_HAVE_OPENSSL_SHA_H
	    u_buffer = 64;
#endif
          } break;
          case MD_TYPE_RIPE160: {	/* 20 bytes */
#if DK_HAVE_OPENSSL_RIPEMD_H
	    u_buffer = 20;
#endif
          } break;
        }
	if(u_buffer) {		
	  switch(cmd->message_digest_encoding) {
	    case MD_ENC_ASCII85: case MD_ENC_RA85: {
	      u_buffer = dkenc_size_bin_to_a85(u_buffer);
	      for(i = 0; i < u_buffer; i++) b[i] = ' ';
	      b[u_buffer - 1] = '\0';
	    } break;
	    default: {
	      u_buffer = dkenc_size_bin_to_hex(u_buffer);
	      for(i = 0; i < u_buffer; i++) b[i] = '-';
	      b[u_buffer - 1] = '\0';
	    } break;
	  }
	}
      }
    }
  }
#endif
  
}



/**	Print one directory entry.
	@param	cmd	Kls job.
	@param	de	Directory entry.
	@param	st	Information about the directory entry.
	@param	name	File/directory name.
*/
static
void
print_entry DK_P4(KlsCmd *,cmd, DirEntry *,de, dk_stat_t *,st, char *,name)
{
  char *cptr;
  int  is_first, last_was_type, perms, xul_err, lgt;
  dk_long_long_unsigned_t xul;

#if (DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H || DK_HAVE_OPENSSL_RIPEMD_H)
  char	digbuf[256];
  /* int	i; */
#endif

  
  if(can_print(cmd,de,st,name)) {
  
  is_first = 1; last_was_type = 0;
#if (DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H)
  digbuf[0] = '\0';
  if(cmd->md5_wanted) {	
    create_digest(cmd, digbuf, sizeof(digbuf), de, st, name);
  } else {	
  }
#endif
  if(cmd->print_list) {
    cptr = cmd->print_list;
    while(*cptr) {
      if(!is_first) {
	if((!last_was_type) || (!(*cptr == 'p'))) {
	  fputc(' ', cmd->output_file);
	}
      }
      switch(*cptr) {
	case 'n': {
	  if(cmd->remove_cwd_from_name) {
	    lgt = strlen(name);
	    if(lgt > (cmd->remove_cwd_length)) {
	      fputs(&(name[cmd->remove_cwd_length]), cmd->output_file);
	    }
	  } else {
	    fputs(name, cmd->output_file);
	  }
	} break;
#if (DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H)
	case 'x': {
	  /* fputs(digest, cmd->output_file); */
	  fputs(digbuf, cmd->output_file);
	} break;
#endif
	case 's': {
	  xul_err = 0;
	  if(de) {
	    pr_ull_or_double(
	      cmd,
	      de->ull_bytes_found,
	      de->bytes_found,
	      de->ull_math_error
	    );
	  } else {
	    xul_err = 0;
	    xul = dkstat_size_ok(st, &xul_err);
	    pr_ull_or_double(cmd, xul, dkma_ull_to_double(xul), xul_err);
	  }
	} break;
	case 't': {
	  fputc(ft_to_char(dkstat_filetype(st)), cmd->output_file);
	} break;
	case 'p': {
	  perms = dkstat_permissions(st);
	  fputc(((perms & DK_PERM_U_READ) ? 'r' : '-'), cmd->output_file);
	  fputc(((perms & DK_PERM_U_WRITE)?'w':'-'), cmd->output_file);
	  if(perms & DK_PERM_SUID) {
    fputc(((perms & DK_PERM_U_EXECUTE) ? 's' : 'S'), cmd->output_file);
	  } else {
    fputc(((perms & DK_PERM_U_EXECUTE) ? 'x' : '-'), cmd->output_file);
	  }
    fputc(((perms & DK_PERM_G_READ) ? 'r' : '-'), cmd->output_file);
    fputc(((perms & DK_PERM_G_WRITE) ? 'w' : '-'),cmd->output_file);
	  if(perms & DK_PERM_SGID) {
	    if((dkstat_filetype(st) & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
	      fputc('l', cmd->output_file);
	    } else {
    fputc(((perms & DK_PERM_G_EXECUTE) ? 's' : 'S'), cmd->output_file);
	    }
	  } else {
    fputc(((perms & DK_PERM_G_EXECUTE) ? 'x' : '-'), cmd->output_file);
	  }
    fputc(((perms & DK_PERM_O_READ) ? 'r' : '-'), cmd->output_file);
    fputc(((perms & DK_PERM_O_WRITE) ? 'w' : '-'), cmd->output_file);
	  if(perms & DK_PERM_VTX) {
    fputc(((perms & DK_PERM_O_EXECUTE) ? 't' : 'T'), cmd->output_file);
	  } else {
    fputc(((perms & DK_PERM_O_EXECUTE) ? 'x' : '-'), cmd->output_file);
	  }
	} break;
	case 'l': {
	  if(cmd->size_output_width != -1) {
	    fprintf(cmd->output_file, "%10lu", dkstat_nlinks(st));
	  } else {
	    fprintf(cmd->output_file, "%lu", dkstat_nlinks(st));
	  }
	} break;
	case 'c': {
	  fputs(dkstat_ctime(st), cmd->output_file);
	} break;
	case 'm': {
	  fputs(dkstat_mtime(st), cmd->output_file);
	} break;
	case 'a': {
	  fputs(dkstat_atime(st), cmd->output_file);
	} break;
	case 'u': {
	  if(cmd->size_output_width != -1) {
	    fprintf(cmd->output_file, "%10lu", dkstat_uid(st));
	  } else {
	    fprintf(cmd->output_file, "%lu", dkstat_uid(st));
	  }
	} break;
	case 'g': {
	  if(cmd->size_output_width != -1) {
	    fprintf(cmd->output_file, "%10lu", dkstat_gid(st));
	  } else {
	    fprintf(cmd->output_file, "%lu", dkstat_gid(st));
	  }
	} break;
	case 'd': {
	  if(cmd->size_output_width != -1) {
	    fprintf(cmd->output_file, "%10lu", dkstat_device(st));
	  } else {
	    fprintf(cmd->output_file, "%lu", dkstat_device(st));
	  }
	} break;
	case 'r': {
	  if(cmd->size_output_width != -1) {
	    fprintf(cmd->output_file, "%10lu", dkstat_rdevice(st));
	  } else {
	    fprintf(cmd->output_file, "%lu", dkstat_rdevice(st));
	  }
	} break;
	case 'i': {
	  if(cmd->size_output_width != -1) {
	    fprintf(cmd->output_file, "%10lu", dkstat_inode(st));
	  } else {
	    fprintf(cmd->output_file, "%lu", dkstat_inode(st));
	  }
	} break;
      }
      last_was_type = ((*cptr == 't') ? 1 : 0);
      is_first = 0;
      cptr++;
    }
    fputc('\n', cmd->output_file);
  } else {
    fputs(name, cmd->output_file);
    fputc('\n', cmd->output_file);
  }
  } else {	
  }
  
}



/**	Output one entry (check with required file types before printing).
	@param	cmd	Kls job.
	@param	de	Directory entry.
	@param	st	Information about the directory entry.
	@param	name	Directory entry name.
*/
static
void
output_for_entry DK_P4(KlsCmd *,cmd, DirEntry *,de, dk_stat_t *,st, char *,name)
{
  int ok;
  ok = 0;
  switch(dkstat_filetype(st) & (~(DK_FT_SYMLINK))) {
    case DK_FT_REG : {
      if(cmd->ft_reg) ok = 1;
    } break;
    case DK_FT_DIR : {
      if(cmd->ft_dir) ok = 1;
    } break;
    case DK_FT_FIFO : {
      if(cmd->ft_pipe) ok = 1;
    } break;
    case DK_FT_CHR : {
      if(cmd->ft_chr) ok = 1;
    } break;
    case DK_FT_BLK : {
      if(cmd->ft_blk) ok = 1;
    } break;
    case DK_FT_SOCKET : {
      if(cmd->ft_sock) ok = 1;
    } break;
    default : {
      if(cmd->ft_oth) ok = 1;
    } break;
  }
  if(ok) {
    print_entry(cmd,de,st,name);
  }
}



/**	Check with directory depth, print if necessary.
	@param	cmd	Kls job.
	@param	current	Current directory entry.
	@param	dir	Flag: current entry is directory.
	@return	1 on success, 0 on error.
*/
static
int
output_check DK_P3(KlsCmd *,cmd, DirEntry *,current, int,dir)
{
  int back = 0;
  
  if(cmd->rec) {
    back = 1;
  } else {
    if(current->prev) {
      if(!((current->prev)->prev)) {
	if(dir) { /* dir  aaa/bbb */
	  if(cmd->dc_contents) {
	    back = 1;
	  } else {
	    if(!(cmd->dc_dir)) {
	      back = 1;
	    }
	  }
	} else {  /* file aaa/bbb/ccc */
	}
      }
    } else {
      if(dir) {   /* dir  aaa */
	if(cmd->dc_dir) {
	  back = 1;
	} else {
	  if(!(cmd->dc_contents)) {
	    back = 1;
	  }
	}
      } else {    /* file aaa/bbb */
	if(cmd->dc_contents) {
	  back = 1;
	} else {
	  if(!(cmd->dc_dir)) {
	    back = 1;
	  }
	}
      }
    }
  } 
  return back;
}



/**	Run for a current directory .
	@param	cmd	Kls job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_current DK_P1(KlsCmd *,cmd)
{
  int back = 0, ft, math_err, old_err, dnt;
  DirEntry *current, *next;
  dk_stat_t *st;
  char *cptr, *sn;
  char *logmsgs[8];
  dk_long_long_unsigned_t xul;
  
  current = next = NULL;
  sn = dkstr_rchr(cmd->current_name, fn_sep[0]);
  if(!sn) sn = cmd->current_name;
  st = dkstat_open(cmd->current_name);
  if(st) {
    back = 1;
    ft = dkstat_filetype(st);
    if(cmd->show_summary) {
      double x;
      math_err = 0;
      switch((ft) & (~(DK_FT_SYMLINK))) {
	case DK_FT_REG: {
	  xul = dkstat_size_ok(st, &math_err);
	  old_err = cmd->me_ull_bytes_found;
	  if(math_err) { cmd->me_ull_bytes_found = math_err; }
	  x = dkma_ull_to_double(xul);
	  cmd->bytes_found = dkma_add_double_ok(
	    cmd->bytes_found,
	    x,
	    &math_err
	  );
	  cmd->ull_bytes_found = dkma_add_ull_ok(
	    cmd->ull_bytes_found,
	    xul,
	    &(cmd->me_ull_bytes_found)
	  );
	  if((cmd->me_ull_bytes_found) && (!old_err)) {
	    /* ##### ERROR: size exceeds range */
	    back = 0;
	  }
	  old_err = cmd->me_ull_bytes_in_files;
	  if(math_err) { cmd->me_ull_bytes_in_files = math_err; }
	  cmd->bytes_in_files = dkma_add_double_ok(
	    cmd->bytes_in_files,
	    x,
	    &math_err
	  );
	  cmd->ull_bytes_in_files = dkma_add_ull_ok(
	    cmd->ull_bytes_in_files,
	    xul,
	    &(cmd->me_ull_bytes_in_files)
	  );
	  if((cmd->me_ull_bytes_in_files) && (!old_err)) {
	    /* ##### ERROR: size exceeds range */
	    back = 0;
	  }
	  cmd->files_found = dkma_add_double_ok(
	    cmd->files_found,
	    1.0,
	    &math_err
	  );
	  if(cmd->ull_files_found == DK_MAX_LONG_LONG_UNSIGNED) {
	    cmd->me_ull_files_found = DK_ERR_MATH_OOR;
	  } else {
	    cmd->ull_files_found += DK_ONE_LONG_LONG_UNSIGNED;
	  }
	} break;
	case DK_FT_DIR: {
	  xul = dkstat_size_ok(st, &math_err);
	  old_err = cmd->me_ull_bytes_found;
	  if(math_err) { cmd->me_ull_bytes_found = math_err ; }
	  cmd->ull_bytes_found = dkma_add_ull_ok(
	    cmd->ull_bytes_found,
	    xul,
	    &(cmd->me_ull_bytes_found)
	  );
	  cmd->bytes_found = dkma_add_double_ok(
	    cmd->bytes_found, dkma_ull_to_double(xul),
	    &math_err
	  );
	  if((cmd->me_ull_bytes_found) && (!old_err)) {
	    /* ##### ERROR: size exceeds range */
	    back = 0;
	  }
	  old_err = cmd->me_ull_dirs_found;
	  if(cmd->ull_dirs_found == DK_MAX_LONG_LONG_UNSIGNED) {
	    cmd->me_ull_dirs_found = DK_ERR_MATH_OOR;
	  } else {
	    cmd->ull_dirs_found += DK_ONE_LONG_LONG_UNSIGNED;
	  }
	  cmd->dirs_found = dkma_add_double_ok(
	    cmd->dirs_found,
	    1.0,
	    &math_err
	  );
	  if((cmd->me_ull_dirs_found) && (!old_err)) {
	    /* ERROR: size exceeds range */
	    back = 0;
	  }
	} break;
      }
      if(math_err) {
	cmd->math_error = 1;
	back = 0;
      }
    }
    if((ft & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {	/* directory */
      if(must_go_sub(cmd, NULL, st)) {
	current = dir_entry_new(cmd->current_name, NULL);
	if(current) {
	  if(ft & DK_FT_SYMLINK) {
	    current->ll = 1UL;
	  }
	  while(current) {
	    if((dnt = dkdir_next(current->dir))) {
	    if(dnt == 1) {
	      if((!(cmd->stay_on_filesystem)) || (dkstat_device(current->st) == dkdir_device(current->dir))) {
	      char *xxshn;
	      xxshn = dkdir_get_shortname(current->dir);
	      if(strcmp(xxshn, ".") && strcmp(xxshn, "..")) {
	      if(cmd->sz_wanted) {
		math_err = 0;
		switch((dkdir_filetype(current->dir)) & (~(DK_FT_SYMLINK))) {
		  case DK_FT_REG: {
		    math_err = 0;
		    old_err = cmd->me_ull_bytes_found;
		    xul = dkdir_size_ok(current->dir, &math_err);
		    if(math_err) { cmd->me_ull_bytes_found = math_err; }
		    current->ull_bytes_found = dkma_add_ull_ok(
		      current->ull_bytes_found,
		      xul,
		      &(cmd->me_ull_bytes_found)
		    );
		    if((cmd->me_ull_bytes_found) && (!old_err)) {
		      /* ##### ERROR: size exceeds range */
		      back = 0;
		    }
		    current->bytes_found = dkma_add_double_ok(
		      current->bytes_found,
		      dkma_ull_to_double(xul),
		      &math_err
		    );
		  } break;
		}
		if(math_err) {
		  cmd->math_error = 1;
		}
	      }
	      if(cmd->show_summary) {
		double x;
		math_err = 0;
		switch((dkdir_filetype(current->dir)) & (~(DK_FT_SYMLINK))) {
		  case DK_FT_REG: {
		    xul = dkdir_size_ok(current->dir, &math_err);
		    x = dkma_ull_to_double(xul);
		    old_err = cmd->me_ull_bytes_found;
		    if(math_err) {
		      cmd->me_ull_bytes_found = math_err;
		      cmd->me_ull_bytes_in_files = math_err;
		    }
		    cmd->ull_bytes_found = dkma_add_ull_ok(
		      cmd->ull_bytes_found,
		      xul,
		      &(cmd->me_ull_bytes_found)
		    );
		    cmd->bytes_found = dkma_add_double_ok(
		      cmd->bytes_found,
		      x,
		      &math_err
		    );
		    if((cmd->me_ull_bytes_found) && (!old_err)) {
		      /* ##### ERROR: size exceeds range */
		      back = 0;
		    }
		    cmd->ull_bytes_in_files = dkma_add_ull_ok(
		      cmd->ull_bytes_in_files,
		      xul,
		      &(cmd->me_ull_bytes_in_files)
		    );
		    cmd->bytes_in_files = dkma_add_double_ok(
		      cmd->bytes_in_files,
		      x,
		      &math_err
		    );
		    if(cmd->ull_files_found == DK_MAX_LONG_LONG_UNSIGNED) {
		      cmd->me_ull_files_found = DK_ERR_MATH_OOR;
		    } else {
		      cmd->ull_files_found += DK_ONE_LONG_LONG_UNSIGNED;
		    }
		    cmd->files_found = dkma_add_double_ok(
		      cmd->files_found,
		      1.0,
		      &math_err
		    );
		  } break;
		  case DK_FT_DIR: {
		    xul = dkdir_size_ok(current->dir, &math_err);
		    old_err = cmd->me_ull_bytes_found;
		    if(math_err) { cmd->me_ull_bytes_found = math_err; }
		    cmd->ull_bytes_found = dkma_add_ull_ok(
		      cmd->ull_bytes_found,
		      xul,
		      &(cmd->me_ull_bytes_found)
		    );
		    cmd->bytes_found = dkma_add_double_ok(
		      cmd->bytes_found,
		      dkma_ull_to_double(xul),
		      &math_err
		    );
		    if((cmd->me_ull_bytes_found) && (!old_err)) {
		      /* ##### ERROR: size exceeds range */
		      back = 0;
		    }
		    if(cmd->ull_dirs_found == DK_MAX_LONG_LONG_UNSIGNED) {
		      cmd->me_ull_dirs_found = DK_ERR_MATH_OOR;
		    } else {
		      cmd->ull_dirs_found += DK_ONE_LONG_LONG_UNSIGNED;
		    }
		    cmd->dirs_found = dkma_add_double_ok(
		      cmd->dirs_found,
		      1.0,
		      &math_err
		    );
		  } break;
		}
		if(math_err) {
		  cmd->math_error = 1;
		  back = 0;
		}
	      }
	      }
	      current->fn = dkdir_get_fullname(current->dir);
	      if(current->fn) {
		current->sn = dkdir_get_shortname(current->dir);
		if(strcmp(current->sn, ".") && strcmp(current->sn, "..")) {
		current->testst = dkdir_stat(current->dir);
		  current->ft = dkstat_filetype(current->testst);
		  if(((current->ft) & (~(DK_FT_SYMLINK))) == DK_FT_DIR) {
		    if(must_go_sub(cmd, current, current->testst)) {
		      current = dir_entry_new(current->fn, current);
		      if(current) {
			if(((current->prev)->ft) & DK_FT_SYMLINK) {
			  current->ll += 1UL;
			}
		      } else {
			logmsgs[0] = kls_str[0];
			logmsgs[1] = current->fn;
			logmsgs[2] = kls_str[1];
			dkapp_log_msg(
			  app, DK_LOG_LEVEL_ERROR, logmsgs, 3
			);
			/*
			  ERROR: Failed to create traversal struct
			*/
			back = 0;
		      }
		    } else {
		      if(output_check(cmd,current,0)) {
		        output_for_entry(
			  cmd, NULL, current->testst, current->fn
			);
		      }
		    }
		  } else {
		    if(output_check(cmd,current,0)) {
		      output_for_entry(
		        cmd, NULL, current->testst, current->fn
		      );
		    }
		  }
		}
	      }
	      }
	    } else {	
	    }
	    } else { /* go up one directory */
	      if(cmd->sz_wanted) {
		math_err = 0;
		xul = dkstat_size_ok(current->st, &math_err);
		old_err = current->ull_math_error;
		current->ull_bytes_found = dkma_add_ull_ok(
		  current->ull_bytes_found,
		  xul,
		  &(current->ull_math_error)
		);
		current->bytes_found = dkma_add_double_ok(
		  current->bytes_found,
		  dkma_ull_to_double(xul),
		  &math_err
		);
		if((current->ull_math_error) && (!old_err)) {
		  /* ##### ERROR: size exceeds range */
		  back = 0;
		}
		if(math_err) {
		  cmd->math_error = 1;
		}
	      }
	      if(output_check(cmd,current,1)) {
	        output_for_entry(
		  cmd, current, current->st, current->name
		);
	      }
	      next = current->prev;
	      if(next) {
		if(cmd->sz_wanted) {
		  math_err = 0;
		  next->ull_bytes_found = dkma_add_ull_ok(
		    next->ull_bytes_found,
		    current->ull_bytes_found,
		    &(next->ull_math_error)
		  );
		  if(current->ull_math_error) {
		    next->ull_math_error = current->ull_math_error;
		  }
		  next->bytes_found = dkma_add_double_ok(
		    next->bytes_found,
		    current->bytes_found,
		    &math_err
		  );
		  if(math_err) {
		    cmd->math_error = 1;
		    back = 0;
		  }
		}
	      }
	      current->testst = NULL;
	      dkdir_close(current->dir);
	      dkstat_close(current->st);
	      cptr = current->name;
	      dk_delete(cptr);
	      current->dir = NULL; current->st = NULL;
	      current->name = NULL; current->prev = NULL;
	      current->ll = 0UL;
	      dk_delete(current);
	      current = next;
	    }
	  }
	} else {
	  logmsgs[0] = kls_str[0];
	  logmsgs[1] = cmd->current_name;
	  logmsgs[2] = kls_str[1];
	  dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsgs, 3);
	  /*
	    ERROR: Failed to create traversal structure
	  */
	  back = 0;
	}
      } else {
	output_for_entry(cmd, NULL, st, cmd->current_name);
      }
    } else {			/* no directory */
      output_for_entry(cmd, NULL, st, cmd->current_name);
    }
    dkstat_close(st);
    st = NULL;
  } else {
    logmsgs[0] = kls_str[2];
    logmsgs[1] = cmd->current_name;
    logmsgs[2] = kls_str[3];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsgs, 3);
    /*
      ERROR: stat failed for name
    */
    back = 0;
  } 
  return back;
}



/**	Find output width for size. 
	@param	cmd	kls job.
*/
static
void
adjust_cmd_size_output_width DK_P1(KlsCmd *,cmd)
{
  char buffer[32], *stptr;
  size_t max, sz;
  max = 0;
  sprintf(buffer, "%20.0lf", cmd->dirs_found);
  stptr = dkstr_start(buffer, NULL);
  if(stptr) {
    sz = strlen(stptr);
    if(sz > max) max = sz;
  }
  sprintf(buffer, "%20.0lf", cmd->files_found);
  stptr = dkstr_start(buffer, NULL);
  if(stptr) {
    sz = strlen(stptr);
    if(sz > max) max = sz;
  }
  sprintf(buffer, "%20.0lf", cmd->bytes_in_files);
  stptr = dkstr_start(buffer, NULL);
  if(stptr) {
    sz = strlen(stptr);
    if(sz > max) max = sz;
  }
  sprintf(buffer, "%20.0lf", cmd->bytes_found);
  stptr = dkstr_start(buffer, NULL);
  if(stptr) {
    sz = strlen(stptr);
    if(sz > max) max = sz;
  }
  max++;
  cmd->size_output_width = max;
}



/**	Run for the given command.
	@param	cmd	Kls job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_cmd DK_P1(KlsCmd *,cmd)
{
  int back = 0;
  size_t maxpathlen;
  int i, need_fne;
  dk_fne_t *fne;
  char *fn, *cptr;
  char *logmsgs[8];
  
#if (DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H)
  if(cmd->print_list) {
    cptr = strchr((cmd->print_list), 'x');
    if(cptr) {
      cmd->md5_wanted = 1;
    }
  }
#endif
  if(cmd->print_list) {
    cptr = strchr((cmd->print_list), 's');
    if(cptr) {
      cmd->sz_wanted = 1;
    }
  }
  if(cmd->file_types) {
    cptr = cmd->file_types;
    while(*cptr) {
      switch(*cptr) {
	case 'f' : cmd->ft_reg = 1; break;
	case 'd' : cmd->ft_dir = 1; break;
	case 'p' : cmd->ft_pipe = 1; break;
	case 'c' : cmd->ft_chr = 1; break;
	case 'b' : cmd->ft_blk = 1; break;
	case 's' : cmd->ft_sock = 1; break;
	case 'o' : cmd->ft_oth = 1; break;
      }
      cptr++;
    }
  } else {
    cmd->ft_reg = 1;
    cmd->ft_dir = 1;
  }
  if((cmd->names_used) == 0) {
    maxpathlen = dksf_get_maxpathlen();
    fn = dk_new(char,maxpathlen);
    if(fn) {
      if(dksf_getcwd(fn,maxpathlen)) {
	cmd->current_name = fn;
	cmd->current_depth = 0UL;
	cmd->current_symlinks = 0UL;
	cmd->remove_cwd_from_name = 1;
	/* cmd->remove_cwd_length = strlen(fn) + 1; */
	cmd->remove_cwd_length = strlen(fn);
	if((cmd->remove_cwd_length) > 0) {
	if(fn[(cmd->remove_cwd_length) - 1] != fn_sep[0]) {
	  cmd->remove_cwd_length += 1;
	}
	}
	cmd->dc_dir = 0;
	cmd->dc_contents = 1;
	back = run_for_current(cmd);
      } else {
	logmsgs[0] = kls_str[4];
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsgs, 1);
	/*
	  ERROR: Failed to estimate current directory
	*/
      }
      dk_delete(fn);
    } else {
      logmsgs[0] = kls_str[5];
      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsgs, 1);
      /*
	ERROR: Not enough memory for current directory
      */
    }
  } else {
    back = 1;
    for(i = 0; i < (cmd->names_used); i++) {
      dksf_correct_fnsep( (cmd->filenames)[i] );
      need_fne = dksf_must_expand_filename( (cmd->filenames)[i] );
      if(need_fne) {	
	fne = dkfne_open(((cmd->filenames)[i]), 1, 1);
	if(fne) {
	  while(dkfne_next(fne)) {
	    fn = dkfne_get_fullname(fne);
	    if(fn) {
	      cmd->current_name = fn;
	      cmd->current_depth = 0UL;
	      cmd->current_symlinks = 0UL;
	      if(!run_for_current(cmd)) {
		back = 0;
	      }
	    } else {
	      logmsgs[0] = kls_str[6];
	      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsgs, 1);
	      /*
		ERROR: Failed to find full name
	      */
	      back = 0;
	    }
	  }
	  dkfne_close(fne);
	} else {
	  logmsgs[0] = kls_str[7];
	  logmsgs[1] = (cmd->filenames)[i];
	  logmsgs[2] = kls_str[8];
	  dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsgs, 3);
	  /*
	    ERROR: Failed to open FNE
	  */
	  back = 0;
	}
      } else {	
	cmd->current_name = (cmd->filenames)[i];
	cmd->current_depth = 0UL;
	cmd->current_symlinks = 0UL;
	if(!run_for_current(cmd)) {
	  back = 0;
	}
      }
    }
  }
  if(cmd->show_summary) {
    adjust_cmd_size_output_width(cmd);
    fputs(kls_str[9], cmd->output_file);
    pr_ull_or_double(
      cmd,
      cmd->ull_dirs_found,
      cmd->dirs_found,
      cmd->me_ull_dirs_found
    );
    fputc('\n', cmd->output_file);
    fputs(kls_str[10], cmd->output_file);
    pr_ull_or_double(
      cmd,
      cmd->ull_files_found,
      cmd->files_found,
      cmd->me_ull_files_found
    );
    fputc('\n', cmd->output_file);
    fputs(kls_str[11], cmd->output_file);
    pr_ull_or_double(
      cmd,
      cmd->ull_bytes_in_files,
      cmd->bytes_in_files,
      cmd->me_ull_bytes_in_files
    );
    fputc('\n', cmd->output_file);
    fputs(kls_str[12], cmd->output_file);
    pr_ull_or_double(
      cmd,
      cmd->ull_bytes_found,
      cmd->bytes_found,
      cmd->me_ull_bytes_found
    );
    fputc('\n', cmd->output_file);
  } 
  return back;
}



/**	Get string length on screen.
	@param	cmd	Kls job.
	@param	max	Maximum screen space already used.
	@param	str	String to print.
	@return	Maximum of old screen space and string length on screen.
*/
static size_t
test_string_length DK_P3(KlsCmd *,cmd, size_t, max, char *, str)
{
  size_t back, x;
  
  back = max;
  if(str) {
    x = dkapp_prlen(app, str);
    if(x > back) back = x;
  }
  
  return back;
}



/**	Print a string right aligned.
	@param	str	String to print.
	@param	max	Available place on screen.
*/
static void
print_string_right_aligned DK_P2(char *,str, size_t, max)
{
  size_t sz, i;
  
  if(str) {
    sz = dkapp_prlen(app, str);
    for(i = sz; i < max; i++) fputc(' ', stdout);
    dkapp_stdout(app, str);
  } else {
    for(i = 0; i < max; i++) fputc(' ', stdout);
  }
  
}



/**	Three spaces are used before and after the value.
*/
static char three_spaces[] = { "   " };



/**	Show one configuration setting.
	@param	max	Maximu screen space.
	@param	str	Option to show.
*/
static void
show_info_type DK_P2(size_t, max, char *,str)
{
  size_t i;
  fputs(&(three_spaces[1]), stdout);
  fputs(three_spaces, stdout);
  fputs(three_spaces, stdout);
  for(i = 0; i < max; i++) fputc(' ', stdout);
  dkapp_stdout(app, str); fputc('\n', stdout);
}



/**	Show configuration.
	@param	cmd	Kls job.
*/
static void
show_conf DK_P1(KlsCmd *,cmd)
{
  size_t max;
  char ldbuffer[32], *chrptr;
  sprintf(ldbuffer, "%lu", cmd->symlinks_allowed);
  max = 0;
  chrptr = kls_str[46];
  if(cmd->symlinks_limited) chrptr = ldbuffer;
  max = test_string_length(cmd, max, chrptr);
  sprintf(ldbuffer, "%d", cmd->size_output_width);
  chrptr = ldbuffer;
  max = test_string_length(cmd, max, kls_str[44]);
  max = test_string_length(cmd, max, kls_str[45]);
  if(cmd->size_output_width == -1) chrptr = kls_str[46];
  if(cmd->size_output_width ==  0) chrptr = kls_str[54];
  max = test_string_length(cmd, max, chrptr);
  max = test_string_length(cmd, max, kls_str[50]);
  max = test_string_length(cmd, max, digest_types[0]);
  max = test_string_length(cmd, max, digest_types[1]);
  max = test_string_length(cmd, max, digest_encodings[cmd->message_digest_encoding]);
  max = test_string_length(cmd, max, cmd->file_types);
  max = test_string_length(cmd, max, cmd->print_list);
  dkapp_stdout(app, kls_str[51]);
  fputc('\n', stdout);
  if(cmd->print_list) {
    dkapp_stdout(app, "-p");
    dkapp_stdout(app, three_spaces);
    print_string_right_aligned(cmd->print_list, max);
    dkapp_stdout(app, three_spaces);
    dkapp_stdout(app, kls_str[17]);
    fputc('\n', stdout);
    chrptr = cmd->print_list;
    while(*chrptr) {
      switch(*chrptr) {
        case 'n': show_info_type(max, kls_str[18]); break;
        case 's': show_info_type(max, kls_str[19]); break;
        case 't': show_info_type(max, kls_str[20]); break;
        case 'p': show_info_type(max, kls_str[21]); break;
        case 'x': show_info_type(max, kls_str[22]); break;
        case 'l': show_info_type(max, kls_str[23]); break;
        case 'c': show_info_type(max, kls_str[24]); break;
        case 'm': show_info_type(max, kls_str[25]); break;
        case 'a': show_info_type(max, kls_str[26]); break;
        case 'u': show_info_type(max, kls_str[27]); break;
        case 'g': show_info_type(max, kls_str[28]); break;
        case 'd': show_info_type(max, kls_str[29]); break;
        case 'r': show_info_type(max, kls_str[30]); break;
        case 'i': show_info_type(max, kls_str[31]); break;
      }
      chrptr++;
    }
  }
  dkapp_stdout(app, "-m");
  dkapp_stdout(app, three_spaces);
  print_string_right_aligned(digest_types[cmd->message_digest_type], max);
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[32]);
  fputc('\n', stdout);

  dkapp_stdout(app, "-e");
  dkapp_stdout(app, three_spaces);
  print_string_right_aligned(
    digest_encodings[cmd->message_digest_encoding], max
  );
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[55]);
  fputc('\n', stdout);
  dkapp_stdout(app, "-r");
  dkapp_stdout(app, three_spaces);
  print_string_right_aligned(((cmd->rec)?(kls_str[44]):(kls_str[45])), max);
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[33]);
  fputc('\n', stdout);
  if(cmd->file_types) {
    dkapp_stdout(app, "-t");
    dkapp_stdout(app, three_spaces);
    print_string_right_aligned(cmd->file_types, max);
    dkapp_stdout(app, three_spaces);
    dkapp_stdout(app, kls_str[34]);
    fputc('\n', stdout);
    chrptr = cmd->file_types;
    while(*chrptr) {
      switch(*chrptr) {
        case 'f': show_info_type(max, kls_str[35]); break;
	case 'd': show_info_type(max, kls_str[36]); break;
	case 'p': show_info_type(max, kls_str[37]); break;
	case 'c': show_info_type(max, kls_str[38]); break;
	case 'b': show_info_type(max, kls_str[39]); break;
	case 's': show_info_type(max, kls_str[40]); break;
	case 'o': show_info_type(max, kls_str[41]); break;
      }
      chrptr++;
    }
  }
  dkapp_stdout(app, "-d");
  dkapp_stdout(app, three_spaces);
  ldbuffer[0]=ldbuffer[1]=ldbuffer[2]='\0';
  if(cmd->dc_dir) {
    if(cmd->dc_contents) {
      ldbuffer[0] = 'd'; ldbuffer[1]='c';
    } else {
      ldbuffer[0] = 'd';
    }
  } else {
    if(cmd->dc_contents) {
      ldbuffer[0]='c';
    } else {
      ldbuffer[0] = 'd'; ldbuffer[1]='c';
    }
  }
  print_string_right_aligned(ldbuffer, max);
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[47]);
  fputc('\n', stdout);
  chrptr = ldbuffer;
  while(*chrptr) {
    switch(*chrptr) {
      case 'd': show_info_type(max, kls_str[48]); break;
      case 'c': show_info_type(max, kls_str[49]); break;
    }
    chrptr++;
  }
  sprintf(ldbuffer, "%lu", cmd->symlinks_allowed);
  chrptr = kls_str[46];
  if(cmd->symlinks_limited) chrptr = ldbuffer;
  dkapp_stdout(app, "-l");
  dkapp_stdout(app, three_spaces);
  print_string_right_aligned(chrptr, max);
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[42]);
  fputc('\n', stdout);
  sprintf(ldbuffer, "%d", cmd->size_output_width);
  chrptr = ldbuffer;
  if(cmd->size_output_width == -1) chrptr = kls_str[46];
  if(cmd->size_output_width ==  0) chrptr = kls_str[54];
  dkapp_stdout(app, "-w");
  dkapp_stdout(app, three_spaces);
  print_string_right_aligned(chrptr, max);
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[53]);
  fputc('\n', stdout);
  dkapp_stdout(app, "-f");
  dkapp_stdout(app, three_spaces);
  print_string_right_aligned(((cmd->stay_on_filesystem)?(kls_str[44]):(kls_str[45])),max);
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[52]);
  fputc('\n', stdout);
  dkapp_stdout(app, "-s");
  dkapp_stdout(app, three_spaces);
  print_string_right_aligned(((cmd->show_summary)?(kls_str[44]):(kls_str[45])),max);
  dkapp_stdout(app, three_spaces);
  dkapp_stdout(app, kls_str[43]);
  fputc('\n', stdout);
}



/**	Long option keywords.
*/
static char *long_options_keywords[] = {
  /* 00 */ "m$essage-digest",
  /* 01 */ "o$utputfile",
  /* 02 */ "p$rint",
  /* 03 */ "w$idth",
  /* 04 */ "r$ecursive",
  /* 05 */ "t$ype",
  /* 06 */ "c$onfigure",
  /* 07 */ "l$inks",
  /* 08 */ "u$nconfigure",
  /* 09 */ "d$irectory",
  /* 10 */ "su$mmary",
  /* 11 */ "v$ersion",
  /* 12 */ "h$elp",
  /* 13 */ "sh$ow-configuration",
  /* 14 */ "st$ay-on-filesystem",
  /* 15 */ "e$ncoding",
  NULL
};



/**	Main program after creating the application.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	1 on success, 0 on error.
*/
static
int
run_main(int argc, char **argv)
{
  int back = 0;
  PCHAR *filenames;
  KlsCmd cmd;
  char *outputname, *cptr;
  FILE *outputfile;
  int   i, lfd; char **argptr, **licptr;
  char *my_info, *my_types;
  char *logmsgs[8];
  
  outputfile = NULL; my_info = NULL; my_types = NULL;
  filenames = dk_new(PCHAR,argc);
  if(filenames) {
    for(i = 0; i < argc; i++) { filenames[i] = NULL; }
    cmd_init(&cmd);
    cmd.filenames = filenames;
    cmd.names_used = 0;
    /*
      Use cmd line arguments
    */
    argptr = argv; argptr++; lfd = 1;
    while(lfd < argc) {
      cptr = *argptr;
      if(cptr) {
	if(*cptr == '-') {
	  cptr++;
	  switch(*cptr) {
	    case '-' : {
	      int found;
	      char *optarg;
	      found = -1;		/* option not found yet */
	      cptr++;			/* move to start of keyword */
	      optarg = NULL;		/* no argument found yet */
	      optarg = dkstr_chr(cptr, '=');
	      if(optarg) {
	        *(optarg++) = '\0';
	      }
	      found = dkstr_array_abbr(long_options_keywords, cptr, '$', 1);
	      if(found > -1) {
	        switch(found) {
		  case 0: {
		    /* message digest type */
		    cmd.mdtype_configured = 1;
		    cmd.message_digest_type = -1;
		    if(optarg) {
		      cmd.message_digest_type = dkstr_array_index(
		        digest_types, optarg, 0
		      );
		    }
		    if(cmd.message_digest_type < 0) cmd.message_digest_type = MD_TYPE_SHA1;
		  } break;
		  case 1: {
		    /* output file name */
		    if(optarg) {
		      outputfile = dkapp_fopen(app, optarg, "w");
		      if(outputfile) {
		        if(cmd.output_file) {
			  fclose(cmd.output_file);
			  cmd.output_file = NULL;
			}
		        cmd.output_file = outputfile;
		      }
		    }
		  } break;
		  case 2: {
		    /* print order */
		    if(optarg) {
		      cmd.print_list = optarg;
		    }
		  } break;
		  case 3: {
		    /* size output width */
		    if(optarg) {
		      int i;
		      cmd.size_output_configured = 1;
		      if(sscanf(optarg, "%d", &i) == 1) {
		        if(i < -1) i = -1;
			if(i > 20) i = 20;
		        cmd.size_output_width = i;
		      } else {
			cmd.size_output_width = -1;
		      }
		    } else {
		      cmd.size_output_configured = 0;
		    }
		  } break;
		  case 4: {
		    /* recursive */
		    cmd.rec_configured = 1;
		    if(optarg) {
		      cmd.rec = dkstr_is_on(optarg);
		    } else {
		      cmd.rec = 1;
		    }
		  } break;
		  case 5: {
		    /* types */
		    if(optarg) {
		      cmd.file_types = optarg;
		    } else {
		      cmd.file_types = NULL;
		    }
		  } break;
		  case 6: {
		    /* configure */
		    if(optarg) {
		      cmd.configure_only = dkstr_is_on(optarg);
		    } else {
		      cmd.configure_only = 1;
		    }
		  } break;
		  case 7: {
		    /* symlink level */
		    unsigned long x;
		    cmd.symlinks_configured = 1;
		    if(optarg) {
		      if(sscanf(optarg, "%lu", &x) == 1) {
		        cmd.symlinks_limited = 1;
			cmd.symlinks_allowed = x;
		      } else {
		        cmd.symlinks_limited = 0;
			cmd.symlinks_allowed = 1UL;
		      }
		    } else {
		      cmd.symlinks_limited = 0;
		      cmd.symlinks_allowed = 1UL;
		    }
		  } break;
		  case 8: {
		    /* unconfigure */
		    if(optarg) {
		      if(dkstr_is_on(optarg)) {
		        cmd.configure_only = 1;
			cmd.unconf_only = 1;
			dkapp_unconfigure(app);
		      }
		    } else {
		      cmd.configure_only = 1;
		      cmd.unconf_only = 1;
		      dkapp_unconfigure(app);
		    }
		  } break;
		  case 9: {
		    /* directory handling */
		    cmd.dc_configured = 1;
		    if(optarg) {
		    cptr = optarg  ;
		      while(*cptr) {
		        switch(*cptr) {
			  case 'd': cmd.dc_dir = 1; break;
			  case 'c': cmd.dc_contents = 1; break;
		        }
		        cptr++;
		      }
		    } else {
		      cmd.dc_contents = 1; cmd.dc_dir = 0;
		    }
		  } break;
		  case 10: {
		    /* summary */
		    cmd.summary_configured = 1;
		    if(optarg) {
		      cmd.show_summary = dkstr_is_on(optarg);
		    } else {
		      cmd.show_summary = 1;
		    }
		  } break;
		  case 11: {
		    /* version */
		    cmd.show_version = 1;
		    if(optarg) cmd.show_version = dkstr_is_on(optarg);
		  } break;
		  case 12: {
		    /* help */
		    cmd.show_help = 1;
		    if(optarg) cmd.show_help = dkstr_is_on(optarg);
		  } break;
		  case 13: {
		    /* show configuration */
		    cmd.show_configuration = 1;
		    if(optarg) cmd.show_configuration = dkstr_is_on(optarg);
		  } break;
		  case 14: {
		    /* stay on filesystem */
		    cmd.stay_configured = 1;
		    cmd.stay_on_filesystem = 1;
		    if(optarg) cmd.stay_on_filesystem = dkstr_is_on(optarg);
		  } break;
		  case 15: {
		    /* encoding */
		    cmd.mdenc_configured = 1;
		    cmd.message_digest_encoding = -1;
		    if(optarg) {
		      cmd.message_digest_encoding = dkstr_array_index(
		        digest_encodings, optarg, 0
		      );
		    }
		    if(cmd.message_digest_encoding < 0) {
		      cmd.message_digest_encoding = MD_ENC_HEX;
		    }
		  } break;
		}
	      }
	    } break;
	    case 'o' : {		/* output file name */
	      outputname = NULL;
	      cptr++;
	      if(*cptr) {
		outputname = cptr;
	      } else {
		lfd++; argptr++;
		if(lfd < argc) {
		  outputname = *argptr;
		}
	      }
	      if(outputname) {
	        dk_fne_t *ofne; char *ofnptr;
	        dksf_correct_fnsep(outputname);
	        if(dksf_must_expand_filename(outputname)) {
		  ofne = dkfne_open(outputname, 1, 0);
		  if(ofne) {
		    ofnptr = dkapp_fne_one(app, ofne, outputname);
		    if(ofnptr) {
		      outputfile = dkapp_fopen(app, ofnptr, "w");
		      if(outputfile) {
		        if(cmd.output_file) {
			  fclose(cmd.output_file);
			  cmd.output_file = NULL;
			}
			cmd.output_file = outputfile;
		      }
		      dk_delete(ofnptr);
		    }
		    dkfne_close(ofne);
		    ofne = NULL;
		  } else {
		    dkapp_err_memory(app, sizeof(dk_fne_t), 1);
		  }
		} else {
		  outputfile = dkapp_fopen(app,outputname,"w");
		  if(outputfile) {
		    if(cmd.output_file) {
		      fclose(cmd.output_file);
		      cmd.output_file = NULL;
		    }
		    cmd.output_file = outputfile;
		  }
		}
	      }
	    } break;
	    case 'u' : {
	      cmd.configure_only = 1;
	      cmd.unconf_only = 1;
	      dkapp_unconfigure(app);
	    } break;
	    case 'f' : {
	      cmd.stay_on_filesystem = 1;
	      cmd.stay_configured = 1;
	    } break;
	    case 'p' : {		/* print info list */
	      cptr++; outputname = NULL;
	      if(*cptr) {
		outputname = cptr;
	      } else {
		lfd++; argptr++;
		if(lfd < argc) {
		  outputname = *argptr;
		}
	      }
	      if(outputname) {
		cmd.print_list = outputname;
	      }
	    } break;
	    case 'r' : {		/* recursively */
	      cmd.rec_configured = 1;
	      if(cptr[1] == '-') { cmd.rec = 0; }
	      else               { cmd.rec = 1; }
	    } break;
	    case 't' : {		/* file types to list */
	      cptr++; outputname = NULL;
	      if(*cptr) {
		outputname = cptr;
	      } else {
		lfd++; argptr++;
		if(lfd < argc) {
		  outputname = *argptr;
		}
	      }
	      if(outputname) {
		cmd.file_types = outputname;
	      }
	    } break;
	    case 'c' : {		/* write configuration */
	      cmd.configure_only = 1;
	    } break;
	    case 'd' : {
	      outputname = NULL; cptr++;
	      if(*cptr) {
		outputname = cptr;
	      } else {
		lfd++; argptr++;
		if(lfd < argc) {
		  outputname = *argptr;
		}
	      }
	      if(outputname) {
		cmd.dc_configured = 1;
		while(*outputname) {
		  switch(*outputname) {
		    case 'd': cmd.dc_dir = 1; break;
		    case 'c': cmd.dc_contents = 1; break;
		  }
		  outputname++;
		}
	      }
	    } break;
	    case 'm' : {
	      outputname = NULL; cptr++;
	      if(*cptr) {
		outputname = cptr;
	      } else {
		lfd++; argptr++;
		if(lfd < argc) {
		  outputname = *argptr;
		}
	      }
	      if(outputname) {
		cmd.message_digest_type = dkstr_array_index(
		  digest_types, outputname, 0
		);
		if(cmd.message_digest_type < 0) {
		  cmd.message_digest_type = MD_TYPE_SHA1;
		} else {
		  cmd.mdtype_configured = 1;
		}
	      }
	    } break;
	    case 'e' : {
	      outputname = NULL; cptr++;
	      if(*cptr) {
	        outputname = cptr;
	      } else {
	        lfd++; argptr++;
		if(lfd < argc) {
		  outputname = *argptr;
		}
	      }
	      if(outputname) {
	        cmd.message_digest_encoding = dkstr_array_index(
		  digest_encodings, outputname, 0
		);
		if(cmd.message_digest_encoding < 0) {
		  cmd.message_digest_encoding = MD_ENC_HEX;
		} else {
		  cmd.mdenc_configured = 1;
		}
	      }
	    } break;
	    case 'l' : {		/* symlink depth */
	      outputname = NULL; cptr++;
	      if(*cptr) {
		outputname = cptr;
	      } else {
		lfd++; argptr++;
		if(lfd < argc) { outputname = *argptr; }
	      }
	      if(outputname) {
		unsigned long x;
		
		if(sscanf(outputname, "%lu", &x) == 1) {
		  cmd.symlinks_allowed = x;
		  cmd.symlinks_limited = 1;
		  cmd.symlinks_configured = 1;
		} else {
		  cmd.symlinks_configured = 1;
		  cmd.symlinks_limited = 0;
		}
	      }
	    } break;
	    case 'w' : {		/* size column width */
	      outputname = NULL; cptr++;
	      if(*cptr) {
	        outputname = cptr;
	      } else {
	        lfd++; argptr++;
		if(lfd < argc) { outputname = *argptr; }
	      }
	      if(outputname) {
	        int x;
		if(sscanf(outputname, "%d", &x) == 1) {
		  if(x < -1) { x = -1; }
		  if(x > 20) { x = 20; }
		  cmd.size_output_width = x;
		  cmd.size_output_configured = 1;
		}
	      }
	    } break;
	    case 's' : {		/* show summary */
	      cmd.summary_configured = 1;
	      if(cptr[1] == '-') { cmd.show_summary = 0; }
	      else               { cmd.show_summary = 1; }
	    } break;
	    case 'v' : {		/* print version */
	      if(cptr[1] == '-') { cmd.show_version = 0; }
	      else               { cmd.show_version = 1; }
	    } break;
	    case 'h' : {		/* print help */
	      if(cptr[1] == '-') { cmd.show_help = 0; }
	      else               { cmd.show_help = 1; }
	    } break;
	    case 'C' : {
	      if(cptr[1] == '-') { cmd.show_configuration = 0; }
	      else		 { cmd.show_configuration = 1; }
	    } break;
	  }
	} else {
	  filenames[cmd.names_used] = cptr;
	  cmd.names_used += 1;
	}
      }
      lfd++; argptr++;
    }
    if(!(cmd.output_file)) {
      cmd.output_file = stdout;
    }
    /*
      Complete configuration from preferences
    */
    complete_from_prefs(&cmd, &my_info, &my_types);
    /*
      Complete configuration by use of built-in defaults
    */
    if(!(cmd.print_list)) {
      cmd.print_list = default_print_list;
    }
    if(!(cmd.file_types)) {
      cmd.file_types = default_file_types;
    }
    cmd.rec_configured = 1;
    cmd.symlinks_configured = 1;
    cmd.summary_configured = 1;
    if(!(cmd.dc_configured)) {
      cmd.dc_configured = 1;
      cmd.dc_contents = 1;
      cmd.dc_dir = 0;
    }
    if(!((cmd.dc_dir) || (cmd.dc_contents))) {
      cmd.dc_contents = 1;
    } 
    /*
      Run
    */
    if(cmd.configure_only) {
      if(!(cmd.unconf_only)) {
        char osbuffer[16]; char dcbuffer[3]; char slbuffer[32];
        slbuffer[0] = '\0';
	
        if(cmd.print_list) {
	  dkapp_set_pref(app, pn_print_list, cmd.print_list);
        }
	
	sprintf(osbuffer, "%d", cmd.size_output_width);
	dkapp_set_pref(app, pn_width, osbuffer);
	
	dkapp_set_pref(app, pn_digest, digest_types[cmd.message_digest_type]);
	
	dkapp_set_pref(app, pn_mdenc, digest_encodings[cmd.message_digest_encoding]);
	
	dkapp_set_pref(app, pn_rec, ((cmd.rec) ? pv_on : pv_off));
	if(cmd.symlinks_limited) {
	  sprintf(slbuffer, "%lu", cmd.symlinks_allowed);
	  dkapp_set_pref(app, pn_links, slbuffer);
	} else {
	  dkapp_set_pref(app, pn_links, pv_unlimited);
	}
	
	dkapp_set_pref(app, pn_summary, ((cmd.show_summary) ? pv_on : pv_off));
        if(cmd.file_types) {
	  dkapp_set_pref(app, pn_type, cmd.file_types);
        }
        dkapp_set_pref(app,pn_stay,((cmd.stay_on_filesystem) ? pv_on : pv_off));
	dcbuffer[0] = dcbuffer[1] = '-';
	dcbuffer[2] = '\0';
	if(cmd.dc_dir) {
	  dcbuffer[0] = 'd';
	}
	if(cmd.dc_contents) {
	  dcbuffer[1] = 'c';
	}
	dkapp_set_pref(app, pn_dir, dcbuffer);
        show_conf(&cmd);
      }
      back = 1;
    } else {
      if((cmd.show_help) || (cmd.show_version) ||(cmd.show_configuration)) {
	if(cmd.show_version) {
	  printf("\n");
	  print_version();
#if DK_HAVE_OPENSSL_MD5_H || DK_HAVE_OPENSSL_SHA_H || DK_HAVE_OPENSSL_RIPEMD_H
	  printf("\n");
	  licptr = openssl_lib_version;
	  while(*licptr) { printf("%s\n", *(licptr++)); }
#endif
	  printf("\n");
	}
	if(cmd.show_help) {
	  dkapp_help(app, "kls.txt", help_text);
	}
	if(cmd.show_configuration) {
	  /* ##### show the configuration */ 
	  show_conf(&cmd);
	}
	back = 1;
      } else { 
	back = run_for_cmd(&cmd);
      }
    }
    /*
      Free the filenames
    */
    if(my_info) { dk_delete(my_info); my_info = NULL; }
    if(my_types) { dk_delete(my_types); my_types = NULL; }
    if(outputfile) { fclose(outputfile); outputfile = NULL; }
    for(i = 0; i < argc; i++) { filenames[i] = NULL; }
    dk_delete(filenames);
  } else {
    logmsgs[0] = kls_str[5];
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsgs, 1);
    /*
       ERROR: Not enough memory to store filenames
    */
  } 
  return back;
}



/**	The main() function of the kls program.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	0 on success, any other value indicates an error.
*/
int main(int argc, char *argv[])
{
  int exval = 0;
  int xargc;
  char **xargv;
  
#line 3439 "kls.ctr"

  allocated_print_list = NULL;
  allocated_dc = NULL;
  app = dkapp_open_ext1(argc, argv, packagename, sysconfdir, 0, 0);
  if(app) {
    xargc = dkapp_get_argc(app);
    xargv = dkapp_get_argv(app);
    dkapp_find_multi(app, kls_mesgs, "kls" );
    exval = run_main(xargc, xargv);
    dkapp_close(app);
    app = NULL;
  } else {
    if(!dkapp_silence_check(argc,argv)) {
      fprintf(stderr, "ERROR: NOT ENOUGH MEMORY\n");
      fflush(stderr);
    }
  }
  if(allocated_print_list) {
    dk_delete(allocated_print_list);
    allocated_print_list = NULL;
  }
  if(allocated_dc) {
    dk_delete(allocated_dc);
    allocated_dc = NULL;
  }
  exval = (exval ? 0 : 1); 
  
#line 3465 "kls.ctr"

  exit(exval);
  return exval;
}




