/* Analizzatore di file multithreaded controllabile con segnali */

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <malloc.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>

#define READ_BUFFER 4096

char * dump_file = "./string_information";
char * file_to_check;
FILE * output_file;
int number_strings_to_check;
char ** strings_to_check;

int status;
int *partial_results;

struct _thread_info {
  int position;
  char *string;
};



void output_result () {
  int i;
  
  for (i=0; i< number_strings_to_check; i++)
	  if (partial_results[i] == -1) fprintf(output_file, "  %s: couldn't check\n", strings_to_check[i]);
	  else fprintf(output_file, "  %s: %d volte\n", strings_to_check[i], partial_results[i]);
  return;
} 

void output_final_result (int complete) {

  output_file = fopen(dump_file, "w");
  if (output_file == NULL) {
    printf("Cannot open dump file!\n");
    exit(-1);
  }

  if (complete) {
    fprintf(output_file, "Analysis complete\nOccurrences:\n");
  } else {
    fprintf(output_file, "Analysis Interrupted\nAt the interruption time I found the following occurrences:\n");
  }
  output_result();
  fclose(output_file);
  return;
}

void output_partial_result() {
 

  output_file = fopen(dump_file, "w");
  if (output_file == NULL) {
    printf("Cannot open dump file!\n");
    exit(-1);
  }
  
  fprintf(output_file, "Analysis still ongoing\nAt this time I already found the following occurrences:\n");
  output_result();
  fclose(output_file);
  return;
}

void suspend() {
  output_partial_result();
  kill(getpid(), SIGSTOP);
  return;
}

void terminate () {
  output_final_result(0);
  exit(0);
}


void lower_priority () {
  nice(10);
  return;
}


void * VerifyFile(void * info)
{
  int fd;
  int i,j;
  char buffer[2*READ_BUFFER];
  struct _thread_info * thread_info;
  int string_length = 0;

  thread_info = (struct _thread_info *) info;
  fd = open(file_to_check, O_RDONLY);
  //printf("Ordering thread active\n");
  //
  if (fd < 0)  {
	  printf("Can't open file %s\n",file_to_check);
	  partial_results[thread_info->position] = -1;
	  status = -1;
	  pthread_exit((void *)&status);
  }
  
   
  string_length = strlen (thread_info->string);
  if (string_length > READ_BUFFER) {
	  printf("String %s too long\n",thread_info->string);
	  partial_results[thread_info->position] = -1;
	  status = -1;
	  pthread_exit((void *)&status);

  }
  
  i = READ_BUFFER;
  j = read(fd, &buffer[READ_BUFFER], READ_BUFFER);
  while (j != 0) {
	  
    for (; i<((READ_BUFFER + j) - string_length + 1); i++) {
      if (strncmp(thread_info->string, &buffer[i], string_length) == 0) {
	    partial_results[thread_info->position]++;
	    i = i + string_length - 1;

      }
    }
    
    memcpy(&buffer[READ_BUFFER - string_length + 1], &buffer[(READ_BUFFER + j) - string_length + 1], (READ_BUFFER + j) - i);
    i = READ_BUFFER - string_length + 1;

    j = read(fd, &buffer[READ_BUFFER], READ_BUFFER);
  }
  
  status = 0;
  pthread_exit((void *)&status);
} 


int main (int argc, char *argv[])
{
  int i, j;
  void * status;
  pthread_t *active_threads;
  struct _thread_info * thread_info = NULL;

  signal (SIGTERM, terminate);
  signal (SIGINT, terminate);
  signal (SIGTSTP, suspend);
  signal (SIGHUP, terminate);
  signal (SIGUSR1, output_partial_result);
  signal (SIGXCPU, lower_priority);

  if (argc < 3) {
	  printf("Usage: analyzer file first_string [other_strings ...]\n");
	  exit(-1);
  }
  
  
  active_threads = malloc (sizeof(pthread_t) * (argc - 2));
  partial_results = malloc (sizeof(int) * (argc - 2));

  file_to_check = argv[1];
  number_strings_to_check = argc - 2;
  strings_to_check = &argv[2];
  
  for (i=0; i<number_strings_to_check; i++) {
    thread_info = malloc (sizeof(struct _thread_info));
    thread_info->position = i;
    thread_info->string = argv[i+2];
    j=pthread_create(&active_threads[i], NULL, VerifyFile, (void *)thread_info);
    if (j) {
        printf("cannot create thread for error %d\n", j);
        exit(-1);
    }
  }

  for (i=0; i<(argc-2); i++) pthread_join(active_threads[i], &status);
  output_final_result(1);

  exit(0);
}

