                           /* chkfile.c, 14 Apr 10 */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
 
#include "global.h"
#if HAVE_FNMATCH_H
#include <fnmatch.h>
#endif
#include "errors.h"
#include "isprop.h"
#include "fltval.h"
#include "../hash/hashtable.h"
#include "hashtab.h"
#include "printnum.h"
#include "matchnum.h"
#include "usage.h"
#include "words.h"
#include "chkfile.h"


DIGIT_CHAR cur_char,
           last_tok,
	   last_last_tok;
BOOL do_trace_progress = TRUE,
     do_print_location = FALSE,
     do_mk_ndb = FALSE,
     do_space_sep = FALSE,
     mk_ndb_all = FALSE,
     have_printed_filename,
     has_dp;
fp_t cur_fp_val;
char *cur_filename;
int num_arg_files = 0;
int num_arg_patterns = 0;
int other_toks;
char arg_files[MAX_ARG_FILE_LEN];
char arg_patterns[MAX_ARG_PATTERN_LEN];


static BOOL last_was_dp;
static BOOL start_mk_ndb;
static int num_exp_digits;
static int num_mant_digits;
static int num_dp;
static DIGIT_CHAR actual_exponent[MAX_NUM_DIGITS];
static DIGIT_CHAR actual_mantissa[MAX_NUM_DIGITS];


void init_seen_numbers(void)
{
if (saved_htable != NULL)
   hashtable_destroy(saved_htable, TRUE);
init_hash_table();
}


void save_mant_digit(DIGIT_CHAR ch)
{
if (is_dec_point(ch))
   num_dp++;
if (num_mant_digits != MAX_NUM_DIGITS)
   {
   actual_mantissa[num_mant_digits]=ch;
   num_mant_digits++;
   }
}


void save_exp_digit(DIGIT_CHAR ch)
{
if (num_exp_digits != MAX_NUM_DIGITS)
   {
   actual_exponent[num_exp_digits]=ch;
   num_exp_digits++;
   }
}


void get_next_char(void)
{
cur_char=fgetc(stdin); // Update to handle wide characters

if (do_trace_input)
   printf("%c", cur_char);
}


void get_hex_literal(void)
{
while (is_num_digit(cur_char) ||
       ((cur_char >= 'a') && (cur_char <= 'f')) ||
       ((cur_char >= 'A') && (cur_char <= 'F')))
   {
   get_next_char();
   }
}


BOOL get_number(void)
{
BOOL last_not_space = TRUE,
     seen_exp_digit;

has_dp=FALSE;
num_dp=0;
num_exp_digits=0;
num_mant_digits=0;
/*
 * Save start_digit, which may not be contained in the set
 * allowed by is_num_digit.
 */
if (last_was_dp)
   save_mant_digit(DP_CHAR);
save_mant_digit(cur_char);
get_next_char();
if (do_space_sep && (cur_char == ' ')) // Not tab, etc, only space character
   get_next_char();
while (is_num_digit(cur_char))
   {
   save_mant_digit(cur_char);
   get_next_char();
   if (last_not_space && do_space_sep && (cur_char == ' '))
      {
      get_next_char();
      last_not_space=FALSE;
      }
   else
      last_not_space=TRUE;
   }

if ((num_mant_digits == 1) &&
    ((cur_char == 'x') || (cur_char == 'X')))
   {
   get_next_char();
   get_hex_literal();
   return FALSE;  // Not yet supported
   }

if (num_dp > 1)
   return FALSE;

if (num_mant_digits == MAX_NUM_DIGITS)
   num_mant_digits--;
actual_mantissa[num_mant_digits]=NULL_CHAR;

// printf("get number %s\n", actual_mantissa);

if (!is_exp_prefix(cur_char))
   {
   has_dp= (num_dp==1);
   actual_exponent[0]=NULL_CHAR;
   if (!string_to_fp(&cur_fp_val, actual_mantissa, num_mant_digits,
							 actual_exponent))
      {
      print_warning("Unable to convert input number\n");
      return FALSE;
      }
   return TRUE;
   }

get_next_char();  // Skip exponent character
if (do_space_sep && (cur_char == ' '))
   get_next_char();
if (is_exp_sign(cur_char))
   {
   save_exp_digit(cur_char);
   get_next_char();
   if (do_space_sep && (cur_char == ' '))
      get_next_char();
   }

while (is_exp_digit(cur_char))
   {
   seen_exp_digit=TRUE;
   save_exp_digit(cur_char);
   get_next_char();
   }

if (!seen_exp_digit)
   return FALSE;

has_dp=TRUE; // presence of one exponent digit is considered equivalent
if (num_exp_digits == MAX_NUM_DIGITS)
   num_exp_digits--;
actual_exponent[num_exp_digits]=NULL_CHAR;
// printf("get number_E %s\n", actual_exponent);

if (!string_to_fp(&cur_fp_val, actual_mantissa, num_mant_digits,
							 actual_exponent))
   {
   print_warning("Unable to convert input number: %se%s\n", actual_mantissa, actual_exponent);
   return FALSE;
   }

return TRUE;
}


void mk_ndb(DIGIT_CHAR mantissa[MAX_NUM_DIGITS],
            int len_mantissa,
	    DIGIT_CHAR exponent[MAX_NUM_DIGITS])
{
// len_mantissa will include at least one decimal point
if ((len_mantissa >= ndb_signif_digits) &&
    !save_match(NDB_MATCH, NULL, cur_fp_val, mantissa, len_mantissa, exponent))
   {
   if (start_mk_ndb)
      {
      start_mk_ndb=FALSE;
      printf("! title %s\n", cur_filename);
      }
   print_number(actual_mantissa, num_mant_digits, actual_exponent);
   printf("\n");
   }
}


void process_number(void)
{
if (!get_number())
   return;
if (do_mk_ndb)
   {
   if (has_dp)
      mk_ndb(actual_mantissa, num_mant_digits, actual_exponent);
   }
else
   {
   match_number(actual_mantissa, num_mant_digits, actual_exponent);
   if (has_dp && (num_mant_digits >= print_signif_digits) &&
       (do_print_all || (do_print_no_match && !found_match)))
      {
      if (do_trace_progress && !have_printed_filename)
	 {
         printf("%s\n", cur_filename);
	 have_printed_filename=TRUE;
	 }
      print_number(actual_mantissa, num_mant_digits, actual_exponent);
      printf("\n");
      }
   }
}


void process_file(CHAR *filename)
{
BOOL cur_is_dp = FALSE;

start_mk_ndb=TRUE;

if (mk_ndb_all)
   init_seen_numbers();

if (filename == NULL)
   cur_filename="stdin";
else
   {
   cur_filename=filename;
// Martin Moene reported problems with close/fopen under WinGW
// but that the following works.
   if (freopen(filename, "r", stdin) == NULL)
      return;
   }
if (do_usage_file)
   cnt_file_processed(filename);
have_printed_filename=FALSE;
other_toks=0;
get_next_char();
while (!feof(stdin))
   {
   last_was_dp=cur_is_dp;
   cur_is_dp=is_dec_point(cur_char);
   if (isspace(cur_char))
      get_next_char();
   else if (is_start_letter(cur_char))
      {
      process_word();
      other_toks++;
      last_tok='I';
      }
   else if (is_start_digit(cur_char))
      process_number();
   else
      {
// Treat as punctuation and single character punctuation at that.
      last_last_tok=last_tok;
      last_tok=cur_char;
      get_next_char();
      other_toks++;
      }
   }
if (do_print_word)
   reset_word_buffer();
reset_need_counts();
}


static BOOL match_file_pattern(char *filename)
{
int pnum;
char *file_pattern = arg_patterns;

#if HAVE_FNMATCH_H
for (pnum=0; pnum < num_arg_patterns; pnum++)
   {
   if (pnum != 0)
      file_pattern+=1+strlen(file_pattern);
// printf("%s %d\n", filename, fnmatch(file_pattern, filename, 0));
   if (fnmatch(file_pattern, filename, 0) == 0)
      return TRUE;
   }
return FALSE;
#else
return TRUE;
#endif
}


void walk_dir(const char *dir)
{
struct dirent *entry;
DIR *dir_ptr;
struct stat fs;
char abs_path[MAX_FILENAME_LEN];

if (!(dir_ptr = opendir(dir)))
   return;
while ((entry = readdir(dir_ptr)))
   {
// printf("entry %s\n", entry->d_name);
   if ((strcmp(".", entry->d_name) == 0) ||
       (strcmp("..", entry->d_name) == 0))
       continue;
   if (stat(entry->d_name, &fs) < 0)
       continue;
   if (S_ISDIR(fs.st_mode))
      {
      total_dir_seen++;
      if (getcwd(abs_path, MAX_FILENAME_LEN) == NULL)
	 continue;
      if (do_trace_progress)
	 printf("%s/%s\n", abs_path, entry->d_name);
      if (chdir(entry->d_name) < 0)
	 return;
      walk_dir(".");
      if (do_trace_progress)
	 printf("%s\n", abs_path);
      if (chdir(abs_path) < 0)
	 return;
      }
    else
      {
      if ((num_arg_patterns == 0) ||
	  match_file_pattern(entry->d_name))
         process_file(entry->d_name);
      else
	 {
	 if (do_usage_file)
	    cnt_file_not_processed(entry->d_name);
         }
      }
    }
closedir(dir_ptr);
}


void chk_number(const char *num_str)
{
}


void process_arg_files(void)
{
struct stat fs;
char *dir_name;
int anum;

if (num_arg_files == 0)
   process_file(NULL); // Input assumed to come from stdin
else
   {
   // Loop over names given as options
   dir_name=arg_files;
   for (anum=0; anum < num_arg_files; anum++)
      {
      if (anum != 0)
         dir_name+=1+strlen(dir_name);
      if (stat(dir_name, &fs) < 0)
         {
         if (is_number(dir_name))
	    chk_number(dir_name);
         else
	    print_warning("Cannot stat %s\n", dir_name);
         return;
         }
      if (S_ISDIR(fs.st_mode))
         {
         if (chdir(dir_name) < 0)
	    print_warning("Unable to change directory to %s\n", dir_name);
         else
	    walk_dir(".");
         }
      else
         process_file(dir_name);
      }
   }
}


void init_chkfile(void)
{
fp_init(&cur_fp_val);
}

