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