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