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  * indiseq.c -- Person sequence operations
26  * Copyright(c) 1991-94 by T.T. Wetmore IV; all rights reserved
27  *   2.3.4 - 24 Jun 93    2.3.5 - 25 Aug 93
28  *   3.0.0 - 09 May 94    3.0.2 - 23 Dec 94
29  *===========================================================*/
30 /* modified 05 Jan 2000 by Paul B. McBride (pmcbride@tiac.net) */
31 /* modified 2000-01-26 J.F.Chandler */
32 /* modified 2000-08-21 J.F.Chandler */
33 
34 #include "llstdlib.h" /* llstdlib.h includes standard.h, config.h, sys_inc.h */
35 #include "zstr.h"
36 #ifdef HAVE_LOCALE_H
37 #include <locale.h>
38 #endif
39 #include "table.h"
40 #include "translat.h"
41 #include "gedcom.h"
42 #include "cache.h"
43 #include "indiseq.h"
44 #include "gedcomi.h"
45 #include "mystring.h" /* lat1_xx */
46 
47 /*
48 	indiseqs are typed as to value
49 	as of 2001/01/07 (Perry)
50 	ival = ints
51 	pval = pointers (caller determined
52 	sval = strings
53 	null = not yet specified
54 
55 	null indiseqs change type as soon as a fixed type value
56 	is assigned to them, but as long as only nulls are attached
57 	(append_indiseq_null) they remain uncommitted
58 
59 	NB: null values can be appended to any type (append_indiseq_null)
60 
61 	pointers are managed by the caller - in practice these are
62 	all PVALUES for report commands
63 */
64 
65 
66 /*********************************************
67  * local enums
68  *********************************************/
69 
70 /*====================
71  * indiseq print types
72  *==================*/
73 #define ISPRN_NORMALSEQ 0
74 #define ISPRN_FAMSEQ 1
75 #define ISPRN_SPOUSESEQ 2
76 
77 /*********************************************
78  * local types
79  *********************************************/
80 
81 /*==================================================================
82  * SORTEL -- Data type for indiseq elements; keys are always present
83  *   and belong to the structure; names are always present for
84  *   persons and belong; values do not belong to the structure
85  *================================================================*/
86 struct tag_sortel {
87 	STRING s_key;	/* person or family key */
88 	STRING s_nam;	/* name of person */
89 	UNION s_val;	/* any value */
90 	STRING s_prn;	/* menu print string */
91 	INT s_pri;	/* key as integer (exc valuesort_indiseq puts values here) */
92 };
93 /* typedef struct tag_sortel *SORTEL; */ /* in indiseq.h */
94 #define skey(s) ((s)->s_key)
95 #define snam(s) ((s)->s_nam)
96 #define sval(s) ((s)->s_val)
97 #define sprn(s) ((s)->s_prn)
98 #define spri(s) ((s)->s_pri)
99 
100 /*********************************************
101  * local function prototypes
102  *********************************************/
103 
104 /* alphabetical */
105 static void append_all_tags(INDISEQ, NODE, STRING tagname, BOOLEAN recurse, BOOLEAN nonptrs);
106 static void append_indiseq_impl(INDISEQ seq, STRING key,
107 	CNSTRING name, UNION val, BOOLEAN sure, BOOLEAN alloc);
108 static void calc_indiseq_name_el(INDISEQ seq, INT index);
109 static INT canonkey_compare(SORTEL el1, SORTEL el2, VPTR param);
110 static INT canonkey_order(char c);
111 static void check_indiseq_valtype(INDISEQ seq, INT valtype);
112 static UNION copyval(INDISEQ seq, UNION uval);
113 static INDISEQ create_indiseq_impl(INT valtype, INDISEQ_VALUE_FNCTABLE fnctable);
114 static void delete_el(INDISEQ seq, SORTEL el);
115 static void deleteval(INDISEQ seq, UNION uval);
116 static INDISEQ dupseq(INDISEQ seq);
117 static STRING get_print_el(INDISEQ, INT i, INT len, RFMT rfmt);
118 static BOOLEAN is_locale_current(INDISEQ seq);
119 static INT key_compare(SORTEL el1, SORTEL el2, VPTR param);
120 static INT name_compare(SORTEL el1, SORTEL el2, VPTR param);
121 static void llqsort2(SORTEL *data, ELCMPFNC cmp, VPTR param, INT a, INT b);
122 static void partition2(SORTEL *arr, ELCMPFNC cmp, VPTR param, INT a, INT b, INT *pi, INT *pj);
123 static STRING qkey_to_name(STRING key);
124 static void update_locale(INDISEQ seq);
125 static INT value_compare(SORTEL el1, SORTEL el2, VPTR param);
126 
127 /*********************************************
128  * local variables
129  *********************************************/
130 
131 static struct tag_indiseq_value_fnctable def_valfnctbl =
132 {
133 	&default_copy_value
134 	, &default_delete_value
135 	, &default_create_gen_value
136 	, &default_compare_values
137 };
138 
139 /*********************************************
140  * local function definitions
141  * body of module
142  *********************************************/
143 
144 /*===============================================
145  * create_indiseq_ival -- Create sequence of INTs
146  * Created: 2001/01/07, Perry Rapp
147  *=============================================*/
148 INDISEQ
create_indiseq_ival(void)149 create_indiseq_ival (void)
150 {
151 	return create_indiseq_impl(ISVAL_INT, NULL);
152 }
153 /*===================================================
154  * create_indiseq_null -- Create sequence of not yet
155  *  determined type of objects
156  * Created: 2001/01/07, Perry Rapp
157  *=================================================*/
158 INDISEQ
create_indiseq_null(void)159 create_indiseq_null (void)
160 {
161 	return create_indiseq_impl(ISVAL_NUL, NULL);
162 }
163 /*===================================================
164  * create_indiseq_pval -- Create sequence of pointers
165  * Created: 2001/01/07, Perry Rapp
166  *=================================================*/
167 INDISEQ
create_indiseq_pval(void)168 create_indiseq_pval (void)
169 {
170 	return create_indiseq_impl(ISVAL_PTR, NULL);
171 }
172 /*==================================================
173  * create_indiseq_sval -- Create sequence of STRINGs
174  * Created: 2001/01/07, Perry Rapp
175  *================================================*/
176 INDISEQ
create_indiseq_sval(void)177 create_indiseq_sval (void)
178 {
179 	return create_indiseq_impl(ISVAL_STR, NULL);
180 }
181 /*=======================================
182  * create_indiseq_impl -- Create sequence
183  * fnctable specifies the value function table for the
184  * new seq, and is optional - if NULL, the default
185  * one will be used
186  *=====================================*/
187 static INDISEQ
create_indiseq_impl(INT valtype,INDISEQ_VALUE_FNCTABLE fnctable)188 create_indiseq_impl (INT valtype, INDISEQ_VALUE_FNCTABLE fnctable)
189 {
190 	INDISEQ seq = (INDISEQ) stdalloc(sizeof *seq);
191 	memset(seq, 0, sizeof(*seq));
192 	IMax(seq) = 20;
193 	IData(seq) = (SORTEL *) stdalloc(20*sizeof(SORTEL));
194 	IPrntype(seq) = ISPRN_NORMALSEQ;
195 	IValtype(seq) = valtype;
196 	IValfnctbl(seq) = fnctable ? fnctable : &def_valfnctbl;
197 	return seq;
198 }
199 /*==================================
200  * remove_indiseq -- Remove sequence
201  *================================*/
202 void
remove_indiseq(INDISEQ seq)203 remove_indiseq (INDISEQ seq)
204 {
205 	SORTEL *d = IData(seq);
206 	INT i, n = ISize(seq);
207 	/* remove each element's heap memory */
208 	for (i = 0; i < n; i++, d++) {
209 		stdfree(skey(*d));
210 		if (snam(*d)) stdfree(snam(*d));
211 		deleteval(seq, sval(*d));
212 		if (sprn(*d)) stdfree(sprn(*d));
213 		stdfree(*d);
214 	}
215 	stdfree(IData(seq));
216 	if (ILocale(seq))
217 		stdfree(ILocale(seq));
218 	stdfree(seq);
219 }
220 /*==============================
221  * copyval -- Copy a value using the value function table
222  * Created: 2001/03/25, Perry Rapp
223  * This takes care of the problem that report indiseqs
224  * must use report-allocated values (pvalues) - and these
225  * cannot be copied directly, a copy must be allocated.
226  * This is a complication that is solved thru a function table,
227  * because indiseq.c is in a layer lower than the interpreter,
228  * and does not know about pvalues. The interpreter registers
229  * a copy function in the seq's function table to solve this.
230  *============================*/
231 static UNION
copyval(INDISEQ seq,UNION uval)232 copyval (INDISEQ seq, UNION uval)
233 {
234 	return (*IValfnctbl(seq)->copy_fnc)(uval, IValtype(seq));
235 }
236 /*==============================
237  * deleteval -- Delete a value using the value function table
238  * Created: 2001/03/25, Perry Rapp
239  *============================*/
240 static void
deleteval(INDISEQ seq,UNION uval)241 deleteval (INDISEQ seq, UNION uval)
242 {
243 	(*IValfnctbl(seq)->delete_fnc)(uval, IValtype(seq));
244 }
245 /*==============================
246  * creategenval -- Create a value for a new element
247  *  of a particular generation
248  * (This is for ancestorset & descendantset)
249  * Handle case that seq is null value type, and callee
250  *  assigns a value type.
251  * Created: 2001/03/25, Perry Rapp
252  *============================*/
253 static UNION
creategenval(INDISEQ seq,INT gen)254 creategenval (INDISEQ seq, INT gen)
255 {
256 	UNION u;
257 	INT valtype = IValtype(seq);
258 	u = (*IValfnctbl(seq)->create_gen_fnc)(gen, &valtype);
259 	if (valtype != IValtype(seq)) {
260 		ASSERT(IValtype(seq) == ISVAL_NUL);
261 		IValtype(seq) = valtype;
262 	}
263 	return u;
264 }
265 /*==============================
266  * copy_indiseq -- Copy sequence
267  *============================*/
268 INDISEQ
copy_indiseq(INDISEQ seq)269 copy_indiseq (INDISEQ seq)
270 {
271 	INDISEQ newseq;
272 	UNION uval;
273 	if (!seq) return NULL;
274 	newseq = create_indiseq_impl(IValtype(seq), IValfnctbl(seq));
275 	FORINDISEQ(seq, el, num)
276 		uval = copyval(seq, sval(el));
277 		append_indiseq_impl(newseq, skey(el), snam(el), uval,
278 		    TRUE, FALSE);
279 	ENDINDISEQ
280 	/* set flags after, so append doesn't do unique checks or generate names */
281 	IFlags(newseq) = IFlags(seq);
282 	IFlags(newseq) &= (~WITHNAMES); /* didn't generate names */
283 	return newseq;
284 }
285 /*==================================================
286  * check_indiseq_valtype -- Check that value type
287  *  is as expected or null (in which case, convert)
288  * Created: 2001/01/07, Perry Rapp
289  *================================================*/
290 static void
check_indiseq_valtype(INDISEQ seq,INT valtype)291 check_indiseq_valtype (INDISEQ seq, INT valtype)
292 {
293 	if (IValtype(seq) == ISVAL_NUL) {
294 		IValtype(seq) = valtype;
295 	} else {
296 		ASSERT(IValtype(seq) == valtype);
297 	}
298 }
299 /*==================================================
300  * append_indiseq_ival -- Append element to sequence
301  *  with INT value
302  * INDISEQ seq:    sequence
303  * STRING key:     key - not NULL
304  * STRING name:    name - may be NULL
305  * INT val:        extra val
306  * BOOLEAN sure:   no dupe check?
307  * BOOLEAN alloc:  key alloced?
308  * Created: 2001/01/05, Perry Rapp
309  *================================================*/
310 void
append_indiseq_ival(INDISEQ seq,STRING key,STRING name,INT val,BOOLEAN sure,BOOLEAN alloc)311 append_indiseq_ival (INDISEQ seq, STRING key, STRING name, INT val
312 	, BOOLEAN sure, BOOLEAN alloc)
313 {
314 	UNION u;
315 	u.i = val;
316 	check_indiseq_valtype(seq, ISVAL_INT);
317 	append_indiseq_impl(seq, key, name, u, sure, alloc);
318 }
319 /*==================================================
320  * append_indiseq_pval -- Append element to sequence
321  *  with pointer value
322  * Created: 2001/01/07, Perry Rapp
323  * INDISEQ seq:    sequence
324  * STRING key:     key - not NULL
325  * STRING name:    name - may be NULL
326  * VPTR pval:      extra val
327  * BOOLEAN sure:   no dupe check?
328  * BOOLEAN alloc:  key alloced?
329  *================================================*/
330 void
append_indiseq_pval(INDISEQ seq,STRING key,STRING name,VPTR pval,BOOLEAN sure)331 append_indiseq_pval (INDISEQ seq,    /* sequence */
332                      STRING key,     /* key - not NULL */
333                      STRING name,    /* name - may be NULL */
334                      VPTR pval,       /* extra val */
335                      BOOLEAN sure)   /* no dupe check? */
336 
337 {
338 	BOOLEAN alloc=FALSE; /* let append_indiseq_impl copy key */
339 	UNION u;
340 	u.w = pval;
341 	check_indiseq_valtype(seq, ISVAL_PTR);
342 	append_indiseq_impl(seq, key, name, u, sure, alloc);
343 }
344 /*==================================================
345  * append_indiseq_sval -- Append element to sequence
346  *  with STRING value
347  * (Should be alloc'd values, unless caller is using
348  *  a custom value function table)
349  * Created: 2001/01/05, Perry Rapp
350  *================================================*/
351 void
append_indiseq_sval(INDISEQ seq,STRING key,CNSTRING name,STRING sval,BOOLEAN sure,BOOLEAN alloc)352 append_indiseq_sval (INDISEQ seq,    /* sequence */
353                      STRING key,     /* key - not NULL */
354                      CNSTRING name,    /* name - may be NULL */
355                      STRING sval,       /* extra val */
356                      BOOLEAN sure,   /* no dupe check? */
357                      BOOLEAN alloc)  /* key alloced? */
358 {
359 	UNION u;
360 	u.w = sval;
361 	check_indiseq_valtype(seq, ISVAL_STR);
362 	append_indiseq_impl(seq, key, name, u, sure, alloc);
363 }
364 /*==================================================
365  * append_indiseq_null -- Append element to sequence
366  *  without value
367  * Created: 2001/01/07, Perry Rapp
368  *================================================*/
369 void
append_indiseq_null(INDISEQ seq,STRING key,CNSTRING name,BOOLEAN sure,BOOLEAN alloc)370 append_indiseq_null (INDISEQ seq,    /* sequence */
371                      STRING key,     /* key - not NULL */
372                      CNSTRING name,    /* name - may be NULL */
373                      BOOLEAN sure,   /* no dupe check? */
374                      BOOLEAN alloc)  /* key alloced? */
375 {
376 	UNION u;
377 	u.i=0;
378 	/* no type check - valid for any seq */
379 	append_indiseq_impl(seq, key, name, u, sure, alloc);
380 }
381 /*==================================================
382  * append_indiseq_impl -- Append element to sequence
383  *  all type appends use this implementation
384  * INDISEQ seq:      sequence
385  * STRING key:       key - not NULL
386  * CNSTRING name:    name - may be NULL
387  * UNION val:        extra val is pointer
388  * BOOLEAN sure:     no dupe check?
389  * BOOLEAN alloc:    key alloced?
390  *================================================*/
391 static void
append_indiseq_impl(INDISEQ seq,STRING key,CNSTRING name,UNION val,BOOLEAN sure,BOOLEAN alloc)392 append_indiseq_impl (INDISEQ seq, STRING key, CNSTRING name, UNION val
393 	, BOOLEAN sure, BOOLEAN alloc)
394 {
395 	INT i, m, n;
396 	SORTEL el, *new, *old;
397 	if (!seq || !key) return;
398 	n = ISize(seq);
399 	old = IData(seq);
400 	if (!sure) {
401 			/* Perry, 2000/11/28 - I'm skipping dupcheck
402 			for FAMs for compatibility, but I don't know
403 			why FAM seqs didn't do dupcheck */
404 		BOOLEAN dupcheck = (*key != 'F' && *key != 'I')
405 			|| (*key == 'I' && !name);
406 		if (dupcheck)
407 		{
408 			for (i = 0; i < n; i++) {
409 				if (eqstr(key, skey(old[i]))) {
410 						/* failed dupe check - bail */
411 					if (alloc)
412 						stdfree(key);
413 					deleteval(seq, val);
414 					return;
415 				}
416 			}
417 		}
418 	}
419 	el = (SORTEL) stdalloc(sizeof(*el));
420 	skey(el) = alloc ? key : strsave(key);
421 	snam(el) = NULL;
422 	if (*key == 'I' && (IFlags(seq) & WITHNAMES)) {
423 		if (!name)
424 			name = qkey_to_name(key);
425 		if (name)
426 			snam(el) = strsave(name);
427 		else
428 			snam(el) = NULL;
429 	}
430 	sval(el) = val;
431 	spri(el) = 0;
432 	sprn(el) = NULL;
433 	if ((n = ISize(seq)) >= IMax(seq))  {
434 		m = 3*n;
435 		new = (SORTEL *) stdalloc(m*sizeof(SORTEL));
436 		for (i = 0; i < n; i++)
437 			new[i] = old[i];
438 		stdfree(old);
439 		IData(seq) = old = new;
440 		IMax(seq) = m;
441 	}
442 	old[ISize(seq)++] = el;
443 	IFlags(seq) = 0;
444 }
445 /*=========================================================
446  * rename_indiseq -- Update element name with standard name
447  *  (actually will update all instances of specified key)
448  *  seq:  [IN]  sequence to modify
449  *  key:  [IN]  particular element(s) to update
450  *=======================================================*/
451 void
rename_indiseq(INDISEQ seq,STRING key)452 rename_indiseq (INDISEQ seq, STRING key)
453 {
454 	INT i, n;
455 	SORTEL *data;
456 	if (!seq || !key || *key != 'I') return;
457 	n = ISize(seq);
458 	data = IData(seq);
459 	for (i = 0; i < n; i++) {
460 		if (eqstr(key, skey(data[i]))) {
461 			STRING name = qkey_to_name(key);
462 			if (snam(data[i])) stdfree(snam(data[i]));
463 			if (name)
464 				snam(data[i]) = strsave(name);
465 			else
466 				snam(data[i]) = NULL;
467 		}
468 	}
469 }
470 /*==============================================
471  * in_indiseq -- See if element is in an INDISEQ
472  *=============================================*/
473 BOOLEAN
in_indiseq(INDISEQ seq,STRING key)474 in_indiseq (INDISEQ seq, STRING key)
475 {
476 	INT i, len;
477 	SORTEL *data;
478 
479 	if (!seq || !key) return FALSE;
480 	len = ISize(seq);
481 	data = IData(seq);
482 	for (i = 0; i < len; i++) {
483 		if (eqstr(key, skey(data[i]))) return TRUE;
484 	}
485 	return FALSE;
486 }
487 /*===============================================================
488  * delete_indiseq -- Remove el from sequence
489  *  if key & name given, look for element matching both
490  *  if key given, look for element matching key
491  *   if neither, use index passed
492  * seq:   [I/O] sequence
493  * key:   [IN]  key - may be NULL
494  * name:  [IN]  name - may be NULL
495  * index: [IN]  index of el to remove - may be computed
496  *==============================================================*/
497 BOOLEAN
delete_indiseq(INDISEQ seq,STRING key,STRING name,INT index)498 delete_indiseq (INDISEQ seq, STRING key, STRING name, INT index)
499 {
500 	INT i, len;
501 	SORTEL *data, el;
502 	if (!seq) return FALSE;
503 	len = ISize(seq);
504 	data = IData(seq);
505 	if (key) {
506 		if (*key != 'I') return FALSE;
507 		for (i = 0; i < len; i++) {
508 			if (eqstr(key, skey(data[i])) && (!name ||
509 			    eqstr(name, snam(data[i])))) break;
510 		}
511 		if (i >= len) return FALSE;
512 		index = i;
513 	}
514 	if (index < 0 || index >= len) return FALSE;
515 	len--;
516 	el = data[index];
517 	for (i = index; i < len; i++)
518 		data[i] = data[i+1];
519 	ISize(seq)--;
520 	delete_el(seq, el);
521 	stdfree(el);
522 	return TRUE;
523 }
524 /*===============================================================
525  * delete_el -- Free contents of element of INDISEQ
526  *==============================================================*/
527 static void
delete_el(INDISEQ seq,SORTEL el)528 delete_el (INDISEQ seq, SORTEL el)
529 {
530 	stdfree(skey(el));
531 	if (snam(el)) {
532 		stdfree(snam(el));
533 		snam(el)=NULL;
534 	}
535 	if (sprn(el)) {
536 		stdfree(sprn(el));
537 		sprn(el)=NULL;
538 	}
539 	deleteval(seq, sval(el));
540 	if (IValtype(seq) == ISVAL_INT)
541 		sval(el).i = 0;
542 	else
543 		sval(el).w = 0;
544 }
545 /*================================================
546  * element_indiseq -- Return element from sequence
547  *  seq:   [IN]  sequence
548  *  index: [IN]  element desired
549  *  pkey:  [OUT] returned key
550  *  pname: [OUT] returned name
551  *==============================================*/
552 BOOLEAN
element_indiseq(INDISEQ seq,INT index,STRING * pkey,STRING * pname)553 element_indiseq (INDISEQ seq, INT index, STRING *pkey, STRING *pname)
554 {
555 	*pkey = *pname = NULL;
556 	if (!seq || index < 0 || index > ISize(seq) - 1) return FALSE;
557 	calc_indiseq_name_el(seq, index);
558 	*pkey =  skey(IData(seq)[index]);
559 	*pname = snam(IData(seq)[index]);
560 	return TRUE;
561 }
562 /*================================================
563  * elementval_indiseq -- Return element & value from sequence
564  * Created: 2000/11/29, Perry Rapp
565  *  seq;   [IN]  sequence
566  *  index: [IN]  index of desired element
567  *  pkey:  [OUT] returned key
568  *  pval:  [OUT] returned val
569  *  pname: [OUT] returned name
570  *==============================================*/
571 BOOLEAN
element_indiseq_ival(INDISEQ seq,INT index,STRING * pkey,INT * pval,STRING * pname)572 element_indiseq_ival (INDISEQ seq, INT index, STRING *pkey, INT *pval
573 	, STRING *pname)
574 {
575 	*pkey = *pname = NULL;
576 	if (!seq || index < 0 || index > ISize(seq) - 1) return FALSE;
577 	*pkey =  skey(IData(seq)[index]);
578 	/* do we need to allow for NUL type here ? */
579 	ASSERT(IValtype(seq) == ISVAL_INT || IValtype(seq) == ISVAL_NUL);
580 	*pval = sval(IData(seq)[index]).i;
581 	*pname = snam(IData(seq)[index]);
582 	return TRUE;
583 }
584 /*================================================
585  * element_key_indiseq -- Return element key from sequence
586  *  or NULL if bad inputs
587  *==============================================*/
588 CNSTRING
element_key_indiseq(INDISEQ seq,INT index)589 element_key_indiseq (INDISEQ seq, INT index)
590 {
591 	if (!seq) return NULL;
592 	if (index<0 || index>=ISize(seq)) return NULL;
593 	return skey(IData(seq)[index]);
594 }
595 /*================================================
596  * element_skey -- Return element key from indiseq element
597  *  Requires valid input
598  *==============================================*/
599 CNSTRING
element_skey(SORTEL el)600 element_skey (SORTEL el)
601 {
602 	ASSERT(el);
603 	return el->s_key;
604 }
605 /*================================================
606  * element_name -- Return element name from indiseq element
607  *  Requires valid input
608  *==============================================*/
609 CNSTRING
element_name(SORTEL el)610 element_name (SORTEL el)
611 {
612 	ASSERT(el);
613 	return el->s_nam;
614 }
615 /*================================================
616  * element_sval -- Return string value from indiseq element
617  *  Requires valid input
618  *==============================================*/
619 CNSTRING
element_sval(SORTEL el)620 element_sval (SORTEL el)
621 {
622 	ASSERT(el);
623 	return el->s_val.w;
624 }
625 /*================================================
626  * element_ikey -- Return integer key value from indiseq element
627  *  Requires valid input
628  *==============================================*/
629 INT
element_ikey(SORTEL el)630 element_ikey (SORTEL el)
631 {
632 	ASSERT(el);
633 	return el->s_pri;
634 }
635 /*================================================
636  * element_pval -- Return pointer value from indiseq element
637  *  Requires valid input
638  *==============================================*/
639 VPTR
element_pval(SORTEL el)640 element_pval (SORTEL el)
641 {
642 	ASSERT(el);
643 	return el->s_val.w;
644 }
645 /*================================================
646  * set_element_pval -- Change pointer value of element
647  *  Requires valid input
648  *==============================================*/
649 void
set_element_pval(SORTEL el,VPTR ptr)650 set_element_pval (SORTEL el, VPTR ptr)
651 {
652 	ASSERT(el);
653 	el->s_val.w = ptr;
654 }
655 /*==================================
656  * name_compare -- Compare two names
657  *================================*/
658 INT
name_compare(SORTEL el1,SORTEL el2,VPTR param)659 name_compare (SORTEL el1, SORTEL el2, VPTR param)
660 {
661 	if (!snam(el2)) {
662 		if (snam(el1))
663 			return -1;
664 	} else if (!snam(el1)) {
665 		if (snam(el2))
666 			return 1;
667 	} else {
668 		INT rel = namecmp(snam(el1), snam(el2));
669 		if (rel) return rel;
670 	}
671 	return canonkey_compare(el1, el2, param);
672 }
673 /*================================
674  * key_compare -- Compare two keys
675  * also used for integer value sort
676  *==============================*/
677 INT
key_compare(SORTEL el1,SORTEL el2,VPTR param)678 key_compare (SORTEL el1, SORTEL el2, VPTR param)
679 {
680 	param = param; /* unused */
681 	return spri(el1) - spri(el2);
682 }
683 /*===========================================
684  * canonkey_order -- Canonical order of a type
685  *  letter (I,F,S,E,X)
686  * Created: 2001/01/06, Perry Rapp
687  *=========================================*/
688 static INT
canonkey_order(char c)689 canonkey_order (char c)
690 {
691 	switch(c) {
692 	case 'I': return 0;
693 	case 'F': return 1;
694 	case 'S': return 2;
695 	case 'E': return 3;
696 	default: return 4;
697 	}
698 }
699 /*================================
700  * canonkey_compare -- Compare two keys
701  * in canonical key order (I,F,S,E,X)
702  * Created: 2001/01/06, Perry Rapp
703  *==============================*/
704 static INT
canonkey_compare(SORTEL el1,SORTEL el2,VPTR param)705 canonkey_compare (SORTEL el1, SORTEL el2, VPTR param)
706 {
707 	char c1=skey(el1)[0], c2=skey(el2)[0];
708 	param = param; /* unused */
709 	if (c1 == c2)
710 		return spri(el1) - spri(el2);
711 	return canonkey_order(c1) - canonkey_order(c2);
712 }
713 /*===================================================
714  * value_compare -- Compare two values as strings
715  *=================================================*/
716 static INT
value_compare(SORTEL el1,SORTEL el2,VPTR param)717 value_compare (SORTEL el1, SORTEL el2, VPTR param)
718 {
719 	INDISEQ seq = (INDISEQ)param;
720 	INT valtype = IValtype(seq);
721 	INT rel = 0;
722 	if (valtype == ISVAL_INT) {
723 		INT i1=sval(el1).i, i2=sval(el2).i;
724 		rel = i1 - i2;
725 	} else if (valtype == ISVAL_STR) {
726 		STRING str1=sval(el1).w, str2=sval(el2).w;
727 		if (!str2) {
728 			if (str1)
729 				rel = -1;
730 		} else if (!str1) {
731 			if (str2)
732 				rel = 1;
733 		} else {
734 			rel = strcoll(str1, str2);
735 		}
736 	} else if (valtype == ISVAL_PTR) {
737 		VPTR ptr1=sval(el1).w, ptr2=sval(el2).w;
738 		rel = (*IValfnctbl(seq)->compare_val_fnc)(ptr1, ptr2, valtype);
739 	} else {
740 		/* nothing -- fall through to default to canonkey_compare */
741 	}
742 	if (!rel)
743 		rel = canonkey_compare(el1, el2, param);
744 	return rel;
745 }
746 /*==========================================
747  * namesort_indiseq -- Sort sequence by name
748  *========================================*/
749 void
namesort_indiseq(INDISEQ seq)750 namesort_indiseq (INDISEQ seq)
751 {
752 	calc_indiseq_names(seq);
753 	if ((IFlags(seq) & NAMESORT) && is_locale_current(seq)) return;
754 	FORINDISEQ(seq, el, num)
755 		spri(el) = atoi(skey(el) + 1);
756 	ENDINDISEQ
757 	partition_sort(IData(seq), ISize(seq), name_compare, seq);
758 	IFlags(seq) &= ~ALLSORTS;
759 	IFlags(seq) |= NAMESORT;
760 	update_locale(seq);
761 }
762 /*========================================
763  * keysort_indiseq -- Sort sequence by key
764  *======================================*/
765 void
keysort_indiseq(INDISEQ seq)766 keysort_indiseq (INDISEQ seq)
767 {
768 	if (IFlags(seq) & KEYSORT) return;
769 	FORINDISEQ(seq, el, num)
770 		spri(el) = atoi(skey(el) + 1);
771 	ENDINDISEQ
772 	partition_sort(IData(seq), ISize(seq), key_compare, seq);
773 	IFlags(seq) &= ~ALLSORTS;
774 	IFlags(seq) |= KEYSORT;
775 }
776 /*=============================================
777  * canonkeysort_indiseq -- Sort sequence by key
778  *  in key canonical order (I,F,S,E,X)
779  * Created: 2001/01/06, Perry Rapp
780  *===========================================*/
781 void
canonkeysort_indiseq(INDISEQ seq)782 canonkeysort_indiseq (INDISEQ seq)
783 {
784 	if (IFlags(seq) & CANONKEYSORT) return;
785 	FORINDISEQ(seq, el, num)
786 		spri(el) = atoi(skey(el) + 1);
787 	ENDINDISEQ
788 	partition_sort(IData(seq), ISize(seq), canonkey_compare, seq);
789 	IFlags(seq) &= ~ALLSORTS;
790 	IFlags(seq) |= CANONKEYSORT;
791 }
792 /*============================================
793  * is_locale_current --
794  *  returns FALSE if locale has changed since
795  *  this was sorted
796  *==========================================*/
797 static BOOLEAN
is_locale_current(INDISEQ seq)798 is_locale_current (INDISEQ seq)
799 {
800 	if (are_locales_supported()) {
801 		if (! ILocale(seq)) return TRUE;
802 		return eqstr(ILocale(seq), llsetlocale(LC_COLLATE, NULL));
803 	} else
804 		return TRUE;
805 }
806 /*============================================
807  * update_locale --
808  *  Annotate seq with current locale
809  *==========================================*/
810 static void
update_locale(INDISEQ seq)811 update_locale (INDISEQ seq)
812 {
813 	const char *locstr = llsetlocale(LC_COLLATE, NULL);
814 	if (!ILocale(seq) || !eqstr(ILocale(seq), locstr)) {
815 		if (ILocale(seq))
816 			stdfree(ILocale(seq));
817 		ILocale(seq) = strsave(locstr);
818 	}
819 }
820 /*============================================
821  * valuesort_indiseq -- Sort sequence by value
822  *==========================================*/
823 void
valuesort_indiseq(INDISEQ seq,BOOLEAN * eflg)824 valuesort_indiseq (INDISEQ seq, BOOLEAN *eflg)
825 {
826 	eflg = eflg; /* unused */
827 	if ((IFlags(seq) & VALUESORT) && is_locale_current(seq)) return;
828 	partition_sort(IData(seq), ISize(seq), value_compare, seq);
829 	IFlags(seq) &= ~ALLSORTS;
830 	IFlags(seq) |= VALUESORT;
831 	update_locale(seq);
832 }
833 /*=========================================
834  * partition_sort -- Partition (quick) sort
835  *=======================================*/
836 /*
837  *  data:  [I/O] array of els to sort
838  *  len:   [IN]  size of data
839  *  cmp:   [IN]  callback to compare two elements
840  *  param: [IN]  opaque parameter for callback
841  */
842 void
partition_sort(SORTEL * data,INT len,ELCMPFNC cmp,VPTR param)843 partition_sort (SORTEL *data, INT len, ELCMPFNC cmp, VPTR param)
844 {
845 	llqsort2(data, cmp, param, 0, len-1);
846 }
847 /*======================================
848  * llqsort2 -- Nonrecursive quicksort
849  *  median of three pivot
850  *====================================*/
851 /*======================================
852  * partition2 -- Median of three pivot
853  *  *pi is set to position of pivot
854  *  *pj is set to index of highest item before *pi not equal to pivot
855  *  (that is, anything between *pj and *pi has value of pivot)
856  *====================================*/
857 #define CMP(qa, qb) ((*cmp)(qa, qb, param))
858 static void
partition2(SORTEL * arr,ELCMPFNC cmp,VPTR param,INT a,INT b,INT * pi,INT * pj)859 partition2 (SORTEL *arr, ELCMPFNC cmp, VPTR param, INT a, INT b, INT *pi, INT *pj)
860 {
861 	INT i, j, c;
862 	SORTEL pivot=0, t;
863 
864 	/* sort left, middle, and right items */
865 	c = ((unsigned)a + (unsigned)b)/2;
866 
867 	if (CMP(arr[a],arr[c]) > 0) {
868 		t = arr[a]; arr[a] = arr[c]; arr[c] = t;
869 	}
870 	if (CMP(arr[a],arr[b]) > 0) {
871 		t = arr[a]; arr[a] = arr[b]; arr[b] = t;
872 	}
873 	if (CMP(arr[c],arr[b]) > 0) {
874 		t = arr[c]; arr[c] = arr[b]; arr[b] = t;
875 	}
876 
877 	/* tuck the pivot, at arr[c], away */
878 	pivot = arr[c]; arr[c] = arr[b-1]; arr[b-1] = pivot;
879 	i = a; j = b-1;
880 
881 	/* swap up & down interval to actually partition it */
882 	while (1) {
883 		/* shrink interval from bottom while elements below pivot */
884 		while (CMP(arr[++i], pivot) < 0)
885 			;
886 		/* shrink interval from top while elements above pivot */
887 		while (CMP(arr[--j], pivot) > 0)
888 			;
889 		if (j < i) break;
890 		/* have out-of-place elements that stopped climb & descent */
891 		t = arr[i]; arr[i] = arr[j]; arr[j] = t;
892 	}
893 
894 	/* put pivot into final resting place */
895 	pivot = arr[i]; arr[i] = arr[b-1]; arr[b-1] = pivot;
896 
897 	/* give caller the partition info */
898 	*pi = i; *pj = j;
899 }
900 static void
llqsort2(SORTEL * data,ELCMPFNC cmp,VPTR param,INT a,INT b)901 llqsort2 (SORTEL *data, ELCMPFNC cmp, VPTR param, INT a, INT b)
902 {
903 	INT stack[64];
904 	INT i, j, top;
905 
906 	top = 0;
907 
908 	while (1) {
909 		while (b > a+1) {
910 			partition2(data, cmp, param, a, b, &i, &j);
911 			/* loop to simulate tail recursion on smaller interval */
912 			if (j-a > b-i) {
913 				/* (i+1,b) is shorter interval than (a,j) */
914 				/* do (i+1, b) now, and push (a,j) */
915 				stack[top++] = a; stack[top++] = j;
916 				a=i+1;
917 			} else {
918 				/* (a,j) is shorter interval than (i+1,b) */
919 				/* do (a,j) now, and push (i+1,b) */
920 				stack[top++] = i+1; stack[top++] = b;
921 				b = j;
922 			}
923 		}
924 		if (b > a) {
925 			/* swap data[a] and data[b] if needed */
926 			if (CMP(data[a], data[b])>0) {
927 				SORTEL t = data[a]; data[a] = data[b]; data[b] = t;
928 			}
929 		}
930 		if (top == 0) break;
931 		/* pop & do whatever is on top of our stack */
932 		b = stack[--top]; a = stack[--top];
933 	}
934 }
935 /*==================================================================
936  * unique_indiseq -- Remove identical (key, name) els from sequence
937  * NOTE: this routine has MEMORY LEAK -- it doesn't free storage for
938  *   els removed from sequence
939  *================================================================*/
940 void
unique_indiseq(INDISEQ seq)941 unique_indiseq (INDISEQ seq)
942 {
943 	INT i, j, n;
944 	SORTEL *d;
945 	if (!seq) return;
946 	n = ISize(seq);
947 	d = IData(seq);
948 	if (n == 0 || (IFlags(seq) & UNIQUED)) return;
949 	if (!(IFlags(seq) & KEYSORT)) keysort_indiseq(seq);
950 	for (j = 0, i = 1; i < n; i++)
951 		if (spri(d[i]) != spri(d[j])) {
952 			d[++j] = d[i];
953 		} else {
954 			/* TO DO - this is untested - Perry 2001/03/25 */
955 			delete_el(seq, d[i]);
956 		}
957 	ISize(seq) = j + 1;
958 	IFlags(seq) |= UNIQUED;
959 }
960 /*================================================
961  * get_combined_valtype -- What valtype should the
962  *  new, combined seq be ?
963  * Created: 2001/01/07, Perry Rapp
964  *==============================================*/
965 static INT
get_combined_valtype(INDISEQ one,INDISEQ two)966 get_combined_valtype (INDISEQ one, INDISEQ two)
967 {
968 	if (length_indiseq(one) && IValtype(one) != ISVAL_NUL) {
969 		if (length_indiseq(two) && IValtype(two) != ISVAL_NUL) {
970 			ASSERT(IValtype(one) == IValtype(two));
971 			return IValtype(one);
972 		} else {
973 			return IValtype(one);
974 		}
975 	} else {
976 		return IValtype(two);
977 	}
978 }
979 /*===============================================
980  * dupseq -- Return a duplicate of input sequence
981  * Created:  2001/04/09, Perry Rapp
982  * copies values from input seq using copyval
983  *=============================================*/
984 static INDISEQ
dupseq(INDISEQ seq)985 dupseq (INDISEQ seq)
986 {
987 	INDISEQ newseq;
988 	STRING key;
989 	UNION uval;
990 	SORTEL *u;
991 	INT i,n;
992 
993 	if (!seq)
994 		return NULL;
995 
996 	/* Create New Sequence */
997 	newseq = create_indiseq_impl(IValtype(seq), IValfnctbl(seq));
998 	u = IData(seq);
999 	n = length_indiseq(seq);
1000 
1001 	/* Copy Every Element */
1002 	for (i=0; i<n; i++) {
1003 			key = strsave(skey(u[i]));
1004 			/* indiseq values must be copied with copyval */
1005 			uval = copyval(seq, sval(u[i]));
1006 			append_indiseq_impl(newseq, key, NULL, uval,
1007 			    TRUE, TRUE);
1008 	}
1009 	return newseq;
1010 }
1011 /*===============================================
1012  * union_indiseq -- Create union of two sequences
1013  * copies values from appropriate input (taking
1014  *  from "one" if in both) using copyval
1015  *=============================================*/
1016 INDISEQ
union_indiseq(INDISEQ one,INDISEQ two)1017 union_indiseq (INDISEQ one, INDISEQ two)
1018 {
1019 	INT n, m, i, j, rel;
1020 	STRING key;
1021 	INDISEQ three;
1022 	SORTEL *u, *v;
1023 	INT valtype;
1024 	UNION uval;
1025 	if (!one && !two) return NULL;
1026 	if (!one)
1027 		return dupseq(two);
1028 	if (!two)
1029 		return dupseq(one);
1030 	if (!(IFlags(one) & KEYSORT)) keysort_indiseq(one);
1031 	if (!(IFlags(one) & UNIQUED)) unique_indiseq(one);
1032 	if (!(IFlags(two) & KEYSORT)) keysort_indiseq(two);
1033 	if (!(IFlags(two) & UNIQUED)) unique_indiseq(two);
1034 	n = length_indiseq(one);
1035 	m = length_indiseq(two);
1036 	valtype = get_combined_valtype(one, two);
1037 	three = create_indiseq_impl(valtype, IValfnctbl(one));
1038 	i = j = 0;
1039 	u = IData(one);
1040 	v = IData(two);
1041 	while (i < n && j < m) {
1042 		if ((rel = spri(u[i]) - spri(v[j])) < 0) {
1043 			key = strsave(skey(u[i]));
1044 			/* indiseq values must be copied with copyval */
1045 			uval = copyval(one, sval(u[i]));
1046 			append_indiseq_impl(three, key, NULL, uval,
1047 			    TRUE, TRUE);
1048 			i++;
1049 		} else if (rel > 0) {
1050 			key = strsave(skey(v[j]));
1051 			/* indiseq values must be copied with copyval */
1052 			uval = copyval(two, sval(v[j]));
1053 			append_indiseq_impl(three, key, NULL, uval,
1054 			    TRUE, TRUE);
1055 			j++;
1056 		} else { /* in both, copy value from one */
1057 			key = strsave(skey(u[i]));
1058 			/* indiseq values must be copied with copyval */
1059 			uval = copyval(one, sval(u[i]));
1060 			append_indiseq_impl(three, key, NULL, uval,
1061 			    TRUE, TRUE);
1062 			i++, j++;
1063 		}
1064 	}
1065 	while (i < n) {
1066 		key = strsave(skey(u[i]));
1067 		/* indiseq values must be copied with copyval */
1068 		uval = copyval(one, sval(u[i]));
1069 		append_indiseq_impl(three, key, NULL, uval, TRUE, TRUE);
1070 		i++;
1071 	}
1072 	while (j < m) {
1073 		key = strsave(skey(v[j]));
1074 		/* indiseq values must be copied with copyval */
1075 		uval = copyval(two, sval(v[j]));
1076 		append_indiseq_impl(three, key, NULL, uval, TRUE, TRUE);
1077 		j++;
1078 	}
1079 	FORINDISEQ(three, el, num)
1080 		spri(el) = atoi(skey(el) + 1);
1081 	ENDINDISEQ
1082 	IFlags(three) = KEYSORT|UNIQUED;
1083 	return three;
1084 }
1085 /*==========================================================
1086  * intersect_indiseq -- Create intersection of two sequences
1087  * copies values from input "one" using copyval
1088  *========================================================*/
1089 INDISEQ
intersect_indiseq(INDISEQ one,INDISEQ two)1090 intersect_indiseq (INDISEQ one, INDISEQ two)
1091 {
1092 	INT n, m, i, j, rel;
1093 	STRING key;
1094 	INDISEQ three;
1095 	SORTEL *u, *v;
1096 	INT valtype;
1097 	UNION uval;
1098 	if (!one || !two) return NULL;
1099 	if (!(IFlags(one) & KEYSORT)) keysort_indiseq(one);
1100 	if (!(IFlags(one) & UNIQUED)) unique_indiseq(one);
1101 	if (!(IFlags(two) & KEYSORT)) keysort_indiseq(two);
1102 	if (!(IFlags(two) & UNIQUED)) unique_indiseq(two);
1103 	n = length_indiseq(one);
1104 	m = length_indiseq(two);
1105 	valtype = get_combined_valtype(one, two);
1106 	three = create_indiseq_impl(valtype, IValfnctbl(one));
1107 	i = j = 0;
1108 	u = IData(one);
1109 	v = IData(two);
1110 	while (i < n && j < m) {
1111 		if ((rel = spri(u[i]) - spri(v[j])) < 0) {
1112 			i++;
1113 		} else if (rel > 0) {
1114 			j++;
1115 		} else {
1116 			key = strsave(skey(u[i]));
1117 			/* indiseq values must be copied with copyval */
1118 			uval = copyval(one, sval(u[i]));
1119 			append_indiseq_impl(three, key, NULL, uval,TRUE, TRUE);
1120 			i++, j++;
1121 		}
1122 	}
1123 	FORINDISEQ(three, el, num)
1124 		spri(el) = atoi(skey(el) + 1);
1125 	ENDINDISEQ
1126 	IFlags(three) = KEYSORT|UNIQUED;
1127 	return three;
1128 }
1129 /*=========================================================
1130  * difference_indiseq -- Create difference of two sequences
1131  * copies values from original seq using copyval
1132  *=======================================================*/
1133 INDISEQ
difference_indiseq(INDISEQ one,INDISEQ two)1134 difference_indiseq (INDISEQ one, INDISEQ two)
1135 {
1136 	INT n, m, i, j, rel;
1137 	STRING key;
1138 	INDISEQ three;
1139 	SORTEL *u, *v;
1140 	INT valtype;
1141 	UNION uval;
1142 	if (!one)
1143 		return NULL;
1144 	if (!two)
1145 		return dupseq(one);
1146 	if (!(IFlags(one) & KEYSORT)) keysort_indiseq(one);
1147 	if (!(IFlags(one) & UNIQUED)) unique_indiseq(one);
1148 	if (!(IFlags(two) & KEYSORT)) keysort_indiseq(two);
1149 	if (!(IFlags(two) & UNIQUED)) unique_indiseq(two);
1150 	n = length_indiseq(one);
1151 	m = length_indiseq(two);
1152 	valtype = get_combined_valtype(one, two);
1153 	three = create_indiseq_impl(valtype, IValfnctbl(one));
1154 	i = j = 0;
1155 	u = IData(one);
1156 	v = IData(two);
1157 	while (i < n && j < m) {
1158 		if ((rel = spri(u[i]) - spri(v[j])) < 0) {
1159 			key = strsave(skey(u[i]));
1160 			/* indiseq values must be copied with copyval */
1161 			uval = copyval(one, sval(u[i]));
1162 			append_indiseq_impl(three, key, NULL, uval, TRUE, TRUE);
1163 			i++;
1164 		} else if (rel > 0) {
1165 			j++;
1166 		} else {
1167 			i++, j++;
1168 		}
1169 	}
1170 	while (i < n) {
1171 		key = strsave(skey(u[i]));
1172 		/* indiseq values must be copied with copyval */
1173 		uval = copyval(one, sval(u[i]));
1174 		append_indiseq_impl(three, key, NULL, uval, TRUE, TRUE);
1175 		i++;
1176 	}
1177 	FORINDISEQ(three, el, num)
1178 		spri(el) = atoi(skey(el) + 1);
1179 	ENDINDISEQ
1180 	IFlags(three) = KEYSORT|UNIQUED;
1181 	return three;
1182 }
1183 /*=====================================================
1184  * parent_indiseq -- Create parent sequence of sequence
1185  * copies values from original seq using copyval
1186  *===================================================*/
1187 INDISEQ
parent_indiseq(INDISEQ seq)1188 parent_indiseq (INDISEQ seq)
1189 {
1190 	TABLE tab=0; /* table of people inserted (values not used) */
1191 	INDISEQ par=0;
1192 	NODE indi=0;
1193 	INT fnum=0, snum=0;
1194 	STRING key=0;
1195 	UNION uval;
1196 	if (!seq) return NULL;
1197 	tab = create_table_vptr();
1198 	par = create_indiseq_impl(IValtype(seq), IValfnctbl(seq));
1199 	FORINDISEQ(seq, el, num)
1200 		indi = key_to_indi(skey(el));
1201 		FORFAMCS(indi, fam, fath, moth, fnum)
1202 			FORFAMSPOUSES(fam, spouse, snum)
1203 				if (spouse && !in_table(tab, key = indi_to_key(spouse))) {
1204 					/* indiseq values must be copied with copyval */
1205 					uval = copyval(seq, sval(el));
1206 					key = strsave(key);
1207 					append_indiseq_impl(par, key, NULL, uval, TRUE, TRUE);
1208 					insert_table_ptr(tab, key, 0);
1209 				}
1210 			ENDFAMSPOUSES
1211 		ENDFAMCS
1212 	ENDINDISEQ
1213 	destroy_table(tab);
1214 	return par;
1215 }
1216 /*======================================================
1217  * child_indiseq -- Create children sequence of sequence
1218  * copies values from original seq using copyval
1219  *====================================================*/
1220 INDISEQ
child_indiseq(INDISEQ seq)1221 child_indiseq (INDISEQ seq)
1222 {
1223 	INT num1, num2;
1224 	TABLE tab=0; /* table of people already inserted (values not used) */
1225 	INDISEQ cseq=0;
1226 	NODE indi=0;
1227 	STRING key=0;
1228 	UNION uval;
1229 	if (!seq) return NULL;
1230 	tab = create_table_vptr();
1231 	cseq = create_indiseq_impl(IValtype(seq), IValfnctbl(seq));
1232 	FORINDISEQ(seq, el, num)
1233 		indi = key_to_indi(skey(el));
1234 		FORFAMS(indi, fam, num1)
1235 			FORCHILDRENx(fam, chil, num2)
1236 				key = indi_to_key(chil);
1237 				if (!in_table(tab, key)) {
1238 					key = strsave(key);
1239 					/* indiseq values must be copied with copyval */
1240 					uval = copyval(seq, sval(el));
1241 					append_indiseq_impl(cseq, key, NULL, uval, TRUE, TRUE);
1242 					insert_table_ptr(tab, key, 0);
1243 				}
1244 			ENDCHILDRENx
1245 		ENDFAMS
1246 	ENDINDISEQ
1247 	destroy_table(tab);
1248 	return cseq;
1249 }
1250 /*=========================================================
1251  * indi_to_children -- Create sequence of person's children
1252  *  (filters out duplicates)
1253  *=======================================================*/
1254 INDISEQ
indi_to_children(NODE indi)1255 indi_to_children (NODE indi)
1256 {
1257 	INDISEQ seq;
1258 	INT num1, num2, len = 0;
1259 	STRING key;
1260 	if (!indi) return NULL;
1261 	seq = create_indiseq_null();
1262 	FORFAMS(indi, fam, num1)
1263 		FORCHILDRENx(fam, chil, num2)
1264 			len++;
1265 			key = indi_to_key(chil);
1266 			append_indiseq_null(seq, key, NULL, FALSE, FALSE);
1267 		ENDCHILDRENx
1268 	ENDFAMS
1269 	if (len) return seq;
1270 	remove_indiseq(seq);
1271 	return NULL;
1272 }
1273 /*=======================================================
1274  * indi_to_spouses -- Create sequence of person's spouses
1275  *  values will be family keynums
1276  * This seq will be ISPRN_SPOUSESEQ style, meaning
1277  *  marriage dates are included with names in print strings
1278  *=====================================================*/
1279 INDISEQ
indi_to_spouses(NODE indi)1280 indi_to_spouses (NODE indi)
1281 {
1282 	INDISEQ seq=0;
1283 	INT num, num1, val, len = 0;
1284 	STRING key=0;
1285 	if (!indi) return NULL;
1286 	seq = create_indiseq_ival();
1287 	IPrntype(seq) = ISPRN_SPOUSESEQ;
1288 	FORFAMS(indi, fam, num)
1289 		FORFAMSPOUSES(fam, spouse, num1)
1290 			if (spouse != indi) {
1291 				len++;
1292 				key = indi_to_key(spouse);
1293 				val = atoi(fam_to_key(fam) + 1);
1294 				append_indiseq_ival(seq, key, NULL, val, TRUE, FALSE);
1295 			}
1296 		ENDFAMSPOUSES
1297 	ENDFAMS
1298 	if (!len) {
1299 		remove_indiseq(seq);
1300 		seq=NULL;
1301 	}
1302 	return seq;
1303 }
1304 /*=======================================================
1305  * fam_to_spouses -- Create sequence of spouses of family
1306  *  (null values)
1307  *=====================================================*/
1308 INDISEQ
fam_to_spouses(NODE fam)1309 fam_to_spouses (NODE fam)
1310 {
1311 	INDISEQ seq=0;
1312 	INT num1, len = 0;
1313 	STRING key=0;
1314 	if (!fam) return NULL;
1315 	seq = create_indiseq_ival();
1316 	FORFAMSPOUSES(fam, spouse, num1)
1317 		len++;
1318 		key = indi_to_key(spouse);
1319 		append_indiseq_null(seq, key, NULL, TRUE, FALSE);
1320 	ENDFAMSPOUSES
1321 	if (!len) {
1322 		remove_indiseq(seq);
1323 		seq=NULL;
1324 	}
1325 	return seq;
1326 }
1327 /*=======================================================
1328  * indi_to_fathers -- Create sequence of person's fathers
1329  *=====================================================*/
1330 INDISEQ
indi_to_fathers(NODE indi)1331 indi_to_fathers (NODE indi)
1332 {
1333 	INDISEQ seq;
1334 	INT num1, num2, len = 0;
1335 	STRING key;
1336 	if (!indi) return NULL;
1337 	seq = create_indiseq_null();
1338 	FORFAMCS(indi, fam, fath, moth, num1)
1339 		FORHUSBS(fam, husb, num2)
1340 			len++;
1341 			key = indi_to_key(husb);
1342 			append_indiseq_null(seq, key, NULL, TRUE, FALSE);
1343 		ENDHUSBS
1344 	ENDFAMCS
1345 	if (len) return seq;
1346 	remove_indiseq(seq);
1347 	return NULL;
1348 }
1349 /*=======================================================
1350  * indi_to_mothers -- Create sequence of person's mothers
1351  *=====================================================*/
1352 INDISEQ
indi_to_mothers(NODE indi)1353 indi_to_mothers (NODE indi)
1354 {
1355 	INDISEQ seq;
1356 	INT num1, num2, len = 0;
1357 	STRING key;
1358 	if (!indi) return NULL;
1359 	seq = create_indiseq_null();
1360 	FORFAMCS(indi, fam, fath, moth, num1)
1361 		FORWIFES(fam, wife, num2)
1362 			len++;
1363 			key = indi_to_key(wife);
1364 			append_indiseq_null(seq, key, NULL, TRUE, FALSE);
1365 		ENDWIFES
1366 	ENDFAMCS
1367 	if (len) return seq;
1368 	remove_indiseq(seq);
1369 	return NULL;
1370 }
1371 /*=========================================================
1372  * indi_to_families -- Create sequence of person's families
1373  *  values will be input person keynum
1374  *  (this is the ISPRN_FAMSEQ style)
1375  * indi: [IN]  find families of this person
1376  * fams: [IN]  if TRUE, find spousal families, else parental
1377  *=======================================================*/
1378 INDISEQ
indi_to_families(NODE indi,BOOLEAN fams)1379 indi_to_families (NODE indi, BOOLEAN fams)
1380 {
1381 	INDISEQ seq=0;
1382 	INT num, num2, val;
1383 	STRING key=0;
1384 	INT mykeynum=0;
1385 	if (!indi) return NULL;
1386 	mykeynum = atoi(indi_to_key(indi) + 1);
1387 	seq = create_indiseq_ival();
1388 	IPrntype(seq) = ISPRN_FAMSEQ;
1389 	if (fams) {
1390 		FORFAMS(indi, fam, num)
1391 		{
1392 			INT spkeynum=0;
1393 			STRING fkey = strsave(fam_to_key(fam));
1394 			/* look for a spouse besides indi */
1395 			FORFAMSPOUSES(fam, spouse, num2)
1396 			{
1397 				INT temp = atoi(indi_to_key(spouse) + 1);
1398 				if (temp && temp != mykeynum)
1399 					spkeynum = temp;
1400 			}
1401 			ENDFAMSPOUSES
1402 			append_indiseq_ival(seq, fkey, NULL, mykeynum, TRUE, FALSE);
1403 			strfree(&fkey);
1404 		}
1405 		ENDFAMS
1406 	} else {
1407 		FORFAMCS(indi, fam, fath, moth, num)
1408 			key = fam_to_key(fam);
1409 			val = fath ? atoi(indi_to_key(fath) + 1) : 0;
1410 			append_indiseq_ival(seq, key, NULL, val, TRUE, FALSE);
1411 		ENDFAMCS
1412 	}
1413 	if (num && ISize(seq)) return seq;
1414 	remove_indiseq(seq);
1415 	return NULL;
1416 }
1417 /*========================================================
1418  * fam_to_children -- Create sequence of family's children
1419  *======================================================*/
1420 INDISEQ
fam_to_children(NODE fam)1421 fam_to_children (NODE fam)
1422 {
1423 	INDISEQ seq;
1424 	INT num;
1425 	STRING key;
1426 	if (!fam) return NULL;
1427 	seq = create_indiseq_null();
1428 	FORCHILDRENx(fam, chil, num)
1429 		key = indi_to_key(chil);
1430 		append_indiseq_null(seq, key, NULL, TRUE, FALSE);
1431 	ENDCHILDRENx
1432 	if (num && ISize(seq)) return seq;
1433 	remove_indiseq(seq);
1434 	return NULL;
1435 }
1436 /*======================================================
1437  * fam_to_fathers -- Create sequence of family's fathers
1438  *====================================================*/
1439 INDISEQ
fam_to_fathers(NODE fam)1440 fam_to_fathers (NODE fam)
1441 {
1442 	INDISEQ seq;
1443 	INT num;
1444 	STRING key;
1445 	if (!fam) return NULL;
1446 	seq = create_indiseq_null();
1447 	FORHUSBS(fam, husb, num)
1448 		key = indi_to_key(husb);
1449 		append_indiseq_null(seq, key, NULL, TRUE, FALSE);
1450 	ENDHUSBS
1451 	if (num && ISize(seq)) return seq;
1452 	remove_indiseq(seq);
1453 	return NULL;
1454 }
1455 /*======================================================
1456  * fam_to_mothers -- Create sequence of family's mothers
1457  *====================================================*/
1458 INDISEQ
fam_to_mothers(NODE fam)1459 fam_to_mothers (NODE fam)
1460 {
1461 	INDISEQ seq;
1462 	INT num;
1463 	STRING key;
1464 	if (!fam) return NULL;
1465 	seq = create_indiseq_null();
1466 	FORWIFES(fam, wife, num)
1467 		key = indi_to_key(wife);
1468 		append_indiseq_null(seq, key, NULL, TRUE, FALSE);
1469 	ENDWIFES
1470 	if (num && ISize(seq)) return seq;
1471 	remove_indiseq(seq);
1472 	return NULL;
1473 }
1474 /*=========================================================
1475  * sibling_indiseq -- Create sibling sequence of a sequence
1476  * close: if TRUE, do not include duplicates
1477  *=======================================================*/
1478 INDISEQ
sibling_indiseq(INDISEQ seq,BOOLEAN close)1479 sibling_indiseq (INDISEQ seq, BOOLEAN close)
1480 {
1481 	INDISEQ fseq=0, sseq=0;
1482 	NODE indi=0, fam=0;
1483 	STRING key=0, fkey=0;
1484 	INT num2=0;
1485 	/* table lists people already listed (values unused) */
1486 	TABLE tab = create_table_vptr();
1487 	fseq = create_indiseq_null(); /* temporary */
1488 	sseq = create_indiseq_null();
1489 	FORINDISEQ(seq, el, num)
1490 		indi = key_to_indi(skey(el));
1491 		if ((fam = indi_to_famc(indi))) {
1492 			fkey = fam_to_key(fam);
1493 			append_indiseq_null(fseq, fkey, NULL, FALSE, FALSE);
1494 		}
1495 		if (!close) insert_table_ptr(tab, skey(el), 0);
1496 	ENDINDISEQ
1497 	FORINDISEQ(fseq, el, num)
1498 		fam = key_to_fam(skey(el));
1499 		FORCHILDRENx(fam, chil, num2)
1500 			key = indi_to_key(chil);
1501 			if (!in_table(tab, key)) {
1502 				key = strsave(key);
1503 				append_indiseq_null(sseq, key, NULL, TRUE, TRUE);
1504 				insert_table_ptr(tab, key, 0);
1505 			}
1506 		ENDCHILDRENx
1507 	ENDINDISEQ
1508 	destroy_table(tab);
1509 	remove_indiseq(fseq);
1510 	return sseq;
1511 }
1512 /*=========================================================
1513  * ancestor_indiseq -- Create ancestor sequence of sequence
1514  *  values are created with the generation number
1515  *  (via value function table)
1516  *=======================================================*/
1517 INDISEQ
ancestor_indiseq(INDISEQ seq)1518 ancestor_indiseq (INDISEQ seq)
1519 {
1520 	/* table lists people already listed (values unused) */
1521 	TABLE tab;
1522 	LIST anclist, genlist;
1523 	INDISEQ anc=0;
1524 	NODE indi=0;
1525 	STRING key, pkey;
1526 	INT gen=0;
1527 	INT fnum=0, snum=0;
1528 	UNION uval;
1529 	if (!seq) return NULL;
1530 		/* table of people already added */
1531 	tab = create_table_vptr();
1532 		/* paired processing list - see comments in descendant_indiseq code */
1533 	anclist = create_list();
1534 	genlist = create_list();
1535 	anc = create_indiseq_impl(IValtype(seq), IValfnctbl(seq));
1536 	FORINDISEQ(seq, el, num)
1537 		enqueue_list(anclist, (VPTR)skey(el));
1538 		enqueue_list(genlist, (VPTR)0);
1539 	ENDINDISEQ
1540 	while (!is_empty_list(anclist)) {
1541 		key = (STRING) dequeue_list(anclist);
1542 		gen = (INT) dequeue_list(genlist) + 1;
1543 		indi = key_to_indi(key);
1544 
1545 		FORFAMCS(indi, fam, fath, moth, fnum)
1546 			FORFAMSPOUSES(fam, spouse, snum)
1547 				if (spouse && !in_table(tab, pkey = indi_to_key(spouse))) {
1548 						/* copy key for list, others make their own copies */
1549 					uval = creategenval(seq, gen);
1550 					append_indiseq_pval(anc, pkey, NULL, uval.w, TRUE);
1551 					enqueue_list(anclist, (VPTR)strsave(pkey));
1552 					enqueue_list(genlist, (VPTR)gen);
1553 					insert_table_ptr(tab, pkey, 0);
1554 				}
1555 			ENDFAMSPOUSES
1556 		ENDFAMCS
1557 	}
1558 	destroy_table(tab);
1559 	destroy_empty_list(anclist);
1560 	destroy_empty_list(genlist);
1561 	return anc;
1562 }
1563 /*=============================================================
1564  * descendant_indiseq -- Create descendant sequence of sequence
1565  *  values are created with the generation number
1566  *  (passed to create_value callback)
1567  *===========================================================*/
1568 INDISEQ
descendent_indiseq(INDISEQ seq)1569 descendent_indiseq (INDISEQ seq)
1570 {
1571 	INT gen;
1572 	/* itab lists people already entered, ftab families
1573 	(values in both are unused) */
1574 	TABLE itab, ftab;
1575 	LIST deslist, genlist;
1576 	INDISEQ des;
1577 	NODE indi;
1578 	STRING key, dkey, fkey;
1579 	UNION uval;
1580 	if (!seq) return NULL;
1581 		/* itab = people already added, value irrelevant */
1582 	itab = create_table_vptr();
1583 		/* ftab = families already added (processed), value irrelevant */
1584 	ftab = create_table_vptr();
1585 		/*
1586 		deslist & genlist are paired -
1587 		dequeue the person from deslist & the generation
1588 		from genlist, for one person to be processed
1589 		(added to result & all children added to processing list)
1590 		(deslist does not own its strings)
1591 		*/
1592 	deslist = create_list();
1593 	genlist = create_list();
1594 		/* result indiseq */
1595 	des = create_indiseq_impl(IValtype(seq), IValfnctbl(seq));
1596 		/* add everyone from original seq to processing list */
1597 	FORINDISEQ(seq, el, num)
1598 		enqueue_list(deslist, (VPTR)skey(el));
1599 		enqueue_list(genlist, (VPTR)0);
1600 	ENDINDISEQ
1601 		/* loop until processing list is empty */
1602 	while (!is_empty_list(deslist)) {
1603 		INT num1, num2;
1604 		key = (STRING) dequeue_list(deslist);
1605 		gen = (INT) dequeue_list(genlist) + 1;
1606 		indi = key_to_indi(key);
1607 		FORFAMS(indi, fam, num1)
1608 				/* skip families already processed */
1609 			if (in_table(ftab, fkey = fam_to_key(fam)))
1610 				goto a;
1611 			insert_table_ptr(ftab, fkey, 0);
1612 			FORCHILDRENx(fam, child, num2)
1613 					/* only do people not processed */
1614 				if (!in_table(itab,
1615 				    dkey = indi_to_key(child))) {
1616 						/* copy key for list, others make their own copies */
1617 					uval = creategenval(seq, gen);
1618 						/* add person to output */
1619 					append_indiseq_pval(des, strsave(dkey), NULL, uval.w, TRUE);
1620 						/* also want descendants, so add person to processing list */
1621 					enqueue_list(deslist, (VPTR)strsave(dkey));
1622 					enqueue_list(genlist, (VPTR)gen);
1623 					insert_table_ptr(itab, dkey, 0);
1624 				}
1625 			ENDCHILDRENx
1626 		a:;
1627 		ENDFAMS
1628 	}
1629 	destroy_table(itab);
1630 	destroy_table(ftab);
1631 	return des;
1632 }
1633 /*========================================================
1634  * spouse_indiseq -- Create spouses sequence of a sequence
1635  *======================================================*/
1636 INDISEQ
spouse_indiseq(INDISEQ seq)1637 spouse_indiseq (INDISEQ seq)
1638 {
1639 	TABLE tab;
1640 	INDISEQ sps;
1641 	STRING spkey;
1642 	NODE indi;
1643 	INT num1;
1644 	if (!seq) return NULL;
1645 	tab = create_table_vptr();
1646 	sps = create_indiseq_impl(IValtype(seq), IValfnctbl(seq));
1647 	FORINDISEQ(seq, el, num)
1648 		indi = key_to_indi(skey(el));
1649 		FORSPOUSES(indi, spouse, fam, num1)
1650 			spkey = indi_to_key(spouse);
1651 			if (!in_table(tab, spkey)) {
1652 				UNION u;
1653 					/* key copy for seq & list - owned by seq */
1654 				spkey = strsave(spkey);
1655 				u = copyval(seq, sval(el));
1656 				append_indiseq_impl(sps, spkey, NULL, u, TRUE, TRUE);
1657 				insert_table_ptr(tab, spkey, 0);
1658 			}
1659 		ENDSPOUSES
1660 	ENDINDISEQ
1661 	destroy_table(tab);
1662 	return sps;
1663 }
1664 /*============================================================
1665  * name_to_indiseq -- Return person sequence whose names match
1666  *==========================================================*/
1667 INDISEQ
name_to_indiseq(STRING name)1668 name_to_indiseq (STRING name)
1669 {
1670 	INDISEQ seq = NULL;
1671 	LIST list=0;
1672 	if (!name || *name == 0) return NULL;
1673 
1674 	list = find_indis_by_name(name);
1675 	ASSERT(list);
1676 	if (length_list(list)) {
1677 		LIST_ITER listit=0;
1678 		VPTR ptr=0;
1679 		/* add all entries from list to sequence */
1680 		seq = create_indiseq_null();
1681 		listit = begin_list(list);
1682 		while (next_list_ptr(listit, &ptr)) {
1683 			CNSTRING key = (CNSTRING)ptr;
1684 			/* skip dupe check, because we do unique_indiseq below */
1685 			append_indiseq_null(seq, strsave(key), NULL, TRUE, TRUE);
1686 		}
1687 		end_list_iter(&listit);
1688 		/* may have duplicates from multiple soundexes or wildcard searches */
1689 		unique_indiseq(seq);
1690 		/* people like to see lists alphabetically by name */
1691 		namesort_indiseq(seq);
1692 	}
1693 	destroy_list(list);
1694 	return seq;
1695 }
1696 /*===========================================
1697  * generic_print_el -- Format a print line of
1698  *  sequence of indis
1699  *  returns heap-alloc'd string
1700  *
1701  *  seq:  [in] sequence containing desired element
1702  *  i:    [in] index of desired element
1703  *  len:  [in] max width description desired
1704  *  rfmt: [in] reformatting information
1705  *=========================================*/
1706 static STRING
generic_print_el(INDISEQ seq,INT i,INT len,RFMT rfmt)1707 generic_print_el (INDISEQ seq, INT i, INT len, RFMT rfmt)
1708 {
1709 	STRING key, name;
1710 	element_indiseq(seq, i, &key, &name);
1711 	return generic_to_list_string(NULL, key, len, ", ", rfmt, TRUE);
1712 }
1713 /*=============================================
1714  * spouseseq_print_el -- Format a print line of
1715  *  sequence of spouses
1716  * assume values are family keys
1717  *===========================================*/
1718 static STRING
spouseseq_print_el(INDISEQ seq,INT i,INT len,RFMT rfmt)1719 spouseseq_print_el (INDISEQ seq, INT i, INT len, RFMT rfmt)
1720 {
1721 	NODE indi, fam;
1722 	STRING key, name, str;
1723 	INT val;
1724 	element_indiseq_ival(seq, i, &key, &val, &name);
1725 	indi = key_to_indi(key);
1726 	fam = keynum_to_fam(val);
1727 	str = indi_to_list_string(indi, fam, len, rfmt, TRUE);
1728 	return str;
1729 }
1730 /*==========================================
1731  * famseq_print_el -- Format a print line of
1732  *  sequence of families
1733  * assume values are spouse keys
1734  *========================================*/
1735 static STRING
famseq_print_el(INDISEQ seq,INT i,INT len,RFMT rfmt)1736 famseq_print_el (INDISEQ seq, INT i, INT len, RFMT rfmt)
1737 {
1738 	NODE fam=0, spouse=0;
1739 	STRING key=0, name=0, str=0;
1740 	INT val=0, num1=0;
1741 	INT spkeynum=0;
1742 	INDISEQ spseq = create_indiseq_null();
1743 	NODE indi=0;
1744 	ZSTR zstr = zs_newn(len);
1745 
1746 	element_indiseq_ival(seq, i, &key, &val, &name);
1747 	fam = key_to_fam(key);
1748 
1749 	/* build sequence of other spouses in this family */
1750 	FORFAMSPOUSES(fam, spouse, num1)
1751 		len++;
1752 		key = indi_to_key(spouse);
1753 		spkeynum = atoi(key + 1);
1754 		if (val != spkeynum) {
1755 			append_indiseq_null(spseq, key, NULL, TRUE, FALSE);
1756 		}
1757 	ENDFAMSPOUSES
1758 
1759 	/* build string list of spouses */
1760 	FORINDISEQ(spseq, el, num)
1761 		indi = key_to_indi(skey(el));
1762 		str = indi_to_list_string(indi, fam, len/length_indiseq(spseq), rfmt, TRUE);
1763 		if (zs_len(zstr)) zs_apps(zstr, ", ");
1764 		zs_apps(zstr, str);
1765 		strfree(&str);
1766 	ENDINDISEQ
1767 
1768 	/* make heap string to return */
1769 	str = strdup(zs_str(zstr));
1770 	zs_free(&zstr);
1771 	return str;
1772 }
1773 /*================================================
1774  * get_print_el -- Get appropriate format line for
1775  *  one element of an indiseq
1776  *==============================================*/
1777 static STRING
get_print_el(INDISEQ seq,INT i,INT len,RFMT rfmt)1778 get_print_el (INDISEQ seq, INT i, INT len, RFMT rfmt)
1779 {
1780 	STRING str;
1781 	switch(IPrntype(seq)) {
1782 	case ISPRN_FAMSEQ: str = famseq_print_el(seq, i, len, rfmt); break;
1783 	case ISPRN_SPOUSESEQ: str = spouseseq_print_el(seq, i, len, rfmt); break;
1784 	default: str = generic_print_el(seq, i, len, rfmt); break;
1785 	}
1786 	return str;
1787 }
1788 /*================================================
1789  * print_indiseq_element -- Format a print line of
1790  *  an indiseq (any type)
1791  *
1792  *  seq:  [in] indiseq of interest
1793  *  i:    [in] index of desired element
1794  *  buf:  [out] buffer to which to print description
1795  *  len:  [in] max length of buffer
1796  *  rfmt: [in] reformatting info
1797  *==============================================*/
1798 void
print_indiseq_element(INDISEQ seq,INT i,STRING buf,INT len,RFMT rfmt)1799 print_indiseq_element (INDISEQ seq, INT i, STRING buf, INT len, RFMT rfmt)
1800 {
1801 	STRING str, ptr=buf;
1802 	BOOLEAN alloc=FALSE;
1803 	buf[0]='\0';
1804 	str = sprn(IData(seq)[i]);
1805 	if (!str) {
1806 		/*
1807 		 If not precomputed, make print string on-the-fly .
1808 		 This is used for long seqs, when we don't want to keep
1809 		 all these strings in memory all the time.
1810 		 *
1811 		 Note: The print_el functions return a strsave'd string.
1812 		 It would be more efficient not to strsave. This requires
1813 		 changing indi_to_list_string, etc.
1814 		*/
1815 		str = get_print_el(seq, i, len-1, rfmt);
1816 		alloc=TRUE;
1817 	}
1818 	llstrcatn(&ptr, str, &len);
1819 	if (alloc)
1820 		stdfree(str);
1821 }
1822 /*=====================================================
1823  * preprint_indiseq -- Preformat print lines of indiseq
1824  *  seq:  [in] sequence to prepare (for display)
1825  *  len:  [in] max line width desired
1826  *  rfmt: [in] reformatting info
1827  *===================================================*/
1828 void
preprint_indiseq(INDISEQ seq,INT len,RFMT rfmt)1829 preprint_indiseq (INDISEQ seq, INT len, RFMT rfmt)
1830 {
1831 	FORINDISEQ(seq, el, num)
1832 		sprn(el) = get_print_el(seq, num, len, rfmt);
1833 	ENDINDISEQ
1834 }
1835 /*==============================================================
1836  * refn_to_indiseq -- Return indiseq whose user references match
1837  *============================================================*/
1838 INDISEQ
refn_to_indiseq(STRING ukey,INT letr,INT sort)1839 refn_to_indiseq (STRING ukey, INT letr, INT sort)
1840 {
1841 	STRING *keys;
1842 	INT num, i;
1843 	INDISEQ seq;
1844 
1845 	if (!ukey || *ukey == 0) return NULL;
1846 	get_refns(ukey, &num, &keys, letr);
1847 	if (num == 0) return NULL;
1848 	seq = create_indiseq_null();
1849 	for (i = 0; i < num; i++) {
1850 		append_indiseq_null(seq, keys[i], NULL, FALSE, FALSE);
1851 	}
1852 	if (sort == NAMESORT)
1853 		namesort_indiseq(seq);
1854 	else
1855 		keysort_indiseq(seq);
1856 	return seq;
1857 }
1858 /*=============================================================
1859  * key_to_indiseq -- Return person sequence of the matching key
1860  *  name:  [IN]  name to search for
1861  *  ctype: [IN]  type of record (eg, 'I') (0 for any)
1862  * returns NULL if name is empty or not found
1863  *=============================================================*/
1864 INDISEQ
key_to_indiseq(STRING name,char ctype)1865 key_to_indiseq (STRING name, char ctype)
1866 {
1867 	STRING key=0;
1868 	INDISEQ seq = NULL;
1869 	RECORD rec=0;
1870 	if (!name) return NULL;
1871 	rec = id_by_key(name, ctype);
1872 	if (!rec) return NULL;
1873 	key = rmvat(nxref(nztop(rec)));
1874 	release_record(rec); /* done with record */
1875 	seq = create_indiseq_null();
1876 	append_indiseq_null(seq, key, NULL, FALSE, FALSE);
1877 	return seq;
1878 }
1879 /*=============================================================
1880  * rec_to_indiseq -- Return new sequence containing specified record
1881  *  rec:  [IN]  record to put in list
1882  * returns NULL if record is null
1883  *=============================================================*/
1884 #ifdef UNUSED_CODE
1885 INDISEQ
rec_to_indiseq(RECORD rec)1886 rec_to_indiseq (RECORD rec)
1887 {
1888 	STRING key=0;
1889 	INDISEQ seq = NULL;
1890 	if (!rec) return NULL;
1891 	key = rmvat(nxref(nztop(rec)));
1892 	seq = create_indiseq_null();
1893 	append_indiseq_null(seq, key, NULL, FALSE, FALSE);
1894 	return seq;
1895 }
1896 #endif /* UNUSED CODE */
1897 /*===========================================================
1898  * str_to_indiseq -- Return person sequence matching a string
1899  *  name:  [IN]  name to search for
1900  *  ctype: [IN]  type of record (eg, 'I') (0 for any)
1901  * The rules of search precedence are implemented here:
1902  *  1. named indiset
1903  *  2. key, with or without the leading "I"
1904  *  3. REFN
1905  *  4. name
1906  * Returned indiseq is null type
1907  *===========================================================*/
1908 INDISEQ
str_to_indiseq(STRING name,char ctype)1909 str_to_indiseq (STRING name, char ctype)
1910 {
1911 	INDISEQ seq;
1912 	/* 'B' is a code meaning try persons first, but allow any type */
1913 	char cmatch = (ctype == 'B' ? 0 : ctype);
1914 	seq = find_named_seq(name);
1915 	if (!seq) seq = key_to_indiseq(name, cmatch);
1916 	if (!seq) seq = refn_to_indiseq(name, cmatch, NAMESORT);
1917 	if (!seq) seq = name_to_indiseq(name);
1918 	return seq;
1919 }
1920 /*=======================================================
1921  * append_all_tags -- append all tags of specified type
1922  *  to indiseq (optionally recursive
1923  * Created: 2000/11/29, Perry Rapp
1924  *=====================================================*/
1925 static void
append_all_tags(INDISEQ seq,NODE node,STRING tagname,BOOLEAN recurse,BOOLEAN nonptrs)1926 append_all_tags(INDISEQ seq, NODE node, STRING tagname
1927 	, BOOLEAN recurse, BOOLEAN nonptrs)
1928 {
1929 	if (!tagname || (ntag(node) && eqstr(ntag(node), tagname))) {
1930 		STRING key;
1931 		INT val=0;
1932 		key = nval(node);
1933 		if (key && key[0]) {
1934 			INT keylen = strlen(key);
1935 			STRING skey = 0;
1936 			BOOLEAN include=TRUE;
1937 			strupdate(&skey, rmvat(key));
1938 			if (skey) {
1939 				val = atoi(skey+1);
1940 			} else {
1941 				if (nonptrs) {
1942 					ZSTR zstr = zs_newn(keylen+100);
1943 					NODE chil;
1944 					/* include non-pointers, but mark invalid with val==-1 */
1945 					val = -1;
1946 					zs_sets(zstr, key);
1947 					/* collect any CONC or CONT children */
1948 					for (chil = nchild(node); chil; chil=nsibling(chil)) {
1949 						STRING text = nval(chil) ? nval(chil) : "";
1950 						BOOLEAN cr=FALSE;
1951 						if (eqstr_ex(ntag(chil), "CONC")) {
1952 						} else if (eqstr_ex(ntag(chil), "CONT")) {
1953 							cr=TRUE;
1954 						} else {
1955 							break;
1956 						}
1957 						if (cr)
1958 							zs_apps(zstr, "||");
1959 						zs_apps(zstr, text);
1960 					}
1961 					strupdate(&skey, zs_str(zstr));
1962 					zs_free(&zstr);
1963 				} else {
1964 					include=FALSE;
1965 				}
1966 			}
1967 			if (include) {
1968 				append_indiseq_ival(seq, skey, NULL, val, FALSE, TRUE);
1969 			}
1970 		}
1971 	}
1972 	if (nchild(node) && recurse )
1973 		append_all_tags(seq, nchild(node), tagname, recurse, nonptrs);
1974 	if (nsibling(node))
1975 		append_all_tags(seq, nsibling(node), tagname, recurse, nonptrs);
1976 
1977 }
1978 /*=======================================================
1979  * node_to_sources -- Create sequence of all sources
1980  *  inside a node record (at any level)
1981  * Created: 2000/11/29, Perry Rapp
1982  *=====================================================*/
1983 INDISEQ
node_to_sources(NODE node)1984 node_to_sources (NODE node)
1985 {
1986 	INDISEQ seq;
1987 	if (!node) return NULL;
1988 	seq = create_indiseq_ival();
1989 	append_all_tags(seq, node, "SOUR", TRUE, TRUE);
1990 	if (!length_indiseq(seq))
1991 	{
1992 		remove_indiseq(seq);
1993 		seq = NULL;
1994 	}
1995 	return seq;
1996 }
1997 /*=======================================================
1998  * node_to_notes -- Create sequence of all notes
1999  *  inside a node record (at any level)
2000  * 2001/02/11, Perry Rapp
2001  *=====================================================*/
2002 INDISEQ
node_to_notes(NODE node)2003 node_to_notes (NODE node)
2004 {
2005 	INDISEQ seq;
2006 	if (!node) return NULL;
2007 	seq = create_indiseq_ival();
2008 	append_all_tags(seq, node, "NOTE", TRUE, TRUE);
2009 	if (!length_indiseq(seq))
2010 	{
2011 		remove_indiseq(seq);
2012 		seq = NULL;
2013 	}
2014 	return seq;
2015 }
2016 /*=======================================================
2017  * node_to_pointers -- Create sequence of all pointers
2018  *  inside a node record (at any level)
2019  * 2001/02/24, Perry Rapp
2020  *=====================================================*/
2021 INDISEQ
node_to_pointers(NODE node)2022 node_to_pointers (NODE node)
2023 {
2024 	INDISEQ seq;
2025 	if (!node) return NULL;
2026 	seq = create_indiseq_ival();
2027 	append_all_tags(seq, node, NULL, TRUE, FALSE);
2028 	if (!length_indiseq(seq))
2029 	{
2030 		remove_indiseq(seq);
2031 		seq = NULL;
2032 	}
2033 	return seq;
2034 }
2035 /*=======================================================
2036  * get_all_sour -- Create sequence of all sources
2037  * Created: 2000/11/29, Perry Rapp
2038  *=====================================================*/
2039 INDISEQ
get_all_sour(void)2040 get_all_sour (void)
2041 {
2042 	INDISEQ seq=NULL;
2043 	int i=0;
2044 	while ((i=xref_nexts(i)))
2045 	{
2046 		static char skey[10];
2047 		if (!seq)
2048 			seq = create_indiseq_ival();
2049 		sprintf(skey, "S%d", i);
2050 		append_indiseq_ival(seq, skey, NULL, i, TRUE, FALSE);
2051 	}
2052 	return seq;
2053 }
2054 /*=======================================================
2055  * get_all_even -- Create sequence of all event records
2056  * Created: 2000/11/29, Perry Rapp
2057  *=====================================================*/
2058 INDISEQ
get_all_even(void)2059 get_all_even (void)
2060 {
2061 	INDISEQ seq=NULL;
2062 	INT i=0;
2063 	while ((i=xref_nexte(i)))
2064 	{
2065 		static char skey[10];
2066 		if (!seq)
2067 			seq = create_indiseq_ival();
2068 		sprintf(skey, "E%ld", i);
2069 		append_indiseq_ival(seq, skey, NULL, i, TRUE, FALSE);
2070 	}
2071 	return seq;
2072 }
2073 /*=======================================================
2074  * get_all_othe -- Create sequence of all other records
2075  * Created: 2000/11/29, Perry Rapp
2076  *=====================================================*/
2077 INDISEQ
get_all_othe(void)2078 get_all_othe (void)
2079 {
2080 	INDISEQ seq=NULL;
2081 	INT i=0;
2082 	while ((i=xref_nextx(i)))
2083 	{
2084 		static char skey[10];
2085 		if (!seq)
2086 			seq = create_indiseq_ival();
2087 		sprintf(skey, "X%ld", i);
2088 		append_indiseq_ival(seq, skey, NULL, i, TRUE, FALSE);
2089 	}
2090 	return seq;
2091 }
2092 /*=======================================================
2093  * indiseq_is_valtype_ival -- Is this full of ivals ?
2094  * Created: 2001/01/21, Perry Rapp
2095  *=====================================================*/
2096 BOOLEAN
indiseq_is_valtype_ival(INDISEQ seq)2097 indiseq_is_valtype_ival (INDISEQ seq)
2098 {
2099 	return IValtype(seq) == ISVAL_INT;
2100 }
2101 /*=======================================================
2102  * indiseq_is_valtype_null -- Is this full of null values ?
2103  * Created: 2001/01/21, Perry Rapp
2104  *=====================================================*/
2105 BOOLEAN
indiseq_is_valtype_null(INDISEQ seq)2106 indiseq_is_valtype_null (INDISEQ seq)
2107 {
2108 	return IValtype(seq) == ISVAL_NUL;
2109 }
2110 /*=======================================================
2111  * get_indiseq_ival -- Get ival from an indiseq element
2112  * Created: 2001/01/21, Perry Rapp
2113  *=====================================================*/
2114 INT
get_indiseq_ival(INDISEQ seq,INT i)2115 get_indiseq_ival (INDISEQ seq, INT i)
2116 {
2117 	ASSERT(i >= 0);
2118 	ASSERT(i < ISize(seq));
2119 	ASSERT(IValtype(seq) == ISVAL_INT || IValtype(seq) == ISVAL_NUL);
2120 	return sval(IData(seq)[i]).i;
2121 
2122 }
2123 /*=======================================================
2124  * set_indiseq_value_funcs -- Set the value function table for an INDISEQ
2125  * This is to allow the report layer to register its functions to
2126  * create, compare, or delete values (so it can work with pvalues)
2127  * Created: 2001/03/25, Perry Rapp
2128  *=====================================================*/
2129 void
set_indiseq_value_funcs(INDISEQ seq,INDISEQ_VALUE_FNCTABLE valfnctbl)2130 set_indiseq_value_funcs (INDISEQ seq, INDISEQ_VALUE_FNCTABLE valfnctbl)
2131 {
2132 	IValfnctbl(seq) = valfnctbl;
2133 }
2134 /*=======================================================
2135  * default_copy_value -- copy a value
2136  * (reports supply their own callback to replace this)
2137  * Created: 2001/03/25, Perry Rapp
2138  *=====================================================*/
2139 UNION
default_copy_value(UNION uval,INT valtype)2140 default_copy_value (UNION uval, INT valtype)
2141 {
2142 	UNION retval;
2143 	/* only copy ints - all other values turn to NULL */
2144 	if (valtype == ISVAL_INT)
2145 		retval.i = uval.i;
2146 	else
2147 		retval.w = 0;
2148 	return retval;
2149 }
2150 /*=======================================================
2151  * default_delete_value -- delete a value
2152  * (reports supply their own callback to replace this)
2153  * Created: 2001/03/25, Perry Rapp
2154  *=====================================================*/
2155 void
default_delete_value(UNION uval,INT valtype)2156 default_delete_value (UNION uval, INT valtype)
2157 {
2158 	if (valtype == ISVAL_STR) {
2159 		if (uval.w) {
2160 			STRING str = (STRING)uval.w;
2161 			stdfree(str);
2162 			uval.w = NULL;
2163 		}
2164 	}
2165 }
2166 /*=======================================================
2167  * default_create_gen_value -- create a value for a specific
2168  *  generation (for ancestorset or descendantset)
2169  *  default implementation
2170  * (reports supply their own callback to replace this)
2171  * Created: 2001/03/25, Perry Rapp
2172  *=====================================================*/
2173 UNION
default_create_gen_value(INT gen,INT * valtype)2174 default_create_gen_value (INT gen, INT * valtype)
2175 {
2176 	UNION uval;
2177 	if (*valtype == ISVAL_INT)
2178 		uval.i = gen;
2179 	else
2180 		uval.w = NULL;
2181 	return uval;
2182 }
2183 /*===========================================================
2184  * default_compare_values -- compare values of two elements
2185  * Created: 2002/02/19, Perry Rapp
2186  *=========================================================*/
2187 INT
default_compare_values(VPTR ptr1,VPTR ptr2,INT valtype)2188 default_compare_values (VPTR ptr1, VPTR ptr2, INT valtype)
2189 {
2190 	valtype = valtype; /* unused */
2191 	/* We don't know how to deal with ptrs here */
2192 	/* Let's just sort them in memory order */
2193 	return (INT)ptr1 - (INT)ptr2;
2194 }
2195 /*=======================================================
2196  * calc_indiseq_names -- fill in element names
2197  *  for any persons on list with names
2198  * Created: 2002/02/14
2199  *=====================================================*/
2200 void
calc_indiseq_names(INDISEQ seq)2201 calc_indiseq_names (INDISEQ seq)
2202 {
2203 	if (IFlags(seq) & WITHNAMES)
2204 		return;
2205 
2206 	FORINDISEQ(seq, el, num)
2207 		if (*skey(el)=='I' && !snam(el)) {
2208 			STRING name = qkey_to_name(skey(el));
2209 			if (name)
2210 				snam(el) = strsave(name);
2211 		}
2212 	ENDINDISEQ
2213 	IFlags(seq) |= WITHNAMES;
2214 }
2215 /*=======================================================
2216  * calc_indiseq_name_el -- Try to find name of requested element
2217  *  if needed & appropriate
2218  *=====================================================*/
2219 static void
calc_indiseq_name_el(INDISEQ seq,INT index)2220 calc_indiseq_name_el (INDISEQ seq, INT index)
2221 {
2222 	STRING key, name;
2223 
2224 	ASSERT(seq);
2225 	ASSERT(index>=0);
2226 	ASSERT(index<ISize(seq));
2227 
2228 	if (IFlags(seq) & WITHNAMES)
2229 		return;
2230 
2231 	if (snam(IData(seq)[index]))
2232 		return;
2233 
2234 	key = skey(IData(seq)[index]);
2235 	if (key[0] != 'I')
2236 		return;
2237 
2238 	name = qkey_to_name(key);
2239 	if (name)
2240 		snam(IData(seq)[index]) = strsave(name);
2241 }
2242 /*=======================================================
2243  * qkey_to_name -- find the name for person with given key
2244  *  key: [IN]  key of person (eg, "46")
2245  * Note: key must be non-NULL, but may be invalid
2246  * Returns pointer into node cache memory
2247  * Created: 2002/02/14
2248  *=====================================================*/
2249 static STRING
qkey_to_name(STRING key)2250 qkey_to_name (STRING key)
2251 {
2252 	NODE indi,name;
2253 
2254 	indi = qkey_to_indi(key);
2255 	if (!indi) return NULL;
2256 	name = NAME(indi);
2257 	if (!name) return NULL;
2258 	return nval(name);
2259 }
2260 /*=======================================================
2261  * addref_indiseq -- Add new live reference to indiset
2262  * Created: 2007/12/19, Perry Rapp
2263  *=====================================================*/
2264 void
addref_indiseq(INDISEQ seq)2265 addref_indiseq (INDISEQ seq)
2266 {
2267 	if (seq)
2268 		++IRefcnt(seq);
2269 }
2270