                      /* singlefile.c, 29 Mar 13 */

/*
 * Process the numeric literals in one file.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>
#include <errno.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 "comments.h"
#include "printnum.h"
#include "matchnum.h"
#include "usage.h"
#include "words.h"
#include "needs.h"
#include "utils.h"
#include "arccomp.h"
#include "singlefile.h"


#define MAX_FILE_BUFF_SIZE (5*1024)
#define MAX_FILE_SUFF_SIZE 200
#define MAX_FILE_SUFF_LEN 6

DIGIT_CHAR cur_char,
           last_tok,
	   last_last_tok;
BOOL do_print_location = TRUE,
     do_mk_ndb = FALSE,
     do_space_sep = FALSE,
     do_ignore_comments = FALSE,
     mk_ndb_all = FALSE,
     have_printed_filename,
     has_dp;
fp_t cur_fp_val;
int other_toks;
char *cur_filename;
int file_bytes_read,
    next_ch_offset,
    line_count;


static BOOL start_mk_ndb;   // Have we output a value for the current file?
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];
static get_buff_func_type get_next_input_buff_func;

static int num_file_suff;
static char file_suff_pat[MAX_FILE_SUFF_SIZE];


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)
{
static char *input_buff;

if (next_ch_offset == file_bytes_read)
   {
   input_buff=get_next_input_buff_func();
   next_ch_offset=0;
   }

/*
 * The main loop tests file_bytes_read != 0 and when this condition
 * is false next_ch_offset is allowed to be greater than file_bytes_read
 * because 'junk' characters are available to be read.
 */
// TODO: Handle wide characters
cur_char=input_buff[next_ch_offset];
next_ch_offset++;

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


void skip_to_eol(void)
{
do  // Skip to end-of-line
   get_next_char();
   while ((cur_char != '\n') && (file_bytes_read != 0));
}


void fortran_comment(void)
{
line_count--;  // Don't count the first newline twice.
while (cur_char == '\n')
   {
   int line_pos;

   line_count++;
   get_next_char();
   line_pos=1;
   while (isspace(cur_char) && (line_pos < 7))
      {
      line_pos++;
      get_next_char();
      }
   if (line_pos == 7)
      return;
   if (!((cur_char == 'C') || (cur_char == 'c')))
      return;
   skip_to_eol();
   }
}


void chk_comment(void)
{
get_next_char();
// Is there a second comment character and if so do we have it?
if ((second_comment_char != 0) && (cur_char != second_comment_char))
   return;
skip_to_eol();
}


BOOL is_file_suffix(void)
{
/*
 * If a known file suffix occurs after the decimal point
 * assume we have a filename rather than a number.
 */
int pnum;
char *cur_file_suff = file_suff_pat;
char suff_str[MAX_FILE_SUFF_LEN+1];
int cur_suff_len = 0;

while (is_ident_ch(cur_char))
   if (cur_suff_len == MAX_FILE_SUFF_LEN)
      return FALSE;
   else
      {
      suff_str[cur_suff_len]=tolower(cur_char);
      cur_suff_len++;
      get_next_char();
      }

if (cur_suff_len == 0)
   return FALSE;

suff_str[cur_suff_len]='\0';

for (pnum=0; pnum < num_file_suff; pnum++)
   {
   if (pnum != 0)
      cur_file_suff+=1+strlen(cur_file_suff);
// printf("%s %d\n", suff_str, fnmatch(cur_file_suff, suff_str, 0));
   if (fnmatch(cur_file_suff, suff_str, 0) == 0)
      return TRUE;
   }

return FALSE;
}


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(BOOL start_dp)
{
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 (start_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);
   if (has_dp && is_file_suffix())
      return FALSE;
   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");
   }
}


static void process_number(BOOL start_dp)
{
if (!get_number(start_dp))
   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)))
      {
      print_location();
      print_number(actual_mantissa, num_mant_digits, actual_exponent);
      printf("\n");
      }
   }
}


void init_file_processing(get_buff_func_type get_buff_func)
{
get_next_input_buff_func=get_buff_func;

next_ch_offset=0;
file_bytes_read=0;
}


void analyse_file(get_buff_func_type get_buff_func,
		  char *filename)
{
if (do_usage_file)
   cnt_file_processed(filename);
if (do_ignore_comments)
   get_lang_comment_info(filename);
cur_filename=filename;
have_printed_filename=FALSE;
line_count=1;
other_toks=0;
init_file_processing(get_buff_func);
get_next_char();
while (file_bytes_read != 0)
   {
   if (isspace(cur_char))
      {
      if (cur_char == '\n')
	 {
	 line_count++;
         if (do_fortran_comments)
	    fortran_comment();
	 else
	    get_next_char();
	 }
      else
	 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(FALSE);
   else if (is_dec_point(cur_char))
      {
      get_next_char();
      if (is_start_digit(cur_char))
	 process_number(TRUE);
      }
   else
      {
      if ((cur_char == first_comment_char) && do_eol_comments)
	 chk_comment();
      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();

finish_needs();
}


static char * read_file_buff(void)
{
static char file_buff[MAX_FILE_BUFF_SIZE];

file_bytes_read = fread(file_buff, 1, sizeof(file_buff), stdin);

// printf("bytes read %d\n", file_bytes_read);

return file_buff;
}


void process_file(CHAR *filename)
{
if (mk_ndb_all)
   {
   start_mk_ndb=TRUE;
   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)
      {
      print_warning("freopen failed (errno=%d)\n", errno);
      return;
      }
   }
if (is_arc_comp(cur_filename))
   handle_arc_comp(cur_filename);
else
   analyse_file(read_file_buff, cur_filename);
}


static void add_file_suffix(char *suff_str, int *cur_file_suff_len)
{
if (save_arg_info(file_suff_pat, cur_file_suff_len, MAX_FILE_SUFF_SIZE,
                suff_str, "Size of file suffixes exceeded\n"))
   num_file_suff++;
}


void init_sing_file(void)
{
int dummy_file_suff_len = 0;

fp_init(&cur_fp_val);

add_file_suffix("asp", &dummy_file_suff_len);
add_file_suffix("aspx", &dummy_file_suff_len);
add_file_suffix("gif", &dummy_file_suff_len);
add_file_suffix("htm", &dummy_file_suff_len);
add_file_suffix("html", &dummy_file_suff_len);
add_file_suffix("jpeg", &dummy_file_suff_len);
add_file_suffix("jpg", &dummy_file_suff_len);
add_file_suffix("jsp", &dummy_file_suff_len);
add_file_suffix("mp3", &dummy_file_suff_len);
add_file_suffix("pdf", &dummy_file_suff_len);
add_file_suffix("php", &dummy_file_suff_len);
add_file_suffix("png", &dummy_file_suff_len);
add_file_suffix("shtml", &dummy_file_suff_len);
add_file_suffix("stm", &dummy_file_suff_len);
add_file_suffix("svg", &dummy_file_suff_len);
add_file_suffix("txt", &dummy_file_suff_len);
add_file_suffix("xls", &dummy_file_suff_len);
add_file_suffix("xml", &dummy_file_suff_len);
}

