/*
Copyright (c) 2008-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	rndbytes.c	The rndbytes program.
*/



/*
	rndbytes -b <blocksize> -n <blocks> -a <prngs>
*/



#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
#if DK_HAVE_STRING_H
#include <string.h>
#endif
#if DK_HAVE_SIGNAL_H
#include <signal.h>
#endif
#if DK_HAVE_CONIO_H
#include <conio.h>
#endif

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

#include "dktools-version.h"




#line 88 "rndbytes.ctr"




/**	Mode: Default mode (no output encoding).
*/
#define PROGRAM_MODE_DEFAULT	0

/**	Mode: Convert output ASCII-Hex.
*/
#define PROGRAM_MODE_HEX	1

/**	Mode: convert output ASCII-85.
*/
#define PROGRAM_MODE_ASCII85	2

/**	Mode mask: Anything set?
*/
#define PROGRAM_MODE_SET	3



/**	Action: Run normaly.
*/
#define ACTION_RUN		0

/**	Action: Show help text.
*/
#define	ACTION_HELP		1

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

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

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

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



/**	Length of buffer for preferences keys and values.
*/
#define DEFAULT_BUFFER_LENGTH	256



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

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



/**	Rndbytes job.
*/
typedef struct {
  int		action;	/**< Action to take. */
  int		randt;	/**< PRNG types. */
  int		exval;	/**< Returned by the main function. */
  dk_app_t	*a;	/**< Application structure. */
  char		**msg;	/**< The messages printed by the program. */
  unsigned char	*dbuf;	/**< Default buffer (static). */
  size_t	s_dbuf;	/**< Size of dbuf. */
  size_t	blksz;	/**< Block size. */
  unsigned long	nblks;	/**< Number of blocks. */
  unsigned char	*b;	/**< Buffer to use. */
} CMD;



/**	Function returning void.
*/
#define P_void(fctname) static void fctname DK_P1(CMD *,c)

/**	Function returning int value.
*/
#define P_int(fctname)  static int  fctname DK_P1(CMD *,c)



/**	Version information.
*/
static char *version_text[] = {
"",
"rndbytes, version 2 (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
};



/**	The used libraries are listed together with version information.
*/
static char *libraries_used[]= {
"",
"Libraries used:",
"",
NULL
};



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



/**	Default help text, shown if the help text file is not found.
*/
static char *help_text[] = {
"",
"rndbytes - Create blocks of random bytes",
"========================================",
"",
"createp -h",
"createp --help",
"	shows this help text",
"",
"createp -v",
"createp --version",
"	shows version information",
"",
"createp -c <option>",
"	save permanent options",
"",
"createp -u",
"	removes all permanent options",
"",
"createp -C",
"	shows the permanent options",
"",
"createp -b <s:size> -n <b:size> -a <prngs:string>",
"	create <b> blocks of size <s> using the PRNGs listed in <prngs>.",
"	In <prngs> either specify ``all'' or a comma-separated list of:",
"	openssl		to use the OpenSSL PRNG",
"	random		to use initstate()/setstate()/random()",
"	rand48		to use nrand48()",
"	rand		to use rand()",
"",
NULL
};



/**	Long options.
*/
static char *long_options[] = {
/*   0 */	"silently",
/*   1 */	"help",
/*   2 */	"version",
/*   3 */	"configure",
/*   4 */	"show-configuration",
/*   5 */	"unconfigure",
/* application specific part starts here */
/*   6 */	"blocksize",
/*   7 */	"nblocks",
/*   8 */	"prngs",
NULL
};


/**	Key value pairs for string finder.
*/
static dk_key_value_t kv[] = {
{ "0",	"Not enough memory (RAM/swap space)!" },
{ "1",	"Current configuration:" },
{ "2",	"block size in bytes" },
{ "3",	"number of blocks (0 means ``infinite'' sequence of blocks)" },
{ "4",  "PRNGs to use, either ``all'' or a comma-separated list of:" },
{ "5",	"openssl     OpenSSL PRNG" },
{ "6",	"random      initstate()/setstate()/random()" },
{ "7",	"rand48      nrand48()" },
{ "8",	"rand        rand()" },
{ "9",	"\"" },
{ "10",	"\" is not a number" },
{ "11",	"Long option \"" },
{ "12", "\" needs an argument!" },
{ "13", "Option \"" },
{ "14",	"\" needs an argument!" },
{ "15",	"Unknown long option \"" },
{ "16",	"\"!" },
{ "17", "Argument \"" },
{ "18",	"\" is too long!" },
{ "19", "Missing block size for \"-b\" option!" },
{ "20",	"Missing number of blocks for \"-n\" option!" },
{ "21",	"Missing PRNG list for \"-a\" option!" },
{ "22",	"Unknown option \"" },
{ "23",	"\"!" },
{ "24",	"\"" },
{ "25", "\" is not an option!" },
{ "26",	"An error occured while writing to standard output!" },
{ "27",	"Programm finished by signal or user interaction!" },
{ "28", "" },
{ "29", " blocks were written." },
};
/**	Number of entries in \a kv.
*/
static size_t szkv = sizeof(kv)/sizeof(dk_key_value_t);



/**	String: comma.
*/
static char comma[] = { "," };

/**	String: Three spaces are used before and after value
	when showing the current configuration.
*/
static char three_spaces[] = { "   " };



/**	Preference keys.
*/
char *pk[] = {
/*   0 */	"/output/blocksize",
/*   1 */	"/output/nblocks",
/*   2 */	"/input/prngs"
};



/**	PRNG names.
*/
char *prng_types[] = {
"all", "openssl", "random", "rand48", "rand"
};



/**	Debug message consisting of three parts.
	@param	c	Rndbytes job.
	@param	s1	Index of first message part in c->msg.
	@param	s2	Index of third message part in c->msg.
	@param	x	Second message part.
*/
static
void
debug_3 DK_P4(CMD *,c, size_t,s1, size_t,s2, char *,x)
{
  char *m[4];
  m[0] = (c->msg)[s1];
  m[1] = x;
  m[2] = (c->msg)[s2];
  m[3] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_DEBUG, m, 3);
}



/**	Information message consisting of three parts.
	@param	c	Rndbytes job.
	@param	s1	Index of message text in c->msg.
*/
static
void
info_1 DK_P2(CMD *,c, size_t,s1)
{
  char *m[4];
  m[0] = (c->msg)[s1];
  m[1] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_INFO, m, 2);
}



/**	Error message consisting of three parts.
	@param	c	Rndbytes job.
	@param	s1	Index of first message part in c->msg.
	@param	s2	Index of third message part in c->msg.
	@param	x	Second message part.
*/
static
void
error_3 DK_P4(CMD *,c, size_t,s1, size_t,s2, char *,x)
{
  char *m[4];
  m[0] = (c->msg)[s1];
  m[1] = x;
  m[2] = (c->msg)[s2];
  m[3] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_ERROR, m, 3);
}



/**	Error message.
	@param	c	Rndbytes job.
	@param	s1	Index of message text in c->msg.
*/
static
void
error_1 DK_P2(CMD *,c, size_t,s1)
{
  char *m[4];
  m[0] = (c->msg)[s1];
  m[1] = NULL;
  dkapp_log_msg(c->a, DK_LOG_LEVEL_ERROR, m, 2);
}



/**	Initialize rndbytes job.
	@param	c	Rndbytes job.
*/
P_void(cmd_init) {
  c->action = ACTION_RUN;
  c->randt = DK_RAND_TYPE_ALL;
  c->exval = 0;
  c->a = NULL;
  c->msg = NULL;
  c->dbuf = NULL;
  c->s_dbuf = 0;
  c->blksz = 512;
  c->nblks = 1UL;
}



/**	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 (silently).
	@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 0: myrs = 1; break;
	}
      } else {
        switch(*ptr) {
	  case 'S': {
	    myrs = 1;
	  } break;
	}
      }
    }
    lfdptr++; i++;
  }
  if(rs) { *rs = myrs; }
  
}



/**	Get default settings from preferences.
	@param	c	Rndbytes job.
*/
P_void(load_defaults) {
  char valbuffer[DEFAULT_BUFFER_LENGTH];
  unsigned u; unsigned long ul;
  if(dkapp_get_pref(c->a, pk[0], valbuffer, sizeof(valbuffer), 0)) {
    if(sscanf(valbuffer, "%u", &u) == 1) {
      c->blksz = (size_t)u;
    }
  }
  if(dkapp_get_pref(c->a, pk[1], valbuffer, sizeof(valbuffer), 0)) {
    if(sscanf(valbuffer, "%lu", &ul) == 1) {
      c->nblks = ul;
    }
  }
  if(dkapp_get_pref(c->a, pk[2], valbuffer, sizeof(valbuffer), 0)) {
    c->randt = dkapp_rand_types_from_string(c->a, valbuffer);
  }
}



/**	Process the command line arguments.
	@param	c	Rndbytes job.
*/
P_void(process_arguments) {
  int xargc, i;
  char *ptr, *optr, *vptr, **xargv, **lfdptr;
  char arg[DEFAULT_BUFFER_LENGTH];
  unsigned u; unsigned long ul;
  
  xargc = dkapp_get_argc(c->a);
  xargv = dkapp_get_argv(c->a);
  lfdptr = xargv; lfdptr++; i = 1;
  while(i < xargc) {
    ptr = *lfdptr; optr = ptr;
    switch(*ptr) {
      case '-': {
        ptr++;
	switch(*ptr) {
	  case '-': {
	    ptr++;
	    if(strlen(ptr) < sizeof(arg)) {
	      strcpy(arg, ptr);
	      vptr = dkstr_chr(arg, '=');
	      if(vptr) { *(vptr++) = '\0'; }
	      switch(dkstr_array_abbr(long_options, ptr, '$', 0)) {
	        case 0: { } break;
		case 1: { c->action |= ACTION_HELP; } break;
		case 2: { c->action |= ACTION_VERSION; } break;
		case 3: { c->action |= ACTION_CONFIGURE; } break;
		case 4: { c->action |= ACTION_SHOWCONF; } break;
		case 5: { c->action |= ACTION_UNCONFIGURE; } break;
		case 6: {
		  if(vptr) {
		    if(sscanf(vptr, "%u", &u) == 1) {
		      c->blksz = (size_t)u;
		    } else {
		      error_3(c, 9, 10, vptr);
		      c->exval = 1; c->action |= ACTION_HELP;
		    }
		  } else {
		    error_3(c, 11, 12, optr);
		    c->exval = 1; c->action |= ACTION_HELP;
		  }
		} break;
		case 7: {
		  if(vptr) {
		    if(sscanf(vptr, "%lu", &ul) == 1) {
		      c->nblks = ul;
		    } else {
		      error_3(c, 9, 10, vptr);
		      c->exval = 1; c->action |= ACTION_HELP;
		    }
		  } else {
		    error_3(c, 11, 12, optr);
		    c->exval = 1; c->action |= ACTION_HELP;
		  }
		} break;
		case 8: {
		  if(vptr) {
		    c->randt = dkapp_rand_types_from_string(c->a, vptr);
		    if(!(c->randt)) {
		      c->exval = 1;
		    }
		  } else {
		    error_3(c, 13, 14, optr);
		    c->exval = 1; c->action |= ACTION_HELP;
		  }
		} break;
		default: {
		  error_3(c, 15, 16, optr);
		  c->exval = 1;
		} break;
	      }
	    } else {
	      error_3(c, 17, 18, optr);
	      c->exval = 1;
	    }
	  } break;
	  case 'S': {
	  } break;
	  case 'c': {
	    c->action |= ACTION_CONFIGURE;
	  } break;
	  case 'C': {
	    c->action |= ACTION_SHOWCONF;
	  } break;
	  case 'u': {
	    c->action |= ACTION_UNCONFIGURE;
	  } break;
	  case 'h': {
	    c->action |= ACTION_HELP;
	  } break;
	  case 'v': {
	    c->action |= ACTION_VERSION;
	  } break;
	  case 'b': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL;
	      lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(sscanf(ptr, "%u", &u) == 1) {
	        c->blksz = (size_t)u;
	      } else {
		error_3(c, 9, 10, ptr);
		c->exval = 1; c->action |= ACTION_HELP;
	      }
	    } else {
	      error_1(c, 19); c->exval = 1;
	    }
	  } break;
	  case 'n': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL;
	      lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      if(sscanf(ptr, "%lu", &ul) == 1) {
	        c->nblks = ul;
	      } else {
		error_3(c, 9, 10, ptr);
		c->exval = 1; c->action |= ACTION_HELP;
	      }
	    } else {
	      error_1(c, 20);
	    }
	  } break;
	  case 'a': {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL;
	      lfdptr++; i++;
	      if(i < xargc) { ptr = *lfdptr; }
	    }
	    if(ptr) {
	      c->randt = dkapp_rand_types_from_string(c->a, ptr);
	      if(!(c->randt)) {
		c->exval = 1;
	      }
	    } else {
	      error_1(c, 21); c->exval = 1;
	    }
	  } break;
	  default: {
	    error_3(c, 22, 23, ptr);
	    c->exval = 1;
	  } break;
	}
      } break;
      default: {
	error_3(c, 24, 25, ptr);
	c->action = ACTION_HELP;
	c->exval = 1;
      } break;
    }
    lfdptr++; i++;
  } 
}



/**	Flag: Signal received.
*/
static volatile int sr = 0;



/**	Check keyboard instead of signal on Windows.
*/
static
int
get_sr DK_P0()
{
  int back = 0;
  back = sr;
  if(!back) {
#if DK_HAVE__KBHIT
    if(_kbhit()) {
      sr = back = 1;
#if DK_HAVE__GETCH
      _getch();
#endif
    }
#endif
  }
  return back;
}



/**	SIGHUP handler.
	@param	signo	Signal number (SIGHUP).
*/
dk_signal_ret_t
sighup_handler DK_P1(int,signo)
{
  dksignal_refresh(signo,sighup_handler);
  sr = 1;
  dksignal_return(0);
}



/**	SIGPIPE handler.
	@param	signo	Signal number (SIGPIPE).
*/
dk_signal_ret_t
sigpipe_handler DK_P1(int,signo)
{
  dksignal_refresh(signo,sigpipe_handler);
  sr = 1;
  dksignal_return(0);
}



/**	SIGTERM handler.
	@param	signo	Signal number (SIGTERM).
*/
dk_signal_ret_t
sigterm_handler DK_P1(int,signo)
{
  dksignal_refresh(signo,sigterm_handler);
  sr = 1;
  dksignal_return(0);
}



/**	SIGINT handler.
	@param	signo	Signal number (SIGINT).
*/
dk_signal_ret_t
sigint_handler DK_P1(int,signo)
{
  dksignal_refresh(signo,sigint_handler);
  sr = 1;
  dksignal_return(0);
}



/**	Create random output.
	@param	c	Rndbytes job.
*/
P_void(go_create)
{
  unsigned long bl;
  char buffer[32];
  int old_stdout_binary = 0;
#ifdef SIGHUP
  dk_signal_disp_t disp_hup = NULL;
#endif
#ifdef SIGTERM
  dk_signal_disp_t disp_term = NULL;
#endif
#ifdef SIGPIPE
  dk_signal_disp_t disp_pipe = NULL;
#endif
#ifdef SIGINT
  dk_signal_disp_t disp_int = NULL;
#endif
  if(!(c->randt)) { c->randt = DK_RAND_TYPE_ALL; }
  c->randt = dkapp_rand_begin(c->a, c->randt);
  if(c->randt) {
    bl = 0UL;
    if(c->nblks == 0UL) {
#ifdef SIGHUP
      disp_hup = dksignal_set(SIGHUP, sighup_handler);
#endif
#ifdef SIGTERM
      disp_term = dksignal_set(SIGTERM, sigterm_handler);
#endif
#ifdef SIGPIPE
      disp_pipe = dksignal_set(SIGPIPE, sigpipe_handler);
#endif
#ifdef SIGINT
      disp_int = dksignal_set(SIGINT, sigint_handler);
#endif
    }
    /* dksf_set_binary(1); */
    old_stdout_binary = dksf_fdesk_binary(1, 1);
    while(((c->nblks == 0) || (bl < c->nblks)) && (get_sr() == 0)) {
      dkapp_rand_bytes(c->a, (void *)(c->b), c->blksz);
      if(write(1, c->b, c->blksz) == -1) {
        if(!sr) {
	  c->exval = 1;
	  error_1(c, 26);
	}
        sr = 1;

      }
      bl++;
    }
    (void)dksf_fdesk_binary(1, old_stdout_binary);
    if(c->nblks == 0UL) {
#ifdef SIGHUP
      if(disp_hup) { dksignal_set(SIGHUP, disp_hup); }
#endif
#ifdef SIGTERM
      if(disp_term) { dksignal_set(SIGTERM, disp_term); }
#endif
#ifdef SIGPIPE
      if(disp_pipe) { dksignal_set(SIGPIPE, disp_pipe); }
#endif
#ifdef SIGINT
      if(disp_int) { dksignal_set(SIGINT, disp_int); }
#endif
    }
    dkapp_rand_end(c->a);
    if(sr) {
      info_1(c, 27);
    }
    sprintf(buffer, "%lu", bl);
    debug_3(c, 28, 29, buffer);
  } else {
    c->exval = 1;
  }
}



/**	Create random output.
	@param	c	Rndbytes job.
*/
P_void(create_random_bytes) {
  unsigned char *x;
  if(c->s_dbuf >= c->blksz) {
    c->b = c->dbuf;
    go_create(c);
  } else {
    x = dk_new(unsigned char,(c->blksz));
    if(x) {
      c->b = x;
      go_create(c);
      dk_delete(x);
    } else {
      error_1(c, 0);
    }
  }
}



/**	Print version information.
	@param	c	Rndbytes job.
*/
P_void(print_version) {
  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	Rndbytes job.
*/
P_void(print_help) {
  
  dkapp_help(c->a, "rndbytes.txt", help_text);
  
}



/**	Add a name to a buffer if a condition is true.
	@param	s	Result buffer.
	@param	sz	Size of \a s in bytes.
	@param	r	Random generator ID.
	@param	rt	OR-combination of allowed random generator IDs.
	@param	str	Name of random generator \a r.
	@return	1 on success, 0 on error.
*/
static
int
add_to_string_if DK_P5(char *,s, size_t,sz, int,r, int,rt, char *,str)
{
  int back = 1;
  if((r & rt) == rt) {
    if(strlen(s)) {
      if((strlen(s) + strlen(comma) + strlen(str)) < sz) {
        strcat(s, comma);
	strcat(s, str);
      } else {
        back = 0;
      }
    } else {
      if(strlen(str) < sz) {
        strcpy(s, str);
      } else {
        back = 0;
      }
    }
  }
  return back;
}



/**	Convert random generator names to text.
	@param	s	Result buffer.
	@param	sz	Size of \a s in bytes.
	@param	r	OR-combination of random generator IDs.
	@return	1 on success, 0 on error.
*/
static
int
rand_types_to_string DK_P3(char *,s, size_t,sz, int,r)
{
  int back = 1;
  s[0] = '\0';
  if(r == DK_RAND_TYPE_ALL) {
    if(strlen(prng_types[0]) < sz) {
      strcpy(s, prng_types[0]);
    } else {
      back = 0;
    }
  } else {
    if(!add_to_string_if(s, sz, r, DK_RAND_TYPE_OPENSSL, prng_types[1])) {
      back= 0;
    }
    if(!add_to_string_if(s, sz, r, DK_RAND_TYPE_STATE, prng_types[2])) {
      back= 0;
    }
    if(!add_to_string_if(s, sz, r, DK_RAND_TYPE_RAND48, prng_types[3])) {
      back= 0;
    }
    if(!add_to_string_if(s, sz, r, DK_RAND_TYPE_SIMPLE, prng_types[4])) {
      back= 0;
    }
  }
  return back;
}



/**	Put a text centered in the available room.
	@param	s	Text to print.
	@param	sz	Available place.
*/
static
void
put_centered DK_P2(char *,s, size_t,sz)
{
  size_t lgt, i, j, k;
  lgt = strlen(s);
  if(lgt < sz) {
    i = ((sz - lgt) / 2);
    j = (sz - lgt -i );
    for(k = 0; k < i; k++) fputc(' ', stdout);
    fputs(s, stdout);
    for(k = 0; k < j; k++) fputc(' ', stdout);
  } else {
    fputs(s, stdout);
  }
}



/**	Find maximum of 2 size_t values.
	@param	s1	Size 1.
	@param	s2	Size 2.
	@return Maximum of \a s1 and \a s2.
*/
static
size_t max_size DK_P2(size_t,s1, size_t,s2)
{
  size_t back;
  if(s1 > s2) back = s1;
  else back = s2;
  return back;
}



/**	Show current configuration.
	@param	c	Rndbytes job.
*/
P_void(show_configuration) {
  char b1[32], b2[32], vb[DEFAULT_BUFFER_LENGTH];
  size_t sz, j, k;
  
  sprintf(b1, "%u", (unsigned)(c->blksz));
  sprintf(b2, "%lu", c->nblks);
  sz = strlen(b1);
  sz = max_size(sz, strlen(b2));
  if(c->randt == DK_RAND_TYPE_ALL) {
    sz = max_size(sz, strlen(prng_types[0]));
  }
  fputc('\n', stdout);
  dkapp_stdout(c->a, (c->msg)[1]);
  fputc('\n', stdout);
  for(k = 0; k < strlen((c->msg)[1]); k++) fputc('-', stdout);
  fputc('\n', stdout);
  fputc('\n', stdout);
  /* block size */
  fputs("-b", stdout);
  fputs(three_spaces, stdout);
  put_centered(b1, sz);
  fputs(three_spaces, stdout);
  dkapp_stdout(c->a, (c->msg)[2]);
  fputc('\n', stdout);
  /* number of blocks */
  fputs("-n", stdout);
  fputs(three_spaces, stdout);
  put_centered(b2, sz);
  fputs(three_spaces, stdout);
  dkapp_stdout(c->a, (c->msg)[3]);
  fputc('\n', stdout);
  /* PRNGs */
  fputs("-a", stdout);
  fputs(three_spaces, stdout);
  if(c->randt == DK_RAND_TYPE_ALL) {
    put_centered(prng_types[0], sz);
    fputs(three_spaces, stdout);
    dkapp_stdout(c->a, (c->msg)[4]);
  } else {
    if((c->randt) && rand_types_to_string(vb, sizeof(vb), c->randt)) {
      fputs(vb, stdout);
      fputc('\n', stdout);
      for(k = 0; k < (sz + 8); k++) fputc(' ', stdout);
      dkapp_stdout(c->a, (c->msg)[4]);
    } else {
      for(k = 0; k < (sz + 3); k++) fputc(' ', stdout);
      dkapp_stdout(c->a, (c->msg)[4]);
    }
  }
  fputc('\n', stdout);
  for(j = 5; j <=8; j++) {
    for(k = 0; k < (sz + 8); k++) fputc(' ', stdout);
    dkapp_stdout(c->a, (c->msg)[j]);
    fputc('\n', stdout);
  }
  fputc('\n', stdout);
  
}



/**	Save configuration.
	@param	c	Rndbytes job.
*/
P_void(save_configuration) {
  char valbuffer[DEFAULT_BUFFER_LENGTH];
  sprintf(valbuffer, "%u", (unsigned)(c->blksz));
  dkapp_set_pref(c->a, pk[0], valbuffer);
  sprintf(valbuffer, "%lu", c->nblks);
  dkapp_set_pref(c->a, pk[1], valbuffer);
  if(rand_types_to_string(valbuffer, sizeof(valbuffer), c->randt)) {
     dkapp_set_pref(c->a, pk[2], valbuffer);
  }
}



/**	Run normally or print help/version.
	@param	c	Rndbytes job.
*/
P_void(run) {
  if(c->action) {
    if((c->action) & (ACTION_HELP | ACTION_VERSION)) {
      print_version(c);
      if((c->action) & ACTION_HELP) {
        print_help(c);
      }
    }
    if((c->action) & ACTION_UNCONFIGURE) {
      dkapp_unconfigure(c->a);
    } else {
      if((c->action) & (ACTION_CONFIGURE | ACTION_SHOWCONF)) {
        if((c->action) & ACTION_CONFIGURE) {
	  save_configuration(c);
	}
        show_configuration(c);
      }
    }
  } else {
    if(c->exval == 0) {
      create_random_bytes(c);
    }
  }
}



/**	Output buffer.
*/
static unsigned char mybuffer[16384];



/**	String table name.
*/
static char string_table_name[] = { "rndbytes" };



/**	The main() function of the rndbytes program.
	@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
{
  CMD c;
  int rs = 0, rf = 1;
  char **msgptr;		/* used to delete c.msg */
  
#line 1151 "rndbytes.ctr"

  
  silence_check(argc, argv, &rs, &rf);
  cmd_init(&c);
  c.a = dkapp_open_ext1(argc, argv, grname, etcdir, rs, rf);
  if(c.a) {
    c.msg = dkapp_find_key_value(c.a, kv, szkv, string_table_name);
    if(c.msg) {
      c.dbuf = mybuffer; c.s_dbuf = sizeof(mybuffer);
      load_defaults(&c);
      process_arguments(&c);
      run(&c);
      msgptr = c.msg; dk_delete(msgptr);
    }
    dkapp_close(c.a);
  } else {
    if(!rs) {
      fprintf(stderr, "rndbytes: ERROR: Not enough memory!\n");
      fflush(stderr);
    }
  }
  
  
#line 1173 "rndbytes.ctr"

  exit(c.exval); return(c.exval);
}



