1 /*
2    Copyright (c) 1991-1999 Thomas T. Wetmore IV
3    "The MIT license"
4    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 */
8 /* modified 05 Jan 2000 by Paul B. McBride (pmcbride@tiac.net) */
9 /*=============================================================
10  * node.c -- Standard GEDCOM NODE operations
11  * Copyright(c) 1992-96 by T.T. Wetmore IV; all rights reserved
12  *   2.3.4 - 24 Jun 93    2.3.5 - 04 Sep 93
13  *   3.0.0 - 29 Aug 94    3.0.2 - 23 Dec 94
14  *   3.0.3 - 16 Jan 96
15  *===========================================================*/
16 
17 #include "llstdlib.h"
18 #include "table.h"
19 #include "translat.h"
20 #include "gedcom.h"
21 #include "gedcomi.h"
22 #include "feedback.h"
23 #include "metadata.h"
24 #include "lloptions.h"
25 #include "date.h"
26 #include "vtable.h"
27 
28 /*********************************************
29  * global/exported variables
30  *********************************************/
31 
32 
33 /*********************************************
34  * external/imported variables
35  *********************************************/
36 
37 extern STRING qSfileof, qSreremp, qSrerlng, qSrernlv, qSrerinc;
38 extern STRING qSrerbln, qSrernwt, qSrerilv, qSrerwlv, qSunsupunix, qSunsupuniv;
39 
40 /*********************************************
41  * local types
42  *********************************************/
43 
44 /* node allocator's freelist */
45 typedef struct blck *NDALLOC;
46 struct blck { NDALLOC next; };
47 
48 /*********************************************
49  * local enums & defines
50  *********************************************/
51 
52 enum { NEW_RECORD, EXISTING_LACKING_WH_RECORD };
53 
54 /*********************************************
55  * local function prototypes, alphabetical
56  *********************************************/
57 
58 static NODE alloc_node(void);
59 static STRING fixup(STRING str);
60 static STRING fixtag (STRING tag);
61 static RECORD indi_to_prev_sib_impl(NODE indi);
62 static void node_destructor(VTABLE *obj);
63 static INT node_strlen(INT levl, NODE node);
64 
65 /*********************************************
66  * unused local function prototypes
67  *********************************************/
68 
69 #ifdef UNUSED_CODE
70 static BOOLEAN all_digits (STRING);
71 NODE children_nodes(NODE faml);
72 NODE father_nodes(NODE faml);
73 NODE mother_nodes(NODE faml);
74 NODE parents_nodes(NODE faml);
75 #endif /* UNUSED_CODE */
76 
77 /*********************************************
78  * local variables
79  *********************************************/
80 
81 /* node allocator's free list */
82 static NDALLOC first_blck = (NDALLOC) 0;
83 static int live_count = 0;
84 
85 static struct tag_vtable vtable_for_node = {
86 	VTABLE_MAGIC
87 	, "node"
88 	, &node_destructor
89 	, &refcountable_isref
90 	, &refcountable_addref
91 	, &refcountable_release
92 	, 0 /* copy_fnc */
93 	, &generic_get_type_name
94 };
95 
96 /*********************************************
97  * local function definitions
98  * body of module
99  *********************************************/
100 
101 /*==============================
102  * fixup -- Save non-tag strings
103  *============================*/
104 static STRING
fixup(STRING str)105 fixup (STRING str)
106 {
107 	if (!str || *str == 0) return NULL;
108 	return strsave(str);
109 }
110 /*=============================
111  * fixtag -- Keep tags in table
112  * returns pointer to table's memory
113  *===========================*/
114 static STRING
fixtag(STRING tag)115 fixtag (STRING tag)
116 {
117 	STRING str = valueof_str(tagtable, tag);
118 	if (!str) {
119 		insert_table_str(tagtable, tag, tag);
120 		str = valueof_str(tagtable, tag);
121 	}
122 	return str;
123 }
124 /*=====================================
125  * change_node_tag -- Give new tag to node
126  *===================================*/
127 void
change_node_tag(NODE node,STRING newtag)128 change_node_tag (NODE node, STRING newtag)
129 {
130 	/* tag belongs to tagtable, so don't free old one */
131 	ntag(node) = fixtag(newtag);
132 }
133 /*=====================================
134  * alloc_node -- Special node allocator
135  *===================================*/
136 static NODE
alloc_node(void)137 alloc_node (void)
138 {
139 	NODE node;
140 	NDALLOC blck;
141 	int i;
142 	if (first_blck == (NDALLOC) 0) {
143 		node = (NODE) stdalloc(100*sizeof(*node));
144 		first_blck = (NDALLOC) node;
145 		for (i = 1; i <= 99; i++) {
146 			blck = (NDALLOC) node;
147 			blck->next = (NDALLOC) (node + 1);
148 			node++;
149 		}
150 		((NDALLOC) node)->next = (NDALLOC) 0;
151 	}
152 	node = (NODE) first_blck;
153 	first_blck = first_blck->next;
154 	++live_count;
155 	return node;
156 }
157 /*======================================
158  * free_node -- Special node deallocator
159  *====================================*/
160 void
free_node(NODE node)161 free_node (NODE node)
162 {
163 	if (nxref(node)) stdfree(nxref(node));
164 	if (nval(node)) stdfree(nval(node));
165 
166 	/*
167 	tag is pointer into shared tagtable
168 	which we cannot delete until all nodes are freed
169 	*/
170 	((NDALLOC) node)->next = first_blck;
171 	first_blck = (NDALLOC) node;
172 	--live_count;
173 }
174 /*===========================
175  * create_node -- Create NODE
176  *
177  * STRING xref  [in] xref
178  * STRING tag   [in] tag
179  * STRING val:  [in] value
180  * NODE prnt:   [in] parent
181  *=========================*/
182 NODE
create_node(STRING xref,STRING tag,STRING val,NODE prnt)183 create_node (STRING xref, STRING tag, STRING val, NODE prnt)
184 {
185 	NODE node = alloc_node();
186 	memset(node, 0, sizeof(*node));
187 	nxref(node) = fixup(xref);
188 	ntag(node) = fixtag(tag);
189 	nval(node) = fixup(val);
190 	nparent(node) = prnt;
191 	if (prnt)
192 		node->n_cel = prnt->n_cel;
193 	return node;
194 }
195 /*===========================
196  * create_temp_node -- Create NODE for temporary use
197  *  (not to be connected to a record)
198  * [All arguments are duplicated, so caller doesn't have to]
199  * STRING xref  [in] xref
200  * STRING tag   [in] tag
201  * STRING val:  [in] value
202  * NODE prnt:   [in] parent
203  * Created: 2003-02-01 (Perry Rapp)
204  *=========================*/
205 NODE
create_temp_node(STRING xref,STRING tag,STRING val,NODE prnt)206 create_temp_node (STRING xref, STRING tag, STRING val, NODE prnt)
207 {
208 	NODE node = create_node(xref, tag, val, prnt);
209 	nflag(node) = ND_TEMP;
210 	return node;
211 }
212 /*===========================
213  * free_temp_node_tree -- Free a node created by create_temp_node
214  * Created: 2003-02-01 (Perry Rapp)
215  *=========================*/
216 void
free_temp_node_tree(NODE node)217 free_temp_node_tree (NODE node)
218 {
219 	NODE n2;
220 	if ((n2 = nchild(node))) {
221 		free_temp_node_tree(n2);
222 		nchild(node) = 0;
223 	}
224 	if ((n2 = nsibling(node))) {
225 		free_temp_node_tree(n2);
226 		nsibling(node) = 0;
227 	}
228 	free_node(node);
229 }
230 /*===================================
231  * is_temp_node -- Return whether node is a temp
232  * Created: 2003-02-04 (Perry Rapp)
233  *=================================*/
234 BOOLEAN
is_temp_node(NODE node)235 is_temp_node (NODE node)
236 {
237 	return !!(nflag(node) & ND_TEMP);
238 }
239 /*===================================
240  * set_temp_node -- make node temp (or not)
241  * Created: 2003-02-04 (Perry Rapp)
242  *=================================*/
243 void
set_temp_node(NODE node,BOOLEAN temp)244 set_temp_node (NODE node, BOOLEAN temp)
245 {
246 	if (is_temp_node(node) ^ temp)
247 		nflag(node) ^= ND_TEMP;
248 }
249 /*=====================================
250  * free_nodes -- Free all NODEs in tree
251  *===================================*/
252 void
free_nodes(NODE node)253 free_nodes (NODE node)
254 {
255 	NODE sib;
256 	while (node) {
257 		if (nchild(node)) free_nodes(nchild(node));
258 		sib = nsibling(node);
259 		free_node(node);
260 		node = sib;
261 	}
262 }
263 /*==============================================================
264  * tree_strlen -- Compute string length of tree -- don't count 0
265  *============================================================*/
266 INT
tree_strlen(INT levl,NODE node)267 tree_strlen (INT levl,       /* level */
268              NODE node)      /* root */
269 {
270 	INT len = 0;
271 	while (node) {
272 		len += node_strlen(levl, node);
273 		if (nchild(node))
274 			len += tree_strlen(levl + 1, nchild(node));
275 		node = nsibling(node);
276 	}
277 	return len;
278 }
279 /*================================================================
280  * node_strlen -- Compute NODE string length -- count \n but not 0
281  *==============================================================*/
282 static INT
node_strlen(INT levl,NODE node)283 node_strlen (INT levl,       /* level */
284              NODE node)      /* node */
285 {
286 	INT len;
287 	char scratch[10];
288 	sprintf(scratch, "%ld", levl);
289 	len = strlen(scratch) + 1;
290 	if (nxref(node)) len += strlen(nxref(node)) + 1;
291 	len += strlen(ntag(node));
292 	if (nval(node)) len += strlen(nval(node)) + 1;
293 	return len + 1;
294 }
295 /*==========================================
296  * unknown_node_to_dbase -- Store node of unknown type
297  *  in database
298  * Created: 2001/04/06, Perry Rapp
299  *========================================*/
300 void
unknown_node_to_dbase(NODE node)301 unknown_node_to_dbase (NODE node)
302 {
303 	/* skip tag validation */
304 	node_to_dbase(node, NULL);
305 }
306 /*==========================================
307  * indi_to_dbase -- Store person in database
308  *========================================*/
309 void
indi_to_dbase(NODE node)310 indi_to_dbase (NODE node)
311 {
312 	/*
313 	To start storing metadata, we need the RECORD here
314 	If we were using RECORD everywhere, we'd pass it in here
315 	We could look it up in the cache, but it might not be there
316 	(new records)
317 	(and this applies to fam_to_dbase, sour_to_dbase, etc)
318 	Perry, 2001/01/15
319 	*/
320 	node_to_dbase(node, "INDI");
321 }
322 /*=========================================
323  * fam_to_dbase -- Store family in database
324  *=======================================*/
325 void
fam_to_dbase(NODE node)326 fam_to_dbase (NODE node)
327 {
328 	node_to_dbase(node, "FAM");
329 }
330 /*=========================================
331  * even_to_dbase -- Store event in database
332  *=======================================*/
333 void
even_to_dbase(NODE node)334 even_to_dbase (NODE node)
335 {
336 	node_to_dbase(node, "EVEN");
337 }
338 /*==========================================
339  * sour_to_dbase -- Store source in database
340  *========================================*/
341 void
sour_to_dbase(NODE node)342 sour_to_dbase (NODE node)
343 {
344 	node_to_dbase(node, "SOUR");
345 }
346 /*================================================
347  * othr_to_dbase -- Store other record in database
348  *==============================================*/
349 void
othr_to_dbase(NODE node)350 othr_to_dbase (NODE node)
351 {
352 	node_to_dbase(node, NULL);
353 }
354 /*===============================================
355  * node_to_dbase -- Store GEDCOM tree in database
356  *=============================================*/
357 void
node_to_dbase(NODE node,STRING tag)358 node_to_dbase (NODE node,
359                STRING tag)
360 {
361 	STRING str;
362 	ASSERT(node);
363 	if (tag) { ASSERT(eqstr(tag, ntag(node))); }
364 	str = node_to_string(node);
365 	ASSERT(store_record(rmvat(nxref(node)), str, strlen(str)));
366 	stdfree(str);
367 }
368 /*==================================================
369  * indi_to_famc -- Return family-as-child for person
370  *================================================*/
371 NODE
indi_to_famc(NODE node)372 indi_to_famc (NODE node)
373 {
374 	if (!node) return NULL;
375 	if (!(node = find_tag(nchild(node), "FAMC"))) return NULL;
376 	return key_to_fam(rmvat(nval(node)));
377 }
378 /*========================================
379  * fam_to_husb_node -- Return husband of family
380  *======================================*/
381 NODE
fam_to_husb_node(NODE node)382 fam_to_husb_node (NODE node)
383 {
384 	CNSTRING key=0;
385 	if (!node) return NULL;
386 	if (!(node = find_tag(nchild(node), "HUSB"))) return NULL;
387 	key = rmvat(nval(node));
388 	if (!key) return NULL;
389 	return qkey_to_indi(key);
390 }
391 /*========================================
392  * fam_to_husb -- Get husband of family
393  *  returns 1 if found, 0 if not found, -1 if bad pointer
394  *======================================*/
395 INT
fam_to_husb(RECORD frec,RECORD * prec)396 fam_to_husb (RECORD frec, RECORD * prec)
397 {
398 	NODE fam = nztop(frec), husb;
399 	CNSTRING key=0;
400 	*prec = NULL;
401 	if (!fam) return 0;
402 	if (!(husb = find_tag(nchild(fam), "HUSB"))) return 0;
403 	key = rmvat(nval(husb));
404 	if (!key) return -1;
405 	*prec = key_to_irecord(key); /* ASSERT if fail */
406 	return 1;
407 }
408 /*=====================================
409  * fam_to_wife_node -- Return wife of family
410  *===================================*/
411 NODE
fam_to_wife_node(NODE node)412 fam_to_wife_node (NODE node)
413 {
414 	CNSTRING key=0;
415 	if (!node) return NULL;
416 	if (!(node = find_tag(nchild(node), "WIFE"))) return NULL;
417 	key = rmvat(nval(node));
418 	if (!key) return NULL;
419 	return qkey_to_indi(key);
420 }
421 /*========================================
422  * fam_to_wife -- Return husband of family
423  *  returns 1 if found, 0 if not found, -1 if bad pointer
424  *======================================*/
425 INT
fam_to_wife(RECORD frec,RECORD * prec)426 fam_to_wife (RECORD frec, RECORD * prec)
427 {
428 	NODE fam = nztop(frec), husb;
429 	CNSTRING key=0;
430 	*prec = NULL;
431 	if (!fam) return 0;
432 	if (!(husb = find_tag(nchild(fam), "WIFE"))) return 0;
433 	key = rmvat(nval(husb));
434 	if (!key) return -1;
435 	*prec = key_to_irecord(key); /* ASSERT if fail */
436 	return 1;
437 }
438 /*===============================================
439  * fam_to_spouse -- Return other spouse of family
440  *=============================================*/
441 NODE
fam_to_spouse(NODE fam,NODE indi)442 fam_to_spouse (NODE fam, NODE indi)
443 {
444 	INT num;
445 	if (!fam) return NULL;
446 	FORHUSBS(fam, husb, num)
447 		if(husb != indi) return(husb);
448 	ENDHUSBS
449 	FORWIFES(fam, wife, num)
450 	  if(wife != indi) return(wife);
451 	ENDWIFES
452 	return NULL;
453 }
454 /*===============================================
455  * next_spouse -- Return next spouse of family
456  * NODE *node     [in/out] pass in nchild(fam) to start
457  *                or nsibling(previous node returned from this routine) to continue
458  * RECORD *spouse [out]     next spouse in family
459  * returns 1 for success, -1 if next HUSB/WIFE record is invalid
460  *         0 no more spouses found
461  * returns addref'd record in *spouse
462  *=============================================*/
463 int
next_spouse(NODE * node,RECORD * spouse)464 next_spouse (NODE *node, RECORD *spouse)
465 {
466 	CNSTRING key=0;
467 	if (!node || !spouse) return 0;
468 	while (*node) {
469 	    if (eqstr(ntag(*node),"HUSB") || eqstr(ntag(*node),"WIFE")) {
470 		key = rmvat(nval(*node));
471 		if (!key) return -1;
472 		*spouse = qkey_to_irecord(key);
473 		if (!*spouse) return -1;
474 		return 1;
475 	    }
476 	    *node = nsibling(*node);
477 	}
478 	return 0;
479 }
480 /*==================================================
481  * fam_to_first_chil -- Return first child of family
482  *================================================*/
483 NODE
fam_to_first_chil(NODE node)484 fam_to_first_chil (NODE node)
485 {
486 	CNSTRING key=0;
487 	if (!node) return NULL;
488 	if (!(node = find_tag(nchild(node), "CHIL"))) return NULL;
489 	key = rmvat(nval(node));
490 	return qkey_to_indi(key);
491 }
492 /*=================================================
493  * fam_to_last_chil -- Return last child of family
494  *===============================================*/
495 NODE
fam_to_last_chil(NODE node)496 fam_to_last_chil (NODE node)
497 {
498 	CNSTRING key=0;
499 	NODE prev = NULL;
500 	if (!node) return NULL;
501 	/* find first CHIL in fam */
502 	if (!(node = find_tag(nchild(node), "CHIL"))) return NULL;
503 	/* cycle thru all remaining nodes, keeping most recent CHIL node */
504 	while (node) {
505 		if (eqstr(ntag(node),"CHIL"))
506 			prev = node;
507 		node = nsibling(node);
508 	}
509 	key = rmvat(nval(prev));
510 	return qkey_to_indi(key);
511 }
512 /*========================================
513  * indi_to_fath -- Return father of person
514  *======================================*/
515 NODE
indi_to_fath(NODE node)516 indi_to_fath (NODE node)
517 {
518 	return fam_to_husb_node(indi_to_famc(node));
519 }
520 /*========================================
521  * indi_to_moth -- Return mother of person
522  *======================================*/
523 NODE
indi_to_moth(NODE node)524 indi_to_moth (NODE node)
525 {
526 	return fam_to_wife_node(indi_to_famc(node));
527 }
528 /*==================================================
529  * indi_to_prev_sib -- Return previous sib of person
530  *  returns addref'd record
531  *================================================*/
532 static RECORD
indi_to_prev_sib_impl(NODE indi)533 indi_to_prev_sib_impl (NODE indi)
534 {
535 	NODE fam, prev, node;
536 	if (!indi) return NULL;
537 	if (!(fam = indi_to_famc(indi))) return NULL;
538 	prev = NULL;
539 	node = CHIL(fam);
540 	/* loop thru all nodes following first child, keeping most recent CHIL */
541 	while (node) {
542 		if (eqstr(nxref(indi), nval(node))) {
543 			if (!prev) return NULL;
544 			return key_to_record(rmvat(nval(prev)));
545 		}
546 		if (eqstr(ntag(node),"CHIL"))
547 			prev = node;
548 		node = nsibling(node);
549 	}
550 	return NULL;
551 }
552 NODE
indi_to_prev_sib_old(NODE indi)553 indi_to_prev_sib_old (NODE indi)
554 {
555 	return nztop(indi_to_prev_sib_impl(indi));
556 }
557 RECORD
indi_to_prev_sib(RECORD irec)558 indi_to_prev_sib (RECORD irec)
559 {
560 	return indi_to_prev_sib_impl(nztop(irec));
561 }
562 /*==============================================
563  * indi_to_next_sib -- Return next sib of person
564  *  returns addref'd record
565  *============================================*/
566 static RECORD
indi_to_next_sib_impl(NODE indi)567 indi_to_next_sib_impl (NODE indi)
568 {
569 	NODE fam, node;
570 	BOOLEAN found;
571 	if (!indi) return NULL;
572 	if (!(fam = indi_to_famc(indi))) return NULL;
573 	node = CHIL(fam);
574 	found = FALSE;  /* until we find indi */
575 	while (node) {
576 		if (!found) {
577 			if (eqstr(nxref(indi), nval(node)))
578 				found = TRUE;
579 		} else {
580 			if (eqstr(ntag(node),"CHIL"))
581 				return key_to_record(rmvat(nval(node)));
582 		}
583 		node = nsibling(node);
584 	}
585 	return NULL;
586 }
587 NODE
indi_to_next_sib_old(NODE indi)588 indi_to_next_sib_old (NODE indi)
589 {
590 	RECORD rec = indi_to_next_sib_impl(indi);
591 	NODE node = nztop(rec);
592 	release_record(rec); /* release ref from indi_to_next_sib_impl */
593 	return node;
594 }
595 RECORD
indi_to_next_sib(RECORD irec)596 indi_to_next_sib (RECORD irec)
597 {
598 	return indi_to_next_sib_impl(nztop(irec));
599 }
600 /*======================================
601  * indi_to_name -- Return name of person
602  *====================================*/
603 STRING
indi_to_name(NODE node,INT len)604 indi_to_name (NODE node, INT len)
605 {
606 	SURCAPTYPE surcaptype = DOSURCAP;
607 	if (node)
608 		node = find_tag(nchild(node), "NAME");
609 	if (!node)
610 		return _("NO NAME");
611 	if (!getlloptint("UppercaseSurnames", 1))
612 		surcaptype = NOSURCAP;
613 	return manip_name(nval(node), surcaptype, REGORDER, len);
614 }
615 /*======================================
616  * indi_to_title -- Return title of person
617  *====================================*/
618 STRING
indi_to_title(NODE node,INT len)619 indi_to_title (NODE node, INT len)
620 {
621 	if (!node) return NULL;
622 	if (!(node = find_tag(nchild(node), "TITL"))) return NULL;
623 	return manip_name(nval(node), NOSURCAP, REGORDER, len);
624 }
625 /*======================================
626  * node_to_tag -- Return a subtag of a node
627  * (presumably top level, but not necessarily)
628  *====================================*/
node_to_tag(NODE node,STRING tag,INT len)629 STRING node_to_tag (NODE node, STRING tag, INT len)
630 {
631 	static char scratch[MAXGEDNAMELEN+1];
632 	STRING refn;
633 	if (!node) return NULL;
634 	if (!(node = find_tag(nchild(node), tag)))
635 		return NULL;
636 	refn = nval(node);
637 	if (len > (INT)sizeof(scratch)-1)
638 		len = sizeof(scratch)-1;
639 	llstrsets(scratch, len, uu8, refn);
640 	return scratch;
641 }
642 /*==============================================
643  * fam_to_event -- Convert event tree to string
644  *============================================*/
645 STRING
fam_to_event(NODE node,STRING tag,STRING head,INT len,RFMT rfmt)646 fam_to_event (NODE node, STRING tag, STRING head
647 	, INT len, RFMT rfmt)
648 {
649 	return indi_to_event(node, tag, head, len, rfmt);
650 }
651 /*==============================================
652  * record_to_date_place -- Find event info
653  *  record:  [IN]  record to search
654  *  tag:     [IN]  desired tag (eg, "BIRT")
655  *  date:    [OUT] date found (optional)
656  *  plac:    [OUT] place found (option)
657  *  count:   [I/O] #instances of tag found (caller must init)
658  * Created: 2003-01-12 (Perry Rapp)
659  *============================================*/
660 void
record_to_date_place(RECORD record,STRING tag,STRING * date,STRING * plac,INT * count)661 record_to_date_place (RECORD record, STRING tag, STRING * date, STRING * plac
662 	, INT * count)
663 {
664 	NODE node=0;
665 	for (node = record_to_first_event(record, tag)
666 		; node
667 		; node = node_to_next_event(node, tag)) {
668 		if (++(*count) == 1)
669 			/* only record first instance */
670 			event_to_date_place(node, date, plac);
671 	}
672 }
673 /*==============================================
674  * record_to_first_event -- Find requested event subtree
675  *  record:  [IN]  record to search
676  *  tag:     [IN]  desired tag (eg, "BIRT")
677  * Created: 2003-01-12 (Perry Rapp)
678  *============================================*/
679 NODE
record_to_first_event(RECORD record,CNSTRING tag)680 record_to_first_event (RECORD record, CNSTRING tag)
681 {
682 	NODE node = nztop(record);
683 	if (!node) return NULL;
684 	return find_tag(nchild(node), tag);
685 }
686 /*==============================================
687  * node_to_next_event -- Find next event after node
688  *  node:  [IN]  start search after node
689  *  tag:   [IN]  desired tag (eg, "BIRT")
690  * Created: 2003-01-12 (Perry Rapp)
691  *============================================*/
692 NODE
node_to_next_event(NODE node,CNSTRING tag)693 node_to_next_event (NODE node, CNSTRING tag)
694 {
695 	return find_tag(nsibling(node), tag);
696 }
697 /*==============================================
698  * indi_to_event -- Convert event tree to string
699  *  node: [IN]  event subtree to search
700  *  ttm:  [IN]  translation table to apply to event strings
701  *  tag:  [IN]  desired tag (eg, "BIRT")
702  *  head: [IN]  header to print in output (eg, "born: ")
703  *  len:  [IN]  max length output desired
704  * Searches node substree for desired tag
705  *  returns formatted string (event_to_string) if found,
706  *  else NULL
707  *============================================*/
708 STRING
indi_to_event(NODE node,STRING tag,STRING head,INT len,RFMT rfmt)709 indi_to_event (NODE node, STRING tag, STRING head
710 	, INT len, RFMT rfmt)
711 {
712 	static char scratch[200];
713 	STRING p = scratch;
714 	INT mylen = sizeof(scratch)/sizeof(scratch[0]);
715 	STRING event;
716 	STRING omit;
717 	if (mylen > len+1) mylen = len+1; /* incl. trailing 0 */
718 	if (!node) return NULL;
719 	if (!(node = find_tag(nchild(node), tag))) return NULL;
720 	event = event_to_string(node, rfmt);
721 	if (!event) return NULL;
722 	/* need at least room for head + 1 character + "..." or no point */
723 	if ((INT)strlen(head)+4>len) return NULL;
724 	p[0] = 0;
725 	/* TODO: break out following code into a subroutine for disp_person_birth to call */
726 	llstrcatn(&p, head, &mylen);
727 	if (mylen<(INT)strlen(event)+1) {
728 		omit = getlloptstr("ShortOmitString", NULL);
729 		if (omit) {
730 			mylen -= strlen(omit)+1; /* plus trailing 0 */
731 			llstrcatn(&p, event, &mylen);
732 			mylen += strlen(omit)+1;
733 			llstrcatn(&p, omit, &mylen);
734 		} else {
735 			llstrcatn(&p, event, &mylen);
736 		}
737 	} else {
738 		llstrcatn(&p, event, &mylen);
739 		if (mylen && p[-1]!='.')
740 		llstrcatn(&p, ".", &mylen);
741 	}
742 	return scratch;
743 }
744 /*===========================================
745  * event_to_date_place  -- Find date & place
746  *  node:  [IN]  node tree of event to describe
747  *  date:  [OUT] value of first DATE line (optional)
748  *  plac:  [OUT] value of first PLACE line (optional)
749  *=========================================*/
750 void
event_to_date_place(NODE node,STRING * date,STRING * plac)751 event_to_date_place (NODE node, STRING * date, STRING * plac)
752 {
753 	INT count=0;
754 	if (date) {
755 		*date = NULL;
756 	} else {
757 		++count;
758 	}
759 	if (plac) {
760 		*plac = NULL;
761 	} else {
762 		++count;
763 	}
764 	if (!node) return;
765 	node = nchild(node);
766 	while (node && count<2) {
767 		if (eqstr("DATE", ntag(node)) && date && !*date) {
768 			*date = nval(node);
769 			++count;
770 		}
771 		if (eqstr("PLAC", ntag(node)) && plac && !*plac) {
772 			*plac = nval(node);
773 			++count;
774 		}
775 		node = nsibling(node);
776 	}
777 }
778 /*===========================================
779  * event_to_string -- Convert event to string
780  * Finds DATE & PLACE nodes, and prints a string
781  * representation of them.
782  *  node:  [IN]  node tree of event to describe
783  *  ttm:   [IN]  translation table to use
784  *  rfmt:  [IN]  reformatting info (may be NULL)
785  *=========================================*/
786 STRING
event_to_string(NODE node,RFMT rfmt)787 event_to_string (NODE node, RFMT rfmt)
788 {
789 	static char scratch1[MAXLINELEN+1];
790 	STRING date, plac;
791 	event_to_date_place(node, &date, &plac);
792 	if (!date && !plac) return NULL;
793 	/* Apply optional, caller-specified date & place reformatting */
794 	if (rfmt && date && rfmt->rfmt_date)
795 		date = (*rfmt->rfmt_date)(date);
796 	if (rfmt && plac && rfmt->rfmt_plac)
797 		plac = (*rfmt->rfmt_plac)(plac);
798 	if (rfmt && rfmt->combopic && date && date[0] && plac && plac[0]) {
799 		sprintpic2(scratch1, sizeof(scratch1), uu8, rfmt->combopic, date, plac);
800 	} else if (date && date[0]) {
801 		llstrncpy(scratch1, date, sizeof(scratch1), uu8);
802 	} else if (plac && plac[0]) {
803 		llstrncpy(scratch1, plac, sizeof(scratch1), uu8);
804 	} else {
805 		return NULL;
806 	}
807 	return scratch1;
808 }
809 /*=======================================
810  * event_to_date -- Convert event to date
811  *  node: [IN]  event node
812  *  ttm:  [IN]  translation table to apply
813  *  shrt: [IN]  flag - use short form if set
814  *=====================================*/
815 STRING
event_to_date(NODE node,BOOLEAN shrt)816 event_to_date (NODE node, BOOLEAN shrt)
817 {
818 	static char scratch[MAXLINELEN+1];
819 	if (!node) return NULL;
820 	if (!(node = DATE(node))) return NULL;
821 	llstrsets(scratch, sizeof(scratch),uu8, nval(node));
822 	if (shrt) return shorten_date(scratch);
823 	return scratch;
824 }
825 /*========================================
826  * event_to_plac -- Convert event to place
827  *======================================*/
828 STRING
event_to_plac(NODE node,BOOLEAN shrt)829 event_to_plac (NODE node, BOOLEAN shrt)
830 {
831 	if (!node) return NULL;
832 	node = PLAC(node);
833 	if (!node) return NULL;
834 	if (shrt) return shorten_plac(nval(node));
835 	return nval(node);
836 }
837 /*================================
838  * show_node -- Show tree -- DEBUG
839  *==============================*/
840 void
show_node(NODE node)841 show_node (NODE node)
842 {
843 	if (!node) llwprintf("(NIL)");
844 	show_node_rec(0, node);
845 }
846 /*================================================
847  * show_node_rec -- Recursive version of show_node
848  *==============================================*/
849 void
show_node_rec(INT levl,NODE node)850 show_node_rec (INT levl,
851                NODE node)
852 {
853 	INT i;
854 	if (!node) return;
855 	for (i = 1;  i < levl;  i++)
856 		llwprintf("  ");
857 	llwprintf("%d", levl);
858 	if (nxref(node)) llwprintf(" %s", nxref(node));
859 	llwprintf(" %s", ntag(node));
860 	if (nval(node)) llwprintf(" %s", nval(node));
861 	llwprintf("\n");
862 	show_node_rec(levl + 1, nchild(node));
863 	show_node_rec(levl    , nsibling(node));
864 }
865 /*===========================================
866  * length_nodes -- Return length of NODE list
867  *=========================================*/
868 INT
length_nodes(NODE node)869 length_nodes (NODE node)
870 {
871 	INT len = 0;
872 	while (node) {
873 		len++;
874 		node = nsibling(node);
875 	}
876 	return len;
877 }
878 /*=================================================
879  * shorten_plac -- Return short form of place value
880  * Returns modified input string, or value from placabbr table
881  *===============================================*/
882 STRING
shorten_plac(STRING plac)883 shorten_plac (STRING plac)
884 {
885 	STRING plac0 = plac, comma, val;
886 	if (!plac) return NULL;
887 	comma = (STRING) strrchr(plac, ',');
888 	if (comma) plac = comma + 1;
889 	while (*plac++ == ' ')
890 		;
891 	plac--;
892 	if (*plac == 0) return plac0;
893 	if ((val = valueof_str(placabbvs, plac))) return val;
894 	return plac;
895 }
896 
897 #ifdef UNUSED_CODE
898 /*============================================
899  * all_digits -- Check if string is all digits
900  * UNUSED CODE
901  *==========================================*/
902 static BOOLEAN
all_digits(STRING s)903 all_digits (STRING s)
904 {
905 	INT c;
906 	while ((c = *s++)) {
907 		if (c < '0' || c > '9') return FALSE;
908 	}
909 	return TRUE;
910 }
911 #endif /* UNUSED_CODE */
912 /*=======================
913  * copy_node -- Copy node
914  *=====================*/
915 NODE
copy_node(NODE node)916 copy_node (NODE node)
917 {
918 	return create_node(nxref(node), ntag(node), nval(node), NULL);
919 }
920 /*========================
921  * copy_node_subtree -- Copy tree
922  *======================*/
923 NODE
copy_node_subtree(NODE node)924 copy_node_subtree (NODE node)
925 {
926 	return copy_nodes(node, TRUE, FALSE);
927 }
928 /*========================
929  * copy_nodes -- Copy tree
930  *======================*/
931 NODE
copy_nodes(NODE node,BOOLEAN kids,BOOLEAN sibs)932 copy_nodes (NODE node, BOOLEAN kids, BOOLEAN sibs)
933 {
934 	NODE newn, kin;
935 	if (!node) return NULL;
936 	newn = copy_node(node);
937 	if (kids && nchild(node)) {
938 		kin = copy_nodes(nchild(node), TRUE, TRUE);
939 		ASSERT(kin);
940 		nchild(newn) = kin;
941 		while (kin) {
942 			nparent(kin) = newn;
943 			kin = nsibling(kin);
944 		}
945 	}
946 	if (sibs && nsibling(node)) {
947 		kin = copy_nodes(nsibling(node), kids, TRUE);
948 		ASSERT(kin);
949 		nsibling(newn) = kin;
950 	}
951 	return newn;
952 }
953 /*===============================================================
954  * traverse_nodes -- Traverse nodes in tree while doing something
955  * NODE node:    root of tree to traverse
956  * func:         function to call at each node (returns FALSE to stop traversal)
957  * param:        opaque pointer for client use, passed thru to callback
958  *=============================================================*/
959 BOOLEAN
traverse_nodes(NODE node,BOOLEAN (* func)(NODE,VPTR),VPTR param)960 traverse_nodes (NODE node, BOOLEAN (*func)(NODE, VPTR), VPTR param)
961 {
962 	while (node) {
963 		if (!(*func)(node, param)) return FALSE;
964 		if (nchild(node)) {
965 			if (!traverse_nodes(nchild(node), func, param))
966 				return FALSE;
967 		}
968 		node = nsibling(node);
969 	}
970 	return TRUE;
971 }
972 /*==================================================
973  * begin_node_it -- Being a node iteration
974  *================================================*/
975 void
begin_node_it(NODE node,NODE_ITER nodeit)976 begin_node_it (NODE node, NODE_ITER nodeit)
977 {
978 	nodeit->start = node;
979 	/* first node to return is node */
980 	nodeit->next = node;
981 }
982 /*==================================================
983  * find_next -- Find next node in an ongoing iteration
984  *================================================*/
985 static NODE
find_next(NODE_ITER nodeit)986 find_next (NODE_ITER nodeit)
987 {
988 	NODE curr = nodeit->next;
989 	/* goto child if there is one */
990 	NODE next = nchild(curr);
991 	if (next)
992 		return next;
993 	/* otherwise try for sibling, going up to ancestors until
994 	we find one, or we hit the start & give up */
995 	while (1) {
996 		if (next == nodeit->start)
997 			return NULL;
998 		if (nsibling(curr))
999 			return nsibling(curr);
1000 		curr = nparent(curr);
1001 		if (!curr)
1002 			return NULL;
1003 	}
1004 
1005 }
1006 /*==================================================
1007  * next_node_it_ptr -- Return next node in an ongoing iteration
1008  *================================================*/
1009 NODE
next_node_it_ptr(NODE_ITER nodeit)1010 next_node_it_ptr (NODE_ITER nodeit)
1011 {
1012 	NODE current = nodeit->next;
1013 	if (current)
1014 		nodeit->next = find_next(nodeit);
1015 	return current;
1016 }
1017 /*==================================================
1018  * num_spouses_of_indi -- Returns number of spouses of person
1019  *================================================*/
1020 INT
num_spouses_of_indi(NODE indi)1021 num_spouses_of_indi (NODE indi)
1022 {
1023 	INT nsp;
1024 	if (!indi) return 0;
1025 	FORSPOUSES(indi, spouse, fam, nsp) ENDSPOUSES
1026 	return nsp;  /* don't include self*/
1027 }
1028 /*===================================================
1029  * find_node -- Find node with specific tag and value
1030  *
1031  * NODE prnt:   [in] parent node
1032  * STRING tag:  [in] tag, may be NULL
1033  * STRING val:  [in] value, may be NULL
1034  * NODE *plast: [out] previous node, may be NULL
1035  *=================================================*/
1036 NODE
find_node(NODE prnt,STRING tag,STRING val,NODE * plast)1037 find_node (NODE prnt, STRING tag, STRING val, NODE *plast)
1038 {
1039 	NODE last, node;
1040 
1041 	if (plast) *plast = NULL;
1042 	if (!prnt || (!tag && !val)) return NULL;
1043 	for (last = NULL, node = nchild(prnt); node;
1044 	     last = node, node = nsibling(node)) {
1045 		if (tag && nestr(tag, ntag(node))) continue;
1046 		if (val && nestr(val, nval(node))) continue;
1047 		if (plast) *plast = last;
1048 		return node;
1049 	}
1050 	return NULL;
1051 }
1052 
1053 #ifdef UNUSED_CODE
1054 /*=======================================================================
1055  * father_nodes -- Given list of FAMS or FAMC nodes, returns list of HUSB
1056  *   lines they contain
1057  * UNUSED CODE
1058  *=====================================================================*/
1059 NODE
father_nodes(NODE faml)1060 father_nodes (NODE faml)      /* list of FAMC and/or FAMS nodes */
1061 {
1062 	NODE fam, refn, husb, wife, chil, rest;
1063 	NODE old = NULL, new = NULL;
1064 	while (faml) {
1065 		ASSERT(eqstr("FAMC", ntag(faml)) || eqstr("FAMS", ntag(faml)));
1066 		ASSERT(fam = key_to_fam(rmvat(nval(faml))));
1067 		split_fam(fam, &refn, &husb, &wife, &chil, &rest);
1068 		new = union_nodes(old, husb, FALSE, TRUE);
1069 		free_nodes(old);
1070 		old = new;
1071 		join_fam(fam, refn, husb, wife, chil, rest);
1072 		faml = nsibling(faml);
1073 	}
1074 	return new;
1075 }
1076 /*=======================================================================
1077  * mother_nodes -- Given list of FAMS or FAMC nodes, returns list of WIFE
1078  *   lines they contain
1079  *=====================================================================*/
1080 NODE
mother_nodes(NODE faml)1081 mother_nodes (NODE faml)      /* list of FAMC and/or FAMS nodes */
1082 {
1083 	NODE fam, refn, husb, wife, chil, rest;
1084 	NODE old = NULL, new = NULL;
1085 	while (faml) {
1086 		ASSERT(eqstr("FAMC", ntag(faml)) || eqstr("FAMS", ntag(faml)));
1087 		ASSERT(fam = key_to_fam(rmvat(nval(faml))));
1088 		split_fam(fam, &refn, &husb, &wife, &chil, &rest);
1089 		new = union_nodes(old, wife, FALSE, TRUE);
1090 		free_nodes(old);
1091 		old = new;
1092 		join_fam(fam, refn, husb, wife, chil, rest);
1093 		faml = nsibling(faml);
1094 	}
1095 	return new;
1096 }
1097 /*=========================================================================
1098  * children_nodes -- Given list of FAMS or FAMC nodes, returns list of CHIL
1099  *   lines they contain
1100  *=======================================================================*/
1101 NODE
children_nodes(NODE faml)1102 children_nodes (NODE faml)      /* list of FAMC and/or FAMS nodes */
1103 {
1104 	NODE fam, refn, husb, wife, chil, rest;
1105 	NODE old = NULL, new = NULL;
1106 	while (faml) {
1107 		ASSERT(eqstr("FAMC", ntag(faml)) || eqstr("FAMS", ntag(faml)));
1108 		ASSERT(fam = key_to_fam(rmvat(nval(faml))));
1109 		split_fam(fam, &refn, &husb, &wife, &chil, &rest);
1110 		new = union_nodes(old, chil, FALSE, TRUE);
1111 		free_nodes(old);
1112 		old = new;
1113 		join_fam(fam, refn, husb, wife, chil, rest);
1114 		faml = nsibling(faml);
1115 	}
1116 	return new;
1117 }
1118 /*========================================================================
1119  * parents_nodes -- Given list of FAMS or FAMC nodes, returns list of HUSB
1120  *   and WIFE lines they contain
1121  *======================================================================*/
1122 NODE
parents_nodes(NODE faml)1123 parents_nodes (NODE faml)      /* list of FAMC and/or FAMS nodes */
1124 {
1125 	NODE fam, refn, husb, wife, chil, rest;
1126 	NODE old = NULL, new = NULL;
1127 	while (faml) {
1128 		ASSERT(eqstr("FAMC", ntag(faml)) || eqstr("FAMS", ntag(faml)));
1129 		ASSERT(fam = key_to_fam(rmvat(nval(faml))));
1130 		split_fam(fam, &refn, &husb, &wife, &chil, &rest);
1131 		new = union_nodes(old, husb, FALSE, TRUE);
1132 		free_nodes(old);
1133 		old = new;
1134 		new = union_nodes(old, wife, FALSE, TRUE);
1135 		free_nodes(old);
1136 		old = new;
1137 		join_fam(fam, refn, husb, wife, chil, rest);
1138 		faml = nsibling(faml);
1139 	}
1140 	return new;
1141 }
1142 #endif /* UNUSED_CODE */
1143 /*=================================================
1144  * node_destructor -- destructor for node
1145  *  (destructor entry in vtable)
1146  *===============================================*/
1147 static void
node_destructor(VTABLE * obj)1148 node_destructor (VTABLE *obj)
1149 {
1150 	NODE node = (NODE)obj;
1151 	ASSERT((*obj) == &vtable_for_node);
1152 	free_node(node);
1153 }
1154 /*=================================================
1155  * check_node_leaks -- Called when database closing
1156  *  for debugging
1157  *===============================================*/
1158 void
check_node_leaks(void)1159 check_node_leaks (void)
1160 {
1161 	if (live_count) {
1162 		STRING report_leak_path = getlloptstr("ReportLeakLog", NULL);
1163 		FILE * fp=0;
1164 		if (report_leak_path)
1165 			fp = fopen(report_leak_path, LLAPPENDTEXT);
1166 		if (fp) {
1167 			LLDATE date;
1168 			get_current_lldate(&date);
1169 			fprintf(fp, _("Node memory leaks:"));
1170 			fprintf(fp, " %s", date.datestr);
1171 			fprintf(fp, "\n  ");
1172 			fprintf(fp, _pl("%d item leaked", "%d items leaked", live_count), live_count);
1173 			fprintf(fp, "\n");
1174 			fclose(fp);
1175 			fp = 0;
1176 		}
1177 	}
1178 }
1179