[comp.databases] Family Tree Problem

flak@slovax.UUCP (Dan Flak) (01/19/88)

Last week, I presented a problem to track a family tree:

The schema looks like:
 
    database people
 
    file family
    
    field name   type character 20 index primary
    field parent type character 20 index dups.

    end

Rules of the game:

   (1)  The first record is "seeded" into the database.
   (2)  All subsequent entries for the "parent" field must
        be validated from the "name" field. (That is, all
        future generations can only come from people who
        already exist).

The challenge:

Write an ACE or C program using the Informix Applications Language
Library (ALL) which will accept as its first argument the name of any
ancestor, and produce a list of all descendants for as many generations
as are loaded in the database.

As some of you have pointed out, and I am inclined to agree, ACE can
handle a fixed number of generations, but can't handle an indefinate
number.

Below is my "hack" in C. Cut away at the dotted lines (don't forget
to remove the .signature), save into a <filename>. Unpack using
"sh <filename>", and then do a "make demo".

#-------------------- cut here ------------------
#! /bin/sh
echo shar: extracting Makefile
if test -f 'Makefile' ; then
    echo shar: will not overwrite Makefile
else
    cat > Makefile << '@EOF Makefile'
demo:			family family.dat family.f.frm
				echo "Welcome to John's family"
				./family "JOHN (0)"
				touch demo

family:			family.c
				cc -o family family.c -ldb

family.dat:		family.s family.dlf
				dbbuild family.s
				dbstatus people < load.cmd

family.f.frm:	family.f
				formbuild family.f
@EOF Makefile
if test 298 -ne "`wc -c < Makefile`"; then
    echo shar: checksum error - number of characters should be 298
fi
fi
chmod 666 Makefile
echo shar: extracting README
if test -f 'README' ; then
    echo shar: will not overwrite README
else
    cat > README << '@EOF README'
The family program was set up to meet the following challenge.

 The schema looks like:
 
database people
 
file family

field name   type character 20 index primary
field parent type character 20 index dups.

end

Rules of the game:

   (1)  The first record is "seeded" into the database.
   (2)  All subsequent entries for the "parent" field must
        be validated from the "name" field. (That is, all
        future generations can only come from people who
        already exist).

The challenge:

Write a C program using the Informix Applications Language Library (ALL)
which will accept as its first argument the name of any ancestor, and
produce a list of all descendants for as many generations as are loaded
in the database.

The family tree (file "tree") is provided to produce a better picture
of what's happening. I've included parenthesized numbers as part of the
20 character name. The parenthesis serve two purposes:

    (1)  They keep the name unique (e.g. MARY (2) vs MARY (6))
    (2)  They also indicate the generation of the person.
@EOF README
if test 1058 -ne "`wc -c < README`"; then
    echo shar: checksum error - number of characters should be 1058
fi
fi
chmod 666 README
echo shar: extracting family.c
if test -f 'family.c' ; then
    echo shar: will not overwrite family.c
else
    cat > family.c << '@EOF family.c'

/*+
 * File: family.c
 * Description:
 *   Get all the descendants of a specified ancestor.
 *
 * Audit Trail:
 *   Program by Dan Flak - 14 Jan 88
 *
-*/

#ifdef VERSION
static char *SCCS = "%Z% %M%:%I% %G% %U%";
#endif

/* #includes */
#include <dbio.h>

/* #defines */
#define MAXCHILD 100                    /* Allowable number of descendants */

/* external variables */

/* referenced external functions */

/* internal functions */
int getchild();

/* global variables */
char ancestor[21];                      /* root level for family tree      */
char offspring[MAXCHILD][21];           /* array of descendant names       */
int  numrecs = 0;                       /* number of members in the tree   */

struct dbview family_view [] =          /* The dbview for the family file  */
    {
    {"name"},                           /* Person's name                   */
    {"parent"}                          /* Person's parent's name          */
    };

int family_view_num = 2;

struct
    {
    char   name[21];
    char   parent[21];
    } family_buf;

/* static variables */


/*<
 * Function: main (argc, argv)
 * Description:
 *    Get the first generation of children.
 *
 * Arguments:
 *    argv[1] = Ancestor's name
 *
 * Returns:
 *    None.
 *
 * Side Effects:
 *    None.
 *
 * Calls:
 *    Standard Informix ALL Calls.
 *
 * PDL:
 *    Open database, set up files, and get the first generation of 
 *    children. Call getchild as long as offspring can be found.
 *
>*/

main (argc, argv)
int  argc;
char *argv[];
{
int cc, val, len;                       /* Informix return values          */
int i;                                  /* Counter                         */

sprintf (ancestor,"%-20.20s",argv[1]);  /* Put ancestor's name into the    *
                                         * correct format                  */

/*  Note, a really "good" C programmer would do error checking on cc       */
/*  Invoking ALL calls to set up database                                  */

cc = dbselect (DBOPEN, "people");

cc = dbselect (FILEOPEN, "family");

cc = dbstructview ("family", family_view, family_view_num);

cc = dbselfield ("family", "parent", ACCKEYED);

getchild (ancestor);                    /* get the first generation of     *
                                         * children                        */

for (i = 0; i < numrecs; i++)           /* get successive generations      */
	{
	getchild (offspring[i]);
	}

cc = dbselect (FILECLOSE, "family");

cc = dbselect (DBCLOSE, "people");

for (i = 0; i < numrecs; i++)
	{
	printf ("%s\n",offspring[i]);
	}
}


/*<
 * Function: getchild (pname)
 * Description:
 *    Get the immediate children of the specified parent.
 *
 * Arguments:
 *    pname = parent name
 *
 * Returns:
 *    None.
 *
 * Side Effects:
 *    The global variable, numrecs, is incremented for each child found.
 *
 * Calls:
 *    dbfind
 *
 * PDL:
 *    Find first child of parent, step through records while still finding
 *    children for that parent.
 *
>*/

int getchild (pname)
char *pname;
{
int cc, val, len;                       /* Informix return values          */

cc = dbfind ("family", COMPARISON, pname, &len, &family_buf);
                                        /* Find the first child belonging  *
                                         * to the parent                   */

while (cc == 0)                         /* Find subsequent children        */
	{
                                        /* Fill the array and increment    *
                                         * the total number of children    *
                                         * found                           */

	strcpy (offspring[numrecs], family_buf.name);
	numrecs++;

                                        /* Find the next child, and see if *
                                         * it still has the same parent    */

	cc = dbfind ("family", NEXT, &val, &len, &family_buf);
	if (cc == 0)
		{
		cc = strcmp (family_buf.parent, pname);
		}
	}
}
@EOF family.c
if test 4015 -ne "`wc -c < family.c`"; then
    echo shar: checksum error - number of characters should be 4015
fi
fi
chmod 666 family.c
echo shar: extracting family.dlf
if test -f 'family.dlf' ; then
    echo shar: will not overwrite family.dlf
else
    cat > family.dlf << '@EOF family.dlf'
JOHN (0)      |              |
JOHN JR (1)   |JOHN (0)      |
MARY (1)      |JOHN (0)      |
SALLY (1)     |JOHN (0)      |
JOHN III (2)  |JOHN JR (1)   |
MARY (2)      |JOHN JR (1)   |
HAL (2)       |MARY (1)      |
CAL (2)       |MARY (1)      |
JOHN IV (3)   |JOHN III (2)  |
JANE (3)      |JOHN III (2)  |
JOE (3)       |MARY (2)      |
WILLIE (3)    |MARY (2)      |
JOHN V (4)    |JOHN IV (3)   |
HARRY (4)     |JANE (3)      |
JOHN VI (5)   |JOHN V (4)    |
JOHN VII (6)  |JOHN VI (5)   |
MARY (6)      |JOHN VI (5)   |
SUSAN (3)     |CAL (2)       |
@EOF family.dlf
if test 558 -ne "`wc -c < family.dlf`"; then
    echo shar: checksum error - number of characters should be 558
fi
fi
chmod 666 family.dlf
echo shar: extracting family.f
if test -f 'family.f' ; then
    echo shar: will not overwrite family.f
else
    cat > family.f << '@EOF family.f'
database people

screen
{

Name     [a                   ]

Parent   [b                   ]
}
end
attributes

a= name, upshift;

b = parent, upshift, lookup from *name;

end
@EOF family.f
if test 174 -ne "`wc -c < family.f`"; then
    echo shar: checksum error - number of characters should be 174
fi
fi
chmod 666 family.f
echo shar: extracting family.s
if test -f 'family.s' ; then
    echo shar: will not overwrite family.s
else
    cat > family.s << '@EOF family.s'
database people

file family

field name   type character 20 index primary
field parent type character 20 index dups

end
@EOF family.s
if test 122 -ne "`wc -c < family.s`"; then
    echo shar: checksum error - number of characters should be 122
fi
fi
chmod 666 family.s
echo shar: extracting load.cmd
if test -f 'load.cmd' ; then
    echo shar: will not overwrite load.cmd
else
    cat > load.cmd << '@EOF load.cmd'
load ascii family from "family.dlf"
bye
@EOF load.cmd
if test 40 -ne "`wc -c < load.cmd`"; then
    echo shar: checksum error - number of characters should be 40
fi
fi
chmod 666 load.cmd
echo shar: extracting tree
if test -f 'tree' ; then
    echo shar: will not overwrite tree
else
    cat > tree << '@EOF tree'
                                         JOHN (0)
                                             |
                        -----------------------------------------
                        |                    |                  |
                    JOHN JR (1)         SALLY (1)           MARY (1)
                        |                                       |
          -----------------------------                   ------------
          |                           |                   |          |
      JOHN III (2)                MARY (2)             CAL (2)    HAL (2)
          |                           |                   |
          --------------         --------------           |
          |            |         |            |           |
      JOHN IV (3)  JANE (3)   JOE (3)   WILLIE (3)   SUSAN (3)
          |
      JOHN V (4)
          |
      JOHN VI (5)
          |
    _______________
    |             |
JOHN VII (6)   MARY (6)
@EOF tree
if test 958 -ne "`wc -c < tree`"; then
    echo shar: checksum error - number of characters should be 958
fi
fi
chmod 666 tree
-- 
                  {hplsla,uw-beaver}!tikal!slovax!flak
Dan Flak-R & D Associates,3625 Perkins Lane SW,Tacoma,Wa 98499,206-581-1322