/*================================= * 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; }
/*==================================== * 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); }
/*========================================================== * ask_for_indi_key -- Have user identify person; return key *========================================================*/ STRING ask_for_indi_key (STRING ttl, ASK1Q ask1) { RECORD indi = ask_for_indi(ttl, ask1); if (!indi) return NULL; return rmvat(nxref(nztop(indi))); }
/*======================================================= * 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; }
/*====================================== * key_util -- Return person's key value *====================================*/ void key_util (void) { RECORD indi = ask_for_indi(_("Whose key value do you want?"), NOASK1); if (!indi) return; msg_info("%s - %s", rmvat(nxref(nztop(indi))), indi_to_name(nztop(indi), 70)); }
/*====================================== * llrpt_writeindi -- Write person to database * usage: writeindi(INDI) -> BOOLEAN *====================================*/ PVALUE llrpt_writeindi (PNODE node, SYMTAB stab, BOOLEAN *eflg) { NODE indi1; PNODE arg = iargs(node); NODE indi2 = eval_indi(arg, stab, eflg, NULL); STRING rawrec=0, msg; INT len, cnt; BOOLEAN rtn=FALSE; if (*eflg || !indi2) { prog_var_error(node, stab, arg, 0, nonind1, "writeindi"); return NULL; } /* make a copy, so we can delete it */ indi2 = copy_node_subtree(indi2); /* get existing record */ rawrec = retrieve_raw_record(rmvat(nxref(indi2)), &len); if (!rawrec) { /* TODO: What do we do here ? Are they adding a new indi ? or did they get the xref wrong ? */ goto end_writeindi; } ASSERT(indi1 = string_to_node(rawrec)); cnt = resolve_refn_links(indi2); /* validate for showstopper errors */ if (!valid_indi_tree(indi2, &msg, indi1)) { /* TODO: What to do with msg ? */ goto end_writeindi; } if (cnt > 0) { /* unresolvable refn links */ /* TODO: optional argument to make this fatal ? */ } if (equal_tree(indi1, indi2)) { /* optimization :) */ rtn = TRUE; goto end_writeindi; } if (readonly) { /* TODO: database is read only error message */ goto end_writeindi; } replace_indi(indi1, indi2); strfree(&rawrec); rtn = TRUE; end_writeindi: return create_pvalue_from_bool(rtn); }
/*===================================== * llrpt_writefam -- Write family to database * usage: writefam(FAM) -> BOOLEAN *===================================*/ PVALUE llrpt_writefam (PNODE node, SYMTAB stab, BOOLEAN *eflg) { NODE fam1; NODE fam2 = eval_fam(iargs(node), stab, eflg, NULL); STRING rawrec=0, msg; INT len, cnt; BOOLEAN rtn=FALSE; if (*eflg) return NULL; /* make a copy, so we can delete it */ fam2 = copy_node_subtree(fam2); /* get existing record */ rawrec = retrieve_raw_record(rmvat(nxref(fam2)), &len); if (!rawrec) { /* TODO: What do we do here ? Are they adding a new fam ? or did they get the xref wrong ? */ goto end_writefam; } ASSERT(fam1 = string_to_node(rawrec)); cnt = resolve_refn_links(fam2); /* validate for showstopper errors */ if (!valid_fam_tree(fam2, &msg, fam1)) { /* TODO: What to do with msg ? */ goto end_writefam; } if (cnt > 0) { /* unresolvable refn links */ /* TODO: optional argument to make this fatal ? */ } if (equal_tree(fam1, fam2)) { /* optimization :) */ rtn = TRUE; goto end_writefam; } if (readonly) { /* TODO: database is read only error message */ goto end_writefam; } replace_fam(fam1, fam2); strfree(&rawrec); rtn = TRUE; end_writefam: return create_pvalue_from_bool(rtn); }
/*================================================= * nodechk -- Check node tree to which this node belongs *===============================================*/ void nodechk (NODE node, CNSTRING scope) { NODE root = node; CNSTRING key = 0; if (!nodechecking) return; while (nparent(root)) { root = nparent(root); } key = nxref(node); check_node_recursively(root, NULL, 1, key, scope); }
/*======================================== * 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"); }
/*================================================================= * 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; }
/*================================================= * file_to_record -- Convert GEDCOM file to in-memory record * * fname:[IN] name of file that holds GEDCOM record * ttm: [IN] character translation table * pmsg: [OUT] possible error message * pemp: [OUT] set true if file is empty * returns addref'd record *===============================================*/ RECORD file_to_record (STRING fname, XLAT ttm, STRING *pmsg, BOOLEAN *pemp) { NODE node = file_to_node(fname, ttm, pmsg, pemp); if (!node) return 0; if (nxref(node)) { CACHEEL cel = node_to_cacheel_old(node); RECORD rec = get_record_for_cel(cel); return rec; } else { RECORD rec = create_record_for_unkeyed_node(node); return rec; } }
/*================================================================= * 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(); }
/*===========================================+ * llrpt_deletefromset -- Remove person from INDISEQ * usage: deletefromset(SET, INDI, BOOL) -> VOID *==========================================*/ PVALUE llrpt_deletefromset (PNODE node, SYMTAB stab, BOOLEAN *eflg) { NODE indi; STRING key=0; BOOLEAN all, rc; INDISEQ seq; PNODE arg1 = builtin_args(node), arg2 = inext(arg1), arg3 = inext(arg2); PVALUE val1 = eval_and_coerce(PSET, arg1, stab, eflg); PVALUE val3=0; if (*eflg) { prog_var_error(node, stab, arg1, val1, nonsetx, "deletefromset", "1"); goto dfs_exit; } ASSERT(seq = pvalue_to_seq(val1)); indi = eval_indi(arg2, stab, eflg, NULL); if (*eflg) { prog_var_error(node, stab, arg2, NULL, nonindx, "deletefromset", "2"); goto dfs_exit; } if (!indi) goto dfs_exit; *eflg = TRUE; if (!(key = strsave(rmvat(nxref(indi))))) { prog_error(node, "major error in deletefromset."); goto dfs_exit; } *eflg = FALSE; val3 = eval_and_coerce(PBOOL, arg3, stab, eflg); if (*eflg) { prog_var_error(node, stab, arg2, NULL, nonboox, "deletefromset", "3"); goto dfs_exit; } all = pvalue_to_bool(val3); delete_pvalue(val3); do { rc = delete_indiseq(seq, key, NULL, 0); } while (rc && all); dfs_exit: /* delay delete of val1 to last minute lest it is a temp owning seq, eg, deletefromset(ancestorset(i),j) */ if (val1) delete_pvalue(val1); if (key) strfree(&key); return NULL; }
/*================================================================ * add_indi_no_cache -- Add new person to database * does not insert into cache * (used by import) * (no user interaction) *==============================================================*/ BOOLEAN add_indi_no_cache (NODE indi) { NODE node, name, refn, sex, body, famc, fams; STRING str, key; split_indi_old(indi, &name, &refn, &sex, &body, &famc, &fams); key = rmvat(nxref(indi)); for (node = name; node; node = nsibling(node)) add_name(nval(node), key); for (node = refn; node; node = nsibling(node)) if (nval(node)) add_refn(nval(node), key); join_indi(indi, name, refn, sex, body, famc, fams); resolve_refn_links(indi); str = node_to_string(indi); store_record(key, str, strlen(str)); stdfree(str); return TRUE; }
/*==================================+ * llrpt_addtoset -- Add person to INDISEQ * usage: addtoset(SET, INDI, ANY) -> VOID *=================================*/ PVALUE llrpt_addtoset (PNODE node, SYMTAB stab, BOOLEAN *eflg) { NODE indi=0; STRING key=0; INDISEQ seq=0; PNODE arg1 = builtin_args(node), arg2 = inext(arg1), arg3 = inext(arg2); PVALUE val1 = eval_and_coerce(PSET, arg1, stab, eflg); PVALUE val2=0; if (*eflg) { prog_var_error(node, stab, arg1, val1, nonsetx, "addtoset", "1"); return NULL; } ASSERT(seq = pvalue_to_seq(val1)); indi = eval_indi(arg2, stab, eflg, NULL); if (*eflg) { prog_var_error(node, stab, arg2, NULL, nonindx, "addtoset","2"); goto ats_exit; } if (!indi) goto ats_exit; *eflg = TRUE; if (!(key = strsave(rmvat(nxref(indi))))) { prog_error(node, "major error in addtoset."); goto ats_exit; } *eflg = FALSE; val2 = evaluate(arg3, stab, eflg); if (*eflg) { prog_error(node, "3rd arg to addtoset is in error."); goto ats_exit; } append_indiseq_pval(seq, key, NULL, val2, FALSE); ats_exit: if (key) strfree(&key); /* append made its own copy */ /* delay to last minute val1 cleanup lest it is a temp owning seq, eg, addtoset(ancestorset(i),j) */ if (val1) delete_pvalue(val1); return NULL; }
/*====================================+ * llrpt_inset -- See if person is in INDISEQ * usage: inset(SET, INDI) -> BOOL *==========================================*/ PVALUE llrpt_inset (PNODE node, SYMTAB stab, BOOLEAN *eflg) { NODE indi; STRING key=0; INDISEQ seq; BOOLEAN rel; PNODE arg1 = builtin_args(node), arg2 = inext(arg1); PVALUE val1 = eval_and_coerce(PSET, arg1, stab, eflg); PVALUE valr=0; if (*eflg ||!val1 || !(seq = pvalue_to_seq(val1))) { *eflg = TRUE; prog_var_error(node, stab, arg1, val1, nonsetx, "inset", "1"); goto inset_exit; } indi = eval_indi(arg2, stab, eflg, NULL); if (*eflg) { prog_var_error(node, stab, arg2, NULL, nonindx, "inset", "2"); goto inset_exit; } if (!indi) { rel = FALSE; } else { if (!(key = strsave(rmvat(nxref(indi))))) { *eflg = TRUE; prog_error(node, "major error in inset."); goto inset_exit; } rel = in_indiseq(seq, key); } valr = create_pvalue_from_bool(rel); inset_exit: /* delay delete of val1 to last minute lest it is a temp owning seq, eg, inset(ancestorset(i),j) */ if (val1) delete_pvalue(val1); if (key) strfree(&key); return valr; }
/*================================================= * 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; }
/*======================================= * edit_record -- Edit record in database * root1: [IN] record to edit (may be NULL) * idedt: [IN] user id prompt * letr: [IN] record type (E, S, or X) * redt: [IN] reedit prompt displayed if hard error after editing * redtopt: [IN] reedit prompt displayed if soft error (unresolved links) * val: [IN] callback to validate routine * cfrm: [IN] confirmation msg string * tag: [IN] tag (SOUR, EVEN, or NULL) * todbase: [IN] callback to write record to dbase * gdmsg: [IN] success message * rfmt: [IN] display reformatter *=====================================*/ static BOOLEAN edit_record (RECORD rec1, STRING idedt, INT letr, STRING redt, STRING redtopt , BOOLEAN (*val)(NODE, STRING *, NODE) , STRING cfrm, void (*todbase)(NODE), STRING gdmsg , RFMT rfmt) { XLAT ttmi = transl_get_predefined_xlat(MEDIN); STRING msg, key; BOOLEAN emp; NODE root0=0, root1=0, root2=0; NODE refn1=0, refn2=0, refnn=0, refn1n=0; NODE body=0, node=0; /* Identify record if need be */ if (!rec1) { rec1 = ask_for_record(idedt, letr); } root1 = nztop(rec1); if (!root1) { message(_(qSnosuchrec)); return FALSE; } /* Have user edit record */ annotate_with_supplemental(root1, rfmt); write_node_to_editfile(root1); resolve_refn_links(root1); do_edit(); if (readonly) { root2 = file_to_node(editfile, ttmi, &msg, &emp); if (!equal_tree(root1, root2)) message(_(qSronlye)); free_nodes(root2); return FALSE; } while (TRUE) { INT cnt; root2 = file_to_node(editfile, ttmi, &msg, &emp); if (!root2) { if (ask_yes_or_no_msg(msg, redt)) { do_edit(); continue; } break; } cnt = resolve_refn_links(root2); /* check validation & allow user to reedit if invalid */ /* this is a showstopper, so alternative is to abort */ if (!(*val)(root2, &msg, root1)) { if (ask_yes_or_no_msg(msg, redt)) { do_edit(); continue; } free_nodes(root2); root2 = NULL; 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(root2); do_edit(); continue; } } break; } /* If error or no change or user backs out return */ if (!root2) return FALSE; if (equal_tree(root1, root2) || !ask_yes_or_no(cfrm)) { free_nodes(root2); return FALSE; } /* Prepare to change database */ /* Move root1 data into root0 & save refns */ split_othr(root1, &refn1, &body); root0 = copy_node(root1); join_othr(root0, NULL, body); /* delete root0 tree & root1 node (root1 is solitary node) */ free_nodes(root0); root0 = 0; free_nodes(root1); root1 = 0; /* now copy root2 node into root1, then root2 tree under it */ root1 = copy_node(root2); split_othr(root2, &refn2, &body); refnn = copy_nodes(refn2, TRUE, TRUE); join_othr(root1, refn2, body); /* now root2 is solitary node, delete it */ free_node(root2); root2 = 0; /* Change the database */ (*todbase)(root1); key = rmvat(nxref(root1)); /* remove deleted refns & add new ones */ classify_nodes(&refn1, &refnn, &refn1n); for (node = refn1; node; node = nsibling(node)) if (nval(node)) remove_refn(nval(node), key); for (node = refnn; node; node = nsibling(node)) if (nval(node)) add_refn(nval(node), key); free_nodes(refn1); free_nodes(refnn); free_nodes(refn1n); msg_info(gdmsg); return TRUE; }
/*================================================= * 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; }
/*=================================== * 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; }
/*================================================ * 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); }