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  * xreffile.c -- Handle the xref file
26  * This module manages the lists of free record numbers
27  * For example, if I1, I2, I4, and I6 are in use
28  *  then irecs contains I3 and I5 (free record numbers)
29  * Copyright(c) 1991-94 by T.T. Wetmore IV; all rights reserved
30  * pre-SourceForge version information:
31  *   2.3.4 - 24 Jun 93    2.3.5 - 02 Sep 93
32  *   3.0.0 - 02 May 94    3.0.2 - 10 Nov 94
33  *===========================================================*/
34 
35 #include "sys_inc.h"
36 #include "llstdlib.h"
37 #include "btree.h"
38 #include "gedcom.h"
39 #include "gedcomi.h"
40 #include "table.h"
41 #include "translat.h"
42 
43 extern BTREE BTR;
44 
45 /*===================================================================
46  * First five words in xrefs file are number of INDI, FAM, EVEN, SOUR
47  *   and other keys in file; remaining words are keys, in respective
48  *   order, for the records; first in each group is next unused key;
49  *   rest are keys of deleted records
50  * nixrefs==1 means there are no deleted INDI keys
51  * nixrefs==2 means there is one deleted INDI key (ixrefs[1])
52  *=================================================================*/
53 /*
54  In memory, data is kept in a DELETESET
55  A brand new DELETE set has
56    n = 1 (0 deleted records)
57    recs[0] = 1 (recs[0] is always next available record above highest in use)
58    max = basic allocation unit (currently 64)
59    ctype varies (eg, 'I' for the INDI set)
60  If there are I1-I4 and I6 are live in the database, and I5 was deleted,
61  then the DELETE set has
62   n = 2 (1 deleted record)
63   recs[0] = 6 (next available record above highest in use)
64   recs[1] = 5 (I5 was deleted, so it is available)
65   max = allocation unit (still 64)
66   ctype as above (eg, 'I' for the INDI set)
67 */
68 
69 /*********************************************
70  * local types
71  *********************************************/
72 
73 typedef enum { DUPSOK, NODUPS } DUPS;
74 
75  /*====================================
76  * deleteset -- set of deleted records
77  *  NB: storage order is IFESX
78  *  whereas canonical order is IFSEX
79  *==================================*/
80 struct deleteset_s
81 {
82 	INT n; /* num keys + 1, ie, starts at 1 */
83 	INT * recs;
84 	INT max;
85 	char ctype;
86 };
87 typedef struct deleteset_s *DELETESET;
88 
89 
90 /*********************************************
91  * local function prototypes
92  *********************************************/
93 
94 /* alphabetical */
95 static BOOLEAN addixref_impl(INT key, DUPS dups);
96 static BOOLEAN addfxref_impl(INT key, DUPS dups);
97 static BOOLEAN addsxref_impl(INT key, DUPS dups);
98 static BOOLEAN addexref_impl(INT key, DUPS dups);
99 static BOOLEAN addxref_impl(CNSTRING key, DUPS dups);
100 static BOOLEAN addxxref_impl(INT key, DUPS dups);
101 static INT find_slot(INT keynum, DELETESET set);
102 static void freexref(DELETESET set);
103 static DELETESET get_deleteset_from_type(char ctype);
104 static STRING getxref(DELETESET set);
105 static void growxrefs(DELETESET set);
106 static STRING newxref(STRING xrefp, BOOLEAN flag, DELETESET set);
107 static INT num_set(DELETESET set);
108 static BOOLEAN parse_key(CNSTRING key, char * ktype, INT * kval);
109 static void readrecs(DELETESET set);
110 static BOOLEAN readxrefs(void);
111 static BOOLEAN xref_isvalid_impl(DELETESET set, INT keynum);
112 static INT xref_last(DELETESET set);
113 
114 /*********************************************
115  * local variables
116  *********************************************/
117 
118 /* INDI, FAM, EVEN, SOUR, other sets */
119 static struct deleteset_s irecs, frecs, srecs, erecs, xrecs;
120 
121 static FILE *xreffp=0;	/* open xref file pointer */
122 static BOOLEAN xrefReadonly = FALSE;
123 
124 static INT maxkeynum=-1; /* cache value of largest key extant (-1 means not sure) */
125 
126 /*********************************************
127  * local & exported function definitions
128  * body of module
129  *********************************************/
130 
131 /*====================================
132  * initdset -- Initialize a delete set
133  *==================================*/
134 static void
initdset(DELETESET set,char ctype)135 initdset (DELETESET set, char ctype)
136 {
137 	set->ctype = ctype;
138 	set->max = 0;
139 	set->n = 1;
140 	set->recs = 0;
141 }
142 /*===================================
143  * initdsets -- Initialize delete sets
144  *=================================*/
145 static void
initdsets(void)146 initdsets (void)
147 {
148 	initdset(&irecs, 'I');
149 	initdset(&frecs, 'F');
150 	initdset(&srecs, 'S');
151 	initdset(&erecs, 'E');
152 	initdset(&xrecs, 'X');
153 }
154 /*==============================
155  * initxref -- Create xrefs file
156  *============================*/
157 void
initxref(void)158 initxref (void)
159 {
160 	char scratch[100];
161 	INT i = 1, j;
162 	ASSERT(!xrefReadonly);
163 	initdsets();
164 	ASSERT(!xreffp);
165 	sprintf(scratch, "%s/xrefs", BTR->b_basedir);
166 	ASSERT(xreffp = fopen(scratch, LLWRITEBINARY));
167 	for (j = 0; j < 10; j++) {
168 		ASSERT(fwrite(&i, sizeof(INT), 1, xreffp) == 1);
169 	}
170 	fclose(xreffp); xreffp=0;
171 }
172 /*============================
173  * openxref -- Open xrefs file
174  *==========================*/
175 BOOLEAN
openxref(BOOLEAN readonly)176 openxref (BOOLEAN readonly)
177 {
178 	char scratch[100];
179 	STRING fmode;
180 
181 	initdsets();
182 	ASSERT(!xreffp);
183 	sprintf(scratch, "%s/xrefs", BTR->b_basedir);
184 	xrefReadonly = readonly;
185 	fmode = xrefReadonly ? LLREADBINARY : LLREADBINARYUPDATE;
186 	if (!(xreffp = fopen(scratch, fmode))) {
187 		return FALSE;
188 	}
189 	return readxrefs();
190 }
191 /*==============================
192  * closexref -- Close xrefs file
193  *  (Safe to call if not open)
194  *============================*/
195 void
closexref(void)196 closexref (void)
197 {
198 	if (xreffp) {
199 		fclose(xreffp); xreffp = 0;
200 	}
201 	freexref(&irecs);
202 	freexref(&frecs);
203 	freexref(&srecs);
204 	freexref(&erecs);
205 	freexref(&xrecs);
206 }
207 /*=========================================
208  * getxrefnum -- Return new keynum for type
209  *  from deleted list if available, or else
210  *  a new highnumber
211  *  generic for all 5 types
212  * Created: 2001/02/04, Perry Rapp
213  *=======================================*/
214 static INT
getxrefnum(DELETESET set)215 getxrefnum (DELETESET set)
216 {
217 	INT keynum;
218 	ASSERT(xreffp);
219 	ASSERT(set->n >= 1);
220 	if (set->n == 1) {
221 		/* no free records on list */
222 		/* take new high number & bump high number */
223 		keynum = set->recs[0]++;
224 	} else {
225 		/* take last free record on list */
226 		keynum = set->recs[set->n - 1];
227 		/* zero out the entry slipping off the top of the list */
228 		set->recs[set->n - 1] = 0;
229 		/* remove just-used entry from list */
230 		--(set->n);
231 	}
232 	ASSERT(writexrefs());
233 	maxkeynum=-1;
234 	return keynum;
235 }
236 /*=========================================
237  * getxref -- Return new key pointer for type
238  *  from deleted list if available, or else
239  *  a new highnumber
240  *  generic for all 5 types
241  *=======================================*/
242 static STRING
getxref(DELETESET set)243 getxref (DELETESET set)
244 {
245 	INT keynum = getxrefnum(set);
246 	static char scratch[12];
247 	sprintf(scratch, "@%c%ld@", set->ctype, keynum);
248 	return scratch;
249 }
250 /*===================================================
251  * get?xref -- Wrappers for each type to getxref (qv)
252  *  symmetric versions
253  *=================================================*/
getfxref(void)254 STRING getfxref (void) { return getxref(&frecs); }
getsxref(void)255 STRING getsxref (void) { return getxref(&srecs); }
getexref(void)256 STRING getexref (void) { return getxref(&erecs); }
getxxref(void)257 STRING getxxref (void) { return getxref(&xrecs); }
258 /*===================================================
259  * get?xrefnum -- Wrappers for each type to getxrefnum (qv)
260  * Created: 2001/02/04, Perry Rapp
261  *=================================================*/
getixrefnum(void)262 INT getixrefnum (void) { return getxrefnum(&irecs); }
263 /*======================================
264  * sortxref -- Sort xrefs after reading
265  *====================================*/
266 static void
sortxref(DELETESET set)267 sortxref (DELETESET set)
268 {
269 	/*
270 	TO DO - call qsort instead
271 	and also flag sorted file by changing file structure
272 	*/
273 	/*
274 	sort from high to low, so lowest at top of
275 	array, ready to be handed out
276 
277 	they should normally already be sorted,
278 	so use watchful bubble-sort for O(n)
279 	*/
280 	INT i,j, temp, ct;
281 	for (i=1; i<set->n; i++) {
282 		ct=0;
283 		for (j=i+1; j<set->n; j++) {
284 			if (set->recs[i] < set->recs[j]) {
285 				ct++;
286 				temp = set->recs[j];
287 				set->recs[j] = set->recs[i];
288 				set->recs[i] = temp;
289 			}
290 		}
291 		if (i==1 && !ct) return; /* already sorted */
292 	}
293 }
294 /*======================================
295  * sortxrefs -- Sort xrefs after reading
296  *====================================*/
297 static void
sortxrefs(void)298 sortxrefs (void)
299 {
300 	sortxref(&irecs);
301 	sortxref(&frecs);
302 	sortxref(&srecs);
303 	sortxref(&erecs);
304 	sortxref(&xrecs);
305 }
306 /*=============================
307  * readxrefs -- Read xrefs file
308  *  storage order: IFESX
309  *===========================*/
310 static BOOLEAN
readxrefs(void)311 readxrefs (void)
312 {
313 	ASSERT(xreffp);
314 	ASSERT(fread(&irecs.n, sizeof(INT), 1, xreffp) == 1);
315 	ASSERT(fread(&frecs.n, sizeof(INT), 1, xreffp) == 1);
316 	ASSERT(fread(&erecs.n, sizeof(INT), 1, xreffp) == 1);
317 	ASSERT(fread(&srecs.n, sizeof(INT), 1, xreffp) == 1);
318 	ASSERT(fread(&xrecs.n, sizeof(INT), 1, xreffp) == 1);
319 	ASSERT(irecs.n > 0);
320 	ASSERT(frecs.n > 0);
321 	ASSERT(erecs.n > 0);
322 	ASSERT(srecs.n > 0);
323 	ASSERT(xrecs.n > 0);
324 	if (irecs.n > irecs.max) growxrefs(&irecs);
325 	if (frecs.n > frecs.max) growxrefs(&frecs);
326 	if (srecs.n > srecs.max) growxrefs(&srecs);
327 	if (erecs.n > erecs.max) growxrefs(&erecs);
328 	if (xrecs.n > xrecs.max) growxrefs(&xrecs);
329 	readrecs(&irecs);
330 	readrecs(&frecs);
331 	readrecs(&erecs);
332 	readrecs(&srecs);
333 	readrecs(&xrecs);
334 	sortxrefs();
335 	return TRUE;
336 }
337 /*=========================================
338  * readrecs -- Read in one array of records
339  *=======================================*/
340 static void
readrecs(DELETESET set)341 readrecs (DELETESET set)
342 {
343 	ASSERT((INT)fread(set->recs, sizeof(INT), set->n, xreffp) == set->n);
344 }
345 /*================================
346  * writexrefs -- Write xrefs file.
347  *  storage order: IFESX
348  *==============================*/
349 BOOLEAN
writexrefs(void)350 writexrefs (void)
351 {
352 	ASSERT(!xrefReadonly);
353 	ASSERT(xreffp);
354 	rewind(xreffp);
355 	ASSERT(fwrite(&irecs.n, sizeof(INT), 1, xreffp) == 1);
356 	ASSERT(fwrite(&frecs.n, sizeof(INT), 1, xreffp) == 1);
357 	ASSERT(fwrite(&erecs.n, sizeof(INT), 1, xreffp) == 1);
358 	ASSERT(fwrite(&srecs.n, sizeof(INT), 1, xreffp) == 1);
359 	ASSERT(fwrite(&xrecs.n, sizeof(INT), 1, xreffp) == 1);
360 	ASSERT((INT)fwrite(irecs.recs, sizeof(INT), irecs.n, xreffp) == irecs.n);
361 	ASSERT((INT)fwrite(frecs.recs, sizeof(INT), frecs.n, xreffp) == frecs.n);
362 	ASSERT((INT)fwrite(erecs.recs, sizeof(INT), erecs.n, xreffp) == erecs.n);
363 	ASSERT((INT)fwrite(srecs.recs, sizeof(INT), srecs.n, xreffp) == srecs.n);
364 	ASSERT((INT)fwrite(xrecs.recs, sizeof(INT), xrecs.n, xreffp) == xrecs.n);
365 	fflush(xreffp);
366 	return TRUE;
367 }
368 /*=====================================
369  * find_slot -- Find slot at which to add key
370  *===================================*/
371 static INT
find_slot(INT keynum,DELETESET set)372 find_slot (INT keynum, DELETESET set)
373 {
374 	INT lo=1;
375 	INT hi=(set->n)-1;
376 	/* binary search to find where to insert key */
377 	while (lo<=hi) {
378 		INT md = (lo + hi)/2;
379 		if (keynum > (set->recs)[md])
380 			hi=--md;
381 		else if (keynum < (set->recs)[md])
382 			lo=++md;
383 		else {
384 			return md;
385 		}
386 	}
387 	return lo;
388 }
389 /*=====================================
390  * add_xref_to_set_impl -- Add deleted key to xrefs.
391  *  generic for all types
392  *===================================*/
393 static BOOLEAN
add_xref_to_set_impl(INT keynum,DELETESET set,DUPS dups)394 add_xref_to_set_impl (INT keynum, DELETESET set, DUPS dups)
395 {
396 	INT lo, i;
397 	if (keynum <= 0 || !xreffp || (set->n) < 1) {
398 		char msg[128];
399 		snprintf(msg, sizeof(msg)/sizeof(msg[0])
400 			, _("Corrupt DELETESET %c"), set->ctype);
401 		FATAL2(msg);
402 	}
403 	/* special case simplification if deleting last record */
404 	if (keynum+1 == set->recs[0]) {
405 		/*
406 		just bump the 'next free' indicator down to
407 		return this keynum next, and we don't even have to
408 		add this to the list
409 		*/
410 		--set->recs[0];
411 		ASSERT(writexrefs());
412 		return TRUE;
413 	}
414 	if (set->n >= set->max)
415 		growxrefs(set);
416 	ASSERT(set->n < set->max);
417 
418 	lo = find_slot(keynum, set);
419 	if (lo < set->n && (set->recs)[lo] == keynum) {
420 		/* key is already free */
421 		char msg[96];
422 		if (dups==DUPSOK)
423 			return FALSE;
424 		snprintf(msg, sizeof(msg)/sizeof(msg[0])
425 			, _("Tried to add already-deleted record (%ld) to xref (%c)!")
426 			, keynum, set->ctype);
427 		FATAL2(msg); /* deleting a deleted record! */
428 	}
429 	/* key replaces xrefs[lo] - push lo+ up */
430 	for (i=set->n-1; i>=lo; --i)
431 		(set->recs)[i+1] = (set->recs)[i];
432 	(set->recs)[lo] = keynum;
433 	(set->n)++;
434 	ASSERT(writexrefs());
435 	maxkeynum=-1;
436 	return TRUE;
437 }
438 /*===================================================
439  * add?xref_impl -- Wrappers for each type to add_xref_to_set (qv)
440  *  5 symmetric versions
441  *=================================================*/
addixref_impl(INT key,DUPS dups)442 static BOOLEAN addixref_impl (INT key, DUPS dups) { return add_xref_to_set_impl(key, &irecs, dups); }
addfxref_impl(INT key,DUPS dups)443 static BOOLEAN addfxref_impl (INT key, DUPS dups) { return add_xref_to_set_impl(key, &frecs, dups); }
addsxref_impl(INT key,DUPS dups)444 static BOOLEAN addsxref_impl (INT key, DUPS dups) { return add_xref_to_set_impl(key, &srecs, dups); }
addexref_impl(INT key,DUPS dups)445 static BOOLEAN addexref_impl (INT key, DUPS dups) { return add_xref_to_set_impl(key, &erecs, dups); }
addxxref_impl(INT key,DUPS dups)446 static BOOLEAN addxxref_impl (INT key, DUPS dups) { return add_xref_to_set_impl(key, &xrecs, dups); }
447 /*===================================================
448  * add?xref -- Wrappers for each type to add_xref_to_set (qv)
449  *  5 symmetric versions
450  *=================================================*/
addixref(INT key)451 void addixref (INT key) { addixref_impl(key, NODUPS); }
addfxref(INT key)452 void addfxref (INT key) { addfxref_impl(key, NODUPS); }
addsxref(INT key)453 void addsxref (INT key) { addsxref_impl(key, NODUPS); }
addexref(INT key)454 void addexref (INT key) { addexref_impl(key, NODUPS); }
addxxref(INT key)455 void addxxref (INT key) { addxxref_impl(key, NODUPS); }
456 /*===================================================
457  * addxref_impl -- Mark key free (accepts string key, any type)
458  *  key:    [IN]  key to delete (add to free set)
459  *  silent: [IN]  if FALSE, ASSERT if record is already free
460  *=================================================*/
461 static BOOLEAN
addxref_impl(CNSTRING key,DUPS dups)462 addxref_impl (CNSTRING key, DUPS dups)
463 {
464 	char ktype=0;
465 	INT keynum=0;
466 	if (!parse_key(key, &ktype, &keynum)) {
467 		char msg[512];
468 		snprintf(msg, sizeof(msg)/sizeof(msg[0]), "Bad key passed to addxref_impl: %s", key);
469 		FATAL2(msg);
470 	}
471 	switch(ktype) {
472 	case 'I': return addixref_impl(keynum, dups);
473 	case 'F': return addfxref_impl(keynum, dups);
474 	case 'S': return addsxref_impl(keynum, dups);
475 	case 'E': return addexref_impl(keynum, dups);
476 	case 'X': return addxxref_impl(keynum, dups);
477 	default: ASSERT(0); return FALSE;
478 	}
479 }
480 /*===================================================
481  * addxref -- Mark key free (accepts string key, any type)
482  * ASSERT if key is already free
483  *=================================================*/
addxref(CNSTRING key)484 void addxref (CNSTRING key)
485 {
486 	addxref_impl(key, NODUPS);
487 }
488 /*===================================================
489  * addxref_if_missing -- Mark key free (accepts string key, any type)
490  * Does nothing if key already free
491  *=================================================*/
addxref_if_missing(CNSTRING key)492 BOOLEAN addxref_if_missing (CNSTRING key)
493 {
494 	return addxref_impl(key, DUPSOK);
495 }
496 /*==========================================
497  * growxrefs -- Grow memory for xrefs array.
498  *  generic for all types
499  *========================================*/
500 static void
growxrefs(DELETESET set)501 growxrefs (DELETESET set)
502 {
503 	INT i, m = set->max, *newp;
504 	if (set->max == 0)
505 		set->max = 64;
506 	while (set->max <= set->n)
507 		set->max = set->max << 1;
508 	newp = (INT *) stdalloc((set->max)*sizeof(INT));
509 	if (m) {
510 		for (i = 0; i < set->n; i++)
511 			newp[i] = set->recs[i];
512 		stdfree(set->recs);
513 	}
514 	set->recs = newp;
515 }
516 /*==========================================
517  * get_deleteset_from_type -- Return deleteset
518  *  of type specified
519  *========================================*/
520 static DELETESET
get_deleteset_from_type(char ctype)521 get_deleteset_from_type (char ctype)
522 {
523 	switch(ctype) {
524 	case 'I': return &irecs;
525 	case 'F': return &frecs;
526 	case 'S': return &srecs;
527 	case 'E': return &erecs;
528 	case 'X': return &xrecs;
529 	}
530 	ASSERT(0); return 0;
531 }
532 /*==========================================
533  * delete_xref_if_present -- If record is listed
534  *  as free, remove it from the free list
535  *========================================*/
536 BOOLEAN
delete_xref_if_present(CNSTRING key)537 delete_xref_if_present (CNSTRING key)
538 {
539 	DELETESET set=0;
540 	INT keynum=0;
541 	INT lo=0;
542 	INT i=0;
543 
544 	ASSERT(key);
545 	ASSERT(key[0]);
546 	ASSERT(key[1]);
547 	set = get_deleteset_from_type(key[0]);
548 	keynum = atoi(key + 1);
549 	ASSERT(keynum>0);
550 	lo = find_slot(keynum, set);
551 	if (!(lo < set->n && (set->recs)[lo] == keynum))
552 		return FALSE;
553 	/* removing xrefs[lo] -- move lo+ down */
554 	for (i=lo; i+1<set->n-1; ++i)
555 		(set->recs)[i] = (set->recs)[i+1];
556 	/* zero out the entry slipping off the top of the list */
557 	if (set->n > 1)
558 		set->recs[set->n - 1] = 0;
559 	--(set->n);
560 	ASSERT(writexrefs());
561 	maxkeynum=-1;
562 	return TRUE;
563 
564 }
565 /*==========================================
566  * parse_key -- Get key type (first char) and numeric value
567  * parse_key("I44") => 'I', 44
568  *========================================*/
569 static BOOLEAN
parse_key(CNSTRING key,char * ktype,INT * kval)570 parse_key (CNSTRING key, char * ktype, INT * kval)
571 {
572 	if (!key || !key[0] || !key[1])
573 		return FALSE;
574 	/* convert "@I44@" to "I44" */
575 	if (key[0] == '@' && key[strlen(key)-1] == '@')
576 		key = rmvat(key);
577 	*ktype = key[0];
578 	*kval = atoi(key+1);
579 	return TRUE;
580 }
581 /*==========================================
582  * is_key_in_use -- Return TRUE if a live record
583  *========================================*/
584 BOOLEAN
is_key_in_use(CNSTRING key)585 is_key_in_use (CNSTRING key)
586 {
587 	DELETESET set=0;
588 	INT keynum=0;
589 	char ktype=0;
590 	CNSTRING barekey=0;
591 	BOOLEAN result=FALSE;
592 
593 	if (!parse_key(key, &ktype, &keynum)) {
594 		char msg[512];
595 		snprintf(msg, sizeof(msg)/sizeof(msg[0]), "Bad key passed to is_key_in_use: %s", key);
596 		FATAL2(msg);
597 	}
598 
599 	set = get_deleteset_from_type(ktype);
600 
601 	ASSERT(keynum>0);
602 
603 	result = xref_isvalid_impl(set, keynum);
604 
605 	strfree((STRING *)&barekey);
606 
607 	return result;
608 }
609 /*==========================================
610  * freexref -- Free memory & clear xrefs array
611  *  Called when database is closed
612  *========================================*/
613 static void
freexref(DELETESET set)614 freexref (DELETESET set)
615 {
616 	ASSERT(set);
617 	if (set->recs) {
618 		stdfree(set->recs);
619 		set->recs = 0;
620 		set->max = 0;
621 		set->n = 0;
622 	} else {
623 		ASSERT(set->max == 0);
624 		ASSERT(set->n == 0);
625 	}
626 }
627 /*==========================================================
628  * num_????s -- Return number of type of things in database.
629  *  5 symmetric versions
630  *========================================================*/
num_set(DELETESET set)631 static INT num_set (DELETESET set)
632 {
633 	ASSERT(set);
634 	return set->recs[0] - set->n;
635 }
num_indis(void)636 INT num_indis (void) { return num_set(&irecs); }
num_fams(void)637 INT num_fams (void) { return num_set(&frecs); }
num_sours(void)638 INT num_sours (void) { return num_set(&srecs); }
num_evens(void)639 INT num_evens (void) { return num_set(&erecs); }
num_othrs(void)640 INT num_othrs (void) { return num_set(&xrecs); }
641 /*========================================================
642  * max_????s -- Return max key number of object type in db
643  * 5 symmetric versions
644  *======================================================*/
max_set(DELETESET set)645 static INT max_set (DELETESET set)
646 {
647 	return set->recs[0];
648 }
xref_max_indis(void)649 INT xref_max_indis (void) { return max_set(&irecs); }
xref_max_fams(void)650 INT xref_max_fams (void) { return max_set(&frecs); }
xref_max_sours(void)651 INT xref_max_sours (void) { return max_set(&srecs); }
xref_max_evens(void)652 INT xref_max_evens (void) { return max_set(&erecs); }
xref_max_othrs(void)653 INT xref_max_othrs (void) { return max_set(&xrecs); }
654 /*======================================================
655  * xref_max_any -- Return largest key number of any type
656  *====================================================*/
657 INT
xref_max_any(void)658 xref_max_any (void)
659 {
660 	if (maxkeynum>=0)
661 		return maxkeynum;
662 	if (xref_max_indis() > maxkeynum)
663 		maxkeynum = xref_max_indis();
664 	if (xref_max_fams() > maxkeynum)
665 		maxkeynum = xref_max_fams();
666 	if (xref_max_sours() > maxkeynum)
667 		maxkeynum = xref_max_sours();
668 	if (xref_max_evens() > maxkeynum)
669 		maxkeynum = xref_max_evens();
670 	if (xref_max_othrs() > maxkeynum)
671 		maxkeynum = xref_max_othrs();
672 	return maxkeynum;
673 }
674 /*================================================
675  * newxref -- Return original or next xref value
676  * xrefp = key of the individual
677  * flag = use the current key
678  *  returns static buffer
679  *==============================================*/
680 static STRING
newxref(STRING xrefp,BOOLEAN flag,DELETESET set)681 newxref (STRING xrefp, BOOLEAN flag, DELETESET set)
682 {
683 	INT keynum;
684 	BOOLEAN changed;
685 	static char scratch[12];
686 	if(flag) {
687 		keynum = atoi(xrefp+1);
688 		changed = ((set->n != 1) || (keynum >= set->recs[0]));
689 		if(set->n != 1)
690 			set->n = 1;	/* forget about deleted entries */
691 		if(keynum >= set->recs[0])
692 			set->recs[0] = keynum+1;	/* next available */
693 		if(changed)
694 			ASSERT(writexrefs());
695 		sprintf(scratch, "@%s@", xrefp);
696 		return(scratch);
697 	}
698 	return(getxref(set));
699 }
700 /*================================================
701  * new?xref -- Return original or next ?xref value
702  * xrefp = key of the record
703  * flag = use the current key
704  *==============================================*/
705 STRING
newixref(STRING xrefp,BOOLEAN flag)706 newixref (STRING xrefp, BOOLEAN flag)
707 {
708 	return newxref(xrefp, flag, &irecs);
709 }
710 STRING
newfxref(STRING xrefp,BOOLEAN flag)711 newfxref (STRING xrefp, BOOLEAN flag)
712 {
713 	return newxref(xrefp, flag, &frecs);
714 }
715 STRING
newsxref(STRING xrefp,BOOLEAN flag)716 newsxref (STRING xrefp, BOOLEAN flag)
717 {
718 	return newxref(xrefp, flag, &srecs);
719 }
720 STRING
newexref(STRING xrefp,BOOLEAN flag)721 newexref (STRING xrefp, BOOLEAN flag)
722 {
723 	return newxref(xrefp, flag, &erecs);
724 }
725 STRING
newxxref(STRING xrefp,BOOLEAN flag)726 newxxref (STRING xrefp, BOOLEAN flag)
727 {
728 	return newxref(xrefp, flag, &xrecs);
729 }
730 /*================================================
731  * xref_isvalid_impl -- is this a valid whatever ?
732  *  generic for all 5 types
733  * (internal use)
734  *==============================================*/
735 static BOOLEAN
xref_isvalid_impl(DELETESET set,INT keynum)736 xref_isvalid_impl (DELETESET set, INT keynum)
737 {
738 	INT lo,hi,md;
739 	if (set->n == set->recs[0]) return FALSE; /* no valids */
740 	if (set->n == 1) return TRUE; /* all valid */
741 	/* binary search deleteds */
742 	lo=1;
743 	hi=(set->n)-1;
744 	while (lo<=hi) {
745 		md = (lo + hi)/2;
746 		if (keynum>(set->recs)[md])
747 			hi=--md;
748 		else if (keynum<(set->recs)[md])
749 			lo=++md;
750 		else
751 			return FALSE;
752 	}
753 	return TRUE;
754 }
755 /*=========================================================
756  * xref_next_impl -- Return next valid of some type after i
757  *  returns 0 if none found
758  *  generic for all 5 types
759  *  this could be more efficient (after first one work
760  *  thru tree)
761  *=======================================================*/
762 static INT
xref_next_impl(DELETESET set,INT i)763 xref_next_impl (DELETESET set, INT i)
764 {
765 	if (set->n == set->recs[0]) return 0; /* no valids */
766 	while (++i < set->recs[0])
767 	{
768 		if (xref_isvalid_impl(set, i)) return i;
769 	}
770 	return 0;
771 }
772 /*==========================================================
773  * xref_prev_impl -- Return prev valid of some type before i
774  *  returns 0 if none found
775  *  generic for all 5 types
776  *========================================================*/
777 static INT
xref_prev_impl(DELETESET set,INT i)778 xref_prev_impl (DELETESET set, INT i)
779 {
780 	if (set->n == set->recs[0]) return 0; /* no valids */
781 	while (--i)
782 	{
783 		if (xref_isvalid_impl(set, i)) return i;
784 	}
785 	return 0;
786 }
787 /*===============================================
788  * xref_next? -- Return next valid indi/? after i
789  *  returns 0 if none found
790  *  5 symmetric versions
791  *=============================================*/
xref_nexti(INT i)792 INT xref_nexti (INT i) { return xref_next_impl(&irecs, i); }
xref_nextf(INT i)793 INT xref_nextf (INT i) { return xref_next_impl(&frecs, i); }
xref_nexts(INT i)794 INT xref_nexts (INT i) { return xref_next_impl(&srecs, i); }
xref_nexte(INT i)795 INT xref_nexte (INT i) { return xref_next_impl(&erecs, i); }
xref_nextx(INT i)796 INT xref_nextx (INT i) { return xref_next_impl(&xrecs, i); }
xref_next(char ntype,INT i)797 INT xref_next (char ntype, INT i)
798 {
799 	switch(ntype) {
800 	case 'I': return xref_nexti(i);
801 	case 'F': return xref_nextf(i);
802 	case 'S': return xref_nexts(i);
803 	case 'E': return xref_nexte(i);
804 	case 'X': return xref_nextx(i);
805 	}
806 	ASSERT(0); return 0;
807 }
808 /*================================================
809  * xref_prev? -- Return prev valid indi/? before i
810  *  returns 0 if none found
811  *  5 symmetric versions
812  *==============================================*/
xref_previ(INT i)813 INT xref_previ (INT i) { return xref_prev_impl(&irecs, i); }
xref_prevf(INT i)814 INT xref_prevf (INT i) { return xref_prev_impl(&frecs, i); }
xref_prevs(INT i)815 INT xref_prevs (INT i) { return xref_prev_impl(&srecs, i); }
xref_preve(INT i)816 INT xref_preve (INT i) { return xref_prev_impl(&erecs, i); }
xref_prevx(INT i)817 INT xref_prevx (INT i) { return xref_prev_impl(&xrecs, i); }
xref_prev(char ntype,INT i)818 INT xref_prev (char ntype, INT i)
819 {
820 	switch(ntype) {
821 	case 'I': return xref_previ(i);
822 	case 'F': return xref_prevf(i);
823 	case 'S': return xref_prevs(i);
824 	case 'E': return xref_preve(i);
825 	case 'X': return xref_prevx(i);
826 	}
827 	ASSERT(0); return 0;
828 }
829 /*=========================================
830  * xref_first? -- Return first valid indi/?
831  *  returns 0 if none found
832  *  5 symmetric versions
833  *=======================================*/
xref_firsti(void)834 INT xref_firsti (void) { return xref_nexti(0); }
xref_firstf(void)835 INT xref_firstf (void) { return xref_nextf(0); }
xref_firsts(void)836 INT xref_firsts (void) { return xref_nexts(0); }
xref_firste(void)837 INT xref_firste (void) { return xref_nexte(0); }
xref_firstx(void)838 INT xref_firstx (void) { return xref_nextx(0); }
839 /*=======================================
840  * xref_last? -- Return last valid indi/?
841  *  returns 0 if none found
842  *  5 symmetric versions
843  *=====================================*/
xref_last(DELETESET set)844 static INT xref_last (DELETESET set)
845 {
846 	return xref_prev_impl(set, set->recs[0]);
847 }
xref_lasti(void)848 INT xref_lasti (void) { return xref_last(&irecs); }
xref_lastf(void)849 INT xref_lastf (void) { return xref_last(&frecs); }
xref_lasts(void)850 INT xref_lasts (void) { return xref_last(&srecs); }
xref_laste(void)851 INT xref_laste (void) { return xref_last(&erecs); }
xref_lastx(void)852 INT xref_lastx (void) { return xref_last(&xrecs); }
853 /*=======================================
854  * xrefs_get_counts_from_unopened_db --
855  *  read record counts out of file on disk
856  * returns FALSE if specified path is not the root of a traditional lifelines database
857  * If db structure error, errptr points to description of error (in static buffer)
858  *=====================================*/
859 BOOLEAN
xrefs_get_counts_from_unopened_db(CNSTRING path,INT * nindis,INT * nfams,INT * nsours,INT * nevens,INT * nothrs,char ** errptr)860 xrefs_get_counts_from_unopened_db (CNSTRING path, INT *nindis, INT *nfams
861 	, INT *nsours, INT *nevens, INT *nothrs, char ** errptr)
862 {
863 	char scratch[100];
864 	static char errstr[256];
865 	FILE * fp = 0;
866 	INT i;
867 	INT ndels[5], nmax[5];
868 
869 	*errptr = 0;
870 
871 	ASSERT(!xreffp);
872 	sprintf(scratch, "%s/xrefs", path);
873 	if (!(fp = fopen(scratch, LLREADBINARY))) {
874 		return FALSE;
875 	}
876 	for (i=0; i<5; ++i) {
877 		if (fread(&ndels[i], sizeof(INT), 1, fp) != 1) {
878 			snprintf(errstr, sizeof(errstr), "ndels[%ld] bad", i);
879 			*errptr = errstr;
880 			fclose(fp);
881 			return FALSE;
882 		}
883 	}
884 	for (i=0; i<5; ++i) {
885 		INT j;
886 		for (j=0; j<ndels[i]; ++j) {
887 			INT k;
888 			if (fread(&k, sizeof(INT), 1, fp) != 1) {
889 				snprintf(errstr, sizeof(errstr), "ndels[%ld]#%ld bad", i, j);
890 				*errptr = errstr;
891 				fclose(fp);
892 				return FALSE;
893 			}
894 			if (!j)
895 				nmax[i] = k;
896 		}
897 	}
898 	*nindis = nmax[0] - ndels[0];
899 	*nfams = nmax[1] - ndels[1];
900 	*nevens = nmax[2] - ndels[2];
901 	*nsours = nmax[3] - ndels[3];
902 	*nothrs = nmax[4] - ndels[4];
903 	fclose(fp);
904 	return TRUE;
905 }
906