Yet another messy commit; this time making a start on getting labels in decent positions.

#!/usr/bin/awk

# TODO:
# cat nfa.dot | dot -Tplain > /tmp/a.mp \
# && mpost -s 'outputformat="svg"' -s 'outputtemplate="%j-%c.%o"' /tmp/a.mp

# Only intended to consume Dot generated from libfsm's .dot output,
# and not graphviz diagrams in general.

function sname(s)
{
	if (s == "start") {
		return "S0";
	}

	return s;
}

# TODO: convert label from graphviz's html to a utf8
# This is either <xyz> or abc (with no quotes)
function slabel(s)
{
	# XXX: placeholder
	if (s == "<>") {
		return "";
	}
	if (s == "\"\"") {
		return "";
	}

# XXX: placeholder
gsub(/ε/, "e", s); # XXX: why not?
gsub(/"/, "", s)
gsub(/</, "", s)
gsub(/>/, "", s)

	return s;
}

BEGIN {

	# TODO: would be simpler to gvpr to rename start to S0
	# TODO: common stuff for multiple graphs
	print "pair S[];"
	print "pair q[];"
	print "path nS[];"
	print "path e;"

	# diameter of node
	print "numeric dS[];"

	# number of self-edges
	print "numeric lS[];"

	print "ahlength := 10bp;"
	print "ahangle  := 45;"

#	print "secondarydef v projectedalong w =";
#	print "	if pair(v) and pair(w):";
#	print "		(v dotprod w) / (w dotprod w) * w";
#	print "	else:";
#	print "		errmessage \"arguments must be pairs\"";
#	print "	fi";
#	print "enddef;";

}

/^graph / {
	printf "beginfig(%s)\n", $2
}

# node S6 0.97222 0.41667 0.30556 0.30556 "" solid circle black lightgrey
# node name x y width height label style shape color fillcolor
/^node / {
	name   = sname($2);
	x      = $3;
	y      = $4;
	width  = $5;
	height = $6;
	label  = slabel($7);
	style  = $8;
	shape  = $9;
	# TODO: label delimited by <>, may contain spaces

	printf "\tl%s := 0;\n", name

	printf "\t%s := (%fin, %fin);\n", name, x, y

# TODO: when plotting in graphviz, assume all nodes have the same width (not zero!). enough for "SXX" perhaps
# that should align them all on the same centre line. and pass fixedsize=true

	# TODO: explain this is diameter
	# TODO: store 8bp as a constant somewhere
# TODO: explain we take diameter from label presence and endstateness.
# ignore graphviz height and width, because it scales with label width and that looks bad for S1 vs S5
	diam = 0.3 + length(label) * 0.05;

	if (shape == "doublecircle") {
		printf "\td%s = %fin + 8bp;\n", name, diam;
	} else {
		printf "\td%s = %fin;\n", name, diam;
	}

	if (shape == "doublecircle") {
# TODO: would rather && together paths to n%s or somesuch
		printf "\tdraw fullcircle scaled (d%s - 8bp) shifted %s withpen pencircle scaled 1bp;\n", name, name
	}

	printf "\tn%s := fullcircle scaled d%s shifted %s;\n", name, name, name

	if ($2 != "start") {
		printf "\tdraw n%s withpen pencircle scaled 1bp;\n", name

		if (label != "") {
			printf "\tlabel(\"%s\", %s);\n", label, name
		}
	}
}

# edge tail head n x1 y1 .. xn yn [label xl yl] style color
# edge S3 S6 10 3.5244 0.20119 3.2727 0.19014 2.7322 0.17358 2.2778 0.20833 1.873 0.23929 1.7755 0.28067 1.375 0.34722 1.3389 0.35322 1.3009 0.35974 1.2635 0.3663 <ε> 2.3264 0.3125 solid black
/^edge / {
	tail = sname($2);
	head = sname($3);
	n    = $4;

	v = split($0, a, " ");
	# TODO: assert n < v - whatever
	# TODO: assert n >= 4 (knot, control, control); mind trailing stuff... label etc

	# TODO: note we assume -Etailclip=false -Eheadclip=false -Edir=none
	# TODO: explain that requirement

	# things i wish the graphviz documentation said about -Tplain:
	# graphviz always outputs cubic b-splines
	# 3i+1 points total. format is { knot, control, control }* knot
	# bezier is ordered tail to head
	# first two points (knot, control) are ontop of each other, in the tail node
	# last two points (control, knot) are ontop of each other, in the head node
	# coordinates are in inches

	printf "\te :=\n";
	for (i = 1; i <= n - 1; i += 3) {
		# knot
		ox = a[(i + 0) * 2 + 3];
		oy = a[(i + 0) * 2 + 4];

		# control
		c1x = a[(i + 1) * 2 + 3];
		c1y = a[(i + 1) * 2 + 4];

		# control
		c2x = a[(i + 2) * 2 + 3];
		c2y = a[(i + 2) * 2 + 4];

		printf "\t(%fin, %fin) .. controls (%fin, %fin) and (%fin, %fin) ..\n", ox, oy, c1x, c1y, c2x, c2y;
	}
	# TODO: knot at end, which i haven't drawn
	# last point:
	{
		# knot
		ox = a[(n + 0) * 2 + 3];
		oy = a[(n + 0) * 2 + 4];
		printf "\t(%fin, %fin);\n", ox, oy;
	}

	printf "\tdraw e withpen pencircle scaled 0.25bp withcolor red;\n";

	# TODO: better explain this
	# TODO: explain reversed for q, else we'd find p again, where head == tail
	printf "pair p; p = e intersectionpoint n%s;\n", head;
	printf "pair q; q = (reverse e) intersectionpoint n%s;\n", tail;

	# XXX: assuming no space in label
label = slabel(a[n * 2 + 5]);
lx    = (a[n * 2 + 6]);
ly    = (a[n * 2 + 7]);

	if (n * 2 == v - 4 - 2) {
		label = "";
	}

	if (head == tail) {
		printf "path pp; pp = %s -- 0.5[p, q];\n", head;
printf "draw p -- q withpen pencircle scaled 0.5bp withcolor green;\n";
printf "draw 0.5[p, q] withpen pencircle scaled 0.8bp withcolor green;\n";
printf "draw %s -- 0.5[p, q] withpen pencircle scaled 0.8bp withcolor green;\n", head;

		# extend loop 1.25 * diameter
		# count number of self-loops for this node, and increment extension
		printf "\tl%s := l%s + 1;", head, head
		print "l := length pp;"
		printf "\tpair m; m := (0.5 + 2 * mlog(l%s + 1) / 256) * d%s * unitvector(direction l of pp) shifted %s;\n", head, head, head
# u3 = u1 projectedalong u2;
		printf "\tdraw %s -- m withpen pencircle scaled 0.5bp withcolor green;\n", head

		printf "\tpath b; b = %s .. q .. m .. p .. %s;\n", tail, head;

		if (label != "") {
			# TODO: top or bottom, depending on green line direction
			printf "\tlabel.top(\"%s\", m);\n", slabel(label)
		}
	} else {
		printf "\tpath b; b = %s .. q .. p .. %s;\n", tail, head;

		# TODO: explain this. we permit a 2% lengthening threshold relative to graphviz's b-spline
		# this affects NFA especially
		print "\tr := arclength b / arclength e;\n";
		print "\tboolean g; g := (r > 1.02) or (r < 0.93);"; # XXX: or (arclength b > 6in);"; - no, wigglyness instead

		print "\tif g:";
		print "\t\tb := e;";
		print "\tfi;";

# if it wouldn't have been a straight line, go via the label point instead:
# XXX: this idea doesn't work, because i don't know which side of the line the label point would be on
#printf "\t\tb := %s .. q .. (%fin, %fin) .. p .. %s;\n", tail, lx, ly, head;

		if (label != "") {
			printf "\tpair bl;\n";
			printf "\tpair ml; ml := .5[%s, %s];\n", tail, head;

# TODO: new idea; draw right angle from midpoint, and use that to find intersection with b
# this way it should always align for multiple edges, even if on a slant

printf "draw (%fin, %fin) withpen pencircle scaled 5bp withcolor red;\n", lx, ly
printf "draw point (length b / 2) of b withpen pencircle scaled 4bp withcolor blue;\n"
#printf "draw %s -- %s withpen pencircle scaled 1bp withcolor green;\n", tail, head
#printf "draw ml withpen pencircle scaled 5bp withcolor green;\n"

			print "\tif g:";
				printf "\t\tbl := (%fin, %fin);", lx, ly;
			print "\telse:";
				printf "\t\tbl := point (length b / 2) of b;\n";
			print "\tfi;";

# TODO: plus label delta, distancing it from point. make a label function
# TODO: maybe an "extend" function, to extend a path in its direction. use dotprod for that?

			# lft | rt | top | bot | ulft | urt | llft | lrt
			# labels ought to be below, if they're below the straight line from head-tail, or above otherwise
			# TODO: round label coordinates to grid? quantize rather
# XXX: if using graphviz's point, label exactly on the spot
print "\tif g:";
# TODO: still want to distance by +/- 4bp or so
			printf "\t\tlabel(\"%s\", bl);\n", label
print "\telse:";
			printf "\tif ypart bl < ypart ml:\n" # TODO: threshold for considering "below"
			printf "\t\tlabel.bot(\"%s\", bl shifted (0, -3bp));\n", label
			printf "\telse:\n"
			printf "\t\tlabel.top(\"%s\", bl shifted (0, +3bp));\n", label
			printf "\tfi;\n"
print "\tfi;";
		}
	}

	printf "drawarrow b cutbefore q cutafter p withpen pencircle scaled 1bp;\n";
}

/^stop$/ {
	# XXX: ought to be per graph; stop is per file
	print "endfig;"
}

END {
	print "end."
}

#!/usr/bin/awk

# TODO:
# cat nfa.dot | dot -Tplain > /tmp/a.mp \
# && mpost -s 'outputformat="svg"' -s 'outputtemplate="%j-%c.%o"' /tmp/a.mp

# Only intended to consume Dot generated from libfsm's .dot output,
# and not graphviz diagrams in general.

# graph 1 5.75 0.93056
# node start 0.15278 0.41667 0.30556 0.5 "" solid none black lightgrey
# node S6 0.97222 0.41667 0.30556 0.30556 "" solid circle black lightgrey
# node S1 5.5417 0.20833 0.41667 0.41667 "" solid doublecircle black lightgrey
# edge S3 S6 10 3.5244 0.20119 3.2727 0.19014 2.7322 0.17358 2.2778 0.20833 1.873 0.23929 1.7755 0.28067 1.375 0.34722 1.3389 0.35322 1.3009 0.35974 1.2635 0.3663 <ε> 2.3264 0.3125 solid black
# edge S3 S2 4 3.835 0.20833 3.9555 0.20833 4.1328 0.20833 4.2831 0.20833 <y> 4.1319 0.3125 solid black
# stop

function sname(s)
{
	if (s == "start") {
		return "S0";
	}

	return s;
}

BEGIN {

	# TODO: would be simpler to gvpr to rename start to S0
	# TODO: common stuff for multiple graphs
	print "pair S[];"
	print "pair q[];"
	print "path nS[];"
	print "path e;"

	# diameter of node
	print "numeric dS[];"

	# number of self-edges
	print "numeric lS[];"

	print "ahlength := 10bp;"
	print "ahangle  := 45;"
}

/^graph / {
	printf "beginfig(%s)\n", $2
}

# node S6 0.97222 0.41667 0.30556 0.30556 "" solid circle black lightgrey
# node name x y width height label style shape color fillcolor
/^node / {
	name   = sname($2);
	x      = $3;
	y      = $4;
	width  = $5;
	height = $6;
	label  = $7;
	style  = $8;
	shape  = $9;
	# TODO: label delimited by <>, may contain spaces

	printf "\tl%s := 0;", name

	printf "\t%s := (%fin, %fin);\n", name, x, y

	# TODO: explain this is diameter
	printf "\td%s = %fin * 0.75;\n", name, (height + width) / 2;

	if (shape == "doublecircle") {
# TODO: would rather && together paths to n%s or somesuch
		printf "\tdraw fullcircle scaled (d%s - 8bp) shifted %s withpen pencircle scaled 1bp;\n", name, name
	}

	printf "\tn%s := fullcircle scaled d%s shifted %s;\n", name, name, name

	if ($2 != "start") {
		printf "\tdraw n%s withpen pencircle scaled 1bp;\n", name
	}
}

# edge tail head n x1 y1 .. xn yn [label xl yl] style color
# edge S3 S6 10 3.5244 0.20119 3.2727 0.19014 2.7322 0.17358 2.2778 0.20833 1.873 0.23929 1.7755 0.28067 1.375 0.34722 1.3389 0.35322 1.3009 0.35974 1.2635 0.3663 <ε> 2.3264 0.3125 solid black
/^edge / {
	tail = sname($2);
	head = sname($3);
	n    = $4;

	v = split($0, a, " ");
	# TODO: assert n < v - whatever
	# TODO: assert n >= 4 (knot, control, control)

	# TODO: note we assume -Etailclip=false -Eheadclip=false -Edir=none
	# TODO: explain that requirement

	# things i wish the graphviz documentation said about -Tplain:
	# graphviz always outputs cubic b-splines
	# 3i+1 points total. format is { knot, control, control }* knot
	# bezier is ordered tail to head
	# first two points (knot, control) are ontop of each other, in the tail node
	# last two points (control, knot) are ontop of each other, in the head node
	# coordinates are in inches

	printf "\te := ";
	for (i = 1; i <= n - 1; i += 3) {
		# knot
		ox = a[(i + 0) * 2 + 3];
		oy = a[(i + 0) * 2 + 4];

		# control
		c1x = a[(i + 1) * 2 + 3];
		c1y = a[(i + 1) * 2 + 4];

		# control
		c2x = a[(i + 2) * 2 + 3];
		c2y = a[(i + 2) * 2 + 4];

		printf "(%fin, %fin) .. controls (%fin, %fin) and (%fin, %fin) .. ", ox, oy, c1x, c1y, c2x, c2y;
	}
	# TODO: knot at end, which i haven't drawn
	# last point:
	{
		# knot
		ox = a[(n + 0) * 2 + 3];
		oy = a[(n + 0) * 2 + 4];
		printf "(%fin, %fin);", ox, oy;
	}

	printf "draw e withpen pencircle scaled 0.25bp withcolor red;\n";

	# TODO: better explain this
	# TODO: explain reversed for q, else we'd find p again, where head == tail
	printf "pair p; p = e intersectionpoint n%s;\n", head;
	printf "pair q; q = (reverse e) intersectionpoint n%s;\n", tail;

	if (head == tail) {
		printf "path pp; pp = %s -- 0.5[p, q];\n", head;
#		printf "draw p -- q withpen pencircle scaled 0.5bp withcolor green;\n";

		# extend loop 1.25 * diameter
		# count number of self-loops for this node, and increment extension
		printf "\tl%s := l%s + 1;", head, head
		print "l := length pp;"
		printf "\tpair m; m := (0.5 + 2 * mlog(l%s + 1) / 256) * d%s * unitvector(direction l of pp) shifted %s;\n", head, head, head
#		printf "\tdraw %s -- m withpen pencircle scaled 0.25bp withcolor green;\n", head

		printf "\tpath b; b = %s .. q .. m .. p .. %s;\n", tail, head;
	} else {
		printf "\tpath b; b = %s .. q .. p .. %s;\n", tail, head;

		# TODO: explain this. we permit a 2% lengthening threshold relative to graphviz's b-spline
		# this affects NFA especially
		print "\tr := arclength b / arclength e;\n";
		print "\tif (r > 1.02) or (r < 0.87):";
		print "\t\tb := e;";
		print "\tfi;";
	}

	printf "drawarrow b cutbefore q cutafter p withpen pencircle scaled 1bp;\n";
}

/^stop$/ {
	# XXX: ought to be per graph; stop is per file
	print "endfig;"
}

END {
	print "end."
}


ahrf.awk: one or more space(s)

ahrf.awk: one or more space(s)

#!/usr/bin/awk -f

BEGIN { FS = "\n"; RS = "" }

{
	# Common shell symbols to HTML
	# Two backslashes for nawk(1) and OpenBSD awk(1)
	gsub(/\&/,"\\&")
	gsub(/</,"\\<")
	gsub(/>/,"\\>")
}

# h1, h2, h3, h4, h5, h6
/^[\t ]*:/ {
	match($0,":+")
	cnt = RLENGTH
	gsub(/^[\t ]*:+[\t ]*|[\t ]*:+[\t ]*$/,"")
	anc = tolower($0)
	gsub(/ +/,"-",anc)
	# length($0) would also work
	if (cnt <= 6 && $0 != "") {
		printf("<h%d id=\"%s\">%s</h%d>\n", cnt, anc, $0, cnt)
	}
	next
}

# Paragraph
/^[A-Za-z0-9_("]+/ {
	printf("
")
	for (p=1; p<NF; p++) {
		if ($p ~ / +$/) {
			gsub(/ +$/,"",$p)
			printf("%s
\n", $p)
		} else {
			printf("%s\n", $p)
		}
	}
	printf("%s
\n", $p)
	next
}

# Code
/^====+/ {
	if (NF > 2 && $NF ~ /====+/) {
		gsub(/^[\t ]*====+[\t ]*\n|\n[\t ]*====+[\t ]*$/,"")
		printf("
")
		for (c=1; c<NF; c++) {
			gsub(/^ +$/,"",$c)
			printf("%s\n", $c)
		}
		printf("%s
\n", $NF)
	}
	next
}

# List
/^[\t ]*\* +/ {
	printf("
\n")
	for (l=1; l<=NF; l++) {
		gsub(/^[\t ]*/,"",$l)
		if ($l ~ /\* +/) {
			match($l,"\\* +")
			str = substr($l,RSTART+RLENGTH)
			printf("\t
%s
\n", str)
		}
	}
	printf("
\n")
	next
}

# End links
/^[\t ]*\[[01]/ {
	printf("
\n")
	for (u=1; u<=NF; u++) {
		gsub(/^[\t ]*/,"",$u)
		if (match($u,"\\[[0-9]+\\]")) {
			# RSTART can be replaced by 1, because we removed useless tabs/spaces
			num = substr($u,RSTART,RLENGTH)
			url = substr($u,RSTART+RLENGTH+1)
			if (length(url) >= 60) {
				printf("\t
%s %.60s…
\n", num, url, url)
			} else {
				printf("\t
%s %s
\n", num, url, url)
			}
		}
	}
	printf("
\n")
	next
}

#!/usr/bin/awk -f

BEGIN { FS = "\n"; RS = "" }

# Common shell symbols to HTML
{
	# Two backslashes for nawk(1) and OpenBSD awk(1)
	gsub(/\&/,"\\&")
	gsub(/</,"\\<")
	gsub(/>/,"\\>")
}

# h1, h2, h3, h4, h5, h6
/^[\t ]*:/ {
	match($0,":+")
	cnt = RLENGTH
	gsub(/^[\t ]*:+[\t ]*|[\t ]*:+[\t ]*$/,"")
	anc = tolower($0)
	gsub(/ /,"-",anc)
	# length($0) would also work
	if (cnt <= 6 && $0 != "") {
		printf("<h%d id=\"%s\">%s</h%d>\n", cnt, anc, $0, cnt)
	}
	next
}

# Paragraph
/^[A-Za-z0-9_("]+/ {
	printf("
")
	for (p=1; p<NF; p++) {
		if ($p ~ / +$/) {
			gsub(/ +$/,"",$p)
			printf("%s
\n", $p)
		} else {
			printf("%s\n", $p)
		}
	}
	printf("%s
\n", $p)
	next
}

# Code
/^====+/ {
	if (NF > 2 && $NF ~ /====+/) {
		gsub(/^[\t ]*====+[\t ]*\n|\n[\t ]*====+[\t ]*$/,"")
		printf("
")
		for (c=1; c<NF; c++) {
			gsub(/^ +$/,"",$c)
			printf("%s\n", $c)
		}
		printf("%s
\n", $NF)
	}
	next
}

# List
/^[\t ]*\* +/ {
	printf("
\n")
	for (l=1; l<=NF; l++) {
		gsub(/^[\t ]*/,"",$l)
		if ($l ~ /\* +/) {
			match($l,"\\* +")
			str = substr($l,RSTART+RLENGTH)
			printf("\t
%s
\n", str)
		}
	}
	printf("
\n")
	next
}

# End links
/^[\t ]*\[[01]/ {
	printf("
\n")
	for (u=1; u<=NF; u++) {
		gsub(/^[\t ]*/,"",$u)
		if (match($u,"\\[[0-9]+\\]")) {
			# RSTART can be replaced by 1, because we removed useless tabs/spaces
			num = substr($u,RSTART,RLENGTH)
			url = substr($u,RSTART+RLENGTH+1)
			if (length(url) >= 60) {
				printf("\t
%s %.60s…
\n", num, url, url)
			} else {
				printf("\t
%s %s
\n", num, url, url)
			}
		}
	}
	printf("
\n")
	next
}


ahrf.awk: Enable comment exported to HTML. This is not final and therefore, it might be removed. Validated (even if the checks are not included yet).

#!/usr/bin/awk -f

BEGIN { FS = "\n"; RS = "" }

# Common shell symbols to HTML
{
	# Two backslashes for nawk(1) and OpenBSD awk(1)
	gsub(/\&/,"\\&")
	gsub(/</,"\\<")
	gsub(/>/,"\\>")
}

# UGLY WIP
/^[\t ]*%+/ {
	gsub(/^[\t ]*%+[\t ]*/,"")
	printf("\n", $0)
	next
}

# h1, h2, h3, h4, h5, h6
/^[\t ]*:/ {
	match($0,":+")
	cnt = RLENGTH
	gsub(/^[\t ]*:+[\t ]*|[\t ]*:+[\t ]*$/,"")
	# length($0) would also work
	if (cnt <= 6 && $0 != "") {
		printf("<h%s>%s</h%s>\n", cnt, $0, cnt)
	}
	next
}

# Paragraph
/^[A-Za-z0-9_("]+/ {
	printf("
")
	for (p=1; p<NF; p++) {
		if ($p ~ / +$/) {
			gsub(/ +$/,"",$p)
			printf("%s
\n", $p)
		} else {
			printf("%s\n", $p)
		}
	}
	printf("%s
\n\n", $p)
	next
}

# Code
/^====+/ {
	if (NF > 2 && $NF ~ /====+/) {
		gsub(/^[\t ]*====+[\t ]*\n|\n[\t ]*====+[\t ]*$/,"")
		printf("
")
		for (c=1; c<NF; c++) {
			gsub(/^ +$/,"",$c)
			printf("%s\n", $c)
		}
		# Solve "space" bug! Less crado (gsub).
		#gsub(/^ +/,"",$NF)
		printf("%s
\n", $NF)
	}
	next
}

# List
/^[\t ]*\* +/ {
	printf("
\n")
	for (l=1; l<=NF; l++) {
		gsub(/^[\t ]*/,"",$l)
		if ($l ~ /\* +/) {
			match($l,"\\* +")
			str = substr($l,RSTART+RLENGTH)
			printf("\t
%s
\n", str)
		}
	}
	printf("
\n")
	next
}

# End links
/^[\t ]*\[[01]/ {
	printf("
\n")
	for (u=1; u<=NF; u++) {
		gsub(/^[\t ]*/,"",$u)
		if (match($u,"\\[[0-9]+\\]")) {
			# RSTART can be replaced by 1, because we removed useless tabs/spaces
			num = substr($u,RSTART,RLENGTH)
			url = substr($u,RSTART+RLENGTH+1)
			if (length(url) >= 60) {
				printf("\t
%s %.60s...
\n", num, url, url)
			} else {
				printf("\t
%s %s
\n", num, url, url)
			}
		}
	}
	printf("
\n")
	next
}

#!/usr/bin/awk -f

BEGIN { FS = "\n"; RS = "" }

# Common shell symbols to HTML
{
	# Two backslashes for nawk(1) and OpenBSD awk(1)
	gsub(/\&/,"\\&")
	gsub(/</,"\\<")
	gsub(/>/,"\\>")
}

# h1, h2, h3, h4, h5, h6
/^[\t ]*:/ {
	match($0,":+")
	cnt = RLENGTH
	gsub(/^[\t ]*:+[\t ]*|[\t ]*:+[\t ]*$/,"")
	# length($0) would also work
	if (cnt <= 6 && $0 != "") {
		printf("<h%s>%s</h%s>\n", cnt, $0, cnt)
	}
	next
}

# Paragraph
/^[A-Za-z0-9_("]+/ {
	printf("
")
	for (p=1; p<NF; p++) {
		if ($p ~ / +$/) {
			gsub(/ +$/,"",$p)
			printf("%s
\n", $p)
		} else {
			printf("%s\n", $p)
		}
	}
	printf("%s
\n\n", $p)
	next
}

# Code
/^====+/ {
	if (NF > 2 && $NF ~ /====+/) {
		gsub(/^[\t ]*====+[\t ]*\n|\n[\t ]*====+[\t ]*$/,"")
		printf("
")
		for (c=1; c<NF; c++) {
			gsub(/^ +$/,"",$c)
			printf("%s\n", $c)
		}
		# Solve "space" bug! Less crado (gsub).
		#gsub(/^ +/,"",$NF)
		printf("%s
\n", $NF)
	}
	next
}

# List
/^[\t ]*\* +/ {
	printf("
\n")
	for (l=1; l<=NF; l++) {
		gsub(/^[\t ]*/,"",$l)
		if ($l ~ /\* +/) {
			match($l,"\\* +")
			str = substr($l,RSTART+RLENGTH)
			printf("\t
%s
\n", str)
		}
	}
	printf("
\n")
	next
}

# End links
/^[\t ]*\[[01]/ {
	printf("
\n")
	for (u=1; u<=NF; u++) {
		gsub(/^[\t ]*/,"",$u)
		if (match($u,"\\[[0-9]+\\]")) {
			# RSTART can be replaced by 1, because we removed useless tabs/spaces
			num = substr($u,RSTART,RLENGTH)
			url = substr($u,RSTART+RLENGTH+1)
			if (length(url) >= 60) {
				printf("\t
%s %.60s...
\n", num, url, url)
			} else {
				printf("\t
%s %s
\n", num, url, url)
			}
		}
	}
	printf("
\n")
	next
}


Start 2016 testing.

Start 2016 testing.

#!/usr/bin/awk -f
# tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back] *.tdb

BEGIN {
  FS="\t"
  pn = 0
    #str = "date +%m-%d-%Y";
    #str | getline date;
    #close str;
}
#$3>date
{
  # add a loop to add line only if either status is (wanted or testing) or ending year is gt or eq to contract year
  # increased to 2016 July 1 2016.
  current_year = 2016
  # end_year is the AU year, or the second half of a range, ie 2014 in 2013-2014
  end_year = 0
  incontract = 0
  # test_year is the contract year, or the back year if there is one.
  test_year = ""
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  #printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1,$2,$3,$4,$7,end_year,test_year
  # Is the AU year >= the contract year or the back year. And then decide what to do with the current year.
  if ($6 == "wanted" || $6 == "testing") {
      incontract = 1;
  } else if ((end_year >= test_year) && ((end_year < current_year) || (end_year == current_year && incl_cur == 1))) {
      incontract = 1;
  }

  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      if ($7 == "") {
        k[pn] = ".."
      } else {
        k[pn] = $7
      }
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "testing"
  s[5] = "notReady"
  s[6] = "ready"
  s[7] = "crawling"
  s[8] = "frozen"
  s[9] = "ingNotReady"
  s[10] = "finished"
  s[11] = "down"
  s[12] = "superseded"
  s[13] = "zapped"
  s[14] = "doNotProcess"
  sn = 15

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "test"
  sc[5] = "notR"
  sc[6] = "read"
  sc[7] = "craw"
  sc[8] = "froz"
  sc[9] = "ingN"
  sc[10] = "fini"
  sc[11] = "down"
  sc[12] = "supe"
  sc[13] = "zapp"
  sc[14] = "doNP"
  scn = 15

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
        printf "\t.."
      } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
  #printf "%s", date
  #printf "\n"
}

#!/usr/bin/awk -f
# tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back] *.tdb

BEGIN {
  FS="\t"
  pn = 0
    #str = "date +%m-%d-%Y";
    #str | getline date;
    #close str;
}
#$3>date
{
  # add a loop to add line only if either status is (wanted or testing) or ending year is gt or eq to contract year
  # increased to 2015 July 1 2015.
  current_year = 2015
  # end_year is the AU year, or the second half of a range, ie 2014 in 2013-2014
  end_year = 0
  incontract = 0
  # test_year is the contract year, or the back year if there is one.
  test_year = ""
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  #printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1,$2,$3,$4,$7,end_year,test_year
  # Is the AU year >= the contract year or the back year. And then decide what to do with the current year.
  if ($6 == "wanted" || $6 == "testing") {
      incontract = 1;
  } else if ((end_year >= test_year) && ((end_year < current_year) || (end_year == current_year && incl_cur == 1))) {
      incontract = 1;
  }

  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      if ($7 == "") {
        k[pn] = ".."
      } else {
        k[pn] = $7
      }
      d[pn] = $4
      r[pn] = $5
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "testing"
  s[5] = "notReady"
  s[6] = "ready"
  s[7] = "crawling"
  s[8] = "frozen"
  s[9] = "ingNotReady"
  s[10] = "finished"
  s[11] = "down"
  s[12] = "superseded"
  s[13] = "zapped"
  s[14] = "doNotProcess"
  sn = 15

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "test"
  sc[5] = "notR"
  sc[6] = "read"
  sc[7] = "craw"
  sc[8] = "froz"
  sc[9] = "ingN"
  sc[10] = "fini"
  sc[11] = "down"
  sc[12] = "supe"
  sc[13] = "zapp"
  sc[14] = "doNP"
  scn = 15

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
        printf "\t.."
      } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
  #printf "%s", date
  #printf "\n"
}


Add automated years.

Add automated years.

#!/usr/bin/awk -f
# tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back],publisher:info[poller] *.tdb

BEGIN {
  FS="\t"
  pn = 0
    #str = "date +%m-%d-%Y";
    #str | getline date;
    #close str;
}
#$3>date
{
  # add a loop to add line only if either status is (wanted or testing) or ending year is gt or eq to contract year

  #After July 1 of this year, start reporting on this year. Before that, ignore this year.
  current_year=`date +%Y`
  week=`date +%W`
  if [ $week -lt 26 ]
    then
    ((current_year--))
    #echo current_year $current_year
  fi
  #echo current_year $current_year

  # end_year is the AU year, or the second half of a range, ie 2014 in 2013-2014
  end_year = 0
  incontract = 0
  # test_year is the contract year, or the back year if there is one.
  test_year = ""
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  #printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1,$2,$3,$4,$7,end_year,test_year
  # Is the AU year >= the contract year or the back year. And then decide what to do with the current year.
  if ($6 == "wanted" || $6 == "testing") {
      incontract = 1;
  } else if ((end_year >= test_year) && ((end_year < current_year) || (end_year == current_year && incl_cur == 1))) {
      incontract = 1;
  }

  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      if ($7 == "") {
        k[pn] = ".."
      } else {
        k[pn] = $7
      }
      d[pn] = $4
      r1[pn] = $5
      r2[pn] = $8
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "testing"
  s[5] = "notReady"
  s[6] = "ready"
  s[7] = "crawling"
  s[8] = "deepCrawl"
  s[9] = "frozen"
  s[10] = "ingNotReady"
  s[11] = "finished"
  s[12] = "down"
  s[13] = "superseded"
  s[14] = "zapped"
  s[15] = "doNotProcess"
  sn = 16

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "test"
  sc[5] = "notR"
  sc[6] = "read"
  sc[7] = "craw"
  sc[8] = "deep"
  sc[9] = "froz"
  sc[10] = "ingN"
  sc[11] = "fini"
  sc[12] = "down"
  sc[13] = "supe"
  sc[14] = "zapp"
  sc[15] = "doNP"
  scn = 16

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tP\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, poller, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r1[i], r2[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
        printf "\t.."
      } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tP\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
  #printf "%s", date
  #printf "\n"
}

#!/usr/bin/awk -f
# tdbout -t publisher,plugin,publisher:info[contract],year,publisher:info[tester],status,publisher:info[back],publisher:info[poller] *.tdb

BEGIN {
  FS="\t"
  pn = 0
    #str = "date +%m-%d-%Y";
    #str | getline date;
    #close str;
}
#$3>date
{
  # add a loop to add line only if either status is (wanted or testing) or ending year is gt or eq to contract year
  # increased to 2018 July 1 ish 2018
  current_year = 2018
  # end_year is the AU year, or the second half of a range, ie 2014 in 2013-2014
  end_year = 0
  incontract = 0
  # test_year is the contract year, or the back year if there is one.
  test_year = ""
  if ($7 == "") {
    test_year = $3
  } else {
    test_year = $7
  }
  if (length($4) > 3) {
    end_year = substr($4,length($4)-3,4)
  }
  #printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1,$2,$3,$4,$7,end_year,test_year
  # Is the AU year >= the contract year or the back year. And then decide what to do with the current year.
  if ($6 == "wanted" || $6 == "testing") {
      incontract = 1;
  } else if ((end_year >= test_year) && ((end_year < current_year) || (end_year == current_year && incl_cur == 1))) {
      incontract = 1;
  }

  if (incontract == 1) {
      nn = split($2,na,/\./)
      lp2 = na[nn]
    if (!(($1,lp2,$4) in b)) {
      p[pn] = $1
      n[pn] = lp2
  #    n[pn] = $2
      t[pn] = $3
      if ($7 == "") {
        k[pn] = ".."
      } else {
        k[pn] = $7
      }
      d[pn] = $4
      r1[pn] = $5
      r2[pn] = $8
      pn++
    }
    b[$1,lp2,$4]++
    c[$1,lp2,$4,$6]++
    x[$6]++
    tt++
  }
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "testing"
  s[5] = "notReady"
  s[6] = "ready"
  s[7] = "crawling"
  s[8] = "deepCrawl"
  s[9] = "frozen"
  s[10] = "ingNotReady"
  s[11] = "finished"
  s[12] = "down"
  s[13] = "superseded"
  s[14] = "zapped"
  s[15] = "doNotProcess"
  sn = 16

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "test"
  sc[5] = "notR"
  sc[6] = "read"
  sc[7] = "craw"
  sc[8] = "deep"
  sc[9] = "froz"
  sc[10] = "ingN"
  sc[11] = "fini"
  sc[12] = "down"
  sc[13] = "supe"
  sc[14] = "zapp"
  sc[15] = "doNP"
  scn = 16

  #print out header
  printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tP\tTotal"
  for (j = 0 ; j < scn ; j++) {
    if (x[s[j]] > 0) {
    printf "\t%s", sc[j]
    }
  }
  printf "\n"

  #print out publisher, plugin, contract year, back, year, tester, poller, total aus
  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], k[i], d[i], r1[i], r2[i], b[p[i],n[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0){
      if (c[p[i],n[i],d[i],s[j]] == 0) {
        printf "\t.."
      } else {
        printf "\t%d", c[p[i],n[i],d[i],s[j]]
      }
    }
    }
    printf "\n"
  }
    #print out bottom line sums
    printf "Publisher\tPlugin\tContr\tBack\tYear\tT\tP\t%d", tt
    for (j = 0 ; j < sn ; j++) {
      if (x[s[j]] > 0) {
        printf "\t%d", x[s[j]]
      }
    }

  printf "\n"
  #printf "%s", date
  #printf "\n"
}


pull in the pretty-printer stylesheet

pull in the pretty-printer stylesheet

BEGIN {
    interesting = 1
}

skipScript && /<\/script>/ {
  skipScript = 0
  next
}

skipScript {
  next
}

### TODO:
#  <link rel="stylesheet" type="text/css" media="screen, projection, print" href="http://www.w3.org/Talks/Tools/Slidy2/styles/slidy.css" />

/<script type="text\/javascript" charset="utf-8">/ {
  print "  <script src='http://www.w3.org/Talks/Tools/Slidy2/scripts/slidy.js' charset='utf-8' type='text/javascript'></script>"
  skipScript = 1
  next
}

{
    sub(/<title>Haskell: Functional Programming, Solid Code, Big Data<\/title>/, "<title>Intro \\“Functional Programming\\”</title>")
    sub(/<meta name="date" content="2011-09-18" \/>/, "<meta name='date' content='2011-10-19' />")
    sub(/<meta name="author" content="Bryan O'Sullivan" \/>/, "<meta name='author' content='Gabor Greif' />\n  <meta name='duration' content='45' />\n  <link type='text/css' rel='stylesheet' href='http://www.gstatic.com/codesite/ph/13841197563397998716/css/ph_detail.css' />")
}

/^<body>/ {
    interesting = 0
    print
    next
}

interesting {
    print
    next
}

BEGIN {
    interesting = 1
}

skipScript && /<\/script>/ {
  skipScript = 0
  next
}

skipScript {
  next
}

### TODO:
#  <link rel="stylesheet" type="text/css" media="screen, projection, print" href="http://www.w3.org/Talks/Tools/Slidy2/styles/slidy.css" />

/<script type="text\/javascript" charset="utf-8">/ {
  print "  <script src='http://www.w3.org/Talks/Tools/Slidy2/scripts/slidy.js' charset='utf-8' type='text/javascript'></script>"
  skipScript = 1
  next
}

{
    sub(/<title>Haskell: Functional Programming, Solid Code, Big Data<\/title>/, "<title>Intro \\“Functional Programming\\”</title>")
    sub(/<meta name="date" content="2011-09-18" \/>/, "<meta name='date' content='2011-10-19' />")
    sub(/<meta name="author" content="Bryan O'Sullivan" \/>/, "<meta name='author' content='Gabor Greif' />\n  <meta name='duration' content='45' />")
}

/^<body>/ {
    interesting = 0
    print
    next
}

interesting {
    print
    next
}


Fix colorize_diff.awk

Fix colorize_diff.awk

#!/usr/bin/awk -f

function append_attr(s, n) {
    if (n == -1)
        return s;
    if (length(s) > 0)
        s = s ";";
    return sprintf("%s%d", s, n);
}

function set_attrs(bold, fg, bg) {
    if (fg != -1)
        fg += 30;
    if (bg != -1)
        bg += 40;
    s = "";
    s = append_attr(s, bold);
    s = append_attr(s, fg);
    s = append_attr(s, bg);
    printf("\033[%sm", s);
    need_reset = 1;
}

function reset_attrs() {
    if (need_reset) {
        printf("\033[0m");
        need_reset = 0;
    }
}

BEGIN {
    NA = -1;

    BOLD = 1;

    BLACK = 0;
    RED = 1;
    GREEN = 2;
    YELLOW = 3;
    BLUE = 4;
    MAGENTA = 5;
    CYAN = 6;
    WHITE = 7;

    need_reset = 0;
}

# Name of changed file / old name of file.
/^--- / {
    set_attrs(BOLD, WHITE, MAGENTA);
}

# Name of changed file / name of new file.
/^\+\+\+ / {
    set_attrs(BOLD, WHITE, BLUE);
}

# Lines which have been added.
/^\+[^+]/ || /^\+$/ {
    set_attrs(BOLD, WHITE, GREEN);
}

# Lines which have been removed.
/^-[^-]/ || /^-$/ {
    set_attrs(BOLD, WHITE, RED);
}

{
    print;
    reset_attrs();
}

#!/usr/bin/awk -f

function append_attr(s, n) {
    if (n == -1)
        return s;
    if (length(s) > 0)
        s = s ";";
    return sprintf("%s%d", s, n);
}

function set_attrs(bold, fg, bg) {
    if (fg != -1)
        fg += 30;
    if (bg != -1)
        bg += 40;
    s = "";
    s = append_attr(s, bold);
    s = append_attr(s, fg);
    s = append_attr(s, bg);
    printf("\033[%sm", s);
    need_reset = 1;
}

function reset_attrs() {
    if (need_reset) {
        printf("\033[0m");
        need_reset = 0;
    }
}

BEGIN {
    NA = -1;

    BOLD = 1;

    BLACK = 0;
    RED = 1;
    GREEN = 2;
    YELLOW = 3;
    BLUE = 4;
    MAGENTA = 5;
    CYAN = 6;
    WHITE = 7;

    need_reset = 0;
}

# Name of changed file / name of new file.
/^\+\+\+/ {
    set_attrs(BOLD, WHITE, BLUE);
}

# Lines which have been added.
/^\+[^+]/ {
    set_attrs(BOLD, WHITE, GREEN);
}

# Lines which have been removed.
/^-[^-]/ {
    set_attrs(BOLD, WHITE, RED);
}

{
    print;
    reset_attrs();
}


mpers: fix compound types with arbitrary member alignments

* mpers.sh: Compound types are now represented as packed
with character arrays as explicit fillers inbetween members
and at the end.

function compare_indices(i1, v1, i2, v2) {
	c1 = strtonum(sprintf("%s", i1))
	c2 = strtonum(sprintf("%s", i2))
	if (c1 < c2)
		return -1
	return (c1 != c2)
}
function what_is(what_idx, type_idx, special, item, \
		 location, prev_location, prev_returned_size)
{
	type_idx = array[what_idx]["type"]
	special = array[what_idx]["special"]
	switch (special) {
	case "base_type":
		switch (array[what_idx]["encoding"]) {
		case 5: # signed
			printf("%s ", "int" \
			8*array[what_idx]["byte_size"] "_t")
			break
		case 7: # unsigned
			printf("%s ", "uint" \
			8*array[what_idx]["byte_size"] "_t")
			break
		default: # float, signed/unsigned char
			printf("%s ", array[what_idx]["name"])
			break
		}
		returned_size = array[what_idx]["byte_size"]
		break
	case "enumeration_type":
		printf("%s ", "uint" 8*array[type_idx]["byte_size"] "_t")
		returned_size = array[what_idx]["byte_size"]
		break
	case "pointer_type":
		printf("%s", "mpers_ptr_t ")
		returned_size = array[what_idx]["byte_size"]
		break
	case "array_type":
		what_is(type_idx)
		to_return = array[what_idx]["upper_bound"]
		returned_size = array[what_idx]["upper_bound"] * returned_size
		return to_return
		break
	case "structure_type":
		print "struct {"
		prev_location = 0
		location = 0
		returned_size = 0
		prev_returned_size = 0
		for (item in array) {
			if ("parent" in array[item] && \
				array[item]["parent"] == what_idx) {
				location = array[item]["location"]
				loc_diff = location - prev_location - \
					prev_returned_size
				if (loc_diff != 0) {
					printf("%s", \
						"unsigned char mpers_filler_" \
						item "[" loc_diff "];\n")
				}
				prev_location = location
				returned = what_is(item)
				prev_returned_size = returned_size
				printf("%s", array[item]["name"])
				if (returned) {
					printf("%s", "[" returned "]")
				}
				print ";"
			}
		}
		returned_size = array[what_idx]["byte_size"]
		loc_diff = returned_size - prev_location - prev_returned_size
		if (loc_diff != 0) {
			printf("%s", "unsigned char mpers_end_filler_" \
				item "[" loc_diff "];\n")
		}
		printf("%s", "} ATTRIBUTE_PACKED ")
		break
	case "union_type":
		print "union {"
		for (item in array) {
			if ("parent" in array[item] && \
				array[item]["parent"] == what_idx) {
				returned = what_is(item)
				printf("%s", array[item]["name"])
				if (returned) {
					printf("%s", "[" returned "]")
				}
				print ";"
			}
		}
		printf("%s", "} ")
		returned_size = array[what_idx]["byte_size"]
		break
	case "typedef":
		return what_is(type_idx)
		break
	case "member":
		return what_is(type_idx)
		break
	default:
		what_is(type_idx)
		break
	}
	return 0
}
BEGIN {
	print "#include <inttypes.h>"
}
/^<[[:xdigit:]]+>/ {
	match($0, /([[:alnum:]]+)><([[:alnum:]]+)/, matches)
	level = matches[1]
	idx = "0x" matches[2]
	array[idx]["idx"] = idx
	parent[level] = idx
	if (level > 1) {
		array[idx]["parent"] = parent[level-1]
	}
}
/^DW_AT_data_member_location/ {
	match($0, /[[:digit:]]+/, temparray)
	array[idx]["location"] = temparray[0]
}
/^DW_AT_name/ {
	match($0, /:[[:space:]]+([[:alpha:]_][[:alnum:]_[:space:]]*)/, \
		temparray)
	array[idx]["name"] = temparray[1]
}
/^DW_AT_byte_size/ {
	match($0, /[[:digit:]]+/, temparray)
	array[idx]["byte_size"] = temparray[0]
}
/^DW_AT_encoding/ {
	match($0, /[[:digit:]]+/, temparray)
	array[idx]["encoding"] = temparray[0]
}
/^DW_AT_type/ {
	match($0, /:[[:space:]]+<(0x[[:xdigit:]]*)>$/, temparray)
	array[idx]["type"] = temparray[1]
}
/^DW_AT_upper_bound/ {
	match($0, /[[:digit:]]+/, temparray)
	array[parent[level-1]]["upper_bound"] = temparray[0] + 1
}
/^Abbrev Number:[^(]+\(DW_TAG_/ {
	if (match($0, /typedef|union_type|structure_type|pointer_type\
|enumeration_type|array_type|base_type|member/, temparray)) {
		array[idx]["special"] = temparray[0]
	}
}
END {
	PROCINFO["sorted_in"] = "compare_indices"
	for (item in array) {
		if (array[item]["special"] == "pointer_type") {
			print "typedef uint" \
				8*array[item]["byte_size"] "_t mpers_ptr_t;"
			break
		}
	}
	for (item in array) {
		if (array[item]["name"] == VAR_NAME) {
			type=array[item]["type"]
			print "typedef"
			what_is(array[item]["type"])
			print ARCH_FLAG "_" array[type]["name"] ";"
			print "#define MPERS_" \
				ARCH_FLAG "_" array[type]["name"] " " \
				ARCH_FLAG "_" array[type]["name"]
			break
		}
	}
}

function compare_indices(i1, v1, i2, v2) {
	c1 = strtonum(sprintf("%s", i1))
	c2 = strtonum(sprintf("%s", i2))
	if (c1 < c2)
		return -1
	return (c1 != c2)
}
function what_is(what_idx, type_idx, special, item)
{
	type_idx = array[what_idx]["type"]
	special = array[what_idx]["special"]
	switch (special) {
	case "base_type":
		switch (array[what_idx]["encoding"]) {
		case 5: # signed
			printf("%s ", "int" \
			8*array[what_idx]["byte_size"] "_t")
			break
		case 7: # unsigned
			printf("%s ", "uint" \
			8*array[what_idx]["byte_size"] "_t")
			break
		default: # float, signed/unsigned char
			printf("%s ", array[what_idx]["name"])
			break
		}
		break
	case "enumeration_type":
		printf("%s ", "uint" 8*array[type_idx]["byte_size"] "_t")
		break
	case "pointer_type":
		printf("%s", "mpers_ptr_t ")
		break
	case "array_type":
		what_is(type_idx)
		to_return = array[what_idx]["upper_bound"]
		return to_return
		break
	case "structure_type":
	case "union_type":
		if (special == "structure_type") {
			print "struct {"
		} else {
			print "union {"
		}
		for (item in array) {
			if ("parent" in array[item] && array[item]["parent"] \
				== what_idx) {
				returned = what_is(item)
				printf("%s", array[item]["name"])
				if (returned) {
					printf("%s", "[" returned "]")
				}
				print ";"
			}
		}
		printf("%s", "} ")
		break
	case "typedef":
		return what_is(type_idx)
		break
	case "member":
		return what_is(type_idx)
		break
	default:
		what_is(type_idx)
		break
	}
	return 0
}
BEGIN {
	print "#include <inttypes.h>"
}
/^<[[:xdigit:]]+>/ {
	match($0, /([[:alnum:]]+)><([[:alnum:]]+)/, matches)
	level = matches[1]
	idx = "0x" matches[2]
	array[idx]["idx"] = idx
	parent[level] = idx
	if (level > 1) {
		array[idx]["parent"] = parent[level-1]
	}
}
/^DW_AT_data_member_location/ {
	match($0, /[[:digit:]]+/, temparray)
	array[idx]["location"] = temparray[1]
}
/^DW_AT_name/ {
	match($0, /:[[:space:]]+([[:alpha:]_][[:alnum:]_[:space:]]*)/, temparray)
	array[idx]["name"] = temparray[1]
}
/^DW_AT_byte_size/ {
	match($0, /[[:digit:]]+/, temparray)
	array[idx]["byte_size"] = temparray[0]
}
/^DW_AT_encoding/ {
	match($0, /[[:digit:]]+/, temparray)
	array[idx]["encoding"] = temparray[0]
}
/^DW_AT_type/ {
	match($0, /:[[:space:]]+<(0x[[:xdigit:]]*)>$/, temparray)
	array[idx]["type"] = temparray[1]
}
/^DW_AT_upper_bound/ {
	match($0, /[[:digit:]]+/, temparray)
	array[parent[level-1]]["upper_bound"] = temparray[0] + 1
}
/^Abbrev Number:[^(]+\(DW_TAG_/ {
	if (match($0, /typedef|union_type|structure_type|pointer_type\
|enumeration_type|array_type|base_type|member/, temparray)) {
		array[idx]["special"] = temparray[0]
	}
}
END {
	PROCINFO["sorted_in"] = "compare_indices"
	for (item in array) {
		if (array[item]["special"] == "pointer_type") {
			print "typedef uint" \
				8*array[item]["byte_size"] "_t mpers_ptr_t;"
			break
		}
	}
	for (item in array) {
		if (array[item]["name"] == VAR_NAME) {
			type=array[item]["type"]
			print "typedef"
			what_is(array[item]["type"])
			print ARCH_FLAG "_" array[type]["name"] ";"
			print "#define MPERS_" \
				ARCH_FLAG "_" array[type]["name"] " " \
				ARCH_FLAG "_" array[type]["name"]
			break
		}
	}
}


ahrf.awk: Allow paragraphs to start with '(' (from Alex)

#!/usr/bin/awk -f

BEGIN { FS = "\n"; RS = "" }

# Common shell symbols to HTML
{
	# Two backslashes for nawk(1) and OpenBSD awk(1)
	gsub(/\&/,"\\&")
	gsub(/</,"\\<")
	gsub(/>/,"\\>")
}

# h1, h2, h3, h4, h5, h6
/^[\t ]*:/ {
	match($0,":+")
	cnt = RLENGTH
	gsub(/^[\t ]*:+[\t ]*|[\t ]*:+[\t ]*$/,"")
	# length($0) would also work
	if (cnt <= 6 && $0 != "") {
		printf("<h%s>%s</h%s>\n", cnt, $0, cnt)
	}
	next
}

# Paragraph
/^[A-Za-z0-9_(]+/ {
	printf("
")
	for (p=1; p<NF; p++) {
		if ($p ~ / +$/) {
			gsub(/ +$/,"",$p)
			printf("%s
\n", $p)
		} else {
			printf("%s\n", $p)
		}
	}
	printf("%s
\n\n", $p)
	next
}

# Code
/^====+/ {
	if (NF > 2 && $NF ~ /====+/) {
		gsub(/^[\t ]*====+[\t ]*\n|\n[\t ]*====+[\t ]*$/,"")
		printf("
")
		for (c=1; c<NF; c++) {
			gsub(/^ +$/,"",$c)
			printf("%s\n", $c)
		}
		# Solve "space" bug! Less crado (gsub).
		#gsub(/^ +/,"",$NF)
		printf("%s
\n", $NF)
	}
	next
}

# List
/^[\t ]*\* +/ {
	printf("
\n")
	for (l=1; l<=NF; l++) {
		gsub(/^[\t ]*/,"",$l)
		if ($l ~ /\* +/) {
			match($l,"\\* +")
			str = substr($l,RSTART+RLENGTH)
			printf("\t
%s
\n", str)
		}
	}
	printf("
\n")
	next
}

# End links
/^[\t ]*\[[01]/ {
	printf("
\n")
	for (u=1; u<=NF; u++) {
		gsub(/^[\t ]*/,"",$u)
		if (match($u,"\\[[0-9]+\\]")) {
			# RSTART can be replaced by 1, because we removed useless tabs/spaces
			num = substr($u,RSTART,RLENGTH)
			url = substr($u,RSTART+RLENGTH+1)
			if (length(url) >= 60) {
				printf("\t
%s %.60s...
\n", num, url, url)
			} else {
				printf("\t
%s %s
\n", num, url, url)
			}
		}
	}
	printf("
\n")
	next
}

#!/usr/bin/awk -f

BEGIN { FS = "\n"; RS = "" }

# Common shell symbols to HTML
{
	# Two backslashes for nawk(1) and OpenBSD awk(1)
	gsub(/\&/,"\\&")
	gsub(/</,"\\<")
	gsub(/>/,"\\>")
}

# h1, h2, h3, h4, h5, h6
/^[\t ]*:/ {
	match($0,":+")
	cnt = RLENGTH
	gsub(/^[\t ]*:+[\t ]*|[\t ]*:+[\t ]*$/,"")
	# length($0) would also work
	if (cnt <= 6 && $0 != "") {
		printf("<h%s>%s</h%s>\n", cnt, $0, cnt)
	}
	next
}

# Paragraph
/^[A-Za-z0-9_]+/ {
	printf("
")
	for (p=1; p<NF; p++) {
		if ($p ~ / +$/) {
			gsub(/ +$/,"",$p)
			printf("%s
\n", $p)
		} else {
			printf("%s\n", $p)
		}
	}
	printf("%s
\n\n", $p)
	next
}

# Code
/^====+/ {
	if (NF > 2 && $NF ~ /====+/) {
		gsub(/^[\t ]*====+[\t ]*\n|\n[\t ]*====+[\t ]*$/,"")
		printf("
")
		for (c=1; c<NF; c++) {
			gsub(/^ +$/,"",$c)
			printf("%s\n", $c)
		}
		# Solve "space" bug! Less crado (gsub).
		#gsub(/^ +/,"",$NF)
		printf("%s
\n", $NF)
	}
	next
}

# List
/^[\t ]*\* +/ {
	printf("
\n")
	for (l=1; l<=NF; l++) {
		gsub(/^[\t ]*/,"",$l)
		if ($l ~ /\* +/) {
			match($l,"\\* +")
			str = substr($l,RSTART+RLENGTH)
			printf("\t
%s
\n", str)
		}
	}
	printf("
\n")
	next
}

# End links
/^[\t ]*\[[01]/ {
	printf("
\n")
	for (u=1; u<=NF; u++) {
		gsub(/^[\t ]*/,"",$u)
		if (match($u,"\\[[0-9]+\\]")) {
			# RSTART can be replaced by 1, because we removed useless tabs/spaces
			num = substr($u,RSTART,RLENGTH)
			url = substr($u,RSTART+RLENGTH+1)
			if (length(url) >= 60) {
				printf("\t
%s %.60s...
\n", num, url, url)
			} else {
				printf("\t
%s %s
\n", num, url, url)
			}
		}
	}
	printf("
\n")
	next
}


*** empty log message ***

*** empty log message ***

# CASED.awk -- awk script to generate CASED.tab
# Copyright (C) 2005
#   National Institute of Advanced Industrial Science and Technology (AIST)
#   Registration Number H15PRO112

# This file is part of the m17n database; a sub-part of the m17n
# library.

# The m17n library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.

# The m17n library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with the m17n library; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307, USA.

BEGIN {
  tohex["0"] = 1;
  tohex["1"] = 2;
  tohex["2"] = 3;
  tohex["3"] = 4;
  tohex["4"] = 5;
  tohex["5"] = 6;
  tohex["6"] = 7;
  tohex["7"] = 8;
  tohex["8"] = 9;
  tohex["9"] = 10;
  tohex["A"] = 11;
  tohex["B"] = 12;
  tohex["C"] = 13;
  tohex["D"] = 14;
  tohex["E"] = 15;
  tohex["F"] = 16;
  tohex["a"] = 11;
  tohex["b"] = 12;
  tohex["c"] = 13;
  tohex["d"] = 14;
  tohex["e"] = 15;
  tohex["f"] = 16;

  FS = "[ \t]*[;#][ \t]*";
}

function decode_hex(str, idx) {
  n = 0;
  len = length(str);
  for (i = idx; i <= len; i++)
    {
      c = tohex[substr(str, i, 1)];
      if (c == 0)
	break;
      n = n * 16 + c - 1;
    }
  return n;
}

function single(str, bit) {
  i = decode_hex(str, 1);
  if (cased[i] != bit)
    cased[i] += bit;
}

function range(str, bit) {
  end = decode_hex(str, index(str, "..") + 2);
  for (i = decode_hex(str, 1); i <= end; i++)
    if (cased[i] != bit)
      cased[i] += bit;
}

/^[^\#]/ {
  if (FILENAME == "UNIDATA/UnicodeData.txt") {
    if ($3 ~ /L[ltu]/)
      single($1, 1);
    else if ($3 ~ /Mn|Me|Cf|Lm|Sk/)
      single($1, 2);
  }

  else if (FILENAME == "UNIDATA/PropList.txt") {
    if ($2 ~ /Other_(Upp|Low)ercase/) {
      if (index($1, "."))
	range($1, 1);
      else
	single($1, 1);
    }
  }

  else {			# FILE == "WordBreakProperty.txt"
    if ($2 == "MidLetter") {
      if (index($1, "."))
	range($1, 2);
      else
	single($1, 2);
    }
  }
}

END {
  for (i in cased)
    printf ("0x%X %d\n", i, cased[i]);
}

# CASED.awk -- awk script to generate CASED.tab
# Copyright (C) 2005
#   National Institute of Advanced Industrial Science and Technology (AIST)
#   Registration Number H15PRO112

# This file is part of the m17n database; a sub-part of the m17n
# library.

# The m17n library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.

# The m17n library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.

# You should have received a copy of the GNU Lesser General Public
# License along with the m17n library; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307, USA.

BEGIN {
  tohex["0"] = 1;
  tohex["1"] = 2;
  tohex["2"] = 3;
  tohex["3"] = 4;
  tohex["4"] = 5;
  tohex["5"] = 6;
  tohex["6"] = 7;
  tohex["7"] = 8;
  tohex["8"] = 9;
  tohex["9"] = 10;
  tohex["A"] = 11;
  tohex["B"] = 12;
  tohex["C"] = 13;
  tohex["D"] = 14;
  tohex["E"] = 15;
  tohex["F"] = 16;
  tohex["a"] = 11;
  tohex["b"] = 12;
  tohex["c"] = 13;
  tohex["d"] = 14;
  tohex["e"] = 15;
  tohex["f"] = 16;

  FS = "[ \t]*[;#][ \t]*";
}

function decode_hex(str, idx) {
  n = 0;
  len = length(str);
  for (i = idx; i <= len; i++)
    {
      c = tohex[substr(str, i, 1)];
      if (c == 0)
	break;
      n = n * 16 + c - 1;
    }
  return n;
}

function single(str, bit) {
  i = decode_hex(str, 1);
  if (cased[i] != bit)
    cased[i] += bit;
}

function range(str, bit) {
  end = decode_hex(str, index(str, "..") + 2);
  for (i = decode_hex(str, 1); i <= end; i++)
    if (cased[i] != bit)
      cased[i] += bit;
}

/^[^\#]/ {

  if (FILENAME == "UNIDATA/UnicodeData.txt") {
    if ($3 ~ /L[ltu]/)
      single($1, 1);
    else if ($3 ~ /Mn|Me|Cf|Lm|Sk/)
      single($1, 2);
    next;
  }

  else if (FILENAME == "UNIDATA/PropList.txt") {
    if ($2 ~ /Other_(Upp|Low)ercase/) {
      if (index($1, "."))
	range($1, 1);
      else
	single($1, 1);
      next;
    }
  }

  else {			# FILE == "WordBreakProperty.txt"
    if ($2 == "MidLetter") {
      if (index($1, "."))
	range($1, 2);
      else
	single($1, 2);
      next;
    }
  }
}

END {
  for (i in cased)
    printf ("0x%X %d\n", i, cased[i]);
}


aux/ematch.awk: detect NetBSD awk bug with character classes

lib/modernish/aux/ematch.awk:
- Don't detect character class support if NetBSD awk bug
  http://gnats.netbsd.org/54424 is present (in UTF-8 locales, only
  the first character class in a bracket expression is matched).
  This triggers the ASCII translation of character classes, which
  is a bit better as it's at least consistent.

#! /usr/bin/awk -f
#
# Script for 'str ematch' for shells that cannot internally handle POSIX
# extended regular expressions (EREs).
#
# Converts POSIX EREs (with bounds) to traditional awk regular expressions
# (without bounds). On very old awk's without support for POSIX character
# classes in bracket expressions, it translates character classes to ASCII
# equivalents. It also disables awk-specific extensions while doing all that.
#
# This script can also be invoked independently as a command.
#
# --- begin license ---
# Copyright (c) 2019 Martijn Dekker <martijn@inlv.org>, Groningen, Netherlands
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# --- end license ---

BEGIN {
	if (ARGC != 3)
		errorout("usage: ematch.awk <string> <ERE>");
	hasclass = match("1", /[[:alpha:][:digit:]]/);
	exit !match(ARGV[1], convertere(ARGV[2]));
}

function errorout(s, ere, i) {
	if (s) printf("str ematch: %s\n", s) | "cat >&2";
	if (ere) printf("%s\n", ere) | "cat >&2";
	if (i) printf(i>1 ? ("%")(i-1)("c^\n") : "^\n", " ") | "cat >&2";
	exit 2;
}

# The terms 'branch', 'piece', 'atom' and 'bound' are used as defined
# in the manual page re_format(7) on BSD/macOS, or regex(7) on Linux.
# OpenBSD has the best version: https://man.openbsd.org/re_format.7

function convertere(ere, par, \
	eere, piece, atom, L, c, i, isave, j, b, m, n, d1, d2, havepunct)
{
	par += 0;  # initialise if not given

	L = length(ere);
	c = substr(ere, 1, 1);
	i = 1;
	while (i <= L) {
		#### Parse just enough ERE grammar to know one atom from the next.
		if (c == "(") {
			# Sub-ERE in parentheses
			atom = ("(")(convertere(substr(ere, ++i), par + 1))(")");
			i += (_Msh_eB_isave - 1);
		} else if (par && c == ")") {
			# End of sub-ERE in parentheses
			_Msh_eB_isave = i;
			return eere;
		} else if (c == "|" || c == "^" || c == "$") {
			# Branch separator, ^/$ anchor, or non-anchor special ^/$
			eere = (eere)(c);
			c = substr(ere, ++i, 1);
			continue;
		} else if (c == "\\" && i < L) {
			# Backslash-escaped character: remove backslash for non-special characters.
			# This disables the C-style backslash escapes that are specific to awk.
			c = substr(ere, ++i, 1);
			if (match(c, /[.[\\()*+?{|^$]/))
				atom = ("\\")(c);
			else
				atom = c;
		} else if (c == "[") {
			# Bracket expression
			havepunct = 0;	# if translating to ASCII, handle [:punct:] specially
			atom = "";
			isave = i;
			i++;
			if (substr(ere, i, 1) == "^") {
				i++;  # negator
				atom = (atom)("^");
			}
			if (substr(ere, i, 1) == "]") {
				i++;  # initial ']' represents itself
				atom = (atom)("]");
			}
			while (i <= L && (c = substr(ere, i, 1)) != "]") {
				if (match(d1 = substr(ere, i, 2), /\[[.=:]/)) {
					# [.collation.], [=equivalence=] or [:character:] class
					d2 = (substr(ere, i+1, 1))("]");
					i += 2;
					isave = i - 1;
					while (i < L && substr(ere, i, 2) != d2)
						i++;
					i++;
					j = substr(ere, isave, i - isave);
					if (!hasclass) {
						# No class/locale support: translate to traditional ASCII. Note that awk supports
						# C-style backslash codes even within bracket expressions, unlike POSIX EREs.
						if (j == ":alnum:" ) {
							j = "A-Za-z0-9";
						} else if (j == ":alpha:") {
							j = "A-Za-z";
						} else if (j == ":blank:") {
							j = " \\t";
						} else if (j == ":cntrl:") {
							j = "\\1-\\37\\177";
						} else if (j == ":digit:") {
							j = "0-9";
						} else if (j == ":graph:") {
							j = "\\41-\\176";
						} else if (j == ":lower:") {
							j = "a-z";
						} else if (j == ":print:") {
							j = "\\40-\\176";
						} else if (j == ":punct:") {
							havepunct++;
							j = "";
						} else if (j == ":space:") {
							# onetrueawk does not support "\v", so use "\13".
							# https://github.com/onetrueawk/awk/pull/44
							j = " \\t\\r\\n\\13\\f";
						} else if (j == ":upper:") {
							j = "A-Z";
						} else if (j == ":xdigit:") {
							j = "A-Fa-f0-9";
						} else if (match(j, /^[=.].*[=.]$/)) {
							# Translate collation and equivalence classes to literal
							# characters in parentheses. TODO: is this a sensible fallback?
							j = ("(")(substr(j, 2, length(j) - 2))(")");
							gsub(/\\/, "\\\\", j);
						} else {
							errorout("invalid character class", ere, isave);
						}
						atom = (atom)(j);
					} else {
						# class/locale support: keep it as is
						atom = (atom)("[")(j)("]");
					}
				} else if (c == "\\") {
					# In POSIX EREs, the backslash is not special at all within a bracket expression, but awk
					# parses C-style backslash escape codes in the entire ERE, including bracket expressions.
					# So to convert a POSIX bracket expression, we have to escape the backslash.
					atom = (atom)("\\\\");
				} else {
					# Other single character in bracket expression.
					atom = (atom)(c);
				}
				i++;
			}
			if (i > L)
				errorout("unterminated bracket expression", ere, isave);
			if (havepunct) {
				# Translate ASCII [:punct:] specially, making sure ']' is the first
				# character, and '-' the last, in the entire bracket expression.
				if (substr(atom, 1, 1) == "]") {
					atom = ("][!\"#$%&'()*+,\\./:;<=>?@^_`{|}~")(substr(atom, 2));
				} else {
					atom = ("][!\"#$%&'()*+,\\./:;<=>?@^_`{|}~")(atom);
				}
				if (substr(atom, length(atom), 1) != "-") {
					atom = (atom)("-");
				}
			}
			atom = ("[")(atom)("]");
		} else if (c == "*" || c == "+" || c == "?") {
			errorout("repetition operator not valid here", ere, i);
		} else {
			# Simple character
			atom = c;
		}

		#### Now that we've got an atom, parse repetitions to get a piece.
		c = substr(ere, ++i, 1);
		if (c == "*" || c == "+" || c == "?") {
			# Traditional repetiton operator.
			piece = (atom)(c);
			c = substr(ere, ++i, 1);
		} else if (c == "{" && match(substr(ere, i+1, 1), /[0123456789]/)) {
			# Interval expression, a.k.a. repetition expression, a.k.a. bound.
			# Expand it in terms of the repetition operators '*', '+' and '?'.
			isave = i;
			b = 1;		# number of bound params
			m = ""; n = "";	# bound params
			while ( (c = substr(ere, ++i, 1)) != "}" && i <= L ) {
				if (c == ",")
					b++;
				else if (!match(c, /[0123456789]/))
					errorout("bound: bad number", ere, i);
				else if (b == 1)
					m = ( (m)(c) ) + 0; # concatenate, convert to int
				else if (b == 2)
					n = ( (n)(c) ) + 0;
			}
			if (i > L)
				errorout("unterminated bound", ere, isave);
			if (b > 2)
				errorout("bound: too many parameters", ere, isave);
			if (b == 1) {
				# {m}: exactly m occurrences
				piece = "";
				for (j = 1; j <= m; j++)
					piece = (piece)(atom);
			} else if (n == "") {
				# {m,}: at least m occurrences
				piece = atom;
				if (m == 0) {
					piece = (piece)("*");
				} else {
					for (j = 2; j <= m; j++)
						piece = (piece)(atom);
					piece = (piece)("+");
				}
			} else {
				# {m,n}: any number of occurrences between m and n, inclusive
				if (n < m)
					errorout("bad bound: max < min", ere, isave);
				piece = "";
				for (j = 1; j <= m; j++)
					piece = (piece)(atom);
				for (; j <= n; j++)
					piece = (piece)(atom)("?");
			}
			c = substr(ere, ++i, 1);
		} else {
			# No repetition.
			piece = atom;
		}
		eere = (eere)(piece);
	}
	# End of ERE.
	if (par)
		errorout("unbalanced parentheses", ere);
	return eere;
}

#! /usr/bin/awk -f
#
# Script for 'str ematch' for shells that cannot internally handle POSIX
# extended regular expressions (EREs).
#
# Converts POSIX EREs (with bounds) to traditional awk regular expressions
# (without bounds). On very old awk's without support for POSIX character
# classes in bracket expressions, it translates character classes to ASCII
# equivalents. It also disables awk-specific extensions while doing all that.
#
# This script can also be invoked independently as a command.
#
# --- begin license ---
# Copyright (c) 2019 Martijn Dekker <martijn@inlv.org>, Groningen, Netherlands
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# --- end license ---

BEGIN {
	if (ARGC != 3)
		errorout("usage: ematch.awk <string> <ERE>");
	hasclass = match("1", /[[:digit:]]/);
	exit !match(ARGV[1], convertere(ARGV[2]));
}

function errorout(s, ere, i) {
	if (s) printf("str ematch: %s\n", s) | "cat >&2";
	if (ere) printf("%s\n", ere) | "cat >&2";
	if (i) printf(i>1 ? ("%")(i-1)("c^\n") : "^\n", " ") | "cat >&2";
	exit 2;
}

# The terms 'branch', 'piece', 'atom' and 'bound' are used as defined
# in the manual page re_format(7) on BSD/macOS, or regex(7) on Linux.
# OpenBSD has the best version: https://man.openbsd.org/re_format.7

function convertere(ere, par, \
	eere, piece, atom, L, c, i, isave, j, b, m, n, d1, d2, havepunct)
{
	par += 0;  # initialise if not given

	L = length(ere);
	c = substr(ere, 1, 1);
	i = 1;
	while (i <= L) {
		#### Parse just enough ERE grammar to know one atom from the next.
		if (c == "(") {
			# Sub-ERE in parentheses
			atom = ("(")(convertere(substr(ere, ++i), par + 1))(")");
			i += (_Msh_eB_isave - 1);
		} else if (par && c == ")") {
			# End of sub-ERE in parentheses
			_Msh_eB_isave = i;
			return eere;
		} else if (c == "|" || c == "^" || c == "$") {
			# Branch separator, ^/$ anchor, or non-anchor special ^/$
			eere = (eere)(c);
			c = substr(ere, ++i, 1);
			continue;
		} else if (c == "\\" && i < L) {
			# Backslash-escaped character: remove backslash for non-special characters.
			# This disables the C-style backslash escapes that are specific to awk.
			c = substr(ere, ++i, 1);
			if (match(c, /[.[\\()*+?{|^$]/))
				atom = ("\\")(c);
			else
				atom = c;
		} else if (c == "[") {
			# Bracket expression
			havepunct = 0;	# if translating to ASCII, handle [:punct:] specially
			atom = "";
			isave = i;
			i++;
			if (substr(ere, i, 1) == "^") {
				i++;  # negator
				atom = (atom)("^");
			}
			if (substr(ere, i, 1) == "]") {
				i++;  # initial ']' represents itself
				atom = (atom)("]");
			}
			while (i <= L && (c = substr(ere, i, 1)) != "]") {
				if (match(d1 = substr(ere, i, 2), /\[[.=:]/)) {
					# [.collation.], [=equivalence=] or [:character:] class
					d2 = (substr(ere, i+1, 1))("]");
					i += 2;
					isave = i - 1;
					while (i < L && substr(ere, i, 2) != d2)
						i++;
					i++;
					j = substr(ere, isave, i - isave);
					if (!hasclass) {
						# No class/locale support: translate to traditional ASCII. Note that awk supports
						# C-style backslash codes even within bracket expressions, unlike POSIX EREs.
						if (j == ":alnum:" ) {
							j = "A-Za-z0-9";
						} else if (j == ":alpha:") {
							j = "A-Za-z";
						} else if (j == ":blank:") {
							j = " \\t";
						} else if (j == ":cntrl:") {
							j = "\\1-\\37\\177";
						} else if (j == ":digit:") {
							j = "0-9";
						} else if (j == ":graph:") {
							j = "\\41-\\176";
						} else if (j == ":lower:") {
							j = "a-z";
						} else if (j == ":print:") {
							j = "\\40-\\176";
						} else if (j == ":punct:") {
							havepunct++;
							j = "";
						} else if (j == ":space:") {
							# onetrueawk does not support "\v", so use "\13".
							# https://github.com/onetrueawk/awk/pull/44
							j = " \\t\\r\\n\\13\\f";
						} else if (j == ":upper:") {
							j = "A-Z";
						} else if (j == ":xdigit:") {
							j = "A-Fa-f0-9";
						} else if (match(j, /^[=.].*[=.]$/)) {
							# Translate collation and equivalence classes to literal
							# characters in parentheses. TODO: is this a sensible fallback?
							j = ("(")(substr(j, 2, length(j) - 2))(")");
							gsub(/\\/, "\\\\", j);
						} else {
							errorout("invalid character class", ere, isave);
						}
						atom = (atom)(j);
					} else {
						# class/locale support: keep it as is
						atom = (atom)("[")(j)("]");
					}
				} else if (c == "\\") {
					# In POSIX EREs, the backslash is not special at all within a bracket expression, but awk
					# parses C-style backslash escape codes in the entire ERE, including bracket expressions.
					# So to convert a POSIX bracket expression, we have to escape the backslash.
					atom = (atom)("\\\\");
				} else {
					# Other single character in bracket expression.
					atom = (atom)(c);
				}
				i++;
			}
			if (i > L)
				errorout("unterminated bracket expression", ere, isave);
			if (havepunct) {
				# Translate ASCII [:punct:] specially, making sure ']' is the first
				# character, and '-' the last, in the entire bracket expression.
				if (substr(atom, 1, 1) == "]") {
					atom = ("][!\"#$%&'()*+,\\./:;<=>?@^_`{|}~")(substr(atom, 2));
				} else {
					atom = ("][!\"#$%&'()*+,\\./:;<=>?@^_`{|}~")(atom);
				}
				if (substr(atom, length(atom), 1) != "-") {
					atom = (atom)("-");
				}
			}
			atom = ("[")(atom)("]");
		} else if (c == "*" || c == "+" || c == "?") {
			errorout("repetition operator not valid here", ere, i);
		} else {
			# Simple character
			atom = c;
		}

		#### Now that we've got an atom, parse repetitions to get a piece.
		c = substr(ere, ++i, 1);
		if (c == "*" || c == "+" || c == "?") {
			# Traditional repetiton operator.
			piece = (atom)(c);
			c = substr(ere, ++i, 1);
		} else if (c == "{" && match(substr(ere, i+1, 1), /[0123456789]/)) {
			# Interval expression, a.k.a. repetition expression, a.k.a. bound.
			# Expand it in terms of the repetition operators '*', '+' and '?'.
			isave = i;
			b = 1;		# number of bound params
			m = ""; n = "";	# bound params
			while ( (c = substr(ere, ++i, 1)) != "}" && i <= L ) {
				if (c == ",")
					b++;
				else if (!match(c, /[0123456789]/))
					errorout("bound: bad number", ere, i);
				else if (b == 1)
					m = ( (m)(c) ) + 0; # concatenate, convert to int
				else if (b == 2)
					n = ( (n)(c) ) + 0;
			}
			if (i > L)
				errorout("unterminated bound", ere, isave);
			if (b > 2)
				errorout("bound: too many parameters", ere, isave);
			if (b == 1) {
				# {m}: exactly m occurrences
				piece = "";
				for (j = 1; j <= m; j++)
					piece = (piece)(atom);
			} else if (n == "") {
				# {m,}: at least m occurrences
				piece = atom;
				if (m == 0) {
					piece = (piece)("*");
				} else {
					for (j = 2; j <= m; j++)
						piece = (piece)(atom);
					piece = (piece)("+");
				}
			} else {
				# {m,n}: any number of occurrences between m and n, inclusive
				if (n < m)
					errorout("bad bound: max < min", ere, isave);
				piece = "";
				for (j = 1; j <= m; j++)
					piece = (piece)(atom);
				for (; j <= n; j++)
					piece = (piece)(atom)("?");
			}
			c = substr(ere, ++i, 1);
		} else {
			# No repetition.
			piece = atom;
		}
		eere = (eere)(piece);
	}
	# End of ERE.
	if (par)
		errorout("unbalanced parentheses", ere);
	return eere;
}


Add percentiles support to stats.awk

Add percentiles support to stats.awk

#!/usr/bin/awk -f

BEGIN {
	min = 10000000
}

{
	val = $2

	if (val < min)
		min = val
	if (val > max)
		max = val
	avg += val

	# percentiles
	array[NR] = val
}

END {
	avg /= NR
	inv = 1000000

	asort(array)

	p50 = int(NR / 2)
	p90 = int(NR * 0.9)
	p95 = int(NR * 0.95)
	p99 = int(NR * 0.99)

	print "Min/avg/max frametimes (us): 	" min " / " avg " / " max
	print "Min/avg/max FPS:		" inv/max " / " inv/avg " / " inv/min
	print ""
	print "50/90/95/99 percentiles (us):	" array[p50] " / " array[p90] " / " \
			array[p95] " / " array[p99]
}

#!/usr/bin/awk -f

BEGIN {
	min = 10000000
}

{
	val = $2

	if (val < min)
		min = val
	if (val > max)
		max = val
	avg += val
}

END {
	avg /= NR
	inv = 1000000
	print "Min/avg/max frametimes (us): 	" min " / " avg " / " max
	print "Min/avg/max FPS:		" inv/max " / " inv/avg " / " inv/min
}


units in initial state too

units in initial state too

BEGIN {
  print "principia_initial_state {";
  first_pass = true
}
/^Target body name:/ {
  if (first_pass) {
    first_pass = false
  } else {
    print "  body {";
    print "    name = " body;
    print "    x    = " x " km";
    print "    y    = " y " km";
    print "    z    = " z " km";
    print "    vx   = " vx " km/s";
    print "    vy   = " vy " km/s";
    print "    vz   = " vz "km/s";
    print "  }"
  }
  body = $0;
  sub(/ \(.*/, "", body);
  sub(/.*: /, "", body)
}
/^ *X = / {
  x = $3;
  y = $6;
  z = $9
}
/^ *VX=/ {
  vx = $2;
  vy = $4;
  vz = $6
}
END {
  print "}"
}

function print_body() {
  print "  body {"
  print "    name = " body;
  print "    x    = " x;
  print "    y    = " y;
  print "    z    = " z;
  print "    vx   = " vx;
  print "    vy   = " vy;
  print "    vz   = " vz;
  print "  }"
}

BEGIN {
  print "principia_initial_state {"
}
/^Target body name:/ {
  body = $0;
  sub(/ \(.*/, "", body);
  sub(/.*: /, "", body)
}
/^ *X = / {
  x = $3;
  y = $6;
  z = $9
}
/^ *VX=/ {
  vx = $2;
  vy = $4;
  vz = $6;
  print_body()
}
END {
  print_body();
  print "}"
}