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



#include <dk.h>

#include <stdio.h>

#if DK_HAVE_CTYPE_H
#include <ctype.h>
#endif
#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 <dkstr.h>
#include <dksto.h>
#include <dkapp.h>
#include <dkmem.h>
#include <dklogc.h>
#include <dkerror.h>

#include "dktools-version.h"




#line 72 "showhex.ctr"




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



/**	String finder data.
*/
static dk_string_finder_t showhex_msg[] = {
  { "/m/00", &(showhex_str[0]), "Current configuration:"},
  { "/m/01", &(showhex_str[1]), "show text"},
  { "/m/02", &(showhex_str[2]), "show addresses"},
  { "/m/03", &(showhex_str[3]), "on"},
  { "/m/04", &(showhex_str[4]), "off"},
  { "/m/05", &(showhex_str[5]), "input buffer size"},
  { "/m/06", &(showhex_str[6]), "Unknown long option: \""},
  { "/m/07", &(showhex_str[7]), "\"!"},
  { "/m/08", &(showhex_str[8]), "Unknown option: \""},
  { "/m/09", &(showhex_str[9]), "\"!"},
  { "/m/10", &(showhex_str[10]), "Too many file names."},
  { NULL, NULL, NULL }
};



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



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

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

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



/**	Flag: Show address.
*/
#define FLAG_SHOWADDRESS	1

/**	Flag: Show text.
*/
#define FLAG_SHOWTEXT		2

/**	Flag: Output octal.
*/
#define FLAG_OCTAL		4



/**	Showhex job.
*/
typedef struct {
  dk_app_t *app;		/**< Application. */
  int flags;			/**< Flags. */
  size_t requested_buffer_size;	/**< Requested buffer size. */
  char *infilename;		/**< Input file name. */
  char *outfilename;		/**< Output file name. */
  /* input and output file */
  FILE *input_file;		/**< Input file. */
  FILE *output_file;		/**< Output file. */
  /* input buffer */
  char *ibuffer;		/**< Input buffer. */
  size_t ibsize;		/**< Size of input buffer. */
  /* output buffer */
  char *obuffer;		/**< Output buffer. */
  size_t obsize;		/**< Size of output buffer. */
  /* address */
  unsigned long laddr;		/**< Left address. */
  unsigned long raddr;		/**< Right address. */
  char *addrptr;		/**< Address pointer. */
  char *hexptr;			/**< Pointer to hexadecimal values. */
  char *textptr;		/**< Pointer to text. */
  char *nlptr;			/**< Pointer to newline. */
  unsigned char_in_line;	/**< Number of current character in line. */
  char *realinname;		/**< Real input file name. */
  char *realoutname;		/**< Real output file name. */
} ShowhexCmd;



/**	Reset showhex job.
	@param	cmd	Showhex job.
*/
static
void
cmd_reset DK_P1(ShowhexCmd *,cmd)
{
  
  if(cmd) {
    cmd->flags = 0;
    cmd->requested_buffer_size = 1024;
  }
  
}



/**	Initialize showhex job.
	@param	cmd	Showhex job.
*/
static
void
cmd_init DK_P1(ShowhexCmd *,cmd)
{
  
  cmd_reset(cmd);
  if(cmd) {
    cmd->app = NULL;
    cmd->infilename = NULL;
    cmd->outfilename = NULL;
    cmd->input_file = NULL;
    cmd->output_file = NULL;
    cmd->ibuffer = NULL;
    cmd->obuffer = NULL;
    cmd->ibsize  = (size_t)0;
    cmd->obsize  = (size_t)0;
    cmd->requested_buffer_size = (size_t)1024;
    cmd->laddr = 0UL; cmd->raddr = 0UL;
    cmd->addrptr = NULL; cmd->hexptr = NULL; cmd->textptr = NULL;
  }
  
}



/**	Check whether to run silently.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@param	rs	Pointer to result variable (run silently).
	@param	rf	Pointer to result variable (run as filter).
*/
static void
silence_check DK_P4(int,argc,char **,argv,int *,rs,int *,rf)
{
  int i, filenames;
  char *ptr, **lfdptr;
  
  filenames = 0; lfdptr = argv;
  lfdptr++; i = 1;
  while(i < argc) {
    ptr = *lfdptr;
    if(*ptr == '-') {
      ptr++;
      if(*ptr == 's') {
        *rs = 1;
      }
    } else {
      filenames++;
    }
    i++; lfdptr++;
  }
  if(filenames < 2) {
    *rf = 1;
  }
  
}



/**	Long options.
*/
static char *long_options[] = {
  /* 00 */ "h$elp",
  /* 01 */ "v$ersion",
  /* 02 */ "c$onfigure",
  /* 03 */ "u$nconfigure",
  /* 04 */ "sh$ow-configuration",
  /* 05 */ "r$eset",
  /* 06 */ "a$ddresses",
  /* 07 */ "t$ext",
  /* 08 */ "b$uffer",
  /* 09 */ "o$ctal",
  NULL
};



/**	Set or reset flag depending on option value.
	@param	flags	Original flags.
	@param	newflag	One flag to set/reset.
	@param	str	Text representation of flag value.
	@return	Modified flag set.
*/
static
int
flags_adjust DK_P3(int, flags, int, newflag, char *,str)
{
  int back;
  
  back = flags;
  if(str) {
    if(*str) {
      switch(*str) {
        case '+': {
	  back |= newflag;
	} break;
	case '-': {
	  back &= (~(newflag));
	} break;
	default: {
	  if(dkstr_is_on(str)) {
	    back |= newflag;
	  } else {
	    back &= (~(newflag));
	  }
	} break;
      }
    } else {
      back |= newflag;
    }
  } else {
    back |= newflag;
  }
  
  return back;
}



/**	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
};



/**	Print version information.
*/
static
void
print_version DK_P0()
{
  char **ptr;
  
  printf(
    "\nshowhex (part of the dktools collection, version %s)\n",
    the_version_number
  );
  printf("Copyright (C) 2003-2010 - Dipl.-Ing. Dirk Krause\n");
  printf("http://dktools.sourceforge.net/\n\n");
  ptr = license_terms;
  while(*ptr) {
    printf("%s\n", *(ptr++));
  }
  
}




/**	Set app component of \a cmd.
	@param	cmd	Showhex job.
	@param	app	Application.
*/
static
void
cmd_set_app DK_P2(ShowhexCmd *,cmd,dk_app_t *,app)
{
  
  if(cmd) {
    cmd->app = app;
  }
  
}



/**	Preference key: Show text.
*/
static char pk_text[] = { "/flag/text" };

/**	Preference key: Show addresses.
*/
static char pk_addr[] = { "/flag/addr" };

/**	Preference key: Buffer size.
*/
static char pk_bufs[] = { "/buffer-size" };



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

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



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




/**	Find screen width of largest string.
	@param	cmd	Showhex job.
	@param	max	Maximum found so far.
	@param	str	String to text.
	@return	Maximum of \a max and screen length of \a str.
*/
static
size_t
correct_strsize DK_P3(ShowhexCmd *,cmd, size_t, max, char *,str)
{
  size_t back, i;
  
  back = max;
  if(str) {
    i = dkapp_prlen(cmd->app, str);
    if(i > back) back = i;
  }
  
  return back;
}



/**	Print string right aligned.
	@param	cmd	Showhex job.
	@param	max	Available space for text.
	@param	str	String to print.
*/
static
void
print_right_aligned DK_P3(ShowhexCmd *,cmd, size_t, max, char *, str)
{
  size_t i;
  
  i = 0;
  if(str) i = dkapp_prlen(cmd->app, str);
  while(i++ < max) fputc(' ', stdout);
  if(str) dkapp_stdout(cmd->app, str);
  
}



/**	Show configuration.
	@param	cmd	Showhex job.
*/
static
void
cmd_show_conf DK_P1(ShowhexCmd *,cmd)
{
  size_t max;
  char buffer[32];
  
  max = 0;
  sprintf(buffer, "%u", (unsigned)(cmd->requested_buffer_size));
  max = correct_strsize(cmd, max, buffer);
  max = correct_strsize(cmd, max, showhex_str[3]);
  max = correct_strsize(cmd, max, showhex_str[4]);
  dkapp_stdout(cmd->app, showhex_str[0]); fputc('\n', stdout);
  dkapp_stdout(cmd->app, "-t");
  dkapp_stdout(cmd->app, three_spaces);
  print_right_aligned(
    cmd, max,
    (((cmd->flags) & FLAG_SHOWTEXT) ? showhex_str[3] : showhex_str[4])
  );
  dkapp_stdout(cmd->app, three_spaces);
  dkapp_stdout(cmd->app, showhex_str[1]);
  fputc('\n', stdout);
  dkapp_stdout(cmd->app, "-a");
  dkapp_stdout(cmd->app, three_spaces);
  print_right_aligned(
    cmd, max,
    (((cmd->flags) & FLAG_SHOWADDRESS) ? showhex_str[3] : showhex_str[4])
  );
  dkapp_stdout(cmd->app, three_spaces);
  dkapp_stdout(cmd->app, showhex_str[2]);
  fputc('\n', stdout);
  dkapp_stdout(cmd->app, "-b");
  dkapp_stdout(cmd->app, three_spaces);
  print_right_aligned(cmd, max, buffer);
  dkapp_stdout(cmd->app, three_spaces);
  dkapp_stdout(cmd->app, showhex_str[5]);
  fputc('\n', stdout);
  
}



/**	Correct number to fit into specified range.
	@param	u	Original number.
	@param	min	Destination range minimum.
	@param	max	Destination range maximum.
	@return	Corrected number.
*/
static
unsigned
correct_range DK_P3(unsigned, u, unsigned, min, unsigned, max)
{
  unsigned back = u;
  
  if(back < min) back = min;
  if(back > max) back = max;
  
  return back;
}



/**	Get default configuration from preferences.
	@param	cmd	Showhex job.
*/
static 
void
cmd_get_conf DK_P1(ShowhexCmd *,cmd)
{
  char buffer[32];
  
  if(dkapp_get_pref(cmd->app, pk_text, buffer, sizeof(buffer), 0)) {
    if(dkstr_is_on(buffer)) {
      cmd->flags |= FLAG_SHOWTEXT;
    } else {
      cmd->flags &= (~(FLAG_SHOWTEXT));
    }
  }
  if(dkapp_get_pref(cmd->app, pk_addr, buffer, sizeof(buffer), 0)) {
    if(dkstr_is_on(buffer)) {
      cmd->flags |= FLAG_SHOWADDRESS;
    } else {
      cmd->flags &= (~(FLAG_SHOWADDRESS));
    }
  }
  if(dkapp_get_pref(cmd->app, pk_bufs, buffer, sizeof(buffer), 0)) {
    unsigned u;
    if(sscanf(buffer, "%u", &u) == 1) {
      u = correct_range(u, 128, 32768);
      cmd->requested_buffer_size = (size_t)u;
    }
  }
  
}



/**	Save current configuration to preferences.
	@param	cmd	Showhex job.
*/
static
void
cmd_save_conf DK_P1(ShowhexCmd *,cmd)
{
  char buffer[32];
  
  dkapp_set_pref(
    cmd->app, pk_text, (((cmd->flags) & FLAG_SHOWTEXT) ? pv_on : pv_off)
  );
  dkapp_set_pref(
    cmd->app, pk_addr, (((cmd->flags) & FLAG_SHOWADDRESS) ? pv_on : pv_off)
  );
  sprintf(buffer, "%u", (unsigned)(cmd->requested_buffer_size));
  dkapp_set_pref(
    cmd->app, pk_bufs, buffer
  );
  
}



/**	Initialize output line to show another 16 bytes.
	@param	cmd	Showhex job.
*/
static
void
init_output_line DK_P1(ShowhexCmd *, cmd)
{
  
#if DK_HAVE_MEMSET
  memset((cmd->obuffer), ' ', ((cmd->obsize) -1));
#else
  char *ptr; size_t i;
  ptr = cmd->obuffer;
  for(i = 0; i < cmd->obsize; i++) *(ptr++) = ' ';
#endif
  (cmd->obuffer)[cmd->obsize] = '\0';
  if(cmd->addrptr) {
    sprintf(cmd->addrptr, "%08lu:%08lu",
      ((cmd->laddr) & 0xFFFFFFFFUL),
      ((cmd->raddr) & 0xFFFFFFFFUL)
    );
    (cmd->addrptr)[17] = ' ';
    cmd->raddr += 16UL;
    if(!((cmd->raddr) & 0xFFFFFFFFUL)) {
      cmd->raddr = 0UL;
      cmd->laddr += 1UL;
    }
  }
  cmd->char_in_line = 0;
  
}



/**	End of line.
*/
static char end_of_line[] = { "\n\0" };



/**	Flush output line.
	@param	cmd	Showhex job.
*/
static
void
flush_output_line DK_P1(ShowhexCmd *,cmd)
{
  
  if(cmd->char_in_line) {
    if(cmd->nlptr) {
      strcpy(cmd->nlptr, end_of_line);
      fputs(cmd->obuffer, cmd->output_file);
    }
    init_output_line(cmd);
  }
  
}



/**	Hexadecimal digits.
*/
static char hexdigits[] = { "0123456789ABCDEF" };



/**	Add one character to output.
	@param	cmd	Showhex job.
	@param	c	Character to add.
*/
static
void
add_output_char DK_P2(ShowhexCmd *,cmd, char, c)
{
  unsigned u1, u2;
  char buffer[16];
  
  if((cmd->flags) & FLAG_OCTAL) {
    sprintf(buffer, "%03o", (unsigned char)c);
    (cmd->hexptr)[4 * (cmd->char_in_line)]     = buffer[0];
    (cmd->hexptr)[4 * (cmd->char_in_line) + 1] = buffer[1];
    (cmd->hexptr)[4 * (cmd->char_in_line) + 2] = buffer[2];
  } else {
    u1 = (unsigned)((unsigned char)c & 0x0F);
    u2 = (unsigned)(((unsigned char)c >> 4) & 0x0F);
    (cmd->hexptr)[3 * (cmd->char_in_line)]     = hexdigits[u2];
    (cmd->hexptr)[3 * (cmd->char_in_line) + 1] = hexdigits[u1];
  }
  if(cmd->textptr) {
    if(isprint(c) && isascii(c)) {
      (cmd->textptr)[cmd->char_in_line] = c;
    } else {
      (cmd->textptr)[cmd->char_in_line] = '.';
    }
  }
  cmd->char_in_line += 1;
  if(((cmd->char_in_line) >= 16)
     || (((cmd->flags) & FLAG_OCTAL) && ((cmd->char_in_line) >= 8)))
  {
    flush_output_line(cmd);
  }
  
}



/**	Process all input.
	@param	cmd	Showhex job.
	@return	1 on success, 0 on error.
*/
static
int
run_with_buffers DK_P1(ShowhexCmd *, cmd)
{
  int back, cc;
  size_t rdbytes, i;
  char *ptr;
  
  cc = 1; back = 0;
  cmd->laddr = cmd->raddr = 0UL;
  cmd->addrptr = NULL;
  cmd->hexptr = NULL;
  cmd->textptr = NULL;
  cmd->nlptr = NULL;
  /* set the pointers */
  if((cmd->flags) & FLAG_OCTAL) {
    if((cmd->flags) & FLAG_SHOWADDRESS) {
      cmd->addrptr = &((cmd->obuffer)[0]);
      cmd->hexptr = &((cmd->obuffer)[20]);
      cmd->nlptr = &((cmd->obuffer)[51]);
      if((cmd->flags) & FLAG_SHOWTEXT) {
        cmd->textptr = &((cmd->obuffer)[54]);
        cmd->nlptr = &((cmd->obuffer)[62]);
      }
    } else {
      cmd->hexptr = &((cmd->obuffer)[0]);
      cmd->nlptr = &((cmd->obuffer)[31]);
      if((cmd->flags) & FLAG_SHOWTEXT) {
        cmd->textptr = &((cmd->obuffer)[34]);
        cmd->nlptr = &((cmd->obuffer)[42]);
      }
    }
  } else {
    if((cmd->flags) & FLAG_SHOWADDRESS) {
      cmd->addrptr = &((cmd->obuffer)[0]);
      cmd->hexptr = &((cmd->obuffer)[20]);
      cmd->nlptr = &((cmd->obuffer)[67]);
      if((cmd->flags) & FLAG_SHOWTEXT) {
        cmd->textptr = &((cmd->obuffer)[70]);
        cmd->nlptr = &((cmd->obuffer)[86]);
      }
    } else {
      cmd->hexptr = &((cmd->obuffer)[0]);
      cmd->nlptr = &((cmd->obuffer)[47]);
      if((cmd->flags) & FLAG_SHOWTEXT) {
        cmd->textptr = &((cmd->obuffer)[50]);
        cmd->nlptr = &((cmd->obuffer)[66]);
      }
    }
  }
  init_output_line(cmd);
  while(cc) {
    rdbytes = fread((void *)(cmd->ibuffer), 1, cmd->ibsize, cmd->input_file);
    if(rdbytes > 0) {
      back = 1;
      ptr = cmd->ibuffer;
      i = rdbytes;
      while(i--) add_output_char(cmd, *(ptr++));
    } else {
      cc = 0;
    }
  }
  flush_output_line(cmd);
  
  return back;
}



/**	Allocate buffers and process.
	@param	cmd	Showhex job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_files DK_P1(ShowhexCmd *, cmd)
{
  int back;
  size_t u; 
  char my_input[128];
  char output_line[128];
  char *myptr;
  
  back = 0;
  cmd->obuffer = output_line; cmd->obsize = sizeof(output_line);
  u = cmd->requested_buffer_size;
  myptr = dk_new(char,u);
  if(myptr) {
    cmd->ibuffer = myptr;
    cmd->ibsize = cmd->requested_buffer_size;
    back = run_with_buffers(cmd);
    dk_delete(myptr);
  } else {
    cmd->ibuffer = my_input;
    cmd->ibsize = sizeof(my_input);
    back = run_with_buffers(cmd);
  }
  cmd->ibuffer = cmd->obuffer = NULL;
  cmd->ibsize = cmd->obsize = (size_t)0;
  
  return back;
}



/**	Open input file and process.
	@param	cmd	Showhex job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_output_file DK_P1(ShowhexCmd *,cmd)
{
  int back = 0;
  dk_fne_t *fne; int found;
  char *the_new_name = NULL;
  
  if(cmd->infilename) {
    dksf_correct_fnsep(cmd->infilename);
    if(dksf_must_expand_filename(cmd->infilename)) {
      fne = dkfne_open(cmd->infilename, 1, 0);
      if(fne) {
        found = 0;
	while(dkfne_next(fne)) {
	  the_new_name = dkfne_get_fullname(fne);
	  if(the_new_name) {
	    found = 1;
	    cmd->input_file = dkapp_fopen(app, the_new_name, "rb");
	    if(cmd->input_file) {
	      back = run_for_files(cmd);
	      fclose(cmd->input_file);
	    } else {
	      dkapp_err_fopenr(app, the_new_name);
	    }
	  }
	}
	if(!found) {
	  dkapp_err_matchfile(app, cmd->infilename);
	}
        dkfne_close(fne);
      } else {
        dkapp_err_matchfile(app, cmd->infilename);
      }
    } else {
      cmd->input_file = dkapp_fopen(app, cmd->infilename, "rb");
      if(cmd->input_file) {
        back = run_for_files(cmd);
        fclose(cmd->input_file);
      } else {
        dkapp_err_fopenr(app, cmd->infilename);
      }
    }
  } else {
    cmd->input_file = stdin;
    back = run_for_files(cmd);
  }
  
  return back;
}



/**	Open input and output file and process.
	@param	cmd	Showhex job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_cmd DK_P1(ShowhexCmd *,cmd)
{
  int back = 0;
  dk_fne_t *fne;
  char *cptr;
  
  if(cmd->outfilename) {
    dksf_correct_fnsep(cmd->outfilename);
    if(dksf_must_expand_filename(cmd->outfilename)) {
      fne = dkfne_open(cmd->outfilename, 1, 0);
      if(fne) {
        cptr = dkapp_fne_one(app, fne, cmd->outfilename);
	if(cptr) {
	  cmd->output_file = dkapp_fopen(app, cptr, "w");
	  if(cmd->output_file) {
	    back = run_for_output_file(cmd);
	    fclose(cmd->output_file);
	  } else {
	    dkapp_err_fopenw(app, cptr);
	  }
	  dk_delete(cptr);
	}
        dkfne_close(fne);
      } else {
        dkapp_err_matchfile(app, cmd->outfilename);
      }
    } else {
      cmd->output_file = dkapp_fopen(app, cmd->outfilename, "w");
      if(cmd->output_file) {
        back = run_for_output_file(cmd);
        fclose(cmd->output_file);
      } else {
        dkapp_err_fopenw(app, cmd->outfilename);
      }
    }
  } else {
    cmd->output_file = stdout;
    back = run_for_output_file(cmd);
  }
#ifdef FIRST_APPROACH
  if(cmd->outfilename) {
  } else {
    /*
    cmd->input_file = stdin;
    cmd->output_file = stdout;
    back = run_for_files(cmd);
    */
  }
#endif
  
  return back;
}



/**	Default help text, printed if help text file is not found.
*/
static char *help_text[] = {
  "showhex <option> <file(s)>",
  "",
  "-t",
  "                   adds text information to output",
  "",
  "-a",
  "                   adds address information to output",
  "",
  "-b <buffer-size>"
  "                   changes the buffer size for the input buffer",
  "",
  NULL
};



/**	Print help text.
	@param	app	Application.
*/
static void
print_help DK_P1(dk_app_t *,app)
{
  dkapp_help(app, "showhex.txt", help_text);
}



/**	Run real main after.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	1 on success, 0 on error.
*/
static
int
run_main DK_P2(int, argc, char **,argv)
{
  int back = 1;
  int do_configure, do_unconfigure, do_reset, do_help, do_version;
  int do_showconf, i, found, norealrun;
  ShowhexCmd cmd;

  char *cptr, *optarg, **lfdptr;
  
  do_configure = do_unconfigure = do_reset = do_help = do_version = 0;
  do_showconf = 0;
  lfdptr = argv; lfdptr++; i = 1;
  cmd_init(&cmd);
  cmd_set_app(&cmd, app);
  cmd_get_conf(&cmd);
  while(i < argc) {
    cptr = *lfdptr;
    if(*cptr == '-') {
      cptr++;
      if(*cptr == '-') {
        cptr++;
	optarg = dkstr_chr(cptr, '=');
	if(optarg) { *(optarg++) = '\0'; }
	found = dkstr_array_abbr(long_options, cptr, '$', 1);
	switch(found) {
	  case 0: {	/* help */
	    do_help = 1;
	  } break;
	  case 1: {	/* version */
	    do_version = 1;
	  } break;
	  case 2: {	/* configure */
	    do_configure = 1;
	  } break;
	  case 3: {	/* unconfigure */
	    do_unconfigure = 1;
	  } break;
	  case 4: {	/* show-configuration */
	    do_showconf = 1;
	  } break;
	  case 5: {	/* reset */
	    cmd_reset(&cmd);
	  } break;
	  case 6: {	/* addresses */
	    cmd.flags = flags_adjust(cmd.flags, FLAG_SHOWADDRESS, optarg);
	  } break;
	  case 7: {	/* text */
	    cmd.flags = flags_adjust(cmd.flags, FLAG_SHOWTEXT, optarg);
	  } break;
	  case 8: {	/* buffer size */
	    unsigned u;
	    if(optarg) {
	      if(sscanf(optarg, "%u", &u)) {
	        u = correct_range(u, 128, 32768);
	        cmd.requested_buffer_size = (size_t)u;
	      }
	    } else {
	    }
	  } break;
	  case 9: {
	    cmd.flags = flags_adjust(cmd.flags, FLAG_OCTAL, optarg);
	  } break;
	  default: {	/* ERROR: Unknown long option */
	    char *msgptr[3];
	    back = 0; do_help = 1;
	    msgptr[0] = showhex_str[6];
	    msgptr[1] = cptr;
	    msgptr[2] = showhex_str[7];
	    dkapp_log_msg(app, DK_LOG_LEVEL_WARNING, msgptr, 3);
	  } break;
	}
      } else {
        switch(*cptr) {
	  case 'h': {
	    do_help = 1;
	  } break;
	  case 'v': {
	    do_version = 1;
	  } break;
	  case 'c': {
	    do_configure = 1;
	  } break;
	  case 'u': {
	    do_unconfigure = 1;
	  } break;
	  case 'r': {
	    cmd_reset(&cmd);
	  } break;
	  case 'C': {
	    do_showconf = 1;
	  } break;
	  case 't': {	/* show text */
	    cptr++;
	    cmd.flags = flags_adjust(cmd.flags, FLAG_SHOWTEXT, cptr);
	  } break;
	  case 'a': {	/* show addresses */
	    cptr++;
	    cmd.flags = flags_adjust(cmd.flags, FLAG_SHOWADDRESS, cptr);
	  } break;
	  case 's': {
	  } break;
	  case 'o': {
	    cptr++;
	    cmd.flags = flags_adjust(cmd.flags, FLAG_OCTAL, cptr);
	  } break;
	  case 'b': {
	    optarg = NULL;
	    cptr++;
	    if(*cptr) {
	      optarg = cptr;
	    } else {
	      lfdptr++; i++;
	      if(i < argc) {
	        optarg = *lfdptr;
	      }
	    }
	    if(optarg) {
	      unsigned u;
	      if(sscanf(optarg, "%u", &u)) {
	        u = correct_range(u, 128, 32768);
	        cmd.requested_buffer_size = (size_t)u;
	      }
	    }
	  } break;
	  default: {	/* ERROR: Unknown option */
	    char *msgptr[3];
	    back = 0; do_help = 1;
	    msgptr[0] = showhex_str[8];
	    msgptr[1] = cptr;
	    msgptr[2] = showhex_str[9];
	    dkapp_log_msg(app, DK_LOG_LEVEL_WARNING, msgptr, 3);
	  } break;
	}
      }
    } else {
      if(cmd.infilename) {
        if(cmd.outfilename) {
	  /* ERROR: Too many file names */
	  char *msgptr[2];
	  do_help = 1; back = 0;
	  msgptr[0] = showhex_str[10];
	  dkapp_log_msg(app, DK_LOG_LEVEL_WARNING, msgptr, 1);
	} else {
	  cmd.outfilename = cptr;
	}
      } else {
        cmd.infilename = cptr;
      }
    }
    i++; lfdptr++;
  }
  norealrun= do_help | do_version | do_configure | do_unconfigure | do_showconf;
  if(norealrun) {
    
    /* not really running */
    if(do_help || do_version) {
      /* show version */
      print_version();
      if(do_help) {
        /* show help */
	print_help(app);
      }
    } else {
      if(do_unconfigure) {
        /* unconfigure */
	dkapp_unconfigure(app);
      } else {
        if(do_configure) {
	  /* save new configuration */
	  cmd_save_conf(&cmd);
	}
	/* show configuration */
	cmd_show_conf(&cmd);
      }
    }
  } else {
    /* really run */
    
    back = run_for_cmd(&cmd);
  }
  
  return back;
}



/**	The main() function of the showhex 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
{
  int exval = 0;	/* return status */
  int rs = 0;		/* run silently */
  int rf = 0;		/* run as filter (to stdout) */
  
#line 1143 "showhex.ctr"

  silence_check(argc, argv, &rs, &rf);
  app = dkapp_open_ext1(argc, argv, packagename, sysconfdir, rs, rf);
  if(app) {
    dkapp_find_multi(app, showhex_msg, "showhex" );
    exval = run_main(dkapp_get_argc(app), dkapp_get_argv(app));
    dkapp_close(app); app = NULL;
  } else {
    if(!rs) {
      fputs("ERROR: Not enough memory!\n", stderr);
      fflush(stdout);
    }
  }
  exval = (exval ? 0 : 1);
  
  
#line 1158 "showhex.ctr"

  exit(exval); return exval;
}



