# periodic.awk version 5 by Ben Collver # # Generate static gopher content for the periodic table of elements. # # Edit gopher_dir, gopher_host, and gopher_port in function main() # # Requires figlet and uncsv. # # http://www.figlet.org # https://tamentis.com/projects/uncsv/ function center(width, str, len, pad, retval) { len = int(width / 2) - int(length(str) / 2) if (len > 0) { pad = sprintf("%" len "s", " ") } retval = sprintf("%-" width "s", pad str) return retval } function csv_read(f, out, base, cmd, header, i, key) { OLDFS = FS FS = "\t" NR = 0 delete out cmd = "uncsv -d \"\t\" \"" f "\"" while ((cmd | getline) > 0) { base = NR NR++ if (NR == 1) { for (i = 1; i <= NF; i++) { header[i] = $i } } else { for (i = 1; i <= NF; i++) { if (!i in header) { print "Error: invalid header index: " i exit 1 } key = base "_" header[i] out[key] = $i } key = base "_length" out[key] = NF } } out["length"] = base close(cmd) FS = OLDFS return } function format_dir(str, retval) { retval = tolower(str) gsub(/ /, "-", retval) return retval } function format_field(f, element, fieldname, key, retval) { key = element "_" fieldname retval = sprintf("%-23s %s", fieldname ":", elements[key]) return retval } # heapsort # # Unstable, and unlike merge and quicksort it relies on random-access # so has poorer cache performance. # # Advantage over quicksort is that its worst-case same as avg: O(n log n) # # This presentation based on http://dada.perl.it/shootout/heapsort.lua5.html # # From: # # Requires 1-based numerically indexed arrays. function hsort(A, n, c, p, t, i) { if (!n) { n = 1 while (n in A) n++ n-- } i = int(n/2) + 1 while (1) { if (i > 1) { i-- t = A[i] } else { t = A[n] A[n] = A[1] n-- if (n == 1) { A[1] = t return } } for (p = i; (c = 2*p) <= n; p = c) { if ((c < n) && (A[c] < A[c+1])) c++ if (t < A[c]) A[p] = A[c] else break } A[p] = t } } function make_source_lookup( i, key, len, source_id) { delete source_lookup len = sources["length"] for (i = 1; i <= len; i++) { key = i "_Source" source_id = sources[key] source_lookup[source_id] = i } return } function make_table( col, cols, i, key, len, row, rows) { delete table_col delete table_row delete table len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Table Column" col = elements[key] if (col > cols) { cols = col } key = i "_Table Row" row = elements[key] if (row > rows) { rows = row } key = row "_" col table[key] = i table_col[i] = col table_row[i] = row } table["cols"] = cols table["rows"] = rows return } function mkdir(dir) { system("mkdir -p \"" dir "\"") return } function print_about( base, dir, f, url) { dir = outdir "/about" mkdir(dir) f = dir "/index.gph" print "# About Gopher Periodic Table of the Elements\n" >f print "I wrote this AWK script as a fun and easy exercise." >>f print "Generating a simple gopher interface from CSV data.\n" >>f print "# See also\n" >>f print "[h|A Visual Exploration (PDF)|URL:https://archive.org" \ "/details/the-elements-by-theodore-gray-and-nick-mann" \ "|tilde.pink|70]" >>f print "[h|Periodic Table (DOS)|URL:https://archive.org" \ "/details/periodic-table-for-dos|tilde.pink|70]" >>f print "[h|ATOMS (DOS)|URL:https://archive.org" \ "/details/atoms-periodic-table-for-dos|tilde.pink|70]" >>f print "[0|ASCII Periodic Table|" \ "/users/amr66/b_scientific/periodic_table.txt" \ "|gopher.srcf.net|70]" >>f base = "https://www.meta-synthesis.com/webbook/35_pt/pt_database.php" url = base "?PT_id=748" print "[h|Hyde's Periodic Relationships of the Elements (web)|" \ "URL:" url "|tilde.club|70]" >>f url = base "?PT_id=167" print "[h|Spiral Format Periodic Table (web)|" \ "URL:" url "|tilde.club|70]" >>f close(f) return } function print_element_gph(element, cmd, count, dir, f, i, key, \ label, mass, name, path, source_list, source, str, symbol) { dir = outdir "/element/" element mkdir(dir) f = dir "/index.gph" key = element "_Atomic Mass" mass = elements[key] key = element "_Name" name = elements[key] key = element "_Symbol" symbol = elements[key] label = sprintf("%d %s %s (plaintext)", element, symbol, name) path = gopher_dir "/element/" element "/index.txt" printf "[0|%s|%s|%s|%s]\n\n", label, path, gopher_host, gopher_port >f print_table(f) printf "\n" >>f # navigation str = "Left" if (table_col[element] > 1) { for (i = table_col[element] - 1; i >= 1; i--) { key = table_row[element] "_" i if (length(table[key]) > 0) { path = gopher_dir "/element/" table[key] "/" str = sprintf("[1|Left|%s|%s|%s]", path, \ gopher_host, gopher_port) break } } } print str >>f str = "Up" if (table_row[element] > 1) { for (i = table_row[element] - 1; i >= 1; i--) { key = i "_" table_col[element] if (length(table[key]) > 0) { path = gopher_dir "/element/" table[key] "/" str = sprintf("[1|Up|%s|%s|%s]", path, gopher_host, \ gopher_port) break } } } print str >>f str = "Down" if (table_row[element] < table["rows"]) { for (i = table_row[element] + 1; i <= table["rows"]; i++) { key = i "_" table_col[element] if (length(table[key]) > 0) { path = gopher_dir "/element/" table[key] "/" str = sprintf("[1|Down|%s|%s|%s]", path, gopher_host, \ gopher_port) break } } } print str >>f str = "Right" if (table_col[element] < table["cols"]) { for (i = table_col[element] + 1; i <= table["cols"]; i++) { key = table_row[element] "_" i if (length(table[key]) > 0) { path = gopher_dir "/element/" table[key] "/" str = sprintf("[1|Right|%s|%s|%s]", path, \ gopher_host, gopher_port) break } } } print str >>f str = "List All" path = gopher_dir "/list/atomic/" printf "[1|%s|%s|%s|%s]\n\n", str, path, gopher_host, gopher_port >>f # element detail cmd = "figlet " symbol printf "+--------------------+\n" >>f NR = 0 while ((cmd | getline) > 0) { NR++ if (NR == 1) { str = sprintf("%3d", element) substr(center(20, $0), 4) } else { str = center(20, $0) } printf "|%s|\n", str >>f } close(cmd) str = center(20, name) printf "|%s|\n", str >>f str = center(20, mass) printf "|%s|\n", str >>f printf "+--------------------+\n\n" >>f str = sprintf("%-23s %s", "Wiki Article:", name) print_wiki_item(f, str, name) if (element == 55) { printf "[0|%-23s Songs of Cesium|/~freet/collected_files/usenet_funnys/Songs of Cesium.txt|aussies.space|70]\n", "Item:" >>f } print format_field(f, element, "Electron Configuration") >>f print format_field(f, element, "Electronegativity") >>f print format_field(f, element, "Atomic Radius") >>f print format_field(f, element, "Ionization Energy") >>f print format_field(f, element, "Electron Affinity") >>f print format_field(f, element, "Oxidation States") >>f print format_field(f, element, "Standard State") >>f print format_field(f, element, "Melting Point") >>f print format_field(f, element, "Boiling Point") >>f print format_field(f, element, "Density") >>f str = format_field(f, element, "Family") key = element "_Family" path = gopher_dir "/family/" format_dir(elements[key]) "/" printf "[1|%s|%s|%s|%s]\n", str, path, gopher_host, gopher_port >>f print format_field(f, element, "Year Discovered") >>f print format_field(f, element, "Number of Neutrons") >>f print format_field(f, element, "Number of Protons") >>f print format_field(f, element, "Number of Electrons") >>f print format_field(f, element, "Number of Valence") >>f print format_field(f, element, "Valency") >>f print format_field(f, element, "Group") >>f print format_field(f, element, "Period") >>f str = format_field(f, element, "Shell") key = element "_Shell" path = gopher_dir "/shell/" elements[key] "/" printf "[1|%s|%s|%s|%s]\n", str, path, gopher_host, gopher_port >>f print format_field(f, element, "Specific Heat") >>f print format_field(f, element, "Radioactive") >>f print format_field(f, element, "Occurrence") >>f key = element "_Source" source_list = elements[key] count = split(source_list, source, / /) for (i = 1; i <= count; i++) { key = source_lookup[source[i]] "_Name" name = sources[key] str = sprintf("%-23s %s", "Source:", name) path = gopher_dir "/source/" source[i] "/" printf "[1|%s|%s|%s|%s]\n", str, path, gopher_host, gopher_port >>f } close(f) return } function print_element_txt(element, cmd, f, key, label, map, mass, \ name, path, str, symbol, txt) { f = outdir "/element/" element "/index.txt" key = element "_Atomic Mass" mass = elements[key] key = element "_Name" name = elements[key] key = element "_Symbol" symbol = elements[key] printf "%d %s %s\n\n", element, symcol, name >f print_table(f) printf "\n" >>f cmd = "figlet " symbol printf "+--------------------+\n" >>f NR = 0 while ((cmd | getline) > 0) { NR++ if (NR == 1) { str = sprintf("%3d", element) substr(center(20, $0), 4) } else { str = center(20, $0) } printf "|%s|\n", str >>f } close(cmd) str = center(20, name) printf "|%s|\n", str >>f str = center(20, mass) printf "|%s|\n", str >>f printf "+--------------------+\n\n" >>f print format_field(f, element, "Electron Configuration") >>f print format_field(f, element, "Electronegativity") >>f print format_field(f, element, "Atomic Radius") >>f print format_field(f, element, "Ionization Energy") >>f print format_field(f, element, "Electron Affinity") >>f print format_field(f, element, "Oxidation States") >>f print format_field(f, element, "Standard State") >>f print format_field(f, element, "Melting Point") >>f print format_field(f, element, "Boiling Point") >>f print format_field(f, element, "Density") >>f print format_field(f, element, "Family") >>f print format_field(f, element, "Year Discovered") >>f print format_field(f, element, "Number of Neutrons") >>f print format_field(f, element, "Number of Protons") >>f print format_field(f, element, "Number of Electrons") >>f print format_field(f, element, "Number of Valence") >>f print format_field(f, element, "Valency") >>f print format_field(f, element, "Group") >>f print format_field(f, element, "Period") >>f print format_field(f, element, "Shell") >>f print format_field(f, element, "Specific Heat") >>f print format_field(f, element, "Radioactive") >>f print format_field(f, element, "Occurrence") >>f key = element "_Source" source_list = elements[key] count = split(source_list, source, / /) for (i = 1; i <= count; i++) { key = source_lookup[source[i]] "_Name" name = sources[key] printf "%-23s %s\n", "Source:", name >>f } close(f) return } function print_elements( i, len) { len = elements["length"] for (i = 1; i <= len; i++) { select_clear() select_atom(i) print_element_gph(i) print_element_txt(i) } return } function print_families( dir, family_id, fdir, i, key, len, name, \ outfile) { dir = outdir "/family" mkdir(dir) outfile = dir "/index.gph" printf "# Families\n\n" >outfile len = families["length"] for (i = 1; i <= len; i++) { key = i "_Family" family_id = families[key] key = i "_Name" name = families[key] fdir = format_dir(family_id) printf "[1|%s|%s/family/%s/|%s|%s]\n", name, gopher_dir, fdir, \ gopher_host, gopher_port >>outfile select_clear() select_family(family_id) print_family_gph(i, family_id, name, fdir) print_family_txt(i, family_id, name, fdir) } close(outfile) return } function print_family_gph(family, family_id, family_name, fdir, \ descr, dir, i, f, key, label, len, name, path, symbol) { key = family "_Description" descr = families[key] dir = outdir "/family/" fdir mkdir(dir) f = dir "/index.gph" label = sprintf("Family: %s", family_name) path = sprintf("%s/family/%s/index.txt", gopher_dir, fdir) printf "[0|%s|%s|%s|%s]\n\n", label, path, gopher_host, gopher_port >f print_table(f) printf "\n" >>f print_wrap(descr, 70, f) printf "\n" >>f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Family" if (elements[key] == family_id) { key = i "_Name" name = elements[key] key = i "_Symbol" symbol = elements[key] label = sprintf("%3d %-2s %s", i, symbol, name) path = sprintf("%s/element/%d/", gopher_dir, i) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f } } label = "Wiki Article: " family_name path = family_id gsub(/ /, "_", path) printf "\n" >>f print_wiki_item(f, label, path) printf "\n" >>f printf "[1|All Families|%s/family/|%s|%s]\n", gopher_dir, \ gopher_host, gopher_port >>f close(f) return } function print_family_txt(family, family_id, name, fdir, descr, i, \ f, key, len, symbol) { key = family "_Description" descr = families[key] f = outdir "/family/" fdir "/index.txt" printf "Family: %s\n\n", name >f print_table(f) printf "\n" >>f print_wrap(descr, 70, f) printf "\n" >>f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Family" if (elements[key] == family_id) { key = i "_Name" name = elements[key] key = i "_Symbol" symbol = elements[key] printf "%3d %-2s %s\n", i, symbol, name >>f } } close(f) return } function print_list_atomic( dir, f, i, key, label, name, path, symbol) { dir = outdir "/list/atomic" mkdir(dir) f = dir "/index.gph" printf "# List Elements By Atomic Number\n\n" >f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Name" name = elements[key] key = i "_Symbol" symbol = elements[key] label = sprintf("%3d %-2s %s", i, symbol, name) path = sprintf("%s/element/%d/", gopher_dir, i) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f } printf "\n" >>f printf "List Elements By Atomic Number\n" >>f label = "List Elements By Name" path = sprintf("%s/list/name/", gopher_dir) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f label = "List Elements By Symbol" path = sprintf("%s/list/symbol/", gopher_dir) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f close(f) return } function print_list_name( dir, element, f, i, key, label, len, \ map, name, order, path, symbol) { delete map delete order dir = outdir "/list/name" mkdir(dir) f = dir "/index.gph" printf "# List Elements By Name\n\n" >f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Name" name = elements[key] map[name] = i order[i] = name } hsort(order, len) for (i = 1; i <= len; i++) { name = order[i] element = map[name] key = element "_Symbol" symbol = elements[key] label = sprintf("%3d %-2s %s", element, symbol, name) path = sprintf("%s/element/%d/", gopher_dir, element) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f } printf "\n" >>f label = "List Elements By Atomic Number" path = sprintf("%s/list/atomic/", gopher_dir) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f printf "List Elements By Name\n" >>f label = "List Elements By Symbol" path = sprintf("%s/list/symbol/", gopher_dir) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f return } function print_list_symbol( dir, element, f, i, key, label, len, \ map, name, order, path, symbol) { delete map delete order dir = outdir "/list/symbol" mkdir(dir) f = dir "/index.gph" printf "# List Elements By Symbol\n\n" >f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Symbol" symbol = elements[key] map[symbol] = i order[i] = symbol } hsort(order, len) for (i = 1; i <= len; i++) { symbol = order[i] element = map[symbol] key = element "_Name" name = elements[key] label = sprintf("%3d %-2s %s", element, symbol, name) path = sprintf("%s/element/%d/", gopher_dir, element) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f } printf "\n" >>f label = "List Elements By Atomic Number" path = sprintf("%s/list/atomic/", gopher_dir) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f label = "List Elements By Name" path = sprintf("%s/list/name/", gopher_dir) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f printf "List Elements By Symbol\n" >>f return } function print_lists() { print_list_atomic() print_list_name() print_list_symbol() return } function print_shells( dir, i, key, len, name, outfile, shell_id) { dir = outdir "/shell" mkdir(dir) outfile = dir "/index.gph" printf "# Shells\n\n" >outfile len = shells["length"] for (i = 1; i <= len; i++) { key = i "_Shell" shell_id = shells[key] key = i "_Name" name = shells[key] printf "[1|%s|%s/shell/%s/|%s|%s]\n", name, gopher_dir, \ shell_id, gopher_host, gopher_port >>outfile select_clear() select_shell(shell_id) print_shell_gph(i, shell_id, name) print_shell_txt(i, shell_id, name) } close(outfile) return } function print_shell_gph(shell, shell_id, name, descr, dir, i, f, \ key, label, len, path, symbol) { key = shell "_Description" descr = shells[key] dir = outdir "/shell/" shell_id mkdir(dir) f = dir "/index.gph" label = sprintf("Shell: %s", name) path = sprintf("%s/shell/%s/index.txt", gopher_dir, shell_id) printf "[0|%s|%s|%s|%s]\n\n", label, path, gopher_host, gopher_port >f print_table(f) printf "\n" >>f print_wrap(descr, 70, f) printf "\n" >>f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Shell" if (elements[key] == shell_id) { key = i "_Name" name = elements[key] key = i "_Symbol" symbol = elements[key] label = sprintf("%3d %-2s %s", i, symbol, name) path = sprintf("%s/element/%d/", gopher_dir, i) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f } } printf "\n" >>f printf "[1|All Shells|%s/shell/|%s|%s]\n", gopher_dir, \ gopher_host, gopher_port >>f close(f) return } function print_shell_txt(shell, shell_id, name, descr, i, \ f, key, len, symbol) { key = shell "_Description" descr = shells[key] f = outdir "/shell/" shell_id "/index.txt" printf "Shell: %s\n\n", name >f print_table(f) printf "\n" >>f print_wrap(descr, 70, f) printf "\n" >>f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Shell" if (elements[key] == shell_id) { key = i "_Name" name = elements[key] key = i "_Symbol" symbol = elements[key] printf "%3d %-2s %s\n", i, symbol, name >>f } } close(f) return } function print_sources( dir, i, key, len, name, outfile, source_id) { dir = outdir "/source" mkdir(dir) outfile = dir "/index.gph" printf "# Sources\n\n" >outfile len = sources["length"] for (i = 1; i <= len; i++) { key = i "_Source" source_id = sources[key] key = i "_Name" name = sources[key] printf "[1|%s|%s/source/%s/|%s|%s]\n", name, gopher_dir, \ source_id, gopher_host, gopher_port >>outfile select_clear() select_source(source_id) print_source_gph(i, source_id, name) print_source_txt(i, source_id, name) } close(outfile) return } function print_source_gph(source, source_id, name, dir, i, f, key, \ label, len, path, symbol) { dir = outdir "/source/" source_id mkdir(dir) f = dir "/index.gph" label = sprintf("Source: %s", name) path = sprintf("%s/source/%s/index.txt", gopher_dir, source_id) printf "[0|%s|%s|%s|%s]\n\n", label, path, gopher_host, gopher_port >f print_table(f) printf "\n" >>f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Source" if (elements[key] == source_id) { key = i "_Name" name = elements[key] key = i "_Symbol" symbol = elements[key] label = sprintf("%3d %-2s %s", i, symbol, name) path = sprintf("%s/element/%d/", gopher_dir, i) printf "[1|%s|%s|%s|%s]\n", label, path, gopher_host, gopher_port >>f } } printf "\n" >>f printf "[1|All Sources|%s/source/|%s|%s]\n", gopher_dir, \ gopher_host, gopher_port >>f close(f) return } function print_source_txt(source, source_id, name, i, \ f, key, len, symbol) { f = outdir "/source/" source_id "/index.txt" printf "Source: %s\n\n", name >f print_table(f) printf "\n" >>f len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Source" if (elements[key] == source_id) { key = i "_Name" name = elements[key] key = i "_Symbol" symbol = elements[key] printf "%3d %-2s %s\n", i, symbol, name >>f } } close(f) return } function print_table(f, col, cols, element, key, row, rows, sel, \ symbol) { cols = table["cols"] rows = table["rows"] for (row = 1; row <= rows; row++) { for (col = 1; col <= cols; col++) { key = row "_" col element = table[key] sel = selection[key] key = element "_Symbol" symbol = elements[key] if (selected > 1) { if (!sel) { gsub(/./, ".", symbol) } printf " %-2s", symbol >>f } else { if (sel) { printf "[%-2s]", symbol >>f } else { key = row "_" (col - 1) sel = selection[key] if (sel) { printf "%-2s", symbol >>f } else { printf " %-2s", symbol >>f } } } } printf "\n" >>f } return } function print_top( f, uri) { mkdir(outdir) f = outdir "/index.gph" select_clear() printf " Periodic Table Of The Elements\n\n" >f print_table(f) printf "\n" >>f printf "[1|List All|%s/list/atomic/|%s|%s]\n", gopher_dir, gopher_host, gopher_port >>f printf "[1|About|%s/about/|%s|%s]\n", gopher_dir, gopher_host, \ gopher_port >>f printf "[1|Source Code|/~bencollver/fossil/periodic/|tilde.pink|70]\n" >>f close(f) return } function print_wiki_item(f, label, id) { printf "[0|%s|/%s|gopherpedia.com|70]\n", label, id >>f } function print_wrap(str, wraplen, outfile, after, before, buf, chunk) { buf = str while (length(buf) > wraplen) { chunk = substr(buf, 0, wraplen) if (match(chunk, / [^ ]*$/)) { before = substr(buf, 0, RSTART) after = substr(buf, RSTART + 1) print before >>outfile buf = after } else { break } } print buf >>outfile return } function select_atom(element, col, key, row) { col = table_col[element] row = table_row[element] key = row "_" col if (selection[key] != 1) { selected++ } selection[key] = 1 return } function select_clear() { delete selection selected = 0 return } function select_family(family_id, i, key, len) { len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Family" if (elements[key] == family_id) { select_atom(i) } } return } function select_shell(shell_id, i, key, len) { len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Shell" if (elements[key] == shell_id) { select_atom(i) } } return } function select_source(source_id, count, i, j, key, len, \ source_list, source) { len = elements["length"] for (i = 1; i <= len; i++) { key = i "_Source" source_list = elements[key] count = split(source_list, source, / /) for (j = 1; j <= count; j++) { if (source[j] == source_id) { select_atom(i) } } } return } function main() { gopher_dir = "/~bencollver/periodic" gopher_host = "server" gopher_port = "port" outdir = "output" csv_read("data/elements.csv", elements) csv_read("data/families.csv", families) csv_read("data/shells.csv", shells) csv_read("data/sources.csv", sources) make_source_lookup() make_table() print_about() print_elements() print_families() print_lists() print_shells() print_sources() print_top() return } BEGIN { main() exit 0 }