예제 #1
0
/*============================================
 * llrpt_detachnode -- Remove node from GEDCOM tree
 * usage: detachnode(NODE) -> VOID
 * (This is the historic deletenode)
 *==========================================*/
PVALUE
llrpt_detachnode (PNODE node, SYMTAB stab, BOOLEAN *eflg)
{
	PNODE arg = iargs(node);
	NODE dead, prnt;
	PVALUE val = eval_and_coerce(PGNODE, arg, stab, eflg);
	if (*eflg) {
		prog_var_error(node, stab, arg, val, nonnod1, "detachnode");
		delete_pvalue(val);
		return NULL;
	}
	dead = pvalue_to_node(val);
	if ((prnt = nparent(dead))) {
		NODE prev = NULL, next;
		NODE curs = nchild(prnt);
		while (curs && curs != dead) {
			prev = curs;
			curs = nsibling(curs);
		}
		ASSERT(curs); /* else broken tree: dead was not child of its parent */
		next = nsibling(dead);
		if (prev == NULL) 
			nchild(prnt) = next;
		else
			nsibling(prev) = next;
	}
	/* unparent node, but ensure its locking is only releative to new parent */
	dolock_node_in_cache(dead, FALSE);
	nparent(dead) = NULL;
	dolock_node_in_cache(dead, TRUE);
	nsibling(dead) = NULL;
	/* we don't actually delete the node, garbage collection must get it */
	return NULL;
}
예제 #2
0
파일: dbverify.c 프로젝트: MarcNo/lifelines
/*=====================================
 * 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);
}
예제 #3
0
파일: nodeutls.c 프로젝트: MarcNo/lifelines
/*========================================================================
 * difference_nodes -- Return difference of two node lists -- all in node1
 *   that are not in node2
 * UNUSED CODE
 *======================================================================*/
NODE
difference_nodes (NODE node1,
                  NODE node2,
                  BOOLEAN kids) /* children matter */
{
	NODE prev1, next1, curs1, curs2;
	node1 = copy_nodes(node1, TRUE, TRUE);
	prev1 = NULL;
	curs1 = node1;
	while (curs1) {
		curs2 = node2;
		while (curs2 && !iso_nodes(curs1, curs2, kids, FALSE))
			curs2 = nsibling(curs2);
		if (curs2) {
			next1 = nsibling(curs1);
			nsibling(curs1) = NULL;
			free_nodes(curs1);
			if (!prev1)
				node1 = next1;
			else
				nsibling(prev1) = next1;
			curs1 = next1;
		} else {
			prev1 = curs1;
			curs1 = nsibling(curs1);
		}
	}
	return node1;
}
예제 #4
0
파일: nodeio.c 프로젝트: MarcNo/lifelines
/*========================================
 * string_to_node -- Read tree from string
 *  (modifies string -- adds 0s between lines)
 *======================================*/
NODE
string_to_node (STRING str)
{
	INT lev;
	INT lev0;
	STRING xref;
	STRING tag;
	STRING val;

	INT curlev;
	NODE root=NULL, node, curnode;
	STRING msg;
	flineno = 0;
	if (!string_to_line(&str, &lev, &xref, &tag, &val, &msg))
		goto string_to_node_fail;
	lev0 = curlev = lev;
	root = curnode = create_node(xref, tag, val, NULL);
	while (string_to_line(&str, &lev, &xref, &tag, &val, &msg)) {
		if (lev == curlev) {
			node = create_node(xref, tag, val, nparent(curnode));
			nsibling(curnode) = node;
			curnode = node;
		} else if (lev == curlev + 1) {
			node = create_node(xref, tag, val, curnode);
			nchild(curnode) = node;
			curnode = node;
			curlev = lev;
		} else if (lev < curlev) {
			if (lev < lev0) {
				llwprintf("Error: line %d: illegal level",
				    flineno);
				goto string_to_node_fail;
			}
			while (lev < curlev) {
				curnode = nparent(curnode);
				curlev--;
			}
			node = create_node(xref, tag, val, nparent(curnode));
			nsibling(curnode) = node;
			curnode = node;
		} else {
			llwprintf("Error: line %d: illegal level", flineno);
			goto string_to_node_fail;
		}
	}
	if (!msg) {
		nodechk(root, "string_to_node");
		return root;
	}
string_to_node_fail:
	free_nodes(root);
	return NULL;
}
예제 #5
0
파일: nodeutls.c 프로젝트: MarcNo/lifelines
/*=========================================================
 * intersect_nodes -- Return intersection of two node trees
 * UNUSED CODE
 *=======================================================*/
NODE
intersect_nodes (NODE node1,
                 NODE node2,
                 BOOLEAN kids)  /* children matter */
{
	NODE prev1, curs1, next1, prev2, curs2, next2;
	NODE node3, curs3;

	if (!node1 || !node2) return NULL;
	node1 = copy_nodes(node1, TRUE, TRUE);
	node2 = copy_nodes(node2, TRUE, TRUE);
	node3 = curs3 = NULL;

	prev1 = NULL;
	curs1 = node1;
	while (curs1) {
		prev2 = NULL;
		curs2 = node2;
		while (curs2 && !iso_nodes(curs1, curs2, kids, FALSE)) {
			prev2 = curs2;
			curs2 = nsibling(curs2);
		}
		if (curs2) {
			next2 = nsibling(curs2);
			nsibling(curs2) = NULL;

			if (node3)
				curs3 = nsibling(curs3) = curs2;
			else
				node3 = curs3 = curs2;
			if (prev2)
				nsibling(prev2) = next2;
			else
				node2 = next2;

			next1 = nsibling(curs1);
			nsibling(curs1) = NULL;
			free_nodes(curs1);
			if (prev1)
				nsibling(prev1) = next1;
			else
				node1 = next1;
			curs1 = next1;

		} else {
			prev1 = curs1;
			curs1 = nsibling(curs1);
		}
	}
	free_nodes(node1);
	free_nodes(node2);
	return node3;
}
예제 #6
0
/*===============================================
 * 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);
	}
}
예제 #7
0
파일: add.c 프로젝트: MarcNo/lifelines
/*================================================================
 * 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;
}
예제 #8
0
파일: nodeutls.c 프로젝트: MarcNo/lifelines
/*================================================================
 * value_in_nodes -- See if a list of nodes contains a given value
 * UNUSED CODE
 *==============================================================*/
BOOLEAN
value_in_nodes (NODE node,
                STRING value)
{
	while (node) {
		if (eqstr(value, nval(node))) return TRUE;
		node = nsibling(node);
	}
	return FALSE;
}
예제 #9
0
/*==============================================
 * check_node_recursively -- Check node and all descendant nodes
 *============================================*/
static void
check_node_recursively (NODE node, NODE parent, INT level, CNSTRING key, CNSTRING scope)
{
	NODE child=0;

	check_node(node, parent, level, key, scope);

	for (child = nchild(node); child; child = nsibling(child)) {
		check_node_recursively(child, node, level+1, key, scope);
	}
}
예제 #10
0
파일: nodeutls.c 프로젝트: MarcNo/lifelines
/*========================================================================
 * classify_nodes -- Convert two value lists to three lists - first
 *   returned list holds all values that were only in original first
 *   list - second returned list holds all values that were just in
 *   original second list - third returned list holds all values that were
 *   in both original lists
 * pnode1 & pnode2 are the input lists to compare
 * pnode1, pnode2, & pnode3 are the output lists.
 *======================================================================*/
void
classify_nodes (NODE *pnode1, NODE *pnode2, NODE *pnode3)
{
	NODE node1, node2, node3, curs1, curs2, curs3;
	NODE prev1, prev2, next2;

	curs1 = node1 = unique_nodes(*pnode1, FALSE);
	curs2 = node2 = unique_nodes(*pnode2, FALSE);
	curs3 = node3 = prev1 = prev2 = NULL;

	while (curs1 && curs2) {
		if (!nvaldiff(curs1, curs2)) {
			if (node3)
				curs3 = nsibling(curs3) = curs1;
			else
				node3 = curs3 = curs1;

			curs1 = nsibling(curs1);
			nsibling(curs3) = NULL;

			if (prev1)
				nsibling(prev1) = curs1;
			else
				node1 = curs1;

			next2 = nsibling(curs2);
			if (prev2)
				nsibling(prev2) = next2;
			else
				node2 = next2;
			nsibling(curs2) = NULL;
			free_nodes(curs2);
			curs2 = next2;
			continue;
		}
		prev2 = curs2;
		if ((curs2 = nsibling(curs2))) continue;
		prev1 = curs1;
		curs1 = nsibling(curs1);
		prev2 = NULL;
		curs2 = node2;
	}
	*pnode1 = node1;
	*pnode2 = node2;
	*pnode3 = node3;
}
예제 #11
0
파일: pedigree.c 프로젝트: MarcNo/lifelines
/*=================================================
 * count_nodes -- count descendent nodes
 *===============================================*/
static void
count_nodes (NODE node, INT gen, INT maxgen, INT * count)
{
	(*count)++;

	if (gen < maxgen) {
		NODE child;
		for (child = nchild(node); child; child = nsibling(child)) {
			count_nodes(child, gen+1, maxgen, count);
		}
	}
}
예제 #12
0
파일: dbverify.c 프로젝트: MarcNo/lifelines
/*============================================
 * cgn_callback -- callback for name traversal
 *  for checking for ghost names
 * Created: 2001/01/01, Perry Rapp
 *==========================================*/
static BOOLEAN
cgn_callback (TRAV_NAMES_FUNC_ARGS(key, name, newset, param))
{
    /* a name record which points at indi=key */
    RECORD indi0 = NULL;
    NODE indi = NULL;
    param=param; /* unused */

    /* bail out immediately if not INDI */
    if (key[0] != 'I') {
        report_error(ERR_NONINDINAME
                     , _("Non-indi name, key=%s, name=%s")
                     , key, name);
        return 1; /* continue traversal */
    }

    indi0 = qkey_to_irecord(key);
    indi = nztop(indi0);

    if (newset) {
        finish_and_delete_nameset();
        soundexseq = create_indiseq_sval();
    }

    append_indiseq_sval(soundexseq, strsave(key), (STRING)name, strsave(name)
                        , TRUE, TRUE); /* sure, alloc */

    if (!indi) {
        report_error(ERR_ORPHANNAME, _("Orphaned name: %s"), name);
        if (todo.fix_ghosts)
            enqueue_list(tofix, (VPTR)alloc_namerefn(name, key, ERR_ORPHANNAME));
    } else {
        NODE node;
        BOOLEAN found=FALSE;
        NODE nam, refn, sex, body, famc, fams;
        split_indi_old(indi, &nam, &refn, &sex, &body, &famc, &fams);
        for (node = nam; node; node = nsibling(node)) {
            if (!strcmp(nval(node), name))
                found=TRUE;
        }
        join_indi(indi, nam, refn, sex, body, famc, fams);
        if (!found) {
            report_error(ERR_GHOSTNAME, _("Ghost name: %s -> %s"), name, key);
            if (todo.fix_ghosts)
                enqueue_list(tofix, (VPTR)alloc_namerefn(name, key, ERR_GHOSTNAME));
        }
    }

    if (noisy)
        report_progress("Name: %s", name);

    return 1; /* continue traversal */
}
예제 #13
0
파일: nodeutls.c 프로젝트: MarcNo/lifelines
/*======================================================================
 * unique_nodes -- Remove duplicates from list of nodes -- original list
 *   is modified
 *====================================================================*/
NODE
unique_nodes (NODE node,
              BOOLEAN kids)     /* children matter */
{
	NODE node0 = node, prev, this, next;

	if (!node) return NULL;
	while (node) {
		prev = node;
		this = nsibling(node);
		while (this) {
			if (iso_nodes(node, this, kids, FALSE)) {
				nsibling(prev) = next = nsibling(this);
				nsibling(this) = NULL;
				free_nodes(this);
				this = next;
			} else {
				prev = this;
				this = nsibling(this);
			}
		}
		node = nsibling(node);
	}
	return node0;
}
예제 #14
0
파일: nodeio.c 프로젝트: MarcNo/lifelines
/*=====================================
 * swrite_nodes -- Write tree to string
 *===================================*/
static STRING
swrite_nodes (INT levl,       /* level */
              NODE node,      /* root */
              STRING p)       /* write string */
{
	while (node) {
		p = swrite_node(levl, node, p);
		if (nchild(node))
			p = swrite_nodes(levl + 1, nchild(node), p);
		node = nsibling(node);
	}
	return p;
}
예제 #15
0
파일: pedigree.c 프로젝트: MarcNo/lifelines
/*=================================
 * trav_pre_print_nod -- traverse node tree,
 *  printing nodes in preorder
 * Created: 2001/01/27, Perry Rapp
 *===============================*/
static void
trav_pre_print_nod (NODE node, INT * row, INT gen, INT indent,
	CANVASDATA canvas, INT gdvw)
{
	NODE child;
	struct node_print_param_s npp;
	npp.node = node;
	npp.gdvw = gdvw;
	/* all display printing passes thru generic print_to_screen,
	which handles scrolling */
	print_to_screen(gen, indent, row, &node_lineprint, &npp, canvas);
	for (child=nchild(node); child; child=nsibling(child))
		trav_pre_print_nod(child, row, gen+1, indent, canvas, gdvw);
}
예제 #16
0
/*===============================
 * 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;
}
예제 #17
0
파일: add.c 프로젝트: MarcNo/lifelines
/*==========================================================
 * add_new_indi_to_db -- Add newly created person to database
 * (no user interaction)
 * creates record & adds to cache
 *========================================================*/
static void
add_new_indi_to_db (RECORD indi0)
{
	NODE name, refn, sex, body, dumb, node;
	char key[MAXKEYWIDTH]="";
	INT keynum=0;
	NODE indi = nztop(indi0);

	split_indi_old(indi, &name, &refn, &sex, &body, &dumb, &dumb);
	keynum = getixrefnum();
	sprintf(key, "I%ld", keynum);
	init_new_record(indi0, key);
	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, NULL, NULL);
	resolve_refn_links(indi);
	indi_to_dbase(indi);
	add_new_indi_to_cache(indi0);
}
예제 #18
0
파일: nodeio.c 프로젝트: MarcNo/lifelines
/*==========================================
 * write_nodes -- Write NODEs to GEDCOM file
 *========================================*/
void
write_nodes (INT levl,       /* level */
             FILE *fp,       /* file */
             XLAT ttm,   /* char map */
             NODE node,      /* root */
             BOOLEAN indent, /* indent? */
             BOOLEAN kids,   /* output kids? */
             BOOLEAN sibs)   /* output sibs? */
{
	if (!node) return;
	write_node(levl, fp, ttm, node, indent);
	if (kids)
		write_nodes(levl+1, fp, ttm, nchild(node), indent, TRUE, TRUE);
	if (sibs)
		write_nodes(levl, fp, ttm, nsibling(node), indent, kids, TRUE);
}
예제 #19
0
파일: dbverify.c 프로젝트: MarcNo/lifelines
/*===================================
 * 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;
}
예제 #20
0
파일: nodeutls.c 프로젝트: MarcNo/lifelines
/*==============================================
 * union_nodes -- Return union of two node trees
 *============================================*/
NODE
union_nodes (NODE node1,
             NODE node2,
             BOOLEAN kids,      /* children matter */
             BOOLEAN copy)      /* copy operands first */
{
	NODE curs1, next1, prev1, curs2, prev2;

	if (copy) node1 = copy_nodes(node1, TRUE, TRUE);
	if (copy) node2 = copy_nodes(node2, TRUE, TRUE);
	prev2 = NULL;
	curs2 = node2;
	while (curs2) {
		prev1 = NULL;
		curs1 = node1;
		while (curs1 && !iso_nodes(curs1, curs2, kids, FALSE)) {
			prev1 = curs1;
			curs1 = nsibling(curs1);
		}
		if (curs1) {
			next1 = nsibling(curs1);
			nsibling(curs1) = NULL;
			free_nodes(curs1);
			if (prev1)
				nsibling(prev1) = next1;
			else
				node1 = next1;
		}
		prev2 = curs2;
		curs2 = nsibling(curs2);
	}
	if (prev2) {
		nsibling(prev2) = node1;
		return node2;
	}
	return node1;
}
예제 #21
0
/*===================================
 * 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;
}
예제 #22
0
파일: nodeio.c 프로젝트: MarcNo/lifelines
/*==============================================================
 * next_fp_to_node -- Convert next GEDCOM record in file to tree
 *
 *  fp:   [IN]  file that holds GEDCOM record/s
 *  list: [IN]  can be list at level 0?
 *  ttm:  [IN]  character translation table
 *  pmsg: [OUT] possible error message
 *  peof: [OUT] set true if file is at end of file
 * callers should probably be converted to calling next_fp_to_record
 *============================================================*/
NODE
next_fp_to_node (FILE *fp, BOOLEAN list, XLAT ttm,
	STRING *pmsg, BOOLEAN *peof)
{
	INT curlev, bcode, rc;
	NODE root, node, curnode;
	static char scratch[100]; /*we return errors with this, keep it around*/
	*pmsg = NULL;
	*peof = FALSE;
	if (ateof) {
		ateof = *peof = TRUE;
		lahead = FALSE;
		return NULL;
	}
	if (!lahead) {
		rc = file_to_line(fp, ttm, &lev, &xref, &tag, &val, pmsg);
		if (rc == DONE) {
			ateof = *peof = TRUE;
			return NULL;
		} else if (rc == ERROR)
			return NULL;
		lahead = TRUE;
	}
	curlev = lev;
	if (curlev != lev0)  {
		*pmsg = _(qSrerwlv);
		return NULL;
	}
	root = curnode = create_node(xref, tag, val, NULL);
	bcode = OKAY;
	rc = file_to_line(fp, ttm, &lev, &xref, &tag, &val, pmsg);
	while (rc == OKAY) {
		if (lev == curlev) {
			if (lev == lev0 && !list) {
				bcode = DONE;
				break;
			}
			node = create_node(xref, tag, val, nparent(curnode));
			nsibling(curnode) = node;
			curnode = node;
		} else if (lev == curlev + 1) {
			node = create_node(xref, tag, val, curnode);
			nchild(curnode) = node;
			curnode = node;
			curlev = lev;
		} else if (lev < curlev) {
			if (lev < lev0) {
				sprintf(scratch, _(qSrerilv), flineno);
				*pmsg = scratch;
				bcode = ERROR;
				break;
			}
			if (lev == lev0 && !list) {
				bcode = DONE;
				break;
			}
			while (lev < curlev) {
				curnode = nparent(curnode);
				curlev--;
			}
			node = create_node(xref, tag, val, nparent(curnode));
			nsibling(curnode) = node;
			curnode = node;
		} else {
			sprintf(scratch, _(qSrerilv), flineno);
			*pmsg = scratch;
			bcode = ERROR;
			break;
		}
		rc = file_to_line(fp, ttm, &lev, &xref, &tag, &val, pmsg);
	}
	if (bcode == DONE) return root;
	if (bcode == ERROR || rc == ERROR) {
		free_nodes(root);
		return NULL;
	}
	lahead = FALSE;
	ateof = *peof = TRUE;
	return root;
}
예제 #23
0
파일: dbverify.c 프로젝트: MarcNo/lifelines
/*=====================================
 * process_fam -- process indi record
 *  checking in pass 1, fixing in pass 2
 *===================================*/
static void
process_fam (RECORD rec)
{
    NODE fam0, fam1;
    NODE fref1, husb1, wife1, chil1, rest1;
    NODE node1;
    INT members = 0;
    BOOLEAN altered=FALSE;
    BOOLEAN needfix=FALSE;
    CNSTRING key = nzkey(rec);

    fam0 = nztop(rec);
    if (todo.pass==1) {
        fam1 = fam0;
    } else {
        fam1 = copy_node_subtree(fam0);
    }
    split_fam(fam1, &fref1, &husb1, &wife1, &chil1, &rest1);

    /* check refns */
    for (node1 = fref1; node1; node1 = nsibling(node1)) {
        /* STRING refn=nval(node1); */
        /* TO DO: verify that refn is in db */
    }
    /* check husbs */
    for (node1 = husb1; node1; node1 = nsibling(node1)) {
        STRING husbkey=rmvat(nval(node1));
        NODE husb = qkey_to_indi(husbkey);
        members++;
        if (!husb) {
            if (todo.pass == 1) {
                report_error(ERR_BADHUSBREF
                             , _("Bad HUSB reference (%s) in family %s")
                             , husbkey, key);
                needfix=TRUE;
            } else {
                if (fix_bad_pointer(key, rec, node1)) {
                    report_fix(ERR_BADHUSBREF
                               , _("Fixed Bad HUSB reference (%s) in family %s")
                               , husbkey, key);
                    altered=TRUE;
                }
            }
        } else {
            /* look for family (key) in husb */
            if (!find_xref(key, husb, "FAMS", NULL)) {
                report_error(ERR_EXTRAHUSB
                             , _("Improper HUSB (%s) in family (%s)")
                             , husbkey, key);
            }
        }
    }
    /* check wives */
    for (node1 = wife1; node1; node1 = nsibling(node1)) {
        STRING wifekey=rmvat(nval(node1));
        NODE wife = qkey_to_indi(wifekey);
        members++;
        if (!wife) {
            if (todo.pass == 1) {
                report_error(ERR_BADWIFEREF
                             , _("Bad wife reference (%s) in family %s")
                             , wifekey, key);
                needfix=TRUE;
            } else {
                if (fix_bad_pointer(key, rec, node1)) {
                    report_fix(ERR_BADWIFEREF
                               , _("Fixed Bad wife reference (%s) in family %s")
                               , printkey(wifekey), key);
                    altered=TRUE;
                }
            }
        } else {
            /* look for family (key) in wife */
            if (!find_xref(key, wife, "FAMS", NULL)) {
                report_error(ERR_EXTRAWIFE
                             , _("Improper wife (%s) in family (%s)")
                             , printkey(wifekey), key);
            }
        }
    }
    /* check children */
    for (node1 = chil1; node1; node1 = nsibling(node1)) {
        STRING chilkey=rmvat(nval(node1));
        NODE child = qkey_to_indi(chilkey);
        members++;
        if (!child) {
            if (todo.pass == 1) {
                report_error(ERR_BADCHILDREF
                             , _("Bad child reference (%s) in family %s")
                             , printkey(chilkey), key);
                needfix=TRUE;
            } else {
                if (fix_bad_pointer(key, rec, node1)) {
                    report_fix(ERR_BADCHILDREF
                               , _("Fixed bad child reference (%s) in family %s")
                               , printkey(chilkey), key);
                    altered=TRUE;
                }
            }
        } else {
            /* look for family (key) in child */
            if (!find_xref(key, child, "FAMC", NULL)) {
                report_error(ERR_EXTRACHILD
                             , _("Improper child: Child (%s) without FAMC reference to family (%s)")
                             , printkey(chilkey), key);
            }
        }
    }
    join_fam(fam1, fref1, husb1, wife1, chil1, rest1);
    /* check for undersized family */
    if (!members) {
        report_error(ERR_EMPTYFAM, _("Empty family (%s)"), key);
    } else if (members == 1) {
        report_error(ERR_SOLOFAM, _("Single person family (%s)"), key);
    }


    if (altered) {
        /* must normalize, as some lineage references may have been
        altered to non-lineage tags to fix broken pointers */
        normalize_fam(fam1);

        /* write to database */
        replace_fam(fam0, fam1);

    } else if (needfix) {
        enqueue_list(tofix, strsave(key));
    }
}
예제 #24
0
파일: dbverify.c 프로젝트: MarcNo/lifelines
/*=====================================
 * process_indi -- process indi record
 *  checking in pass 1, fixing in pass 2
 *===================================*/
static void
process_indi (RECORD rec)
{
    NODE indi0, indi1;
    NODE name1, refn1, sex1, body1, famc1, fams1;
    NODE node1;
    BOOLEAN altered=FALSE;
    BOOLEAN needfix=FALSE;
    CNSTRING key = nzkey(rec);

    indi0 = nztop(rec);
    if (todo.pass==1) {
        indi1 = indi0;
    } else {
        indi1 = copy_node_subtree(indi0);
    }
    split_indi_old(indi1, &name1, &refn1, &sex1, &body1, &famc1, &fams1);

    if (todo.pass == 1) {
        /* check names */
        for (node1 = name1; node1; node1 = nsibling(node1)) {
            STRING name=nval(node1);
            if (!valid_name(name)) {
                report_error(ERR_BADNAME, _("Bad name for individual %s: %s"), key, name);
            } else {
                /* TO DO: verify that name is in db */
            }
        }
        /* check refns */
        for (node1 = refn1; node1; node1 = nsibling(node1)) {
            /* STRING refn=nval(node1); */
            /* TO DO: verify that refn is in db */
        }
    }

    /* check parents */
    for (node1 = famc1; node1; node1 = nsibling(node1)) {
        STRING famkey=rmvat(nval(node1));
        NODE fam2 = qkey_to_fam(famkey);
        if (!fam2) {
            if (todo.pass == 1) {
                report_error(ERR_BADFAMREF, _("Bad family reference (%s) individual %s"), famkey, key);
            }
        } else {
            /* look for indi1 (key) in fam2's children */
            if (!find_xref(key, fam2, "CHIL", NULL)) {
                if (todo.pass == 1) {
                    report_error(ERR_MISSINGCHILD, _("Missing child (%s) in family (%s)"), key, famkey);
                    needfix=TRUE;
                } else {
                    if (fix_bad_pointer(key, rec, node1)) {
                        report_fix(ERR_MISSINGCHILD, _("Fixed missing child (%s) in family (%s)"), key, famkey);
                        altered=TRUE;
                    }
                }
            }
        }
    }

    /* check spouses */
    for (node1 = fams1; node1; node1 = nsibling(node1)) {
        STRING famkey=rmvat(nval(node1));
        NODE fam2 = qkey_to_fam(famkey);
        if (!fam2) {
            if (todo.pass == 1) {
                report_error(ERR_BADFAMREF, _("Bad family reference (%s) individual %s"), famkey, key);
            }
        } else {
            /* look for indi1 (key) in fam2's spouses */
            if (!find_xref(key, fam2, "HUSB", "WIFE")) {
                if (todo.pass == 1) {
                    report_error(ERR_MISSINGSPOUSE, _("Missing spouse (%s) in family (%s)"), key, famkey);
                    needfix=TRUE;
                } else {
                    if (fix_bad_pointer(key, rec, node1)) {
                        report_fix(ERR_MISSINGSPOUSE, _("Fixed missing spouse (%s) in family (%s)"), key, famkey);
                        altered=TRUE;
                    }
                }
            }
        }
    }

    join_indi(indi1, name1, refn1, sex1, body1, famc1, fams1);
    if (altered) {
        /* must normalize, as some lineage references may have been
        altered to non-lineage tags to fix broken pointers */
        normalize_indi(indi1);

        /* write to database */
        replace_indi(indi0, indi1);

    } else if (needfix) {
        enqueue_list(tofix, strsave(key));
    }
}
예제 #25
0
/*================================================
 * 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);
}
예제 #26
0
/*=======================================
 * 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;
}
예제 #27
0
파일: pedigree.c 프로젝트: MarcNo/lifelines
/*=================================================
 * 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;
}
예제 #28
0
/*=======================================
 * llrpt_addnode -- Add a node to a GEDCOM tree
 * usage: addnode(NODE, NODE, NODE) -> VOID
 * args: (node being added, parent, previous child)
 *=====================================*/
PVALUE
llrpt_addnode (PNODE node, SYMTAB stab, BOOLEAN *eflg)
{
	PNODE arg = iargs(node);
	NODE newchild, next, prnt, prev;

	/* first argument, node (must be nonnull) */
	PVALUE val = eval_and_coerce(PGNODE, arg, stab, eflg);
	if (*eflg) {
		prog_var_error(node, stab, arg, val, nonnodx, "addnode", "1");
		delete_pvalue(val);
		return NULL;
	}
	newchild = remove_node_and_delete_pvalue(&val);
	if (!newchild) {
		prog_var_error(node, stab, arg, val, nonnodx, "addnode", "1");
		return NULL;
	}

	/* second argument, parent (must be nonnull) */
	val = eval_and_coerce(PGNODE, arg=inext(arg), stab, eflg);
	if (*eflg) {
		prog_var_error(node, stab, arg, val, nonnodx, "addnode", "2");
		return NULL;
	}
	prnt = remove_node_and_delete_pvalue(&val);
	if (!prnt) {
		prog_var_error(node, stab, arg, val, nonnodx, "addnode", "2");
		return NULL;
	}

	/* third argument, prior sibling (may be null) */
	val = eval_and_coerce(PGNODE, arg=inext(arg), stab, eflg);
	if (*eflg) {
		prog_var_error(node, stab, arg, val, nonnodx, "addnode", "3");
		delete_pvalue(val);
		return NULL;
	}
	prev = remove_node_and_delete_pvalue(&val);

	if (prev) {
		/* Check that previous sibling actually is child of new parent */
		if (prnt != nparent(prev)) {
			prog_error(node, "2nd arg to addnode must be parent of 3rd arg");
			*eflg = 1;
			return NULL;
		}
	}

	/* reparent node, but ensure its locking is only relative to new parent */
	dolock_node_in_cache(newchild, FALSE);
	nparent(newchild) = prnt;
	newchild->n_cel = prnt->n_cel;
	set_temp_node(newchild, is_temp_node(prnt));
	dolock_node_in_cache(newchild, TRUE);
	if (prev == NULL) {
		next = nchild(prnt);
		nchild(prnt) = newchild;
	} else {
		next = nsibling(prev);
		nsibling(prev) = newchild;
	}
	nsibling(newchild) = next;
	return NULL;
}