[BACK]Return to dict.c CVS log [TXT][DIR] Up to [contributed] / early-roguelike / urogue

Annotation of early-roguelike/urogue/dict.c, Revision 1.1.1.1

1.1       rubenllo    1: /*
                      2:     dict.c
                      3:
                      4:     UltraRogue: The Ultimate Adventure in the Dungeons of Doom
                      5:     Copyright (C) 1995 Herb Chong
                      6:     All rights reserved.
                      7:
                      8:     See the file LICENSE.TXT for full copyright and licensing information.
                      9: */
                     10:
                     11: /******************
                     12: **  Change history:
                     13: **
                     14: **  (AK:04/03/95) - In dict_create, initialize hook for extensions to dictionary structure.
                     15: **  (AK:04/04/95) - In dict_insert, only set any_ptr when a new string entry is created.
                     16: **  (AK:04/17/95) - Added dict_union and dict_merge.
                     17: **  (AK:04/18/95) - In dict_create, added code to create signature,
                     18: **                  table of contents, and parameter array.
                     19: **  (AK:04/18/95) - Revised dict_load, dict_save
                     20: **  (AK:04/18/95) - Added dict_import
                     21: **
                     22: ******************/
                     23:
                     24: static char sccsid[] = "%W% %G%";
                     25:
                     26: #include <stdlib.h>
                     27: #include <string.h>
                     28: #if !defined(OS2) && !defined(_WIN32)
                     29:    #include <unistd.h>
                     30: #endif
                     31:
                     32: #include "dict.h"
                     33: #include "dictutil.h"
                     34:
                     35: #define BUFLEN      300
                     36:
                     37: /*************************************************************************
                     38: *       Generic dictionary and hash table functions for word and
                     39: *       string storage.
                     40: *
                     41: *       Implements generic dictionary management by using a
                     42: *       hash table with direct chaining.  All terms are stored in a
                     43: *       separate, unsorted, string table. When inserting into the
                     44: *       hash table exceeds the current size, allocate a new hash table
                     45: *       and rehash the old contents into the new table. When the string
                     46: *       table fills, append more empty entries onto the old table and
                     47: *       continue adding. Inserting a string that already exists increments
                     48: *       the count for the string. Deleting a string only decrements the
                     49: *       count to no less than 0. This will allow a 0 occurence string to be
                     50: *       retrieved. To remove the 0 occurence string table entries, you must
                     51: *       rebuild the dictionary. Merging from one dictionary to a new one will
                     52: *       do this because it only copies nonzero occurences.
                     53: *
                     54: *       Assumptions:
                     55: *               unsigned long >= 32 bits
                     56: *               int >= 16 bits
                     57: *               char == 8 bits
                     58: *               number of entries < 2^28
                     59: *************************************************************************/
                     60:
                     61: /*************************************************************************
                     62: *       hash_string: calculate hash value for string, modified
                     63: *       version of hashpjw() from the new Dragon book.
                     64: *
                     65: *       s - string to calculate hash for
                     66: *
                     67: *       Returns: hash value
                     68: *************************************************************************/
                     69: static unsigned long hash_string(const char *s)
                     70: {
                     71:         unsigned long h = 0;
                     72:         unsigned long g = 0;
                     73:
                     74:         while (*s) {
                     75:                 h <<= 4;
                     76:                 h += *s++;
                     77:                 if ((g = h & 0xf0000000) != 0)
                     78:                         h ^= g >> 24;
                     79:         }
                     80:         return h ^ g;
                     81: }
                     82:
                     83: /*************************************************************************
                     84: *       dict_create: create a dictionary and initialize its structures
                     85: *
                     86: *       toc_size - number of entries in table of contents ( >= 4 )
                     87: *       initial_string_count - number of strings to hold as initial allocation
                     88: *       initial_hash_chains - size of hash table, must be power of 4
                     89: *       max_chain_length - max number of elements in hash chain before signalling growth
                     90: *
                     91: *       Returns: pointer to dictionary
                     92: *               NULL - error creating dictionary
                     93: *               non-NULL - sucessful creation
                     94: *************************************************************************/
                     95:
                     96: DICTIONARY *dict_create(
                     97:         const long toc_size,
                     98:         const long initial_string_count,
                     99:         const long initial_hash_chains,
                    100:         const long max_chain_length )
                    101: {
                    102:         DICTIONARY      *dtemp = NULL;
                    103:         STRING_ENTRY    *stemp = NULL;
                    104:         long    *ltemp = NULL;
                    105:         char    *sltemp;
                    106:         long    i, j, tocsize;
                    107:
                    108:         /* check for a power of 4 in number of initial hash entries */
                    109:         switch(initial_hash_chains) {
                    110:         case 0x00000004:
                    111:         case 0x00000010:
                    112:         case 0x00000040:
                    113:         case 0x00000100:
                    114:         case 0x00000400:
                    115:         case 0x00001000:
                    116:         case 0x00004000:
                    117:         case 0x00010000:
                    118:         case 0x00040000:
                    119:         case 0x00100000:
                    120:         case 0x00400000:
                    121:         case 0x01000000:
                    122:         case 0x04000000:
                    123:                 break;
                    124:         default:
                    125:                 return NULL;
                    126:         }
                    127:
                    128:         /* Allocate the dictionary structure */
                    129:         if ((dtemp = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL)
                    130:                 goto err_exit;
                    131:
                    132:         /* string count has to be within range */
                    133:         j = initial_string_count;
                    134:         if (j > 0x04000000)
                    135:                 return NULL;
                    136:
                    137:         /* force a reasonable value */
                    138:         if (j < 100)
                    139:                 j = 100;
                    140:
                    141:         /* Allocate the string table and string array */
                    142:         if ((stemp = (STRING_ENTRY *)malloc(sizeof(STRING_ENTRY) * j)) == NULL)
                    143:                 goto err_exit;
                    144:         if ((sltemp = (char *)malloc(8 * j)) == NULL)
                    145:                 goto err_exit;
                    146:
                    147:         /* Allocate the hash table */
                    148:         if ((ltemp = (long *)malloc(sizeof(long) * initial_hash_chains)) == NULL)
                    149:                 goto err_exit;
                    150:
                    151:         /* Allocate the parameter array */
                    152:         if ( (dtemp->parm = (DICT_PARM_ENTRY *)malloc(14*sizeof(DICT_PARM_ENTRY))) == NULL)
                    153:                 goto err_exit;
                    154:
                    155:         /* Allocate the signature structure */
                    156:         if ( (dtemp->sig=(DICT_SIG*)malloc(sizeof(DICT_SIG))) == NULL )
                    157:                 goto err_exit;
                    158:
                    159:         /* Allocate the Table of Contents */
                    160:         tocsize = toc_size;
                    161:         if ( tocsize < 4 )
                    162:            tocsize = 4;
                    163:         dtemp->sig->toc_size = tocsize;
                    164:         if ( (dtemp->toc=(DICT_TOC_ENTRY*)malloc(dtemp->sig->toc_size*sizeof(DICT_TOC_ENTRY))) == NULL )
                    165:                 goto err_exit;
                    166:
                    167:         dtemp->check_value = DICT_VALIDATE;                     /* validation value */
                    168:         dtemp->flags = DICT_NONE;                               /* no flags set */
                    169:         dtemp->entry_count = 0;                                 /* nothing in either table */
                    170:
                    171:         dtemp->string_table = stemp;                            /* connect string table */
                    172:         dtemp->string_max = j;                                  /* size of string table */
                    173:         dtemp->scan_string_index = DICT_ENTRY_NONE;             /* not pointing to anything in scan */
                    174:         dtemp->string_growth_count = 0;                         /* haven't grown any */
                    175:
                    176:         dtemp->string_array = sltemp;                           /* character array for strings */
                    177:         dtemp->array_size = j * 8;                              /* how many bytes available */
                    178:         dtemp->array_used = 0;                                  /* nothing used yet */
                    179:         dtemp->array_growth_count = 0;                          /* haven't grown any */
                    180:
                    181:         /* force maximum hash chain length to a reasonable value */
                    182:         i = max_chain_length;
                    183:         if (i < 1 || i > 200)
                    184:                 i = 200;
                    185:         dtemp->chains = ltemp;                                  /* connect hash table */
                    186:         dtemp->longest_chain_length = 0;                        /* nothing in table yet */
                    187:         dtemp->allowable_chain_length = i;                      /* chain length limit */
                    188:         dtemp->table_size = initial_hash_chains;                /* size of hash table */
                    189:         dtemp->hash_mask = initial_hash_chains - 1;             /* mask for mod() function */
                    190:         dtemp->hash_growth_count = 0;                           /* haven't grown any */
                    191:
                    192:         /* string table is empty */
                    193:         for (i = 0 ; i < j ; i++) {
                    194:                 dtemp->string_table[i].string_offset = DICT_ENTRY_NONE;
                    195:                 dtemp->string_table[i].count = 0;
                    196:                 dtemp->string_table[i].next = DICT_ENTRY_NONE;
                    197:                 dtemp->string_table[i].flags = 0;
                    198:                 dtemp->string_table[i].hash_value = 0;
                    199:         }
                    200:
                    201:         /* no entries chained */
                    202:         for (i = 0; i < initial_hash_chains; i++)
                    203:                 dtemp->chains[i] = DICT_ENTRY_NONE;
                    204:
                    205:         /* Initialize hook to extended dictionary structure  */
                    206:         dtemp->ext = NULL;
                    207:
                    208:         /* Set up the parameter array */
                    209:         if ( dict_set_parm_ids(dtemp) == FALSE )
                    210:                 goto err_exit;
                    211:         if ( dict_set_parm_values(dtemp) == FALSE )
                    212:                 goto err_exit;
                    213:
                    214:         /* Set up the Table of Contents */
                    215:         strcpy( dtemp->toc[0].id , "PARM" );
                    216:         dtemp->toc[0].size = dtemp->sig->nparms * sizeof(DICT_PARM_ENTRY);
                    217:         dtemp->toc[0].ptr = dtemp->parm;
                    218:         dtemp->toc[0].type = 0;
                    219:
                    220:         strcpy( dtemp->toc[1].id , "HASH" );
                    221:         dtemp->toc[1].size = dtemp->table_size * sizeof(long);
                    222:         dtemp->toc[1].ptr = dtemp->chains;
                    223:         dtemp->toc[1].type = 0;
                    224:
                    225:         strcpy( dtemp->toc[2].id , "STTB" );
                    226:         dtemp->toc[2].size = dtemp->string_max * sizeof(char);
                    227:         dtemp->toc[2].ptr = dtemp->string_table;
                    228:         dtemp->toc[2].type = 0;
                    229:
                    230:         strcpy( dtemp->toc[3].id , "STAR" );
                    231:         dtemp->toc[3].size = dtemp->array_size * sizeof(STRING_ENTRY);
                    232:         dtemp->toc[3].ptr = dtemp->string_array;
                    233:         dtemp->toc[3].type = 0;
                    234:
                    235:         /* Set up the signature */
                    236:         dtemp->sig->check_value = DICT_VALIDATE;
                    237:         dtemp->sig->toc_size = tocsize;
                    238:         dtemp->sig->nparms = 14;
                    239:
                    240:         return dtemp;
                    241:
                    242:         /* error exit - saves duplicate code */
                    243: err_exit:
                    244:
                    245:         dict_destroy( dtemp );
                    246:         return NULL;
                    247: }
                    248:
                    249: /*************************************************************************
                    250: *       dict_destroy: discard a dictionary and its contents
                    251: *               multiple calls to destroy the same dictionary is safe
                    252: *
                    253: *       dict - pointer to a dictionary to discard
                    254: *
                    255: *       Returns: status code
                    256: *               TRUE - dictionary destroyed
                    257: *               FALSE - error when destroying dictionary
                    258: *       Note: Does free the dictionary structure itself.
                    259: *************************************************************************/
                    260: BOOLEANC dict_destroy(DICTIONARY *dict)
                    261: {
                    262:         int  i;
                    263:
                    264:         /* have to point to something */
                    265:         if (dict == NULL)
                    266:                 return FALSE;
                    267:
                    268:         /* check value has to be OK */
                    269:         if (dict->check_value != DICT_VALIDATE)
                    270:                 return FALSE;
                    271:
                    272:         if ( (dict->sig==NULL) || (dict->toc==NULL) || (dict->parm==NULL) )
                    273:                 return FALSE;
                    274:
                    275:         /* Free the type=0 tables */
                    276:         for ( i = 0 ; i < dict->sig->toc_size ; i++ ) {
                    277:            if ( dict->toc[i].ptr != NULL && dict->toc[i].type == 0 )
                    278:                 free( dict->toc[i].ptr );
                    279:         } /* endfor */
                    280:
                    281:         /* Free the Table of Contents and signature */
                    282:         free( dict->toc );
                    283:         free( dict->sig );
                    284:
                    285:         /* Free the dictionary structure itself */
                    286:         free( dict );
                    287:
                    288:         return TRUE;
                    289: }
                    290:
                    291: /*************************************************************************
                    292: *       dict_insert: add entries into the dictionary, growing as necessary
                    293: *
                    294: *       dict - pointer to dictionary to insert into
                    295: *       s - string to insert
                    296: *       count - count of occurences of string
                    297: *       flags - flag bits associated with the string
                    298: *       number - pointer to long to return word number
                    299: *
                    300: *       Returns: pointer to new entry in dictionary
                    301: *               NULL - error during insert
                    302: *               non-NULL - pointer to inserted or updated entry
                    303: *
                    304: *       Notes: if the entry already exists, increment the count.
                    305: *       If NULL is returned, the dictionary state can no longer
                    306: *       be trusted. Terminating with an error code would be
                    307: *       safest.
                    308: *************************************************************************/
                    309: STRING_ENTRY *dict_insert(DICTIONARY *dict, char *s,
                    310:         const long occurences, const unsigned long flags,
                    311:         void *any_ptr, long *number)
                    312: {
                    313:         unsigned long   hash_value;
                    314:         long    hash_index;
                    315:         long    string_index = -1;
                    316:         long    he2;
                    317:         int     chain_len;
                    318:
                    319:         /* have to point to something */
                    320:         if (dict == NULL)
                    321:                 return FALSE;
                    322:
                    323:         /* check value has to be OK */
                    324:         if (dict->check_value != DICT_VALIDATE)
                    325:                 return FALSE;
                    326:
                    327:         /* must have a string */
                    328:         if (s == NULL) {
                    329:                 *number = -1;
                    330:                 return FALSE;
                    331:         }
                    332:
                    333:         /* must be non-NULL */
                    334:         if (s[0] == '\0') {
                    335:                 *number = -1;
                    336:                 return FALSE;
                    337:         }
                    338:
                    339:         /* figure out which chain it should go into */
                    340:         hash_value = hash_string(s);
                    341:         hash_index = hash_value & dict->hash_mask;
                    342:
                    343:         /* look for the entry in the chain */
                    344:         for (he2 = dict->chains[hash_index], chain_len = 0; he2 != DICT_ENTRY_NONE;
                    345:                         he2 = dict->string_table[he2].next, chain_len++) {
                    346:                 char    *sa = &dict->string_array[dict->string_table[he2].string_offset];
                    347:
                    348:                 /* compare hash_value and then string, in that order, for speed */
                    349:                 if (dict->string_table[he2].hash_value == hash_value && !strcmp(s, sa)) {
                    350:                         string_index = he2;
                    351:                         break;
                    352:                 }
                    353:         }
                    354:
                    355:         /* string wasn't found */
                    356:         if (string_index < 0) {
                    357:                 /* make sure there is room in string entry table */
                    358:                 if (dict->entry_count + 1 > dict->string_max) {
                    359:                         /* make new table 20% bigger */
                    360:                         dict->string_max = (dict->string_max * 6) / 5;
                    361:                         dict->string_table = (STRING_ENTRY *)realloc(dict->string_table,
                    362:                                         sizeof(STRING_ENTRY) * dict->string_max);
                    363:                         if (dict->string_table == NULL)
                    364:                                 return NULL;
                    365:                         dict->string_growth_count++;
                    366:                 }
                    367:                 string_index = dict->entry_count;
                    368:                 dict->entry_count++;
                    369:
                    370:                 /* make sure there is room in the string array */
                    371:                 if (dict->array_used + (long)strlen(s) + 1 > dict->array_size) {
                    372:                         dict->array_size = (dict->array_size * 6) / 5;
                    373:                         dict->string_array = (char *) realloc(dict->string_array,
                    374:                                 dict->array_size);
                    375:                         if (dict->string_array == NULL)
                    376:                                 return NULL;
                    377:                 }
                    378:
                    379:                 /* fill in starting values */
                    380:                 strcpy(&dict->string_array[dict->array_used], s);
                    381:                 dict->string_table[string_index].string_offset = dict->array_used;
                    382:                 dict->array_used += (long) strlen(s) + 1;
                    383:                 dict->string_table[string_index].count = 0 ;
                    384:                 dict->string_table[string_index].flags = DICT_NONE;
                    385:                 dict->string_table[string_index].any_ptr = any_ptr; /* (AK:04/04/95) */
                    386:                 dict->string_table[string_index].hash_value = hash_value;
                    387:
                    388:                 /* hook entry at beginning of chain */
                    389:                 dict->string_table[string_index].next = dict->chains[hash_index];
                    390:                 dict->chains[hash_index] = string_index;
                    391:
                    392:                 /* record chain lengths */
                    393:                 chain_len++;
                    394:                 if (chain_len > dict->longest_chain_length)
                    395:                         dict->longest_chain_length = chain_len;
                    396:
                    397:                 /* if a chain is too long */
                    398:                 if (chain_len > dict->allowable_chain_length) {
                    399:                         long    new_size = dict->table_size * 4;
                    400:                         long    new_mask = new_size - 1;
                    401:                         long    *hetemp;
                    402:                         long    i;
                    403:                         int     longest_chain;
                    404:
                    405:                         if ((hetemp = (long *)malloc(sizeof(long) * new_size)) == NULL)
                    406:                                 return NULL;
                    407:
                    408:                         /* hash table chains are empty */
                    409:                         for (i = 0; i < new_size; i++)
                    410:                                 hetemp[i] = DICT_ENTRY_NONE;
                    411:
                    412:                         /* reset all chains */
                    413:                         for (i = 0; i < dict->entry_count; i++)
                    414:                                 dict->string_table[i].next = DICT_ENTRY_NONE;
                    415:
                    416:                         /* recreate hash table entries by reinserting all strings */
                    417:                         for (i = 0; i < dict->entry_count; i++) {
                    418:                                 long    he = dict->string_table[i].hash_value & new_mask;
                    419:
                    420:                                 dict->string_table[i].next = hetemp[he];
                    421:                                 hetemp[he] = i;
                    422:                         }
                    423:
                    424:                         /* find longest chain length */
                    425:                         for (i = 0, longest_chain = 0; i < new_size; i++) {
                    426:                                 int     len;
                    427:                                 long    cur;
                    428:
                    429:                                 for (cur = hetemp[i], len = 0; cur != DICT_ENTRY_NONE;
                    430:                                                 cur = dict->string_table[cur].next)
                    431:                                     len++;
                    432:                                         ;
                    433:                                 if (len > longest_chain)
                    434:                                         longest_chain = len;
                    435:                         }
                    436:
                    437:                         /* delete old table and attach new one */
                    438:                         free(dict->chains);
                    439:                         dict->chains = hetemp;
                    440:                         dict->longest_chain_length = longest_chain;
                    441:                         dict->table_size = new_size;
                    442:                         dict->hash_mask = new_mask;
                    443:
                    444:                         /* keep track of growth */
                    445:                         dict->hash_growth_count++;
                    446:                 }
                    447:         }
                    448:
                    449:         dict->string_table[string_index].count += occurences;
                    450:         dict->string_table[string_index].flags |= flags;
                    451:         *number = string_index;
                    452:
                    453:         return &dict->string_table[string_index];
                    454: }
                    455:
                    456: /*************************************************************************
                    457: *       dict_delete: deletes an entry from the dictionary
                    458: *       (Actually, only decrements the entry's count)
                    459: *
                    460: *       dict - pointer to dictionary to delete
                    461: *       s - string to find and delete
                    462: *       count - count to decrement entry by
                    463: *
                    464: *       Returns: status code
                    465: *               TRUE - entry has been deleted
                    466: *               FALSE - entry wasn't found or error occured
                    467: *************************************************************************/
                    468: BOOLEANC dict_delete(const DICTIONARY *dict, const char *s, const long count)
                    469: {
                    470:         STRING_ENTRY    *se;
                    471:         long    n;
                    472:
                    473:         /* find the string */
                    474:         if ((se = dict_search(dict, s, &n)) == NULL)
                    475:                 return FALSE;
                    476:
                    477:         /* decrement count and make sure it stays valid */
                    478:         se->count -= count;
                    479:         if (se->count < 0)
                    480:                 se->count = 0;
                    481:         return TRUE;
                    482: }
                    483:
                    484: /*************************************************************************
                    485: *       dict_search: look for entries in the dictionary
                    486: *
                    487: *       dict - pointer to dictionary to search
                    488: *       s - string to search for
                    489: *       number - pointer to long to return string number
                    490: *
                    491: *       Returns: pointer to string entry
                    492: *               NULL - entry not found
                    493: *               non-NULL - pointer to string entry
                    494: *************************************************************************/
                    495: STRING_ENTRY *dict_search(const DICTIONARY *dict, const char *s,
                    496:         long *number)
                    497: {
                    498:         unsigned long   hash_value;
                    499:         long    hash_index;
                    500:         long    string_index = -1;
                    501:         long    he;
                    502:
                    503:         /* have to point to something */
                    504:         if (dict == NULL)
                    505:                 return NULL;
                    506:
                    507:         /* check value has to be OK */
                    508:         if (dict->check_value != DICT_VALIDATE)
                    509:                 return NULL;
                    510:
                    511:         /* must have a string */
                    512:         if (s == NULL) {
                    513:                 *number = -1;
                    514:                 return NULL;
                    515:         }
                    516:
                    517:         /* must be non-NULL */
                    518:         if (s[0] == '\0') {
                    519:                 *number = -1;
                    520:                 return FALSE;
                    521:         }
                    522:
                    523:         /* figure out which chain it should be in */
                    524:         hash_value = hash_string(s);
                    525:         hash_index = hash_value & dict->hash_mask;
                    526:
                    527:         /* look for the entry in the chain */
                    528:         for (he = dict->chains[hash_index]; he != DICT_ENTRY_NONE;
                    529:                         he = dict->string_table[he].next) {
                    530:                 char    *sa = (char*)(&dict->string_array[dict->string_table[he].string_offset]);
                    531:
                    532:                 /* compare hash_value and then string, in that order, for speed */
                    533:                 if (dict->string_table[he].hash_value == hash_value && !strcmp(s, sa)) {
                    534:                         string_index = he;
                    535:                         break;
                    536:                 }
                    537:         }
                    538:         if (string_index < 0) {
                    539:                 *number = -1;
                    540:                 return NULL;
                    541:         }
                    542:
                    543:         *number = string_index;
                    544:         return dict->string_table+string_index;
                    545: }
                    546:
                    547: /*************************************************************************
                    548: *       dict_union: merges contents of 2 dictionaries into
                    549: *       a third
                    550: *
                    551: *       dict1 - pointer to dictionary 1
                    552: *       dict2 - pointer to dictionary 2
                    553: *
                    554: *       Returns: dictionary pointer
                    555: *               NULL - failed to merge
                    556: *               non-NULL - pointer to new dictionary
                    557: *
                    558: *       Notes: entries of the same string have their counts
                    559: *       added and their flags ORed together.
                    560: *************************************************************************/
                    561: DICTIONARY *dict_union( const DICTIONARY *dict1 , const DICTIONARY *dict2 )
                    562: {
                    563:         DICTIONARY    *dict = NULL;
                    564:         STRING_ENTRY  *se, *se2;
                    565:         long          initial_string_count, initial_hash_chains, max_chain_length;
                    566:         long          i, string_index;
                    567:
                    568:         /***********
                    569:         **  Initialize the new dictionary.
                    570:         ***********/
                    571:
                    572:         if ( dict1==NULL || dict2==NULL )
                    573:                 goto err_exit;
                    574:         if ((dict=(DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL)
                    575:                 goto err_exit;
                    576:
                    577:         initial_string_count = dict1->string_max;
                    578:         initial_hash_chains = dict1->table_size;
                    579:         max_chain_length = dict1->allowable_chain_length;
                    580:         dict = dict_create( 4,
                    581:                             initial_string_count,
                    582:                             initial_hash_chains,
                    583:                             max_chain_length );
                    584:
                    585:         /***********
                    586:         **  Copy the entries from dict1 into the new dictionary.
                    587:         ***********/
                    588:
                    589:         for ( i = 0 ; i < dict1->entry_count ; i++ ) {
                    590:                 se = (STRING_ENTRY*)&dict1->string_table[i];
                    591:                 if ( se->count > 0 ) {
                    592:                         se2 = dict_insert(
                    593:                                   dict,
                    594:                                   dict1->string_array+se->string_offset,
                    595:                                   se->count,
                    596:                                   se->flags,
                    597:                                   NULL,
                    598:                                   &string_index );
                    599:                         if ( se2 == NULL )
                    600:                                 goto err_exit;
                    601:                 } /* endif */
                    602:         } /* endfor */
                    603:
                    604:         /*  Merge the entries from dict2 into the new dictionary. */
                    605:         if ( dict_merge(dict,dict2,FALSE) == FALSE )
                    606:                 goto err_exit;
                    607:
                    608:         /*  Success. Return a pointer to the new dictionary.  */
                    609:         return( dict );
                    610:
                    611:         /*  Failure. Ignominiously erase our tracks and return NULL. */
                    612: err_exit:
                    613:         dict_destroy( dict );
                    614:         return NULL;
                    615: }
                    616:
                    617: /*************************************************************************
                    618: *       dict_merge: merges the contents of a dictionary into
                    619: *       another one, updating the contents of the destination
                    620: *
                    621: *       dst - dictionary to update
                    622: *       src - dictionary to add
                    623: *       move - boolean flag
                    624: *               TRUE - move to dest
                    625: *               FALSE - copy to dest
                    626: *
                    627: *       Returns: status code
                    628: *               TRUE - merge completed
                    629: *               FALSE - error on merge
                    630: *
                    631: *       Notes: entries of the same string have their counts
                    632: *       added. At the end of a move, src is empty. If there
                    633: *       is an error during a move, the contents of both
                    634: *       dictionaries cannot be trusted.
                    635: *************************************************************************/
                    636: BOOLEANC dict_merge(const DICTIONARY *dst, const DICTIONARY *src, const BOOLEANC move)
                    637: {
                    638:         STRING_ENTRY  *se, *se2;
                    639:         DICTIONARY    *dict1, *dict2;
                    640:         long          i, string_index, index;
                    641:
                    642:         dict1 = (DICTIONARY*)src;
                    643:         dict2 = (DICTIONARY*)dst;
                    644:
                    645:         /***********
                    646:         **  Copy the dictionary entries into the new dictionary.
                    647:         ***********/
                    648:
                    649:         for ( i = 0 ; i < dict1->entry_count ; i++ ) {
                    650:                 se = (STRING_ENTRY*)&dict1->string_table[i];
                    651:                 if ( se->count > 0 ) {
                    652:                         se2 = dict_insert(
                    653:                                   dict2,
                    654:                                   dict1->string_array+se->string_offset,
                    655:                                   se->count,
                    656:                                   se->flags,
                    657:                                   NULL,
                    658:                                   &string_index );
                    659:                         if ( se2 == NULL )
                    660:                                 goto err_exit;
                    661:                 } /* endif */
                    662:         } /* endfor */
                    663:
                    664:         /***********
                    665:         **  Set up the dictionary parameter vector.
                    666:         ***********/
                    667:
                    668:         if ( dict_set_parm_values(dict2) == FALSE )
                    669:                 return( FALSE );
                    670:
                    671:         /***********
                    672:         **  Update the table of contents.
                    673:         **     PARM HASH STTB STAR
                    674:         ***********/
                    675:
                    676:         if ( (index=dict_toc_index(dict2,"HASH")) == -1)
                    677:                 goto err_exit;
                    678:         dict2->toc[index].size = dict2->table_size * sizeof(long);
                    679:         dict2->toc[index].ptr = dict2->chains;
                    680:
                    681:         if ( (index=dict_toc_index(dict2,"STTB")) == -1)
                    682:                 goto err_exit;
                    683:         dict2->toc[index].size = dict2->string_max * sizeof(char);
                    684:         dict2->toc[index].ptr = dict2->string_table;
                    685:
                    686:         if ( (index=dict_toc_index(dict2,"STAR")) == -1)
                    687:                 goto err_exit;
                    688:         dict2->toc[index].size = dict2->array_size * sizeof(STRING_ENTRY);
                    689:         dict2->toc[index].ptr = dict2->string_array;
                    690:
                    691:         /***********
                    692:         **  Update the signature
                    693:         ***********/
                    694:
                    695:         dict2->sig->checksum =
                    696:                 compute_checksum( 4*sizeof(DICT_TOC_ENTRY) , (char*)(dict2->toc) );
                    697:
                    698:         /***********
                    699:         **  If this is a move, destroy the source dictionary
                    700:         ***********/
                    701:
                    702:         if ( move == TRUE )
                    703:                 if ( dict_destroy(dict1) == FALSE )
                    704:                         goto err_exit;
                    705:
                    706:         /***********
                    707:         **  Success
                    708:         ***********/
                    709:
                    710:         return( TRUE );
                    711:
                    712:         /***********
                    713:         **  Failure
                    714:         ***********/
                    715:
                    716: err_exit:
                    717:         dict_destroy( dict2 );
                    718:         return FALSE;
                    719: }
                    720:
                    721: /*************************************************************************
                    722: *       dict_string_by_number: return string pointer for
                    723: *       a given string number
                    724: *
                    725: *       number - string number
                    726: *
                    727: *       Returns: pointer to string entry
                    728: *               NULL - entry not found
                    729: *               non-NULL - pointer to string entry
                    730: *************************************************************************/
                    731: STRING_ENTRY *dict_string_by_number(const DICTIONARY *dict, const long number)
                    732: {
                    733:         if (dict == NULL)
                    734:                 return NULL;
                    735:
                    736:         /* check value has to be OK */
                    737:         if (dict->check_value != DICT_VALIDATE)
                    738:                 return NULL;
                    739:
                    740:         /* string number has to be within range */
                    741:         if (number < 0 || number >= dict->entry_count)
                    742:                 return NULL;
                    743:
                    744:         return dict->string_table+number;
                    745: }
                    746:
                    747: /*************************************************************************
                    748: *       dict_scan_begin: begin a scan of the dictionary
                    749: *
                    750: *       dict - pointer to dictionary to scan
                    751: *
                    752: *       Returns: status code
                    753: *               TRUE - scan initialized
                    754: *               FALSE - error
                    755: *
                    756: *       Notes: if a scan is already in progress, it is
                    757: *       abandoned and the scan is reset to the
                    758: *       beginning.
                    759: *************************************************************************/
                    760: BOOLEANC dict_scan_begin(DICTIONARY *dict)
                    761: {
                    762:         /* have to point to something */
                    763:         if (dict == NULL)
                    764:                 return FALSE;
                    765:
                    766:         /* check value has to be OK */
                    767:         if (dict->check_value != DICT_VALIDATE)
                    768:                 return FALSE;
                    769:
                    770:         /* point to first entry in string table */
                    771:         dict->scan_string_index = 0;
                    772:         return TRUE;
                    773: }
                    774:
                    775: /*************************************************************************
                    776: *       dict_scan_next: get the next entry in a scan
                    777: *
                    778: *       dict - pointer to dictionary to continue scanning
                    779: *
                    780: *       Returns: pointer to string entry
                    781: *               NULL - no more entries
                    782: *               non-NULL - next string entry
                    783: *************************************************************************/
                    784: STRING_ENTRY *dict_scan_next(DICTIONARY *dict)
                    785: {
                    786:         /* have to point to something */
                    787:         if (dict == NULL)
                    788:                 return NULL;
                    789:
                    790:         /* check value has to be OK */
                    791:         if (dict->check_value != DICT_VALIDATE)
                    792:                 return NULL;
                    793:
                    794:         /* scan index has to be within range */
                    795:         if (dict->scan_string_index < 0
                    796:                         || dict->scan_string_index > dict->entry_count)
                    797:                 return NULL;
                    798:
                    799:         /* for first non-empty table entry */
                    800:         while (dict->scan_string_index < dict->entry_count
                    801:                         && dict->string_table[dict->scan_string_index].count == 0)
                    802:                 dict->scan_string_index++;
                    803:
                    804:         /* past end of table? */
                    805:         if (dict->scan_string_index >= dict->entry_count)
                    806:                 return NULL;
                    807:
                    808:         return &dict->string_table[dict->scan_string_index++];
                    809: }
                    810:
                    811:
                    812: /*************************************************************************
                    813: *       dict_load - load a compiled dictionary into memory
                    814: *                   creates a new dictionary
                    815: *
                    816: *       fname - fully qualified file name
                    817: *
                    818: *       Returns: pointer to created dictionary structure
                    819: *                (NULL on failure)
                    820: *************************************************************************/
                    821: DICTIONARY *dict_load(const char *fname)
                    822: {
                    823:         DICTIONARY       *dict = NULL;
                    824:         int              code, index;
                    825:         FILE             *fi;
                    826:         int              ntoc;
                    827:
                    828:         if ( (fi=fopen((char*)fname,"rb")) == NULL ) {
                    829:                 signal_error( "dict_load: could not open file" , (char*)fname , 0 );
                    830:                 goto err_exit;
                    831:         } /* endif */
                    832:
                    833:         if ((dict = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) {
                    834:                 /* signal_error( "dict_load: alloc failed" , "" , 0 ); */
                    835:                 goto err_exit;
                    836:         } /* endif */
                    837:
                    838:         /* Read the dictionary signature record */
                    839:         if ((dict->sig = (DICT_SIG *)malloc(sizeof(DICT_SIG))) == NULL) {
                    840:                 goto err_exit;
                    841:         } /* endif */
                    842:         code = block_read( fi , (char*)(dict->sig) , sizeof(DICT_SIG) , 0 );
                    843:         if ( code == -1 ) {
                    844:                 signal_error( "dict_load: could not read signature" , (char*)fname , 0 );
                    845:                 goto err_exit;
                    846:         } /* endif */
                    847:
                    848:         if ( dict->sig->check_value != DICT_VALIDATE ) {
                    849:                 signal_error( "dict_load: could not validate file" , (char*)fname , 0 );
                    850:                 goto err_exit;
                    851:         } /* endif */
                    852:         dict->check_value = dict->sig->check_value;
                    853:
                    854:         /* Read the dictionary Table of Contents */
                    855:         ntoc = dict->sig->toc_size;
                    856:         dict->toc = (DICT_TOC_ENTRY *) malloc( ntoc * sizeof(DICT_TOC_ENTRY) );
                    857:         if ( dict->toc == NULL ) {
                    858:                 signal_error( "dict_load: alloc of TOC failed" , "" , 0 );
                    859:                 goto err_exit;
                    860:         } /* endif */
                    861:         code = block_read( fi ,
                    862:                            (char*)(dict->toc) ,
                    863:                            ntoc * sizeof(DICT_TOC_ENTRY) ,
                    864:                            sizeof(DICT_SIG) );
                    865:         if ( code == -1 ) {
                    866:                 signal_error( "dict_load: could not read Table of Contents" , (char*)fname , 0 );
                    867:                 goto err_exit;
                    868:         } /* endif */
                    869:
                    870:         /* Read the dictionary parameters */
                    871:         dict->parm = (DICT_PARM_ENTRY *)
                    872:                          dict_load_block( dict , "PARM" , fi , NULL );
                    873:         if ( dict->parm == NULL ) {
                    874:                 signal_error( "dict_load: could not load parameter table" , "" , 0 );
                    875:                 goto err_exit;
                    876:         } /* endif */
                    877:         index = dict_toc_index( dict , "PARM" );
                    878:         dict->sig->nparms = dict->toc[index].size / sizeof(DICT_PARM_ENTRY);
                    879:
                    880:         /* Set the parameter values in the dictionary structure */
                    881:         if ( dict_set_parm_variables(dict) == FALSE )
                    882:                 goto err_exit;
                    883:
                    884:         /* Load the string array */
                    885:         dict->string_array = (char *)
                    886:                 dict_load_block( dict , "STAR" , fi , NULL );
                    887:         if ( dict->string_array == NULL ) {
                    888:                 signal_error( "dict_load: could not load string array" , (char*)fname , 0 );
                    889:                 goto err_exit;
                    890:         } /* endif */
                    891:
                    892:         /* Load the string table */
                    893:         dict->string_table = (STRING_ENTRY *)
                    894:                 dict_load_block( dict , "STTB" , fi , NULL );
                    895:         if ( dict->string_table == NULL ) {
                    896:                 signal_error( "dict_load: could not load string table" , (char*)fname , 0 );
                    897:                 goto err_exit;
                    898:         } /* endif */
                    899:
                    900:         /* Load the hash table */
                    901:         dict->chains = (long *)
                    902:                 dict_load_block( dict , "HASH" , fi , NULL );
                    903:         if ( dict->chains == NULL ) {
                    904:                 signal_error( "dict_load: could not load hash table" , (char*)fname , 0 );
                    905:                 goto err_exit;
                    906:         } /* endif */
                    907:
                    908:         /*  Initialize the hook for dictionary extensions  */
                    909:         dict->ext = NULL;
                    910:
                    911:         /*  Success  */
                    912:         fclose( fi );
                    913:         return( dict );
                    914:
                    915:         /*  Failure  */
                    916: err_exit:
                    917:         if ( fi != NULL )
                    918:                 fclose( fi );
                    919:         dict_destroy( dict );
                    920:         return NULL;
                    921: }
                    922:
                    923:
                    924: /*************************************************************************
                    925: *       dict_save - save a dictionary from memory into a file
                    926: *
                    927: *       dict - pointer to dictionary to save
                    928: *       fname - full qualified file name prefix of dictionary
                    929: *
                    930: *       Returns: status code
                    931: *               TRUE - dictionary was saved sucessfully
                    932: *               FALSE - error during save
                    933: *************************************************************************/
                    934: BOOLEANC dict_save( DICTIONARY *dict, const char *fname )
                    935: {
                    936:         int   index, ret_code;
                    937:         FILE  *fo = NULL;
                    938:
                    939:         /* Have to be pointing at a valid dictionary */
                    940:         if ( dict == NULL || dict->sig->check_value != DICT_VALIDATE )
                    941:                 goto err_exit;
                    942:
                    943:         /* Open the file for output */
                    944:         if ( (fo=fopen((char*)fname,"wb")) == NULL )
                    945:                 goto err_exit;
                    946:
                    947:         /*  Make the table of contents entries current  */
                    948:         /*  Note: This will not be necessary once the data is stored in EVECTORs  */
                    949:
                    950:         if ( (index=dict_toc_index(dict,"PARM")) == -1 )
                    951:                 goto err_exit;
                    952:         dict->toc[index].size = dict->sig->nparms * sizeof(DICT_PARM_ENTRY);
                    953:         dict->toc[index].ptr = dict->parm;
                    954:
                    955:         if ( (index=dict_toc_index(dict,"STAR")) == -1 )
                    956:                 goto err_exit;
                    957:         dict->toc[index].size = dict->array_size * sizeof(char);
                    958:         dict->toc[index].ptr = dict->string_array;
                    959:
                    960:         if ( (index=dict_toc_index(dict,"STTB")) == -1 )
                    961:                 goto err_exit;
                    962:         dict->toc[index].size = dict->string_max * sizeof(STRING_ENTRY);
                    963:         dict->toc[index].ptr = dict->string_table;
                    964:
                    965:         if ( (index=dict_toc_index(dict,"HASH")) == -1 )
                    966:                 goto err_exit;
                    967:         dict->toc[index].size = dict->table_size * sizeof(long);
                    968:         dict->toc[index].ptr = dict->chains;
                    969:
                    970:         /*  Reset the TOC offsets and checksums for ALL tables  */
                    971:         /*  (not just type=0 tables)                            */
                    972:         dict_reset_toc_offsets( dict );
                    973:
                    974:         /* Set the dictionary parm structure from the parameter values */
                    975:         if ( dict_set_parm_values(dict) == FALSE )
                    976:                 goto err_exit;
                    977:
                    978:         /* Save the signature */
                    979:         dict->sig->checksum = compute_checksum( sizeof(DICT_SIG) , (char*)(dict->sig) );
                    980:         ret_code = block_write( fo ,
                    981:                                 (char*)dict->sig ,
                    982:                                 sizeof(DICT_SIG) );
                    983:         if ( ret_code == -1 )
                    984:                 goto err_exit;
                    985:
                    986:         /* Save the table of contents */
                    987:         ret_code = block_write( fo,
                    988:                                 (char*)dict->toc,
                    989:                                 dict->sig->toc_size * sizeof(DICT_TOC_ENTRY) );
                    990:         if ( ret_code == -1 )
                    991:                 goto err_exit;
                    992:
                    993:         /* Save the tables */
                    994:         /* For now, only save type=0 tables */
                    995:         for ( index = 0 ; index < dict->sig->toc_size ; index++ ) {
                    996:                 if ( dict->toc[index].type == 0 ) {  /* Ordinary table */
                    997:                         ret_code = dict_save_block( dict , dict->toc[index].id , fo );
                    998:                         if ( ret_code == FALSE )
                    999:                                 goto err_exit;
                   1000:                      } /* endif */
                   1001:         } /* endfor */
                   1002:
                   1003:         /*  Success  */
                   1004:         fclose( fo );
                   1005:         return TRUE;
                   1006:
                   1007:         /*  Failure  */
                   1008: err_exit:
                   1009:         if ( fo != NULL )
                   1010:                 fclose( fo );
                   1011:         return FALSE;
                   1012: }
                   1013:
                   1014:
                   1015: /*************************************************************************
                   1016: *       dict_import: read in an ASCII dictionary.
                   1017: *
                   1018: *       dict_fname - name of dictionary file
                   1019: *       parameters to create a DICTIONARY structure (see dict_create)
                   1020: *
                   1021: *       Returns: pointer to created DICTIONARY structure
                   1022: *                (NULL on failure)
                   1023: *
                   1024: *************************************************************************/
                   1025: DICTIONARY *dict_import( const char *dict_fname ,
                   1026:                          const long initial_string_count ,
                   1027:                          const long initial_hash_entries ,
                   1028:                          const long max_chain_length )
                   1029: {
                   1030:         DICTIONARY     *dict;
                   1031:         char           buffer[BUFLEN], ch;
                   1032:         int            index, c, c0;
                   1033:         long           number;
                   1034:         FILE           *fi = NULL;
                   1035:
                   1036:         /***********
                   1037:         **  Dictionary setup.
                   1038:         ***********/
                   1039:
                   1040:         dict = dict_create( 4,
                   1041:                             initial_string_count ,
                   1042:                             initial_hash_entries ,
                   1043:                             max_chain_length );
                   1044:         if ( dict == NULL )
                   1045:            goto err_exit;
                   1046:
                   1047:         /***********
                   1048:         **  Read the dictionary file
                   1049:         **  Each line should have one word or a string delimited by '|'
                   1050:         ***********/
                   1051:
                   1052:         if ( (fi=fopen(dict_fname,"r")) == NULL )
                   1053:                 goto err_exit;
                   1054:         while( fgets(buffer,BUFLEN,fi) != NULL ) {
                   1055:                 c0 = 0;
                   1056:                    /*  Skip to non-blank  */
                   1057:                 while ( (c0<BUFLEN-2) && (buffer[c0]==' ') ) ++c0;
                   1058:                 if ( buffer[c0] == '|' ) {
                   1059:                         c = ++c0;
                   1060:                         ch = '|';
                   1061:                 } else {
                   1062:                         c = c0;
                   1063:                         ch = ' ';
                   1064:                 } /* endif */
                   1065:                    /*  Scan to blank or matching '|' */
                   1066:                 while ( (c<BUFLEN-1) && (buffer[c]!='\0') &&
                   1067:                         (buffer[c]!='\n') && (buffer[c]!=ch)  )
                   1068:                         ++c;
                   1069:                 buffer[c] = '\0';
                   1070:                    /*  Insert the word  */
                   1071:                 if ( dict_insert(dict,buffer+c0,1,0,NULL,&number) == NULL )
                   1072:                         goto err_exit;
                   1073:         } /* endwhile */
                   1074:
                   1075:         /***********
                   1076:         **  Fill in the dictionary parameter vector.
                   1077:         ***********/
                   1078:
                   1079:         if ( dict_set_parm_values(dict) == FALSE )
                   1080:                 goto err_exit;
                   1081:
                   1082:         /***********
                   1083:         **  Update the table of contents for HASH STTB STAR
                   1084:         ***********/
                   1085:
                   1086:         if ( (index=dict_toc_index(dict,"HASH")) == -1 )
                   1087:                 goto err_exit;
                   1088:         dict->toc[index].size = dict->table_size * sizeof(long);
                   1089:
                   1090:         if ( (index=dict_toc_index(dict,"STTB")) == -1 )
                   1091:                 goto err_exit;
                   1092:         dict->toc[index].size = dict->string_max * sizeof(char);
                   1093:
                   1094:         if ( (index=dict_toc_index(dict,"STAR")) == -1 )
                   1095:                 goto err_exit;
                   1096:         dict->toc[index].size = dict->array_size * sizeof(STRING_ENTRY);
                   1097:
                   1098:            /*  Success. Return a pointer to the new dictionary.  */
                   1099:         fclose(fi);
                   1100:         return( dict );
                   1101:
                   1102:            /*  Failure. Ignominiously erase our tracks and return NULL.  */
                   1103: err_exit:
                   1104:         if ( fi != NULL )
                   1105:            fclose(fi);
                   1106:         dict_destroy( dict );
                   1107:         return NULL;
                   1108: }
                   1109:
                   1110: /*************************************************************************
                   1111: *       dict_export - save an extended dictionary from memory into
                   1112: *                     an ASCII file
                   1113: *
                   1114: *       dict - pointer to dictionary to save
                   1115: *       fname - full qualified file name prefix of dictionary
                   1116: *
                   1117: *       Returns: status code
                   1118: *               TRUE - dictionary was saved sucessfully
                   1119: *               FALSE - error during save
                   1120: *
                   1121: *************************************************************************/
                   1122: BOOLEANC dict_export( DICTIONARY *dict , const char *fname )
                   1123: {
                   1124:         FILE          *fp = NULL;
                   1125:         STRING_ENTRY  *se;
                   1126:         int       i;
                   1127:
                   1128:         /* have to point to something */
                   1129:         if (dict == NULL)
                   1130:                 goto err_exit;
                   1131:
                   1132:         /* check value has to be OK */
                   1133:         if (dict->check_value != DICT_VALIDATE)
                   1134:                 goto err_exit;
                   1135:
                   1136:         /* must have a filename */
                   1137:         if (fname == NULL)
                   1138:                 goto err_exit;
                   1139:
                   1140:         fp = fopen( (char*)fname , "w" );
                   1141:         if ( fp == NULL )
                   1142:                 goto err_exit;
                   1143:
                   1144:         for ( i = 0 ; i < dict->entry_count ; i++ ) {
                   1145:            se = &dict->string_table[i];
                   1146:            fprintf( fp , "|%s|\n" , dict->string_array + se->string_offset );
                   1147:         } /* endfor */
                   1148:         fclose( fp );
                   1149:
                   1150:         /*  Success.  */
                   1151:         fclose(fp);
                   1152:         return TRUE;
                   1153:
                   1154:         /*  Failure.  */
                   1155: err_exit:
                   1156:         if ( fp != NULL )
                   1157:            fclose(fp);
                   1158:         dict_destroy( dict );
                   1159:         return FALSE;
                   1160: }

CVSweb