Mercurial > hg > early-roguelike
diff urogue/dict.c @ 256:c495a4f288c6
Import UltraRogue from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Tue, 31 Jan 2017 19:56:04 -0500 |
parents | |
children | 13b482bd9e66 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/dict.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1160 @@ +/* + dict.c + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/****************** +** Change history: +** +** (AK:04/03/95) - In dict_create, initialize hook for extensions to dictionary structure. +** (AK:04/04/95) - In dict_insert, only set any_ptr when a new string entry is created. +** (AK:04/17/95) - Added dict_union and dict_merge. +** (AK:04/18/95) - In dict_create, added code to create signature, +** table of contents, and parameter array. +** (AK:04/18/95) - Revised dict_load, dict_save +** (AK:04/18/95) - Added dict_import +** +******************/ + +static char sccsid[] = "%W% %G%"; + +#include <stdlib.h> +#include <string.h> +#if !defined(OS2) && !defined(_WIN32) + #include <unistd.h> +#endif + +#include "dict.h" +#include "dictutil.h" + +#define BUFLEN 300 + +/************************************************************************* +* Generic dictionary and hash table functions for word and +* string storage. +* +* Implements generic dictionary management by using a +* hash table with direct chaining. All terms are stored in a +* separate, unsorted, string table. When inserting into the +* hash table exceeds the current size, allocate a new hash table +* and rehash the old contents into the new table. When the string +* table fills, append more empty entries onto the old table and +* continue adding. Inserting a string that already exists increments +* the count for the string. Deleting a string only decrements the +* count to no less than 0. This will allow a 0 occurence string to be +* retrieved. To remove the 0 occurence string table entries, you must +* rebuild the dictionary. Merging from one dictionary to a new one will +* do this because it only copies nonzero occurences. +* +* Assumptions: +* unsigned long >= 32 bits +* int >= 16 bits +* char == 8 bits +* number of entries < 2^28 +*************************************************************************/ + +/************************************************************************* +* hash_string: calculate hash value for string, modified +* version of hashpjw() from the new Dragon book. +* +* s - string to calculate hash for +* +* Returns: hash value +*************************************************************************/ +static unsigned long hash_string(const char *s) +{ + unsigned long h = 0; + unsigned long g = 0; + + while (*s) { + h <<= 4; + h += *s++; + if ((g = h & 0xf0000000) != 0) + h ^= g >> 24; + } + return h ^ g; +} + +/************************************************************************* +* dict_create: create a dictionary and initialize its structures +* +* toc_size - number of entries in table of contents ( >= 4 ) +* initial_string_count - number of strings to hold as initial allocation +* initial_hash_chains - size of hash table, must be power of 4 +* max_chain_length - max number of elements in hash chain before signalling growth +* +* Returns: pointer to dictionary +* NULL - error creating dictionary +* non-NULL - sucessful creation +*************************************************************************/ + +DICTIONARY *dict_create( + const long toc_size, + const long initial_string_count, + const long initial_hash_chains, + const long max_chain_length ) +{ + DICTIONARY *dtemp = NULL; + STRING_ENTRY *stemp = NULL; + long *ltemp = NULL; + char *sltemp; + long i, j, tocsize; + + /* check for a power of 4 in number of initial hash entries */ + switch(initial_hash_chains) { + case 0x00000004: + case 0x00000010: + case 0x00000040: + case 0x00000100: + case 0x00000400: + case 0x00001000: + case 0x00004000: + case 0x00010000: + case 0x00040000: + case 0x00100000: + case 0x00400000: + case 0x01000000: + case 0x04000000: + break; + default: + return NULL; + } + + /* Allocate the dictionary structure */ + if ((dtemp = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) + goto err_exit; + + /* string count has to be within range */ + j = initial_string_count; + if (j > 0x04000000) + return NULL; + + /* force a reasonable value */ + if (j < 100) + j = 100; + + /* Allocate the string table and string array */ + if ((stemp = (STRING_ENTRY *)malloc(sizeof(STRING_ENTRY) * j)) == NULL) + goto err_exit; + if ((sltemp = (char *)malloc(8 * j)) == NULL) + goto err_exit; + + /* Allocate the hash table */ + if ((ltemp = (long *)malloc(sizeof(long) * initial_hash_chains)) == NULL) + goto err_exit; + + /* Allocate the parameter array */ + if ( (dtemp->parm = (DICT_PARM_ENTRY *)malloc(14*sizeof(DICT_PARM_ENTRY))) == NULL) + goto err_exit; + + /* Allocate the signature structure */ + if ( (dtemp->sig=(DICT_SIG*)malloc(sizeof(DICT_SIG))) == NULL ) + goto err_exit; + + /* Allocate the Table of Contents */ + tocsize = toc_size; + if ( tocsize < 4 ) + tocsize = 4; + dtemp->sig->toc_size = tocsize; + if ( (dtemp->toc=(DICT_TOC_ENTRY*)malloc(dtemp->sig->toc_size*sizeof(DICT_TOC_ENTRY))) == NULL ) + goto err_exit; + + dtemp->check_value = DICT_VALIDATE; /* validation value */ + dtemp->flags = DICT_NONE; /* no flags set */ + dtemp->entry_count = 0; /* nothing in either table */ + + dtemp->string_table = stemp; /* connect string table */ + dtemp->string_max = j; /* size of string table */ + dtemp->scan_string_index = DICT_ENTRY_NONE; /* not pointing to anything in scan */ + dtemp->string_growth_count = 0; /* haven't grown any */ + + dtemp->string_array = sltemp; /* character array for strings */ + dtemp->array_size = j * 8; /* how many bytes available */ + dtemp->array_used = 0; /* nothing used yet */ + dtemp->array_growth_count = 0; /* haven't grown any */ + + /* force maximum hash chain length to a reasonable value */ + i = max_chain_length; + if (i < 1 || i > 200) + i = 200; + dtemp->chains = ltemp; /* connect hash table */ + dtemp->longest_chain_length = 0; /* nothing in table yet */ + dtemp->allowable_chain_length = i; /* chain length limit */ + dtemp->table_size = initial_hash_chains; /* size of hash table */ + dtemp->hash_mask = initial_hash_chains - 1; /* mask for mod() function */ + dtemp->hash_growth_count = 0; /* haven't grown any */ + + /* string table is empty */ + for (i = 0 ; i < j ; i++) { + dtemp->string_table[i].string_offset = DICT_ENTRY_NONE; + dtemp->string_table[i].count = 0; + dtemp->string_table[i].next = DICT_ENTRY_NONE; + dtemp->string_table[i].flags = 0; + dtemp->string_table[i].hash_value = 0; + } + + /* no entries chained */ + for (i = 0; i < initial_hash_chains; i++) + dtemp->chains[i] = DICT_ENTRY_NONE; + + /* Initialize hook to extended dictionary structure */ + dtemp->ext = NULL; + + /* Set up the parameter array */ + if ( dict_set_parm_ids(dtemp) == FALSE ) + goto err_exit; + if ( dict_set_parm_values(dtemp) == FALSE ) + goto err_exit; + + /* Set up the Table of Contents */ + strcpy( dtemp->toc[0].id , "PARM" ); + dtemp->toc[0].size = dtemp->sig->nparms * sizeof(DICT_PARM_ENTRY); + dtemp->toc[0].ptr = dtemp->parm; + dtemp->toc[0].type = 0; + + strcpy( dtemp->toc[1].id , "HASH" ); + dtemp->toc[1].size = dtemp->table_size * sizeof(long); + dtemp->toc[1].ptr = dtemp->chains; + dtemp->toc[1].type = 0; + + strcpy( dtemp->toc[2].id , "STTB" ); + dtemp->toc[2].size = dtemp->string_max * sizeof(char); + dtemp->toc[2].ptr = dtemp->string_table; + dtemp->toc[2].type = 0; + + strcpy( dtemp->toc[3].id , "STAR" ); + dtemp->toc[3].size = dtemp->array_size * sizeof(STRING_ENTRY); + dtemp->toc[3].ptr = dtemp->string_array; + dtemp->toc[3].type = 0; + + /* Set up the signature */ + dtemp->sig->check_value = DICT_VALIDATE; + dtemp->sig->toc_size = tocsize; + dtemp->sig->nparms = 14; + + return dtemp; + + /* error exit - saves duplicate code */ +err_exit: + + dict_destroy( dtemp ); + return NULL; +} + +/************************************************************************* +* dict_destroy: discard a dictionary and its contents +* multiple calls to destroy the same dictionary is safe +* +* dict - pointer to a dictionary to discard +* +* Returns: status code +* TRUE - dictionary destroyed +* FALSE - error when destroying dictionary +* Note: Does free the dictionary structure itself. +*************************************************************************/ +BOOLEANC dict_destroy(DICTIONARY *dict) +{ + int i; + + /* have to point to something */ + if (dict == NULL) + return FALSE; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return FALSE; + + if ( (dict->sig==NULL) || (dict->toc==NULL) || (dict->parm==NULL) ) + return FALSE; + + /* Free the type=0 tables */ + for ( i = 0 ; i < dict->sig->toc_size ; i++ ) { + if ( dict->toc[i].ptr != NULL && dict->toc[i].type == 0 ) + free( dict->toc[i].ptr ); + } /* endfor */ + + /* Free the Table of Contents and signature */ + free( dict->toc ); + free( dict->sig ); + + /* Free the dictionary structure itself */ + free( dict ); + + return TRUE; +} + +/************************************************************************* +* dict_insert: add entries into the dictionary, growing as necessary +* +* dict - pointer to dictionary to insert into +* s - string to insert +* count - count of occurences of string +* flags - flag bits associated with the string +* number - pointer to long to return word number +* +* Returns: pointer to new entry in dictionary +* NULL - error during insert +* non-NULL - pointer to inserted or updated entry +* +* Notes: if the entry already exists, increment the count. +* If NULL is returned, the dictionary state can no longer +* be trusted. Terminating with an error code would be +* safest. +*************************************************************************/ +STRING_ENTRY *dict_insert(DICTIONARY *dict, char *s, + const long occurences, const unsigned long flags, + void *any_ptr, long *number) +{ + unsigned long hash_value; + long hash_index; + long string_index = -1; + long he2; + int chain_len; + + /* have to point to something */ + if (dict == NULL) + return FALSE; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return FALSE; + + /* must have a string */ + if (s == NULL) { + *number = -1; + return FALSE; + } + + /* must be non-NULL */ + if (s[0] == '\0') { + *number = -1; + return FALSE; + } + + /* figure out which chain it should go into */ + hash_value = hash_string(s); + hash_index = hash_value & dict->hash_mask; + + /* look for the entry in the chain */ + for (he2 = dict->chains[hash_index], chain_len = 0; he2 != DICT_ENTRY_NONE; + he2 = dict->string_table[he2].next, chain_len++) { + char *sa = &dict->string_array[dict->string_table[he2].string_offset]; + + /* compare hash_value and then string, in that order, for speed */ + if (dict->string_table[he2].hash_value == hash_value && !strcmp(s, sa)) { + string_index = he2; + break; + } + } + + /* string wasn't found */ + if (string_index < 0) { + /* make sure there is room in string entry table */ + if (dict->entry_count + 1 > dict->string_max) { + /* make new table 20% bigger */ + dict->string_max = (dict->string_max * 6) / 5; + dict->string_table = (STRING_ENTRY *)realloc(dict->string_table, + sizeof(STRING_ENTRY) * dict->string_max); + if (dict->string_table == NULL) + return NULL; + dict->string_growth_count++; + } + string_index = dict->entry_count; + dict->entry_count++; + + /* make sure there is room in the string array */ + if (dict->array_used + (long)strlen(s) + 1 > dict->array_size) { + dict->array_size = (dict->array_size * 6) / 5; + dict->string_array = (char *) realloc(dict->string_array, + dict->array_size); + if (dict->string_array == NULL) + return NULL; + } + + /* fill in starting values */ + strcpy(&dict->string_array[dict->array_used], s); + dict->string_table[string_index].string_offset = dict->array_used; + dict->array_used += (long) strlen(s) + 1; + dict->string_table[string_index].count = 0 ; + dict->string_table[string_index].flags = DICT_NONE; + dict->string_table[string_index].any_ptr = any_ptr; /* (AK:04/04/95) */ + dict->string_table[string_index].hash_value = hash_value; + + /* hook entry at beginning of chain */ + dict->string_table[string_index].next = dict->chains[hash_index]; + dict->chains[hash_index] = string_index; + + /* record chain lengths */ + chain_len++; + if (chain_len > dict->longest_chain_length) + dict->longest_chain_length = chain_len; + + /* if a chain is too long */ + if (chain_len > dict->allowable_chain_length) { + long new_size = dict->table_size * 4; + long new_mask = new_size - 1; + long *hetemp; + long i; + int longest_chain; + + if ((hetemp = (long *)malloc(sizeof(long) * new_size)) == NULL) + return NULL; + + /* hash table chains are empty */ + for (i = 0; i < new_size; i++) + hetemp[i] = DICT_ENTRY_NONE; + + /* reset all chains */ + for (i = 0; i < dict->entry_count; i++) + dict->string_table[i].next = DICT_ENTRY_NONE; + + /* recreate hash table entries by reinserting all strings */ + for (i = 0; i < dict->entry_count; i++) { + long he = dict->string_table[i].hash_value & new_mask; + + dict->string_table[i].next = hetemp[he]; + hetemp[he] = i; + } + + /* find longest chain length */ + for (i = 0, longest_chain = 0; i < new_size; i++) { + int len; + long cur; + + for (cur = hetemp[i], len = 0; cur != DICT_ENTRY_NONE; + cur = dict->string_table[cur].next) + len++; + ; + if (len > longest_chain) + longest_chain = len; + } + + /* delete old table and attach new one */ + free(dict->chains); + dict->chains = hetemp; + dict->longest_chain_length = longest_chain; + dict->table_size = new_size; + dict->hash_mask = new_mask; + + /* keep track of growth */ + dict->hash_growth_count++; + } + } + + dict->string_table[string_index].count += occurences; + dict->string_table[string_index].flags |= flags; + *number = string_index; + + return &dict->string_table[string_index]; +} + +/************************************************************************* +* dict_delete: deletes an entry from the dictionary +* (Actually, only decrements the entry's count) +* +* dict - pointer to dictionary to delete +* s - string to find and delete +* count - count to decrement entry by +* +* Returns: status code +* TRUE - entry has been deleted +* FALSE - entry wasn't found or error occured +*************************************************************************/ +BOOLEANC dict_delete(const DICTIONARY *dict, const char *s, const long count) +{ + STRING_ENTRY *se; + long n; + + /* find the string */ + if ((se = dict_search(dict, s, &n)) == NULL) + return FALSE; + + /* decrement count and make sure it stays valid */ + se->count -= count; + if (se->count < 0) + se->count = 0; + return TRUE; +} + +/************************************************************************* +* dict_search: look for entries in the dictionary +* +* dict - pointer to dictionary to search +* s - string to search for +* number - pointer to long to return string number +* +* Returns: pointer to string entry +* NULL - entry not found +* non-NULL - pointer to string entry +*************************************************************************/ +STRING_ENTRY *dict_search(const DICTIONARY *dict, const char *s, + long *number) +{ + unsigned long hash_value; + long hash_index; + long string_index = -1; + long he; + + /* have to point to something */ + if (dict == NULL) + return NULL; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return NULL; + + /* must have a string */ + if (s == NULL) { + *number = -1; + return NULL; + } + + /* must be non-NULL */ + if (s[0] == '\0') { + *number = -1; + return FALSE; + } + + /* figure out which chain it should be in */ + hash_value = hash_string(s); + hash_index = hash_value & dict->hash_mask; + + /* look for the entry in the chain */ + for (he = dict->chains[hash_index]; he != DICT_ENTRY_NONE; + he = dict->string_table[he].next) { + char *sa = (char*)(&dict->string_array[dict->string_table[he].string_offset]); + + /* compare hash_value and then string, in that order, for speed */ + if (dict->string_table[he].hash_value == hash_value && !strcmp(s, sa)) { + string_index = he; + break; + } + } + if (string_index < 0) { + *number = -1; + return NULL; + } + + *number = string_index; + return dict->string_table+string_index; +} + +/************************************************************************* +* dict_union: merges contents of 2 dictionaries into +* a third +* +* dict1 - pointer to dictionary 1 +* dict2 - pointer to dictionary 2 +* +* Returns: dictionary pointer +* NULL - failed to merge +* non-NULL - pointer to new dictionary +* +* Notes: entries of the same string have their counts +* added and their flags ORed together. +*************************************************************************/ +DICTIONARY *dict_union( const DICTIONARY *dict1 , const DICTIONARY *dict2 ) +{ + DICTIONARY *dict = NULL; + STRING_ENTRY *se, *se2; + long initial_string_count, initial_hash_chains, max_chain_length; + long i, string_index; + + /*********** + ** Initialize the new dictionary. + ***********/ + + if ( dict1==NULL || dict2==NULL ) + goto err_exit; + if ((dict=(DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) + goto err_exit; + + initial_string_count = dict1->string_max; + initial_hash_chains = dict1->table_size; + max_chain_length = dict1->allowable_chain_length; + dict = dict_create( 4, + initial_string_count, + initial_hash_chains, + max_chain_length ); + + /*********** + ** Copy the entries from dict1 into the new dictionary. + ***********/ + + for ( i = 0 ; i < dict1->entry_count ; i++ ) { + se = (STRING_ENTRY*)&dict1->string_table[i]; + if ( se->count > 0 ) { + se2 = dict_insert( + dict, + dict1->string_array+se->string_offset, + se->count, + se->flags, + NULL, + &string_index ); + if ( se2 == NULL ) + goto err_exit; + } /* endif */ + } /* endfor */ + + /* Merge the entries from dict2 into the new dictionary. */ + if ( dict_merge(dict,dict2,FALSE) == FALSE ) + goto err_exit; + + /* Success. Return a pointer to the new dictionary. */ + return( dict ); + + /* Failure. Ignominiously erase our tracks and return NULL. */ +err_exit: + dict_destroy( dict ); + return NULL; +} + +/************************************************************************* +* dict_merge: merges the contents of a dictionary into +* another one, updating the contents of the destination +* +* dst - dictionary to update +* src - dictionary to add +* move - boolean flag +* TRUE - move to dest +* FALSE - copy to dest +* +* Returns: status code +* TRUE - merge completed +* FALSE - error on merge +* +* Notes: entries of the same string have their counts +* added. At the end of a move, src is empty. If there +* is an error during a move, the contents of both +* dictionaries cannot be trusted. +*************************************************************************/ +BOOLEANC dict_merge(const DICTIONARY *dst, const DICTIONARY *src, const BOOLEANC move) +{ + STRING_ENTRY *se, *se2; + DICTIONARY *dict1, *dict2; + long i, string_index, index; + + dict1 = (DICTIONARY*)src; + dict2 = (DICTIONARY*)dst; + + /*********** + ** Copy the dictionary entries into the new dictionary. + ***********/ + + for ( i = 0 ; i < dict1->entry_count ; i++ ) { + se = (STRING_ENTRY*)&dict1->string_table[i]; + if ( se->count > 0 ) { + se2 = dict_insert( + dict2, + dict1->string_array+se->string_offset, + se->count, + se->flags, + NULL, + &string_index ); + if ( se2 == NULL ) + goto err_exit; + } /* endif */ + } /* endfor */ + + /*********** + ** Set up the dictionary parameter vector. + ***********/ + + if ( dict_set_parm_values(dict2) == FALSE ) + return( FALSE ); + + /*********** + ** Update the table of contents. + ** PARM HASH STTB STAR + ***********/ + + if ( (index=dict_toc_index(dict2,"HASH")) == -1) + goto err_exit; + dict2->toc[index].size = dict2->table_size * sizeof(long); + dict2->toc[index].ptr = dict2->chains; + + if ( (index=dict_toc_index(dict2,"STTB")) == -1) + goto err_exit; + dict2->toc[index].size = dict2->string_max * sizeof(char); + dict2->toc[index].ptr = dict2->string_table; + + if ( (index=dict_toc_index(dict2,"STAR")) == -1) + goto err_exit; + dict2->toc[index].size = dict2->array_size * sizeof(STRING_ENTRY); + dict2->toc[index].ptr = dict2->string_array; + + /*********** + ** Update the signature + ***********/ + + dict2->sig->checksum = + compute_checksum( 4*sizeof(DICT_TOC_ENTRY) , (char*)(dict2->toc) ); + + /*********** + ** If this is a move, destroy the source dictionary + ***********/ + + if ( move == TRUE ) + if ( dict_destroy(dict1) == FALSE ) + goto err_exit; + + /*********** + ** Success + ***********/ + + return( TRUE ); + + /*********** + ** Failure + ***********/ + +err_exit: + dict_destroy( dict2 ); + return FALSE; +} + +/************************************************************************* +* dict_string_by_number: return string pointer for +* a given string number +* +* number - string number +* +* Returns: pointer to string entry +* NULL - entry not found +* non-NULL - pointer to string entry +*************************************************************************/ +STRING_ENTRY *dict_string_by_number(const DICTIONARY *dict, const long number) +{ + if (dict == NULL) + return NULL; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return NULL; + + /* string number has to be within range */ + if (number < 0 || number >= dict->entry_count) + return NULL; + + return dict->string_table+number; +} + +/************************************************************************* +* dict_scan_begin: begin a scan of the dictionary +* +* dict - pointer to dictionary to scan +* +* Returns: status code +* TRUE - scan initialized +* FALSE - error +* +* Notes: if a scan is already in progress, it is +* abandoned and the scan is reset to the +* beginning. +*************************************************************************/ +BOOLEANC dict_scan_begin(DICTIONARY *dict) +{ + /* have to point to something */ + if (dict == NULL) + return FALSE; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return FALSE; + + /* point to first entry in string table */ + dict->scan_string_index = 0; + return TRUE; +} + +/************************************************************************* +* dict_scan_next: get the next entry in a scan +* +* dict - pointer to dictionary to continue scanning +* +* Returns: pointer to string entry +* NULL - no more entries +* non-NULL - next string entry +*************************************************************************/ +STRING_ENTRY *dict_scan_next(DICTIONARY *dict) +{ + /* have to point to something */ + if (dict == NULL) + return NULL; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return NULL; + + /* scan index has to be within range */ + if (dict->scan_string_index < 0 + || dict->scan_string_index > dict->entry_count) + return NULL; + + /* for first non-empty table entry */ + while (dict->scan_string_index < dict->entry_count + && dict->string_table[dict->scan_string_index].count == 0) + dict->scan_string_index++; + + /* past end of table? */ + if (dict->scan_string_index >= dict->entry_count) + return NULL; + + return &dict->string_table[dict->scan_string_index++]; +} + + +/************************************************************************* +* dict_load - load a compiled dictionary into memory +* creates a new dictionary +* +* fname - fully qualified file name +* +* Returns: pointer to created dictionary structure +* (NULL on failure) +*************************************************************************/ +DICTIONARY *dict_load(const char *fname) +{ + DICTIONARY *dict = NULL; + int code, index; + FILE *fi; + int ntoc; + + if ( (fi=fopen((char*)fname,"rb")) == NULL ) { + signal_error( "dict_load: could not open file" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + if ((dict = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) { + /* signal_error( "dict_load: alloc failed" , "" , 0 ); */ + goto err_exit; + } /* endif */ + + /* Read the dictionary signature record */ + if ((dict->sig = (DICT_SIG *)malloc(sizeof(DICT_SIG))) == NULL) { + goto err_exit; + } /* endif */ + code = block_read( fi , (char*)(dict->sig) , sizeof(DICT_SIG) , 0 ); + if ( code == -1 ) { + signal_error( "dict_load: could not read signature" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + if ( dict->sig->check_value != DICT_VALIDATE ) { + signal_error( "dict_load: could not validate file" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + dict->check_value = dict->sig->check_value; + + /* Read the dictionary Table of Contents */ + ntoc = dict->sig->toc_size; + dict->toc = (DICT_TOC_ENTRY *) malloc( ntoc * sizeof(DICT_TOC_ENTRY) ); + if ( dict->toc == NULL ) { + signal_error( "dict_load: alloc of TOC failed" , "" , 0 ); + goto err_exit; + } /* endif */ + code = block_read( fi , + (char*)(dict->toc) , + ntoc * sizeof(DICT_TOC_ENTRY) , + sizeof(DICT_SIG) ); + if ( code == -1 ) { + signal_error( "dict_load: could not read Table of Contents" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Read the dictionary parameters */ + dict->parm = (DICT_PARM_ENTRY *) + dict_load_block( dict , "PARM" , fi , NULL ); + if ( dict->parm == NULL ) { + signal_error( "dict_load: could not load parameter table" , "" , 0 ); + goto err_exit; + } /* endif */ + index = dict_toc_index( dict , "PARM" ); + dict->sig->nparms = dict->toc[index].size / sizeof(DICT_PARM_ENTRY); + + /* Set the parameter values in the dictionary structure */ + if ( dict_set_parm_variables(dict) == FALSE ) + goto err_exit; + + /* Load the string array */ + dict->string_array = (char *) + dict_load_block( dict , "STAR" , fi , NULL ); + if ( dict->string_array == NULL ) { + signal_error( "dict_load: could not load string array" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Load the string table */ + dict->string_table = (STRING_ENTRY *) + dict_load_block( dict , "STTB" , fi , NULL ); + if ( dict->string_table == NULL ) { + signal_error( "dict_load: could not load string table" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Load the hash table */ + dict->chains = (long *) + dict_load_block( dict , "HASH" , fi , NULL ); + if ( dict->chains == NULL ) { + signal_error( "dict_load: could not load hash table" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Initialize the hook for dictionary extensions */ + dict->ext = NULL; + + /* Success */ + fclose( fi ); + return( dict ); + + /* Failure */ +err_exit: + if ( fi != NULL ) + fclose( fi ); + dict_destroy( dict ); + return NULL; +} + + +/************************************************************************* +* dict_save - save a dictionary from memory into a file +* +* dict - pointer to dictionary to save +* fname - full qualified file name prefix of dictionary +* +* Returns: status code +* TRUE - dictionary was saved sucessfully +* FALSE - error during save +*************************************************************************/ +BOOLEANC dict_save( DICTIONARY *dict, const char *fname ) +{ + int index, ret_code; + FILE *fo = NULL; + + /* Have to be pointing at a valid dictionary */ + if ( dict == NULL || dict->sig->check_value != DICT_VALIDATE ) + goto err_exit; + + /* Open the file for output */ + if ( (fo=fopen((char*)fname,"wb")) == NULL ) + goto err_exit; + + /* Make the table of contents entries current */ + /* Note: This will not be necessary once the data is stored in EVECTORs */ + + if ( (index=dict_toc_index(dict,"PARM")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->sig->nparms * sizeof(DICT_PARM_ENTRY); + dict->toc[index].ptr = dict->parm; + + if ( (index=dict_toc_index(dict,"STAR")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->array_size * sizeof(char); + dict->toc[index].ptr = dict->string_array; + + if ( (index=dict_toc_index(dict,"STTB")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->string_max * sizeof(STRING_ENTRY); + dict->toc[index].ptr = dict->string_table; + + if ( (index=dict_toc_index(dict,"HASH")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->table_size * sizeof(long); + dict->toc[index].ptr = dict->chains; + + /* Reset the TOC offsets and checksums for ALL tables */ + /* (not just type=0 tables) */ + dict_reset_toc_offsets( dict ); + + /* Set the dictionary parm structure from the parameter values */ + if ( dict_set_parm_values(dict) == FALSE ) + goto err_exit; + + /* Save the signature */ + dict->sig->checksum = compute_checksum( sizeof(DICT_SIG) , (char*)(dict->sig) ); + ret_code = block_write( fo , + (char*)dict->sig , + sizeof(DICT_SIG) ); + if ( ret_code == -1 ) + goto err_exit; + + /* Save the table of contents */ + ret_code = block_write( fo, + (char*)dict->toc, + dict->sig->toc_size * sizeof(DICT_TOC_ENTRY) ); + if ( ret_code == -1 ) + goto err_exit; + + /* Save the tables */ + /* For now, only save type=0 tables */ + for ( index = 0 ; index < dict->sig->toc_size ; index++ ) { + if ( dict->toc[index].type == 0 ) { /* Ordinary table */ + ret_code = dict_save_block( dict , dict->toc[index].id , fo ); + if ( ret_code == FALSE ) + goto err_exit; + } /* endif */ + } /* endfor */ + + /* Success */ + fclose( fo ); + return TRUE; + + /* Failure */ +err_exit: + if ( fo != NULL ) + fclose( fo ); + return FALSE; +} + + +/************************************************************************* +* dict_import: read in an ASCII dictionary. +* +* dict_fname - name of dictionary file +* parameters to create a DICTIONARY structure (see dict_create) +* +* Returns: pointer to created DICTIONARY structure +* (NULL on failure) +* +*************************************************************************/ +DICTIONARY *dict_import( const char *dict_fname , + const long initial_string_count , + const long initial_hash_entries , + const long max_chain_length ) +{ + DICTIONARY *dict; + char buffer[BUFLEN], ch; + int index, c, c0; + long number; + FILE *fi = NULL; + + /*********** + ** Dictionary setup. + ***********/ + + dict = dict_create( 4, + initial_string_count , + initial_hash_entries , + max_chain_length ); + if ( dict == NULL ) + goto err_exit; + + /*********** + ** Read the dictionary file + ** Each line should have one word or a string delimited by '|' + ***********/ + + if ( (fi=fopen(dict_fname,"r")) == NULL ) + goto err_exit; + while( fgets(buffer,BUFLEN,fi) != NULL ) { + c0 = 0; + /* Skip to non-blank */ + while ( (c0<BUFLEN-2) && (buffer[c0]==' ') ) ++c0; + if ( buffer[c0] == '|' ) { + c = ++c0; + ch = '|'; + } else { + c = c0; + ch = ' '; + } /* endif */ + /* Scan to blank or matching '|' */ + while ( (c<BUFLEN-1) && (buffer[c]!='\0') && + (buffer[c]!='\n') && (buffer[c]!=ch) ) + ++c; + buffer[c] = '\0'; + /* Insert the word */ + if ( dict_insert(dict,buffer+c0,1,0,NULL,&number) == NULL ) + goto err_exit; + } /* endwhile */ + + /*********** + ** Fill in the dictionary parameter vector. + ***********/ + + if ( dict_set_parm_values(dict) == FALSE ) + goto err_exit; + + /*********** + ** Update the table of contents for HASH STTB STAR + ***********/ + + if ( (index=dict_toc_index(dict,"HASH")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->table_size * sizeof(long); + + if ( (index=dict_toc_index(dict,"STTB")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->string_max * sizeof(char); + + if ( (index=dict_toc_index(dict,"STAR")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->array_size * sizeof(STRING_ENTRY); + + /* Success. Return a pointer to the new dictionary. */ + fclose(fi); + return( dict ); + + /* Failure. Ignominiously erase our tracks and return NULL. */ +err_exit: + if ( fi != NULL ) + fclose(fi); + dict_destroy( dict ); + return NULL; +} + +/************************************************************************* +* dict_export - save an extended dictionary from memory into +* an ASCII file +* +* dict - pointer to dictionary to save +* fname - full qualified file name prefix of dictionary +* +* Returns: status code +* TRUE - dictionary was saved sucessfully +* FALSE - error during save +* +*************************************************************************/ +BOOLEANC dict_export( DICTIONARY *dict , const char *fname ) +{ + FILE *fp = NULL; + STRING_ENTRY *se; + int i; + + /* have to point to something */ + if (dict == NULL) + goto err_exit; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + goto err_exit; + + /* must have a filename */ + if (fname == NULL) + goto err_exit; + + fp = fopen( (char*)fname , "w" ); + if ( fp == NULL ) + goto err_exit; + + for ( i = 0 ; i < dict->entry_count ; i++ ) { + se = &dict->string_table[i]; + fprintf( fp , "|%s|\n" , dict->string_array + se->string_offset ); + } /* endfor */ + fclose( fp ); + + /* Success. */ + fclose(fp); + return TRUE; + + /* Failure. */ +err_exit: + if ( fp != NULL ) + fclose(fp); + dict_destroy( dict ); + return FALSE; +}