/*

  file.cc

  file access subroutines for xlogmaster.cc
  Copyright (C) 1998 Georg C. F. Greve
  This is a GNU program
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  
  Contact: 
           mailto:xlogmaster-bugs@gnu.org
           http://www.gnu.org/software/xlogmaster/
  Secondary sources:
           http://porter.desy.de/~greve/xlogmaster/
           http://www.fusebox.hanse.de/xlogmaster/
	 
*/

/*{{{  Header  */

#include "../config.h"
#include "sysinc.H"
#include "logclass.H"
#include "file.H"
#include "extern.H"

/*}}}*/

/*{{{  Read Configuration  */
int read_configuration(char* fname){
  int fd = open(fname, O_RDONLY);
  if ( fd == -1 ) return FALSE;
  fstat( fd,  &status );
  int length = (int) status.st_size;
  char* buffer = new char[length+2];

  fillups = 0;
  int got = (int) read(fd, buffer, length);
  close(fd);
  if ( got != length ){
    delete buffer;
    return FALSE;
  }
  buffer[length] = 0; // Make it zero terminated

  // Now we got the file completely in "buffer" and it has "length" bytes...

  int index = 0;
  int position = 0;
  int new_mode;               
  char *new_filename;         
  char *new_help;             
  char *new_buttontext;       
  int new_interval;       
  char* new_int_string;

  while ( index < length-5 ){
    char **new_filter = NULL;
    char **new_filter_exec = NULL;
    int *new_filter_mode = NULL;
    char **new_commandline = NULL;

    if ( match(tail_keyword, buffer, index) ||
	 match(cat_keyword, buffer, index) ){
      /* We got a possible hit */
      if ( match(tail_keyword, buffer, index) ) new_mode = TAIL_FILE;
      if ( match(cat_keyword, buffer, index) ) new_mode = CAT_FILE;

      index = searchfor('{', buffer, index);
      if ( index >= length ){
	delete buffer;
	return TRUE;
      }
      index++;

      new_filename = getstring(',', buffer, index);
      if ( new_filename == NULL ){
	delete buffer;
	return TRUE;
      }
      index += strlen(new_filename)+1;
      new_filename = clean_string(new_filename);

      new_interval = 0;
      new_int_string = getstring(',', buffer, index);
      if ( new_int_string == NULL ){
	delete buffer;
	return TRUE;
      }
      index += strlen(new_int_string)+1;
      new_int_string = clean_string(new_int_string);
      int x = 0;
      while ( new_int_string[x] != 0 ){
	int a = new_int_string[x] - '0';
	if ( a >= 0 && a <= 9 ){
	  new_interval *= 10;
	  new_interval += a;
	}
	x++;
      }
      if ( new_interval == 0 ||
	   strlen(new_int_string) == 0 ) new_interval = INTERVAL;
      delete new_int_string;

      new_buttontext = getstring(',', buffer, index);
      if ( new_buttontext == NULL ){
	delete buffer;
	return TRUE;
      }
      index += strlen(new_buttontext)+1;
      new_buttontext = clean_string(new_buttontext);

      new_help = getstring('}', buffer, index);
      if ( new_help == NULL ){
	delete buffer;
	return TRUE;
      }
      index += strlen(new_help)+1;
      new_help = clean_string(new_help);
      if ( strlen(new_help) == 0 ){
	delete new_help;
	new_help = new char[strlen(new_filename)+1];
	strcpy(new_help, new_filename);
      }

      find_filters( &new_filter, &new_filter_exec, &new_filter_mode, buffer, &index);
 
      if ( fillups == 0 ){
	fillups = 1;
	mode = new int[fillups];
	filename = new char* [fillups];
	help = new char* [fillups];
	buttontext = new char* [fillups];
	interval = new int[fillups];
	commandline = new char**[fillups];
	filter = new char**[fillups];
	filter_exec = new char**[fillups];
	filter_mode = new int*[fillups];
	mode[fillups-1] = new_mode;
	filename[fillups-1] = new_filename;
	help[fillups-1] = new_help;
	buttontext[fillups-1] = new_buttontext;
	interval[fillups-1] = new_interval;
	commandline[fillups-1] = NULL;
	filter[fillups-1] = new_filter;
	filter_exec[fillups-1] = new_filter_exec;
	filter_mode[fillups-1] = new_filter_mode;
      } else {
	int *tmp_mode= mode;                 
	char **tmp_filename = filename;
	char **tmp_help = help;  
	char **tmp_buttontext = buttontext;  
	int *tmp_interval = interval;
	char ***tmp_commandline = commandline;
	char ***tmp_filter = filter;
	char ***tmp_filter_exec = filter_exec;
	int **tmp_filter_mode = filter_mode;
	fillups++;
	mode = new int[fillups];
	filename = new char* [fillups];
	help = new char* [fillups];
	buttontext = new char* [fillups];
	interval = new int[fillups];
	commandline = new char**[fillups];
	filter = new char**[fillups];
	filter_exec = new char**[fillups];
	filter_mode = new int*[fillups];
	for ( int i = 0 ; i < fillups-1 ; i++ ){
	  mode[i]= tmp_mode[i];                 
	  filename[i] = tmp_filename[i];
	  help[i] = tmp_help[i];  
	  buttontext[i] = tmp_buttontext[i];  
	  interval[i] = tmp_interval[i];
	  commandline[i] = tmp_commandline[i];
	  filter[i] = tmp_filter[i];
	  filter_exec[i] = tmp_filter_exec[i];
	  filter_mode[i] = tmp_filter_mode[i];
	}
	mode[fillups-1] = new_mode;
	filename[fillups-1] = new_filename;
	help[fillups-1] = new_help;
	buttontext[fillups-1] = new_buttontext;
	interval[fillups-1] = new_interval;
	commandline[fillups-1] = NULL;
	filter[fillups-1] = new_filter;
	filter_exec[fillups-1] = new_filter_exec;
	filter_mode[fillups-1] = new_filter_mode;
	delete tmp_mode;
	delete tmp_filename;
	delete tmp_help;
	delete tmp_buttontext;
	delete tmp_interval;
	delete tmp_commandline;
	delete tmp_filter;
	delete tmp_filter_exec;
	delete tmp_filter_mode;
      }

    } else index++;
  }

  delete buffer;
  return TRUE;
}
/*}}}*/

/*{{{  Find Filter settings  */
void find_filters(char*** new_filter, char*** new_filter_exec, int** new_filter_mode, char* buffer, int* index){
  int i = *index;
  while ( buffer[i] != 0 ){
    int _mode = NO_CHANGE;
    char* _string;
    while ( match( tail_keyword, buffer, i ) == FALSE
	    && match( cat_keyword, buffer, i ) == FALSE 
	    && _mode == NO_CHANGE 
	    && buffer[i] != 0 ){
      if ( match( raise_keyword, buffer, i) == TRUE ) _mode = RAISE;
      if ( match( lower_keyword, buffer, i) == TRUE ) _mode = LOWER;
      if ( match( hide_keyword, buffer, i) == TRUE ) _mode = HIDE;
      if ( match( alert_keyword, buffer, i) == TRUE ) _mode = ALERT;
      if ( match( notice_keyword, buffer, i) == TRUE ) _mode = NOTICE;
      if ( match( uniconify_keyword, buffer, i) == TRUE ) _mode = UNICONIFY;
      if ( match( execute_keyword, buffer, i) == TRUE ) _mode = EXECUTE;
      i++;
    }
    if ( _mode == NO_CHANGE ){
      *index = i-1;
      return;
    }

    /*
      Alright - we got a keyword at the right place...
      now check for others (if present)
    */
    int max = searchfor('{', buffer, i);
    while ( i <= max-1 ){
      if ( match( alert_keyword, buffer, i) == TRUE ) _mode |= ALERT;
      if ( match( notice_keyword, buffer, i) == TRUE ) _mode |= NOTICE;
      if ( match( uniconify_keyword, buffer, i) == TRUE ) _mode |= UNICONIFY;
      if ( match( execute_keyword, buffer, i) == TRUE ) _mode |= EXECUTE;
      i++;
    }

    /*
      Alright - we got all keywords...
      now get the string.
    */
    
    i = searchfor('{', buffer, i);
    if ( buffer[i] == 0 ){
      *index = i;
      return;
    }
    i++;
    _string = getstring('}', buffer, i);
    if ( _string == NULL ){
      *index = i;
      return;
    }
    i += strlen(_string)+1;

    gchar* _execline = NULL;
    if ( _mode & EXECUTE ){
      i = searchfor('{', buffer, i);
      if ( buffer[i] == 0 ){
	*index = i;
	return;
      }
      i++;
      _execline = getstring('}', buffer, i);
      if ( _execline == NULL ){
	*index = i;
	return;
      }
      i += strlen(_execline)+1;
    }
    
    if ( *new_filter == NULL ){
      (*new_filter) = new char*[2];
      (*new_filter_exec) = new char*[2];
      (*new_filter_mode) = new int[2];
      (*new_filter)[0] = _string;
      (*new_filter_exec)[0] = _execline;
      (*new_filter_mode)[0] = _mode;
      (*new_filter)[1] = NULL;
      (*new_filter_exec)[1] = NULL;
      (*new_filter_mode)[1] = 0;
    } else {
      int x = 0;
      while ( (*new_filter)[x] != NULL ) x++;
      char** tmp_new_filter = new char*[x+2];
      char** tmp_new_filter_exec = new char*[x+2];
      int* tmp_new_filter_mode = new int[x+2];
      x = 0;
      while ( (*new_filter)[x] != NULL ){
	tmp_new_filter[x] = (*new_filter)[x];
	tmp_new_filter_exec[x] = (*new_filter_exec)[x];
	tmp_new_filter_mode[x] = (*new_filter_mode)[x];
	x++;
      }
      delete (*new_filter);
      delete (*new_filter_exec);
      delete (*new_filter_mode);
      (*new_filter) = tmp_new_filter;
      (*new_filter_exec) = tmp_new_filter_exec;
      (*new_filter_mode) =  tmp_new_filter_mode;
      (*new_filter)[x] = _string;
      (*new_filter_exec)[x] = _execline;
      (*new_filter_mode)[x] = _mode;
      (*new_filter)[x+1] = NULL;
      (*new_filter_exec)[x+1] = NULL;
      (*new_filter_mode)[x+1] = 0;
    }
  }
  *index = i;
  return;
};
/*}}}*/

/*{{{  Matching subroutines  */

// legalize filter mode, only one of the CLASS0 filters is allowed
gint legalize(gint fmode, gint def){
  gint activated = 0;
  gint x = 0;
  while ( class0_filters[x] != -1 ){
    if ( fmode & class0_filters[x] ) activated++;
    x++;
  }

  if ( activated > 1 ){
    fmode &= CLASS1_MASK; // leave class1 untouched and delete all class0
    fmode |= def; // set default
  }
 
  return fmode;
}
// tries to find "string" in "buffer" at position "x"
int match(char* string, char* buffer, int x){
  int i = 0;
  while( string[i] != 0 )
    if ( string[i++] != buffer[x++] )
      return FALSE;
  
  return TRUE;
}
// searches for char 'c' in buffer starting at x and returning position
int searchfor(char c, char* buffer, int x){
  while( buffer[x] != c && buffer[x] != 0 ) x++;
  return x;
}
// tries to create string from x to position of 'delim'
// returns NULL if out of bounds...
char* getstring(char delim, char* buffer, int x){
  int y = searchfor( delim, buffer, x);
  if ( buffer[y] == 0 ) return NULL;
  char* string = new char[y-x+1];
  int i = 0;
  while ( i < y-x ){
    string[i] = buffer[x+i];
    i++;
  }
  string[y-x] = 0;
  return string;
}
char* clean_string(char* source){
  if ( source == NULL ) return NULL;
  char* target = new char [strlen(source)+1];
  // eliminate leading and trailing spaces + all CR/LF's
  int a = 0;  // source
  int b = 0;  // target
  while ( source[a] == ' ' || source[a] == 10 || source[a] == 13 ) a++;
  while ( source[a] != 0 ){
    if ( source[a] != 10 && source[a] != 13 ) target[b++] = source[a++]; 
    else a++;
  }
 target[b--] = 0;
  while ( target[b] == ' ' || target[b] == 10 || target[b] == 13 ) target[b--] = 0;
  char* result = new char[strlen(target)+1];
  strcpy(result, target);
  delete source;
  delete target;

  return result;
}
/*}}}*/

/*{{{  Write Configuration  */
int write_configuration(char* fname){
  ofstream file;
  file.open(fname, ios::out);
  if ( file.fail() ) return FALSE;

  for ( int i = 0 ; i < syslogs ; i++ ){
    if ( syslog[i]->mode == TAIL_FILE ) file << "TAIL{";
    else if ( syslog[i]->mode == CAT_FILE ) file << "CAT{";
    file << syslog[i]->filename << ",";
    file << syslog[i]->interval << ",";
    file << syslog[i]->buttontext << ",";
    file << syslog[i]->help << "}" << endl;

    if ( syslog[i]->filter != NULL ){
      int a = 0;
      while ( syslog[i]->filter[a] != NULL ){
	int fe = 0;
	if ( syslog[i]->filter[a]->mode & RAISE ){
	  file << "RAISE";
	  fe++;
	}
	
	if ( syslog[i]->filter[a]->mode & LOWER ){
	  if ( fe > 0 ) file << ",";
	  file << "LOWER";
	  fe++;
	}

	if ( syslog[i]->filter[a]->mode & HIDE ){
	  if ( fe > 0 ) file << ",";
	  file << "HIDE";
	  fe++;
	}
	
	if ( syslog[i]->filter[a]->mode & ALERT ){
	  if ( fe > 0 ) file << ",";
	  file << "ALERT";
	  fe++;
	}
	
	if ( syslog[i]->filter[a]->mode & NOTICE ){
	  if ( fe > 0 ) file << ",";
	  file << "NOTICE";
	  fe++;
	}
	
	if ( syslog[i]->filter[a]->mode & UNICONIFY ){
	  if ( fe > 0 ) file << ",";
	  file << "UNICONIFY";
	  fe++;
	}

	if ( syslog[i]->filter[a]->mode & EXECUTE ){
	  if ( fe > 0 ) file << ",";
	  file << "EXECUTE";
	  fe++;
	}
	
	file << "{" << syslog[i]->filter[a]->string << "}";
	
	if ( syslog[i]->filter[a]->mode & EXECUTE )
	  file << "{" << syslog[i]->filter[a]->execline << "}";
	
	file << endl;
	
	a++;
	
      }
    }
  }
  
  file.close();
  return TRUE;
}
/*}}}*/
