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