/*
	WARNING: This file was generated by dkct.
	Changes you make here will be lost if dkct is run again!
	You should modify the original source and run dkct on it.
	Original source: dkt-chks.ctr
*/

/*
Copyright (C) 2011-2013, Dirk Krause

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 author 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 dkt-chks.c The dkt-chks module.
*/


#line 10 "dkt-chks.ctr"

#include "dk3all.h"
#include "dkt.h"





#line 17 "dkt-chks.ctr"



/**	Job structure for dkt checksum.
*/
typedef struct {
  dkChar const * const	*msg;		/**< Localized message texts. */
  dkChar const * const	*kwnl;		/**< Keywords, not localized. */
  dk3_app_t		*app;		/**< Application structure. */
  dk3_option_set_t	*opt;		/**< Option set. */
  int			 exval;		/**< Exit status code. */
  int			 enc_s;		/**< Stdin default encoding. */
  int			 enc_f; 	/**< File default encoding. */
  int			 mdt;		/**< Message digest type. */
  int			 mde;		/**< Message digest encoding. */
  int			 f_lst;		/**< Flag: Arguments are file lists */
  int			 f_chk;		/**< Flag: Check. */
  int			 f_sz;		/**< Flag: Size (1) or checksum (0). */
  int			 l_vrb;		/**< Verbosity level. */
  int			 f_first;	/**< Flag: First entry to handle. */
  int			 f_msm;		/**< Flag: Mismatch found! */
} DKT_CHKS_J;



/**	Directory chain cell.
*/
typedef struct {
  void			*parent;	/**< Parent chain element. */
  dk3_dir_t		*dir;		/**< Directory structure. */
  dk3_stat_t		 ds;		/**< Stat information for directory. */
} DKT_CHKS_DIR;



/**	Options used by dkt checksum.
*/
static dk3_option_t const dkt_chks_options[] = {
  { dkT('R'), dkT("reset"), 0 },
  { dkT('m'), dkT("message-digest"), 1 },
  { dkT('l'), dkT("file-list"), 0 },
  { dkT('i'), dkT("input-encoding"), 1 },
  { dkT('p'), dkT("plain"), 0 },
  { dkT('c'), dkT("check"), 0 },
  { dkT('v'), dkT("verbose"), 0 },
  { dkT('q'), dkT("quiet"), 0 },
};

/**	Number of options in the dkt_chks_options array.
*/
static size_t const dkt_chks_szoptions =
sizeof(dkt_chks_options)/sizeof(dk3_option_t);



/**	Configuration file keywords.
*/
static dkChar const * const	dkt_chks_conf_kw[] = {
dkT("reset"),
dkT("stdin-encoding"),
dkT("file-encoding"),
dkT("message-digest"),
NULL
};



/**	Delete directory chain element, release memory.
	@param	dp	Direcrory chain element.
*/
static
void
dkt_chks_dir_delete(DKT_CHKS_DIR *dp)
{
  if(dp) {
    if(dp->dir) {
      dk3dir_close(dp->dir);
    } dp->dir = NULL;
    dp->parent = NULL;
    dk3_delete(dp);
  }
}



/**	Create directory chain element, allocate memory.
	@param	parent	Parent chain element.
	@param	fn	File name.
	@param	stb	Stat buffer for the file.
	@param	app	Application structure for diagnostics.
	@return	Pointer to new element on success, NULL on error.
*/
static
DKT_CHKS_DIR *
dkt_chks_dir_new(
  DKT_CHKS_DIR *parent, dkChar const *fn, dk3_stat_t *stb, dk3_app_t *app
)
{
  DKT_CHKS_DIR *back = NULL;
  back = dk3_new_app(DKT_CHKS_DIR,1,app);
  if(back) {
    back->parent = NULL; back->dir = NULL;
    back->parent = parent;
    dk3mem_cpy(&(back->ds),stb,sizeof(dk3_stat_t));
    back->dir = dk3dir_open_app(fn, app);
    if(!(back->dir)) {
      dkt_chks_dir_delete(back); back = NULL;
    }
  }
  return back;
}



/**	Initialize job structure.
	@param	j	Structure to initialize.
*/
static
void
dkt_chks_job_init(DKT_CHKS_J *j)
{
  j->msg = NULL;
  j->kwnl = NULL;
  j->app = NULL;
  j->opt = NULL;
  j->enc_s = DK3_FILE_ENCODING_ASCII;
  j->enc_f = DK3_FILE_ENCODING_ASCII;
  j->exval = DKT_RESULT_ERR_UNSPECIFIC;
  j->mdt = DK3_MD_TYPE_SHA_1;
  j->mde = DK3_DATA_ENCODING_ASCII85;
  j->f_lst = 0;
  j->f_chk = 0;
  j->f_sz = 0;
  j->l_vrb = 1;
  j->f_first = 1;
  j->f_msm = 0;
}



/**	Reset options in a job structure (-R or --reset option).
	@param	j	Job structure.
*/
static
void
dkt_chks_job_reset(DKT_CHKS_J *j)
{
  j->enc_s = dk3app_get_default_stdin_encoding(j->app);
  j->enc_f = dk3app_get_default_file_encoding(j->app);
  j->mdt = DK3_MD_TYPE_SHA_1;
  j->mde = DK3_DATA_ENCODING_ASCII85;
}



/**	Clean up job structure.
*/
static
void
dkt_chks_job_cleanup(DKT_CHKS_J *j)
{
  if(j->opt) {
    dk3opt_close(j->opt);
  } j->opt = NULL;
}



/**	Process one key/value pair from configuration files.
	@param	jv	Pointer to job structure casted to void *.
	@param	k	Key.
	@param	v	Value.
	@return	1 to indicate success.
*/
static
int
dkt_chks_conf_line(void *jv, dkChar const *k, dkChar const *v)
{
  int back = 0;
  DKT_CHKS_J *j;
  

#line 198 "dkt-chks.ctr"
  j = (DKT_CHKS_J *)jv;
  switch(dk3str_array_index(dkt_chks_conf_kw, k, 0)) {
    case 0: {	/* reset */
      dkt_chks_job_reset(j); back = 1;
    } break;
    case 1: {	/* stdin encoding */
      if(v) {
        back = dkt_tool_set_encoding(
	  j->app, &(j->enc_s), v,
	  dk3app_get_input_stdin_encoding(j->app)
	);
      }
    } break;
    case 2: {	/* file encoding */
      if(v) {
        back = dkt_tool_set_encoding(
	  j->app, &(j->enc_f), v,
	  dk3app_get_input_stdin_encoding(j->app)
	);
      }
    } break;
    case 3: {	/* message digest */
      if(v) {
	back = dkt_tool_set_md_type(
	  &(j->mdt), &(j->mde), &(j->exval), v, j->app, j->msg, 0
	);
      }
    } break;
  } 

#line 227 "dkt-chks.ctr"
  return back;
}



/**	Process options and command line arguments.
	@param	j	Job structure.
	@return	1 on success (can continue), 0 on error (exit).
*/
static
int
dkt_chks_process_arguments(DKT_CHKS_J *j)
{
  int			back = 0;
  int			res = 0;	/* Checksum operation result. */
  int			 xargc = 0;	/* Number of command line arguments. */
  dkChar const		*arg = NULL;	/* Option argument. */
  dkChar const * const	*xargv = NULL;	/* Command line arguments array. */
  

#line 246 "dkt-chks.ctr"
  xargc = dk3app_get_argc(j->app);
  xargv = dk3app_get_argv(j->app);
  xargv++; xargv++; xargc--; xargc--;
  j->opt = dk3opt_open_app(
    dkt_chks_options,
    dkt_chks_szoptions,
    0x00,
    NULL,
    xargc,
    xargv,
    j->app
  );
  if(j->opt) {
    if(0 == dk3opt_get_error_code(j->opt)) {
      back = 1;
      if(dk3opt_is_set(j->opt, dkT('R'))) {
        dkt_chks_job_reset(j);
      }
      if(dk3opt_is_set(j->opt, dkT('m'))) {
        arg = dk3opt_get_short_arg(j->opt, dkT('m'));
	if(arg) {
	  res = dkt_tool_set_md_type(
	    &(j->mdt), &(j->mde), &(j->exval), arg, j->app, j->msg, 1
	  );
	  if(!res) {
	    back = 0;
	  }
	} else {
	  back = 0;
	  j->exval = DKT_RESULT_ERR_OPTION;
	}
      }
      if(dk3opt_is_set(j->opt, dkT('c'))) {
        j->f_chk = 1;
      }
      if(dk3opt_is_set(j->opt, dkT('l'))) {
	j->f_lst = 1;
      }
      if(dk3opt_is_set(j->opt, dkT('i'))) {
        arg = dk3opt_get_short_arg(j->opt, dkT('i'));
	if(arg) {
	  res = dkt_tool_set_encoding(
	    j->app, &(j->enc_f), arg,
	    dk3app_get_input_stdin_encoding(j->app)
	  );
	  j->enc_s = j->enc_f;
	  if(!res) {
	    back = 0;
	  }
	} else {
	  back = 0;
	  j->exval = DKT_RESULT_ERR_OPTION;
	}
        if(dk3opt_is_set(j->opt, dkT('p'))) {
	  /* ERROR: -i and -p exclusive */
	  dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 0);
	  back = 0;
	  j->exval = DKT_RESULT_ERR_OPTION;
	}
      } else {
        if(dk3opt_is_set(j->opt, dkT('p'))) {
	  j->enc_s = j->enc_f = DK3_FILE_ENCODING_ASCII;
	}
      }
      if(dk3opt_is_set(j->opt, dkT('v'))) {
        j->l_vrb = 2;
        if(dk3opt_is_set(j->opt, dkT('q'))) {
	  /* ERROR: -v and -q exclusive */
	  dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 19);
	  back = 0;
	  j->exval = DKT_RESULT_ERR_OPTION;
	}
      } else {
        if(dk3opt_is_set(j->opt, dkT('q'))) {
	  j->l_vrb = 0;
	}
      }
    } else {
      /* ERROR */
      j->exval = DKT_RESULT_ERR_OPTION;
    }
  } else {
    /* ERROR */
    j->exval = DKT_RESULT_ERR_OPTION;
  }
  

#line 332 "dkt-chks.ctr"
  return back;
}



/**	Show a test result.
	@param	j	Job structure.
	@param	fn	File name.
	@param	msgi	Message index (17=success, 18=failure).
*/
static
void
dkt_chks_result(DKT_CHKS_J *j, dkChar const *fn, size_t msgi)
{
  if(j->f_first) {
    dk3sf_initialize_stdout();
  } j->f_first = 0;
  dk3sf_fputs(fn, stdout);
  dk3sf_fputc(dkT(':'), stdout);
  dk3sf_fputc(dkT(' '), stdout);
  dk3sf_fputs((j->msg)[msgi], stdout);
  dk3sf_fputc(dkT('\n'), stdout);
}



/**	Check whether the line is a control instruction.
	@param	p	Line pointer.
	@return	1 for control instruction, 0 for normal line.
*/
static
int
dkt_chks_is_instruction(dkChar const *p)
{
  int back = 0;
  if(*p == dkT('#')) {
    if((p[1] == dkT(' ')) || (p[1] == dkT('\t'))) {
      back = 1;
    }
  }
  return back;
}



/**	Handler for one line from a check list file.
	@param	vj	Job structure.
	@param	il	Input line to process.
	@return	1 on success, 0 on error (can continue), -1 on error (abort).
*/
static
int
dkt_chks_line_handler_2(void *vj, dkChar *il)
{
  int			 back = 0;
  dkChar		*p1 = NULL;	/* Size or checksum. */
  dkChar		*p2 = NULL;	/* File name. */
  int			 res = 0;	/* Checksum result. */
  dk3_stat_t		 stb;		/* Stat buffer for file. */
  DKT_CHKS_J		 *j = NULL;	/* Job structure. */
  dkChar		 bu[256];	/* Message digest as dkChar text. */
  char			 md[256];	/* Message digest as 8-bit text. */
  dk3_um_t		 um;		/* Size found in input line. */
  

#line 396 "dkt-chks.ctr"
  j = (DKT_CHKS_J *)vj;
  p1 = dk3str_start(il, NULL);
  if(p1) {
    dk3str_delnl(p1);
    if(dkt_chks_is_instruction(p1)) {
      p1++;
      p1 = dk3str_start(p1, NULL);
      if(p1) {
        p2 = dk3str_chr(p1, dkT('='));
	if(p2) {
	  *(p2++) = dkT('\0');
	  p2 = dk3str_start(p2, NULL);
	}
	dk3str_chomp(p1, NULL);
	if(p2) { dk3str_chomp(p2, NULL); }
	dk3str_normalize(p1, NULL, dkT('-'));
	switch(dk3str_array_index(dkt_chks_conf_kw, p1, 0)) {
	  case 3: {
	    if(p2) {
	      res = dkt_tool_set_md_type(
	        &(j->mdt), &(j->mde), &(j->exval), p2, j->app, j->msg, 1
	      );
	      if(res) {
	        back = 1;
	      }
	    } else {
	      /* ERROR: Missing value! */
	      dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 20);
	      j->exval = DKT_RESULT_ERR_INPUT;
	    }
	  } break;
	  default: {
	    /* ERROR: Unknown control instruction */
	    dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 21, 22, p1);
	    j->exval = DKT_RESULT_ERR_INPUT;
	  } break;
	}
      } else {				

#line 434 "dkt-chks.ctr"
        /* ERROR: Not produced by dkt checksum */
        j->exval = DKT_RESULT_ERR_INPUT;
      }
    } else {
      p2 = dk3str_next(p1, NULL);
      if(p2) {
        res = 0;
        dk3str_correct_filename(p2);
	if(dk3sf_stat_app(&stb, p2, j->app)) {
	  switch((stb.ft) & (~(DK3_FT_SYMLINK))) {
	    case DK3_FT_REGULAR: {
              if(j->f_sz) {			

#line 446 "dkt-chks.ctr"
	        if(dk3ma_uintmax_from_string(&um, p1)) {
		  if(um == stb.sz) {
		    res = back = 1;
		  }
		} else {
		  /* ERROR: p1 is not a number! */
		  dk3app_log_i3(j->app, DK3_LL_ERROR, 141, 142, p1);
		  j->exval = DKT_RESULT_ERR_INPUT;
		}
	      } else {			

#line 456 "dkt-chks.ctr"
		res = dk3checksum_build_app(
		  md, sizeof(md), p2, j->mdt, j->mde, j->app
		);
		if(res) {
		  res = 0;
		  if(
		    dk3str_cnv_c8_to_str_app(bu,DK3_SIZEOF(bu,dkChar),md,j->app)
		  )
		  { 

#line 465 "dkt-chks.ctr"
		    switch(j->mde) {
		      case DK3_DATA_ENCODING_HEX: {
		        if(dk3str_casecmp(p1, bu) == 0) {
			  res = back = 1;
			}
		      } break;
		      default: {
		        if(dk3str_cmp(p1, bu) == 0) {
			  res = back = 1;
			}
		      } break;
		    } 

#line 477 "dkt-chks.ctr"
		  }
		  else
		  {
		    /* ERROR: Failed to convert MD to string! */
		    j->exval = DKT_RESULT_ERR_UNSPECIFIC;
		  }
		} else {
		  /* ERROR: Failed to build checksum! */
		  j->exval = DKT_RESULT_ERR_UNSPECIFIC;
		}
	      }
	    } break;
	    default: {
	      /* ERROR: Not a regular file! */
	      dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, p2);
	      j->exval = DKT_RESULT_ERR_FILENAME;
	    } break;
	  }
	} else {
	  /* ERROR: No such file or directory! */
	  j->exval = DKT_RESULT_ERR_FILENAME;
	}
	if(res) {
	  if(j->l_vrb > 1) {
	    /* SUCCESS message, */
	    dkt_chks_result(j, p2, 17);
	  }
	} else {
	  if(j->l_vrb > 0) {
	    /* ERROR message. */
	    dkt_chks_result(j, p2, 18);
	  }
	  j->f_msm = 1;
	}
      } else {
        /* ERROR: No file name! */
	dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 25);
	j->exval = DKT_RESULT_ERR_INPUT;
      }
    }
  } else {				

#line 518 "dkt-chks.ctr"
    /* Empty line, not produced by dkt checksum. */
    j->exval = DKT_RESULT_ERR_INPUT;
  } 

#line 521 "dkt-chks.ctr"
  return back;
}



/**	Perform checks for a check list.
	@param	j	Job structure.
*/
static
void
dkt_chks_check(DKT_CHKS_J *j)
{
  dkChar	ib[2 * DK3_MAX_PATH + 256]; /* Input line from check list. */
  dkChar	bu[DK3_MAX_PATH];	/* File name buffer. */
  int		nfn = 0;		/* Number of cmd line args. */
  int		i = 0;			/* Current arg index. */
  dkChar const	*arg = NULL;		/* Current arg. */
  dk3_dir_t	*fne = NULL;		/* File name expander. */
  dkChar const	*en = NULL;		/* Current entry name. */
  dk3_stat_t	*es = NULL;		/* Entry stat buffer. */
  int		found = 0;		/* Flag: At least one file found. */
  

#line 543 "dkt-chks.ctr"
  if(dk3opt_get_num_args(j->opt) > 0) {	

#line 544 "dkt-chks.ctr"
    nfn = dk3opt_get_num_args(j->opt);
    for(i = 0; i < nfn; i++) {
      arg = dk3opt_get_arg(j->opt, i);
      if(arg) {
        if(dk3str_len(arg) < DK3_SIZEOF(bu,dkChar)) {
	  dk3str_cpy(bu, arg);
	  dk3str_correct_filename(bu);
	  if(dk3sf_must_expand(bu)) {
	    fne = dk3dir_fne_open_app(bu, j->app);
	    if(fne) {
	      found = 0;
	      while(dk3dir_get_next_file(fne)) {
	        en = dk3dir_get_fullname(fne);
		es = dk3dir_get_stat(fne);
		if((en) && (es)) {
		  switch((es->ft) & (~(DK3_FT_SYMLINK))) {
		    case DK3_FT_REGULAR: {
		      (void)dk3stream_process_filename_lines_app(
		        (void *)j,
			dkt_chks_line_handler_2,
			en,
			ib,
			DK3_SIZEOF(ib,dkChar),
			dk3app_get_encoding(j->app),
			j->enc_f,
			j->app
		      );
		    } break;
		    default: {
		      /* ERROR: Not a regular file! */
		      dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, en);
		      j->exval = DKT_RESULT_ERR_FILENAME;
		    } break;
		  }
		} else {
		  /* BUG */
		  j->exval = DKT_RESULT_ERR_UNSPECIFIC;
		}
	      }
	      dk3dir_close(fne);
	    } else {
	      /* ERROR: No file name expander! */
	      j->exval = DKT_RESULT_ERR_FILENAME;
	    }
	  } else {
	    (void)dk3stream_process_filename_lines_app(
	        (void *)j,
		dkt_chks_line_handler_2,
		bu,
		ib,
		DK3_SIZEOF(ib,dkChar),
		dk3app_get_encoding(j->app),
		j->enc_f,
		j->app
	    );
	  }
	} else {
	  /* ERROR: Name too long! */
	  dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, arg);
	  j->exval = DKT_RESULT_ERR_FILENAME;
	}
      } else {
        /* BUG */
	j->exval = DKT_RESULT_ERR_UNSPECIFIC;
      }
    }
  } else {				

#line 611 "dkt-chks.ctr"
    (void)dk3app_process_stdin_lines(
      j->app,
      (void *)j,
      dkt_chks_line_handler_2,
      ib,
      DK3_SIZEOF(ib,dkChar),
      j->enc_s
    );
  }
  if(j->f_msm) {
    /* ERROR: Mismatches found! */
    dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 26);
    j->exval = DKT_RESULT_ERR_MISMATCH;
  } 

#line 625 "dkt-chks.ctr"
}



/**	Show checksum/size and name for one file.
	@param	j	Job structure.
	@param	fn	File name.
	@param	stb	Stat buffer for that file.
*/
static
void
dkt_chks_show_one_file(DKT_CHKS_J *j, dkChar const *fn, dk3_stat_t *stb)
{
  dkChar	bu[256];	/* Corrected file name. */
  dkChar const	*p1 = NULL;	/* File name or option name. */
  dkChar const	*p2 = NULL;	/* Option argument. */
  char		md[256];	/* Checksum buffer. */
#if DK3_CHAR_SIZE > 1
  char		*ptr = NULL;	/* Walk through md. */
#endif
  int		res = 0;
  

#line 647 "dkt-chks.ctr"
  if(j->f_sz) {				

#line 648 "dkt-chks.ctr"
    if(dk3ma_uintmax_to_string(bu, DK3_SIZEOF(bu,dkChar), stb->sz)) {
      if(j->f_first) {
        dk3sf_initialize_stdout();
      } j->f_first = 0;			

#line 652 "dkt-chks.ctr"
      dk3sf_fputs(bu, stdout);
      dk3sf_fputc(dkT(' '), stdout);
      dk3sf_fputs(fn, stdout);
      dk3sf_fputc(dkT('\n'), stdout);
    } else {
      /* ERROR */
      dk3app_log_i1(j->app, DK3_LL_ERROR, 162);
      j->exval = DKT_RESULT_ERR_UNSPECIFIC;
    }
  } else {
    res = dk3checksum_build_app(md, sizeof(md), fn, j->mdt, j->mde, j->app);
    if(res) {			

#line 664 "dkt-chks.ctr"
      if(j->f_first) {	

#line 665 "dkt-chks.ctr"
	dk3sf_initialize_stdout();
	/* PRINT DIGEST TYPE AND ENCODING */
	p1 = dk3checksum_get_type_name(j->mdt);
	p2 = dk3enc_get_data_encoding_name(j->mde);
	dk3sf_fputs((j->kwnl)[5], stdout);
	dk3sf_fputs(p1, stdout);
	dk3sf_fputs((j->kwnl)[4], stdout);
	dk3sf_fputs(p2, stdout);
	dk3sf_fputc(dkT('\n'), stdout);
      } j->f_first = 0;		

#line 675 "dkt-chks.ctr"
#if DK3_CHAR_SIZE > 1
      ptr = md;
      while(*ptr) { dk3sf_fputc((dkChar)(*(ptr++)), stdout); }
#else
      fputs(md, stdout);
#endif
      dk3sf_fputc(dkT(' '), stdout);
      dk3sf_fputs(fn, stdout);
      dk3sf_fputc(dkT('\n'), stdout);
    } else {			

#line 685 "dkt-chks.ctr"
      /* ERROR: Failed to build checksum! */
      j->exval = DKT_RESULT_ERR_INPUT;
    }
  } 

#line 689 "dkt-chks.ctr"
}



/**	Process one directory.
	@param	j	Job structure.
	@param	fn	Directory name.
	@param	stb	Stat buffer containing information about directory.
*/
static
void
dkt_chks_show_directory(DKT_CHKS_J *j, dkChar const *fn, dk3_stat_t *stb)
{
  DKT_CHKS_DIR	*curdir = NULL;	 /* Current directory chain element. */
  DKT_CHKS_DIR	*nextdir = NULL; /* Next directory chain element. */
  dkChar const	*en = NULL;	 /* Current directory entry name. */
  dk3_stat_t	*es = NULL;	 /* Current directory entry stat buffer. */
  int		 can_go_sub = 0; /* Flag: Can go down into subdirectory. */
  

#line 708 "dkt-chks.ctr"
  curdir = dkt_chks_dir_new(NULL, fn, stb, j->app);
  if(curdir) {
    while(curdir) {
      if(dk3dir_get_next_directory(curdir->dir)) {
        en = dk3dir_get_fullname(curdir->dir);
	es = dk3dir_get_stat(curdir->dir);
	if((en) && (es)) {
	  can_go_sub = 1;
#if DK3_HAVE_INODES
	  nextdir = curdir;
	  while((nextdir) && (can_go_sub)) {
	    if((nextdir->ds).inode == es->inode) {
	      if((nextdir->ds).device == es->device) {
	        can_go_sub = 0;
	      }
	    }
	    if(can_go_sub) {
	      nextdir = (DKT_CHKS_DIR *)(nextdir->parent);
	    }
	  }
#endif
	  if(can_go_sub) {
	    nextdir = dkt_chks_dir_new(curdir, en, es, j->app);
	    if(nextdir) {
	      curdir = nextdir;
	    } else {
	      /* ERROR: Memory */
	      j->exval = DKT_RESULT_ERR_MEMORY;
	    }
	  }
	} else {
	  /* BUG */
	  j->exval = DKT_RESULT_ERR_UNSPECIFIC;
	}
      } else {		

#line 743 "dkt-chks.ctr"
        while(dk3dir_get_next_file(curdir->dir)) {
	  en = dk3dir_get_fullname(curdir->dir);
	  es = dk3dir_get_stat(curdir->dir);
	  if((en) && (es)) {
	    switch((es->ft) & (~(DK3_FT_SYMLINK))) {
	      case DK3_FT_REGULAR: {
	        dkt_chks_show_one_file(j, en, es);
	      } break;
	      default: {
	        /* ERROR: Not a regular file! */
		dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, en);
		j->exval = DKT_RESULT_ERR_FILENAME;
	      } break;
	    }
	  } else {
	    /* BUG */
	    j->exval = DKT_RESULT_ERR_UNSPECIFIC;
	  }
	}
        nextdir = (DKT_CHKS_DIR *)(curdir->parent);
	dkt_chks_dir_delete(curdir);
	curdir = nextdir;
      }
    }
  } else {
    /* ERROR */
    j->exval = DKT_RESULT_ERR_MEMORY;
  } 

#line 771 "dkt-chks.ctr"
}



/**	Process one file name.
	@param	j	Job structure.
	@param	fn	File name.
*/
static
void
dkt_chks_process_one_filename(DKT_CHKS_J *j, dkChar const *fn)
{
  dk3_stat_t	stb;	/* Stat buffer for file. */
  

#line 785 "dkt-chks.ctr"
  if(dk3sf_stat_app(&stb, fn, j->app)) {
    switch((stb.ft) & (~(DK3_FT_SYMLINK))) {
      case DK3_FT_DIRECTORY: {		

#line 788 "dkt-chks.ctr"
        dkt_chks_show_directory(j, fn, &stb);
      } break;
      case DK3_FT_REGULAR: {		

#line 791 "dkt-chks.ctr"
        dkt_chks_show_one_file(j, fn, &stb);
      } break;
      default: {			

#line 794 "dkt-chks.ctr"
        /* ERROR: Not a regular file! */
	dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, fn);
	j->exval = DKT_RESULT_ERR_FILENAME;
      } break;
    }
  } else {
    j->exval = DKT_RESULT_ERR_FILENAME;
  }
  

#line 803 "dkt-chks.ctr"
}



/**	Handler function for file list lines.
	@param	vj	Job structure.
	@param	il	Input line.
	@return	1 on success, 0 on error (can continue), -1 on error (abort).
*/
static
int
dkt_chks_line_handler_1(void *vj, dkChar *il)
{
  int		back = 1;
  int		found = 0;	/* Flag: At least one file found. */
  dk3_dir_t	*fne = NULL;	/* File name expander. */
  dkChar const	*en = NULL;	/* Entry name. */
  dkChar	*p1 = NULL;	/* Start of input line. */
  DKT_CHKS_J	*j = NULL;	/* Job structure. */

  j = (DKT_CHKS_J *)vj;
  p1 = dk3str_start(il, NULL);
  if(p1) {
    dk3str_delnl(p1);
    dk3str_chomp(p1, NULL);
    dk3str_correct_filename(p1);
    if(dk3sf_must_expand(p1)) {
      fne = dk3dir_fne_open_app(p1, j->app);
      if(fne) {
        while(dk3dir_get_next_directory(fne)) {
	  found = 1;
	  en = dk3dir_get_fullname(fne);
	  if(en) {
	    dkt_chks_process_one_filename(j, en);
	  } else {
	    /* BUG */
	    j->exval = DKT_RESULT_ERR_FILENAME;
	  }
	}
	while(dk3dir_get_next_file(fne)) {
	  found = 1;
	  en = dk3dir_get_fullname(fne);
	  if(en) {
	    dkt_chks_process_one_filename(j, en);
	  } else {
	    /* BUG */
	    j->exval = DKT_RESULT_ERR_FILENAME;
	  }
	}
        dk3dir_close(fne);
	if(!found) {
	  /* ERROR: No file name matching pattern! */
	  dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, p1);
	  j->exval = DKT_RESULT_ERR_FILENAME;
	}
      } else {
        /* ERROR: File name expander! */
	j->exval = DKT_RESULT_ERR_FILENAME;
      }
    } else {
      dkt_chks_process_one_filename(j, p1);
    }
  } else {
    j->exval = DKT_RESULT_ERR_INPUT;
  }
  return back;
}



/**	Process a file name list, either from file or standard input.
	@param	j	Job structure.
*/
static
void
dkt_chks_process_filename_list(DKT_CHKS_J *j)
{
  dkChar	bu[DK3_MAX_PATH];	/* Private copy of file name. */
  dkChar	ib[DK3_MAX_PATH];	/* Input line buffer. */
  dkChar const	*arg = NULL;		/* Current argument. */
  dkChar const	*en = NULL;		/* Entry name. */
  dk3_dir_t	*fne = NULL;		/* File name expander. */
  dk3_stat_t	*es = NULL;		/* Entry stat buffer. */
  int		found = 0;		/* Flag: At least one entry found. */
  int		nfn = 0;		/* Number of file names. */
  int		i = 0;			/* Current file name index. */
  

#line 890 "dkt-chks.ctr"
  if(dk3opt_get_num_args(j->opt) > 0) {	

#line 891 "dkt-chks.ctr"
    nfn = dk3opt_get_num_args(j->opt);
    for(i = 0; i < nfn; i++) {
      arg = dk3opt_get_arg(j->opt, i);
      if(arg) {
        if(dk3str_len(arg) < DK3_SIZEOF(bu,dkChar)) {
	  dk3str_cpy(bu, arg);
	  dk3str_correct_filename(bu);
	  if(dk3sf_must_expand(bu)) {
	    fne = dk3dir_fne_open_app(bu, j->app);
	    if(fne) {
	      while(dk3dir_get_next_file(fne)) {
		en = dk3dir_get_fullname(fne);
		es = dk3dir_get_stat(fne);
		if((en) && (es)) {
		  switch((es->ft) & (~(DK3_FT_SYMLINK))) {
		    case DK3_FT_REGULAR: {
		      found = 1;
		      (void)dk3stream_process_filename_lines_app(
		        (void *)j,
			dkt_chks_line_handler_1,
			en,
			ib,
			DK3_SIZEOF(ib,dkChar),
			dk3app_get_encoding(j->app),
			j->enc_f,
			j->app
		      );
		    } break;
		    default: {
		      j->exval = DKT_RESULT_ERR_FILENAME;
	      	      dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 23, 24, en);
		    } break;
		  }
		} else {
		  /* BUG */
		  j->exval = DKT_RESULT_ERR_FILENAME;
		}
	      }
	      if(!found) {
	        /* ERROR: No such file! */
		j->exval = DKT_RESULT_ERR_FILENAME;
		dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, bu);
	      }
	      dk3dir_close(fne);
	    } else {
	      /* ERROR: Failed to expand file name. */
	      j->exval = DKT_RESULT_ERR_FILENAME;
	    }
	  } else {
	    dk3stream_process_filename_lines_app(
	      (void *)j,
	      dkt_chks_line_handler_1,
	      bu,
	      ib,
	      DK3_SIZEOF(ib,dkChar),
	      dk3app_get_encoding(j->app),
	      j->enc_f,
	      j->app
	    );
	  }
	} else {
	  /* ERROR: Name too long! */
	  dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, arg);
	  j->exval = DKT_RESULT_ERR_FILENAME;
	}
      } else {
        /* BUG */
	j->exval = DKT_RESULT_ERR_UNSPECIFIC;
      }
    }
  } else {				

#line 962 "dkt-chks.ctr"
    (void)dk3app_process_stdin_lines(
      j->app,
      (void *)j,
      dkt_chks_line_handler_1,
      ib,
      DK3_SIZEOF(ib,dkChar),
      j->enc_s
    );
  } 

#line 971 "dkt-chks.ctr"
}



/**	Process the command line arguments.
	@param	j	Job structure.
*/
static
void
dkt_chks_process_argv_arguments(DKT_CHKS_J *j)
{
  int		nfn = 0;		/* Number of file names available. */
  int		i = 0;			/* Current argument index. */
  int		found = 0;		/* Flag: At least one file found. */
  dk3_dir_t	*fne = NULL;		/* File name expander. */
  dkChar const *arg = NULL;		/* Current argument. */
  dkChar const *en = NULL;		/* Entry name. */
  dkChar	bu[DK3_MAX_PATH];	/* Private file name copy. */
  if((nfn = dk3opt_get_num_args(j->opt)) > 0) {
    for(i = 0; i < nfn; i++) {
      arg = dk3opt_get_arg(j->opt, i);
      if(arg) {
        if(dk3str_len(arg) < DK3_SIZEOF(bu,dkChar)) {
	  dk3str_cpy(bu, arg);
	  dk3str_correct_filename(bu);
	  if(dk3sf_must_expand(bu)) {
	    fne = dk3dir_fne_open_app(bu, j->app);
	    if(fne) {
	      while(dk3dir_get_next_directory(fne)) {
	        found = 1;
		en = dk3dir_get_fullname(fne);
		if(en) {
		  dkt_chks_process_one_filename(j, en);
		} else {
		  /* BUG */
		  j->exval = DKT_RESULT_ERR_FILENAME;
		}
	      }
	      while(dk3dir_get_next_file(fne)) {
	        found = 1;
		en = dk3dir_get_fullname(fne);
		if(en) {
		  dkt_chks_process_one_filename(j, en);
		} else {
		  /* BUG */
		  j->exval = DKT_RESULT_ERR_FILENAME;
		}
	      }
	      dk3dir_close(fne);
	      if(!found) {
	        /* ERROR: No file name matching pattern! */
		j->exval = DKT_RESULT_ERR_FILENAME;
		dk3app_log_i3(j->app, DK3_LL_ERROR, 215, 216, bu);
	      }
	    } else {
	      j->exval = DKT_RESULT_ERR_FILENAME;
	    }
	  } else {
	    dkt_chks_process_one_filename(j, bu);
	  }
	} else {
	  /* ERROR: Name too long! */
	  j->exval = DKT_RESULT_ERR_FILENAME;
	  dk3app_log_i3(j->app, DK3_LL_ERROR, 65, 66, arg);
	}
      } else {
        /* BUG */
	j->exval = DKT_RESULT_ERR_UNSPECIFIC;
      }
    }
  } else {
    dkt_chks_process_one_filename(j, (j->kwnl)[4]);
  }
}



/**	Run checksum building or checking.
	@param	j	Job structure.
*/
static
void
dkt_chks_run(DKT_CHKS_J *j)
{
  

#line 1056 "dkt-chks.ctr"
  if(j->f_chk) {	

#line 1057 "dkt-chks.ctr"
    dkt_chks_check(j);
  } else {		

#line 1059 "dkt-chks.ctr"
    if(j->f_lst) {	

#line 1060 "dkt-chks.ctr"
      dkt_chks_process_filename_list(j);
    } else {		

#line 1062 "dkt-chks.ctr"
      dkt_chks_process_argv_arguments(j);
    }
  }
  

#line 1066 "dkt-chks.ctr"
}



int
dkt_check(
  dk3_app_t *app,
  dkChar const	       *sn,
  dkChar const * const *msg,
  dkChar const * const *kwnl,
  int what
)
{
  DKT_CHKS_J	j;
  int		back = DKT_RESULT_ERR_UNSPECIFIC;
  

#line 1082 "dkt-chks.ctr"
  dkt_chks_job_init(&j);
  j.app = app; j.msg = msg; j.kwnl = kwnl;
  j.f_sz = what;
  j.enc_s = dk3app_get_input_stdin_encoding(app);
  j.enc_f = dk3app_get_input_file_encoding(app);
  dkt_tool_read_conf(app, sn, (void *)(&j), dkt_chks_conf_line);
  if(dkt_chks_process_arguments(&j)) {
    j.exval = DKT_RESULT_OK;
    dkt_chks_run(&j);
  }
  dkt_chks_job_cleanup(&j);
  back = j.exval;
  

#line 1095 "dkt-chks.ctr"
  return back;
}



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

