1 /*
2    Copyright (c) 1991-1999 Thomas T. Wetmore IV
3 
4    Permission is hereby granted, free of charge, to any person
5    obtaining a copy of this software and associated documentation
6    files (the "Software"), to deal in the Software without
7    restriction, including without limitation the rights to use, copy,
8    modify, merge, publish, distribute, sublicense, and/or sell copies
9    of the Software, and to permit persons to whom the Software is
10    furnished to do so, subject to the following conditions:
11 
12    The above copyright notice and this permission notice shall be
13    included in all copies or substantial portions of the Software.
14 
15    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22    SOFTWARE.
23 */
24 /*=============================================================
25  * keytonod.c -- Cache for lifelines custom btree database
26  * Copyright(c) 1992-94 by T.T. Wetmore IV; all rights reserved
27  *   2.3.4 - 24 Jun 93    2.3.5 - 01 Sep 93
28  *   3.0.0 - 08 May 94    3.0.2 - 23 Dec 94
29  *   3.0.3 - 16 Jul 95
30  *===========================================================*/
31 
32 #include "llstdlib.h"
33 #include "table.h"
34 #include "translat.h"
35 #include "gedcom.h"
36 #include "gedcomi.h"
37 #include "cache.h"
38 #include "liflines.h"
39 #include "feedback.h"
40 #include "zstr.h"
41 
42 /*********************************************
43  * global variables (no header)
44  *********************************************/
45 
46 char badkeylist[100] = "";
47 int listbadkeys = 0;
48 
49 
50 
51 /*===============================
52  * CACHEEL -- Cache element type.
53  *=============================*/
54 /* typedef struct tag_cacheel *CACHEEL; */
55 struct tag_cacheel {
56 	CNSTRING c_magic; /* points to cel_magic */
57 	NODE c_node;      /* root node */
58 	CACHEEL c_prev;   /* previous el */
59 	CACHEEL c_next;   /* next el */
60 	STRING c_key;     /* record key */
61 	INT c_lock;       /* lock count (includes report locks) */
62 	INT c_rptlock;    /* report lock count */
63 	RECORD c_record;
64 };
65 #define cnode(e)      ((e)->c_node)
66 #define cprev(e)      ((e)->c_prev)
67 #define cnext(e)      ((e)->c_next)
68 #define ckey(e)       ((e)->c_key)
69 #define cclock(e)     ((e)->c_lock)
70 #define ccrptlock(e)  ((e)->c_rptlock)
71 #define crecord(e)    ((e)->c_record)
72 
73 /*==============================
74  * CACHE -- Internal cache type.
75  *============================*/
76 typedef struct {
77 	char c_name[5];
78 	TABLE c_data;        /* table of keys */
79 	CACHEEL c_firstdir;  /* first direct */
80 	CACHEEL c_lastdir;   /* last direct */
81 	CACHEEL c_array;     /* big array of cacheels, all alloc'd in a block */
82 	CACHEEL c_free;      /* root of free list */
83 	INT c_maxdir;        /* max in direct */
84 	INT c_sizedir;       /* cur in direct */
85 } *CACHE;
86 #define cacname(c)     ((c)->c_name)
87 #define cacdata(c)     ((c)->c_data)
88 #define cacfirstdir(c) ((c)->c_firstdir)
89 #define caclastdir(c)  ((c)->c_lastdir)
90 #define cacarray(e) ((e)->c_array)
91 #define cacfree(e) ((e)->c_free)
92 #define cacmaxdir(c)   ((c)->c_maxdir)
93 #define cacsizedir(c)  ((c)->c_sizedir)
94 
95 
96 /*********************************************
97  * local function prototypes
98  *********************************************/
99 
100 /* static void add_record_to_direct(CACHE cache, RECORD rec, STRING key); */
101 static void cache_get_lock_counts(CACHE ca, INT * locks);
102 static CACHE create_cache(STRING name, INT dirsize);
103 static void delete_cache(CACHE * pcache);
104 static void ensure_cel_has_record(CACHEEL cel);
105 static ZSTR get_cache_stats(CACHE ca);
106 static CACHEEL get_free_cacheel(CACHE cache);
107 static void init_cel(CACHEEL cel);
108 static CACHEEL key_to_cacheel(CACHE cache, CNSTRING key, STRING tag, INT reportmode);
109 static CACHEEL key_to_even_cacheel(CNSTRING key);
110 static NODE key_typed_to_node(CACHE cache, CNSTRING key, STRING tag);
111 static RECORD key_to_record_impl(CNSTRING key, INT reportmode);
112 static RECORD key_typed_to_record(CACHE cache, CNSTRING key, STRING tag);
113 static CACHEEL key_to_othr_cacheel(CNSTRING key);
114 static CACHEEL key_to_sour_cacheel(CNSTRING key);
115 static CACHEEL node_to_cache(CACHE, NODE);
116 static void put_node_in_cache(CACHE cache, CACHEEL cel, NODE node, STRING key);
117 static void remove_cel_from_cache(CACHE cache, CACHEEL cel, BOOLEAN delcache);
118 static NODE qkey_to_node(CACHE cache, CNSTRING key, STRING tag);
119 static RECORD qkey_typed_to_record(CACHE cache, CNSTRING key, STRING tag);
120 /* static CACHEEL qkey_to_typed_cacheel(STRING key); */
121 static void remove_from_cache(CACHE, CNSTRING);
122 
123 
124 INT csz_indi = 200;		/* cache size for indi */
125 INT csz_fam = 200;		/* cache size for fam */
126 INT csz_sour = 200;		/* cache size for sour */
127 INT csz_even = 200;		/* cache size for even */
128 INT csz_othr = 200;		/* cache size for othr */
129 
130 /*********************************************
131  * local variables
132  *********************************************/
133 
134 static CACHE indicache, famcache, evencache, sourcache, othrcache;
135 
136 static CNSTRING cel_magic = "CEL_MAGIC"; /* fixed pointer to identify cel */
137 
138 /* keybuf circular list of last 10 keys we looked up in cache
139  * kept for printing debug messages in crash log
140  */
141 static char keybuf[10][32] = { "", "", "", "", "", "", "", "", "", ""};
142 static int keyidx = 0;
143 
144 /*********************************************
145  * local function definitions
146  * body of module
147  *********************************************/
148 
149 /*================================================
150  * node_to_record -- Find record from node
151  *==============================================*/
152 RECORD
node_to_record(NODE node)153 node_to_record (NODE node)
154 {
155 	STRING key = rmvat(nxref(node));
156 	return key_to_record(key);
157 }
158 /*================================================
159  * keynum_to_indi -- Convert a numeric key to an indi node
160  * assert if failed (ie, no indi with that number)
161  *==============================================*/
162 NODE
keynum_to_indi(int keynum)163 keynum_to_indi (int keynum)
164 {
165 	char keystr[20];
166 	sprintf(keystr,"I%d",keynum);
167 	return key_to_indi(keystr);
168 }
169 RECORD
keynum_to_irecord(int keynum)170 keynum_to_irecord (int keynum)
171 {
172 	char keystr[20];
173 	sprintf(keystr,"I%d",keynum);
174 	return key_to_irecord(keystr);
175 }
176 /*=========================================================
177  * qkeynum_to_indi -- Convert a numeric key to an indi node
178  *  report mode - it returns NULL if failed
179  *  (ie, no indi with that number)
180  *=======================================================*/
181 NODE
qkeynum_to_indi(int keynum)182 qkeynum_to_indi (int keynum)
183 {
184 	char keystr[20];
185 	sprintf(keystr,"I%d",keynum);
186 	return qkey_to_indi(keystr);
187 }
188 /*================================================
189  * keynum_to_fam -- Convert a numeric key to a fam node
190  *  assert if failed (ie, no fam with that number)
191  *==============================================*/
192 NODE
keynum_to_fam(int keynum)193 keynum_to_fam (int keynum)
194 {
195 	char keystr[20];
196 	sprintf(keystr,"F%d",keynum);
197 	return key_to_fam(keystr);
198 }
199 RECORD
keynum_to_frecord(int keynum)200 keynum_to_frecord (int keynum)
201 {
202 	char keystr[20];
203 	sprintf(keystr,"F%d",keynum);
204 	return key_to_frecord(keystr);
205 }
206 /*======================================================
207  * qkeynum_to_frecord -- Convert a numeric key to a fam record
208  *  report mode - it returns NULL if failed
209  *  (ie, no fam with that number)
210  *====================================================*/
211 RECORD
qkeynum_to_frecord(int keynum)212 qkeynum_to_frecord (int keynum)
213 {
214 	char keystr[20];
215 	sprintf(keystr,"F%d",keynum);
216 	return qkey_to_frecord(keystr);
217 }
218 /*================================================
219  * keynum_to_sour -- Convert a numeric key to a sour node
220  *  assert if failed (ie, no sour with that number)
221  *==============================================*/
222 NODE
keynum_to_sour(int keynum)223 keynum_to_sour (int keynum)
224 {
225 	return nztop(keynum_to_srecord(keynum));
226 }
227 RECORD
keynum_to_srecord(int keynum)228 keynum_to_srecord (int keynum)
229 {
230 	char keystr[20];
231 	sprintf(keystr,"S%d",keynum);
232 	return key_to_srecord(keystr);
233 }
234 /*================================================
235  * keynum_to_even -- Convert a numeric key to a even node
236  *  assert if failed (ie, no sour with that number)
237  *==============================================*/
238 NODE
keynum_to_even(int keynum)239 keynum_to_even (int keynum)
240 {
241 	return nztop(keynum_to_erecord(keynum));
242 }
243 RECORD
keynum_to_erecord(int keynum)244 keynum_to_erecord (int keynum)
245 {
246 	char keystr[MAXKEYWIDTH+1];
247 	sprintf(keystr,"E%d",keynum);
248 	return key_to_erecord(keystr);
249 }
250 /*================================================
251  * keynum_to_othr -- Convert a numeric key to an other node
252  *  assert if failed (ie, no sour with that number)
253  *==============================================*/
254 NODE
keynum_to_othr(int keynum)255 keynum_to_othr (int keynum)
256 {
257 	char keystr[20];
258 	sprintf(keystr,"X%d",keynum);
259 	return key_to_othr(keystr);
260 }
261 RECORD
keynum_to_orecord(int keynum)262 keynum_to_orecord (int keynum)
263 {
264 	char keystr[20];
265 	sprintf(keystr,"X%d",keynum);
266 	return key_to_orecord(keystr);
267 }
268 /*=====================================
269  * keynum_to_node -- Convert keynum to node
270  *===================================*/
271 NODE
keynum_to_node(char ntype,int keynum)272 keynum_to_node (char ntype, int keynum)
273 {
274 	switch(ntype) {
275 	case 'I': return keynum_to_indi(keynum);
276 	case 'F': return keynum_to_fam(keynum);
277 	case 'S': return keynum_to_sour(keynum);
278 	case 'E': return keynum_to_even(keynum);
279 	case 'X': return keynum_to_othr(keynum);
280 	}
281 	ASSERT(0);
282 	return 0;
283 }
284 RECORD
keynum_to_record(char ntype,int keynum)285 keynum_to_record (char ntype, int keynum)
286 {
287 	switch(ntype) {
288 	case 'I': return keynum_to_irecord(keynum);
289 	case 'F': return keynum_to_frecord(keynum);
290 	case 'S': return keynum_to_srecord(keynum);
291 	case 'E': return keynum_to_erecord(keynum);
292 	case 'X': return keynum_to_orecord(keynum);
293 	}
294 	ASSERT(0);
295 	return 0;
296 }
297 /*=====================================
298  * key_to_type -- Convert key to node
299  * TO DO - should become obsoleted by key_to_record
300  *===================================*/
301 NODE
key_to_type(CNSTRING key,INT reportmode)302 key_to_type (CNSTRING key, INT reportmode)
303 {
304 	RECORD rec = key_to_record_impl(key, reportmode);
305 	NODE node = nztop(rec);
306 	release_record(rec); /* avoiding leaking reference */
307 	return node;
308 }
309 /*=====================================
310  * qkey_to_type -- Convert key to node
311  * quiet -- that is, returns NULL if record not in database
312  *===================================*/
313 NODE
qkey_to_type(CNSTRING key)314 qkey_to_type (CNSTRING key)
315 {
316 	return key_to_type(key, TRUE);
317 }
318 /*=====================================
319  * key_to_record_impl -- Convert key (any type) to RECORD
320  *  returns addref'd record
321  *===================================*/
322 static RECORD
key_to_record_impl(CNSTRING key,INT reportmode)323 key_to_record_impl (CNSTRING key, INT reportmode)
324 {
325 	switch(key[0])
326 	{
327 	case 'I': return reportmode ? qkey_to_irecord(key) : key_to_irecord(key);
328 	case 'F': return reportmode ? qkey_to_frecord(key) : key_to_frecord(key);
329 	case 'S': return reportmode ? qkey_to_srecord(key) : key_to_srecord(key);
330 	case 'E': return reportmode ? qkey_to_erecord(key) : key_to_erecord(key);
331 	}
332 	return reportmode ? qkey_to_orecord(key) : key_to_orecord(key);
333 }
334 /*=====================================
335  * key_to_record -- Convert key (any type) to RECORD
336  * ASSERTS if record not found in database
337  *  returns addref'd record
338  *===================================*/
339 RECORD
key_to_record(CNSTRING key)340 key_to_record (CNSTRING key)
341 {
342 	return key_to_record_impl(key, FALSE);
343 }
344 /*=====================================
345  * qkey_to_record -- Convert key (any type) to RECORD
346  * quiet -- that is, returns NULL if record not in database
347  *  returns addref'd record
348  *===================================*/
349 RECORD
qkey_to_record(CNSTRING key)350 qkey_to_record (CNSTRING key)
351 {
352 	return key_to_record_impl(key, TRUE);
353 }
354 /*=====================================
355  * key_to_??? -- Convert key to person
356  *  (asserts if failure)
357  *  5 symmetric versions
358  * TO DO - should become obsoleted by key_to_???0
359  *===================================*/
360 NODE
key_to_indi(CNSTRING key)361 key_to_indi (CNSTRING key)
362 {
363 	return key_typed_to_node(indicache, key, "INDI");
364 }
key_to_fam(CNSTRING key)365 NODE key_to_fam (CNSTRING key)
366 {
367 	return key_typed_to_node(famcache, key, "FAM");
368 }
key_to_even(CNSTRING key)369 NODE key_to_even (CNSTRING key)
370 {
371 	return key_typed_to_node(evencache, key, "EVEN");
372 }
key_to_sour(CNSTRING key)373 NODE key_to_sour (CNSTRING key)
374 {
375 	return key_typed_to_node(sourcache, key, "SOUR");
376 }
key_to_othr(CNSTRING key)377 NODE key_to_othr (CNSTRING key)
378 {
379 	return key_typed_to_node(othrcache, key, NULL);
380 }
381 /*=====================================
382  * key_to_???0 -- Convert key to person
383  *  (asserts if failure)
384  *  5 symmetric versions
385  * returns addref'd record
386  *===================================*/
key_to_irecord(CNSTRING key)387 RECORD key_to_irecord (CNSTRING key)
388 {
389 	return key_typed_to_record(indicache, key, "INDI");
390 }
key_to_frecord(CNSTRING key)391 RECORD key_to_frecord (CNSTRING key)
392 {
393 	return key_typed_to_record(famcache, key, "FAM");
394 }
key_to_erecord(CNSTRING key)395 RECORD key_to_erecord (CNSTRING key)
396 {
397 	return key_typed_to_record(evencache, key, "EVEN");
398 }
key_to_srecord(CNSTRING key)399 RECORD key_to_srecord (CNSTRING key)
400 {
401 	return key_typed_to_record(sourcache, key, "SOUR");
402 }
key_to_orecord(CNSTRING key)403 RECORD key_to_orecord (CNSTRING key)
404 {
405 	return key_typed_to_record(othrcache, key, NULL);
406 }
407 /*========================================
408  * qkey_to_??? -- Convert key to node type
409  *  report mode (returns NULL if failure)
410  *  5 symmetric versions
411  * TO DO - should become obsoleted by key_to_???0
412  *======================================*/
qkey_to_indi(CNSTRING key)413 NODE qkey_to_indi (CNSTRING key)
414 {
415 	return qkey_to_node(indicache, key, "INDI");
416 }
qkey_to_fam(CNSTRING key)417 NODE qkey_to_fam (CNSTRING key)
418 {
419 	return qkey_to_node(famcache, key, "FAM");
420 }
qkey_to_even(CNSTRING key)421 NODE qkey_to_even (CNSTRING key)
422 {
423 	return qkey_to_node(evencache, key, "EVEN");
424 }
qkey_to_sour(CNSTRING key)425 NODE qkey_to_sour (CNSTRING key)
426 {
427 	return qkey_to_node(sourcache, key, "SOUR");
428 }
qkey_to_othr(CNSTRING key)429 NODE qkey_to_othr (CNSTRING key)
430 {
431 	return qkey_to_node(othrcache, key, NULL);
432 }
433 /*========================================
434  * qkey_to_???0 -- Convert key to node type
435  *  report mode (returns NULL if failure)
436  *  5 symmetric versions
437  * All return addref'd records
438  *======================================*/
qkey_to_irecord(CNSTRING key)439 RECORD qkey_to_irecord (CNSTRING key)
440 {
441 	return qkey_typed_to_record(indicache, key, "INDI");
442 }
qkey_to_frecord(CNSTRING key)443 RECORD qkey_to_frecord (CNSTRING key)
444 {
445 	return qkey_typed_to_record(famcache, key, "FAM");
446 }
qkey_to_erecord(CNSTRING key)447 RECORD qkey_to_erecord (CNSTRING key)
448 {
449 	return qkey_typed_to_record(evencache, key, "EVEN");
450 }
qkey_to_srecord(CNSTRING key)451 RECORD qkey_to_srecord (CNSTRING key)
452 {
453 	return qkey_typed_to_record(sourcache, key, "SOUR");
454 }
qkey_to_orecord(CNSTRING key)455 RECORD qkey_to_orecord (CNSTRING key)
456 {
457 	return qkey_typed_to_record(othrcache, key, NULL);
458 }
459 /*=====================================================
460  * key_to_unknown_cacheel -- Convert any key to cacheel
461  *===================================================*/
462 CACHEEL
key_to_unknown_cacheel(STRING key)463 key_to_unknown_cacheel (STRING key)
464 {
465 	switch(key[0]) {
466 		case 'I': return key_to_indi_cacheel(key);
467 		case 'F': return key_to_fam_cacheel(key);
468 		case 'S': return key_to_sour_cacheel(key);
469 		case 'E': return key_to_even_cacheel(key);
470 		default: return key_to_othr_cacheel(key);
471 	}
472 }
473 /*=====================================================
474  * key_to_indi_cacheel -- Convert key to person cacheel
475  *===================================================*/
476 CACHEEL
key_to_indi_cacheel(STRING key)477 key_to_indi_cacheel (STRING key)
478 {
479 	return key_to_cacheel(indicache, key, "INDI", FALSE);
480 }
481 /*====================================================
482  * key_to_fam_cacheel -- Convert key to family_cacheel
483  *==================================================*/
484 CACHEEL
key_to_fam_cacheel(STRING key)485 key_to_fam_cacheel (STRING key)
486 {
487 	return key_to_cacheel(famcache, key, "FAM", FALSE);
488 }
489 /*=====================================================
490  * key_to_sour_cacheel -- Convert key to source_cacheel
491  *===================================================*/
492 static CACHEEL
key_to_sour_cacheel(CNSTRING key)493 key_to_sour_cacheel (CNSTRING key)
494 {
495 	return key_to_cacheel(sourcache, key, "SOUR", FALSE);
496 }
497 /*====================================================
498  * key_to_even_cacheel -- Convert key to event_cacheel
499  *==================================================*/
500 static CACHEEL
key_to_even_cacheel(CNSTRING key)501 key_to_even_cacheel (CNSTRING key)
502 {
503 	return key_to_cacheel(evencache, key, "EVEN", FALSE);
504 }
505 /*====================================================
506  * key_to_othr_cacheel -- Convert key to other_cacheel
507  *==================================================*/
508 static CACHEEL
key_to_othr_cacheel(CNSTRING key)509 key_to_othr_cacheel (CNSTRING key)
510 {
511 	return key_to_cacheel(othrcache, key, NULL, FALSE);
512 }
513 /*======================================
514  * init_caches -- Create and init caches
515  *====================================*/
516 void
init_caches(void)517 init_caches (void)
518 {
519 	indicache = create_cache("INDI", csz_indi);
520 	famcache  = create_cache("FAM", csz_fam);
521 	evencache = create_cache("EVEN", csz_even);
522 	sourcache = create_cache("SOUR", csz_sour);
523 	othrcache = create_cache("OTHR", csz_othr);
524 }
525 /*======================================
526  * free_caches -- Release cache memory
527  * Created: 2003-02-02 (Perry Rapp)
528  *====================================*/
529 void
free_caches(void)530 free_caches (void)
531 {
532 	delete_cache(&indicache);
533 	delete_cache(&famcache);
534 	delete_cache(&evencache);
535 	delete_cache(&sourcache);
536 	delete_cache(&othrcache);
537 }
538 /*=============================
539  * create_cache -- Create cache
540  *===========================*/
541 static CACHE
create_cache(STRING name,INT dirsize)542 create_cache (STRING name, INT dirsize)
543 {
544 	CACHE cache;
545 	INT i;
546 	if (dirsize < 1) dirsize = 1;
547 	cache = (CACHE) stdalloc(sizeof(*cache));
548 	memset(cache, 0, sizeof(*cache));
549 	llstrncpy(cacname(cache), name, sizeof(cacname(cache)), uu8);
550 	/*
551 	It would be nice to set the table hash size larger for large
552 	caches, but right now (2003-10-08), tables do not expose a
553 	method to set their hash size.
554 	*/
555 	cacdata(cache) = create_table_vptr(); /* pointers to cache elements, owned by cacarray */
556 	cacfirstdir(cache) = caclastdir(cache) = NULL;
557 	cacsizedir(cache) = 0;
558 	cacmaxdir(cache) = dirsize;
559 	/* Allocate all the cache elements in a big block */
560 	cacarray(cache) = (CACHEEL) stdalloc(cacmaxdir(cache) * sizeof(cacarray(cache)[0]));
561 	/* Link all the elements together on the free list */
562 	for (i=0; i<cacmaxdir(cache); ++i) {
563 		CACHEEL cel = &cacarray(cache)[i];
564 		CACHEEL celnext = cacfree(cache);
565 		init_cel(cel);
566 		if (celnext) {
567 			cnext(cel) = celnext;
568 			cprev(celnext) = cel;
569 		}
570 		cacfree(cache) = cel;
571 	}
572 	return cache;
573 }
574 /*=============================
575  * delete_cache -- Delete cache entirely
576  *===========================*/
577 static void
delete_cache(CACHE * pcache)578 delete_cache (CACHE * pcache)
579 {
580 	INT num=0;
581 	CACHE cache = *pcache;
582 	CACHEEL frst=0;
583 	if (!cache) return;
584 	/* Loop through all cache elements, freeing each */
585 	while ((frst = cacfirstdir(cache)) != 0) {
586 		BOOLEAN delcache = TRUE;
587 		remove_cel_from_cache(cache, frst, delcache);
588 	}
589 	/* TODO: Need to delete cache elements on free list */
590 	num = get_table_count(cacdata(cache));
591 	ASSERT(num == 0);
592 	destroy_table(cacdata(cache));
593 	stdfree(cacarray(cache));
594 	stdfree(cache);
595 	*pcache = 0;
596 }
597 /*=============================
598  * init_cel -- Initialize new cacheel
599  *===========================*/
600 static void
init_cel(CACHEEL cel)601 init_cel (CACHEEL cel)
602 {
603 	memset(cel, 0, sizeof(*cel));
604 	cel->c_magic = cel_magic;
605 }
606 /*=================================================
607  * remove_direct -- Unlink CACHEEL from direct list
608  *===============================================*/
609 static void
remove_direct(CACHE cache,CACHEEL cel)610 remove_direct (CACHE cache, CACHEEL cel)
611 {
612 	CACHEEL prev = cprev(cel);
613 	CACHEEL next = cnext(cel);
614 	ASSERT(cache);
615 	ASSERT(cel);
616 	if (prev) cnext(prev) = next;
617 	if (next) cprev(next) = prev;
618 	if (!prev) cacfirstdir(cache) = next;
619 	if (!next) caclastdir(cache) = prev;
620 	cacsizedir(cache)--;
621 }
622 /*===========================================================
623  * first_direct -- Make unlinked CACHEEL first in direct list
624  *=========================================================*/
625 static void
first_direct(CACHE cache,CACHEEL cel)626 first_direct (CACHE cache, CACHEEL cel)
627 {
628 	CACHEEL frst = cacfirstdir(cache);
629 	ASSERT(cache);
630 	ASSERT(cel);
631 	cacsizedir(cache)++;
632 	cprev(cel) = NULL;
633 	cnext(cel) = frst;
634 	if (frst) cprev(frst) = cel;
635 	if (!frst) caclastdir(cache) = cel;
636 	cacfirstdir(cache) = cel;
637 }
638 /*============================================================
639  * direct_to_first -- Make direct CACHEEL first in direct list
640  *==========================================================*/
641 static void
direct_to_first(CACHE cache,CACHEEL cel)642 direct_to_first (CACHE cache, CACHEEL cel)
643 {
644 	ASSERT(cache);
645 	ASSERT(cel);
646 	if (cel == cacfirstdir(cache)) return;
647 	remove_direct(cache, cel);
648 	first_direct(cache, cel);
649 }
650 /*========================================================
651  * add_to_direct -- Add new CACHEEL to direct part of cache
652  * reportmode: if True, then return NULL rather than aborting
653  *   if there is no record. Also return NULL for deleted
654  *   records (of length less than 6???)
655  *  cache:      [IN]  which cache to which to add
656  *  key:        [IN]  key of record to be added
657  *  reportmode: [IN] if non-zero, failures should be silent
658  * Only called if record is not already in cache (caller's responsibility)
659  *======================================================*/
660 static CACHEEL
add_to_direct(CACHE cache,CNSTRING key,INT reportmode)661 add_to_direct (CACHE cache, CNSTRING key, INT reportmode)
662 {
663 	STRING rawrec=0;
664 	INT len=0;
665 	CACHEEL cel=0;
666 	RECORD rec=0;
667 	int i, j;
668 
669 	ASSERT(cache);
670 	ASSERT(key);
671 	rec = NULL;
672 	if ((rawrec = retrieve_raw_record(key, &len)))
673 		/* 2003-11-22, we should use string_to_node here */
674 		rec = string_to_record(rawrec, key, len);
675 	if (!rec)
676 	{
677 		ZSTR zstr=zs_newn(256);
678 		if(listbadkeys) {
679 			if(strlen(badkeylist) < 80 - strlen(key) - 2) {
680 				if (badkeylist[0])
681 					strcat(badkeylist, ",");
682 				strcat(badkeylist, key);
683 			}
684 			return(NULL);
685 		}
686 		if (reportmode) return(NULL);
687 		crashlogn(_("Database error caused by reference to nonexisting key <%s>."), (char *)key);
688 		crashlogn(_("It might be possible to fix this with btedit."));
689 		zs_sets(zstr, _("Neighboring keys include:"));
690 		for(i = 0; i < 10; i++)
691 		{
692 			j = keyidx + i;
693 			if(j >= 10) j -= 10;
694 			if (keybuf[j][0]) {
695 				zs_appc(zstr, ' ');
696 				zs_apps(zstr, keybuf[j]);
697 			}
698 		}
699 		crashlogn(zs_str(zstr));
700 		zs_free(&zstr);
701 		/* deliberately fall through to let ASSERT(rec) fail */
702 	}
703 	ASSERT(rec);
704 	/* record was just loaded, nztop should not need to load it */
705 	cel = node_to_cache(cache, nztop(rec));
706 	ASSERT(!crecord(cel));
707 	/* node_to_cache did a first_direct call, so record in cache */
708 	record_set_cel(rec, cel);
709 	/* our new rec above has one reference, which is held by cel */
710 	crecord(cel) = rec;
711 	stdfree(rawrec);
712 	ASSERT(cel->c_magic == cel_magic);
713 	return cel;
714 }
715 /*======================================================
716  * key_to_cacheel -- Return CACHEEL corresponding to key
717  *====================================================*/
718 static CACHEEL
key_to_cacheel(CACHE cache,CNSTRING key,STRING tag,INT reportmode)719 key_to_cacheel (CACHE cache, CNSTRING key, STRING tag, INT reportmode)
720 {
721 	CACHEEL cel;
722 
723 	strncpy(keybuf[keyidx], (key ? (char *)key : "NULL"), 31);
724 	keybuf[keyidx][31] = '\0';
725 	keyidx++;
726 	if(keyidx >= 10) keyidx = 0;
727 	if ((cel = (CACHEEL) valueof_ptr(cacdata(cache), key))) {
728 		ASSERT(cnode(cel));
729 		ASSERT(cel->c_magic == cel_magic);
730 		direct_to_first(cache, cel);
731 		if (tag) {
732 			ASSERT(eqstr(tag, ntag(cnode(cel))));
733 			ASSERT(crecord(cel));
734 			ASSERT(eqstr(key, nzkey(crecord(cel))));
735 		}
736 		return cel;
737 	}
738 	cel = add_to_direct(cache, key, reportmode);
739 	if (cel && tag) {
740 		ASSERT(eqstr(tag, ntag(cnode(cel))));
741 		ASSERT(crecord(cel));
742 		ASSERT(eqstr(key, nzkey(crecord(cel))));
743 	}
744 	return cel;
745 }
746 /*===============================================================
747  * key_to_node -- Return tree from key; add to cache if not there
748  * asserts if failure
749  * TO DO - should become obsoleted by key_typed_to_record
750  *=============================================================*/
751 static NODE
key_typed_to_node(CACHE cache,CNSTRING key,STRING tag)752 key_typed_to_node (CACHE cache, CNSTRING key, STRING tag)
753 {
754 	CACHEEL cel;
755 	ASSERT(cache);
756 	ASSERT(key);
757 	if (!(cel = key_to_cacheel(cache, key, tag, FALSE)))
758 		return NULL;
759 	return cnode(cel);
760 }
761 /*===============================================================
762  * key_typed_to_record -- Return record from key; add to cache if not there
763  * asserts if failure
764  * returns addref'd record
765  *=============================================================*/
766 static RECORD
key_typed_to_record(CACHE cache,CNSTRING key,STRING tag)767 key_typed_to_record (CACHE cache, CNSTRING key, STRING tag)
768 {
769 	CACHEEL cel;
770 	ASSERT(cache);
771 	ASSERT(key);
772 	if (!(cel = key_to_cacheel(cache, key, tag, FALSE)))
773 		return NULL;
774 	return get_record_for_cel(cel);
775 }
776 /*===================================
777  * get_record_for_cel -- get or create new record from cacheel
778  * returns addref'd record
779  *=================================*/
780 RECORD
get_record_for_cel(CACHEEL cel)781 get_record_for_cel (CACHEEL cel)
782 {
783 	RECORD rec=0;
784 	NODE node=0;
785 	STRING key=0;
786 
787 	ASSERT(cel);
788 	if (crecord(cel)) {
789 		rec = crecord(cel);
790 		addref_record(rec);
791 		return rec;
792 	}
793 	ASSERT(cnode(cel));
794 	node = cnode(cel);
795 	ASSERT(nxref(node));
796 
797 	key = ckey(cel);
798 
799 	rec = make_new_record_with_info(key, cel);
800 	record_set_cel(rec, cel);
801 	crecord(cel) = rec;
802 	key = node_to_key(node);
803 
804 	set_record_key_info(rec, key);
805 	addref_record(rec);
806 	return rec;
807 }
808 /*===============================================================
809  * qkey_to_node -- Return tree from key; add to cache if not there
810  * report mode - returns NULL if failure
811  * TO DO - should become obsoleted by qkey_typed_to_record
812  *=============================================================*/
813 static NODE
qkey_to_node(CACHE cache,CNSTRING key,STRING tag)814 qkey_to_node (CACHE cache, CNSTRING key, STRING tag)
815 {
816 	CACHEEL cel;
817 	ASSERT(cache);
818 	if (!key) return NULL;
819 	if (!(cel = key_to_cacheel(cache, key, tag, TRUE)))
820 		return NULL;
821 	return cnode(cel);
822 }
823 /*===============================================================
824  * qkey_typed_to_record -- Return record from key; add to cache if not there
825  * report mode - returns NULL if failure
826  * returns addref'd record
827  *=============================================================*/
828 static RECORD
qkey_typed_to_record(CACHE cache,CNSTRING key,STRING tag)829 qkey_typed_to_record (CACHE cache, CNSTRING key, STRING tag)
830 {
831 	CACHEEL cel=0;
832 	RECORD rec=0;
833 
834 	ASSERT(cache);
835 	ASSERT(key);
836 	if (!(cel = key_to_cacheel(cache, key, tag, TRUE)))
837 		return NULL;
838 	rec = get_record_for_cel(cel);
839 	return rec;
840 }
841 /*======================================
842  * lock_cache -- Lock CACHEEL into direct cache
843  *====================================*/
844 void
lock_cache(CACHEEL cel)845 lock_cache (CACHEEL cel)
846 {
847 	ASSERT(cnode(cel)); /* must be in direct */
848 	++cclock(cel);
849 	ASSERT(cclock(cel)>0);
850 }
851 /*======================================
852  * lockrpt_cache -- Lock CACHEEL into direct cache (for report program)
853  *====================================*/
854 void
lockrpt_cache(CACHEEL cel)855 lockrpt_cache (CACHEEL cel)
856 {
857 	ASSERT(cnode(cel)); /* must be in direct */
858 	/* put real lock on to actually lock it */
859 	++cclock(cel);
860 	/* put report lock on, so we can free orphaned report locks */
861 	++ccrptlock(cel);
862 	ASSERT(cclock(cel)>0);
863 }
864 /*======================================
865  * cel_rptlocks -- return report lock count for specified cache element
866  *====================================*/
867 INT
cel_rptlocks(CACHEEL cel)868 cel_rptlocks (CACHEEL cel)
869 {
870 	return ccrptlock(cel);
871 }
872 /*======================================
873  * lock_record_in_cache -- Load record into cache & lock it
874  *====================================*/
875 void
lock_record_in_cache(RECORD rec)876 lock_record_in_cache (RECORD rec)
877 {
878 	NODE node=0;
879 	CACHEEL cel=0;
880 	ASSERT(rec);
881 	node = nztop(rec); /* force record to be loaded in cache */
882 	cel = nzcel(rec);
883 	++cclock(cel);
884 	ASSERT(cclock(cel) > 0);
885 }
886 /*==========================================
887  * unlock_cache -- Unlock CACHEEL from direct cache
888  *========================================*/
889 void
unlock_cache(CACHEEL cel)890 unlock_cache (CACHEEL cel)
891 {
892 	ASSERT(cclock(cel) > 0);
893 	ASSERT(cnode(cel));
894 	--cclock(cel);
895 }
896 /*==========================================
897  * unlockrpt_cache -- Unlock CACHEEL from direct cache (report program call)
898  *========================================*/
899 void
unlockrpt_cache(CACHEEL cel)900 unlockrpt_cache (CACHEEL cel)
901 {
902 	ASSERT(cclock(cel) > 0);
903 	ASSERT(ccrptlock(cel) > 0);
904 	ASSERT(cnode(cel));
905 	--cclock(cel);
906 	--ccrptlock(cel);
907 }
908 /*======================================
909  * unlock_record_from_cache -- Remove lock on record
910  *====================================*/
911 void
unlock_record_from_cache(RECORD rec)912 unlock_record_from_cache (RECORD rec)
913 {
914 	CACHEEL cel=0;
915 	ASSERT(rec);
916 	cel = nzcel(rec);
917 	ASSERT(cel);
918 	ASSERT(cclock(cel) > 0);
919 	--cclock(cel);
920 }
921 /*=========================================
922  * cache_get_lock_counts -- Fill in lock counts
923  * does not include report locks (but, this isn't
924  *  called during reports anyway)
925  *=======================================*/
926 static void
cache_get_lock_counts(CACHE ca,INT * locks)927 cache_get_lock_counts (CACHE ca, INT * locks)
928 {
929 	CACHEEL cel;
930 	for (cel = cacfirstdir(ca); cel; cel = cnext(cel)) {
931 		if (cclock(cel) && locks) ++(*locks);
932 	}
933 }
934 /*=========================================
935  * get_cache_stats -- Calculate cache stats
936  *  returns static buffer
937  *=======================================*/
938 static ZSTR
get_cache_stats(CACHE ca)939 get_cache_stats (CACHE ca)
940 {
941 	ZSTR zstr = zs_new();
942 	INT lo=0;
943 	cache_get_lock_counts(ca, &lo);
944 	zs_appf(zstr
945 		, "d:%d/%d (l:%d)"
946 		, cacsizedir(ca), cacmaxdir(ca), lo
947 		);
948 	return zstr;
949 }
950 /*=========================================
951  * get_cache_stats_indi -- Return indi cache stats
952  *=======================================*/
953 ZSTR
get_cache_stats_indi(void)954 get_cache_stats_indi (void)
955 {
956 	return get_cache_stats(indicache);
957 }
958 /*=========================================
959  * get_cache_stats_fam -- Return fam cache stats
960  *=======================================*/
961 ZSTR
get_cache_stats_fam(void)962 get_cache_stats_fam (void)
963 {
964 	return get_cache_stats(famcache);
965 }
966 /*============================================
967  * ensure_cel_has_record -- Make sure cache element has record
968  *  (node_to_cache, which creates cels, doesn't create records)
969  *==========================================*/
970 static void
ensure_cel_has_record(CACHEEL cel)971 ensure_cel_has_record (CACHEEL cel)
972 {
973 	RECORD rec = get_record_for_cel(cel); /* addref'd */
974 	release_record(rec);
975 }
976 /*============================================
977  * add_new_indi_to_cache -- Add person to person cache
978  *==========================================*/
979 void
add_new_indi_to_cache(RECORD rec)980 add_new_indi_to_cache (RECORD rec)
981 {
982 	CACHEEL cel=0;
983 	NODE root = is_record_temp(rec);
984 	cel = node_to_cache(indicache, root);
985 	ASSERT(!crecord(cel));
986 	record_set_cel(rec, cel);
987 	crecord(cel) = rec;
988 	addref_record(rec); /* cel holds reference */
989 }
990 /*===========================================
991  * fam_to_cache -- Add family to family cache
992  *=========================================*/
993 void
fam_to_cache(NODE node)994 fam_to_cache (NODE node)
995 {
996 	CACHEEL cel = node_to_cache(famcache, node);
997 	ensure_cel_has_record(cel);
998 }
999 /*==========================================
1000  * even_to_cache -- Add event to event cache
1001  *========================================*/
1002 void
even_to_cache(NODE node)1003 even_to_cache (NODE node)
1004 {
1005 	CACHEEL cel = node_to_cache(evencache, node);
1006 	ensure_cel_has_record(cel);
1007 }
1008 /*============================================
1009  * sour_to_cache -- Add source to source cache
1010  *==========================================*/
1011 void
sour_to_cache(NODE node)1012 sour_to_cache (NODE node)
1013 {
1014 	CACHEEL cel = node_to_cache(sourcache, node);
1015 	ensure_cel_has_record(cel);
1016 }
1017 /*===========================================
1018  * othr_to_cache -- Add other record to cache
1019  *=========================================*/
1020 void
othr_to_cache(NODE node)1021 othr_to_cache (NODE node)
1022 {
1023 	CACHEEL cel = node_to_cache(othrcache, node);
1024 	ensure_cel_has_record(cel);
1025 }
1026 /*========================================
1027  * node_to_cache -- Add node tree to cache
1028  *  This is a high-level entry point, which validates
1029  *  and delegates the work
1030  *  node tree must be valid, and of the correct type
1031  *  (INDI node trees may only be added to INDI cache, etc)
1032  *  This puts node into cache (first_direct)
1033  *======================================*/
1034 static CACHEEL
node_to_cache(CACHE cache,NODE top)1035 node_to_cache (CACHE cache, NODE top)
1036 {
1037 	STRING key=0;
1038 	CACHEEL cel=0;
1039 	ASSERT(cache);
1040 	ASSERT(top);
1041 	ASSERT(!nparent(top));	/* should be a root */
1042 	ASSERT(!nsibling(top)); /* should be a root */
1043 	if (nestr(cacname(cache), "OTHR")) {
1044 		/* only INDI records in INDI cache, etc */
1045 		if (!eqstr(cacname(cache), ntag(top))) {
1046 			crashlog(_("Bad cache entry <%s> != <%s>"), cacname(cache), ntag(top));
1047 			ASSERT(0);
1048 		}
1049 	}
1050 	key = node_to_key(top);
1051 	ASSERT(key);
1052 	/* ASSERT that record is not in cache */
1053 	/* We're not supposed to be called if record in cache */
1054 	ASSERT(!valueof_ptr(cacdata(cache), key));
1055 	cel = get_free_cacheel(cache);
1056 	put_node_in_cache(cache, cel, top, key);
1057 	return cel;
1058 }
1059 /*=======================================================
1060  * get_free_cacheel -- Remove and return entry from free list
1061  *=====================================================*/
1062 static CACHEEL
get_free_cacheel(CACHE cache)1063 get_free_cacheel (CACHE cache)
1064 {
1065 	CACHEEL cel=0, celnext=0;
1066 
1067 	/* If free list is empty, move least recently used entry to free list */
1068 	if (!cacfree(cache)) {
1069 		/* find least recently used by unlocked entry */
1070 		for (cel = caclastdir(cache); cel && cclock(cel); cel = cprev(cel)) {
1071 		}
1072 		if (!cel) {
1073 			crashlog(_("Cache [%s] overflowed its max size (%d)"), cacname(cache), cacmaxdir(cache));
1074 			ASSERT(0);
1075 		}
1076 		remove_from_cache(cache, ckey(cel));
1077 	}
1078 
1079 	cel = cacfree(cache);
1080 	ASSERT(cel);
1081 
1082 	/* remove entry from free list */
1083 	celnext = cnext(cel);
1084 	cacfree(cache) = celnext;
1085 	if (celnext)
1086 		cprev(celnext) = 0;
1087 	/* reinitialize entry */
1088 	init_cel(cel);
1089 
1090 	return cel;
1091 }
1092 /*=======================================================
1093  * set_all_nodetree_to_cel -- clear all the cel pointers in a node tree
1094  *=====================================================*/
1095 static void
set_all_nodetree_to_cel(NODE node,CACHEEL cel)1096 set_all_nodetree_to_cel (NODE node, CACHEEL cel)
1097 {
1098 	BOOLEAN travdone = FALSE;
1099 	/* Now set all nodes in tree to point to cache record */
1100 	while (!travdone) {
1101 		node->n_cel = cel;
1102 		/* go to bottom of tree */
1103 		while (nchild(node)) {
1104 			node = nchild(node);
1105 			node->n_cel = cel;
1106 		}
1107 		/* find next node in traversal/ascent */
1108 		while (!nsibling(node)) {
1109 			if (!nparent(node)) {
1110 				travdone=TRUE;
1111 				break;
1112 			}
1113 			node = nparent(node);
1114 		}
1115 		node = nsibling(node);
1116 	}
1117 }
1118 /*=======================================================
1119  * put_node_in_cache -- Low-level work of loading node into cacheel supplied
1120  *=====================================================*/
1121 static void
put_node_in_cache(CACHE cache,CACHEEL cel,NODE node,STRING key)1122 put_node_in_cache (CACHE cache, CACHEEL cel, NODE node, STRING key)
1123 {
1124 	BOOLEAN travdone = FALSE;
1125 	ASSERT(cache);
1126 	ASSERT(node);
1127 	ASSERT(cacsizedir(cache) < cacmaxdir(cache));
1128 	init_cel(cel);
1129 	insert_table_ptr(cacdata(cache), key, cel);
1130 	cnode(cel) = node;
1131 	ckey(cel) = strsave(key);
1132 	cclock(cel) = FALSE;
1133 	first_direct(cache, cel);
1134 	/* Now set all nodes in tree to point to cache record */
1135 	while (!travdone) {
1136 		node->n_cel = cel;
1137 		/* go to bottom of tree */
1138 		while (nchild(node)) {
1139 			node = nchild(node);
1140 			node->n_cel = cel;
1141 		}
1142 		/* find next node in traversal/ascent */
1143 		while (!nsibling(node)) {
1144 			if (!nparent(node)) {
1145 				travdone=TRUE;
1146 				break;
1147 			}
1148 			node = nparent(node);
1149 		}
1150 		node = nsibling(node);
1151 	}
1152 }
1153 /*==============================================
1154  * remove_indi_cache -- Remove person from cache
1155  *============================================*/
1156 void
remove_indi_cache(STRING key)1157 remove_indi_cache (STRING key)
1158 {
1159 	remove_from_cache(indicache, key);
1160 }
1161 /*=============================================
1162  * remove_fam_cache -- Remove family from cache
1163  *===========================================*/
1164 void
remove_fam_cache(STRING key)1165 remove_fam_cache (STRING key)
1166 {
1167 	remove_from_cache(famcache, key);
1168 }
1169 /*=============================================
1170  * remove_from_cache_by_key -- Remove record from cache
1171  *===========================================*/
1172 void
remove_from_cache_by_key(CNSTRING key)1173 remove_from_cache_by_key (CNSTRING key)
1174 {
1175 	switch(key[0]) {
1176 	case 'I': remove_from_cache(indicache, key); break;
1177 	case 'F': remove_from_cache(famcache, key); break;
1178 	case 'S': remove_from_cache(sourcache, key); break;
1179 	case 'E': remove_from_cache(evencache, key); break;
1180 	case 'X': remove_from_cache(othrcache, key); break;
1181 	default: ASSERT(0); break;
1182 	}
1183 }
1184 /*=============================================
1185  * remove_from_cache -- Move cache entry to free list
1186  *===========================================*/
1187 static void
remove_from_cache(CACHE cache,CNSTRING key)1188 remove_from_cache (CACHE cache, CNSTRING key)
1189 {
1190 	BOOLEAN delcache = FALSE;
1191 	CACHEEL cel=0;
1192 
1193 	if (!key || *key == 0 || !cache)
1194 		return;
1195 	/* If it has a key, it is in the cache */
1196 	cel = valueof_ptr(cacdata(cache), key);
1197 	remove_cel_from_cache(cache, cel, delcache);
1198 }
1199 /*=============================================
1200  * remove_from_cache -- Move cache entry to free list
1201  *  Requires non-null input
1202  *===========================================*/
1203 static void
remove_cel_from_cache(CACHE cache,CACHEEL cel,BOOLEAN delcache)1204 remove_cel_from_cache (CACHE cache, CACHEEL cel, BOOLEAN delcache)
1205 {
1206 	CACHEEL celnext=0;
1207 	STRING key = ckey(cel);
1208 
1209 	/* caller ensured cache && key are non-null */
1210 	ASSERT(cel);
1211 
1212 	if (cclock(cel)) {
1213 		/*
1214 		not supposed to remove locked elements!
1215 		Construct informational message and cause exception
1216 		*/
1217 		char msg[1024] = "";
1218 		NODE node = cnode(cel);
1219 		if (delcache) {
1220 			llstrapps(msg, sizeof(msg), uu8
1221 			, _("Locked cache element found when deleting cache"));
1222 		} else {
1223 			llstrapps(msg, sizeof(msg), uu8
1224 			, _("Locked cache element found when releasing cache element"));
1225 		}
1226 		if (node && nxref(node)) {
1227 			llstrappf(msg, sizeof(msg), uu8, _(" (node %s)"), nxref(node));
1228 		}
1229 		FATAL2(msg);
1230 	}
1231 	ASSERT(!cclock(cel));
1232 	ASSERT(cnode(cel));
1233 	remove_direct(cache, cel);
1234 
1235 	/* Clear all node tree info */
1236 	if (1) {
1237 		NODE node = cnode(cel);
1238 		if (node)
1239 			set_all_nodetree_to_cel(node, 0);
1240 		cnode(cel) = 0;
1241 		free_nodes(node);
1242 	}
1243 
1244 	celnext = cacfree(cache);
1245 	cnext(cel) = celnext;
1246 	if (celnext)
1247 		cprev(celnext) = cel;
1248 	cprev(cel) = 0;
1249 	ckey(cel) = 0;
1250 	if (crecord(cel)) {
1251 		/* cel holds the original reference to the record */
1252 		RECORD rec = crecord(cel);
1253 		record_remove_cel(rec, cel);
1254 		release_record(rec);
1255 		crecord(cel) = 0;
1256 	}
1257 	cacfree(cache) = cel;
1258 	delete_table_element(cacdata(cache), key);
1259 	stdfree(key); /* alloc'd when assigned to ckey(cel) */
1260 }
1261 /*================================================================
1262  * value_to_xref -- Converts a string to a record key, if possible
1263  *==============================================================*/
1264 STRING
value_to_xref(STRING val)1265 value_to_xref (STRING val)
1266 {
1267 	INT c;
1268 
1269 	if (!val || (*val != '@') || (strlen(val) < 4) ||
1270 		(val[strlen(val)-1] != '@')) return NULL;
1271 	val = rmvat(val);
1272 	if ((c = *val) != 'I' && c != 'F' && c != 'S' && c != 'E' &&
1273 		c != 'X') return NULL;
1274 	if (!isnumeric(val + 1)) return NULL;
1275 	return val;
1276 }
1277 /*===================================================
1278  * indi_to_cacheel -- Convert person to cache element
1279  *=================================================*/
1280 CACHEEL
indi_to_cacheel(RECORD indi)1281 indi_to_cacheel (RECORD indi)
1282 {
1283 	CACHEEL cel=0;
1284 	if (!indi || !nztop(indi)) return NULL;
1285 	cel = nzcel(indi);
1286 	if (cel) return cel;
1287 	/*
1288 	This is not efficient, rereading the record
1289 	But we can't just steal the record given us
1290 	without some transfer of memory ownership
1291 	*/
1292 	cel = key_to_indi_cacheel(rmvat(nxref(nztop(indi))));
1293 	ASSERT(cel);
1294 	return cel;
1295 }
1296 /*===================================================
1297  * indi_to_cacheel_old -- Convert person to cache element
1298  *  should be obsoleted by indi_to_cacheel
1299  *=================================================*/
1300 CACHEEL
indi_to_cacheel_old(NODE indi)1301 indi_to_cacheel_old (NODE indi)
1302 {
1303 	CACHEEL cel;
1304 	if (!indi) return NULL;
1305 #ifdef DEBUG
1306 	llwprintf("indi_to_cacheel_old: %s\n", nxref(indi));
1307 #endif
1308 	cel = key_to_indi_cacheel(rmvat(nxref(indi)));
1309 	ASSERT(cel);
1310 	return cel;
1311 }
1312 /*==================================================
1313  * fam_to_cacheel -- Convert family to cache element
1314  *================================================*/
1315 CACHEEL
fam_to_cacheel(RECORD frec)1316 fam_to_cacheel (RECORD frec)
1317 {
1318 	CACHEEL cel;
1319 	if (!frec) return NULL;
1320 	cel = key_to_fam_cacheel(rmvat(nxref(nztop(frec))));
1321 	ASSERT(cel);
1322 	return cel;
1323 }
1324 /*==================================================
1325  * fam_to_cacheel_old -- Convert family to cache element
1326  *================================================*/
1327 CACHEEL
fam_to_cacheel_old(NODE fam)1328 fam_to_cacheel_old (NODE fam)
1329 {
1330 	CACHEEL cel;
1331 	if (!fam) return NULL;
1332 	cel = key_to_fam_cacheel(rmvat(nxref(fam)));
1333 	ASSERT(cel);
1334 	return cel;
1335 }
1336 /*===================================================
1337  * sour_to_cacheel -- Convert source to cache element
1338  *=================================================*/
1339 CACHEEL
sour_to_cacheel(NODE node)1340 sour_to_cacheel (NODE node)
1341 {
1342 	CACHEEL cel;
1343 	if (!node) return NULL;
1344 	cel = key_to_sour_cacheel(rmvat(nxref(node)));
1345 	ASSERT(cel);
1346 	return cel;
1347 }
1348 /*==================================================
1349  * even_to_cacheel -- Convert event to cache element
1350  *================================================*/
1351 CACHEEL
even_to_cacheel(NODE even)1352 even_to_cacheel (NODE even)
1353 {
1354 	CACHEEL cel;
1355 	if (!even) return NULL;
1356 	cel = key_to_even_cacheel(rmvat(nxref(even)));
1357 	ASSERT(cel);
1358 	return cel;
1359 }
1360 /*==================================================
1361  * othr_to_cacheel -- Convert other to cache element
1362  *================================================*/
1363 CACHEEL
othr_to_cacheel(NODE othr)1364 othr_to_cacheel (NODE othr)
1365 {
1366 	CACHEEL cel;
1367 	if (!othr) return NULL;
1368 	cel = key_to_othr_cacheel(rmvat(nxref(othr)));
1369 	ASSERT(cel);
1370 	return cel;
1371 }
1372 /*==================================================
1373  * record_to_cacheel -- Convert any record to cache element
1374  *================================================*/
1375 CACHEEL
record_to_cacheel(RECORD rec)1376 record_to_cacheel (RECORD rec)
1377 {
1378 	STRING key = rmvat(nxref(nztop(rec)));
1379 	switch(key[0]) {
1380 	case 'I': return indi_to_cacheel(rec);
1381 	case 'F': return fam_to_cacheel(rec);
1382 	case 'S': return sour_to_cacheel(nztop(rec));
1383 	case 'E': return even_to_cacheel(nztop(rec));
1384 	case 'X': return othr_to_cacheel(nztop(rec));
1385 	}
1386 	ASSERT(0); return NULL;
1387 }
1388 /*==================================================
1389  * node_to_cacheel_old -- Convert any node to cache element
1390  *================================================*/
1391 CACHEEL
node_to_cacheel_old(NODE node)1392 node_to_cacheel_old (NODE node)
1393 {
1394 	STRING key = rmvat(nxref(node));
1395 	switch(key[0]) {
1396 	case 'I': return indi_to_cacheel_old(node);
1397 	case 'F': return fam_to_cacheel_old(node);
1398 	case 'S': return sour_to_cacheel(node);
1399 	case 'E': return even_to_cacheel(node);
1400 	case 'X': return othr_to_cacheel(node);
1401 	}
1402 	ASSERT(0); return NULL;
1403 }
1404 /*==============================================
1405  * key_of_record -- Return display key of record
1406  *  returns static buffer
1407  *============================================*/
1408 STRING
key_of_record(NODE node)1409 key_of_record (NODE node)
1410 {
1411 	NODE refn;
1412 	ASSERT(node);
1413 	refn = REFN(node);
1414 	if (refn && nval(refn)) {
1415 		return nval(refn);
1416 	}
1417 	return rmvat(nxref(node)) + 1;
1418 }
1419 /*==============================================
1420  * qkey_to_???_cacheel -- Convert key to cacheel
1421  *  (report mode - returns NULL if failure)
1422  *  5 symmetric versions
1423  *============================================*/
qkey_to_indi_cacheel(STRING key)1424 CACHEEL qkey_to_indi_cacheel (STRING key)
1425 {
1426 	return key_to_cacheel(indicache, key, "INDI", TRUE);
1427 }
qkey_to_fam_cacheel(STRING key)1428 CACHEEL qkey_to_fam_cacheel (STRING key)
1429 {
1430 	return key_to_cacheel(famcache, key, "FAM", TRUE);
1431 }
qkey_to_even_cacheel(STRING key)1432 CACHEEL qkey_to_even_cacheel (STRING key)
1433 {
1434 	return key_to_cacheel(evencache, key, "EVEN", TRUE);
1435 }
qkey_to_sour_cacheel(STRING key)1436 CACHEEL qkey_to_sour_cacheel (STRING key)
1437 {
1438 	return key_to_cacheel(sourcache, key, "SOUR", TRUE);
1439 }
qkey_to_othr_cacheel(STRING key)1440 CACHEEL qkey_to_othr_cacheel (STRING key)
1441 {
1442 	return key_to_cacheel(othrcache, key, NULL, TRUE);
1443 }
1444 /*==============================================
1445  * qkey_to_typed_cacheel -- Lookup/load key
1446  *============================================*/
1447 /* unused
1448 static CACHEEL
1449 qkey_to_typed_cacheel (STRING key)
1450 {
1451 	char ntype;
1452 	ASSERT(key);
1453 	ASSERT(key[0]);
1454 	ntype = key[0];
1455 	switch(ntype) {
1456 	case 'I': return qkey_to_indi_cacheel(key);
1457 	case 'F': return qkey_to_fam_cacheel(key);
1458 	case 'S': return qkey_to_even_cacheel(key);
1459 	case 'E': return qkey_to_sour_cacheel(key);
1460 	case 'X': return qkey_to_othr_cacheel(key);
1461 	}
1462 	ASSERT(0);
1463 }
1464 unused */
1465 /*==============================================
1466  * cacheel_to_key -- Return key of record inside of cache element
1467  *  handle NULL input
1468  *============================================*/
1469 CNSTRING
cacheel_to_key(CACHEEL cel)1470 cacheel_to_key (CACHEEL cel)
1471 {
1472 	CNSTRING key = cel ? ckey(cel) : 0;
1473 	return key;
1474 }
1475 /*==============================================
1476  * cacheel_to_node -- Return root node of record inside of cache element
1477  *  loads cache element if needed
1478  *  handle NULL input
1479  *============================================*/
1480 NODE
cacheel_to_node(CACHEEL cel)1481 cacheel_to_node (CACHEEL cel)
1482 {
1483 	if (!cel) return NULL;
1484 	if (!cnode(cel)) {
1485 		CACHEEL cel2 = key_to_indi_cacheel(ckey(cel));
1486 		ASSERT(cel2 == cel);
1487 		ASSERT(cnode(cel));
1488 	}
1489 	return cnode(cel);
1490 }
1491 /*==============================================
1492  * is_cel_loaded -- If cache element is loaded, return root
1493  *  handle NULL input
1494  *============================================*/
1495 NODE
is_cel_loaded(CACHEEL cel)1496 is_cel_loaded (CACHEEL cel)
1497 {
1498 	if (!cel) return NULL;
1499 	return cnode(cel);
1500 }
1501 /*==============================================
1502  * cel_remove_record -- Our record informing us it is destructing
1503  *  Requires non-null inputs
1504  *============================================*/
1505 void
cel_remove_record(CACHEEL cel,RECORD rec)1506 cel_remove_record (CACHEEL cel, RECORD rec)
1507 {
1508 	ASSERT(cel);
1509 	ASSERT(cel->c_magic == cel_magic);
1510 	ASSERT(rec);
1511 	if (crecord(cel) == rec) {
1512 		crecord(cel) = 0;
1513 	}
1514 }
1515 /*=======================================================
1516  * set_all_nodetree_to_root_cel -- Propagate cel from root to all descendants
1517  *=====================================================*/
1518 void
set_all_nodetree_to_root_cel(NODE root)1519 set_all_nodetree_to_root_cel (NODE root)
1520 {
1521 	set_all_nodetree_to_cel(root, root->n_cel);
1522 }
1523 /*=======================================================
1524  * free_all_rprtlocks_in_cache -- Remove any rptlocks on any
1525  *  elements in this cache, and return number removed
1526  *=====================================================*/
1527 static int
free_all_rprtlocks_in_cache(CACHE cache)1528 free_all_rprtlocks_in_cache (CACHE cache)
1529 {
1530 	INT ct=0;
1531 	CACHEEL cel=0;
1532 
1533 	for (cel = caclastdir(cache); cel; cel = cprev(cel)) {
1534 		if (ccrptlock(cel)) {
1535 			INT delta = ccrptlock(cel);
1536 			ccrptlock(cel) = 0;
1537 			ASSERT(cclock(cel) >= delta);
1538 			cclock(cel) -= delta;
1539 			++ct;
1540 		}
1541 	}
1542 	return ct;
1543 }
1544 /*=======================================================
1545  * free_all_rprtlocks -- Remove any rptlocks on any
1546  *  elements in all caches, and return number removed
1547  *=====================================================*/
1548 int
free_all_rprtlocks(void)1549 free_all_rprtlocks (void)
1550 {
1551 	int ct=0;
1552 	ct += free_all_rprtlocks_in_cache(indicache);
1553 	ct += free_all_rprtlocks_in_cache(famcache);
1554 	ct += free_all_rprtlocks_in_cache(sourcache);
1555 	ct += free_all_rprtlocks_in_cache(sourcache);
1556 	ct += free_all_rprtlocks_in_cache(othrcache);
1557 	return ct;
1558 }
1559