#include <sys/param.h>
#include <ndbm.h>
#include <dictionary.h>
#include <dicterrors.h>
#include <dict_errs.h>
#include <stdio.h>
#include <dictvariables.h>
#include <strings.h>
#include <errno.h>

dictFetchIndexEntry(word, index_entry)
char *word;
indexEntry_t *index_entry;
{
     datum key, content;
     
     if (! _dict_paths_initialized) {
	  dict_set_error(DICT_PATHS_UNINITIALIZED);
	  return(dict_error_code);
     }

     if (! _dict_database) {
	  dict_set_error(DICT_DATABASE_CLOSED);
	  return(dict_error_code);
     }

     key.dptr = word;
     key.dsize = strlen(word);

     content = dbm_fetch(_dict_database, key);

     if (! content.dptr) {
	  dict_set_error(DICT_NOT_FOUND);
	  return(dict_error_code);
     }

     bcopy(content.dptr, (char *) index_entry, content.dsize);

     return(0);
}

/*
 * Minimum size:		Average size:
 * 0				230
 * 231				430
 * 431				740
 * 741				1200
 * 1201				1890
 * 1891				3070
 * 3071				6120
 * 6121				14830
 * 14831			47230
 */
static unsigned int definition_sizes[] = {
     230, 430, 740, 1200, 1890, 3070, 6120, 14830, 47230, 0
};

dictFetchDefinition(definition, retdefn)
definition_t definition;
char **retdefn;
{
     char filename[MAXPATHLEN];
     FILE *input_file;
     unsigned int *size_ptr = definition_sizes;
     int current_size = 0;
     int current_max = 0;
     char *defn_string = NULL;
     char buf[DICTIONARYLINELENGTH];
     extern char *malloc(), *realloc();
     int result = 0;

     /* I initialize buf above, except that saber barfs on it (ARGH!) */
     *buf = '\0';
     
     if (! _dict_paths_initialized) {
	  dict_set_error(DICT_PATHS_UNINITIALIZED);
	  return(dict_error_code);
     }

     if (! _dict_database) {
	  dict_set_error(DICT_DATABASE_CLOSED);
	  return(dict_error_code);
     }
     
     if (definition.file > _dict_num_files) {
	  dict_set_error(DICT_BAD_FILE_NUMBER);
	  return(dict_error_code);
     }
     
     sprintf(filename, "%s%s.d", _dict_directory,
	     _dict_file_names[definition.file - 1]);
				       
     defn_string = malloc((unsigned) 1);
     if (! defn_string) {
	  dict_set_error(errno ? errno : -1);
	  return(dict_error_code);
     }
	  
     *defn_string = '\0';
     current_size = 1;
     
     input_file = fopen(filename, "r");
     if (! input_file) {
	  dict_set_error(errno ? errno : -1);
	  return(dict_error_code);
     }

     if (fseek(input_file, definition.file_position, 0) == -1) {
	  dict_set_error(errno ? errno : -1);
	  fclose(input_file);
	  return(dict_error_code);
     }

     if (fgets(buf, DICTIONARYLINELENGTH, input_file) == NULL) {
	  if (ferror(input_file)) {
	       dict_set_error(errno ? errno : -1);
	       result = dict_error_code;
	  }
	  goto done;
     }
	       
     do {
	  current_size += strlen(buf);

	  if (current_size > current_max) {
	       if (*size_ptr == 0) {
		    dict_set_error(DICT_DEFN_TOO_BIG);
		    result = dict_error_code;
		    break;
	       }
	       defn_string = realloc(defn_string, *size_ptr);
	       if (! defn_string) {
		    dict_set_error(errno ? errno : -1);
		    result = dict_error_code;
		    break;
	       }
	       current_max = *size_ptr++;
	  }

	  strcat(defn_string, buf);

	  if (fgets(buf, DICTIONARYLINELENGTH, input_file) == NULL) {
	       if (ferror(input_file)) {
		    dict_set_error(errno ? errno : -1);
		    result = dict_error_code;
	       }
	       break; /* at EOF */
	  }
     } while (*buf != 'F');

done:
     
     if (result && defn_string)
	  free(defn_string);

     if (fclose(input_file) == EOF) {
	  dict_set_error(errno ? errno : -1);
	  result = dict_error_code;
     }

     if (result)
	  *retdefn = NULL;
     else
	  *retdefn = defn_string;
	  
     return(result);
}



dictSpell(word)
char *word;
{
     indexEntry_t index_entry;
     
     if (! _dict_paths_initialized) {
	  dict_set_error(DICT_PATHS_UNINITIALIZED);
	  return(dict_error_code);
     }

     if (! _dict_database) {
	  dict_set_error(DICT_DATABASE_CLOSED);
	  return(dict_error_code);
     }

     return(dictFetchIndexEntry(word, &index_entry));
}



dictSearch(word, matches)
char *word;
char ***matches;
{
     int retval;
     off_t where;
     char buf[DICTIONARYLINELENGTH+SOUNDEX_LENGTH+2];
     char **array = NULL;
     int array_size, array_ind;
     extern char *malloc(), *realloc();
     char *ptr;
     int result = 0;
     int len;
     char *soundex = dictSoundex(word);
     int soundex_len = strlen(soundex);
     
     retval = _dictBinarySearch(_dict_index_file, _dict_index_size,
				soundex, &where);
     if (retval)
	  return(retval);

     if (fseek(_dict_index_file, where, 0) == -1) {
	  dict_set_error(errno ? errno : -1);
	  return(dict_error_code);
     }

     array_size = 100;
     array_ind  = 0;
     array = (char **) malloc((unsigned) (sizeof(char *) * array_size));
     if (! array) {
	  dict_set_error(errno ? errno : -1);
	  result = dict_error_code;
	  goto done;
	  
     }

     array[0] = NULL;
     while (1) {
	  if (fgets(buf, sizeof(buf), _dict_index_file) == NULL) {
	       if (feof(_dict_index_file))
		    break;
	       else {
		    dict_set_error(errno? errno : -1);
		    result = dict_error_code;
		    break;
		    
	       }
	  }
	  if (strncmp(soundex, buf, soundex_len))
	       break;
	  ptr = rindex(buf, '\n');
	  if (! ptr) {
	       dict_set_error(DICT_CORRUPT_INDEX);
	       result = dict_error_code;
	       break;
	  }
	  *ptr = '\0';
	  ptr = index(buf, ' ');
	  if (! ptr++) {
	       dict_set_error(DICT_CORRUPT_INDEX);
	       result = dict_error_code;
	       break;
	  }
	  len = strlen(ptr) + 1;
	  if (array_ind + 2 > array_size) {
	       array_size += 100;
	       array = (char **) realloc(array, (unsigned)
					 (sizeof(char *) * array_size));
	       if (! array) {
		    dict_set_error(errno ? errno : -1);
		    result = dict_error_code;
		    break;
	       }
	  }
	  array[array_ind] = malloc((unsigned) len);
	  if (! array[array_ind]) {
	       dict_set_error(errno ? errno : -1);
	       result = dict_error_code;
	       break;
	  }
	  strcpy(array[array_ind], ptr);
	  array[++array_ind] = NULL;
     }

done:

     array_size = array_ind + 1;
     if (result) {
	  if (array) {
	       for (array_ind = 0; array_ind < array_size - 1; array_ind++)
		    free(array[array_ind]);
	       free(array);
	  }
	  return(result);
     }
     array = (char **) realloc(array, (unsigned)
			       (sizeof(char *) * array_size));
     if (! array) {
	  /* Should never happen, since it should always be possible */
	  /* to realloc to a smaller size			     */
	  dict_set_error(errno ? errno : -1);
	  return(dict_error_code);
     }
     *matches = array;
     return(0);
}



dictMatch(word, results)
char *word;
char ***results;
{
     char *soundex, *ptr1, *ptr2;
     char buf[DICTIONARYLINELENGTH+SOUNDEX_LENGTH+2];
     int retval, result = 0;
     extern char *malloc(), *realloc();
     off_t where;
     char **array;
     int array_ind = 0, array_size = 0;
     int s_len;
     
     /*
      * First, verify that there actually are wildcards in the word,
      * and if not, just try to spell-check it.
      */
     strncpy(buf, word, sizeof(buf) - 1);
     buf[sizeof(buf) - 1] = '\0';
     ptr1 = index(buf, DICT_WILD_ONE);
     ptr2 = index(buf, DICT_WILD_ANY);
     if (! ptr1)
	  ptr1 = ptr2;
     if (ptr2 && ptr2 < ptr1)
	  ptr1 = ptr2;

     if (! ptr1) {
	  /* There are no wildcards in the word, so just spell it */
	  retval = dictSpell(word);
	  if (retval)
	       return(retval);
	  *results = (char **) malloc((unsigned) (2 * sizeof(char *)));
	  if (! *results) {
	       dict_set_error(errno ? errno : -1);
	       return(dict_error_code);
	  }
	  (*results)[0] = malloc((unsigned) (strlen(word) + 1));
	  if (! **results) {
	       dict_set_error(errno ? errno : -1);
	       return(dict_error_code);
	  }
	  strcpy((*results)[0], word);
	  (*results)[1] = NULL;
	  return(0);
     }
     
     /*
      * Now, calculate the soundex of the non-wildcard prefix of the word,
      * and use it to pick a starting location in the index file.
      */
     if (ptr1 != buf) {
	  *ptr1 = '\0';
	  soundex = dictSoundex(buf);
	  ptr1 = index(soundex, '0');
	  if (ptr1)
	       *ptr1 = '\0';
	  s_len = strlen(soundex);
	  retval = _dictBinarySearch(_dict_index_file, _dict_index_size,
				     soundex, &where);
	  if (retval)
	       return(retval);
     }
     else {
	  soundex = NULL;
	  s_len = 0;
	  where = 0;
     }

     array_size = 100;
     array = (char **) malloc((unsigned) (sizeof(char *) * array_size));
     if (! array) {
	  dict_set_error(errno ? errno : -1);
	  return(dict_error_code);
     }
     array[0] = NULL;

     if (fseek(_dict_index_file, where, 0) == -1) {
	  dict_set_error(errno ? errno : -1);
	  result = dict_error_code;
	  goto done;
     }
     
     while (1) {
	  if (fgets(buf, sizeof(buf), _dict_index_file) == NULL) {
	       if (feof(_dict_index_file))
		    break;
	       dict_set_error(errno ? errno : -1);
	       result = dict_error_code;
	       goto done;
	  }

	  ptr1 = rindex(buf, '\n');
	  if (! ptr1) {
	       dict_set_error(DICT_CORRUPT_INDEX);
	       result = dict_error_code;
	       goto done;
	  }
	  *ptr1 = '\0';
	  
	  if (soundex && strncmp(buf, soundex, s_len))
	       goto done;

	  ptr1 = index(buf, ' ');
	  if (! ptr1++) {
	       dict_set_error(DICT_CORRUPT_INDEX);
	       result = dict_error_code;
	       goto done;
	  }
	  
	  if (dictPatternMatches(word, ptr1)) {
	       if (array_ind + 2 > array_size) {
		    array_size += 100;
		    array = (char **) realloc(array, (unsigned)
					      (sizeof(char *) * array_size));
		    if (! array) {
			 dict_set_error(errno ? errno : -1);
			 result = dict_error_code;
			 goto done;
		    }
	       }
	       array[array_ind] = malloc((unsigned) (strlen(ptr1) + 1));
	       if (! array[array_ind]) {
		    dict_set_error(errno ? errno : -1);
		    result = dict_error_code;
		    goto done;
	       }
	       strcpy(array[array_ind], ptr1);
	       array[++array_ind] = NULL;
	       if (array_ind == DICT_WILD_MAX_MATCHES)
		    goto done;
	  }
     }	  

done:
     
     if (result && array) {
	  int i;
	  for (i = 0; i < array_ind; i++)
	       free(array[i]);
	  free(array);
	  array = NULL;
     }
     else if (array_ind == 0) {
	  free(array);
	  return(DICT_NOT_FOUND);
     }
     else {
	  array_size = array_ind + 1;
	  array = (char **) realloc(array, (unsigned)
				    (sizeof(char *) * array_size));
	  if (! array) {
	       dict_set_error(errno ? errno : -1);
	       result = dict_error_code;
	  }
     }
	  
     *results = array;
     return(result);
}
