[net.sources] gen - draw family trees

colonel@sunybcs.UUCP (Col. G. L. Sicherman) (01/06/86)

This is an embryonic program for drawing family trees.  The input
syntax is very compact and fairly readable.  If anybody can get
it working I shall be glad; I don't have time to do it myself.
In any case, it generates satisfactory non-pictorial dumps of the
data.
---------- GEN ---------- GEN ---------- GEN ---------- GEN ----------

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# beget.c check.c dump.c embed.c ext.c gen.y gen.l main.c marry.c snap.c gen.h ext.h Makefile gen.1 gen.ms test.data

echo x - beget.c
sed -e 's/^X//' > "beget.c" << '//E*O*F beget.c//'
X/*
X *	beget.c
X */
X
X#include <stdio.h>
X#include "gen.h"
X
Xbeget1(p,c)
XGEN *p, *c;
X{
X	char mess[50];
X	if (c->g_parents) {
X		sprintf(mess,"%16s has multiply defined parents",
X			c->g_name? c->g_name: c->g_label);
X		yyerror(mess);
X	}
X	else if (c->g_parent) {
X		if (c->g_parent != p) {
X			sprintf(mess,"%16s has multiply defined parent",
X				c->g_name? c->g_name: c->g_label);
X			yyerror(mess);
X		}
X	}
X	else c->g_parent = p;
X	mrglist(&p->g_offlist, c);
X}
X
Xbeget(m,c)
XMAR *m;
XGEN *c;
X{
X	char mess[50];
X	if (c->g_parent) {
X		sprintf(mess,"%16s has multiply defined parents",
X			c->g_name? c->g_name: c->g_label);
X		yyerror(mess);
X	}
X	else if (c->g_parents) {
X		if (c->g_parents != m) {
X			sprintf(mess,"%16s has multiply defined parents",
X				c->g_name? c->g_name: c->g_label);
X			yyerror(mess);
X		}
X	}
X	else c->g_parents = m;
X	mrglist(&m->m_offlist, c);
X}
X
Xmrglist(l,c)
XGEN **l, *c;
X/*
X *	l is the address of a pointer to a siblist.
X */
X{
X	GEN **lmove, *ctemp, *lptr;
X	while (c) {
X		lmove=l;
X		while (*lmove) {
X			if (*lmove==c) break;	/* Already in list. */
X			lmove = &((*lmove)->g_sibnext);
X		}
X		*lmove = c;
X/*
X *	detach the siblings of the new entry, and merge them into list.
X */
X		ctemp = c->g_sibnext;
X		c->g_sibnext = (struct gen *)0;
X		c = ctemp;
X	}
X}
//E*O*F beget.c//

echo x - check.c
sed -e 's/^X//' > "check.c" << '//E*O*F check.c//'
X/*
X *	check.c
X */
X
X#include "gen.h"
X#include "ext.h"
X
Xchecklabels()
X{
X	int i;
X	for (i=0; i<nper; i++) if (!per[i]->g_name)
X	printf("gen: undefined label %%%s\n",per[i]->g_label);
X}
X
Xchecksibs()
X{
X/*
X *	for now, just fill in sibprev fields.
X */
X	int i;
X	register GEN *s;
X	for (i=0; i<nper; i++) per[i]->g_sibprev = (GEN *)0;
X	for (i=0; i<nper; i++)
X		if (s=per[i]->g_sibnext) s->g_sibprev = per[i];
X}
X
Xchecksexes()
X{
X	int i, j, cha;
X	MAR *m;
X	GEN *spo;
X	do {
X		cha=0;
X		for (i=0; i<nper; i++) {
X			if (!per[i]->g_sex) continue;
X			for (j=0; j<per[i]->g_nmarriages; j++) {
X				m=per[i]->g_marriage[j];
X				spo = SPOUSE(per[i],m);
X				if (!spo->g_sex) {
X					cha=1;
X					spo->g_sex = MALE+FEMALE-per[i]->g_sex;
X				}
X				else if (spo->g_sex == per[i]->g_sex)
X				printf("gen: sex conflict, %s\n",
X					per[i]->g_name);
X			}
X		}
X	} while (cha);
X}
//E*O*F check.c//

echo x - dump.c
sed -e 's/^X//' > "dump.c" << '//E*O*F dump.c//'
X/*
X *	dump.c
X */
X
X#include <stdio.h>
X#include "gen.h"
X#include "ext.h"
X#define ESTLEN(g) (psize*strlen((g)->g_name)*0.0075)
X#define ONPAGE(p)	(p->g_x/COLS==col0/COLS && p->g_y/ROWS==row0/ROWS)
X/*
X *	per page, that is:
X */
X#define COLS	6
X#define ROWS	16
X
X#define	LEFTEDGE	(col0-1)
X#define	RIGHTEDGE	(col0+COLS)
X#define Y(g)	(-(boxht+spaceht)*g->g_y)
X#define X(g)	(1.0*g->g_x)
X
Xint row0, col0;
X
Xprinttree()
X{
X	int i, j, k;
X	int ncol, page;
X	float z;
X	MAR *m;
X	register GEN *s, *s0, *s1;
X	embed();
X	page = 0;
X/*
X *	define .PS for left justification.
X */
X	printf(".de PS\n");
X	printf(".if t .sp .3\n");
X	printf(".ne \\\\$1u\n");
X	printf("..\n");
X/*
X *	save various parameters.
X */
X	printf(".nr &i \\n(.i\n");
X	printf(".in 0\n");
X	printf(".nr &l \\n(.l\n");
X	printf(".ll 8i\n");
X	printf(".nr &s \\n(.s\n");
X	printf(".ps %d\n", psize);
X	for (row0=0; row0<nrows; row0+=ROWS)
X	for (col0=0; col0<ncols; col0+=COLS) {
X/*
X *	new page if not first page.
X */
X		if (page++) printf(".bp\n");
X		printf(".\\\"	PAGE %d\n",page);
X		printf(".PS\n");
X		printf("boxwid = 0.75\n");
X		printf("boxht = %f\n",boxht);
X		if (eflag) {
X			printf("ellipsewid = 0.75\n");
X			printf("ellipseht = %f\n",boxht);
X		}
X/*
X *	print page designation.
X */
X		printf("box invis \"Page %d:%d\" at %5f,%5f\n",
X			row0/ROWS+1, col0/COLS+1,
X			col0*1.0, 0.4-(boxht+spaceht)*row0);
X/*
X *	draw the boxes.
X */
X		for (i=0; i<nper; i++) if (ONPAGE(per[i])) {
X			printf("P%d: %s ", i,
X			eflag && FEMALE==per[i]->g_sex? "ellipse": "box");
X			if ((z=ESTLEN(per[i])) > 0.75) printf("width %.3f ",z);
X			printf("at %.2f,%.2f \"%s\"",
X			X(per[i]), Y(per[i]), per[i]->g_name);
X			if (per[i]->g_birth.d_year || per[i]->g_death.d_year)
X			printf(" \"%s\\-%s\"",
X				per[i]->g_birth.d_year, per[i]->g_death.d_year);
X			printf("\n");
X		}
X		for (i=0; i<nper; i++) if (ONPAGE(per[i])) {
X/*
X *	draw marriage lines.
X */
X			for (j=0; j<2; j++) {
X				if (m=per[i]->g_marriage[j]) {
X					s=SPOUSE(per[i],m);
X					if (ONPAGE(s)) {
X						if (s->g_number < i)
X						hconnect(per[i],s);
X					}
X					else hrunoff(per[i],s);
X				}
X			}
X/*
X *	draw sibling lines.
X */
X			if (per[i]->g_sibnext || per[i]->g_sibprev) {
X/*
X *	is sibling the highest-numbered on this page?
X */
X				for (s=per[i]; s; s=s->g_sibnext) {
X					if (!ONPAGE(s)) break;
X					if (s->g_number > per[i]->g_number)
X					goto nosib;
X				}
X				for (s=per[i]; s; s=s->g_sibprev) {
X					if (!ONPAGE(s)) break;
X					if (s->g_number > per[i]->g_number)
X					goto nosib;
X				}
X				for (s0=per[i];s0->g_sibprev; s0=s0->g_sibprev)
X					if (!ONPAGE(s0->g_sibprev)) break;
X				for (s1=per[i];s1->g_sibnext; s1=s1->g_sibnext)
X					if (!ONPAGE(s1->g_sibnext)) break;
X				sibline(s0,s1);
X				for (; ONPAGE(s0); s0=s0->g_sibnext)
X				printf("line from P%d.n up %.3f\n",
X					s0->g_number,spaceht*0.5);
X			}
Xnosib:			
X/*
X *	draw line to parent.
X */
X			if (s=per[i]->g_parent) {
X				if (ONPAGE(s)) jconnect(s,per[i]);
X				else {
X				}
X			}
X/*
X *	draw line to parents.
X */
X			else if (m=per[i]->g_parents) {
X				k=(m->m_spouse[0]->g_x+m->m_spouse[1]->g_x)/2;
X				if (k/COLS==col0/COLS &&
X				m->m_spouse[0]->g_y/ROWS==row0/ROWS)
X					j2connect(m,per[i]);
X				else {
X				}
X			}
X		}
X		printf(".PE\n");
X	}
X	printf(".in \\n(&i\n");
X	printf(".ll \\n(&l\n");
X	printf(".ps \\n(&s\n");
X}
X
Xinfodump()
X/*
X *	This is just a plain dump of the info.
X */
X{
X	register int i, j;
X	printf(".nr %%G \\n(.j\n");
X	printf(".nf\n");
X	for (i=0; i<nper; i++) {
X		printf("P%d:",i);
X		printf(" %.10s",per[i]->g_name);
X		if (per[i]->g_parents)
X			printf(" <{%.10s,%.10s}",
X			per[i]->g_parents->m_spouse[0]->g_name,
X			per[i]->g_parents->m_spouse[1]->g_name);
X		else if (per[i]->g_parent)
X			printf(" <{%.10s}",per[i]->g_parent->g_name);
X		for (j=0; j<per[i]->g_nmarriages; j++)
X		printf(" [%.10s,%.10s]",
X		per[i]->g_marriage[j]->m_spouse[0]->g_name,
X		per[i]->g_marriage[j]->m_spouse[1]->g_name);
X		if (per[i]->g_sibnext)
X			printf(" |%.10s",per[i]->g_sibnext->g_name);
X		if (per[i]->g_sibprev)
X			printf(" \\\\%.10s",per[i]->g_sibprev->g_name);
X		printf("\n");
X	}
X	printf(".ad \\n(%%G\n");
X}
X
Xjconnect(b1,b2)
XGEN *b1, *b2;
X{
X	printf("line from P%d.n up %.3f", b2->g_number, spaceht*0.5);
X	if (b1->g_x != b2->g_x)
X	printf(" then right P%d.x-P%d.x", b1->g_number, b2->g_number);
X	printf(" then to P%d.s\n", b1->g_number);
X}
X
Xj2connect(m1,b2)
XMAR *m1;
XGEN *b2;
X{
X	GEN *ml, *mr;
X	ml = m1->m_spouse[0];
X	mr = m1->m_spouse[1];
X	if (ml->g_x > mr->g_x) {
X		mr = ml;
X		ml = m1->m_spouse[1];
X	}
X	if (ONPAGE(ml) && ONPAGE(mr)) printf("m = (P%d.e.x + P%d.w.x)/2\n",
X		ml->g_number, mr->g_number);
X	else if (ONPAGE(ml)) printf("m = (P%d.e.x + %d)/2\n",
X		ml->g_number, mr->g_x);
X	else if (ONPAGE(mr)) printf("m = (%d + P%d.w.x)/2\n",
X		ml->g_x, mr->g_number);
X	else printf("m = (%d + %d)/2\n", ml->g_x, mr->g_x);
X	printf("line from P%d.n up %.3f then right m-(%d) ",
X		b2->g_number, spaceht*0.5, b2->g_x);
X	printf("then to (m,%.3f)\n", Y(ml));
X}
X
Xhconnect(b1,b2)
XGEN *b1, *b2;
X{
X	if (b2->g_x < b1->g_x)
X	printf("line from P%d.w to P%d.e\n", b1->g_number, b2->g_number);
X	else printf("line from P%d.e to P%d.w\n", b1->g_number, b2->g_number);
X}
X
Xhrunoff(b1,b2)
XGEN *b1,*b2;
X{
X	if (b2->g_x < b1->g_x)
X	printf("line from P%d.w left P%d.w.x - %d\n",
X		b1->g_number, b1->g_number, LEFTEDGE);
X	else printf("line from P%d.e right %d-P%d.e.x\n",
X		b1->g_number, RIGHTEDGE, b1->g_number);
X}
X
Xsibline(s0,s1)
XGEN *s0, *s1;
X{
X	if (s0->g_sibprev) {
X/*
X *	run off the page.
X */
X		printf("line from (%d,%.3f)",
X			LEFTEDGE, Y(s0)+0.5*(boxht+spaceht));
X		if (s1->g_sibnext) printf(" right %d\n", RIGHTEDGE-LEFTEDGE);
X		else printf(" to P%d.n+(0,%.3f)\n", s1->g_number, spaceht*0.5);
X	}
X	else {
X		printf("line from P%d.n+(0,%.3f)", s0->g_number, 0.5*spaceht);
X		if (s1->g_sibnext)
X			printf(" right %d\n", RIGHTEDGE-s0->g_x);
X		else printf(" to P%d.n+(0,%.3f)\n",
X			s1->g_number,spaceht*0.5);
X	}
X}
//E*O*F dump.c//

echo x - embed.c
sed -e 's/^X//' > "embed.c" << '//E*O*F embed.c//'
X#include <stdio.h>
X#include "gen.h"
X#include "ext.h"
X
X#define	DOWNFREE(p) (p->g_y+1<nrows && I_VACANT==array[p->g_y+1][p->g_x].i_what)
X#define	UPFREE(p)   (p->g_y-1>=0 && I_VACANT==array[p->g_y-1][p->g_x].i_what)
X#define	RIGHTFREE(p) (p->g_x+1<ncols&&I_VACANT==array[p->g_y][p->g_x+1].i_what)
X#define	MRIGHTFREE(m) (m->m_x+1<ncols&&I_VACANT==array[m->m_y][m->m_x+1].i_what)
X#define	LEFTFREE(p) (p->g_x-1>=0 && I_VACANT==array[p->g_y][p->g_x-1].i_what)
X#define	MLEFTFREE(m) (m->m_x-1>=0 && I_VACANT==array[m->m_y][m->m_x-1].i_what)
X
Xembed()
X{
X	int i;
X	nrows = ncols = 0;
X	if (!nper) return;
X	for (i=0; i<nper; i++)
X	per[i]->g_mark = per[i]->g_hassibbar = per[i]->g_ismapped = 0;
X	addarr(0,0,0);
X}
X
Xaddarr(p,x,y)
Xint p, x, y;
X{
X/*
X *	presumes that array[y][x] exists and is vacant.
X */
X	GEN *s0, *s1, *st, *s;
X	MAR *m;
X	register int i, xgo, ygo, j, j0, j1;
X	register SPOT *q;
X	if (debug) fprintf(stderr,"adding #%d at (%d,%d)\n",p, x, y);
X	if (0==p) {
X/*
X *	put the first person in.
X */
X		array[0][0].i_what = I_PERSON;
X		array[0][0].i_num = p;
X		nrows=ncols=1;
X		per[p]->g_ismapped=1;
X		per[p]->g_x = per[p]->g_y = 0;
X	}
X	else {
X		per[p]->g_ismapped=1;
X		per[p]->g_x = x;
X		per[p]->g_y = y;
X		array[y][x].i_what = I_PERSON;
X		array[y][x].i_num = p;
X	}
X/*
X *	time for a snapshot?
X */
X	if (0 < sflag--) snap(x,y);
X/*
X *	add siblings.
X */
X	if ((s=per[p]->g_sibnext) && !s->g_ismapped) {
X		xgo = per[p]->g_x+1;
X		ygo = per[p]->g_y;
X		if (!per[p]->g_hassibbar) {
X			sb[nsbars].s_y = ygo;
X			sb[nsbars].s_x0 = per[p]->g_x;
X			sb[nsbars].s_first = per[p];
X			per[p]->g_hassibbar = 1;
X			per[p]->g_sbar = nsbars++;
X		}
X		s->g_hassibbar = 1;
X		s->g_sbar = per[p]->g_sbar;
X		sb[s->g_sbar].s_x1 = xgo;
X		if (!RIGHTFREE(per[p])) if (inscol(xgo)) return;
X		addarr(s->g_number,xgo,ygo);
X	}
X	if ((s=per[p]->g_sibprev) && s->g_ismapped) {
X		xgo = per[p]->g_x-1;
X		ygo = per[p]->g_y;
X		if (!per[p]->g_hassibbar) {
X			sb[nsbars].s_y = ygo;
X			sb[nsbars].s_x1 = per[p]->g_x;
X			per[p]->g_hassibbar = 1;
X			per[p]->g_sbar = nsbars++;
X		}
X		s->g_hassibbar = 1;
X		s->g_sbar = per[p]->g_sbar;
X		sb[s->g_sbar].s_x0 = xgo;
X		sb[s->g_sbar].s_first = s;
X		if (!LEFTFREE(per[p])) if (inscol(++xgo)) return;
X		addarr(s->g_number,xgo,ygo);
X	}
X/*
X *	add a parent.
X */
X	if ((s=per[p]->g_parent) && s->g_x < 0 ||
X	(m=per[p]->g_parents) && (s=m->m_spouse[0]) && s->g_x < 0) {
X		xgo = per[p]->g_x;
X		ygo = per[p]->g_y-1;
X		if (!UPFREE(per[p])) {
X			if (ygo < 0) {
X				if (insrow(0)) return;
X			}
X			clearimarks();
X			else if (movedown(per[p]->g_x,per[p]->g_y)) return;
X			ygo++;
X		}
X		addarr(s->g_number,xgo,ygo);
X	}
X/*
X *	add spouses.
X */
X	switch(per[p]->g_nmarriages) {
X	case 1:
X		s0 = SPOUSE(per[p],m=per[p]->g_marriage[0]);
X		if (s0->g_ismapped) break;
X		switch (s0->g_sex) {
X		default:
X			if (RIGHTFREE(per[p]) || !LEFTFREE(per[p]))
X				goto right;
X		case MALE:
X			xgo = per[p]->g_x-1;
X			ygo = per[p]->g_y;
X			if (marryleft(p, m, xgo, ygo, s0)) return;
X			break;
Xright:
X		case FEMALE:
X			xgo = per[p]->g_x+1;
X			ygo = per[p]->g_y;
X			if (marryright(p, m, xgo, ygo, s0)) return;
X			break;
X		}
X		break;
X	case 2:
X		s0 = SPOUSE(per[p],per[p]->g_marriage[0]);
X		s1 = SPOUSE(per[p],per[p]->g_marriage[1]);
X		if (s0->g_ismapped && s1->g_ismapped) break;
X		if (s1->g_ismapped) {
X			st = s0;
X			s0 = s1;
X			s1 = st;
X		}
X		if (s0->g_ismapped) {
X/*
X *	add one spouse.
X */
X			if (s0->g_x < per[p]->g_x) {
X				xgo = per[p]->g_x+1;
X				ygo = per[p]->g_y;
X				if (marryright(p, per[p]->g_marriage[1],
X					xgo, ygo, s1)) return;
X			}
X			else {
X				xgo = per[p]->g_x-1;
X				ygo = per[p]->g_y;
X				if (marryleft(p, per[p]->g_marriage[1],
X					xgo, ygo, s1)) return;
X			}
X		}
X		else {
X/*
X *	add both spouses.
X */
X			xgo = per[p]->g_x+1;
X			ygo = per[p]->g_y;
X			if (marryright(p, per[p]->g_marriage[0], xgo, ygo, s1))
X				return;
X			xgo = per[p]->g_x-1;
X			ygo = per[p]->g_y;
X			if (marryleft(p, per[p]->g_marriage[0], xgo, ygo, s0))
X				return;
X		}
X	}
X/*
X *	add own child.
X */
X	if ((s=per[p]->g_offlist) && !s->g_ismapped) {
X		if (!DOWNFREE(per[p])) {
X/*
X *	make room.
X */
X			if (per[p]->g_y+1 >= nrows) {
X				if (insrow(per[p]->g_y+1)) return;
X			}
X			else {
X				array[per[p]->g_y][per[p]->g_x].i_what =
X					I_OPERSON;
X				q = &array[per[p]->g_y+1][per[p]->g_x];
X				switch (q->i_what) {
X				case I_SIBLINE:
X					i=hitleft(per[p]->g_x,per[p]->g_y+1);
X					break;
X				case I_PERSON:
X					i=q->i_num;
X					break;
X				case I_MARRIAGE:
X					i=q->i_mar->m_spouse[0]->g_number;
X					break;
X				}
X				s0 = per[i];
X				clearimarks();
X				if (movedown(per[p]->g_x,per[p]->g_y+1))
X					return;	/* FAILED */
X				else unmark(s0);
X			}
X		}
X		addarr(s->g_number, per[p]->g_x, per[p]->g_y+1);
X	}
X/*
X *	add joint children.
X */
X	for (i=0; i<2; i++) {
X		if (!(m=per[p]->g_marriage[i])) continue;	/* MARRIED? */
X		if (!(s=m->m_offlist)) continue;	/* ANY OFFSPRING? */
X		if (s->g_x >= 0) continue;	/* ALREADY PLACED? */
X		j0 = per[p]->g_x;
X		s0 = SPOUSE(per[p],m);
X		j1 = s0->g_x;
X		if (j0>j1) {
X			j=j0;
X			j0=j1;
X			j1=j;
X		}
X/*
X *		look for a vacant spot somewhere beneath the couple.
X */
X		if (per[p]->g_y+1 < nrows) {
X			for (j=j0; j<=j1; j++)
X				if (I_VACANT==array[per[p]->g_y+1][j].i_what)
X				break;
X			if (j <= j1) xgo = j;
X			else {
X				if (insrow(per[p]->g_y+1)) return;
X				xgo = m->m_x;
X			}
X		}
X		else {
X			if (insrow(nrows)) return;
X			xgo = m->m_x;
X		}
X		addarr(s->g_number, xgo,  per[p]->g_y+1);
X	}
X}
X
Xint
Xmarryright(p, m, xgo, ygo, s)
Xint	p;
XMAR	*m;
Xint	xgo, ygo;
XGEN	*s;
X{
X	if (!RIGHTFREE(per[p])) if (inscol(xgo)) return 1;
X	array[ygo][xgo].i_what = I_MARRIAGE;
X	array[ygo][xgo].i_mar = m;
X	m->m_ismapped=1;
X	m->m_x = xgo;
X	m->m_y = ygo;
X	++xgo;
X	if (!MRIGHTFREE(m)) if (inscol(xgo)) return 1;
X	addarr(s->g_number,xgo,ygo);
X	return 0;
X}
X
Xint
Xmarryleft(p, m, xgo, ygo, s)
Xint	p;
XMAR	*m;
Xint	xgo, ygo;
XGEN	*s;
X{
X	if (!LEFTFREE(per[p])) if (inscol(++xgo)) return 1;
X	array[ygo][xgo].i_what = I_MARRIAGE;
X	array[ygo][xgo].i_mar = m;
X	m->m_ismapped=1;
X	m->m_x = xgo;
X	m->m_y = ygo;
X	--xgo;
X	if (!MLEFTFREE(m)) if (inscol(++xgo)) return 1;
X	addarr(s->g_number,xgo,ygo);
X	return 0;
X}
X
X/*
X *	move something a step down, and everybody connected sideways or below.
X */
X#define	MDEXIT(x,y)	{if (movedown(x,y)) return 1;}
Xint
Xmovedown(x0,y0)
Xint	x0, y0;
X{
X	GEN *gg;
X	int i;
X	SPOT *sp, *spd;
X	switch((sp = &array[y0][x0])->i_what) {
X	case I_VACANT:
X		return 0;
X	case I_VLINE:
X		if (y0 + 1 >= nrows) {
X			if (insrow(nrows)) return 1;
X/*
X *	insrow extends vlines, so ...
X */
X			return 0;
X		}
X		else return 0;	/* the line gets shorter! */
X	case I_OMARRIAGE:
X	case I_MARLINE:
X	case I_MARRIAGE:
X		if (y0+1 >= nrows) {
X			if (insrow(nrows)) return 1;
X			array[y0+1][x0] = array[y0][x0];
X			if (I_MARLINE != sp->i_what)
X				array[y0+1][x0].i_mar->m_y += 1;
X			sp->i_what = I_VACANT;
X			sp->i_mark = 1;
X		}
X		else {
X			spd = &array[y0+1][x0];
X			if (spd->i_what = I_VACANT) break;
X			if (sp->i_what == I_OMARRIAGE &&
X				array[y0+1][x] == I_VLINE) break;
X			if (movedown(x0,y0+1)) return 1;
X			array[y0+1][x0] = array[y0][x0];
X			if (I_MARLINE != sp->i_what)
X				array[y0+1][x0].i_mar->m_y += 1;
X			sp->i_what = I_VACANT;
X			sp->i_mark = 1;
X		}
X		if (!array[y0][x0-1].i_mark) if (movedown(x0-1,y0)) return 1;
X		if (!array[y0][x0+1].i_mark) if (movedown(x0+1,y0)) return 1;
X		return 0;
X	case I_SIBLINE:
X	if (g->g_x < 0) return 0;
X	if (!DOWNFREE(g)) {
X		if (g->g_y+1 >= nrows) {
X			if (insrow(g->g_y+1)) return;
X		}
X		else switch (array[g->g_y+1][g->g_x].i_what) {
X		case I_SIBLINE:
X		case I_MARLINE:
X			i=hitleft(g->g_x, g->g_y+1);
X			clearimarks();
X			if (movedown(per[i])) return 1;
X			break;
X		case I_PERSON:
X			clearimarks();
X			if (movedown(per[array[g->g_y+1][g->g_x].i_num]))					return 1;
X			break;
X		}
X	}
X	array[g->g_y++][g->g_x].i_what = I_VACANT;
X	array[g->g_y][g->g_x].i_num = g->g_number;
X	g->g_mark = 1;
X	if ((gg=g->g_sibprev) && !gg->g_mark) MDEXIT(gg);
X	if ((gg=g->g_sibnext) && !gg->g_mark) MDEXIT(gg);
X	for (i=0; i<g->g_nmarriages; i++) {
X		gg=SPOUSE(g,g->g_marriage[i]);
X		if (!gg) {
X			fprintf(stderr,"gen: runaway %s for %s\n",
X				(g->g_sex==MALE?"wife":
X				g->g_sex==FEMALE?"husband":"spouse"),
X				g->g_name);
X			exit(1);
X		}
X		if (!gg->g_mark) MDEXIT(gg);
X		if ((gg=g->g_marriage[i]->m_offlist) &&
X			gg->g_y <= g->g_y) MDEXIT(gg);
X	}
X	if ((gg=g->g_offlist) && gg->g_y <= g->g_y) MDEXIT(gg);
X	return 0;
X}
X
Xinsrow(r,x)
Xint r, x;
X/*
X *	r is the row above which to add a row.
X *	If r==nrows, a row is appended.
X *
X *	x == 0:	do not move sibling bars down at insertion point.
X *	x != 0:	do move sibling bars down at insertion point.
X */
X{
X	int i, j;
X	MAR m;
X	if (nrows >= MAXROWS) {
X		fprintf(stderr,"gen: row overflow\n");
X		return 1;
X	}
X	for (i=nrows-1; i>=r; i--) for (j=0; j<ncols; j++) {
X		array[i+1][j] = array[i][j];
X		switch (array[i+1][j].i_what) {
X		case I_MARRIAGE:
X		case I_OMARRIAGE:
X			array[i+1][j].i_mar->m_y += 1;
X			break;
X		case I_PERSON:
X		case I_OPERSON:
X			per[array[i+1][j].i_num]->g_y += 1;
X			break;
X		}
X	}
X	nrows++;
X	for (j=0; j<ncols; j++) {
X/*
X *	vertical lines must be extended through the new row.
X */
X		if (r+1<nrows && (I_VLINE == array[r+1][j].i_what ||
X			I_OMARRIAGE == array[r+1][j].i_what ||
X			I_OPERSON == array[r+1][j].i_what))
X		array[r][j].i_what = I_VLINE;
X		else if (r+1<nrows && (I_SIBLINE == array[r+1][j].i_what ||
X			I_PERSON == array[r+1][j].i_what &&
X			array[r+1][j].i_hassibbar)
X		array[r][j].i_what = I_SIBLINE;
X		else array[r][j].i_what = I_VACANT;
X	}
X	return 0;
X}
X
Xinscol(r)
Xint r;
X{
X	int i, j, k;
X	GEN *g;
X	if (ncols >= MAXCOLS) {
X		fprintf(stderr,"gen: column overflow\n");
X		return 1;
X	}
X	for (i=ncols-1; i>=r; i--) for (j=0; j<nrows; j++)
X		array[j][i+1] = array[j][i];
X	ncols++;
X	for (i=0; i<nper; i++) if (per[i]->g_x >= r) per[i]->g_x++;
X	if (r == ncols-1)
X		for (j=0; j<nrows; j++) array[j][r].i_what = I_VACANT;
X	else for (j=0; j<nrows; j++) {
X/*
X *	add/extend horizontal lines if necessary.
X */
X		switch (i=array[j][r+1].i_what) {
X		case I_MARLINE:
X		case I_SIBLINE:
X			array[j][r].i_what = i;
X			break;
X		case I_MARRIAGE:
X		case I_OMARRIAGE:
X			array[j][r].i_what = I_MARLINE;
X			break;
X		case I_PERSON:
X		case I_OPERSON:
X/*
X *	check for marriage or marline at LEFT.
X */
X			if (r-1 >= 0) switch (array[j][r-1].i_what) {
X			case I_MARRIAGE:
X			case I_OMARRIAGE:
X			case I_MARLINE:
X				array[j][r].i_what = I_MARLINE;
X			}
X			break;
X		}
X	}
X	return 0;
X}
X
X/*
X *	recursively remove marks.
X */
Xunmark(g)
XGEN *g;
X{
X	int i;
X	register GEN *gg;
X	g->g_mark = 0;
X	if ((gg=g->g_parent) && gg->g_mark) unmark(gg);
X	else if (g->g_parents) for (i=0; i<2; i++)
X		if ((gg=g->g_parents->m_spouse[i])->g_mark) unmark(gg);
X	if ((gg=g->g_sibnext) && gg->g_mark) unmark(gg);
X	if ((gg=g->g_sibprev) && gg->g_mark) unmark(gg);
X	if ((gg=g->g_offlist) && gg->g_mark) unmark(gg);
X	for (i=0; i<g->g_nmarriages; i++) {
X		gg=SPOUSE(g,g->g_marriage[i]);
X		if (gg->g_mark) unmark(gg);
X		if ((gg=g->g_marriage[i]->m_offlist) && gg->g_mark) unmark(gg);
X	}
X}
X
X/*
X *	find occupied box at left end of hline.
X */
Xint
Xhitleft(x,y)
Xint x,y;
X{
X	SPOT *i;
X	while (x >= 0)
X		if (I_PERSON==(i = &array[y][x--])->i_what) return i->i_num;
X	fprintf(stderr,"gen: internal error, endless hline in row %d\n",y);
X	exit(1);
X}
X
Xclearimarks()
X{
X	int x, y;
X	for (y=0; y<nrows; y++) for (x=0; x<ncols; x++)
X	array[y][x].i_mark = 0;
X}
//E*O*F embed.c//

echo x - ext.c
sed -e 's/^X//' > "ext.c" << '//E*O*F ext.c//'
X#include	"gen.h"
X
Xint	nper = 0;
XGEN	*per[MAXP];
Xchar	errmsg[80];
XSPOT	array[MAXROWS][MAXCOLS];
Xint	nrows = 0, ncols = 0;
Xfloat	boxht = BOXHT, spaceht = SPACEHT;
Xint	nsbars = 0;
Xstruct sbar	sb[MAXSBARS];
Xint	iflag = 0;
Xint	eflag = 0;
Xint	psize = PSIZE;
Xint	debug = 0;
Xint	bflag = 0;	/* flag for euro date parsing */
Xint	sflag = 0;	/* debug flag for snapshots */
//E*O*F ext.c//

echo x - gen.y
sed -e 's/^X//' > "gen.y" << '//E*O*F gen.y//'
X%token QUOTE2 QUOTE1 LESS GREATER LBRACK RBRACK WORD
X%token AMPERSAND LBRACE RBRACE BAR
X%token HASH SEX DATE SEMICOLON MISC
X%token COLON PERCENT
X%start familytree
X%%
Xfamilytree:	familytree SEMICOLON object 
X	|	object
X	;
Xobject	:	gen_person
X	|	asc_s
X	|	siblings
X	;
Xgen_person:	asc
X	|	marriage
X			{$$ = (int)(((MAR *)$1)->m_spouse[0]);}
X	|	asc_m
X			{$$ = (int)(((MAR *)$1)->m_spouse[0]);}
X	|	desc
X	|	person
X	;
Xnon_marriage:	desc
X	|	asc
X	|	person
X	;
Xmarriage:	LBRACK gen_person AMPERSAND gen_person RBRACK
X			{$$=domarriage($2,$4);}
X	;
Xdesc	:	marriage GREATER gen_person
X			{
X			beget((MAR *)$1, (GEN *)$3);
X			$$=(int)((MAR *)$1)->m_spouse[0];
X			}
X	|	marriage GREATER siblings
X			{
X			beget((MAR *)$1, (GEN *)$3);
X			$$=(int)((MAR *)$1)->m_spouse[0];
X			}
X	|	person GREATER gen_person
X			{beget1((GEN *)$1, (GEN *)$3);}
X	|	person GREATER siblings
X			{beget1((GEN *)$1, (GEN *)$3);}
X	;
Xasc_m	:	marriage LESS marriage
X			{
X			beget((MAR *)$3,
X				((MAR *)$1)->m_spouse[0]);
X			}
X	|	marriage LESS non_marriage
X			{
X			beget1(((MAR *)$3)->m_spouse[0],
X			(GEN *)$1);
X			}
X	;
Xasc_s	:	siblings LESS non_marriage
X			{beget1((GEN *)$3, (GEN *)$1);}
X	|	siblings LESS marriage
X			{
X			beget((MAR *)$3, (GEN *)$1);
X			}
X	;
Xasc	:	person LESS marriage
X			{
X			beget((MAR *)$3, (GEN *)$1);
X			}
X	|	person LESS non_marriage
X			{beget1((GEN *)$3, (GEN *)$1);}
X	;
Xsiblings:	LBRACE personlist RBRACE
X			{$$=$2;}
X	;
Xpersonlist:	gen_person BAR personlist
X			{ mrglist((GEN **)&$1, (GEN *)$3); }
X	|	gen_person
X	;
Xperson	:	person date word
X			{
X			if (dt=='+')
X				dateparse(&((GEN *)$1)->g_death,(char *)$3);
X			else dateparse(&((GEN *)$1)->g_birth,(char *)$3);
X			}
X	|	person date quote
X			{
X			DTE *dtemp;
X			if (dt=='+') dtemp = &((GEN *)$1)->g_death;
X			else dtemp = &((GEN *)$1)->g_birth;
X			dtemp->d_year = (char *)$3;
X			}
X	|	person sex
X			{
X			if (sx=='@') ((GEN *)$1)->g_sex=FEMALE;
X			else ((GEN *)$1)->g_sex=MALE;
X			}
X	|	person HASH word	/* check for valid ordinal */
X	|	basic_p
X	;
Xsex	:	SEX
X			{sx = yytext[0];}
Xdate	:	DATE
X			{dt = yytext[0];}
X	;
Xbasic_p	:	simple_p
X			{
X			per[nper] = GALLOC;
X			per[nper]->g_number = nper;
X			per[nper]->g_name=strcpy(malloc(strlen(name)+1),name);
X			per[nper]->g_ismapped = 0;
X			per[nper]->g_parent = NULL;
X			per[nper]->g_parents = NULL;
X			per[nper]->g_nmarriages = 0;
X			per[nper]->g_label = NULL;
X			$$ = (int)per[nper++];
X			}
X	|	quote
X			{
X			per[nper] = GALLOC;
X			per[nper]->g_number = nper;
X			per[nper]->g_name= (char *)$1;
X			per[nper]->g_label = NULL;
X			per[nper]->g_ismapped = 0;
X			per[nper]->g_parent = NULL;
X			per[nper]->g_parents = NULL;
X			per[nper]->g_nmarriages = 0;
X			$$ = (int)per[nper++];
X			}
X	|	PERCENT word	/* check that word is alphanum! */
X			{
X			if (!is_goodlabel((char *)$2)) yyerror("bad label");
X			$$ = (int)findlabel((char *)$2);
X			((GEN *)$$)->g_label = (char *)$2;
X			}
X	|	label simple_p
X			{
X			$$ = (int)findlabel((char *)$1);
X			if (((GEN *)$$)->g_name) {
X				sprintf(errmsg,"label %s multiply defined",
X				(char *)$1);
X				yyerror(errmsg);
X			}
X			((GEN *)$$)->g_name=strcpy(malloc(strlen(name)+1),name);
X			((GEN *)$$)->g_label = (char *)$1;
X			((GEN *)$$)->g_ismapped = 0;
X			((GEN *)$$)->g_parent = NULL;
X			((GEN *)$$)->g_parents = NULL;
X			((GEN *)$$)->g_nmarriages = 0;
X			}
X	|	label quote
X			{
X			$$ = (int)findlabel((char *)$1);
X			if (((GEN *)$$)->g_name) {
X				sprintf(errmsg,"label %s multiply defined",
X				(char *)$1);
X				yyerror(errmsg);
X			}
X			((GEN *)$$)->g_name= (char *)$2;
X			((GEN *)$$)->g_label = (char *)$1;
X			((GEN *)$$)->g_ismapped = 0;
X			((GEN *)$$)->g_parent = NULL;
X			((GEN *)$$)->g_parents = NULL;
X			((GEN *)$$)->g_nmarriages = 0;
X			}
X	;
Xlabel	:	word COLON
X	;
Xsimple_p:	simple_p word
X			{
X			strcat(name," ");
X			strcat(name,(char *)$2);
X			}
X	|	word
X			{strcpy(name,(char *)$1);}
X	;
Xquote	:	QUOTE1	
X			{
X			yytext[strlen(yytext)-1]='\0';
X			$$ = (int)strcpy(malloc(strlen(yytext)),yytext+1);
X			}
X	|	QUOTE2
X			{
X			yytext[strlen(yytext)-1]='\0';
X			$$ = (int)strcpy(malloc(strlen(yytext)),yytext+1);
X			}
X	;
Xword	:	WORD
X			{
X			$$ = (int)strcpy(malloc(strlen(yytext)+1),yytext);
X			}
X	;
X%%
X#include <ctype.h>
X#include "gen.h"
X#include "ext.h"
X#include "lex.yy.c"
X
X#define	GALLOC	(GEN *)malloc(sizeof (GEN))
X#define	STREQ(a,b)	(!strcmp((a),(b)))
X
Xchar	*malloc(), *strcpy();
Xchar name[100];
Xchar sx, dt;
X
Xyyerror(s)
Xchar *s;
X{
X	printf("gen: error - %s; last name read = %s\n",s,name);
X}
X
Xint
Xis_goodlabel(s)
Xchar *s;
X{
X	while (*s) {
X		if (!(isalpha(*s) || isdigit(*s) || '_'== *s))
X		return 0;
X		else s++;
X	}
X	return 1;
X}
X
XGEN *
Xfindlabel(s)
Xchar *s;
X{
X	int i;
X	for (i=0; i<nper; i++)
X	if (STREQ(s, per[i]->g_label)) return per[i];
X	per[nper] = GALLOC;
X	per[nper]->g_number = nper;
X	return per[nper++];
X}
X
Xdateparse(d,s)
XDTE	*d;
Xchar	*s;
X{
X	char *s0, *s1;
X	d->d_year = s;	/* IN CASE PARSE FAILS */
X	for (s0=s; *s0; s0++) if ('-'==*s0) break;
X	if (!*s0) return;
X	for (s1=s0+1; *s1; s1++) if ('-'==*s1) break;
X	if (!*s1) {	/* MONTH BUT NO DAY */
X		*s0++ = NULL;
X		d->d_year = s0;
X		d->d_month = s;
X		return;
X	}
X	*s1++ = NULL;
X	d->d_year = s1;
X	*s0++ = NULL;
X	if (bflag) {
X		d->d_month = s0;
X		d->d_day = s;
X	}
X	else {
X		d->d_day = s0;
X		d->d_month = s;
X	}
X}
//E*O*F gen.y//

echo x - gen.l
sed -e 's/^X//' > "gen.l" << '//E*O*F gen.l//'
Xs		[A-Za-z0-9!$(),-.=?\\_`~]
XS		[A-Za-z0-9!$(),-.=?\\_`~'"]
X%%
X^\.GE.*\n	{return 0;}
X\/[^/\n]*\/	{;}
X\/[^/\n]*	{fprintf(stderr,"gen: unmatched /, line %d\n",yylineno);}
X:		{return COLON;}
X"%"		{return PERCENT;}
X\"[^"\n]*\"	{return QUOTE2;}
X\"[^"\n]*	{fprintf(stderr,"gen: unmatched \"\n"); return QUOTE2;}
X\'[^'\n]*\'	{return QUOTE1;}
X\'[^'\n]*	{fprintf(stderr,"gen: unmatched '\n"); return QUOTE1;}
X\<		{return LESS;}
X>		{return GREATER;}
X{s}{S}*		{return WORD;}
X"["		{return LBRACK;}
X"]"		{return RBRACK;}
X"&"		{return AMPERSAND;}
X"{"		{return LBRACE;}
X"}"		{return RBRACE;}
X"|"		{return BAR;}
X\#		{return HASH;}
X[@^]		{return SEX;}
X[*+]		{return DATE;}
X;		{return SEMICOLON;}
X[ \t\n]+	{;}
X.		{return MISC;}
X\177		|
X[\013-\037]	|
X[\001-\010]	{fprintf(stderr,"gen: illegal character \\0%2o\n",yytext[0]);}
X%%
//E*O*F gen.l//

echo x - main.c
sed -e 's/^X//' > "main.c" << '//E*O*F main.c//'
X/*
X *	main.c
X */
X
X#include <stdio.h>
X#include "gen.h"
X#include "ext.h"
X
Xint	yydebug;
Xint	inpipe[2], pid;
Xextern	FILE *yyin;
X
Xbomb()
X{
X	fprintf(stderr,"usage: gen [-e] [-i] [-p nn] [file ...]\n");
X	exit(1);
X}
X
Xmain(argc,argv)
Xint	argc;
Xchar	**argv;
X{
X	int col0, ch0;
X	col0 = 1;
X	yydebug=1;
X	while (--argc) {
X		if ('-'==**++argv) switch(*++*argv) {
X		case 'b':
X			bflag++;
X			break;
X		case 'd':
X			debug++;
X			break;
X		case 'e':
X			eflag++;
X			break;
X		case 'i':
X			iflag++;
X			break;
X		case 'p':
X			if (!--argc) bomb();
X			psize = atoi(*++argv);
X			break;
X		case 's':
X			if (*++*argv) sflag=atoi(**argv);
X			else sflag = 9999;
X			break;
X		default:
X			fprintf(stderr,"gen: unknown switch %s\n",--*argv);
X			exit(1);
X		}
X		else break;
X	}
X	if (argc) {
X		pipe(inpipe);
X		if ((pid = fork()) == 0) {
X			close(inpipe[0]);
X			if (inpipe[1] != 1) {
X				dup2(inpipe[1], 1);
X				close(inpipe[1]);
X			}
X			*--argv = "cat";
X			execv("/bin/cat", argv);
X			_exit(1);
X		}
X		close(inpipe[1]);
X		yyin = fdopen(inpipe[0], "r");
X	}
X	while (EOF!=(ch0=getc(yyin))) {
X		if (col0 && ch0=='.') {
X			ch0=getc(yyin);
X			if (EOF==ch0) {
X				printf(".");
X				exit(0);
X			}
X			if ('G'!=ch0) {
X				putchar('.');
X				putchar(ch0);
X				continue;
X			}
X			ch0=getc(yyin);
X			if (EOF==ch0) {
X				printf(".G");
X				exit(0);
X			}
X			if ('S'!=ch0) {
X				printf(".G");
X				putchar(ch0);
X				continue;
X			}
X			while (EOF!=(ch0=getc(yyin))) if ('\n'==ch0) break;
X			if (yyparse()) printf("gencheck: rejected\n");
X			checklabels();
X			checksexes();
X			checksibs();
X			if (iflag) infodump();
X			else printtree();
X		}
X		putchar(ch0);
X		col0 = (ch0=='\n');
X	}
X	if (pid) pclos();
X	exit(0);
X}
X
Xpclos()
X{
X	register r;
X	int status;
X	fclose(yyin);
X	while((r = wait(&status)) != pid && r != -1)
X		;
X}
//E*O*F main.c//

echo x - marry.c
sed -e 's/^X//' > "marry.c" << '//E*O*F marry.c//'
X/*
X *	marry.c
X */
X
X#include <stdio.h>
X#include "gen.h"
X
Xmarry(a,m)
XGEN *a;
XMAR *m;
X{
X	char mess[50], *sps;
X	if (a->g_nmarriages>=2) {
X		switch(a->g_sex) {
X		case 0:
X			sps="spouses";
X			break;
X		case MALE:
X			sps="wives";
X			break;
X		case FEMALE:
X			sps="husbands";
X			break;
X		}
X		sprintf(mess,"%16s has more than two %s",
X			a->g_name? a->g_name: a->g_label, sps);
X		yyerror(mess);
X	}
X	else a->g_marriage[a->g_nmarriages++] = m;
X}
X
XMAR *
Xstoremarriage(a,b)
XGEN *a, *b;
X{
X	MAR *c;
X	c = (MAR *)malloc(sizeof(MAR));
X	c->m_spouse[0] = a;
X	c->m_spouse[1] = b;
X	c->m_ismapped = 0;
X	return c;
X}
X
XMAR *
Xdomarriage(a,b)
XGEN *a, *b;
X{
X	MAR *m;
X	int j;
X/*
X *	First check for existing marriage.  (Unlikely but possible.)
X */
X	for (j=0; j<a->g_nmarriages; j++)
X	if (a->g_marriage[j]->m_spouse[0] == b ||
X		a->g_marriage[j]->m_spouse[1] == b) return a->g_marriage[j];
X	m = storemarriage(a, b);
X	marry(a,m);
X	marry(b,m);
X	return m;
X}
//E*O*F marry.c//

echo x - snap.c
sed -e 's/^X//' > "snap.c" << '//E*O*F snap.c//'
X/*
X *	snap - print snapshot of array.
X *	used for debugging embed().
X */
X
X#include <ctype.h>
X#include <stdio.h>
X#include "gen.h"
X#include "ext.h"
X
X#define MAX(a,b) ((a)>(b)?(a):(b))
X
Xint
Xsnap(xc, yc)
Xint xc, yc;
X{
X	int x, y;
X	SPOT *z;
X	char *snapname();
X	if (nrows < 1 || ncols < 1) {
X		fprintf(stderr,"[empty array]\n\n");
X		return 0;
X	}
X	for (y=MAX(0,yc-2); y<nrows && y<=yc+2; y++) {
X		for (x=MAX(0,xc-3); x<ncols && x<=xc+3; x++)
X		switch ((z = &array[y][x])->i_what) {
X		case I_VACANT:
X			fprintf(stderr,"        ");
X			break;
X		case I_MARLINE:
X			fprintf(stderr,"------- ");
X			break;
X		case I_SIBLINE:
X			fprintf(stderr,"   !    ");
X			break;
X		case I_VLINE:
X			fprintf(stderr,"   |    ");
X			break;
X		case I_PERSON:
X			fprintf(stderr,"%-8s",snapname(per[z->i_num]->g_name));
X			break;
X		case I_MARRIAGE:
X			fprintf(stderr,"---O--- ");
X			break;
X		}
X		fprintf(stderr,"\n");
X	}
X	fprintf(stderr,"\n");
X	return 0;
X}
X
Xchar *
Xsnapname(s)
Xchar *s;
X{
X	char *s0, *s1;
X	static char hold[8];
X	s1 = hold;
X	s0 = s-1;
X	for (; *s; s++) if (isupper(*s)) {
X		if (s1 >= hold+7) {
X			*s1 = NULL;
X			return hold;
X		}
X		*s1++ = *s;
X		s0 = s;
X	}
X	for (++s0; *s0; s0++) {
X		if (s1 >= hold+7) break;
X		*s1++ = *s0;
X	}
X	*s1 = NULL;
X	return hold;
X}
//E*O*F snap.c//

echo x - gen.h
sed -e 's/^X//' > "gen.h" << '//E*O*F gen.h//'
X/*
X *	gen.h - definitions for gen.
X */
X
X/*	default point size	*/
X#define	PSIZE	8
X/*
X *	max no. of people
X */
X#define	MAXP	300
X/*
X *	max nos. of rows and columns
X */
X#define	MAXROWS	25
X#define	MAXCOLS	80
X/*
X *	max no. of sibling groups
X */
X#define	MAXSBARS 50
X
X#define	MALE	1
X#define	FEMALE	2
X#define GEN	struct gen
X#define MAR	struct mar
X#define	DTE	struct dte
X#define	SPOT	struct spot
X#define	SBAR	struct sbar
X/*
X *	a real hack, but ...
X */
X#define	SPOUSE(g,m) ((g)!=(m)->m_spouse[0]?(m)->m_spouse[0]:(m)->m_spouse[1])
X/*
X *	defaults in inches:
X */
X#define	BOXHT	0.375
X#define	SPACEHT	0.15
X
Xstruct dte {
X	char *d_year;
X	char *d_month;
X	char *d_day;
X};
X
Xstruct gen {
X	short int g_number;	/* position in per[] */
X	char *g_name;	/* name to be displayed */
X	char *g_label;	/* cross-reference name */
X	char g_sex;	/* 1 or 2 */
X	DTE g_birth, g_death;
X	short int g_ordinal;	/* position among siblings */
X	short int g_nmarriages;	/* how many marriages */
X	short int g_ismapped;	/* 1 if it's installed in matrix */
X	short int g_x, g_y;	/* co-ordinates in matrix */
X	struct gen *g_parent;
X	struct mar *g_parents;
X	struct mar *g_marriage[2];
X	struct gen *g_offlist;	/* points to first solo offspring */
X	struct gen *g_sibnext;	/* points to next sibling */
X/*
X *	we need this to find the head of an unparented list:
X */
X	struct gen *g_sibprev;	/* points to previous sibling */
X	short int g_hassibbar;	/* does it hang from a sibling bar? */
X	short int g_sbar;	/* index of overhead sbar */
X	char g_mark;		/* for recursive operations */
X};
X
Xstruct mar {
X	short int m_ismapped;	/* 1 if it's installed in matrix */
X	short int m_x, m_y;	/* co-ordinates in matrix */
X	struct gen *m_spouse[2];/* point to members of union */
X	struct gen *m_offlist;	/* points to first offspring */
X};
X/*
X *	sibling horizontal bar.
X */
Xstruct sbar {
X	short int s_x0, s_x1, s_y;	/* coordinates. s_y=0 is ABOVE row 0 */
X	GEN *s_first;			/* pointer to first sibling */
X};
X
X/*
X *	map items.
X */
X#define	I_VACANT	0
X#define	I_PERSON	1
X#define	I_MARRIAGE	2
X#define	I_SIBLINE	3
X#define	I_MARLINE	4
X#define	I_VLINE		5
X/*
X *	with offspring lines:
X */
X#define	I_OPERSON	6
X#define	I_OMARRIAGE	7
X/*
X *	spots on the map.
X */
Xstruct spot {
X	short int i_mark;	/* for recursive operations */
X	short int i_what;	/* what is it? */
X	short int i_num;	/* person/sibline number */
X	MAR *i_mar;		/* pointer to marriage, if it's a marriage */
X};
//E*O*F gen.h//

echo x - ext.h
sed -e 's/^X//' > "ext.h" << '//E*O*F ext.h//'
X/*
X *	ext.h
X */
X
Xextern int nper, psize, iflag, eflag, debug, bflag, sflag;
Xextern GEN *per[];
Xextern char errmsg[];
Xextern	SPOT	array[MAXROWS][MAXCOLS];
Xextern	int	nrows, ncols, nsbars;
Xextern	float	boxht, spaceht;
Xextern	struct sbar sb[];
//E*O*F ext.h//

echo x - Makefile
sed -e 's/^X//' > "Makefile" << '//E*O*F Makefile//'
XOBJECTS= beget.o check.o dump.o embed.o ext.o gen.o main.o marry.o snap.o
XSOURCES= beget.c check.c dump.c embed.c ext.c gen.y gen.l main.c marry.c \
X snap.c gen.h ext.h
XAUXES= Makefile gen.1 gen.ms test.data
Xgen: $(OBJECTS) lex.yy.c
X	cc -o gen $(OBJECTS) -ll
Xlex.yy.c: gen.l
X	lex gen.l
X$(OBJECTS): gen.h ext.h
Xclean:
X	rm -f $(OBJECTS)
Xshar:
X	shar $(SOURCES) $(AUXES) > gen.shar
//E*O*F Makefile//

echo x - gen.1
sed -e 's/^X//' > "gen.1" << '//E*O*F gen.1//'
X.TH GEN 1 "July 18, 1985"
X.SH NAME
Xgen \- draw family trees
X.SH SYNOPSIS
X.B gen
X[
X.B \-b
X] [
X.B \-e
X] [
X.B \-i
X] [
X.B \-p
X.I nn
X[ file ... ] \||\|
X.B pic
X[ options ]
X.SH DESCRIPTION
X.I Gen
Xis a
X.IR pic (1)
Xpreprocessor for drawing family trees.
X.I Gen
Xsource input is embedded in
X.IR troff (1)
Xsource text between .GS and .GE lines.
XInput syntax in brief:
X.sp
X.RS
X.B name:
X.I word
X\&...
X.br
X.B label:
X.I word
X.br
X.B person:
X.BI [ label
X.RB : ]
X.I name
X.BR [ #
X.IB position ]
X.BR [ *
X.IB birthdate ]
X.BR [ +
X.IB deathdate ]
X.br
X.B person:
X.RI % label
X.br
X.B marriage:
X.RI [ person
X&
X.IR person ]
X.br
X.B siblings:
X.RI { person
X| ... |
X.IR person }
X.br
X.B descent:
X.I person
X>
X.I person
X\&...
X.RE
X.sp
XSee the manual for further information.
X.PP
XThe
X.B \-b
Xoption parses dates with dashes as
X.I day\-month\-year
Xrather than the default
X.I month\-day\-year.
XThe
X.B \-e
Xoption draws ellipses instead of boxes around women's names.
XThe
X.B \-i
Xoption suppresses
X.I pic
Xcode and produces instead a dump of the data read by
X.I gen.
XThe
X.B \-p
Xoption prints names and dates in point size \fInn.\fP
XThe default is 8-point.
X.SH AUTHOR
XCol. G. L. Sicherman
X.SH "SEE ALSO"
Xpic(1), troff(1)
X.br
XG. L. Sicherman,
X.I "Gen \(em A Program for Drawing Family Trees"
X.SH BUGS
XDumps core unless you use the
X.B \-i
Xoption.
//E*O*F gen.1//

echo x - gen.ms
sed -e 's/^X//' > "gen.ms" << '//E*O*F gen.ms//'
X.\" tbl | troff -ms ...
X.if t .ds bu \(bu
X.if n .ds bu *
X.OH +\fBGen\fP++\fB%\fP+
X.EH +\fB%\fP++\fBGen\fP+
X.de Ib
X.IP \fB\\$1\fP
X..
X.de QU
X\\*Q\\$1\\*U\\$2
X..
X.TL
XGen \- A Program to Print Family Trees
XVersion 0
X.AU
XG. L. Sicherman
X.AI
XDepartment of Computer Science
XState University of New York at Buffalo
X.ND
XDecember 31, 1985
X.AB
XThis paper describes
X.I gen,
Xa program for drawing family trees.
X.AE
X.NH 1
XDescription.
X.PP
X.I Gen
Xis a program to print family trees.
XIt reads data in a compact format and produces input code for
X.I pic (1),
Xthe picture preprocessor for
X.I troff (1).
X.NH 1
XInput specfications.
X.PP
X.I Gen
Xis actually a preprocessor, like
X.I pic.
XA
X.I gen
Xsource file looks like this:
X.DS
Xtext text text.
X\&.GS
Xfamily tree specification
X\&...
X\&.GE
Xtext text text.
X\&.GS
Xanother family tree specification
X\&...
X\&.GE
Xtext text text ...
X.DE
XThe input language for
X.I gen
Xis defined recursively using
X.I yacc (1).
X.NH 2
XPersons.
X.PP
XThe simplest kind of input is a single person:
X.DS
XThorin Oakenshield ^ * 2746 + 2941
X.DE
XThe * introduces the person's date of birth, and the + his date of
Xdeath; both are optional.
XNames and dates may be quoted; e.g.,
X.DS
XAragorn ^ * 2931 + "120 F.A."
X.DE
XA date with dashes, such as
X.QU 5\-18\-49,
Xis broken into month, day, and year;
Xquoted dates are never broken so.
XSex is denoted by
X.QU ^
X(for male)
Xor
X.QU @
X(for female).
XThe order of qualifiers is immaterial; you could just as well say
X.DS
XAragorn + "120 F.A." ^ * 2931
X.DE
X.PP
XNormally the order of siblings in a family is taken to be the
Xorder of their birth or of their appearance in the input.
XYou can override this by using
X.QU # :
X.DS
XIsengar ^ * 1262 + 1360 # 12
X.DE
XThis identifies Isengar as the twelfth offspring of a marriage.
XIf you use this feature for one offspring, you must use
Xit for all of them.
X.NH 2
XMarriages.
X.PP
XTo marry two persons, bracket them with an ampersand in between:
X.DS
X[Bungo Baggins ^ * 1246 + 1326 & Belladonna Took]
X.DE
X.I Gen
Xwill infer that Belladonna Took is female.
X.PP
X.I
XA marriage may be used syntactically to represent its first member.
X.R
XFor example,
X.DS
X[[Fargo Flatfoot ^ & Grosilla Heath] & Nivola Ferntoes]
X.DE
Xsignifies that Fargo Flatfoot was twice married.
XYou cannot specify the order of a person's marriages
Xor marry a person more than twice.
X.NH 2
XSiblings.
X.PP
XTo declare that persons are offspring of the same parent or
Xparents, enclose them in braces and separate them with
Xvertical bars:
X.DS
X{Hamson | Halfred | Daisy | May | Samwise | Marigold}
X.DE
XHalf-siblings and step-siblings should not be treated this way,
Xunless they have one parent in common and the other parents
Xare not specified.
X.NH 2
XDescent.
X.PP
XTo indicate descent, use
X.QU > :
X.DS
X[Saradoc ^ * 1340 + 1432 & Esmeralda Took] > Meriadoc
X.DE
XChains of descent may run on to any length:
X.DS
X[Gormadoc & Malva] > [Madoc & Hanna] > [Marmadoc & Adaldrida]
X.DE
XYou need not specify both parents:
X.DS
XSaradas ^ * 1308 + 1407 > [Saradic ^ * 1348 & Hilda Bracegirdle]
X.DE
XYou can also specify a group of siblings as the offspring:
X.DS
XMarmadas ^ * 1343 > {Merimas * 1381 | Mentha * 1383 | Melilot * 1385}
X.DE
XWhen you like, you can ascend with
X.QU < :
X.DS
X[Peony Baggins @ & Milo Burrows * 1347 < [Asphodel @ & Rufus Burrows]]
X.DE
XIn the preceding example, Milo Burrows is descended from Asphodel
Xand Rufus.
XIf the ascent sign lay outside the first set of brackets,
X.DS
X[Peony Baggins @ & Milo Burrows * 1347] < [Asphodel @ & Rufus Burrows]]
X.DE
Xit would indicate that Peony Baggins was descended
Xfrom Asphodel and Rufus,
Xaccording to the rule that a marriage is syntactically equivalent
Xto its first member.
XHowever, this rule does not apply to the parents.
XMilo is the son of Asphodel and Rufus, not of Asphodel alone.
XTo indicate that Milo is the son of Asphodel alone, you could write:
X.DS
X[Asphodel > Milo Burrows & Rufus Burrows]
X.DE
XThat it is Asphodel and not Milo who married Rufus follows from
Xthe rule that
X.I
Xa chain of descent (or ascent) is syntactically equivalent
Xto its first member.
X.R
X.PP
XOf course,
Xthis rule does not apply within a chain;
Xe.g.,
X.DS
XOdo Proudfoot > Olo > Sancho
X.DE
Xis not parsed as
X.DS
X(Odo Proudfoot > Olo) > Sancho
X.DE
Xsince that would cause Sancho to be descended from Odo instead of Olo.
X.NH 2
XChopping up a tree.
X.PP
XThe input data to
X.I gen
Xshould form a single tree, simply connected.
XThat is, there should be exactly one route from any
Xperson to any other person.
XWhen it is not convenient to specify a complicated tree
Xin a single statement,
Xyou can write several statements separated by semicolons:
X.DS
X[Belba & Rudigar Bolger] ; [Longo & Camellia Sackville] ; {Belba | Longo}
X.DE
X.PP
XTo connect the parts,
Xyou must identify persons who occur in more than one part.
XThis is done with
X.I labels:
X.DS
Xpippin : Peregrin Took ^ * 1390
X.DE
XLabels must be unique (since names need not be).
XA person referred to in more than one part must be labeled and named
Xin exactly one part, and specified in other parts with a
X.QU %
Xand the label:
X.DS
X[belba: Belba & Rudigar Bolger] ;
X[longo: Longo & Camellia Sackville] ;
X{%belba | %longo}
X.DE
XA label may occur before it is defined.
X.DS
X[%belba & Rudigar Bolger] ;
X[%longo & Camellia Sackville] ;
X{belba: Belba @ * 1256 + 1356 | longo: Longo ^ * 1260 + 1350}
X.DE
X.NH 2
XComments.
X.PP
XAnything enclosed in slashes is ignored.
X.DS
X\h'1i'/ The Longfodder Line of Alpo Fathead /
X[Alpo Fathead ^ & Gurkha Baksheesh /Wasn't she a half-orc?/ ]
X.DE
X.NH 1
XUsage.
X.PP
X.I Gen
Xproduces input for
X.I pic,
Xand is useless without it.
XYou typically invoke
X.I gen
Xlike this:
X.DS
Xgen [options] sourcefile | pic [options] | troff [options]
X.DE
XThe options to
X.I gen
Xare:
X.Ib \-b
Xparse dates with dashes as
X.I day\-month\-year.
X.Ib \-e
XDraw ellipses instead of rectangles
Xaround people identified as women.
X.Ib \-i
XInstead of drawing a family tree, just dump the people and
Xhow they are related.
XUsed for debugging source input.
X.Ib \-p
XThe next argument is the point size in which to print people's names.
XDefault is 8-point.
X.PP
XThe input must be simply connected;
Xotherwise
X.I gen
Xwill complain.
XThe only way
Xto print the tree for an intermarried family
Xis to enter the same person twice, causing him to
Xappear in two places on the tree.
X.PP
XAt present
X.I gen
Xdumps core when it tries to generate
X.I pic
Xinput.
XThus you can use it only with the
X.B \-i
Xoption.
XIf you can hack the code so as to get it working,
Xgood for you.
XI shall have no time for this project for some years.
//E*O*F gen.ms//

echo x - test.data
sed -e 's/^X//' > "test.data" << '//E*O*F test.data//'
X.LP
X.ft RC		\" Roman Condensed
X.GS
X	/an anonymous family/
X{ Kaodso Whjb ^ |
X  Gyee Ekyx ^ > {
X	Owryru |
X	Zlzfam |
X	Mnmw |
X	?
X  } |
X  [[Luhnm Bfkk @ *1910 & Hcmetm Qbdasigo] > {
X	[Lyyufv Yijxfpps ^ & Hpltuueox (Qxug?) ?] > {
X		[Izbto ^ & Hfdqd] > Qjejrac ^ |
X		Gksupsk @ > Sunxqtn Dvrjj @ |
X		Qfep @
X	} |
X	[Zxevseeht Ftxczboi @ & Zydpjw Dcfxpl] > {
X		Llmevca Hnghfh ^ |
X		[Rdyhw Mgcdhu @ & Ltrnmcu Ffeisbhr] > {
X			Krvtvs ^ |
X			Vjlvj? @
X		}
X	} |
X	[Folw Zwvoznnr ^ & Mywvoi ?] > {
X		[Xlim ^ & Bdgkz ?] > {
X			Hmtc @ | Dsnk @ | Nomcp Rocmn @ *1985
X		} |
X		[Qaziue @ & Psumgte Quzuhy] > {
X			Fydqi | Ckcroq
X		} |
X		[Kfumetf ^ & Vfaqz @]
X	} |
X	[Ydalfbq Uirji Uwgaxyjf ^ & tiwfmuv: Plpsdys Wokg >
X		[[Wnqas Egtlpqrl @ & Bxbvj Yynjzzlm] > Ogtadt
X		& Ztfjn ``Hkn'' Gmojcj]] > {
X		[Fmpvdvr *1956 @ & Gyrqxj Lmqgwlsby *1949] > Oeatm *1984 ^ |
X		Quzmz |
X		[Otxhk ^ & NiAopm Pqxmu] > {
X			Senrux *1980 | Nzdptf *1982 @
X		}
X	} |
X	Aotib Iubcyxtu @ |
X	[Pqaxrkf Nsedubif ^ & [Lpqx ? & ?] > ?] > Ypwvugd ``Unui'' @
X  } & vnk: ``Sbu'' Rcna]
X}
X;
X[%vnk & ?] > {
X	Chmtuwyn |
X	Gpmhgo |
X	[Dxcp @ & Uhzpndi N'Zbztq] > {Xrfwfqvg @ | Jcpyuzz ^}
X}
X;
X[Tooso Fleuaqwcw ^ & [Xhzfza Wooh Hlaks & Hamqbm Zlfoqb]] > {
X	Tuwgfe ^ |
X	[Eszaag ^ & Ruqks ?] > Gipzy Lvci @ |
X	[Rvaw ^ & Rovdbaqws] > {
X		[Zyjjc @ & Dmysxev Eayvwa] |
X		[Mgfu ^ & Ibaet Jocxyymyh] > {Zmgplfv ^ | Ndwlw @ | ? | ?} |
X		[Geyydgy ^ & ?] > Btxcfi @
X	} |
X	[[Ewboc @ & Cfvm ?] > [Kfkzzt Aarfpfvox ^ & Pxdo YyEpjjqzr] > {
X			[Utgt @ & ?] > ? |
X			Ixja @ | Dmcynk @ | Whgnmcp | Fyzzzfv | Whjlfezqk @ |
X			Xhqgrssa @
X		} & Juddrcx Rocqb] > {
X		[Kiwsnju @ & Kuubgdxpp Gdaho] |
X		[Cfsljq @ & Suaopo Ndofwpkmnzrg]
X	} |
X	[Unnv @ & Xrj Pjeknq] > [Qbxlfs @ & Fmvnn Irkdcoav] >
X		{Mdmfcpmo @ | Dbvyd | Pdtlppz @} |
X	[EsQzdbu Zudztozia @ *1913 +1984 & Cowpt Fomv <
X			[Ucntng Zbenpve HwQuho ^ & jqoo: Rbev Nhkte Tlsipy]] > {
X		[%tiwfmuv & Kqbvlb J. Jevjqvre < Oqcnxn Hspldslh] >
X		 Hjwzcx Y. Suakddcj |
X		[Epqsodd @ & Heqiim Rzhvnot] > {
X			Rbqswhuq @ | Ytnzcfuoh ^ | Ehxqgw Okge @ | Oyfpobw ^
X		} |
X		[Qfmekcyn @ & Yfnilce Lenwxc] > {
X			[Lhewljn Oeyr Owlbtd ^ & Dtyl] > Jojuvn Yuwj |
X			[Pbgik Sarjxco Cbygot ^ & Znwua Ga. Fjrq] |
X			Scah Lohulx Iyhsfj ^
X		} |
X		[[Peeipg ^ & [Nbvejtofa ? & Vdhpk Htelu] > {
X				Behrq | Zzbuvow }] > {
X			[Pvtyan ^ & Npzdzcstt] > Jyxocwtv Eiddxg @ |
X			[Zngejir L. Qncd ^ & Dtfwxtxsy Ykmjq Lpbvjfwc <
X				Yvwmkz Ruhluuyg ^] |
X			[Ynqij K. Bmvx ^ & Xtccwe F. Kvdfm]
X		} & [Ytksunhp ? & Xkoid? Nira] > {
X			Wdpvobb Fwof | Kapcj Adab |
X			[Prgzpbva Ihkk @ & Fbhmop Xmkoih]
X			> {? | ? | ?}}] > Yciazjxet @
X	}
X}
X;
X{%jqoo | ? > [Hjnzf ? @ & Orrutf Bxeucupe] >
X{Gddged Zepbrgre^ | Rghfvf Hushkoot^}}
X.GE
//E*O*F test.data//

exit 0
-- 
Col. G. L. Sicherman
UU: ...{rocksvax|decvax}!sunybcs!colonel
CS: colonel@buffalo-cs
BI: csdsicher@sunyabva