v18i012: Geneal, a genealogy browsing program, Part02/03

Rich Salz rsalz at uunet.uu.net
Fri Mar 10 08:56:16 AEST 1989


Submitted-by: Jim McBeath <voder!sci!gumby!jimmc>
Posting-number: Volume 18, Issue 12
Archive-name: geneal/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 4)."
# Contents:  Makefile PGMR.DOC famlists.c fgsubs.c gconsist.c index.c
#   lists.c misc.c
# Wrapped by rsalz at fig.bbn.com on Thu Mar  9 15:55:00 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(4743 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#Makefile for geneal
X#last edit 11-Jan-1988 09:29:05 by jimmc ()
X
XPROGRAM	      = geneal
XDEST	      = /tmp
X
X#Program to pack file into shell archive
XSHAR =	shar
X
X#Program to check for symbol name clashes
XCLASH = clash
X
XMKMF =	mkmf
X
XMAKE		=	make $(MFLAGS)
XMAKEFILE	= Makefile
X
XSPINDIR = ../spin
X
XBKPMACH = wheel
XBKPDIR = geneal/geneal.bak
X
XINCLUDE = -I$(SPINDIR)
XCCOPTS = -g
X
X# careful with this variable.  it is really set down in target "depend:"
XCFLAGS  = $(CCOPTS) -I../spin
X
XLINTFLAGS = -bauz $(INCLUDE)
XLINTLIBS = $(SPINDIR)/llib-lspin.a.ln
X
XEXTHDRS	      = ../spin/spin.h \
X		../spin/xalloc.h \
X		/usr/include/ctype.h \
X		/usr/include/ctype.h \
X		/usr/include/stdio.h \
X		/usr/include/strings.h \
X		/usr/include/sys/stat.h \
X		/usr/include/sys/types.h
X
XHDRS	      = dataman.h \
X		famntree.h \
X		geneal.h \
X		index.h \
X		pagemap.h
X
XLDFLAGS	      = -g
X
XLINKER	      = cc
X
XMAKEFILE      = Makefile
X
XOBJS	      = browse.o \
X		dataman.o \
X		dates.o \
X		datesort.o \
X		dlists.o \
X		errorman.o \
X		famatree.o \
X		famdtree.o \
X		family.o \
X		familyh.o \
X		famlists.o \
X		famntree.o \
X		fgdat.o \
X		fgsubs.o \
X		gconsist.o \
X		geneal.o \
X		index.o \
X		indivs.o \
X		lists.o \
X		misc.o \
X		pagemap.o \
X		strings.o \
X		vsprintf.o
X
XSRCS	      = browse.c \
X		dataman.c \
X		dates.c \
X		datesort.c \
X		dlists.c \
X		errorman.c \
X		famatree.c \
X		famdtree.c \
X		family.c \
X		familyh.c \
X		famlists.c \
X		famntree.c \
X		fgdat.c \
X		fgsubs.c \
X		gconsist.c \
X		geneal.c \
X		index.c \
X		indivs.c \
X		lists.c \
X		misc.c \
X		pagemap.c \
X		strings.c \
X		vsprintf.c
X
XLIBS          =	$(SPINDIR)/spin.a
X
XPRINT	      = pr
X
XDOCFILES =	README History PGMR.DOC geneal.n
XSHARFILES1 =	$(DOCFILES) $(MAKEFILE) $(HDRS) sample.dat
XSHARFILES2 =	[a-eh-zA-Z]*.c
XSHARFILES3 =	[fg]*.c
XSHAROUT1 =	$(PROGRAM).1.sh
XSHAROUT2 =	$(PROGRAM).2.sh
XSHAROUT3 =	$(PROGRAM).3.sh
X
Xall:		$(PROGRAM)
X
Xshar:		$(SHAROUT1) $(SHAROUT2) $(SHAROUT3)
X
Xclash:;		$(CLASH) $(HDRS) $(SRCS)
X
Xman:		$(PROGRAM).n
X		nroff -man $(PROGRAM).n > $(PROGRAM).man.new
X		mv $(PROGRAM).man.new $(PROGRAM).man
X
X$(SHAROUT1):	$(SHARFILES1)
X		$(SHAR) -n 1 -e 3 $(SHARFILES1) > $(SHAROUT1)
X
X$(SHAROUT2):	$(SHARFILES2)
X		$(SHAR) -n 2 -e 3 $(SHARFILES2) > $(SHAROUT2)
X
X$(SHAROUT3):	$(SHARFILES3)
X		$(SHAR) -n 3 -e 3 $(SHARFILES3) > $(SHAROUT3)
X
X$(PROGRAM):     $(OBJS) $(LIBS)
X		@echo -n "Loading $(PROGRAM) ... "
X		@$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -lm -o $(PROGRAM).new
X		@mv -f $(PROGRAM).new $(PROGRAM)
X		@echo "done"
X
Xclean:		tidy
X		@rm -f $(PROGRAM)
X
Xtidy:;		@rm -f $(OBJS)
X
Xdepend:		$(XDEP)
X		@echo Updating $(MAKEFILE) ...
X		@$(MKMF) -f $(MAKEFILE) \
X			CFLAGS='$$(CCOPTS) $(INCLUDE)'
X# PROGRAM makes mkmf use p.Makefile template
X#			PROGRAM=$(PROGRAM)
X
Xindex:;		@ctags -wx $(HDRS) $(SRCS)
X
Xlint:;		lint $(LINTFLAGS) $(SRCS) $(LINTLIBS)
X
Xinstall:	$(PROGRAM)
X		@echo Installing $(PROGRAM) in $(DEST)
X		@install -s $(PROGRAM) $(DEST)
X
Xprint:;		@$(PRINT) $(SRCS) $(HDRS)
X
Xprogram:        $(PROGRAM)
X
Xtags:           $(HDRS) $(SRCS); @ctags $(HDRS) $(SRCS)
X
Xupdate:		$(DEST)/$(PROGRAM)
X
X$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
X		@make -f $(MAKEFILE) DEST=$(DEST) install
X
Xbackup:;	tar chf - $(HDRS) $(SRCS) $(MAKEFILE) | \
X			rsh $(BKPMACH) 'cd $(BKPDIR); tar xBf -'
X###
Xbrowse.o: /usr/include/stdio.h geneal.h
Xdataman.o: /usr/include/stdio.h /usr/include/sys/types.h \
X	/usr/include/sys/stat.h \
X	/usr/include/ctype.h /usr/include/strings.h index.h dataman.h
Xdates.o: /usr/include/stdio.h /usr/include/ctype.h /usr/include/strings.h \
X	geneal.h
Xdatesort.o: /usr/include/stdio.h ../spin/xalloc.h geneal.h
Xdlists.o: /usr/include/stdio.h geneal.h
Xerrorman.o: /usr/include/stdio.h /usr/include/strings.h
Xfamatree.o: /usr/include/stdio.h geneal.h
Xfamdtree.o: /usr/include/stdio.h geneal.h /usr/include/ctype.h
Xfamily.o: /usr/include/stdio.h /usr/include/ctype.h geneal.h pagemap.h
Xfamilyh.o: /usr/include/stdio.h /usr/include/ctype.h /usr/include/strings.h \
X	geneal.h
Xfamlists.o: /usr/include/stdio.h geneal.h
Xfamntree.o: /usr/include/stdio.h /usr/include/ctype.h \
X	/usr/include/strings.h geneal.h famntree.h ../spin/xalloc.h
Xfgdat.o: /usr/include/stdio.h /usr/include/strings.h geneal.h \
X	../spin/xalloc.h
Xfgsubs.o: /usr/include/stdio.h /usr/include/strings.h dataman.h geneal.h \
X	../spin/xalloc.h
Xgconsist.o: /usr/include/stdio.h geneal.h
Xgeneal.o: /usr/include/stdio.h /usr/include/ctype.h ../spin/spin.h geneal.h
Xindex.o: index.h
Xindivs.o: /usr/include/stdio.h geneal.h ../spin/xalloc.h
Xlists.o: /usr/include/stdio.h ../spin/xalloc.h geneal.h ../spin/spin.h
Xmisc.o: /usr/include/stdio.h /usr/include/strings.h ../spin/spin.h geneal.h
Xpagemap.o: pagemap.h ../spin/xalloc.h /usr/include/stdio.h
Xstrings.o: /usr/include/ctype.h /usr/include/strings.h ../spin/xalloc.h
Xvsprintf.o: /usr/include/stdio.h
END_OF_FILE
if test 4743 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'PGMR.DOC' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'PGMR.DOC'\"
else
echo shar: Extracting \"'PGMR.DOC'\" \(7887 characters\)
sed "s/^X//" >'PGMR.DOC' <<'END_OF_FILE'
XProgrammer Documentation for geneal			 8.Jan.88
XWritten by Jim McBeath (jimmc) at SCI
X
XThis document describes some of the files used in geneal.  It is
Xintended to assist someone who may want to fix a bug or improve the
Xprogram, or perhaps use one of the general modules for another program.
XIf you do any of the above, I would be interested in hearing about it.
X
XGeneal uses the spin library and some include files from that directory;
Xthis document does not describe those file.
X
XThe geneal program is divided up into a number of general purpose files
Xand some special files specifically for the geneal program.
XThe general files include:
X    dataman.c		Simple reader for formatted data file
X    errorman.c		Error message routines
X    fgdatsubs.c		Routines to simply getting info from the data file
X    index.c		Routines to manipulate large virtual arrays
X    pagemap.c		Build up an image of a page in memory and output it
X    strings.c		string manipulation routines
X    vsprintf.c		sprintf with a vector of arguments
XThe geneal-specific files include:
X    browse.c		the browse routines (T*)
X    dates.c		routines to reformat dates (used for BD and Ann lists)
X    datesort.c		routines to sort dates (for BD and Ann lists)
X    dlists.c		more routines to manipulate dates
X    fgdat.c		routines to generate one-line strings of data
X    family.c		generate a page of info about a family
X    familyh.c		generate alternate format page of info about a family
X    famatree.c		generate simple text ancestor trees
X    famdtree.c		generate simple text descendant trees
X    famlists.c		routines to deal with id lists
X    famntree.c		generate treepar format output for family trees
X    gconsist.c		check consistency of data
X    geneal.c		main program for geneal (includes spin function defs)
X    indivs.c		generate info about an individual
X    lists.c		id list manipulation routines
X    misc.c		miscellaneous routines
X
XMost of the general files are simple, and can be easily understood by
Xinspection.  The ones which deserve more extensive comment are dataman,
Xindex, and pagemap.
X
XDATAMAN is the interface to the datafile.  The datafile is in a particular
Xformat (records, lines, keywords/values) which is described at the beginning
Xof that file.  The routines in this module allow access to those data items,
Xtypically given a record number and a keyword name. There were two primary
Xconsiderations behind selecting the format used in the data file and the
Xtechniques used to read that file: 1) It
Xshould be in a text format so that it can be edited by a text editor (I
Xdidn't want to have to write a special datafile editor) and so that it is
Xhuman readable (so it could be used even before all the output routines
Xwere written for any particular program); 2) The program should be able to
Xhandle extremely large files.
X
XThe current implementation of DATAMAN works as follows: during initialization,
Xif there is not already an existing index file, it creates one by
Xscanning through the data file looking for the start of each record.  It
Xthen reads in the record number (an arbitrary but unique integer) and records
X(using the INDEX module) the seek location in the file for that record.
XWhen an access request is processed, it seeks to the location for that record
Xand then scans until it finds the requested keyword.  While this may not
Xbe particularly fast, it does satisfy the above two requirements of simple
Xtext file format and the ability to handle extremely large files.
X
XINDEX implements a large dynamic virtual array.  Each location in the
Xarray is allowed to contain an integer (or a pointer, if you have more
Xdata to store).  The routines allow you to set or get that value
X(integer or pointer) for a specified index (conceptually the index
Xinto a large table).  Internally, the data are stored in a number of
Xsmaller tables, so that unused locations in the array need not take
Xup memory space.  For example, if you needed an array with indexes of
X1 and 1000000, the amount of storage needed would be something like
X1K (due to chunk size).  The approach used works well for arrays with
Xclusters of dense use, e.g. the number 1 to 1000, 1M to 1M+1000, 10M to
X10M+1000, etc.  It does not work well for sparse but regular distributions,
Xe.g. 1K, 2K, 3K, etc.
X
XThe index table is implemented by a multi-level table.  The bottom level
Xtable contains N data items; the next level up contains N pointers to
Xbottom level tables; the next level up contains N pointers to first
Xlevel pointer tables, etc. as far as needed.  The number N can be selected
Xin the initialization call for an array.  When the set routine is called,
Xenough tables are built to access the requested index.  When the get
Xroutine is called, those tables are then followed to get to the data.
XThus the size of the table can grow as needed.
X
XPAGEMAP is an embryonic module used to generate a page of character
Xdata when it is desired to place things at particular locations on
Xthe page.  The caller first initializes a page (giving the size), and
Xthen calls routines to output strings and characters to particular
Xlocations (row/column) on the page.  At the end, he calls a function
Xto output the page.  This function was originally written in order to
Xdo the family tree part of geneal, but that was eventually done in
Xa different manner.
X
X
XThe geneal program has a number of non-general files intended only
Xfor use in this program.  These were mentioned above and will be
Xdiscussed in a little more detail here.
X
XBROWSE is the module which controls browsing through the data.  This is
Xwhere all the T* routines are.
X
XDATES, DATESORT, and DLIST are all routines which deal with dates.
X
XFGDAT is the basic interface to dataman.  It is used to read certain
Xitems of data for a particular person and return a string.  For example,
Xone of the functions reads the birth date and place, formats them into
Xa string ("b: 12-Oct-1855, Arlington, VA") and returns a pointer to that
Xstring.  This type of routine is used to build up a list of information
Xabout someone, to be output in some particular format.
X
XFAMILY and FAMILYH are functions to output information about a family in
Xa particular format.  The family page consists of information about the
Xparents, their children, and the children's spouses.  These functions
Xcall fgdat a lot to collect information, then format and outputs it.
XMostly pretty straightforward code.
X
XFAMATREE is a function to produce simple ancestor trees.  These trees
Xare produced using normal ascii characters on a printing terminal.
X
XFAMDTREE is a function to produce simple descendant trees.  As in famatree,
Xthey are produced with normal ascii characters.
X
XFAMLISTS contains the data-dependent list generation and manipulation
Xroutines (some of the L* routines).  See also LISTS.
X
XFAMNTREE is the routine which outputs treepar files which can be fed into
Xthe treepar program to draw trees.
X
XGCONSIST is the data consistency checker.  It looks for such things as
Xreferences which are not bi-directional (e.g. between parent and child),
Xetc.
X
XGENEAL is the main program.  It scans the command line for arguments
Xand switches, calls any initialization functions, initializes the spin
Xparser and defines all the functions available to the user.
XIf you are looking to make modifications to a specific command, you
Xshould look at the table of functions at the end of this file to see
Xwhat C function gets called for each user-visible function.
X
XINDIVS creates a short form data page for an individual.
X
XLISTS contains the data-independent list manipulation routines
X(some of the L* commands).  See also FAMLISTS.
X
XMISC contains a bunch of random stuff.  Much of this has to do with
Xinterfacing to the spin interpreter, doing such things as converting
Xfrom lists to arrays and back.  All of the G* commands are in this file.
X
XHave fun and send me your improvements!
X
X			-Jim McBeath
X			  8.Jan.1988
END_OF_FILE
if test 7887 -ne `wc -c <'PGMR.DOC'`; then
    echo shar: \"'PGMR.DOC'\" unpacked with wrong size!
fi
# end of 'PGMR.DOC'
fi
if test -f 'famlists.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'famlists.c'\"
else
echo shar: Extracting \"'famlists.c'\" \(4714 characters\)
sed "s/^X//" >'famlists.c' <<'END_OF_FILE'
X/* famlists.c - routines to generate or modify lists of id numbers
X *
X * 18.Aug.87  jimmc  Initial defintion
X *  8.Jan.88  jimmc  Lint cleanup
X * 18.Jan.88  jimmc  Add limit to famalist, famdlist, make them take input list
X * 19.Jan.88  jimmc  Call fgenum instead of enumIndex
X */
X
X#include <stdio.h>
X#include "geneal.h"
X
Xstatic
Xfamafsub(clist,famid,n)	/* add an individual */
XGroup *clist;
Xint famid;
Xint n;
X{
Xint hid,wid;
X
X	GroupSortedAdd(clist,famid);
X	if (n==1) return;	/* return if this was the last one */
X	hid = fgnum(famid,"H");
X	wid = fgnum(famid,"W");
X	if (hid>0) famaisub(clist,hid,n-1);
X	if (wid>0) famaisub(clist,wid,n-1);
X}
X
Xstatic
Xfamaisub(clist,id,n)	/* add an individual */
XGroup *clist;
Xint id;
Xint n;
X{
Xint famid;
X
X	famid = fgnum(id,"P");
X	if (famid<=0) {
X		GroupSortedAdd(clist,id);
X		return;
X	}
X	famafsub(clist,famid,n);
X}
X
Xint
Xfamalist(ac,av,pav,n)
Xint ac;
Xint *av;
Xint **pav;
Xint n;
X{
Xint i;
Xint tac,rac,nac;
Xint *tav,*rav,*nav;
X
X	rac = 0;
X	rav = 0;
X	for (i=0; i<ac; i++) {
X		nac = famaidlist(av[i],&nav,n);
X		tac = llunion(rac,rav,nac,nav,&tav);
X		if (nav) free((char *)nav);
X		rac = tac;
X		rav = tav;
X	}
X	*pav = rav;
X	return rac;
X}
X
Xstatic
Xint			/* returns count of entries in list */
Xfamaidlist(id,pav,n)	/* list of ancestors */
Xint id;			/* id to get list for */
Xint **pav;		/* where to return list pointer */
Xint n;			/* number of generations to do (-1 means no limit) */
X{
XGroup clist;
X
X	if (n>=0) n++;	/* total number of generations to include in list */
X	clist.count = clist.alloc = 0;
X	clist.n = 0;
X	switch (fgtype(id)) {
X	case 'I':
X		famaisub(&clist,id,n);	/* enter recursive search */
X		break;
X	case 'F':
X		famafsub(&clist,id,n);	/* enter recursive search */
X		break;
X	default:
X		printf("bad type code for id %d\n",id);
X		break;
X	}
X	if (clist.count) *pav = clist.n;
X	return clist.count;
X}
X
Xstatic
Xfamdfsub(clist,famid,n)	/* add an individual */
XGroup *clist;
Xint famid;
Xint n;
X{
Xint cnt, *ll;
Xint i;
X
X	GroupSortedAdd(clist,famid);
X	if (n==1) return;
X	cnt = fglist(famid,"C",&ll);
X	for (i=0; i<cnt; i++) {
X		famdisub(clist,ll[i],n-1);
X	}
X	free((char *)ll);
X}
X
Xstatic
Xfamdisub(clist,id,n)	/* add an individual */
XGroup *clist;
Xint id;
Xint n;
X{
Xint cnt, *ll;
Xint i;
X
X	cnt = fglist(id,"S",&ll);
X	for (i=0; i<cnt; i++) {
X		famdfsub(clist,ll[i],n);
X	}
X	free((char *)ll);
X}
X
Xint
Xfamdlist(ac,av,pav,n)
Xint ac;
Xint *av;
Xint **pav;
Xint n;
X{
Xint i;
Xint tac,rac,nac;
Xint *tav,*rav,*nav;
X
X	rac = 0;
X	rav = 0;
X	for (i=0; i<ac; i++) {
X		nac = famdidlist(av[i],&nav,n);
X		tac = llunion(rac,rav,nac,nav,&tav);
X		if (nav) free((char *)nav);
X		rac = tac;
X		rav = tav;
X	}
X	*pav = rav;
X	return rac;
X}
X
Xstatic
Xint			/* returns count of entries in list */
Xfamdidlist(id,pav,n)	/* list of descendants */
Xint id;			/* id to get list for */
Xint **pav;		/* where to return list pointer */
Xint n;			/* number of generations to add */
X{
XGroup clist;
X
X	if (n>=0) n++;	/* total number of generations to include in list */
X	clist.count = clist.alloc = 0;
X	clist.n = 0;
X	switch (fgtype(id)) {
X	case 'I':
X		GroupSortedAdd(&clist,id);
X		famdisub(&clist,id,n);	/* enter recursive search */
X		break;
X	case 'F':
X		famdfsub(&clist,id,n);	/* enter recursive search */
X		break;
X	default:
X		printf("bad type code for id %d\n",id);
X		break;
X	}
X	if (clist.count) *pav = clist.n;
X	return clist.count;
X}
X
Xstatic Group allclist;
X
X/* ARGSUSED */
Xint
Xalladdone(id,v)
Xint id;		/* index number */
Xint v;		/* value (seek offset) */
X{
X	GroupSortedAdd(&allclist,id);
X	return 0;
X}
X
Xint			/* returns count of entries in list */
Xalllist(pav)		/* list of all indexes in the entire database */
Xint **pav;		/* where to return list pointer */
X{
X	allclist.count = 0;
X	fgenum(alladdone);
X	if (allclist.count) *pav = allclist.n;
X	return allclist.count;
X}
X
Xint
Xlfieldmatch(ac,av,fname,fvalue,pav)
Xint ac;			/* input list */
Xint *av;
Xchar *fname;		/* name of field to check */
Xchar *fvalue;		/* value to look for */
Xint **pav;		/* where to return list pointer */
X{
X	int i;
X	Group clist;
X	int id;
X	char *dvalue;
X
X	clist.count = clist.alloc = 0;
X	clist.n = 0;
X	for (i=0; i<ac; i++) {
X		id = av[i];
X		dvalue = fgstr(id,fname);
X		if (strcmp(dvalue,fvalue)==0) {
X			GroupSortedAdd(&clist,id);
X		}
X		freestr(dvalue);
X	}
X	if (clist.count) *pav = clist.n;
X	return clist.count;
X}
X
Xint
Xlrefs(ac,av,fname,pav)
Xint ac;			/* input list */
Xint *av;
Xchar *fname;		/* name of field to follow references of */
Xint **pav;		/* where to return list pointer */
X{
X	int i,j;
X	Group clist;
X	int id;
X	int n;
X	int flist[1000];
X
X	clist.count = clist.alloc = 0;
X	clist.n = 0;
X	for (i=0; i<ac; i++) {
X		id = av[i];
X		n = fgblist(id,fname,flist);
X		for (j=0; j<n; j++)
X			GroupSortedAdd(&clist,flist[j]);
X	}
X	if (clist.count) *pav = clist.n;
X	return clist.count;
X}
X
X/* end */
END_OF_FILE
if test 4714 -ne `wc -c <'famlists.c'`; then
    echo shar: \"'famlists.c'\" unpacked with wrong size!
fi
# end of 'famlists.c'
fi
if test -f 'fgsubs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'fgsubs.c'\"
else
echo shar: Extracting \"'fgsubs.c'\" \(5179 characters\)
sed "s/^X//" >'fgsubs.c' <<'END_OF_FILE'
X/* fgsubs.c - underlying subroutines used from fgdat
X *
X * All access to the data file should go through this module, so that
X * we can properly change over to another data file when needed.
X *
X * Basically, this file contains routines which do not specifically
X * name the fields, whereas routines in fgdat do specifically name fields.
X *
X * 26.Oct.87  jimmc  Extracted from fgdat
X *  8.Jan.88  jimmc  Lint cleanup
X * 19.Jan.88  jimmc  Add fgenum, fginit
X */
X
X/* Functions which return an allocated string are:
X * fgstr(n,s)	the basic string routine which returns field s
X *
X * Functions which return an integer are:
X * fgnum(n,s)	returns the integer value of field s
X * fgblist(n,s,av)	get a list; return valus is ac, fills array av
X * fglist(n,s,avp)	get a list; return value is ac, fills pointer avp
X *
X * Other functions:
X * fgbstr(n,s,b)	like fgstr, but returns string in buffer b
X * fgchar(n)		returns single character (first char of data word)
X * fgenum(funcp)	enumerates all entries in the data file
X */
X
X#include <stdio.h>
X#include <strings.h>
X#include "dataman.h"
X#include "geneal.h"
X#include "xalloc.h"
X
Xextern struct dpoint *initDataFile();
X
Xextern char *dataErrMsg;
X
Xstatic struct dpoint *gendp;		/* data file handle */
Xstatic char *datfilename;
Xstatic char *opendatfilename;
X
X/*..........*/
X
Xfgopenfn(fn)
Xchar *fn;
X{
X	fgsetfn(fn);
X	fgopen();
X}
X
Xfgsetfn(fn)
Xchar *fn;
X{
X	if (datfilename) freestr(datfilename);
X	datfilename = strsav(fn);
X}
X
Xfgopen()
X{
X	if (!datfilename)
X		fatalerr("No data filename specified");
X	gendp = initDataFile(datfilename);
X	if (!gendp)
X		fatalerr("error opening data file %s: %s",
X			datfilename,dataErrMsg);
X	if (opendatfilename) freestr(opendatfilename);
X	opendatfilename = datfilename;
X	datfilename = 0;
X}
X
Xfgclose()
X{
X	if (gendp) {
X		closeDataFile(gendp);
X		gendp = 0;
X		if (opendatfilename) freestr(opendatfilename);
X	}
X}
X
X/*..........*/
X
Xstatic
Xcheckdp()
X{
X	if (gendp==0) {
X		fgopen();
X		if (gendp==0) fatalerr("no data file open");
X	}
X	/* return and continue */
X}
X
X/*..........*/
X
Xint
Xfgenum(funcp)
Xint (*funcp)();
X{
X	int t;
X
X	checkdp();
X	t = enumIndex(gendp->xx,funcp);
X	return t;
X}
X
X/*..........*/
X
Xint
Xfgnum(n,s)		/* get an item number from the data file */
Xint n;			/* person to get data item for */
Xchar *s;		/* name of data item */
X{
X	int tt, dd;
X	char *str;
X
X	checkdp();
X	if (n<=0) return -1;
X	str = getData(gendp,n,s);	/* get pointer to data item */
X	if (str==0) return -1;		/* -1 if no such item */
X	tt = sscanf(str,"%d", &dd);	/* get the number */
X	if (tt!=1) return -1;		/* if no succesful scan */
X	return dd;			/* return the number we found */
X}
X
X/*..........*/
X
Xint
Xfgchar(n,s)		/* get the first character from a field */
Xint n;			/* record to get data from */
Xchar *s;		/* name of data item */
X{
X	char *word;
X
X	checkdp();
X	if (n<=0) return 0;
X	word = getData(gendp,n,s);
X	if (!word) return 0;
X	return word[0];
X}
X
X/*..........*/
X
Xchar *
Xfgstr(n,s)		/* get an item string from the data file */
Xint n;			/* person to get data item for */
Xchar *s;		/* name of data item */
X{
X	char *str;
X
X	checkdp();
X	if (n<=0) return "";
X	str = getData(gendp,n,s);	/* get pointer to data item */
X	if (str==0) return "";		/* null string if no such item */
X	return strsav(str);
X}
X
X/*..........*/
X
Xint			/* returns 1 if found, 0 if not */
Xfgbstr(n,s,b)		/* get an item string from the data file */
Xint n;			/* person to get data item for */
Xchar *s;		/* name of data item */
Xchar *b;		/* the buffer to put it into */
X{
X	char *str;
X
X	checkdp();
X	if (n<=0) { 
X		*b=0; 
X		return 0; 
X	}	/* make string null */
X	str = getData(gendp,n,s);	/* get pointer to data item */
X	if (str==0) { 
X		*b=0; 
X		return 0; 
X	}	/* null string if no such item */
X	strcpy(b,str);		/* copy the string to his buffer */
X	return 1;
X}
X
X/*..........*/
X
Xint			/* returns count */
Xfgblist(n,t,av)
Xint n;			/* id of family */
Xchar *t;		/* the keyword tag for the field to read */
Xint *av;		/* array to put data into */
X{
X	char cstr[200];
X	char *cstrp;
X	int *clist;
X	int i, ac, tt, dd;
X
X	fgbstr(n,t,cstr);	/* get list of kids */
X	for (ac=0, cstrp=cstr; cstrp!=NULL && *cstrp!='\0'; ac++)
X		cstrp=index(cstrp+1,',');	/* count separators */
X	if (ac==0) return 0;
X	clist = av;			/* put data into his array */
X	for(cstrp=cstr, i=0; cstrp!=NULL && *cstrp!='\0'; i++) {
X		if (i>=ac) fatalerr("loop too far on %s list in fgblist", t);
X		tt = sscanf(cstrp,"%d",&dd);	/* read number */
X		if (tt==1) clist[i]=dd;
X		else {
X			warning("bad %s list format in family %d", t, n);
X			clist[i] = dd = -1;
X		}
X		cstrp = index(cstrp,',');
X		if (cstrp!=NULL) cstrp++;
X	}
X	if (i!=ac) {
X		warning("bad %s list format in family %d", t, n);
X		for (; i<ac; i++) clist[i] = -1;	/* fill with -1 */
X	}
X	return ac;		/* return count */
X}
X
X/*..........*/
X
Xint			/* returns count */
Xfglist(n,t,avp)
Xint n;			/* id of family */
Xchar *t;		/* the keyword of the list to get */
Xint **avp;		/* ADDRESS of where to put the av we return */
X{
X	int *clist;
X	int cbuf[1000];
X	int i, ac;
X
X	ac = fgblist(n,t,cbuf);
X	i = (ac==0)? 1 : ac;
X	clist = XALLOCM(int, i, "fglist");
X	for (i=0; i<ac; i++) clist[i] = cbuf[i];	/* copy the list */
X	*avp = clist;	/* fill in his pointer */
X	return ac;		/* return count */
X}
X
X/* end */
END_OF_FILE
if test 5179 -ne `wc -c <'fgsubs.c'`; then
    echo shar: \"'fgsubs.c'\" unpacked with wrong size!
fi
# end of 'fgsubs.c'
fi
if test -f 'gconsist.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'gconsist.c'\"
else
echo shar: Extracting \"'gconsist.c'\" \(6217 characters\)
sed "s/^X//" >'gconsist.c' <<'END_OF_FILE'
X/* gconsist.c - consistency checker for geneal
X * Written by Jim McBeath (jimmc) at SCI
X *
X * Revision history:
X * 24-Jan-85	Jim McBeath	Initial definition
X *  8-Mar-85	Jim McBeath	minor cleanups for lint
X * 14.Sep.87  jimmc  Add address stuff
X * 12.Dec.87  jimmc  Allow reference record type ('R')
X * 19.Jan.88  jimmc  Use fgenum instead of enumIndex
X */
X
X#include <stdio.h>
X#include "geneal.h"
X
Xstatic int gconerrcount;	/* count of errors detected */
Xstatic int gicount;		/* number of individuals */
Xstatic int gfcount;		/* number of families */
Xstatic int gacount;		/* number of addresses */
Xstatic int gmcount;		/* number of males */
Xstatic int gfmcount;		/* number of females */
X
X/*..........*/
X
X/* VARARGS1 */
Xstatic
Xdataerr(fmt,args)		/* print out a data error message */
Xchar *fmt;			/* printf style format list */
Xint args;
X{
Xchar buf[1000];
X
X	vsprintf(buf,fmt,&args);
X	printf("data error: %s\n", buf);
X	gconerrcount++;		/* count the error */
X}
X
X/*..........*/
X
Xstatic int
Xicmp(a,b)		/* compare two integers for qsort */
Xint *a, *b;
X{
X	return (*a - *b);
X}
X
X/*..........*/
X
Xstatic
Xgenumi(x)
Xint x;
X{
Xchar sbuf[1000];
Xint nlist[1000];
Xint scount, ccount;
Xint i;
Xint pp;
Xint addr;
X
X	gicount++;		/* count the individual */
X	fgbstr(x,"SX",sbuf);	/* look at the sex */
X	if (strcmp(sbuf,"M")==0) gmcount++;
X	else if (strcmp(sbuf,"F")==0) gfmcount++;
X	pp = fgnum(x,"P");	/* see if parents */
X	if (pp>0)
X	{
X	    ccount = fgbclist(pp,nlist);
X	    if (ccount==0) dataerr(
X		"person %d claims family %d, which claims no children",
X		x, pp);
X	    else
X	    {
X		for (i=0; i<ccount; i++) if (nlist[i]==x) break;
X		if (i==ccount) dataerr(
X		    "person %d claims family %d, but not vice-versa",
X		    x, pp);
X	    }
X	}
X	else ccount=0;
X	scount = fgbslist(x,nlist);	/* add spouse numbers */
X	if (scount>0)
X	{
X	    for (i=0; i<scount; i++)	/* check each marriage/union */
X	    {
X		if (x!=fgnum(nlist[i],"H") && x!=fgnum(nlist[i],"W"))
X		    dataerr("person %d claims marriage %d but not vice-versa",
X			x, nlist[i]);
X	    }
X	    qsort((char*)nlist,scount,sizeof(nlist[0]),icmp);	/* sort list */
X	    for (i=0; i<scount-1; i++) if (nlist[i]==nlist[i+1])
X		dataerr("duplicate marriage number %d in person %d",
X		    nlist[i], x);
X	}
X	addr = fgnum(x,"ADDR");
X	if (addr>0) {
X		if (fgnum(addr,"WHO")!=x)
X		  dataerr("person %d points to address %d, but not vice-versa",
X			x, addr);
X	}
X}
X
Xstatic
Xgenumf(x)
Xint x;
X{
Xchar sbuf[1000];
Xint nlist[1000];
Xint scount, ccount;
Xint i;
Xint hh, ww;
Xint addr;
X
X 	gfcount++;		/* count the family */
X	ccount = fgbclist(x,nlist);
X	if (ccount>0)
X	{
X	    for (i=0; i<ccount; i++)	/* check children */
X	    {
X		if (fgnum(nlist[i],"P")!=x)
X		    dataerr("family %d claims child %d, but not vice-versa",
X			x, nlist[i]);
X	    }
X	    qsort((char*)nlist,ccount,sizeof(nlist[0]),icmp);
X	    for (i=0; i<ccount-1; i++) if (nlist[i]==nlist[i+1])
X		dataerr("duplicate child %d in family %d", nlist[i], x);
X	}
X	hh = fgnum(x,"H");		/* check out the husband */
X	ww = fgnum(x,"W");		/* check out the wife */
X	for (i=0; i<ccount; i++)
X	{
X	    if (hh>0 && nlist[i]==hh)
X		dataerr("person %d is both husband and child in family %d",
X		    hh, x);
X	    if (ww>0 && nlist[i]==ww)
X		dataerr("person %d is both wife and child in family %d",
X		    ww, x);
X	}
X	if (hh>0)
X	{
X	    if (ww==hh)
X		dataerr("family %d claims %d as both husband and wife",
X			x, hh);	/* not likely to happen, but what the heck */
X	    fgbstr(hh,"SX",sbuf);	/* look at his sex */
X	    if (strcmp(sbuf,"M")!=0)
X		dataerr(
X"family %d claims husband %d, who is not designated as a male", x, hh);
X	    scount = fgbslist(hh,nlist);
X	    for (i=0; i<scount; i++) if (nlist[i]==x) break;
X	    if (i==scount)
X		dataerr("family %d claims husband %d, but not vice-versa",
X		    x, hh);
X	}
X	if (ww>0)
X	{
X	    fgbstr(ww,"SX",sbuf);	/* look at her sex */
X	    if (strcmp(sbuf,"F")!=0)
X		dataerr(
X"family %d claims wife %d, who is not designated as a female", x, ww);
X	    scount = fgbslist(ww,nlist);
X	    for (i=0; i<scount; i++) if (nlist[i]==x) break;
X	    if (i==scount)
X		dataerr("family %d claims wife %d, but not vice-versa",
X		    x, ww);
X	}
X	addr = fgnum(x,"ADDR");
X	if (addr>0) {
X		if (fgnum(addr,"WHO")!=x)
X		  dataerr("family %d points to address %d, but not vice-versa",
X			x, addr);
X	}
X}
X
Xstatic
Xgenuma(x)
Xint x;
X{
Xint who;
X
X	gacount++;
X	who = fgnum(x,"WHO");
X	if (who<=0) dataerr("address %d has no WHO reference",x);
X	else if (fgnum(who,"ADDR")!=x)
X		dataerr("address %d points to %d, but not vice-versa",x,who);
X}
X
X/* genum is the function which is called for each record.  It is what
X * does the consistency checking.
X */
X
X/* ARGSUSED */
Xint			/* returns 0 if OK */
Xgenum(x,d)		/* our enumeration function for enumIndex */
Xint x;			/* the index number */
Xint d;			/* its value */
X{
X
X	if (x==0) return 0;	/* ignore record 0, it's just for comments */
X
X	switch (fgtype(x)) {
X	case 'I':	/* individual */
X		genumi(x);
X		break;
X	case 'F':	/* family */
X		genumf(x);
X		break;
X	case 'A':	/* address */
X		genuma(x);
X		break;
X	case 'R':	/* reference */
X		/* nothing to check in this record type at the moment */
X		break;
X	default:
X		dataerr("bad type in record %d", x);
X		break;
X	}
X	return 0;			/* always continue */
X}
X
X/*..........*/
X
X/* gconsist is the top level consistency checker. */
X
Xint		/* returns 0 if OK */
Xgconsist()		/* check the consistecny of the data */
X{
Xint n;
X
X	gfcount = gicount = gacount = gconerrcount = 0;	/* clear data */
X	printf("Checking data consistency...\n");
X	n = fgenum(genum);
X	if (n<0) printf("error enumerating indexes\n");
X	else	/* no errors, give report */
X	{
X		if (gconerrcount>0)
X			printf("%d data consistency error%s detected\n",
X				gconerrcount, (gconerrcount==1?"":"s") );
X		else printf("No data consistency errors detected\n");
X		printf("%d individual%s, %d famil%s, %d address%s\n",
X			gicount, (gicount==1?"":"s"),
X			gfcount, (gfcount==1?"y":"ies"),
X			gacount, (gacount==1?"":"es") );
X		gicount -= (gmcount+gfmcount);
X			/* convert to count of unspeced */
X		printf("%d male%s, %d female%s, %d not indicated\n",
X			gmcount, (gmcount==1?"":"s"),
X			gfmcount, (gfmcount==1?"":"s"),
X			gicount, (gicount==1?"":"s") );
X	}
X	if (gconerrcount==0) return 0;	/* all OK */
X	else return 1;		/* error */
X}
X
X/* end */
END_OF_FILE
if test 6217 -ne `wc -c <'gconsist.c'`; then
    echo shar: \"'gconsist.c'\" unpacked with wrong size!
fi
# end of 'gconsist.c'
fi
if test -f 'index.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'index.c'\"
else
echo shar: Extracting \"'index.c'\" \(7152 characters\)
sed "s/^X//" >'index.c' <<'END_OF_FILE'
X/* index - index handler for large pseudo-arrays
X * Written by Jim McBeath (jimmc) at SCI
X *
X * Revision history:
X * 24-Jan-85	Jim McBeath	Add enumIndex function
X *  8-Mar-85	Jim McBeath	remove unused var numentries from enumIndex
X * 17-Apr-86	Jim McBeath	Add field nzcount
X * 19.Jan.87	J. McBeath	Add freeIndex
X * 13.Feb.87	J. McBeath	Set nzcount to 0 on init
X *  8.Jan.99  jimmc  Lint cleanup
X */
X
X/* This module implements a very large pseudo-array.  There are four
X   entry points: to initialize a pseudo-array, to insert an item,
X   to read an item, and to enumerate the entries in an array.  The
X   array contains integers (which could of course be used for another
X   purpose, such as pointers to something else).
X
X   Structure of a table:
X   A table is a recursive structure which contains 2 words of size
X   information and n words of pointers.  The first word of size
X   information tells how many entries are represented by this and
X   all lower tables; the second word of size information tells how
X   many entries are actually in this table.  If the two numbers are
X   the same, then the table is an end node which actually has the
X   data entries in it.
X*/
X
X/*..........*/
X
X#include "index.h"
X
Xextern char *malloc(), *calloc();
X
Xint indexDebug=0;		/* a debugging flag */
X
X/*..........*/
X
Xstruct toplevel *	/* pointer to the structure for future calls */
XinitIndex()			/* init a new index table */
X{
X	struct toplevel *tt;
X	struct tblblock *dd;
X
X	tt = (struct toplevel *)calloc((unsigned)sizeof(struct toplevel),1);
X	/* get space for top level block */
X	if (!tt) return 0;
X	dd = (struct tblblock *)calloc((unsigned)sizeof(struct tblblock),1);
X	/* get space for top level table */
X	if (!dd) {
X		free((char *)tt);
X		return 0;
X	}
X	tt->data = dd;			/* save pointer in our block */
X	tt->numlevs = 1;		/* we always start with one level */
X	tt->nzcount = 0;
X	dd->repnum = TBLSIZ;
X	dd->count = TBLSIZ;
X	return tt;			/* return pointer to top level block */
X}
X
X/*..........*/
X
XfreeIndex(tt)
Xstruct toplevel *tt;
X{
X	freeBlock(tt->data);
X	free((char *)tt);
X}
X
Xstatic freeBlock(dd)
Xstruct tblblock *dd;
X{
X	int i;
X	if (dd->repnum > dd->count) {	/* not at lowest level */
X		for (i=0; i<dd->count; i++)
X			if (dd->tdata[i].p)
X				freeBlock(dd->tdata[i].p);
X		/* free lower level tables */
X	}
X	free((char *)dd);
X}
X
X/*..........*/
X
XsetIndex(tt,ix,num)		/* put index value into table */
Xstruct toplevel *tt;		/* table to use */
Xint ix;				/* the index where it goes */
Xlong num;			/* the value to put there */
X{
X	struct tblblock *dd, *dd0;
X
X	if (indexDebug) {
X		printf("setIndex: index=%d, value=%d\n", ix, num);
X		if (!tt) printf("setIndex: no table index\n");
X		else if (!(tt->numlevs)) printf("setIndex: no numlevs\n");
X		else if (!(tt->data)) printf("setIndex: no data array\n");
X	}
X	if (!tt) return -1;		/* check for errors */
X	if (!(tt->numlevs)) return -1;
X	if (!(tt->data)) return -1;
X	dd = tt->data;			/* get data pointer */
X	while (ix >= dd->repnum) {	/* create a higher level */
X		if (indexDebug)
X			printf("setIndex: %d(ix) > %d(repnum)\n",
X				ix, dd->repnum);
X		dd0 = (struct tblblock *)calloc(
X			(unsigned)sizeof(struct tblblock),1);
X		/* get space for a higher level */
X		if (!dd0) return -1;		/* error */
X		dd0->repnum = dd->repnum*TBLSIZ;
X		dd0->count = TBLSIZ;
X		dd0->tdata[0].p = dd;
X			/* put in pointer to next level down */
X		tt->data = dd0;		/* put in new top-level pointer */
X		tt->numlevs++;
X		if (indexDebug)
X			printf("setIndex: numlevs=%d\n", tt->numlevs);
X		dd = dd0;
X	}
X	while (dd->repnum > dd->count) {
X		/* scan down to the last level */
X		if (indexDebug) printf("setIndex: %d(repnum) > %d(count)\n",
X		    dd->repnum, dd->count);
X		dd0 = dd->tdata[ix/dd->count].p;
X		/* get pointer to next table lower */
X		if (!dd0) {
X			/* if no table there, have to make one */
X			dd0 = (struct tblblock *)calloc(
X				(unsigned)sizeof(struct tblblock),1);
X			if (!dd0) return -1;		/* error */
X			dd0->repnum = dd->repnum/dd->count;
X			dd0->count = TBLSIZ;
X			dd->tdata[ix/dd->count].p = dd0;
X				/* save pointer to it */
X		}
X		ix %= dd->count;		/* lower the index */
X		dd = dd0;
X	}
X	if (dd->tdata[ix].n) tt->nzcount--;
X	dd->tdata[ix].n = num;		/* put it in */
X	if (num) tt->nzcount++;
X	if (indexDebug)
X		printf("setIndex: table %X, index %d, value %d\n", dd,ix,num);
X	return ix;
X}
X
X/*..........*/
X
Xlong				/* return value out of table */
X				/*  returns 0 if no entry */
XgetIndex(tt,ix)
Xstruct toplevel *tt;
Xint ix;				/* the index to look for */
X{
X	struct tblblock *dd, *dd0;
X
X	if (indexDebug) {
X		printf("getIndex: index=%d\n", ix);
X		if (!tt) printf("getIndex: no table\n");
X		else if (!tt->data) printf("getIndex: no data array\n");
X		else if (!tt->numlevs) printf("genIndex: no numlevs\n");
X	}
X	if (!tt) return 0;		/* check for errors */
X	if (!tt->data) return 0;
X	if (!tt->numlevs) return 0;
X	dd = tt->data;
X	if (ix >= dd->repnum) {
X		if (indexDebug)
X			printf("getIndex: index %d > repnum %d\n",
X				ix,dd->repnum);
X		return 0;	/* we don't have them that high */
X	}
X	while (dd->repnum > dd->count) {	/* scan down to bottom level */
X		if (indexDebug) printf("getIndex: %d(repnum) > %d(count)\n",
X		    dd->repnum, dd->count);
X		dd0 = dd->tdata[ix/dd->count].p;
X			/* get pointer to next level */
X		if (!dd0) {
X			if (indexDebug) printf("getIndex: no table\n");
X			return 0;		/* nothing there */
X		}
X		ix %= dd->count;
X		dd = dd0;
X	}
X	if (indexDebug)
X		printf("getIndex: table %X, index %d, value %d\n",
X		    dd, ix, dd->tdata[ix].n);
X	return dd->tdata[ix].n;		/* this is the data entry */
X}
X
X/*..........*/
X
X/* the enumerate function scans throught the table and calls (*f)(x,d) for
X * each non-zero entry n in the table.  If f returns <>0, the enum routine
X * stops scanning and returns an error.  (x is the index number, d is its
X * value.)
X */
Xint			/* returns number of entries or <0 on error */
XenumIndex(tt,f)		/* enumerate the table */
Xstruct toplevel *tt;		/* the table to enumerate */
Xint (*f)();			/* the function to call for each item */
X{
X
X	if (indexDebug) {
X		if (!tt) printf("enumIndex: no table index\n");
X		else if (!(tt->numlevs)) printf("enumIndex: no numlevs\n");
X		else if (!(tt->data)) printf("enumIndex: no data array\n");
X	}
X	if (!tt) return -1;		/* check for errors */
X	if (!(tt->numlevs)) return -1;
X	if (!(tt->data)) return -1;
X	return enum1(tt->data,tt->numlevs,0,f);
X		/* do the enumeration recursively */
X}
X
Xstatic int
Xenum1(dd,levs,b,f)
Xstruct tblblock *dd;		/* the datablock to enumerate */
Xint levs;			/* the number of levels left to descend */
Xint b;				/* the base number for this segment */
Xint (*f)();			/* the function to call */
X{
X	int count=0;
X	int i, n;
X	int binc;
X
X	if (levs<=1) {
X		for (i=0; i<TBLSIZ; i++) {	/* scan through the table */
X			if (dd->tdata[i].n) {
X				n = (*f)(b+i,dd->tdata[i].n);
X				if (n!=0) return -1;	/* error */
X				count++;
X			}
X		}
X		return count;
X			/* return the number of entries processed */
X	}
X	else {		/* not yet at the bottom, keep descending */
X		binc = dd->repnum/TBLSIZ;
X		for (i=0; i<TBLSIZ; i++, b+=binc) {
X			if (dd->tdata[i].p) {
X				n = enum1(dd->tdata[i].p,levs-1,b,f);
X				if (n<0) return n;
X				count += n;
X			}
X		}
X	}
X	return count;
X}
X
X/* end */
END_OF_FILE
if test 7152 -ne `wc -c <'index.c'`; then
    echo shar: \"'index.c'\" unpacked with wrong size!
fi
# end of 'index.c'
fi
if test -f 'lists.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lists.c'\"
else
echo shar: Extracting \"'lists.c'\" \(5191 characters\)
sed "s/^X//" >'lists.c' <<'END_OF_FILE'
X/* lists.c - list manipulation routines for geneal
X *
X * 18.Aug.87  jimmc  Initial definition
X * 28.Sep.87  jimmc  Add LAndNot function
X * 17.Oct.87  jimmc  Convert to spin interface
X *  6.Nov.87  jimmc  Name change: xalloc to xallocm, xrealloc to xreallom
X *  4.Jan.88  jimmc  Add LRange
X *  8.Jan.88  jimmc  Add olisttoiarr; lint cleanup
X */
X
X#include <stdio.h>
X#include "xalloc.h"
X#include "geneal.h"
X#include "spin.h"
X
Xextern SPtoken *SPiarrtolist();
X
XGroupAppend(group,item)
XGroup *group;
Xint item;
X{
Xstatic char *mm="GroupAppend";
X
X	if (group->count >= group->alloc) {
X		if (group->alloc==0) group->alloc=15;
X		else group->alloc *= 2;
X		if (group->n) group->n = (int *)xreallocm((char *)group->n,
X				group->alloc*sizeof(group->n[0]),mm);
X		else group->n = (int *)xallocm(
X				group->alloc*sizeof(group->n[0]),mm);
X	}
X	group->n[group->count++] = item;
X}
X
X/* Assumes the list is sorted; inserts the new item in sorted order if
X * it does not already exist */
XGroupSortedAdd(group,item)
XGroup *group;
Xint item;
X{
Xint i,j;
X
X	/* The following linear search could be replaced
X	 * by something more intelligent, like a binary search. */
X	for (i=0; i<group->count; i++) {
X		if (group->n[i]==item) return;	/* already there */
X		if (group->n[i]>item) break;
X	}
X	GroupAppend(group,item);	/* make space for it */
X	if (i<group->count-1) {
X		for (j=group->count-1; j>i; j--)
X			group->n[j] = group->n[j-1];
X		group->n[i] = item;	/* insert in sorted order */
X	}
X}
X
Xstatic int icmp(a,b) int *a,*b; { return *a - *b; }
X
Xint
Xllunion(ac1,av1,ac2,av2,pav3)
Xint ac1, *av1;
Xint ac2, *av2;
Xint **pav3;	/* return value - we malloc an array and return pointer */
X{
XGroup clist;
Xint i1,i2;
Xint n1,n2;
X
X	qsort((char *)av1,ac1,sizeof(av1[0]),icmp);
X	qsort((char *)av2,ac2,sizeof(av2[0]),icmp);
X	clist.count = clist.alloc = 0;
X	clist.n = 0;
X	i1 = i2 = 0;
X	while (i1<ac1 && i2<ac2) {
X		n1 = av1[i1];
X		n2 = av2[i2];
X		if (n1<n2) {
X			if (clist.count==0 || n1>clist.n[clist.count-1]) {
X				GroupAppend(&clist,n1);
X			}
X			i1++;
X		}
X		else {	/* n1>=n2 */
X			if (clist.count==0 || n2>clist.n[clist.count-1]) {
X				GroupAppend(&clist,n2);
X			}
X			i2++;
X			if (n1==n2) i1++;
X		}
X	}
X	while (i1<ac1) {
X		n1 = av1[i1];
X		if (clist.count==0 || n1>clist.n[clist.count-1]) {
X			GroupAppend(&clist,n1);
X		}
X		i1++;
X	}
X	while (i2<ac2) {
X		n2 = av2[i2];
X		if (clist.count==0 || n2>clist.n[clist.count-1]) {
X			GroupAppend(&clist,n2);
X		}
X		i2++;
X	}
X	*pav3 = clist.n;
X	return clist.count;
X}
X
Xstatic int
Xllintersect(ac1,av1,ac2,av2,pav3)
Xint ac1, *av1;
Xint ac2, *av2;
Xint **pav3;	/* return value - we malloc an array and return pointer */
X{
XGroup clist;
Xint i1,i2;
Xint n1,n2;
X
X	qsort((char *)av1,ac1,sizeof(av1[0]),icmp);
X	qsort((char *)av2,ac2,sizeof(av2[0]),icmp);
X	clist.count = clist.alloc = 0;
X	clist.n = 0;
X	i1 = i2 = 0;
X	while (i1<ac1 && i2<ac2) {
X		n1 = av1[i1];
X		n2 = av2[i2];
X		if (n1<n2) i1++;
X		else if (n1>n2) i2++;
X		else {	/* n1==n2 */
X			if (clist.count==0 || n2>clist.n[clist.count-1]) {
X				GroupAppend(&clist,n2);
X			}
X			i1++; i2++;
X		}
X	}
X	*pav3 = clist.n;
X	return clist.count;
X}
X
Xstatic int
Xllandnot(ac1,av1,ac2,av2,pav3)
Xint ac1, *av1;
Xint ac2, *av2;
Xint **pav3;	/* return value - we malloc an array and return pointer */
X{
XGroup clist;
Xint i1,i2;
Xint n1,n2;
X
X	qsort((char *)av1,ac1,sizeof(av1[0]),icmp);
X	qsort((char *)av2,ac2,sizeof(av2[0]),icmp);
X	clist.count = clist.alloc = 0;
X	clist.n = 0;
X	i2 = 0;
X	for (i1=0; i1<ac1; i1++) {
X		n1 = av1[i1];
X		for ( ; i2<ac2; i2++) {
X			n2 = av2[i2];
X			if (n1==n2) goto no_insert;
X			if (n2>n1) break;
X		}
X		if (clist.count==0 || n1>clist.n[clist.count-1]) {
X			GroupAppend(&clist,n1);
X		}
Xno_insert: ;
X	}
X	*pav3 = clist.n;
X	return clist.count;
X}
X
Xstatic int
Xllrange(from,to,pav3)
Xint from, to;		/* range, inclusive */
Xint **pav3;	/* return value - we malloc an array and return pointer */
X{
Xint r,i;
Xint *v;
X
X	r = to - from + 1;	/* number of items in the range */
X	if (r<=0) return 0;	/* no range */
X	v = XALLOC(int,r);
X	for (i=0; i<r; i++) {
X		v[i] = from+i;
X	}
X	*pav3 = v;
X	return r;
X}
X
XSPtoken *
XLibin2(i1,i2,opfunc)
Xint i1, i2;
Xint (*opfunc)();
X{
Xint ac3, *av3;
XSPtoken *l;
X
X	ac3 = (*opfunc)(i1,i2,&av3);
X	l = SPiarrtolist(ac3,av3);
X	free((char *)av3);
X	return l;
X}
X
XSPtoken *
XLRange(i1,i2)
Xint i1,i2;
X{
X	return Libin2(i1,i2,llrange);
X}
X
XSPtoken *
XLbinop(l1,l2,opfunc)
XSPtoken *l1, *l2;
Xint (*opfunc)();
X{
Xint ac1, *av1;
Xint ac2, *av2;
Xint ac3, *av3;
XSPtoken *l;
X
X	ac1 = olisttoiarr(l1,&av1);
X	ac2 = olisttoiarr(l2,&av2);
X	ac3 = (*opfunc)(ac1,av1,ac2,av2,&av3);
X	l = SPiarrtolist(ac3,av3);
X	free((char *)av3);
X	return l;
X}
X
XSPtoken *
XLIntersect(l1,l2)
XSPtoken *l1, *l2;
X{
X	return Lbinop(l1,l2,llintersect);
X}
X
XSPtoken *
XLUnion(l1,l2)
XSPtoken *l1, *l2;
X{
X	return Lbinop(l1,l2,llunion);
X}
X
XSPtoken *
XLAndNot(l1,l2)
XSPtoken *l1, *l2;
X{
X	return Lbinop(l1,l2,llandnot);
X}
X
Xint			/* returns number of entries in list */
Xolisttoiarr(l,pav)
XSPtoken *l;		/* list of ints or single int */
Xint **pav;		/* where to put the return pointer to array of ints */
X{
Xint ac;
Xint *av;
X
X	if (l->type==SPTokInt) {	/* special case for single int */
X		ac = 1;
X		av = XALLOC(int,1);
X		av[0] = l->value.n;
X		*pav = av;
X	}
X	else {
X		ac = SPlisttoiarr(l,pav);
X	}
X	return ac;
X}
X/* end */
END_OF_FILE
if test 5191 -ne `wc -c <'lists.c'`; then
    echo shar: \"'lists.c'\" unpacked with wrong size!
fi
# end of 'lists.c'
fi
if test -f 'misc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'misc.c'\"
else
echo shar: Extracting \"'misc.c'\" \(5963 characters\)
sed "s/^X//" >'misc.c' <<'END_OF_FILE'
X/* misc.c - miscellaneous routines for geneal
X *
X * 17.Aug.87  jimmc  Initial definition
X *  4.Jan.88  jimmc  Add gdblist, annlist, LAll, LFieldMatch, LRefs
X *  8.Jan.88  jimmc  Add GSetOutput, GGetOutput, GFlushOutput, GSetSep, GGetSep
X *			gfamily, gfamilyh, gindivs; lint cleanup
X *  1.Mar.88  jimmc  Make GVersion return a string intead of printing it
X */
X
X#include <stdio.h>
X#include <strings.h>
X#include "spin.h"
X#include "geneal.h"
X
Xextern SPtoken *SPiarrtolist();
X
Xextern char *genealVersion;
Xextern int dataDebug;
Xextern int indexDebug;
X
Xextern char *strsave();
Xextern int olisttoiarr();
X
Xchar outfpname[1024+1];
Xchar *sepstr;
X
Xchar Gflag[256];
Xint Gflagcount = sizeof(Gflag)/sizeof(Gflag[0]);
X
Xchar Gflagr[256+1];
Xchar *Label;
X
Xchar *
XGFlags(flags)
Xchar *flags;
X{
Xint i;
Xchar *cp, *np;
X
X	if (!flags) flags="";
X	switch (flags[0]) {
X	case '=':
X		for (i=0; i<Gflagcount; i++) Gflag[i]=0;
X		/* FALL THROUGH */
X	case '+':
X		for (cp=flags+1; *cp; cp++) {
X			Gflag[*cp] = 1;
X			switch (*cp) {	/* some special flags */
X			case 'D': dataDebug=1; break;
X			case 'I': indexDebug=1; break;
X			default:  break;	/* do nothing */
X			}
X		}
X		break;
X	case '-':
X		for (cp=flags+1; *cp; cp++) {
X			Gflag[*cp] = 0;
X			switch (*cp) {	/* some special flags */
X			case 'D': dataDebug=0; break;
X			case 'I': indexDebug=0; break;
X			default:  break;	/* do nothing */
X			}
X		}
X		break;
X	case '?':
X	case '\0':
X		break;		/* just return the value */
X	case 'h':
X		GFlagsHelp();
X		break;
X	default:
X		break;	/*** should output error message */
X	}
X	for (np=Gflagr, i=0; i<Gflagcount; i++)
X		if (Gflag[i]) *np++ = i;
X	*np = '\0';
X	return Gflagr;
X}
X
XGFlagsHelp()
X{
X	printf("GFlags [+-=?][flagchars]\n");
X	printf("a  enables output of addresses in family pages\n");
X	printf("b  enables additional birth/death info in famtree\n");
X	printf("m  includes spouses in famtrees\n");
Xprintf("N  enables additional printing of index numbers (only if +n)\n");
X	printf("n  enables printing of index numbers\n");
X	printf("s  enables printing of sibling info in atrees\n");
X	printf("t  include TNOTEs in famtree\n");
X	printf("y  makes date sorts include year (by default they do not)\n");
X	printf("D  debug data routines\n");
X	printf("I  debug index routines\n");
X}
X
Xchar *
XGVersion()
X{
X	return genealVersion;
X}
X
Xint
XGSetOutput(name)
Xchar *name;
X{
XFILE *f;
Xint t;
Xchar *mode;
Xchar m[4];
X
X	if (outfp!=stdout && outfp!=stderr) {
X		t = ferror(outfp) || fclose(outfp);
X		if (t) {
X			warning("error closing previous output file %s",
X					outfpname);
X		}
X	}
X	t = 0;		/* assume no errors */
X	if (strcmp(name,"stdout")==0)
X		f = stdout;
X	else if (strcmp(name,"stderr")==0)
X		f = stderr;
X	else {
X		if (name[0]=='+') {
X			name++;
X			mode = "append";
X		}
X		else {
X			mode = "write";
X		}
X		m[0] = mode[0];
X		m[1] = 0;
X		f = fopen(name,m);
X		if (!f) {
X			warning("can't open file %s for %s",name,mode);
X			f = stdout;
X			name = "stdout";
X			t = 1;		/* error */
X		}
X	}
X	strcpy(outfpname,name);		/* save for later messages */
X	outfp = f;
X	return t;		/* non-zero is error */
X}
X
Xchar *
XGGetOutput()
X{
X	return outfpname;
X}
X
XGFlushOutput()
X{
X	fflush(outfp);
X}
X
XGSetSep(s)
Xchar *s;		/* page separator string */
X{
X	if (sepstr) freestr(sepstr);
X	sepstr = strsav(s);
X}
X
Xchar *
XGGetSep()
X{
X	return sepstr;
X}
X
XGSetLabel(s)
Xchar *s;
X{
X	if (Label) free(Label);
X	Label = strsave(s);
X}
X
Xchar *
XGGetLabel()
X{
X	return Label;
X}
X
Xint
Xgfamily(list)
XSPtoken *list;
X{
Xint ac;
Xint *av;
Xint t;
X
X	ac = olisttoiarr(list,&av);
X	t =  family(ac,av);
X	free((char *)av);
X	return t;
X}
X
Xint
Xgfamilyh(list)
XSPtoken *list;
X{
Xint ac;
Xint *av;
Xint t;
X
X	ac = olisttoiarr(list,&av);
X	t =  familyh(ac,av);
X	free((char *)av);
X	return t;
X}
X
Xint
Xgindivs(list)
XSPtoken *list;
X{
Xint ac;
Xint *av;
Xint t;
X
X	ac = olisttoiarr(list,&av);
X	t =  indivs(ac,av);
X	free((char *)av);
X	return t;
X}
X
Xint
Xgfamntree(list)
XSPtoken *list;
X{
Xint ac;
Xint *av;
Xint t;
X
X	ac = olisttoiarr(list,&av);
X	t = famntree(ac,av);
X	free((char *)av);
X	return t;
X}
X
Xint
Xgannlist(list)
XSPtoken *list;
X{
Xint ac;
Xint *av;
Xint t;
X
X	ac = olisttoiarr(list,&av);
X	t = annlist(ac,av);
X	free((char *)av);
X	return t;
X}
X
Xint
Xgbdlist(list)
XSPtoken *list;
X{
Xint ac;
Xint *av;
Xint t;
X
X	ac = olisttoiarr(list,&av);
X	t = bdlist(ac,av);
X	free((char *)av);
X	return t;
X}
X
XSPtoken *
XLAnc(list,n)		/* ancestor list */
XSPtoken *list;
Xint n;
X{
Xint iac, *iav;
Xint ac, *av;
XSPtoken *l;
X
X	iac = olisttoiarr(list,&iav);
X	ac = famalist(iac,iav,&av,n);
X	l = SPiarrtolist(ac,av);
X	free((char *)av);
X	free((char *)iav);
X	return l;
X}
X
XSPtoken *
XLDesc(list,n)		/* descendant list */
XSPtoken *list;
Xint n;
X{
Xint iac, *iav;
Xint ac, *av;
XSPtoken *l;
X
X	iac = olisttoiarr(list,&iav);
X	ac = famdlist(iac,iav,&av,n);
X	l = SPiarrtolist(ac,av);
X	free((char *)av);
X	free((char *)iav);
X	return l;
X}
X
XSPtoken *
XLAll()			/* all entries in the database */
X{
Xint ac, *av;
XSPtoken *l;
X
X	ac = alllist(&av);
X	l = SPiarrtolist(ac,av);
X	free((char *)av);
X	return l;
X}
X
XSPtoken *
XLFieldMatch(il,fname,fvalue)	/* match value of a field */
XSPtoken *il;		/* the list of entry id's to check */
Xchar *fname;		/* field name */
Xchar *fvalue;		/* field value to look for */
X{
Xint iac, *iav;
Xint ac, *av;
XSPtoken *l;
X
X	iac = olisttoiarr(il,&iav);
X	ac = lfieldmatch(iac,iav,fname,fvalue,&av);
X	l = SPiarrtolist(ac,av);
X	free((char *)av);
X	return l;
X}
X
XSPtoken *
XLRefs(il,refname)	/* follow field references */
XSPtoken *il;		/* the list of entry id's to check */
Xchar *refname;		/* field name */
X{
Xint iac, *iav;
Xint ac, *av;
XSPtoken *l;
X
X	iac = olisttoiarr(il,&iav);
X	ac = lrefs(iac,iav,refname,&av);
X	l = SPiarrtolist(ac,av);
X	free((char *)av);
X	return l;
X}
X
Xint
XGPfi(il)	/*print family or individual pages */
XSPtoken *il;
X{
Xint ac;
Xint *av;
Xint n;
Xint i,t;
X
X	t = 0;
X	ac = olisttoiarr(il,&av);
X	for (i=0; i<ac; i++) {
X		if (i>0) fprintf(outfp,sepstr);
X		n = av[i];
X		switch (fgtype(n)) {
X		case 'I':
X			t += indivs1(n);
X			break;
X		case 'F':
X			t += family1(n);
X			break;
X		default:
X			warning("bad record %d\n", n);
X			break;
X		}
X	}
X	return t;
X}
X
X/* end */
END_OF_FILE
if test 5963 -ne `wc -c <'misc.c'`; then
    echo shar: \"'misc.c'\" unpacked with wrong size!
fi
# end of 'misc.c'
fi
echo shar: End of archive 2 \(of 4\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list