/*================================= * node_lineprint -- print a node line * in a node tree * This is used in gedcom view & extended * gedcom views * returns static buffer * Created: 2001/01/27, Perry Rapp *===============================*/ static STRING node_lineprint (INT width, void * param) { static char line[120]; STRING ptr=line; INT mylen=sizeof(line); NODE_PRINT_PARAM npp = (NODE_PRINT_PARAM)param; NODE node=npp->node; if (mylen>width) mylen=width; if (nxref(node)) { llstrcatn(&ptr, nxref(node), &mylen); llstrcatn(&ptr, " ", &mylen); } if (ntag(node)) { llstrcatn(&ptr, ntag(node), &mylen); llstrcatn(&ptr, " ", &mylen); } if (nval(node)) { llstrcatn(&ptr, nval(node), &mylen); llstrcatn(&ptr, " ", &mylen); } if (npp->gdvw == GDVW_EXPANDED && nval(node)) { STRING key = rmvat(nval(node)), str; if (key) { str = generic_to_list_string(NULL, key, mylen, ",", &disp_long_rfmt, TRUE); llstrcatn(&ptr, " : ", &mylen); llstrcatn(&ptr, str, &mylen); } } return line; }
/*==================================================== * disp_pvalue -- Display details of specified pvalue * Drilldown in variable debugger * This is primarily to display contents of container values * val: [IN] value to display *==================================================*/ static void disp_pvalue (PVALUE val) { switch (which_pvalue_type(val)) { case PGNODE: { NODE node = pvalue_to_node(val); char buffer[256] = ""; size_t len = sizeof(buffer); STRING str = buffer; if (ntag(node)) { llstrappf(str, len, uu8, "%s: ", ntag(node)); } if (nval(node)) { llstrapps(str, len, uu8, nval(node)); } msg_info(str); } return; case PINDI: case PFAM: case PSOUR: case PEVEN: case POTHR: { RECORD rec = pvalue_to_record(val); NODE node = nztop(rec); size_t len = 128; STRING txt = generic_to_list_string(node, NULL, len, " ", NULL, TRUE); msg_info(txt); } return; case PLIST: { LIST list = pvalue_to_list(val); disp_list(list); } return; case PTABLE: { TABLE tab = pvalue_to_table(val); disp_table(tab); } return; case PSET: { INDISEQ seq = pvalue_to_seq(val); disp_seq(seq); } return; } }
/*======================================================= * resolve_node -- Traverse routine for resolve_refn_links (q.v.) * node: Current node in traversal * returns FALSE if bad refn pointer *=====================================================*/ static BOOLEAN resolve_node (NODE node, BOOLEAN annotate_pointers) { STRING val = nval(node); STRING refn=0; if (!val) return TRUE; refn = symbolic_link(val); if (refn) { INT letr = record_letter(ntag(node)); NODE refr = refn_to_record(refn, letr); if (refr) { stdfree(nval(node)); nval(node) = strsave(nxref(refr)); } else { return FALSE; } } if (annotate_pointers) { INT i=0,len=0; if (is_annotated_xref(nval(node), &len)) { char newval[20]; ASSERT(len < (INT)sizeof(newval)); for (i=0; i<len; ++i) { newval[i] = nval(node)[i]; } newval[i] = 0; stdfree(nval(node)); nval(node) = strsave(newval); } } return TRUE; }
/*===================================== * check_node -- check node for bad pointers * and bad levels, and continue traverse * 2001/02/18, Perry Rapp *===================================*/ static void check_node (CNSTRING n0key, NODE node, INT level) { BOOLEAN lineage=FALSE; /* ignore lineage links - they are checked elsewhere */ if (level==1) { INT i; for (i=0; i<ARRSIZE(lineage_tags); i++) { if (eqstr(ntag(node), lineage_tags[i])) { lineage=TRUE; break; } } } /* TO DO: How do we tell non-pointers that *ought* to be pointers, eg "1 SOUR <FamilyHistory>" ? */ if (!lineage) { STRING skey = rmvat(nval(node)); if (skey) { NODE xnode = qkey_to_type(skey); if (!xnode) { report_error(ERR_BADPOINTER , _("Bad pointer (in %s): %s") , n0key, nval(node)); } } } if (nchild(node)) check_node(n0key, nchild(node), level+1); if (nsibling(node)) check_node(n0key, nsibling(node), level); }
/*====================================== * valid_othr_tree -- Validate OTHR tree * node: [IN] source to validate * pmsg, [OUT] error message, if any * orig: [IN] OTHR node to match *====================================*/ BOOLEAN valid_othr_tree (NODE node, STRING *pmsg, NODE orig) { orig = NULL; /* keep compiler happy */ *pmsg = NULL; if (!node) { *pmsg = _(qSbademp); return FALSE; } if (eqstr("INDI", ntag(node)) || eqstr("FAM", ntag(node)) || eqstr("SOUR", ntag(node)) || eqstr("EVEN", ntag(node))) { *pmsg = _(qSbadothr0); return FALSE; } return TRUE; }
/*==================================== * swrite_node -- Write NODE to string *==================================*/ static STRING swrite_node (INT levl, /* level */ NODE node, /* node */ STRING p) /* write string */ { char scratch[600]; STRING q = scratch; sprintf(q, "%ld ", levl); q += strlen(q); if (nxref(node)) { strcpy(q, nxref(node)); q += strlen(q); *q++ = ' '; } strcpy(q, ntag(node)); q += strlen(q); if (nval(node)) { *q++ = ' '; strcpy(q, nval(node)); q += strlen(q); } *q++ = '\n'; *q = 0; strcpy(p, scratch); return p + strlen(p); }
/*=============================================== * index_by_refn - Index node tree by REFN values * Adds all REFN values in node tree to refn index * Assumes all are new (only for use with brand new records) *=============================================*/ void index_by_refn (NODE node, STRING key) { if (!node || !key) return; for (node = nchild(node); node; node = nsibling(node)) { if (eqstr("REFN", ntag(node)) && nval(node)) add_refn(nval(node), key); } }
/*=================================== * find_xref -- Search node for a cross-reference key * 2001/01/21, Perry Rapp *=================================*/ static BOOLEAN find_xref (CNSTRING key, NODE node, CNSTRING tag1, CNSTRING tag2) { NODE node2; CACHEEL ncel2; BOOLEAN found=FALSE; ncel2 = node_to_cacheel_old(node); lock_cache(ncel2); for (node2 = nchild(node); node2; node2 = nsibling(node2)) { if (eqstr(tag1, ntag(node2)) || (tag2 && eqstr(tag2, ntag(node2)))) { STRING key2 = rmvat(nval(node2)); if (key2 && eqstr(key, key2)) { found = TRUE; goto exit_find; } } } exit_find: unlock_cache(ncel2); return found; }
/*=============================== * valid_fam_tree -- Validate FAM tree * fam1, [IN] family to validate * pmsg: [OUT] error message, if any * fam0: [IN] family to match - may be NULL * Should be replaced by valid_fam(RECORD,...) ? *=============================*/ BOOLEAN valid_fam_tree (NODE fam1, STRING *pmsg, NODE fam0) { NODE refn0, husb0, wife0, chil0, body0; NODE refn1, husb1, wife1, chil1, body1; if (!fam1) { *pmsg = _(qSbademp); return FALSE; } if (nestr("FAM", ntag(fam1))) { *pmsg = _(qSbadfm0); return FALSE; } if (nsibling(fam1)) { *pmsg = _(qSbadmul); return FALSE; } refn0 = husb0 = wife0 = chil0 = body0 = NULL; if (fam0) split_fam(fam0, &refn0, &husb0, &wife0, &chil0, &body0); split_fam(fam1, &refn1, &husb1, &wife1, &chil1, &body1); if (fam0 && !iso_nodes(fam1, fam0, FALSE, TRUE)) { *pmsg = _(qSbadfam); goto bad3; } if (!iso_nodes(husb1, husb0, FALSE, TRUE)) { *pmsg = _(qSbadhsb); goto bad3; } if (!iso_nodes(wife1, wife0, FALSE, TRUE)) { *pmsg = _(qSbadwif); goto bad3; } if (!iso_nodes(chil1, chil0, FALSE, TRUE)) { *pmsg = _(qSbadchl); goto bad3; } if (fam0) join_fam(fam0, refn0, husb0, wife0, chil0, body0); join_fam(fam1, refn1, husb1, wife1, chil1, body1); return TRUE; bad3: if (fam0) join_fam(fam0, refn0, husb0, wife0, chil0, body0); join_fam(fam1, refn1, husb1, wife1, chil1, body1); return FALSE; }
/*================================================================= * expand_tree -- Create copy of node tree with additional link info *===============================================================*/ static NODE expand_tree (NODE root0) { NODE copy, node, sub; STRING key; static NODE root; /* root of record being edited */ LIST subs; /* list of contained records */ NODE expd; /* expanded main record - copy - our retval */ root = root0; expd = copy_nodes(root, TRUE, TRUE); subs = create_list(); traverse_nodes(expd, advedit_expand_traverse, subs); /* expand the list of records into the copied record */ FORLIST(subs, el) node = (NODE) el; #ifdef DEBUG llwprintf("in list: %s %s\n", ntag(node), nval(node)); #endif key = rmvat(nval(node)); if ((sub = nztop(key_possible_to_record(key, *key)))) { copy = copy_node_subtree(sub); nxref(node) = nxref(copy); ntag(node) = ntag(copy); nchild(node) = nchild(copy); nparent(node) = nparent(copy); /*MEMORY LEAK; MEMORY LEAK; MEMORY LEAK: node not removed (because its value and possibly xref [probably not] are still being referred to */ } ENDLIST /* Shouldn't we free subs now ? Perry 2001/06/22 */ #ifdef DEBUG show_node(expd); #endif return expd; }
/*====================================== * valid_even_tree -- Validate EVEN tree * node: [IN] source to validate * pmsg, [OUT] error message, if any * orig: [IN] EVEN node to match *====================================*/ BOOLEAN valid_even_tree (NODE node, STRING *pmsg, NODE orig) { orig = NULL; /* keep compiler happy */ *pmsg = NULL; if (!node) { *pmsg = _(qSbademp); return FALSE; } if (nestr("EVEN", ntag(node))) { *pmsg = _(qSbadev0); return FALSE; } return TRUE; }
/*================================================================= * advanced_person_edit -- *===============================================================*/ void advanced_person_edit (NODE root0) { FILE *fp; NODE expd; #ifdef DEBUG llwprintf("advanced_person_edit: %s %s %s\n", nxref(root0), ntag(root0),nval(root0)); #endif expd = expand_tree(root0); ASSERT(fp = fopen(editfile, LLWRITETEXT)); write_nodes(0, fp, NULL, expd, TRUE, TRUE, TRUE); fclose(fp); do_edit(); }
/*======================================== * write_node -- Write NODE to GEDCOM file * * INT levl: [in] level * FILE *fp: [in] file * TRANTABLE tt [in] char map * NODE node: [in] node * BOOLEAN indent: [in]indent? *======================================*/ static void write_node (INT levl, FILE *fp, XLAT ttm, NODE node, BOOLEAN indent) { char out[MAXLINELEN+1]; STRING p; if (indent) { INT i; for (i = 1; i < levl; i++) fprintf(fp, " "); } fprintf(fp, "%ld", levl); if (nxref(node)) fprintf(fp, " %s", nxref(node)); fprintf(fp, " %s", ntag(node)); if ((p = nval(node))) { if (ttm) { translate_string(ttm, nval(node), out, MAXLINELEN+1); p = out; } fprintf(fp, " %s", p); } fprintf(fp, "\n"); }
/*================================================================= * advedit_expand_traverse -- Traverse routine called when expanding record *===============================================================*/ static BOOLEAN advedit_expand_traverse (NODE node, VPTR param) { LIST subs = (LIST)param; STRING key = value_to_xref(nval(node)); if (!key) return TRUE; key = strsave(key); #ifdef DEBUG llwprintf("expand_traverse: %s %s\n", ntag(node), nval(node)); #endif /* DEBUG */ FORLIST(subs, el) #ifdef DEBUG llwprintf("expand_traverse: %s %s\n", key, rmvat(nval((NODE) el))); #endif /* DEBUG */ if (eqstr(key, rmvat(nval((NODE) el)))) { STOPLIST stdfree(key); return TRUE; } ENDLIST enqueue_list(subs, node); stdfree(key); return TRUE; }
/*================================================= * do_import -- Read GEDCOM file to database * ifeed: [IN] output methods * fp: [I/O] GEDCOM file whence to load data *===============================================*/ static BOOLEAN do_import (IMPORT_FEEDBACK ifeed, FILE *fp) { NODE node, conv; XLAT ttm = 0; STRING msg; BOOLEAN emp; INT nindi = 0, nfam = 0, neven = 0; INT nsour = 0, nothr = 0, type, num = 0; INT totkeys = 0, totused = 0; char msgbuf[80]; BOOLEAN succeeded=FALSE; STRING str,unistr=0; ZSTR zerr=0; TABLE metadatatab = create_table_str(); STRING gdcodeset=0; INT warnings=0; /* start by assuming default */ strupdate(&gdcodeset, gedcom_codeset_in); /* rptui_init(); *//* clear ui time counter */ /* Open and validate GEDCOM file */ if ((unistr=check_file_for_unicode(fp)) && !eqstr(unistr, "UTF-8")) { msg_error(_(qSunsupuniv), unistr); goto end_import; } if (eqstr_ex(unistr, "UTF-8")) { strupdate(&gdcodeset, "UTF-8"); } if (!scan_header(fp, metadatatab, &zerr)) { msg_error(zs_str(zerr)); goto end_import; } if ((str = valueof_str(metadatatab, "GEDC.FORM"))!= NULL) { if (!eqstr(str, "LINEAGE-LINKED")) { if (!ask_yes_or_no_msg( _("This is not a lineage linked GEDCOM file.") , _("Proceed anyway?") )) goto end_import; } } if (!unistr && (str = valueof_str(metadatatab, "CHAR"))!= NULL) { /* if no BOM, use file's declared encoding if present */ strupdate(&gdcodeset, str); } /* TODO: Push this codeset question down to after the validation, where we can know if the incoming file happened to really be all ASCII */ if (!int_codeset[0]) { /* TODO: ask if user would like to adopt codeset of incoming file, if we found it */ if (!ask_yes_or_no_msg( _("No current internal codeset, so no codeset conversion can be done") , _("Proceed without codeset conversion?") )) goto end_import; } /* Warn if lossy code conversion likely */ if (gdcodeset[0] && int_codeset[0]) { if (is_lossy_conversion(gdcodeset, int_codeset)) { ZSTR zstr=zs_new(); zs_setf(zstr, _("Lossy codeset conversion (from <%s> to <%s>) likely") , gdcodeset, int_codeset); if (!ask_yes_or_no_msg( zs_str(zstr) , _("Proceed anyway?") )) goto end_import; } } /* validate */ if (ifeed && ifeed->validating_fnc) (*ifeed->validating_fnc)(); if (!validate_gedcom(ifeed, fp)) { if (ifeed && ifeed->error_invalid_fnc) (*ifeed->error_invalid_fnc)(_(qSgdnadd)); goto end_import; } warnings = validate_get_warning_count(); if (warnings) { ZSTR zstr=zs_new(); zs_setf(zstr, _pl("%d warning during import", "%d warnings during import", warnings), warnings); if (!ask_yes_or_no_msg(zs_str(zstr), _(qSproceed))) { goto end_import; } } if (gdcodeset[0] && int_codeset[0]) { retry_input_codeset: ttm = transl_get_xlat(gdcodeset, int_codeset); if (!transl_is_xlat_valid(ttm)) { ZSTR zstr=zs_new(); char csname[64]; BOOLEAN b; transl_release_xlat(ttm); ttm = 0; zs_setf(zstr, _("Cannot convert codeset (from <%s> to <%s>)") , gdcodeset, int_codeset); b = ask_for_string(zs_str(zstr) , _("Enter codeset to assume (* for none)") , csname, sizeof(csname)) && csname[0]; zs_free(&zstr); if (!b) goto end_import; if (!eqstr(csname, "*")) { strupdate(&gdcodeset, csname); goto retry_input_codeset; } } } if((num_indis() > 0) || (num_fams() > 0) || (num_sours() > 0) || (num_evens() > 0) || (num_othrs() > 0)) gd_reuse = FALSE; else if((gd_reuse = check_stdkeys())) { totused = gd_itot + gd_ftot + gd_stot + gd_etot + gd_xtot; totkeys = gd_imax + gd_fmax + gd_smax + gd_emax + gd_xmax; if((totkeys-totused) > 0) { INT delkeys = totkeys-totused; snprintf(msgbuf, sizeof(msgbuf) , _pl("Using original keys, %d deleted record will be in the database." , "Using original keys, %d deleted records will be in the database." , delkeys) , delkeys ); } else strcpy(msgbuf, " "); gd_reuse = ask_yes_or_no_msg(msgbuf, _(qScfoldk)); /* TODO: why were these here ? touchwin(uiw_win(stdout_win)); wrefresh(uiw_win(stdout_win)); */ } /* start loading the file */ rewind(fp); /* test for read-only database here */ if(readonly) { if (ifeed && ifeed->error_readonly_fnc) (*ifeed->error_readonly_fnc)(); goto end_import; } /* tell user we are beginning real part of import */ if (ifeed && ifeed->beginning_import_fnc) { if(gd_reuse) (*ifeed->beginning_import_fnc)(_(qSdboldk)); else (*ifeed->beginning_import_fnc)(_(qSdbnewk)); } /* Add records to database */ node = convert_first_fp_to_node(fp, FALSE, ttm, &msg, &emp); while (node) { if (!(conv = node_to_node(node, &type))) { free_nodes(node); node = next_fp_to_node(fp, FALSE, ttm, &msg, &emp); continue; } switch (type) { case INDI_REC: num = ++nindi; break; case FAM_REC: num = ++nfam; break; case EVEN_REC: num = ++neven; break; case SOUR_REC: num = ++nsour; break; case OTHR_REC: num = ++nothr; break; default: FATAL(); } restore_record(conv, type, num); if (ifeed && ifeed->added_rec_fnc) ifeed->added_rec_fnc(nxref(conv)[1], ntag(conv), num); free_nodes(node); node = next_fp_to_node(fp, FALSE, ttm, &msg, &emp); } if (msg) { msg_error(msg); } if(gd_reuse && ((totkeys - totused) > 0)) { if (ifeed && ifeed->adding_unused_keys_fnc) (*ifeed->adding_unused_keys_fnc)(); addmissingkeys(INDI_REC); addmissingkeys(FAM_REC); addmissingkeys(EVEN_REC); addmissingkeys(SOUR_REC); addmissingkeys(OTHR_REC); } succeeded = TRUE; end_import: validate_end_import(); zs_free(&zerr); destroy_table(metadatatab); strfree(&gdcodeset); return succeeded; }
/*================================================= * add_dnodes -- add dnodes to dnode tree * recursively, traversing NODE tree & building corresponding * dnode tree * if a line overflows, give it succeeding sibling dnodes * also, check for subordinate CONT & CONC dnodes to be assimilated *===============================================*/ static DISPNODE add_dnodes (NODE node, INT gen, INT indent, INT maxgen, INT * count, CANVASDATA canvas) { DISPNODE tn; DISPNODE tn0, tn1, tn2; NODE child, anode; INT width = (canvas->rect->right - canvas->rect->left) - 2 - gen*indent; static char line[MAXLINELEN], output[MAXLINELEN]; /* must be same size */ STRING ptr=output; INT leader; LIST list=NULL; INT mylen=sizeof(output), mylenorig; if (mylen>width) mylen = width; mylenorig = mylen; /* build xref & tag into line */ line[0] = 0; ptr = line; mylen = sizeof(line); if (nxref(node)) { llstrcatn(&ptr, nxref(node), &mylen); llstrcatn(&ptr, " ", &mylen); } if (ntag(node)) { llstrcatn(&ptr, ntag(node), &mylen); llstrcatn(&ptr, " ", &mylen); } leader = ptr-line; width -= leader; if (width < 10) { /* insufficient space */ return NULL; } /* output is available as scratch */ list = text_to_list("", width, LISTDOFREE); if (nval(node)) { STRING valtxt = nval(node); append_to_text_list(list, valtxt, width, FALSE); } /* anode is first child */ anode = nchild(node); /* check for text continuation nodes to assimilate */ if (nchild(node)) { for ( ; anode && !nchild(anode); anode = nsibling(anode)) { BOOLEAN newline=FALSE; STRING valtxt=NULL; if (eqstr(ntag(anode), "CONC")) { append_to_text_list(list, " ", width, FALSE); newline = FALSE; } else if (eqstr(ntag(anode), "CONT")) { newline = TRUE; } else { break; } valtxt = nval(anode); append_to_text_list(list, valtxt, width, newline); } } /* anode is now first non-assimilated child */ /* now add all list elements to tree as siblings first one will be tn, which we return as our result tn0 refers to previous one, for the nsibling links */ tn = tn0 = tn1 = 0; FORLIST(list, el) tn1 = alloc_displaynode(); if (!tn) { INT i; tn = tn1; /* ptr & mylen still point after leader */ llstrcatn(&ptr, el, &mylen); /* put original line */ tn1->str = strsave(line); /* now build leader we will keep reusing */ for (i=0; i<leader; i++) line[i] = '.'; line[leader-1] = ' '; } else { llstrcatn(&ptr, el, &mylen); tn1->str = strsave(line); } /* now we keep resetting ptr & mylen to after blank leader */ /* so we keep reusing that leader we built in line earlier */ ptr=line+leader; mylen=mylenorig-leader; tn1->firstchild = 0; tn1->nextsib = 0; if (tn0) tn0->nextsib = tn1; tn0 = tn1; (*count)++; ENDLIST /* special handling for empty list, which didn't get its leader */ if (is_empty_list(list)) { tn1 = alloc_displaynode(); tn = tn1; tn1->str = strsave(line); tn1->firstchild = 0; tn1->nextsib = 0; tn0 = tn1; (*count)++; } destroy_list(list); list=0; if (gen < maxgen) { /* our children hang off of tn2, which is last node of our sibling tree; tn0 is previously added child */ tn2 = tn1; tn0 = 0; /* anode was last unassimilated child */ for (child = anode; child; child = nsibling(child)) { tn1 = add_dnodes(child, gen+1, indent, maxgen, count, canvas); if (!tn1) continue; /* child was skipped */ /* link new displaynode into tree we're building */ if (tn0) tn0 = tn0->nextsib = tn1; else /* first child - first time thru loop */ tn0 = tn2->firstchild = tn1; /* child displaynode might have (overflow or assimilated) siblings */ while (tn0->nextsib) tn0 = tn0->nextsib; } } return tn; }
/*================================================ * edit_add_record -- Add record to database by editing * recstr: [IN] default record * redt: [IN] re-edit message * ntype, [IN] S, E, or X * cfrm: [IN] confirm message *==============================================*/ static RECORD edit_add_record (STRING recstr, STRING redt, STRING redtopt, char ntype, STRING cfrm) { FILE *fp; NODE node=0, refn; STRING msg, key; BOOLEAN emp; XLAT ttmi = transl_get_predefined_xlat(MEDIN); STRING (*getreffnc)(void) = NULL; /* get next internal key */ void (*todbasefnc)(NODE) = NULL; /* write record to dbase */ void (*tocachefnc)(NODE) = NULL; /* write record to cache */ /* set up functions according to type */ if (ntype == 'S') { getreffnc = getsxref; todbasefnc = sour_to_dbase; tocachefnc = sour_to_cache; } else if (ntype == 'E') { getreffnc = getexref; todbasefnc = even_to_dbase; tocachefnc = even_to_cache; } else { /* X */ getreffnc = getxxref; todbasefnc = othr_to_dbase; tocachefnc = othr_to_cache; } /* Create template for user to edit */ if (!(fp = fopen(editfile, LLWRITETEXT))) { msg_error(_(qSnofopn), editfile); return FALSE; } prefix_file_for_edit(fp); fprintf(fp, "%s\n", recstr); /* Have user edit new record */ fclose(fp); do_edit(); while (TRUE) { INT cnt; node = file_to_node(editfile, ttmi, &msg, &emp); if (!node) { if (ask_yes_or_no_msg(msg, redt)) { /* yes, edit again */ do_edit(); continue; } break; } cnt = resolve_refn_links(node); /* check validation & allow user to reedit if invalid */ /* this is a showstopper, so alternative is to abort */ if (!valid_node_type(node, ntype, &msg, NULL)) { if (ask_yes_or_no_msg(msg, redt)) { do_edit(); continue; } free_nodes(node); node = NULL; /* fail out */ break; } /* Allow user to reedit if desired if any refn links unresolved */ /* this is not a showstopper, so alternative is to continue */ if (cnt > 0) { char msgb[120]; snprintf(msgb, sizeof(msgb) , get_unresolved_ref_error_string(cnt), cnt); if (ask_yes_or_no_msg(msgb, redtopt)) { write_node_to_editfile(node); do_edit(); continue; } } break; } if (!node || !ask_yes_or_no(cfrm)) { if (node) free_nodes(node); return NULL; } nxref(node) = strsave((STRING)(*getreffnc)()); key = rmvat(nxref(node)); for (refn = nchild(node); refn; refn = nsibling(refn)) { if (eqstr("REFN", ntag(refn)) && nval(refn)) add_refn(nval(refn), key); } (*todbasefnc)(node); (*tocachefnc)(node); return key_to_record(key); }
/*=================================== * valid_indi_tree -- Validate person tree * indi1: [IN] person to validate * pmsg: [OUT] error message, if any * orig: [IN] person to match - may be NULL * rtn: FALSE for bad * Should be replaced by valid_indi(RECORD,...) ? *=================================*/ BOOLEAN valid_indi_tree (NODE indi1, STRING *pmsg, NODE orig) { NODE name1, refn1, sex1, body1, famc1, fams1, node; NODE name0, refn0, sex0, body0, famc0, fams0; INT isex, num; STRING *keys, ukey; if (!indi1) { *pmsg = _(qSbademp); return FALSE; } if (nestr("INDI", ntag(indi1))) { *pmsg = _(qSbadin0); return FALSE; } if (nsibling(indi1)) { *pmsg = _(qSbadmul); return FALSE; } split_indi_old(indi1, &name1, &refn1, &sex1, &body1, &famc1, &fams1); if (getlloptint("RequireNames", 0) && !name1) { *pmsg = _("This person record does not have a name line."); goto bad2; } for (node = name1; node; node = nsibling(node)) { if (!valid_name(nval(node))) { *pmsg = _(qSbadenm); goto bad2; } } name0 = refn0 = sex0 = body0 = famc0 = fams0 = NULL; if (orig) split_indi_old(orig, &name0, &refn0, &sex0, &body0, &famc0, &fams0); if (orig && !iso_nodes(indi1, orig, FALSE, FALSE)) { *pmsg = _(qSbadind); goto bad1; } if (!iso_nodes(famc1, famc0, FALSE, TRUE)) { *pmsg = _(qSbadfmc); goto bad1; } if (!iso_nodes(fams1, fams0, FALSE, TRUE)) { *pmsg = _(qSbadfms); goto bad1; } isex = val_to_sex(sex0); if (!fams0) isex = SEX_UNKNOWN; if (isex != SEX_UNKNOWN && isex != val_to_sex(sex1)) { *pmsg = _(qSbadparsex); goto bad1; } ukey = (refn1 ? nval(refn1) : NULL); get_refns(ukey, &num, &keys, 'I'); if (num > 1 || (num == 1 && (!orig || nestr(keys[0], rmvat(nxref(indi1)))))) { *pmsg = _(qSbadirefn); goto bad1; } if (orig) join_indi(orig, name0, refn0, sex0, body0, famc0, fams0); join_indi(indi1, name1, refn1, sex1, body1, famc1, fams1); return TRUE; bad1: if (orig) join_indi(orig, name0, refn0, sex0, body0, famc0, fams0); bad2: join_indi(indi1, name1, refn1, sex1, body1, famc1, fams1); return FALSE; }