############################################################################### # Generate Macky Family Genealogy Tree -- Ian Macky, Feb 2009 # # Data files are named after the genealogy code (e.g. B1d1.dat) and # have one datum per line. Format is: # [details] # # where is one of: # n name # b birth-date # d death-date # w website URL # a arrival-date # = marriage-date # - divorce-date # s separation-date # h extra html to include # p photo # # number of children # # for example: # n Samuel Cochrane MACKY # b 7/11/1844 # d 11/2/1914 # = 1865 # p Samuel_Cochrane_Macky.jpg Samuel Cochrane Macky # # 1 # = 1868 # n Janet (Jessie) SHAW NEE WALLACE # # 3 # # $Id: gentree.awk,v 1.23 2018/07/18 03:57:29 ian Exp $ ############################################################################### BEGIN { debug = 0 public_levels = 3 # of top levels which are public cell_spacing = 5 # spacing around child cells max_kids_row = 6 # max number children per row spouse_spacer = 10 # horizontal space between spouses body_color = "white" # background page color text_color = "black" link_color = "black" # un-visited links vlink_color = "#333333" # visited-links color code_color = "red" # color for genealogy code checker_color = "#CCCCCC" # color alternating child cells hr_color = "#666666" # color nav_current = "red" # current place in nav bar nav_border = "#666666" # navigation box border nav_bg = "#CCCCFF" # navigation box background up_icon = "up.gif" # icon for up-arrow right_icon = "right.gif" # icon for right-arrow left_icon = "left.gif" # icon for left-arrow icon_size = 33 # width & height of square icons root_name = "James MACKY" # top_level name root_spouse = "Dorcas" private_subdir = "table/" # private HTML goes here table_boiler1 = "tables0" # first boilerplate for master tables table_boiler2 = "tables1" # second boilerplate ditto month[1] = "Jan" month[2] = "Feb" month[3] = "Mar" month[4] = "Apr" month[5] = "May" month[6] = "Jun" month[7] = "Jul" month[8] = "Aug" month[9] = "Sep" month[10] = "Oct" month[11] = "Nov" month[12] = "Dec" html_doctype = "" html_lang = "en-US" html_ext = ".html" data_subdir = "data/" data_ext = ".dat" Upper_AZ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" Lower_AZ = "abcdefghijklmnopqrstuvwxyz" title = "The Macky Family in New Zealand" TITLE = toupper(title) name[""] = "Tables" level_of["header.html"] = 1 ###################################################################### print "Generating \"" title "\" genealogy files..." print " Loading data..." load_data("", 0) print " Writing HTML..." write_page(start) print master_count " descendents" print "Done" } # recursively load genealogy data files ### function load_data(code, level, k, tk) { level_of[code] = level undotted[code] = undottify(code) # public files are top-level and named table_CODE; private files # are just named CODE and live in ### if (!level) filename_of[code] = "tables" html_ext else if (level > public_levels) filename_of[code] = undotted[code] html_ext else filename_of[code] = "table_" undotted[code] html_ext ns = 0 data_file = data_subdir undotted[code] data_ext for (n = 0; getline < data_file > 0; n++) { if ($1 == "=") marriage_date[code, ++ns] = rest(2) else if ($1 == "-") { if (!(divorce_date[code, ns] = rest(2))) divorce_date[code, ns] = "?" } else if ($1 == "s") { if (!(separation_date[code, ns] = rest(2))) separation_date[code, ns] = "?" } else if (ns) { if ($1 == "n") spouse_name[code, ns] = rest(2) else if ($1 == "b") spouse_birth[code, ns] = $2 else if ($1 == "d") spouse_death[code, ns] = $2 else if ($1 == "h") spouse_html[code, ns] = rest(2) else if ($1 == "w") spouse_website[code, ns] = $2 else if ($1 == "#") { spouse_kids[code, ns] = $2 total_kids[code] += $2 } else fatal_error("UNKNOWN SPOUSE LINE FOR CODE " code ": " $0) } else { if ($1 == "n") name[code] = rest(2) else if ($1 == "a") arrival[code] = rest(2) else if ($1 == "b") birth[code] = $2 else if ($1 == "d") death[code] = $2 else if ($1 == "h") { nh = ++n_html[code] extra_html[code, nh] = rest(2) } else if ($1 == "w") website[code] = $2 else if ($1 == "p") { pn = ++photos[code] photo[code "." pn] = $2 caption[code "." pn] = rest(3) } else if ($1 == "P") photo_link[code] = $2 else if ($1 == "#") { kids[code] = $2 total_kids[code] += $2 } else fatal_error("UNKNOWN MAIN LINE FOR CODE " code ": " $0) } } close(file) if (!n) fatal_error("MISSING " data_file) n_spouse[code] = ns # load children ### tk = total_kids[code] for (k = 1; k <= tk; k++) { kid = child_code(code, k) child_of[code, k] = kid load_data(kid, level + 1) } # set up children's parent/previous/next linkages ### last_kid = "" for (k = 1; k <= tk; k++) { kid = child_of[code, k] parent_of[kid] = code if (total_kids[kid]) { if (k > 1) { prev_of[kid] = last_kid next_of[last_kid] = kid } last_kid = kid } } } # write master tree of direct descendents ### function write_master(code, k, tk) { master_count++ if (debug) print "write_master[" code "]" if (website[code]) nm = "" name[code] "" else nm = name[code] tk = total_kids[code] if (tk) { print "
  • " code " " nm > path print "
      " > path for (k = 1; k <= tk; k++) write_master(child_of[code, k]) print "
    " > path } else print "
  • " code " " nm > path } # write page for a person with children ### function write_page(code, k) { if (debug > 1) print "write_page[" code "]" if (!name[code]) fatal_error("UNKNOWN CODE " code) if (!total_kids[code]) fatal_error(code " HAS NO CHILDREN") path = path_to("", code) filename_of[code] print html_doctype > path print "" > path print "" > path print "" > path if (code) print " Table " code " · " name[code] " · " title "" > path else print " Tables of Descent · " title "" > path print " " > path print " " > path print "" > path print "" > path print "
    " > path write_nav(code) print "
    " > path if (code) write_up(code) else { copy_file(table_boiler1) print "
    " > path } write_who(code) if (!code) { print "

    " > path copy_file(table_boiler2) } print "
    " > path if (!code) { print "
      " > path print "
    • " root_name > path print "
        " > path write_master("") print "
      " > path print "
    " > path } # add photos if (photos[code] > 0) { print "
    " > path print "

    " > path print "" > path print "" > path for (pn = 1; pn <= photos[code]; pn++) { if ((pn > 1) && !((pn - 1) % 3)) print "\n" > path img = photo[code "." pn] image_size(img) cap = caption[code "." pn] thumb = img gsub(/\./, "_thumb.", thumb) image_size(thumb) ipath = img tpath = thumb if (level_of[code] > public_levels) { ipath = "../" ipath tpath = "../" tpath } print "" > path } print "" > path print "
    \""
    " cap "
    " > path print "
    " > path } else if (photo_link[code]) print "

    PHOTOS

    " > path # add extra html for (nh = 1; nh <= n_html[code]; nh++) while (getline path for (sn = 1; sn <= n_spouse[code]; sn++) { h = spouse_html[code, sn] if (h) while (getline path } print "" > path print "" > path close(path) # each child which in turn has children gets its own page for (k = 1; k <= total_kids[code]; k++) { kid = child_of[code, k] if (total_kids[kid]) write_page(kid) } } # write page navigation (nav bar and up, left, right) ### function write_nav(code) { top_dir = path_to(code, "") if (!code) { prev_is = "letter69" next_is = "journal" } else { left_code = prev_of[code] if (left_code) { prev_is = path_to(code, left_code) filename_of[left_code] prev_name = name[left_code] " [" left_code "]" } else { prev_is = "" prev_name = "" } right_code = next_of[code] if (right_code) { next_is = path_to(code, right_code) filename_of[right_code] next_name = name[right_code] " [" right_code "]" } else { next_is = "" next_name = "" } } print " path print " $GLOBALS[\"prev\"] = \"" prev_is "\";" > path print " $GLOBALS[\"prev_name\"] = \"" prev_name "\";" > path print " $GLOBALS[\"next\"] = \"" next_is "\";" > path print " $GLOBALS[\"next_name\"] = \"" next_name "\";" > path header_path = path_to(code, "header.html") print " include(\"" header_path "header.html\");" > path print "?>" > path } # write "up" pointer ### function write_up(code) { if (level_of[code] == 1) { parent_code = "" up = "tables" html_ext } else { parent_code = parent_of[code] up = filename_of[parent_code] } top_dir = path_to(code, "") up_dir = path_to(code, parent_code) if (!parent_code) up_name = "Tables" else up_name = name[parent_code] " [" parent_code "]" print "Up
    " > path } # write special root entry for JAMES MACKY ### function write_root(path) { print "" > path print "" > path print "" > path print "" > path print "
    " root_name "
    " > path print "=
    " > path print root_spouse "

    " > path } # write main body of person's page: their data & children ### function write_who(code, tk) { if (!total_kids[code]) return level = level_of[code] if (code) print "
    " code "
    " > path # else # write_root(path) print "
    " > path if (website[code]) print "" name[code] "" > path else print name[code] > path print "
    " > path write_bd(code) arr = arrival[code] if (arr) print "arr. " arr "
    " > path # if more than max_kids_row children, they go on multiple rows ### tk = total_kids[code] n_rows = int((tk - 1) / max_kids_row) + 1 per_row = round(tk / n_rows) max_row_cols = 0 nsp = n_spouse[code] if (!nsp) nsp = 1 cols_spouse = round(per_row / nsp) # figure how many columns are needed for each spouse ### for (si = 1; si <= nsp; si++) { sc = spouse_kids[code, si] / n_rows if ((sc > 0) && (sc < 1)) sc = 1 # if (n_rows > 1) # if (sc > cols_spouse) # sc = cols_spouse # if (!sc) # sc = 1 # spouse_cols[si] = sc spouse_cols[si] = round(sc) } n_cols = n_spouse[code] if (total_kids[code] > n_cols) n_cols = total_kids[code] print "" > path # output marrage dates, names, birth & death dates for all spouses ### print "" > path if (n_spouse[code]) { for (si = 1; si <= n_spouse[code]; si++) { if (si > 1) print "" > path } } else print "" > path print "" > path # output the children (possibly on several rows) ### for (ri = 1; ri <= n_rows; ri++) { print "" > path row_count = per_row gray = (ri % 2) for (si = 1; si <= nsp; si++) { ncols = spouse_cols[si] ki = (si == 1) ? 1 : spouse_kids[code, si - 1] + 1 if (ri > 1) ki += (ri - 1) * ncols for (ci = 1; ci <= ncols; ci++) { sk = spouse_kids[code, si] if (!sk) { sk = kids[code] if (!sk) { print "" > path ki++ } } print "" > path } print "
    " > path ncols = spouse_cols[si] print "" > path # if multiple spouses, show spouse# in paren after = ### if (n_spouse[code] > 1) print "= (" si ") " marriage_dates(code, si) "
    " > path else print "= " marriage_dates(code, si) "
    " > path if (spouse_name[code, si]) { if (spouse_website[code, si]) print "" spouse_name[code, si] "" > path else print "" spouse_name[code, si] "" > path print "
    " > path } write_spouse_bd(code, si) # always draw
    so valign-bottom works; but if no kids, # hr's in bg-color so it's effectively invisible ### hr = ((si == 1) && kids[code]) || spouse_kids[code, si] ? hr_color : body_color print "
    " > path print "

    " > path continue } } if (ci > sk) break kid_code = child_of[code, ki] if (!kid_code || !name[kid_code]) { print "" > path continue } # color alternating cells (checkerboard) ### if (gray) print "" > path else print "" > path gray = !gray if (total_kids[kid_code]) { down_dir = path_to(code, kid_code) print "" kid_code "
    " > path } else print "" kid_code "
    " > path print name[kid_code] "
    " > path write_bd(kid_code) ns = n_spouse[kid_code] if (ns == 1) { print "= " marriage_dates(kid_code, 1) "
    " > path print spouse_name[kid_code, 1] "
    " > path if (!spouse_kids[kid_code, 1]) write_spouse_bd(kid_code, 1) } else if (ns > 1) { print " " > path for (sp = 1; sp <= ns; sp++) { print " " > path if (!spouse_kids[kid_code, sp]) write_spouse_bd(kid_code, sp) } print "
    (" sp ") " marriage_dates(kid_code, sp) "
    " > path print spouse_name[kid_code, sp] "
    " > path } print "
    " > path } # write birth & death line ### function write_bd(code) { return write_bd0(birth[code], death[code]) } function write_spouse_bd(code, si) { return write_bd0(spouse_birth[code, si], spouse_death[code, si]) } function write_bd0(b, d) { if (b && d) print "b. " date(b) " d. " date(d) "
    " > path else if (b) print "b. " date(b) "
    " > path else if (d) print "d. " date(d) "
    " > path } # return date in unambiguous DD-MMM-YYYY format ### function date(d) { dmy = d n = split(d, part, "/") if (n == 2) # MM/YYYY -> Jan YYYY dmy = month[part[1]] " " part[2] if (n == 3) # MM/DD/YYYY -> 3 Jan YYYY dmy = part[2] " " month[part[1]] " " part[3] return dmy } # return marriage/divorce/separation dates ### function marriage_dates(code, si) { when = marriage_date[code, si] if (when) m = date(when) else m = "" if (when = divorce_date[code, si]) { if (m) m = m " " m = m "div." if (when != "?") m = m " " date(when) } else if (when = separation_date[code, si]) { if (m) m = m " " m = m "sep." if (when != "?") m = m " " date(when) } return m } # return "rest of line" after first word ### function rest(n) { s = $n for (i = n + 1; i <= NF; i++) s = s " " $i return s } # return ith child of code (e.g., A.1 child#2 is A.1.b) ### function child_code(code, i) { if (!code) return substr(Upper_AZ, i, 1) # start w/A B C... level = level_of[code] if (level % 2 == 1) return code "." i # add 1... if (level % 4 == 0) return code "." substr(Upper_AZ, i, 1) # add A else return code "." substr(Lower_AZ, i, 1) # add a } # replace the last part of a code ### function code_suffix(parts, n, new_last) { if (n == 1) return new_last code = parts[1] for (i = 2; i < n; i++) code = code "." parts[i] return code "." new_last } # remove all the dots from a code (e.g. B.1.d.1 -> B1d1) ### function undottify(code) { if (!code) return "0" n = split(code, parts, /\./) short = parts[1] for (i = 2; i <= n; i++) short = short parts[i] return short } # return path from one code to another. the top levels # are in the public top-level directory; lower levels in a password- # protected private subdirectory ### function path_to(from, to) { if ((level_of[from] <= public_levels) && (level_of[to] > public_levels)) return private_subdir if ((level_of[from] > public_levels) && (level_of[to] <= public_levels)) return "../" return "" } # copy file contents to path ### function copy_file(file) { while (getline < file) print $0 > path close(file) } # round up a positive floating-point number ### function round(i) { return int(i + 0.5) } # emit fatal error message and quit ### function fatal_error(msg) { print "!!! " msg " !!!" exit(1) } # get image dimensions function image_size(image, cmd, got, w, h) { if (image_width[image] > 0) return # already seen it if (image ~ /\.gif/) { cmd = "file " image if (debug > 3) print " cmd=\"" cmd "\"" got = cmd | getline close(cmd) if (got <= 0) { print "***** ERROR: can't size gif " image; exit 223; } w = $(NF-2) h = $(NF) } else if (image ~ /\.jpg/) { cmd = "rdjpgcom -verbose " image " 2>&1 | grep 'image is'" if (debug > 3) print " cmd=\"" cmd "\"" got = cmd | getline close(cmd) if (!NF || (got <= 0) || ($4 == "")) { print "***** ERROR: can't size jpg " image; exit 223 } w = $4; h = $6 } else { print "***** ERROR: unknown image type " image; exit 234 } image_width[image] = w + 0 if (!image_width[image]) { print "***** ERROR: zero width image " image; exit 235; } image_height[image] = h + 0 if (!image_height[image]) { print "***** ERROR: zero height image " image; exit 236; } } # EOF ###############################################################################