/*
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	createp.c	Create passwords.
*/

/*
	createp -c -tdefault -l6-8 -d1-2 -s1-2 -k1-2 -o default
		configure type (length, digits, specials, upper-case)

	createp -c -ttest -l6-8 -o hex -a openssl,random,rand48,rand
		configure type (length, ASCII-Hex encoded random data)

	createp -c -ttest -l6-8 -o ascii85
		configure type (length, ASCII85-endcoded random data)

	createp -c -t wpa-key -l32 -o charset aA0
		configure type (length, character classes)

	createp -u
		unconfigure

	createp -C
		show current configuration

	createp -h
		show help text

	createp -v
		show version

	createp -tdefault erwin
		create password of type default for user erwin

*/


#include <dk.h>

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

#include <dkmem.h>
#include <dkstr.h>
#include <dkapp.h>
#include <dkrandc.h>
#include <dksto.h>
#include <dkbf.h>
#include <dklogc.h>
#include <dklic.h>


#include "dktools-version.h"




#line 100 "createp.ctr"




/**	Output mode: Default output mode. */
#define PROGRAM_MODE_DEFAULT	0

/**	Output mode: Random data, ASCII-Hex encoded. */
#define PROGRAM_MODE_HEX	1

/**	Output mode: Random data, ASCII-85 encoded. */
#define PROGRAM_MODE_ASCII85	2

/**	Output mode: Use specified character set. */
#define PROGRAM_MODE_SET	3




/**	Action: Run normally (create passwords/WPA keys). */
#define ACTION_RUN		0

/**	Action: Print help text, */
#define	ACTION_HELP		1

/**	Action: Show version. */
#define ACTION_VERSION		2

/**	Action: Save preferences. */
#define ACTION_CONFIGURE	4

/**	Action: Unconfigure. */
#define ACTION_UNCONFIGURE	8

/**	Action: Show configuration. */
#define ACTION_SHOWCONF		16



/**	Buffer length for preferences keys and values.
*/
#define DEFAULT_BUFFER_LENGTH	256



/**	Createp job.
*/
typedef struct {
  int		rand_types_allowed;	/**< Allowed PRNG types (or-comb). */
  int		program_mode;		/**< Output mode. */
  int		program_action;		/**< Action to take. */
  int		exval;			/**< Exit status. */
  unsigned	l_min;			/**< Minimum password length. */
  unsigned	l_max;			/**< Maximum password length. */
  unsigned	l;			/**< Password length. */
  unsigned	d_min;			/**< Minimum number of digits. */
  unsigned	d_max;			/**< Maximum number of digits. */
  unsigned	d;			/**< Number of digits. */
  unsigned	s_min;			/**< Minimum number of specials. */
  unsigned	s_max;			/**< Maximum number of specials. */
  unsigned	s;			/**< Number of specials. */
  unsigned	u_min;			/**< Minimum number of upper-case. */
  unsigned	u_max;			/**< Maximum number of upper-case. */
  unsigned	u;			/**< Number of upper-case. */
  size_t	u_cs_buffer;		/**< Used bytes in \a cs_buffer. */
  size_t	s_b_type;		/**< Size of type buffer. */
  unsigned char	pw_only;		/**< Flag: Print password only. */
  unsigned char	f_pw_only;		/**< Flag: Found setting pw only. */
  unsigned char	f_cmd_rand_types_allowed;	/**< Flag: Found allowed. */
  unsigned char	f_cmd_mode;		/**< Flag: Found output mode. */
  unsigned char	f_cmd_l;	/**< Flag: Length specified in command line. */
  unsigned char	f_cmd_d;	/**< Flag: Digits specified in command line. */
  unsigned char	f_cmd_s; /**< Flag: Specials specified in command line. */
  unsigned char f_cmd_u; /**< Flag: uppercase specified in command line. */
  unsigned char f_cmd_charset; /**< Flag: Charset specified in command line. */
  char		**msg;			/**< Message text array. */
  dk_app_t	*a;			/**< Application. */
  unsigned char	*cs_buffer;		/**< Buffer, length 256. */
  char		*b_type;		/**< Type. */
  dk_storage_t	*s_names;		/**< user names container. */
  dk_storage_iterator_t	*i_names;	/**< User names iterator. */
  dk_bitfield_t	*b_cs_buffer;	/**< Bitfield, type set for position. */
} CMD;



/**	Struct to store user names in order of appearance on the command line.
*/
typedef struct {
  unsigned long	number;	/**< Line number. */
  char		*name;	/**< Name. */
} NAME;



/*
	Name of the program group
*/
#ifndef GROUPNAME
#define GROUPNAME "dktools"
#endif
/**	Application group name.
*/
static char grname[] = { GROUPNAME };


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



/**	Entry names and default values for the strings used by the program.
*/
static dk_key_value_t kv[] = {
{ "0",	"Not enough memory (RAM/swap space)!" },
{ "1",	"String \"" },
{ "2",	"\" is to long!" },
{ "3",  "\"" },
{ "4",	"\" is not a number or range!" },
{ "5",	"Unknown output mode \"" },
{ "6",	"\"!" },
{ "7",	"Invalid output character set \"" },
{ "8",	"\"!" },
{ "9",	"Not a boolean value: \"" },
{ "10",	"\"!" },
{ "11",	"on" },
{ "12",	"off" },
{ "13",	"Password type" },
{ "14", "PRNG types, either \"all\" or a combination of:" },
{ "15", "openssl     the OpenSSL PRNG" },
{ "16", "random      the PRNG using initstate()/setstate()/random()" },
{ "17", "rand48      the PRNG using nrand48()" },
{ "18", "rand        the PRNG using rand()" },
{ "19", "Output mode, one of:" },
{ "20", "default                 specify number of digits, special" },
{ "21", "                        and upper-case characters as options" },
{ "22", "hex                     '0'...'9','a'...'f'" },
{ "23", "ascii85                 characters 33...127" },
{ "24", "charset  <characters>   specify all characters manually" },
{ "25", "         any lower-case character represents all" },
{ "26", "                        lower-case characters" },
{ "27", "         any upper-case character represents all" },
{ "28", "                        upper-case characters" },
{ "29", "         any digit      represents all digits" },
{ "30", "Password length" },
{ "31", "Number of digits" },
{ "32", "Number of special characters" },
{ "33", "Number of upper-case characters" },
{ "34", "Length undefined for \"" },
{ "35", "\"!" },
{ "36", "Number of digits undefined for \"" },
{ "37", "\"!" },
{ "38", "Number of special characters undefined for \"" },
{ "39", "\"!" },
{ "40", "Number of upper-case characters undefined for \"" },
{ "41", "\"!" },
{ "42", "PRNGs undefined for \"" },
{ "43", "\"!" },
{ "44", "Output mode undefined for \"" },
{ "45", "\"!" },
{ "46", "Password-only setting undefined for \"" },
{ "47", "\"!" },
{ "48", "Character set undefined for \"" },
{ "49", "\"!" },
{ "50",	"Preference \"" },
{ "51",	"\" contains an empty string." },
{ "52",	"No value found for \"" },
{ "53", "\"." },
{ "54",	"The type name \"" },
{ "55",	"\" is too long!" },
{ "56",	"The default type name \"" },
{ "57",	"\" is too long!" },
{ "58", "No default password type set!" },
{ "59",	"A password type is needed!" },
{ "60",	"No output character set defined!" },
{ "61", "Illegal password length 0!" },
{ "62", "Argument missing for long option \"" },
{ "63", "\"!" },
{ "64", "Output character set missing for long option \"--mode=charset\"" },
};
/**	Size of \a kv (number of entries).
*/
static size_t szkv = sizeof(kv)/sizeof(dk_key_value_t);



/**	Preferences keys.
*/
static char *pk[] = {
/*   0 */	"/password/",
/*   1 */	"/mode",
/*   2 */	"/charset",
/*   3 */	"/password/type",
/*   4 */	"default",
/*   5 */	"/suppress-name",
/*   6 */	"/prngs",
NULL
};



/**	Preferences keys for numeric values.
*/
static char *pk_numerics[] = {
/*   0 */	"/length",
/*   1 */	"/digits",
/*   2 */	"/specials",
/*   3 */	"/capitals",
NULL
};



/**	Patterns to compare output mode.
*/
static char *pk_modes[] = {
"d$efault", "h$ex", "a$scii85", "c$harset", NULL
};



/**	Output modes.
*/
static char *pk_modes_to_write[] = {
"default", "hex", "ascii85", "charset", NULL
};



/**	Long options.
*/
static char *long_options[] = {
/*  0 */	"h$elp",
/*  1 */	"v$ersion",
/*  2 */	"c$onfigure",
/*  3 */	"sh$ow-configuration",
/*  4 */	"un$configure",
/*  5 */	"r$eset",
/*  6 */	"l$ength",
/*  7 */	"d$igits",
/*  8 */	"sp$ecials",
/*  9 */	"up$per-case",
/* 10 */	"ou$tput",
/* 11 */	"pr$ng",
/* 12 */	"pa$ssword-only",
/* 13 */	"sil$ently",
NULL
};



/**	Names of PRNGs.
*/
static char *prngs_to_write[] = {
"all", "openssl", "random", "rand48", "rand", NULL
};



/**	Boolean values to write.
*/
static char *booleans_to_write[] = {
"off", "on", NULL
};



/**	Comma
*/
static char comma[] = { "," };



/**	Name of string table to search.
*/
static char string_table_name[] = { "createp" };



/**	String containing three spaces.
*/
static char three_spaces[] = { "   " };



/**	Hex numbers.
*/
static char hex_values[] = { "0123456789ABCDEF" };



/**	Set of special characters.
*/
static char special_characters[] = { "^°!\"§$%&/()=?{[]}\\#'+*~-_.:,;<>|" };



/**	Version text.
*/
static char *version_text[] = {
"",
"createp, version 3 (part of the dktools collection, version " VERSNUMB ")",
"Copyright (C) 2002-2010   Dipl.-Ing. D. Krause",
"http://dktools.sourceforge.net",
"",
NULL
};


/**	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.",
NULL
};


/**	Section head ``Libraries used'' used to show license.
*/
static char *libraries_used[]= {
"",
"Libraries used:",
"",
NULL
};

#if DK_HAVE_OPENSSL_RAND_H
/**	Text referring to OpenSSL shown in license.
*/
static char *ossl_txt[] = {
"OpenSSL      The OpenSSL cryptographic library",
"\t     http://www.openssl.org",
NULL
};
#endif



/**	Default help text if help text file is not found.
*/
static char *help_txt[] = {
"",
"createp - Password suggestor",
"============================",
"",
"Usage",
"-----",
""
"createp -c -t <name> <options>",
"	configures a password type.",
"",
"createp -C",
"	shows the current configuration.",
"",
"createp -u",
"	removes the permanent options.",
"",
"createp -h",
"	shows this help text.",
""
"createp -v",
"	shows the version information.",
"",
"createp -t <name> [<options>] <username(s)>",
"	suggests passwords.",
"",
"",
"Options",
"-------",
"",
"-l <number>",
"-l <number>-<number>",
"	chooses the password length.",
"",
"-o <mode>",
"	chooses the output mode, one of:",
"	default         specify number of digits, special and uppercase chars",
"	hex             output hex digits (0-9a-f)",
"	ascii85         use all characters from 33 ... 127",
"	charset <set>   specify a set of possible output characters manually",
"",
"-d <number>",
"-d <number>-<number>",
"	chooses the number of digits in the password.",
"	(for default output mode only)",
"",
"-k <number>",
"-k <number>-<number>",
"	chooses the number of upper-case characters in the password.",
"	(for default output mode only)",
"",
"-s <number>",
"-s <number>-<number>",
"	chooses the number of special characters in the password.",
"	(for default output mode only)",
"",
"-a <prng-algorithms>",
"	chooses a set of possible PRNGs, either \"all\" (attempt all supported",
"	PRNGs) or a combination of:",
"	openssl		the OpenSSL PRNG (the one you should prefer)",
"	random		the PRNG using initstate()/setstate()/random()",
"	rand48		the PRNG using nrand48()",
"	rand		the PRNG using rand()",
"",
"-p",
"	suppresses user name output, prints password suggestions only.",
"",
NULL
};



/**	Debug message.
	@param	c	Createp job.
	@param	n	Index of message text in message text array.
*/
static
void
debug_1 DK_P2(CMD *,c, size_t,n)
{
  char *logmsgs[2];
  logmsgs[0] = (c->msg)[n];
  logmsgs[1] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_DEBUG, logmsgs, 1);
}



/**	Debug message consisting of three parts.
	@param	c	Createp job.
	@param	n1	Index of first part in message text array.
	@param	n2	Index of third part in message text array.
	@param	s	Second part (i.e. file name, user name).
*/
static
void
debug_3 DK_P4(CMD *,c, size_t,n1, size_t,n2, char*,s)
{
  char *logmsgs[4];
  logmsgs[0] = (c->msg)[n1];
  logmsgs[1] = s;
  logmsgs[2] = (c->msg)[n2];
  logmsgs[3] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_DEBUG, logmsgs, 3);
}



/**	Warning message consisting of three parts.
	@param	c	Createp job.
	@param	n1	Index of first part in message text array.
	@param	n2	Index of third part in message text array.
	@param	s	Second part (i.e. file name, user name).
*/
static
void
warn_3 DK_P4(CMD *,c, size_t,n1, size_t,n2, char*,s)
{
  char *logmsgs[4];
  logmsgs[0] = (c->msg)[n1];
  logmsgs[1] = s;
  logmsgs[2] = (c->msg)[n2];
  logmsgs[3] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_WARNING, logmsgs, 3);
}



/**	Error message.
	@param	c	Createp job.
	@param	n	Index of message text in message text array.
*/
static
void
err_1 DK_P2(CMD *,c, size_t,n)
{
  char *logmsgs[2];
  logmsgs[0] = (c->msg)[n];
  logmsgs[1] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_ERROR, logmsgs, 1);
}



/**	Error message consisting of three parts.
	@param	c	Createp job.
	@param	n1	Index of first part in message text array.
	@param	n2	Index of third part in message text array.
	@param	s	Second part (i.e. file name, user name).
*/
static
void
err_3 DK_P4(CMD *,c, size_t,n1, size_t,n2, char*,s)
{
  char *logmsgs[4];
  logmsgs[0] = (c->msg)[n1];
  logmsgs[1] = s;
  logmsgs[2] = (c->msg)[n2];
  logmsgs[3] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_ERROR, logmsgs, 3);
}



/**	Compare two name entries by order of
	appearance in the command line.
	This function is used to build the sorted
	storage.
	@param	l	Left entry.
	@param	r	Right entry.
	@param	cr	Comparison criteria (ignored).
	@return	-1, 0 or 1 (comparison result).
*/
static
int
compare_names DK_P3(void *,l, void *,r, int,cr)
{
  int back = 0;
  NAME *lname, *rname;

  lname = (NAME *)l; rname = (NAME *)r;
  if(lname) {
    if(rname) {
      if(lname->number > rname->number) {
        back = 1;
      } else {
        if(lname->number < rname->number) {
	  back = -1;
	}
      }
    } else {
      back = 1;
    }
  } else {
    if(rname) {
      back = -1;
    }
  }
  return back;
}



/**	Check whether to run absolutely silently.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@param	rs	Pointer to result variable (silent).
	@param	rf	Pointer to result variable (filter).
*/
static
void
silence_check DK_P4(int,argc, char **,argv, int *,rs, int *,rf)
{
  int i; char *ptr, **lfdptr;
  int myrs = 0;
  lfdptr = argv; lfdptr++; i = 1;
  
  while(i < argc) {
    ptr = *lfdptr;
    if(*ptr == '-') {
      ptr++;
      if(*ptr == '-') {
        ptr++;
	switch(dkstr_array_abbr(long_options, ptr, '$', 0)) {
	  case 13: myrs = 1; break;
	}
      } else {
        switch(*ptr) {
	  case 't': case 'l': case 'd': case 's': case 'k': case 'o':
	  {
	    ptr++;
	    if(!(*ptr)) {
	      lfdptr++; i++;
	    }
	  } break;
	  case 'S': {
	    myrs = 1;
	  } break;
	}
      }
    }
    lfdptr++; i++;
  }
  if(rs) { *rs = myrs; }
  
}



/**	Set default values in CMD.
	@param	c	Createp job.
*/
static
void
cmd_reset DK_P1(CMD *,c)
{
  
  c->rand_types_allowed = DK_RAND_TYPE_ALL;
  c->program_mode = PROGRAM_MODE_DEFAULT;
  c->l_min = 6; c->l_max = 6;
  c->d_min = 2; c->d_max = 2;
  c->s_min = 0; c->s_max = 0;
  c->u_min = 0; c->u_max = 0;
  c->exval = 1;
  c->f_cmd_rand_types_allowed = 0x00;
  c->f_cmd_mode = 0x00;
  c->f_cmd_l = 0x00;
  c->f_cmd_d = 0x00;
  c->f_cmd_s = 0x00;
  c->f_cmd_u = 0x00;
  c->f_cmd_charset = 0x00;
  c->pw_only = 0x00;
  c->f_pw_only = 0x00;
  
}



/**	Reset the CMD to default values if -r occurs on the command line.
	@param	c	Createp job.
*/
static
void
option_reset DK_P1(CMD *,c)
{
  
  cmd_reset(c);
  (c->b_type)[0] = '\0';
  
}



/**	Initialize a CMD struct.
	@param	c	Createp job.
*/
static
void
cmd_init DK_P1(CMD *,c)
{
  
  c->program_action = ACTION_RUN;
  c->cs_buffer = NULL; c->u_cs_buffer = 0;
  c->b_cs_buffer = NULL;
  c->s_names = NULL; c->i_names = NULL;
  c->b_type = NULL; c->s_b_type = 0;
  cmd_reset(c);
  
}



/**	Prepare a CMD struct for usage, set up data structures.
	@param	c	Createp job.
	@return	1 on success, 0 on error.
*/
static
int
prepare_cmd DK_P1(CMD *,c)
{
  int back = 0;
  
  c->s_names = dksto_open(0);
  if(c->s_names) {
    dksto_set_comp(c->s_names, compare_names, 0);
    c->i_names = dksto_it_open(c->s_names);
    if(c->i_names) {
      c->b_cs_buffer = dkbf_open(256);
      if(c->b_cs_buffer) {
        dkbf_reset(c->b_cs_buffer);
        back = 1;
      } else {				
	err_1(c, 0);
      }
    } else {				
      err_1(c, 0);
    }
  } else {				
    err_1(c, 0);
  } 
  return back;
}



/**	Clean up the CMD structure, release dynamic memory.
	@param	c	Createp job.
*/
static
void
cleanup_cmd DK_P1(CMD *,c)
{
  NAME *nptr;
  
  if(c->s_names) {
    if(c->i_names) {
      dksto_it_reset(c->i_names);
      while((nptr = (NAME *)dksto_it_next(c->i_names)) != NULL) {
        if(nptr->name) {	
	  dk_delete(nptr->name);
	}
	nptr->name = NULL; nptr->number = 0UL;
	dk_delete(nptr);
      }
      dksto_it_close(c->i_names);
    }
    dksto_close(c->s_names);
  }
  if(c->b_cs_buffer) {
    dkbf_close(c->b_cs_buffer);
  }	
}



/**	Retrieve one password-type-specific preference.
	@param	c	Createp job.
	@param	k	Preference sub name (/type/xxx/kkk).
	@param	b	Result buffer.
	@param	sz	Size of \a b.
	@return	Pointer (b) on success, NULL on error.
*/
static
char *
get_pw_type_pref DK_P4(CMD *,c, char *,k, char *,b, size_t,sz)
{
  char *back = NULL;
  char kb[DEFAULT_BUFFER_LENGTH];
  if(strlen(c->b_type) > 0) {
    if((strlen(pk[0]) + strlen(c->b_type) + strlen(k)) < sizeof(kb)) {
      strcpy(kb, pk[0]); strcat(kb, c->b_type); strcat(kb, k);
      if(dkapp_get_pref(c->a, kb, b, sz, 0)) {
        back = dkstr_start(b, NULL);
	if(back) {
	  dkstr_chomp(back, NULL);
	} else {
	  debug_3(c, 50, 51, kb);
	}
      } else {
	debug_3(c, 52, 53, kb);
      }
    } else {
      err_3(c, 54, 55, c->b_type);
    }
  } else {	
    /* INTERNAL ERROR: No type defined, should not happen */
  }
  return back;
}



/**	Convert text to a number or to a range of numbers.
	@param	min	Pointer to result minimum.
	@param	max	pointer to result maximum.
	@param	s	String to convert.
	@return	1 on success, 0 on error.
*/
static
int
convert_text_to_numbers DK_P3(unsigned *,min, unsigned *,max, char *,s)
{
  int back = 0;
  unsigned u1, u2;
  char *ptr;
  
  ptr = dkstr_chr(s, '-');
  if(ptr) {
    *(ptr++) = '\0';
    if(sscanf(s, "%u", &u1) == 1) {
      back = 1;
      *min = u1; *max = u1;
      if(sscanf(ptr, "%u", &u2) == 1) {
        if(u1 < u2) {
	  *max = u2;
	} else {
	  *min = u2; *max = u1;
	}
      }
    }
  } else {
    if(sscanf(s, "%u", &u1) == 1) {
      back = 1;
      *min = u1; *max = u1;
    }
  } 
  return back;
}



/**	Retrieve numeric value or range from preferences.
	@param	c	Createp job.
	@param	f	Flag: already found on command line.
	@param	min	Pointer to minimum result buffer.
	@param	max	Pointer to maximum result buffer.
	@param	ind	Type: 0=length, 1=digits, 2=specials, 3=uppercase.
*/
static
void
get_numerics DK_P5(CMD *,c,unsigned char,f,unsigned *,min,unsigned *,max, unsigned,ind)
{
  char valbuffer[DEFAULT_BUFFER_LENGTH], x[sizeof(valbuffer)], *ptr;
  
  if(!f) {
    ptr = get_pw_type_pref(c, pk_numerics[ind], valbuffer, sizeof(valbuffer));
    if(ptr) {
      strcpy(x, ptr);
      if(!convert_text_to_numbers(min, max, ptr)) {
        warn_3(c, 3, 4, x);
      }
    } else {
      if(!((c->program_action) & ACTION_CONFIGURE)) {
	switch(ind) {
	  case 0: {
	    warn_3(c, 34, 35, c->b_type);
	  } break;
	  case 1: {
	    warn_3(c, 36, 37, c->b_type);
	  } break;
	  case 2: {
	    warn_3(c, 38, 39, c->b_type);
	  } break;
	  case 3: {
	    warn_3(c, 40, 41, c->b_type);
	  } break;
	}
      }
    }
  } 
}



/**	Retrieve output mode from string.
	@param	c	Createp job.
	@param	s	String containing output mode.
	@return	1 on success, 0 on error.
*/
static
int
get_mode_from_string DK_P2(CMD *,c, char *,s)
{
  int back = 0;
  int i;
  
  i = dkstr_array_abbr(pk_modes, s, '$', 0);
  if(i > -1) {
    c->program_mode = i; back = 1;
  } 
  return back;
}



/**	Retrieve output mode from preferences.
	@param	c	Createp job.
	@return	1 on success, 0 on error.
*/
static
int
get_mode DK_P1(CMD *,c)
{
  int back = 1;
  char valbuffer[DEFAULT_BUFFER_LENGTH], *ptr1;
  
  if(!(c->f_cmd_mode)) {
    ptr1 = get_pw_type_pref(c, pk[1], valbuffer, sizeof(valbuffer));
    if(ptr1) {
      back = get_mode_from_string(c, ptr1);
      if(!back) {
        warn_3(c, 5, 6, ptr1);
      }
    } else {
      if(!((c->program_action) & ACTION_CONFIGURE)) {
	warn_3(c, 44, 45, c->b_type);
      }
    }
  } 
  return back;
}



/**	Retrieve allowed output character set from a string.
	@param	c	Createp job.
	@param	s	String containing allowed output characters.
*/
static
void
get_charset_from_string DK_P2(CMD *,c, char *,s)
{
  char cc, *ptr1;
  unsigned char uc, *uptr;
  unsigned u;
  
  ptr1 = s;
  uptr = c->cs_buffer;
  c->u_cs_buffer = 0;
  dkbf_reset(c->b_cs_buffer);
  
  while(*ptr1) {
    cc = *(ptr1++);
    
    uc = (unsigned char)cc;
    u = (unsigned)uc;
    u = (u & 255);
    switch(cc) {
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
      {
        
        for(cc = '0'; cc <= '9'; cc++) {
          uc = (unsigned char)cc; u = (unsigned)uc;
          u &= 255;
          if(!dkbf_get(c->b_cs_buffer, u)) {
            c->u_cs_buffer += 1;
            *(uptr++) = uc;
            dkbf_set(c->b_cs_buffer, u, 1);
          }
        }
      } break;
      case 'a': case 'b': case 'c': case 'd': case 'e':
      case 'f': case 'g': case 'h': case 'i': case 'j':
      case 'k': case 'l': case 'm': case 'n': case 'o':
      case 'p': case 'q': case 'r': case 's': case 't':
      case 'u': case 'v': case 'w': case 'x': case 'y':
      case 'z':
      {
        
        for(cc = 'a'; cc <= 'z'; cc++) {
          uc = (unsigned char)cc; u = (unsigned)uc;
          u &= 255;
          if(!dkbf_get(c->b_cs_buffer, u)) {
            c->u_cs_buffer += 1;
            *(uptr++) = uc;
            dkbf_set(c->b_cs_buffer, u, 1);
          }
        }
      } break;
      case 'A': case 'F': case 'K': case 'P': case 'U':
      case 'B': case 'G': case 'L': case 'Q': case 'V':
      case 'C': case 'H': case 'M': case 'R': case 'W':
      case 'D': case 'I': case 'N': case 'S': case 'X':
      case 'E': case 'J': case 'O': case 'T': case 'Y':
      case 'Z':
      {
        
        for(cc = 'A'; cc <= 'Z'; cc++) {
          uc = (unsigned char)cc; u = (unsigned)uc;
          u &= 255;
          if(!dkbf_get(c->b_cs_buffer, u)) {
            c->u_cs_buffer += 1;
            *(uptr++) = uc;
            dkbf_set(c->b_cs_buffer, u, 1);
          }
        }
      } break;
      default:
      {
        
        if(!dkbf_get(c->b_cs_buffer, u)) {
          if((c->u_cs_buffer) < 256) {
            c->u_cs_buffer += 1;
            *uptr++ = uc;
            dkbf_set(c->b_cs_buffer, u, 1);
          }
        }
      } break;
    }
  } 
  
}



/**	Retrieve allowed output character set from preferences.
	@param	c	Createp job.
	@return	1 on success, 0 on error.
*/
static
int
get_charset DK_P1(CMD *,c)
{
  int back = 0;
  char valbuffer[DEFAULT_BUFFER_LENGTH], *ptr1;
  
  if(!(c->f_cmd_charset)) {
    ptr1 = get_pw_type_pref(c, pk[2], valbuffer, sizeof(valbuffer));
    if(ptr1) {
      get_charset_from_string(c, ptr1);
      if(c->u_cs_buffer) {
        back = 1;
      } else {
        warn_3(c, 7, 8, ptr1);
      }
    } else {
      if(!((c->program_action) & ACTION_CONFIGURE)) {
	warn_3(c, 48, 48, c->b_type);
      }
    }
  } 
  return back;
}



/**	Check whether to print user name / passord pair to output
	or just the password.
	@param	c	Createp job.
*/
static
void
get_pw_only DK_P1(CMD *,c)
{
  char valbuffer[DEFAULT_BUFFER_LENGTH], *ptr1;
  if(!(c->f_pw_only)) {
    ptr1 = get_pw_type_pref(c, pk[5], valbuffer, sizeof(valbuffer));
    if(ptr1) {
      if(dkstr_is_bool(ptr1)) {
        if(dkstr_is_on(ptr1)) {
	  c->pw_only = 0x01;
	} else {
	  c->pw_only = 0x00;
	}
      } else {
        warn_3(c, 9, 10, ptr1);
      }
    } else {
      if(!((c->program_action) & ACTION_CONFIGURE)) {
	warn_3(c, 46,47, c->b_type);
      }
    }
  }
}



/**	Retrieve allowed PRNG types from preferences.
	@param	c	Createp job.
*/
static
void
get_rand_types_allowed_for_type DK_P1(CMD *,c)
{

  char valbuffer[DEFAULT_BUFFER_LENGTH], *ptr1;
  if(!(c->f_cmd_rand_types_allowed)) {
    ptr1 = get_pw_type_pref(c, pk[6], valbuffer, sizeof(valbuffer));
    if(ptr1) {
      c->rand_types_allowed = dkapp_rand_types_from_string(c->a, ptr1);
    } else {
      if(!((c->program_action) & ACTION_CONFIGURE)) {
	warn_3(c, 42, 43, c->b_type);
      }
    }
  }
}



/**	Get missing attributes for required password type from preferences.
	@param	c	Createp job.
	@return	1 on success, 0 on error.
*/
static
int
configure_for_type DK_P1(CMD *,c)
{
  int back = 1;
  
  get_rand_types_allowed_for_type(c);
  get_numerics(c, c->f_cmd_l, &(c->l_min), &(c->l_max), 0);
  get_mode(c);
  get_pw_only(c);
  switch(c->program_mode) {
    case PROGRAM_MODE_DEFAULT: {
      get_numerics(c, c->f_cmd_d, &(c->d_min), &(c->d_max), 1);
      get_numerics(c, c->f_cmd_s, &(c->s_min), &(c->s_max), 2);
      get_numerics(c, c->f_cmd_u, &(c->u_min), &(c->u_max), 3);
    } break;
    case PROGRAM_MODE_SET: {
      get_charset(c);
    } break;
  } 
  return back;
}



/**	Load defaults from the preferences system.
	Use built-in defaults if preferences are
	not yet set.
	@param	c	Createp job.
*/
static
void
load_defaults_from_preferences DK_P1(CMD *,c)
{
  int found = 0;
  char valbuffer[DEFAULT_BUFFER_LENGTH], *ptr1;
  
  if(dkapp_get_pref(c->a, pk[3], valbuffer, sizeof(valbuffer), 0)) {
    ptr1 = dkstr_start(valbuffer, NULL);
    if(ptr1) {
      dkstr_chomp(ptr1, NULL);
      if(strlen(ptr1) < c->s_b_type) {
        strcpy(c->b_type, ptr1); found = 1;
      } else {
	warn_3(c, 56, 57, ptr1);
      }
    } else {
      debug_1(c, 58);
    }
  } else {
    debug_1(c, 58);
  }
  if(!found) {
    if(strlen(pk[4]) < c->s_b_type) {
      strcpy(c->b_type, pk[4]); found = 1;
    }
  }
  if(found) {
    configure_for_type(c);
  } 
}



/** Process the command line arguments.
	@param	c	Createp job.
	@return	1 on success, 0 on error.
*/
static
int
process_arguments DK_P1(CMD *,c)
{
  int back = 1;
  unsigned long namenumber = 0UL;
  int xargc, i;
  char argbuffer[256], *ptr, **xargv, **lfdptr, *optptr, *vptr;
  NAME *nptr;
  
  load_defaults_from_preferences(c);
  xargc = dkapp_get_argc(c->a);
  xargv = dkapp_get_argv(c->a);
  lfdptr = xargv; lfdptr++; i = 1;
  while(i < xargc) {
    ptr = *lfdptr;
    switch(*ptr) {
      case '-': {
        optptr = ptr;
        ptr++;
	switch(*ptr) {
	  case '-': {	
	    ptr++;
	    if(strlen(ptr) < sizeof(argbuffer)) {
	      strcpy(argbuffer, ptr);
	      vptr = dkstr_chr(argbuffer, '=');
	      if(vptr) {
	        *(vptr++) = '\0';
		vptr = dkstr_start(vptr, NULL);
		if(vptr) {
		  dkstr_chomp(vptr, NULL);
		}
	      }	
	      switch(dkstr_array_abbr(long_options, argbuffer, '$', 0)) {
	        case 0: {	
		  c->program_action |= ACTION_HELP;
		} break;
	        case 1: {	
		  c->program_action |= ACTION_VERSION;
		} break;
	        case 2: {	
		  c->program_action |= ACTION_CONFIGURE;
		} break;
	        case 3: {	
		  c->program_action |= ACTION_SHOWCONF;
		} break;
	        case 4: {	
		  c->program_action |= ACTION_UNCONFIGURE;
		} break;
	        case 5: {	
		  option_reset(c);
		} break;
	        case 6: {	
		  if(vptr) {
		    if(convert_text_to_numbers(&(c->l_min), &(c->l_max), vptr)) {
		      c->f_cmd_l = 0x01;
		    } else {
		      warn_3(c, 3, 4, vptr);
		    }
		  } else {
		    err_3(c, 62, 63, ptr);
		  }
		} break;
	        case 7: {	
		  if(vptr) {
		    if(convert_text_to_numbers(&(c->d_min), &(c->d_max), vptr)) {
		      c->f_cmd_d = 0x01;
		    } else {
		      warn_3(c, 3, 4, vptr);
		    }
		  } else {
		    err_3(c, 62, 63, optptr);
		  }
		} break;
	        case 8: {	
		  if(vptr) {
		    if(convert_text_to_numbers(&(c->s_min), &(c->s_max), vptr)) {
		      c->f_cmd_s = 0x01;
		    } else {
		       warn_3(c, 3, 4, vptr);
		    }
		  } else {
		    err_3(c, 62, 63, optptr);
		  }
		} break;
	        case 9: {	
		  if(vptr) {
		    if(convert_text_to_numbers(&(c->u_min), &(c->u_max), vptr)) {
		      c->f_cmd_u = 0x01;
		    } else {
		      warn_3(c, 3, 4, vptr);
		    }
		  } else {
		    err_3(c, 62, 63, optptr);
		  }
		} break;
	        case 10: {	
		  if(vptr) {
		    char *xptr;
		    xptr = dkstr_chr(vptr, ':');
		    if(xptr) { *(xptr++) = '\0'; }
		    if(get_mode_from_string(c, vptr)) {
		      c->f_cmd_mode = 0x01;
		      switch(c->program_mode) {
		        case PROGRAM_MODE_SET: {
			  if(xptr) {
			    get_charset_from_string(c, xptr);
			    c->f_cmd_charset = 0x01;
			    if(!(c->u_cs_buffer)) {
			      warn_3(c, 7, 8, xptr);
			    }
			  } else {
			    err_1(c, 64);
			  }
			} break;
		      }
		    } else {
		      err_3(c, 5, 6, vptr);
		    }
		  } else {
		    err_3(c, 62, 63, optptr);
		  }
		} break;
	        case 11: {	
		  if(vptr) {
		    c->rand_types_allowed =
		    dkapp_rand_types_from_string(c->a, vptr);
		    c->f_cmd_rand_types_allowed = 0x01;
		  } else {
		    err_3(c, 62, 63, optptr);
		  }
		} break;
	        case 12: {	
		  if(vptr) {
		    if(dkstr_is_bool(vptr)) {
		      if(dkstr_is_on(vptr)) {
		        c->pw_only = 0x01;
		      } else {
		        c->pw_only = 0x00;
		      }
		      c->f_pw_only = 0x01;
		    } else {
		      err_3(c, 9, 10, vptr);
		    }
		  } else {
		    c->pw_only = 0x01; c->f_pw_only = 0x01;
		  }
		} break;
	      }
	    } else {					
	      err_3(c, 1, 2, optptr); back = 0;
	    }
	  } break;
	  case 'a': {	
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL; lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(strlen(ptr) < sizeof(argbuffer)) {
	        strcpy(argbuffer, ptr);
	        c->rand_types_allowed =
		dkapp_rand_types_from_string(c->a, argbuffer);
		c->f_cmd_rand_types_allowed = 0x01;
	      } else {
	        err_3(c, 1, 2, ptr); back = 0;
	      }
	    }
	  } break;
	  case 't': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL; lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(strlen(ptr) < c->s_b_type) {
	        strcpy(c->b_type, ptr);
		if(!configure_for_type(c)) {
		  back = 0;
		}
	      }
	    }
	  } break;
	  case 'p': {
	    ptr++;
	    if(*ptr == '-') {
	      c->pw_only = 0x00;
	    } else {
	      c->pw_only = 0x01;
	    }
	    c->f_pw_only = 0x01;
	  } break;
	  case 'o': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL; lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	    if(strlen(ptr) < sizeof(argbuffer)) {
	      strcpy(argbuffer, ptr);
	      if(get_mode_from_string(c, argbuffer)) {
	        c->f_cmd_mode = 0x01;
	        switch(c->program_mode) {
		  case PROGRAM_MODE_SET: {
		    lfdptr++; i++;
		    if(i < xargc) {
		      ptr = *lfdptr;
		      if(strlen(ptr) < sizeof(argbuffer)) {
		        strcpy(argbuffer, ptr);
		        get_charset_from_string(c, argbuffer);
		        c->f_cmd_charset = 0x01;
			if(!(c->u_cs_buffer)) {
			  warn_3(c, 7, 8, argbuffer);
			}
		      } else {		
			err_3(c, 1, 2, ptr); back = 0;
		      }
		    }
		  } break;
		}
	      } else {			
		err_3(c, 5, 6, argbuffer);
	      }
	    }
	    }
	  } break;
	  case 'l': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL; lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(strlen(ptr) < sizeof(argbuffer)) {
	        strcpy(argbuffer, ptr);
	        if(convert_text_to_numbers(&(c->l_min), &(c->l_max), argbuffer)) {
	          c->f_cmd_l = 0x01;
	        } else {
		  warn_3(c, 3, 4, ptr);
		}
	      } else {
	        /* ERROR: String too long */
		err_3(c, 1, 2, ptr); back = 0;
	      }
	    }
	  } break;
	  case 'd': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL; lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(strlen(ptr) < sizeof(argbuffer)) {
	        strcpy(argbuffer, ptr);
	        if(convert_text_to_numbers(&(c->d_min), &(c->d_max), argbuffer)) {
	          c->f_cmd_d = 0x01;
	        } else {
		  warn_3(c, 3, 4, ptr);
		}
	      } else {
	        /* ERROR STRING too long */
		err_3(c, 1, 2, ptr); back = 0;
	      }
	    }
	  } break;
	  case 's': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL; lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(strlen(ptr) < sizeof(argbuffer)) {
	        strcpy(argbuffer, ptr);
	        if(convert_text_to_numbers(&(c->s_min), &(c->s_max), argbuffer)) {
	          c->f_cmd_s = 0x01;
	        } else {
		  warn_3(c, 3, 4, ptr);
		}
	      } else {
	        /* ERROR: String too long */
		err_3(c, 1, 2, ptr); back = 0;
	      }
	    }
	  } break;
	  case 'k': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL; lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(strlen(ptr) < sizeof(argbuffer)) {
	        strcpy(argbuffer, ptr);
	        if(convert_text_to_numbers(&(c->u_min), &(c->u_max), argbuffer)) {
	          c->f_cmd_u = 0x01;
	        } else {
		  warn_3(c, 3, 4, ptr);
		}
	      } else {
	        /* ERROR: String too long */
		err_3(c, 1, 2, ptr); back = 0;
	      }
	    }
	  } break;
	  case 'r': {
	    option_reset(c);
	  } break;
	  case 'S': { } break;
	  case 'h': { c->program_action |= ACTION_HELP; } break;
	  case 'v': { c->program_action |= ACTION_VERSION; } break;
	  case 'c': { c->program_action |= ACTION_CONFIGURE; } break;
	  case 'C': { c->program_action |= ACTION_SHOWCONF; } break;
	  case 'u': { c->program_action |= ACTION_UNCONFIGURE; } break;
	}
      } break;
      default: {
        nptr = dk_new(NAME,1);
	if(nptr) {
	  nptr->name = NULL;
	  nptr->number = namenumber++;
	  nptr->name = dkstr_dup(ptr);
	  if(nptr->name) {
	    if(!dksto_add(c->s_names, nptr)) {
	      /* ERROR: Memory */
	      err_1(c, 0);
	      back = 0;
	      dk_delete(nptr->name);
	      nptr->name = NULL;
	      nptr->number = 0UL;
	      dk_delete(nptr); nptr = NULL;
	    }
	  } else {
	    /* ERROR: Memory */
	    err_1(c, 0);
	    back = 0;
	    dk_delete(nptr); nptr = NULL;
	  }
	} else {
	  /* ERROR: Memory */
	  err_1(c, 0);
	  back = 0;
	}
      } break;
    }
    lfdptr++; i++;
  } 
  return back;
}



/**	Print version number.
	@param	c	Createp job.
*/
static
void
print_version DK_P1(CMD *,c)
{
  char **ptr;
  
  ptr = version_text;
  while(*ptr) { fputs(*(ptr++), stdout); fputc('\n', stdout); }
  ptr = license_terms;
  while(*ptr) { fputs(*(ptr++), stdout); fputc('\n', stdout); }
  ptr = libraries_used;
  while(*ptr) { fputs(*(ptr++), stdout); fputc('\n', stdout); }
  ptr = dklic_get();
  while(*ptr) { fputs(*(ptr++), stdout); fputc('\n', stdout); }
#if DK_HAVE_OPENSSL_RAND_H
  ptr = ossl_txt;
  while(*ptr) { fputs(*(ptr++), stdout); fputc('\n', stdout); }
#endif
  
}



/**	Print help text.
	@param	c	Createp job.
*/
static
void
print_help DK_P1(CMD *,c)
{
  
  dkapp_help(c->a, "createp.txt", help_txt);
  
}



/**	Convert allowed character set to short description.
	@param	c	Createp job.
	@param	vb	Result buffer.
	@param	szvb	Size of \a vb.
*/
static
void
charset_to_short DK_P3(CMD *,c, char *,vb, size_t,szvb)
{
  char *vptr;
  size_t used, i;
  unsigned char have_small, have_large, have_digits;
  

  DK_MEMRES(vb,szvb) ;
  vptr = vb; used = 0;
  have_small = have_large = have_digits = 0x00;
  for(i = 0; ((i < c->u_cs_buffer) && (used < szvb)); i++) {
    if(((c->cs_buffer)[i] >= 'a') && ((c->cs_buffer)[i] <= 'z')) {
      if(!have_small) {
        have_small = 0x01;
        *(vptr++) = 'a'; used++;
      }
    } else {
      if(((c->cs_buffer)[i] >= 'A') && ((c->cs_buffer)[i] <= 'Z')) {
        if(!have_large) {
          have_large = 0x01;
          *(vptr++) = 'A'; used++;
        }
      } else {
        if(((c->cs_buffer)[i] >= '0') && ((c->cs_buffer)[i] <= '9')) {
          if(!have_digits) {
            have_digits = 0x01;
            *(vptr++) = '0'; used++;
          }
        } else {
          *(vptr++) = (c->cs_buffer)[i]; used++;
        }
      }
    }
  }
  if(used < szvb) {
    *vptr = '\0';
  } else {
    vb[szvb-1] = '\0';
  }
  
}



/**	Save password type attribute to preferences.
	@param	c	Createp job.
	@param	k	Key.
	@param	v	Value.
*/
static
void
save_pw_type_pref DK_P3(CMD *,c, char *,k, char *,v)
{
  char kb[DEFAULT_BUFFER_LENGTH];
  if(strlen(c->b_type) > 0) {
    if((strlen(pk[0]) + strlen(c->b_type) + strlen(k)) < sizeof(kb)) {
      strcpy(kb, pk[0]); strcat(kb, c->b_type); strcat(kb, k);
      dkapp_set_pref(c->a, kb, v);
    } else {
      err_3(c, 54, 55, c->b_type);
    }
  } else {
    err_1(c, 59);
  }
}



/**	Write number of range to buffer.
	@param	b	Result buffer.
	@param	min	Minimum value.
	@param	max	Maximum value.
*/
static
void
num_or_range_to_buffer DK_P3(char *,b, unsigned,min, unsigned,max)
{
  if(min < max) {
    sprintf(b, "%u-%u", min, max);
  } else {
    sprintf(b, "%u", min);
  }
}



/**	Add one allowed PRNG name to buffer.
	@param	c	Createp job.
	@param	b	Result buffer.
	@param	szb	Size of \a b.
	@param	r	PRNG ID.
	@param	i	Index of PRNG name in messages.
*/
static
void
add_one_prng DK_P5(CMD *,c, char *,b, size_t,szb, int,r, int,i)
{
  if((c->rand_types_allowed) & r) {
    if(strlen(b) > 0) {
      if((strlen(b) + strlen(comma) + strlen(prngs_to_write[i])) < szb) {
        strcat(b, comma);
	strcat(b, prngs_to_write[i]);
      }
    } else {
      if(strlen(prngs_to_write[i]) < szb) {
        strcpy(b, prngs_to_write[i]);
      }
    }
  }
}



/**	Write list of allowed PRNGs to buffer.
	@param	c	Createp job.
	@param	b	Buffer.
	@param	szb	Size of \a b.
*/
static
void
prngs_to_buffer DK_P3(CMD *,c, char *,b, size_t,szb)
{
  
  DK_MEMRES(b,szb) ;
  if((c->rand_types_allowed) == DK_RAND_TYPE_ALL) {
    if(strlen(prngs_to_write[0]) < szb) {
      strcpy(b, prngs_to_write[0]);
    }
  } else {
    add_one_prng(c, b, szb, DK_RAND_TYPE_OPENSSL, 1);
    add_one_prng(c, b, szb, DK_RAND_TYPE_STATE, 2);
    add_one_prng(c, b, szb, DK_RAND_TYPE_RAND48, 3);
    add_one_prng(c, b, szb, DK_RAND_TYPE_SIMPLE, 4);
  } 
}



/**	Save configuration to preferences.
	@param	c	Createp job.
*/
static
void
save_configuration DK_P1(CMD *,c)
{
  char valbuffer[DEFAULT_BUFFER_LENGTH];
  
  if(!strlen(c->b_type)) {
    if(strlen(pk[4]) < c->s_b_type) {
      strcpy(c->b_type, pk[4]);
    }
  }
  if(strlen(c->b_type)) {
    dkapp_set_pref(c->a, pk[3], c->b_type);
    /* PRNGs */
    prngs_to_buffer(c, valbuffer, sizeof(valbuffer));
    save_pw_type_pref(c, pk[6], valbuffer);
    /* length */
    num_or_range_to_buffer(valbuffer, c->l_min, c->l_max);
    save_pw_type_pref(c, pk_numerics[0], valbuffer);
    /* mode */
    save_pw_type_pref(c, pk[5], booleans_to_write[(c->pw_only) ? 1 : 0]);
    save_pw_type_pref(c, pk[1], pk_modes_to_write[c->program_mode]);
    /* password only */
    switch(c->program_mode) {
      case PROGRAM_MODE_DEFAULT: {
        /* digits */
	num_or_range_to_buffer(valbuffer, c->d_min, c->d_max);
	save_pw_type_pref(c, pk_numerics[1], valbuffer);
        /* specials */
	num_or_range_to_buffer(valbuffer, c->s_min, c->s_max);
	save_pw_type_pref(c, pk_numerics[2], valbuffer);
        /* upper-case */
	num_or_range_to_buffer(valbuffer, c->u_min, c->u_max);
	save_pw_type_pref(c, pk_numerics[3], valbuffer);
      } break;
      case PROGRAM_MODE_SET: {
        /* charset */
	charset_to_short(c, valbuffer, sizeof(valbuffer));
	save_pw_type_pref(c, pk[2], valbuffer);
      } break;
    }
  } 
}



/**	Get maximum value of two sizes.
	@param	s1	One size.
	@param	s2	Another size.
	@return	Maximum of \a s1 and \a s2.
*/
static
size_t
max_size DK_P2(size_t,s1,size_t,s2)
{
  size_t back;
  back = ((s1 > s2) ? s1 : s2);
  return back;
}



/**	Put text centered.
	@param	a	Application.
	@param	s	String to print.
	@param	lgts	Length of string printed on screen.
	@param	range	Available length to print string.
*/
static
void
put_centered DK_P4(dk_app_t *,a, char *,s, size_t,lgts, size_t,range)
{
  size_t i, j, k;
  if(lgts < range) {
    i = (range - lgts) / 2;
    j = range - lgts - i;
    for(k = 0; k < i; k++) fputc(' ', stdout);
    dkapp_stdout(a, s);
    for(k = 0; k < j; k++) fputc(' ', stdout);
  } else {
    dkapp_stdout(a, s);
  }

}



/**	Convert a line number to text describing the used PRNG.
	@param	i	Index of PRNG.
	@return	Index in message array containing PRNG name.
*/
static
size_t
line_number_to_prng_description DK_P1(size_t,i)
{
  size_t back = 15;
  switch(i) {
    case 1: back = 16; break;
    case 2: back = 17; break;
    case 3: back = 18; break;
  }
  return back;
}



/**	Show configuration.
	@param	c	Createp job.
*/
static
void
show_configuration DK_P1(CMD *,c)
{
  char bl[64], bd[64], bs[64], bu[64];
  char br[DEFAULT_BUFFER_LENGTH], bc[DEFAULT_BUFFER_LENGTH], *bm;
  char *suppress_name;
  size_t	sz, i, j, k;
  
  num_or_range_to_buffer(bl, c->l_min, c->l_max);
  num_or_range_to_buffer(bd, c->d_min, c->d_max);
  num_or_range_to_buffer(bs, c->s_min, c->s_max);
  num_or_range_to_buffer(bu, c->u_min, c->u_max);
  prngs_to_buffer(c, br, sizeof(br));
  if(c->program_mode == PROGRAM_MODE_SET) {
    charset_to_short(c, bc, sizeof(bc));
  } else {
    bc[0] = '\0';
  }
  bm = pk_modes_to_write[c->program_mode];
  suppress_name = (c->msg)[(c->pw_only) ? 11 : 12];
  
  sz = strlen(bm);
  if(c->rand_types_allowed == DK_RAND_TYPE_ALL) {
    sz = max_size(sz, strlen(br));
  }
  sz = max_size(sz, dkapp_prlen(c->a, c->b_type));
  sz = max_size(sz, strlen(bl));
  sz = max_size(sz, dkapp_prlen(c->a, suppress_name));
  sz = max_size(sz, strlen(bm));
  switch(c->program_mode) {
    case PROGRAM_MODE_DEFAULT: {
      sz = max_size(sz, strlen(bd));
      sz = max_size(sz, strlen(bs));
      sz = max_size(sz, strlen(bu));
    } break;
    case PROGRAM_MODE_SET: {
      sz = max_size(sz, strlen(bc));
    } break;
  }
  /* type */
  fputs("-t", stdout);
  fputs(three_spaces, stdout);
  put_centered(c->a, c->b_type, dkapp_prlen(c->a, c->b_type), sz);
  fputs(three_spaces, stdout);
  dkapp_stdout(c->a, (c->msg)[13]);
  fputc('\n', stdout);
  /* PRNGS */
  fputs("-a", stdout);
  fputs(three_spaces, stdout);
  if(c->rand_types_allowed == DK_RAND_TYPE_ALL) {
    put_centered(c->a, br, strlen(br), sz);
    fputs(three_spaces, stdout);
    dkapp_stdout(c->a, (c->msg)[14]);
  } else {
    fputs(br, stdout);
    fputc('\n', stdout);
    for(i = 0; i < sz + 8; i++) fputc(' ', stdout);
    dkapp_stdout(c->a, (c->msg)[14]);
  }
  fputc('\n', stdout);
  for(j = 0; j < 4; j++) {
    k = line_number_to_prng_description(j);
    for(i = 0; i < sz + 8; i++) fputc(' ', stdout);
    dkapp_stdout(c->a, (c->msg)[k]);
    fputc('\n', stdout);
  }
  /* output mode */
  fputs("-o", stdout);
  fputs(three_spaces, stdout);
  put_centered(c->a, bm, strlen(bm), sz);
  fputs(three_spaces, stdout);
  if(c->program_mode == PROGRAM_MODE_SET) {
    fputs(bc, stdout);
    fputc('\n', stdout);
    for(i = 0; i < sz + 8; i++) fputc(' ', stdout);
  }
  dkapp_stdout(c->a, (c->msg)[19]);
  fputc('\n', stdout);
  for(j = 0; j < 10; j++) {
    for(i = 0; i < sz + 8; i++) fputc(' ', stdout);
    dkapp_stdout(c->a, (c->msg)[20+j]);
    fputc('\n', stdout);
  }

  /* length */
  fputs("-l", stdout);
  fputs(three_spaces, stdout);
  put_centered(c->a, bl, strlen(bl), sz);
  fputs(three_spaces, stdout);
  dkapp_stdout(c->a, (c->msg)[30]);
  fputc('\n', stdout);
  switch(c->program_mode) {
    case PROGRAM_MODE_DEFAULT: {
      /* digits */
      fputs("-d", stdout);
      fputs(three_spaces, stdout);
      put_centered(c->a, bd, strlen(bd), sz);
      fputs(three_spaces, stdout);
      dkapp_stdout(c->a, (c->msg)[31]);
      fputc('\n', stdout);
      /* specials */
      fputs("-s", stdout);
      fputs(three_spaces, stdout);
      put_centered(c->a, bs, strlen(bs), sz);
      fputs(three_spaces, stdout);
      dkapp_stdout(c->a, (c->msg)[32]);
      fputc('\n', stdout);
      /* uppercase */
      fputs("-k", stdout);
      fputs(three_spaces, stdout);
      put_centered(c->a, bu, strlen(bu), sz);
      fputs(three_spaces, stdout);
      dkapp_stdout(c->a, (c->msg)[33]);
      fputc('\n', stdout);
    } break;
  }
  
}



/**	Create a random unsigned in a specified range.
	@param	c	Createp job
	@param	v	Pointer to result buffer.
	@param	min	Minimum value.
	@param	max	maximum value.
*/
static
void
create_unsigned DK_P4(CMD *,c, unsigned *,v, unsigned,min, unsigned,max)
{
  unsigned l;
  if(min == max) {
    *v = min;
  } else {
    dkapp_rand_bytes(c->a, (void *)(&l), sizeof(unsigned));
    l = min + (l % (1U + max - min));
    if(l < min) l = min;
    if(l > max) l = max;
    *v = l;
  }
}



/**	Create one password or WPA key using a specified output character set.
	@param	c	Createp job
	@param	n	User or host name.
*/
static
void
one_set DK_P2(CMD *,c, char *,n)
{
  unsigned long ul;
  unsigned i;
  size_t sz;
  create_unsigned(c, &(c->l), c->l_min, c->l_max);
  if((!(c->pw_only)) && (n)) {
    fputs(n, stdout); fputc(' ', stdout);
  }
  for(i = 0; i < c->l; i++) {
    dkapp_rand_bytes(c->a, (void *)(&ul), sizeof(unsigned long));
    ul = ul % (unsigned long)(c->u_cs_buffer);
    sz = (size_t)ul;
    if(sz >= c->u_cs_buffer) sz = c->u_cs_buffer - 1;
    fputc((c->cs_buffer)[sz], stdout);
  }
  fputc('\n', stdout);
}



/**	Create passwords or WPA keys using a specified
	output character set.
	@param	c	Createp job.
*/
static
void
do_set DK_P1(CMD *,c)
{
  unsigned char found;
  NAME *nptr;
  
  if(c->u_cs_buffer > 0) {
    found = 0x00;
    dksto_it_reset(c->i_names);
    while((nptr = (NAME *)dksto_it_next(c->i_names)) != NULL) {
      found = 0x01;
      one_set(c, nptr->name);
    }
    if(!found) {
      one_set(c, NULL);
    }
  } else {
    err_1(c, 60);
    c->exval = 1;
  }
  
}



/**	Create one password or WPA key using random data and ASCII-Hex
	encoding.
	@param	c	Createp job.
	@param	n	User or host name.
*/
static
void
one_hex DK_P2(CMD *,c, char *,n)
{
  unsigned long rval;
  unsigned i;
  size_t ind;
  size_t pos;
  size_t max;
  create_unsigned(c, &(c->l), c->l_min, c->l_max);
  if((!(c->pw_only)) && (n)) {
    fputs(n, stdout); fputc(' ', stdout);
  }
  dkapp_rand_bytes(c->a, (void *)(&rval), sizeof(unsigned long));
  pos = 0; max = 2 * sizeof(unsigned long);
  for(i = 0; i < c->l; i++) {
    ind = (size_t)(rval & 0x0FUL);
    fputc(hex_values[ind], stdout);
    rval = rval >> 4;
    pos++;
    if((pos >= max) && (i < (c->l - 1))) {
      dkapp_rand_bytes(c->a, (void *)(&rval), sizeof(unsigned long));
      pos = 0;
    }
  }
  fputc('\n', stdout);
}



/**	Create passwords or WPA keys using random data and ASCII-Hex encoding.
	@param	c	Createp job.
*/
static
void
do_hex DK_P1(CMD *,c)
{
  unsigned char found;
  NAME *nptr;
  
  found = 0x00;
  dksto_it_reset(c->i_names);
  while((nptr = (NAME *)dksto_it_next(c->i_names)) != NULL) {
    found = 0x01;
    one_hex(c, nptr->name);
  }
  if(!found) {
    one_hex(c, NULL);
  }
  
}



/**	Create one password or WPA key using random data and ASCII-85 encoding.
	@param	c	Createp job.
	@param	n	User or host name.
*/
static
void
one_ascii85 DK_P2(CMD *,c, char *,n)
{
  unsigned i;
  size_t pos, sz;
  unsigned long rval;
  char uc;
  create_unsigned(c, &(c->l), c->l_min, c->l_max);
  if((!(c->pw_only)) && (n)) {
    fputs(n, stdout); fputc(' ', stdout);
  }
  dkapp_rand_bytes(c->a, (void *)(&rval), sizeof(unsigned long));
  pos = 0;
  for(i = 0; i < c->l; i++) {
    sz = (size_t)(rval % 85);
    uc = (char)(33 + sz);
    fputc(uc, stdout);
    rval = rval / 85UL;
    pos++;
    if((pos >= 5) && (i < (c->l - 1))) {
      dkapp_rand_bytes(c->a, (void *)(&rval), sizeof(unsigned long));
      pos = 0;
    }
  }
  fputc('\n', stdout);
}



/**	Create passwords or WPA keys
	by getting random data and applying ASCII-85 encoding.
	@param	c	Create job.
*/
static
void
do_ascii85 DK_P1(CMD *,c)
{
  unsigned char found;
  NAME *nptr;
  
  found = 0x00;
  dksto_it_reset(c->i_names);
  while((nptr = (NAME *)dksto_it_next(c->i_names)) != NULL) {
    found = 0x01;
    one_ascii85(c, nptr->name);
  }
  if(!found) {
    one_ascii85(c, NULL);
  }
  
}




#line 2229 "createp.ctr"

#line 2230 "createp.ctr"

#line 2231 "createp.ctr"

#line 2232 "createp.ctr"

#line 2233 "createp.ctr"

#line 2234 "createp.ctr"

#line 2235 "createp.ctr"

#line 2236 "createp.ctr"

#line 2237 "createp.ctr"

#line 2238 "createp.ctr"

#line 2239 "createp.ctr"

#line 2240 "createp.ctr"

#line 2241 "createp.ctr"

#line 2242 "createp.ctr"

#line 2243 "createp.ctr"



/**	Create one password using the default algorithm.
	@param	c	Createp job.
	@param	n	User name.
	@param	p1	Pointer to result buffer.
	@param	p2	In: result buffer length, out: result buffer used.
*/
static
void
one_default DK_P4(CMD *,c, char *,n, unsigned char *,p1, size_t *,p2)
{
  unsigned i;
  size_t j, k, l, avail, sz;
  for(i = 0; i < c->l_max; i++) {
    p1[i] = 0x00;
    p2[i] = (size_t)i;
  }
  
#line 2263 "createp.ctr"
  create_unsigned(c, &(c->l), c->l_min, c->l_max);
  create_unsigned(c, &(c->d), c->d_min, c->d_max);
  create_unsigned(c, &(c->s), c->s_min, c->s_max);
  create_unsigned(c, &(c->u), c->u_min, c->u_max);
  if((!(c->pw_only)) && (n)) {
    fputs(n, stdout); fputc(' ', stdout);
  }
  avail = c->l;
  
#line 2272 "createp.ctr"
  if(c->d > 0) {
    for(j = 0; ((j < c->d) && (avail > 0)); j++) {
      dkapp_rand_bytes(c->a, (void *)(&k), sizeof(size_t));
      k = k % avail;	
      l = p2[k];
      p1[l] = 0x01;	
      while(k < (avail - 1)) { p2[k] = p2[k+1]; k++; }
      avail--;
      
#line 2281 "createp.ctr"
    }
  }
  if(c->s > 0) {
    for(j = 0; ((j < c->s) && (avail > 0)); j++) {
      dkapp_rand_bytes(c->a, (void *)(&k), sizeof(size_t));
      k = k % avail;	
      l = p2[k];
      p1[l] = 0x02;	
      while(k < (avail - 1)) { p2[k] = p2[k+1]; k++; }
      avail--;
      
#line 2292 "createp.ctr"
    }
  }
  if(c->u > 0) {
    for(j = 0; ((j < c->u) && (avail > 0)); j++) {
      dkapp_rand_bytes(c->a, (void *)(&k), sizeof(size_t));
      k = k % avail;	
      l = p2[k];
      p1[l] = 0x03;	
      while(k < (avail - 1)) { p2[k] = p2[k+1]; k++; }
      avail--;
      
#line 2303 "createp.ctr"
    }
  }
  for(i = 0; i < c->l; i++) {
    dkapp_rand_bytes(c->a, (void *)(&sz), sizeof(size_t));
    if(p1[i] == 0x03) {
      sz = sz % 26;
      fputc(((char)('A' + sz)), stdout);
    } else {
      if(p1[i] == 0x02) {
        sz = sz % (sizeof(special_characters) - 1);
	fputc(special_characters[sz], stdout);
      } else {
        if(p1[i] == 0x01) {
	  sz = sz % 10;
	  fputc(((char)('0' + sz)), stdout);
	} else {
	  sz = sz % 26;
	  fputc(((char)('a' + sz)), stdout);
	}
      }
    }
  }
  fputc('\n', stdout);
}



/**	Create and print passwords using the default algorithm.
	@param	c	Createp job.
*/
static
void
do_default DK_P1(CMD *,c)
{
  unsigned char *p1;
  size_t *p2;
  unsigned char found;
  NAME *nptr;
  
  if(c->l_max > 0) {
    p1 = dk_new(unsigned char,(c->l_max));
    if(p1) {
      p2 = dk_new(size_t,(c->l_max));
      if(p2) {
        found = 0x00;
	dksto_it_reset(c->i_names);
	while((nptr = (NAME *)dksto_it_next(c->i_names)) != NULL) {
	  found = 0x01;
	  one_default(c, nptr->name, p1, p2);
	}
	if(!found) {
	  one_default(c, NULL, p1, p2);
	}
        dk_delete(p2);
      } else {
	err_1(c, 0);
	c->exval = 1;
      }
      dk_delete(p1);
    } else {
      err_1(c, 0);
      c->exval = 1;
    }
  } else {
    err_1(c, 61);
    c->exval = 1;
  }
  
}



/**	Create the passwords.
	@param	c	Createp job.
*/
static
void
create_the_passwords DK_P1(CMD *,c)
{
  
  if(dkapp_rand_begin(c->a, c->rand_types_allowed)) {
    switch(c->program_mode) {
      case PROGRAM_MODE_SET: {
        do_set(c);
      } break;
      case PROGRAM_MODE_HEX: {
        do_hex(c);
      } break;
      case PROGRAM_MODE_ASCII85: {
        do_ascii85(c);
      } break;
      default: {
        do_default(c);
      } break;
    }
    if(!dkapp_rand_end(c->a)) {
      c->exval = 1;
      /* Failed to save seed file ..., reported by dkappr */
    }
  } else {
    c->exval = 1;
  }
  
}



/**	Apply corrections if length, number of digits,
	special and upper-case characters is inconsistent.
	@param	c	Createp job.
*/
static
void
correct_the_numbers DK_P1(CMD *,c)
{
  size_t minval;
  switch(c->program_mode) {
    case PROGRAM_MODE_DEFAULT: {
      minval = c->d_max + c->s_max + c->u_max;
      if(c->l_min < minval) {
        c->l_min = minval;
      }
      if(c->l_max < c->l_min) {
        c->l_max = c->l_min;
      }
    } break;
  }
}



/**	Run. Main has done the setup.
	@param	c	Createp job.
*/
static
void
run DK_P1(CMD *,c)
{
  
  if(prepare_cmd(c)) {
    if(process_arguments(c)) {
      correct_the_numbers(c);
      
      
      
      
      
      
      
      
      
      
      c->exval = 0;	/* no error yet */
      if(c->program_action == ACTION_RUN) {
        create_the_passwords(c);
      } else {
        if((c->program_action) & (ACTION_HELP | ACTION_VERSION)) {
	  print_version(c);
	  if((c->program_action) & ACTION_HELP) {
	    print_help(c);
	  }
	} else {
	  if((c->program_action) & (ACTION_CONFIGURE | ACTION_SHOWCONF)) {
	    if((c->program_action) & ACTION_CONFIGURE) {
	      save_configuration(c);
	    }
	    show_configuration(c);
	  } else {
	    if((c->program_action) & ACTION_UNCONFIGURE) {
	      dkapp_unconfigure(c->a);
	    }
	  }
	}
      }
    }
  }
  cleanup_cmd(c);
  
}



/**	Buffer to hold the output character set.
*/
static unsigned char char_set[256];



/**	Buffer to store the password type name.
*/
static char b_type[256];



/**	The createp programs main() function.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	0 on success, any other value indicates an error.
*/
#if DK_HAVE_PROTOTYPES
int main(int argc, char *argv[])
#else
int main(argc, argv) int argc; char *argv[];
#endif
{
  int exval = 1;
  int rs = 0;
  int rf = 1;
  CMD cmd;
  char **xptr;
  
#line 2513 "createp.ctr"

  
  silence_check(argc, argv, &rs, &rf);
  cmd_init(&cmd);
  cmd.cs_buffer = char_set;
  cmd.b_type = b_type; cmd.s_b_type = sizeof(b_type);
  b_type[0] = '\0';
  cmd.a = dkapp_open_ext1(argc, argv, grname, etcdir, rs, rf);
  if(cmd.a) {
    cmd.msg = dkapp_find_key_value(cmd.a, kv, szkv, string_table_name);
    if(cmd.msg) {
      run(&cmd);
      xptr = cmd.msg; dk_delete(xptr); cmd.msg = NULL;
      exval = cmd.exval;
    }
    dkapp_close(cmd.a); cmd.a = NULL;
  } else {
    if(!rs) {
      fprintf(
        stderr,
	"createp: ERROR: Not enough memory!\n"
      ); fflush(stderr);
    }
  }
  
  
#line 2538 "createp.ctr"

  exit(exval); return exval;
}



/* vim: set ai sw=2 foldopen=all : */

