/*
* @progname ll2html.ll
* @version 2005-11-19
* @author JRE Jim Eggert
* @category
* @output HTML
* @description
*
* This report program converts a LifeLines database into html documents.
* Family group records are created for each selected individual in
* the database. These records are written in files containing clumps
* of individuals of a user-selected size. Index files are generated
* for an index document. Or, optionally, all output is sent to
* one file.
*
* You will need to change the contents of proc html_address() and to
* set the value of HREF appropriately to your server.
* You need to set the value of PATH to point to the directory to put
* the files into. If you have 1000 individuals in your database this
* program will create up to 1027 files, one for each individual and
* up to 27 index files, if you set the clump size to one.
*
* This program will also generate three pedigree charts for the root
* individual and descendants charts for selected individuals.
*
* You also need to set the value of HOST to be the http server and
* path where you will server these files from.
*
* History
* 01-07-94 sew; Created.
* 11-18-94 jre; Added clump capability.
* 02-16-95 jre; Added privacy option.
* 03-06-95 jre; Added pedigree table, better sorting.
* 05-10-95 jre; Added descendants charts.
* 05-02-97 jre; Added ISO8859 encoding in GENDEX.txt file.
* 07-09-99 jre; Added background decorations, improved HTML.
* 01-15-00 jre; Fixed quicksort bug
* 11-19-05 jre; Updated released version to rev 12. Many changes.
*
*/
global(INDEX)
global(INDEXTABLE)
global(HREF)
global(PATH)
global(PEDIGREE_NAME)
global(INDEX_NAME)
global(TITLE)
global(ADDRESS)
global(FB)
global(nl)
global(qt)
global(CURRENTCLUMPFILE)
global(root_person)
global(root_key)
global(separate_clumps)
global(PRIVTABLE)
global(privacytern)
global(sort_xlat)
global(html_xlat)
global(ISO8859_xlat)
/* These globals are for descendant reports */
global(grouped_henry)
global(comma_separation)
global(first_comma)
global(generations)
/* This is for descendant and ancestor reports */
global(written_people)
global(tree)
global(ancestors)
global(qt)
global(deltax)
global(deltay)
global(html_xlat)
/* These constants are for estimating birth years */
global(years_between_kids)
global(mother_age)
global(father_age)
/* These globals are for time limits on privacy */
global(hundred_years_ago)
global(eighty_years_ago)
/* Decoration globals */
global(male_gif)
global(female_gif)
global(unknown_gif)
global(logo_gif)
global(background_gif)
proc main()
{
/* Change these to suit your needs */
set(TITLE,"Eggert Family Genealogy") /* Title of main genealogy page */
set(PEDIGREE_NAME,"Eggert Family Ancestry") /* Pedigree chart title */
set(INDEX_NAME,"Eggert Family Genealogy Home ") /* Index title */
set(DESC_NAME,"Eggert Family Descendant List") /* Descendant list title */
set(PATH, "") /* path for file references */
set(HREF, "") /* host and path */
set(qt, qt())
set(male_gif,concat(qt,"7m.gif",qt," HEIGHT=68 WIDTH=68"))
set(female_gif,concat(qt,"7f.gif",qt," HEIGHT=80 WIDTH=50"))
set(unknown_gif,concat(qt,"5U.GIF",qt))
set(logo_gif,concat(""))
set(background_gif,concat(qt,"oldyellow.gif",qt))
set(FB, 0)
set(nl, nl())
list(INDEX)
table(INDEXTABLE)
table(PRIVTABLE)
table(sort_xlat)
table(html_xlat)
table(ISO8859_xlat)
call init_xlat()
call init_years()
indiset(people)
getindimsg(root_person,"Enter root individual:")
set(root_key,key(root_person))
set(clumpsize,0)
while (le(clumpsize,0))
{
getintmsg(clumpsize,"Enter number of individuals per file:")
}
/* getintmsg(separate_clumps,
* "Do you want clumps in separate files (0=no,1=yes)?")
*/
set(separate_clumps,1)
list(choices)
enqueue(choices,"all")
enqueue(choices,"deceased individuals only")
enqueue(choices,"none")
set(privacytern,sub(menuchoose(choices,"Include notes and dates for:"),1))
list(nonprivates)
if (privacytern) {
set(person,1)
while(person) {
set(person,0)
getindimsg(person,"Enter non-private person:")
if (person) { enqueue(nonprivates,key(person)) }
}
}
list(desc_roots)
set(person,1)
while(person) {
set(person,0)
getindimsg(person,"Enter root for descendant list:")
if (person) { enqueue(desc_roots,key(person)) }
}
print("Finding ancestry... ")
addtoset(people, root_person, 0)
set(people,union(ancestorset(people),descendantset(people)))
addtoset(people, root_person, 0)
set(people,union(people,spouseset(people)))
/* set(people,union(people,childset(people))) */
set(indicount,0)
set(clumpcount,1)
print("done\nCollating index... 1")
forindiset(people,me,val,num)
{
/* print(".") */
incr(indicount)
if (ge(indicount,clumpsize))
{
incr(clumpcount)
set(indicount,0)
print(" ", d(clumpcount))
}
set(k,key(me))
enqueue(INDEX,k)
insert(INDEXTABLE,k,clumpcount)
if (eq(privacytern,1)) { insert(PRIVTABLE,k,privacy(me)) }
elsif (eq(privacytern,0)) { insert(PRIVTABLE,k,0) }
else { insert(PRIVTABLE,k,1) }
}
if (privacytern) {
while (pkey,dequeue(nonprivates)) {
insert(PRIVTABLE,pkey,0)
}
}
print(" done\nWriting index(slow)...")
/* */
call create_index_file(desc_roots)
/* */
print(" done\nWriting name files...")
call start_clumpfile(1)
forindiset(people, me, val, num)
{
call write_indi(me)
}
call end_clumpfile()
/* */
/* */
print(" done\nWriting pedigree chart...")
call pedigree_chart(indi(root_key))
/* */
/* Disable privacy checks for protected access reports */
set(privacyternsave,privacytern)
set(privacytern,0)
print(" done\nWriting descendant lists...")
call descendant_lists(desc_roots)
print("done\n")
set(privacytern,privacyternsave)
}
proc descendant_lists(desc_roots) {
set(grouped_henry,0)
set(comma_separation,3)
set(first_comma,0)
set(generations,0)
while (desc_key,dequeue(desc_roots)) {
print(desc_key," ")
set(desc_root,indi(desc_key))
list(henry_list)
table(written_people)
push(henry_list,substring(mysurname(desc_root),1,1))
set(fn, concat(PATH, "onlyfamilydesc",desc_key,".html"))
if (separate_clumps) { newfile(fn, FB) }
call html_header(DESC_NAME, 0)
"
\n"
call html_trailer("","Pedigree%20list")
set(fn, concat(PATH, "pedigreen.html"))
if (separate_clumps) { newfile(fn, FB) }
call html_header(PEDIGREE_NAME, 0)
"Go to graphic version or sort by lineage or "
"name.
\n"
call ahnen(person)
"\n"
call html_trailer("","Ahnentafel%20list")
set(fn, concat(PATH, "pedigreea.html"))
if (separate_clumps) { newfile(fn, FB) }
call html_header(PEDIGREE_NAME, 0)
"Go to graphic version or sort by lineage or "
"generation.
\n
\n"
call ahnensort(person)
"
\n"
call html_trailer("","Ancestor%20list")
set(fn, concat(PATH, "pedigreeg.html"))
if (separate_clumps) { newfile(fn, FB) }
call html_header_graphic(PEDIGREE_NAME, 0)
"Go to text version or sort by generation or name."
call tableau(person)
call html_trailer_graphic("","Pedigree%20graph")
}
proc pedigree(in, ah, indi) {
if (didah,lookup(written_people,key(indi))) {
rjustify(d(ah),add(1,mul(in,2))) " " call href(indi,neg(1)) " (see " d(didah) ")" nl
} else {
if (par, father(indi)) { call pedigree(add(1,in), mul(2,ah), par) }
rjustify(d(ah),add(1,mul(in,2))) " " call href(indi,neg(1)) nl
insert(written_people,key(indi),ah)
if (par, mother(indi)) { call pedigree(add(1,in), add(1,mul(2,ah)), par) }
}
}
proc ahnen(person) {
table(written_people)
list(plist)
list(nlist)
enqueue(plist,person)
enqueue(nlist,1)
set(twotothen,1)
set(greatcount,neg(2))
while(p,dequeue(plist)) {
set(n,dequeue(nlist))
while (ge(n,twotothen)) {
if (eq(twotothen,1)) { set(label,"Self") }
elsif (eq(twotothen,2)) { set(label,"Parents") }
elsif (eq(twotothen,4)) { set(label,"Grandparents") }
elsif (eq(twotothen,8)) { set(label,"Great-Grandparents") }
else { set(label,concat("Great(x",d(greatcount),")-Grandparents")) }
"
" label "
\n"
set(twotothen,add(twotothen,twotothen))
incr(greatcount)
}
d(n) " " call href(p,neg(1))
if (other,lookup(written_people,key(p))) {
" (see " d(other) " above)"
} else {
insert(written_people,key(p),n)
if (f,father(p)) {
enqueue(plist,f)
enqueue(nlist,mul(2,n))
}
if (m,mother(p)) {
enqueue(plist,m)
enqueue(nlist,add(1,mul(2,n)))
}
}
" \n"
}
}
proc ahnensort(person) {
list(plist)
list(nlist)
list(klist)
list(nklist)
table(written_people)
enqueue(plist,person)
enqueue(klist,key(person))
enqueue(nlist,1)
enqueue(nklist,1)
while(p,dequeue(plist)) {
set(n,dequeue(nlist))
if (f,father(p)) {
if (didit,lookup(written_people,key(f))) { "" }
else {
insert(written_people,key(f),n)
enqueue(plist,f)
enqueue(klist,key(f))
set(nf,add(n,n))
if (gt(nf,nmax)) { set(nmax,nf) }
enqueue(nlist,nf)
enqueue(nklist,nf)
}
}
if (m,mother(p)) {
if (didit,lookup(written_people,key(m))) { "" }
else {
insert(written_people,key(m),n)
enqueue(plist,m)
enqueue(klist,key(m))
set(nm,add(n,n,1))
if (gt(nm,nmax)) { set(nmax,nm) }
enqueue(nlist,nm)
enqueue(nklist,nm)
}
}
}
list(sortindex)
list(transindex)
call translate(klist,transindex)
call quicksort(transindex,sortindex)
set(maxspacecount,strlen(d(nmax)))
forlist(sortindex,sindex,counter)
{
set(p,indi(getel(klist,sindex)))
set(n,getel(nklist,sindex))
set(spacecount,sub(maxspacecount,strlen(d(n))))
while(spacecount) { " " decr(spacecount) }
d(n) " " call href(p,neg(1)) nl
}
}
proc do_header(indi_root)
{
"desc-henry: Descendant report for " fullname(indi_root,0,1,80)
"\nGenerated by the LifeLines Genealogical System on "
stddate(gettoday()) ".\n\n"
}
proc do_trailer(indi_root)
{
"\nEnd of Report\n"
}
proc tableau(indi_root)
{
set(deltax,80)
set(deltay,16)
list(tree) /* this will be a list of generations, most recent first */
/* each generation will be a list of ancestors, most paternal first */
/* each ancestor will be a list containing their data:
key (can be duplicate), generation, ahnentafel, y position, father ancestor, mother ancestor, duplicate boolean */
table(ancestors) /* keys are ancestors, entries are lowest ahnentafel numbers */
list(plist)
list(ancestor)
enqueue(ancestor,key(indi_root))
enqueue(ancestor,1)
enqueue(ancestor,1)
enqueue(plist,ancestor)
/* Generate basic pedigree tree */
while (ancestor,dequeue(plist)) {
set(key,getel(ancestor,1)) /* get basic information */
set(gen,getel(ancestor,2))
set(ahn,getel(ancestor,3))
set(person,indi(key))
if (lt(length(tree),gen)) { /* make another generation if we need it */
list(generation)
enqueue(tree,generation) /* Note: can't skip a generation! */
}
set(generation,getel(tree,gen)) /* get the generation */
enqueue(generation,ancestor) /* put this ancestor on it */
if (oldahn,lookup(ancestors,key)) { /* if we have already done this ancestor ... */
setel(ancestor,7,oldahn) /* mark it as a duplicate */
} else {
setel(ancestor,7,0) /* mark it as a non-duplicate */
insert(ancestors,key,ahn) /* put it in the table of ancestors */
if (par,father(person)) { /* and look for a father to enqueue */
list(father)
enqueue(father,key(par)) enqueue(father,add(gen,1)) enqueue(father,add(ahn,ahn))
enqueue(plist,father)
setel(ancestor,5,father)
}
if (par,mother(person)) { /* and look for a mother to enqueue */
list(mother)
enqueue(mother,key(par)) enqueue(mother,add(gen,1)) enqueue(mother,add(ahn,ahn,1))
enqueue(plist,mother)
setel(ancestor,6,mother)
}
}
}
/* Make the geometry of the tree */
call make_geometry()
/* Write the output */
call write_tree()
}
proc make_geometry() { /* figure out y positions of all the ancestors */
list(tofix)
set(gennum,length(tree))
while (gennum) { /* for each generation, oldest generation first */
set(generation,getel(tree,gennum))
set(lasty,0)
forlist(generation,ancestor,ancnum) { /* for each ancestor within the generation, patrilineal first */
if(and(getel(ancestor,5),getel(ancestor,6))) { /* has father and mother */
set(thisy,div(add(getel(getel(ancestor,5),4),getel(getel(ancestor,6),4)),2))
} elsif (getel(ancestor,5)) { /* has father */
set(thisy,getel(getel(ancestor,5),4))
} elsif (getel(ancestor,6)) { /* has mother */
set(thisy,getel(getel(ancestor,6),4))
} else {
set(thisy,add(lasty,deltay))
}
setel(ancestor,4,thisy)
set(fix,add(lasty,deltay,neg(thisy)))
if (gt(fix,0)) { /* too close to previous ancestor within the generation, fix this person */
/* and all his/her ancestors */
enqueue(tofix,ancestor)
/* plus all parents of those persons below this one and their ancestors */
set(found,0)
forlist(generation,ancestor2,ancnum2) {
if (found) {
if (getel(ancestor2,5)) { enqueue(tofix,getel(ancestor2,5)) }
if (getel(ancestor2,6)) { enqueue(tofix,getel(ancestor2,6)) }
} elsif (eq(ancestor,ancestor2)) { set(found,1) }
}
while(fixee,dequeue(tofix)) {
setel(fixee,4,add(fix,getel(fixee,4)))
if (getel(fixee,5)) { enqueue(tofix,getel(fixee,5)) }
if (getel(fixee,6)) { enqueue(tofix,getel(fixee,6)) }
}
}
set(lasty,getel(ancestor,4))
}
decr(gennum)
}
}
proc write_tree() { /* this procedure destroys (recycles?) the tree and all its generations */
set(x,8)
set(maxx,add(x,mul(deltax,length(tree))))
set(maxy,0)
forlist(tree,generation,gennum) {
set(thismaxy,getel(getel(generation,length(generation)),4))
if (gt(thismaxy,maxy)) { set(maxy,thismaxy) }
}
set(maxy,add(maxy,deltay))
"
\n"
while(generation,dequeue(tree)) {
while(ancestor,dequeue(generation)) {
set(person,indi(getel(ancestor,1)))
/* first write the person in a box */
"
\n"
/* then draw any connectors to his/her parents */
set(top,add(getel(ancestor,4),5))
set(left,add(x,deltax,neg(18)))
if(getel(ancestor,7)) { /* duplicate */
if(or(father(person),mother(person))) { /* draw a short line */
"
\n"
}
} elsif(and(getel(ancestor,5),getel(ancestor,6))) { /* has father and mother */
"
\n"
"This database contains the families of the ancestors of my children.\n"
"Most of them are German, German-American,\n"
"Syrian, and Syrian-American.\n"
"This list contains about a twelfth of\n"
"my entire genealogical database. If you would like to see more,\n"
"please send e-mail."
"
There is also a PDF file (~400KB, 162 pages) of the entire ancestry.\n"
"
Here are some of my special projects.\n"
indiset(baseset)
addtoset(baseset,indi(root_key),1)
indiset(addset)
addtoset(addset,indi(root_key),1)
set(generations,4)
while(gt(generations,0)) {
set(addset,parentset(addset))
forindiset(addset,addperson,pval,pnum) {
if (female(addperson)) { addtoset(baseset,addperson,1) }
}
decr(generations)
}
namesort(baseset)
forindiset(baseset,person,pval,pnum) {
if (eq(pnum,1)) {
"
These are the base surnames in this ancestry: \n"
}
"" mysurname(person) ""
if (eq(pnum,sub(lengthset(baseset),1))) { ", and\n" }
elsif (eq(pnum,lengthset(baseset))) { ".\n" }
else { ",\n" }
}
"
You can also find surnames alphabetically by their first letter: \n"
set(first_dash,1)
while (initial,dequeue(initials))
{
set(count,dequeue(initialcounters))
if (first_dash) {
set(first_dash,0)
} else {
" - "
}
""
initial ""
}
"\n"
"
There are " d(length(INDEX))
" main entries in this website, from "
set(pcount,0)
forindi(person,pnum) { set(pcount,pnum) }
d(pcount) " in my database, last updated "
dayformat(2) monthformat(6) dateformat(0)
stddate(gettoday()) ".\n"
"