Add SDT DTrace probes for VFS vnode operations in the vfs:vop
provider namespace.  These are inserted dynamically into the
VOP_..._AP() functions created from vnode_if.src.  Each VOP has
entry and return probes, as arg0 the primary vnode, arg1 the
vnode operation argument structure pointer, providing access to
IN and OUT arguments, and for return probes, arg2 the return
value.

MFC after:	1 month
Sponsored by:	Google, Inc.

#!/usr/bin/awk -f

#-
# Copyright (c) 1992, 1993
#	The Regents of the University of California.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 4. Neither the name of the University nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

#
#	@(#)vnode_if.sh	8.1 (Berkeley) 6/10/93
# $FreeBSD$
#
# Script to produce VFS front-end sugar.
#
# usage: vnode_if.awk <srcfile> [-c | -h | -p | -q]
#	(where <srcfile> is currently /sys/kern/vnode_if.src)
#	The source file must have a .src extension
#

function usage()
{
	print "usage: vnode_if.awk <srcfile> [-c|-h|-p|-q]";
	exit 1;
}

function die(msg, what)
{
	printf srcfile "(" fnr "): " > "/dev/stderr";
	printf msg "\n", what > "/dev/stderr";
	exit 1;
}

function t_spc(type)
{
	# Append a space if the type is not a pointer
	return (type ~ /\*$/) ? type : type " ";
}

# These are just for convenience ...
function printc(s) {print s > cfile;}
function printh(s) {print s > hfile;}
function printp(s) {print s > pfile;}
function printq(s) {print s > qfile;}

function add_debug_code(name, arg, pos, ind)
{
	if (arg == "vpp")
		star = "*";
	else
		star = "";
	if (lockdata[name, arg, pos] && (lockdata[name, arg, pos] != "-")) {
		printc(ind"ASSERT_VI_UNLOCKED("star"a->a_"arg", \""uname"\");");
		# Add assertions for locking
		if (lockdata[name, arg, pos] == "L")
			printc(ind"ASSERT_VOP_LOCKED(" star "a->a_"arg", \""uname"\");");
		else if (lockdata[name, arg, pos] == "U")
			printc(ind"ASSERT_VOP_UNLOCKED(" star "a->a_"arg", \""uname"\");");
		else if (lockdata[name, arg, pos] == "E")
			printc(ind"ASSERT_VOP_ELOCKED(" star "a->a_"arg", \""uname"\");");
		else if (0) {
			# XXX More checks!
		}
	}
}

function add_pre(name)
{
	if (lockdata[name, "pre"]) {
		printc("\t"lockdata[name, "pre"]"(a);");
	}
}

function add_post(name)
{
	if (lockdata[name, "post"]) {
		printc("\t"lockdata[name, "post"]"(a, rc);");
	}
}

function find_arg_with_type (type)
{
	for (jj = 0; jj < numargs; jj++) {
		if (types[jj] == type) {
			return "VOPARG_OFFSETOF(struct " \
			    name "_args,a_" args[jj] ")";
		}
	}

	return "VDESC_NO_OFFSET";
}

BEGIN{

# Process the command line
for (i = 1; i < ARGC; i++) {
	arg = ARGV[i];
	if (arg !~ /^-[chpq]+$/ && arg !~ /\.src$/)
		usage();
	if (arg ~ /^-.*c/)
		cfile = "vnode_if.c";
	if (arg ~ /^-.*h/)
		hfile = "vnode_if.h";
	if (arg ~ /^-.*p/)
		pfile = "vnode_if_newproto.h";
	if (arg ~ /^-.*q/)
		qfile = "vnode_if_typedef.h";
	if (arg ~ /\.src$/)
		srcfile = arg;
}
ARGC = 1;

if (!cfile && !hfile && !pfile && !qfile)
	exit 0;

if (!srcfile)
	usage();

common_head = \
    "/*\n" \
    " * This file is produced automatically.\n" \
    " * Do not modify anything in here by hand.\n" \
    " *\n" \
    " * Created from $FreeBSD$\n" \
    " */\n" \
    "\n";

if (pfile) {
	printp(common_head)
	printp("struct vop_vector {")
	printp("\tstruct vop_vector\t*vop_default;")
	printp("\tvop_bypass_t\t*vop_bypass;")
}

if (qfile) {
	printq(common_head)
}

if (hfile) {
	printh(common_head "extern struct vnodeop_desc vop_default_desc;");
	printh("#include \"vnode_if_typedef.h\"")
	printh("#include \"vnode_if_newproto.h\"")
}

if (cfile) {
	printc(common_head \
	    "#include \"opt_kdtrace.h\"\n" \
	    "\n" \
	    "#include <sys/param.h>\n" \
	    "#include <sys/event.h>\n" \
	    "#include <sys/kernel.h>\n" \
	    "#include <sys/mount.h>\n" \
	    "#include <sys/sdt.h>\n" \
	    "#include <sys/systm.h>\n" \
	    "#include <sys/vnode.h>\n" \
	    "\n" \
	    "SDT_PROVIDER_DECLARE(vfs);\n" \
	    "\n" \
	    "struct vnodeop_desc vop_default_desc = {\n" \
	    "	\"default\",\n" \
	    "	0,\n" \
	    "	(vop_bypass_t *)vop_panic,\n" \
	    "	NULL,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "};\n");
}

while ((getline < srcfile) > 0) {
	fnr++;
	if (NF == 0)
		continue;
	if ($1 ~ /^%%/) {
		if (NF != 6 ||
		    $2 !~ /^[a-z]+$/  ||  $3 !~ /^[a-z]+$/  ||
		    $4 !~ /^.$/  ||  $5 !~ /^.$/  ||  $6 !~ /^.$/) {
			die("Invalid %s construction", "%%");
			continue;
		}
		lockdata["vop_" $2, $3, "Entry"] = $4;
		lockdata["vop_" $2, $3, "OK"]    = $5;
		lockdata["vop_" $2, $3, "Error"] = $6;
		continue;
	}

	if ($1 ~ /^%!/) {
		if (NF != 4 ||
		    ($3 != "pre" && $3 != "post")) {
			die("Invalid %s construction", "%!");
			continue;
		}
		lockdata["vop_" $2, $3] = $4;
		continue;
	}
	if ($1 ~ /^#/)
		continue;

	# Get the function name.
	name = $1;
	uname = toupper(name);

	# Start constructing a ktrpoint string
	ctrstr = "\"" uname;
	# Get the function arguments.
	for (numargs = 0; ; ++numargs) {
		if ((getline < srcfile) <= 0) {
			die("Unable to read through the arguments for \"%s\"",
			    name);
		}
		fnr++;
		if ($1 ~ /^\};/)
			break;

		# Delete comments, if any.
		gsub (/\/\*.*\*\//, "");

		# Condense whitespace and delete leading/trailing space.
		gsub(/[[:space:]]+/, " ");
		sub(/^ /, "");
		sub(/ $/, "");

		# Pick off direction.
		if ($1 != "INOUT" && $1 != "IN" && $1 != "OUT")
			die("No IN/OUT direction for \"%s\".", $0);
		dirs[numargs] = $1;
		sub(/^[A-Z]* /, "");

		if ((reles[numargs] = $1) == "WILLRELE")
			sub(/^[A-Z]* /, "");
		else
			reles[numargs] = "WONTRELE";

		# kill trailing ;
		if (sub(/;$/, "") < 1)
			die("Missing end-of-line ; in \"%s\".", $0);

		# pick off variable name
		if ((argp = match($0, /[A-Za-z0-9_]+$/)) < 1)
			die("Missing var name \"a_foo\" in \"%s\".", $0);
		args[numargs] = substr($0, argp);
		$0 = substr($0, 1, argp - 1);

		# what is left must be type
		# remove trailing space (if any)
		sub(/ $/, "");
		types[numargs] = $0;

		# We can do a maximum of 6 arguments to CTR*
		if (numargs <= 6) {
			if (numargs == 0)
				ctrstr = ctrstr "(" args[numargs];
			else
				ctrstr = ctrstr ", " args[numargs];
			if (types[numargs] ~ /\*/)
				ctrstr = ctrstr " 0x%lX";
			else
				ctrstr = ctrstr " %ld";
		}
	}
	if (numargs > 6)
		ctrargs = 6;
	else
		ctrargs = numargs;
	ctrstr = "\tCTR" ctrargs "(KTR_VOP,\n\t    " ctrstr ")\",\n\t    ";
	ctrstr = ctrstr "a->a_" args[0];
	for (i = 1; i < ctrargs; ++i)
		ctrstr = ctrstr ", a->a_" args[i];
	ctrstr = ctrstr ");";

	if (pfile) {
		printp("\t"name"_t\t*"name";")
	}
	if (qfile) {
		printq("struct "name"_args;")
		printq("typedef int "name"_t(struct "name"_args *);\n")
	}

	if (hfile) {
		# Print out the vop_F_args structure.
		printh("struct "name"_args {\n\tstruct vop_generic_args a_gen;");
		for (i = 0; i < numargs; ++i)
			printh("\t" t_spc(types[i]) "a_" args[i] ";");
		printh("};");
		printh("");

		# Print out extern declaration.
		printh("extern struct vnodeop_desc " name "_desc;");
		printh("");

		# Print out function prototypes.
		printh("int " uname "_AP(struct " name "_args *);");
		printh("int " uname "_APV(struct vop_vector *vop, struct " name "_args *);");
		printh("");
		printh("static __inline int " uname "(");
		for (i = 0; i < numargs; ++i) {
			printh("\t" t_spc(types[i]) args[i] \
			    (i < numargs - 1 ? "," : ")"));
		}
		printh("{");
		printh("\tstruct " name "_args a;");
		printh("");
		printh("\ta.a_gen.a_desc = &" name "_desc;");
		for (i = 0; i < numargs; ++i)
			printh("\ta.a_" args[i] " = " args[i] ";");
		printh("\treturn (" uname "_APV("args[0]"->v_op, &a));");
		printh("}");

		printh("");
	}

	if (cfile) {
		# Print out the vop_F_vp_offsets structure.  This all depends
		# on naming conventions and nothing else.
		printc("static int " name "_vp_offsets[] = {");
		# as a side effect, figure out the releflags
		releflags = "";
		vpnum = 0;
		for (i = 0; i < numargs; i++) {
			if (types[i] == "struct vnode *") {
				printc("\tVOPARG_OFFSETOF(struct " name \
				    "_args,a_" args[i] "),");
				if (reles[i] == "WILLRELE") {
					releflags = releflags \
					    "|VDESC_VP" vpnum "_WILLRELE";
				}
				vpnum++;
			}
		}

		sub(/^\|/, "", releflags);
		printc("\tVDESC_NO_OFFSET");
		printc("};");

		printc("\n");
		printc("SDT_PROBE_DEFINE2(vfs, vop, " name ", entry, \"struct vnode *\", \"struct " name "_args *\");\n");
		printc("SDT_PROBE_DEFINE3(vfs, vop, " name ", return, \"struct vnode *\", \"struct " name "_args *\", \"int\");\n");

		# Print out function.
		printc("\nint\n" uname "_AP(struct " name "_args *a)");
		printc("{");
		printc("");
		printc("\treturn(" uname "_APV(a->a_" args[0] "->v_op, a));");
		printc("}");
		printc("\nint\n" uname "_APV(struct vop_vector *vop, struct " name "_args *a)");
		printc("{");
		printc("\tint rc;");
		printc("");
		printc("\tVNASSERT(a->a_gen.a_desc == &" name "_desc, a->a_" args[0]",");
		printc("\t    (\"Wrong a_desc in " name "(%p, %p)\", a->a_" args[0]", a));");
		printc("\twhile(vop != NULL && \\");
		printc("\t    vop->"name" == NULL && vop->vop_bypass == NULL)")
		printc("\t\tvop = vop->vop_default;")
		printc("\tVNASSERT(vop != NULL, a->a_" args[0]", (\"No "name"(%p, %p)\", a->a_" args[0]", a));")
		printc("\tSDT_PROBE(vfs, vop, " name ", entry, a->a_" args[0] ", a, 0, 0, 0);\n");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Entry", "\t");
		add_pre(name);
		printc("\tif (vop->"name" != NULL)")
		printc("\t\trc = vop->"name"(a);")
		printc("\telse")
		printc("\t\trc = vop->vop_bypass(&a->a_gen);")
		printc(ctrstr);
		printc("\tSDT_PROBE(vfs, vop, " name ", return, a->a_" args[0] ", a, rc, 0, 0);\n");
		printc("\tif (rc == 0) {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "OK", "\t\t");
		printc("\t} else {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Error", "\t\t");
		printc("\t}");
		add_post(name);
		printc("\treturn (rc);");
		printc("}\n");

		# Print out the vnodeop_desc structure.
		printc("struct vnodeop_desc " name "_desc = {");
		# printable name
		printc("\t\"" name "\",");
		# flags
		vppwillrele = "";
		for (i = 0; i < numargs; i++) {
			if (types[i] == "struct vnode **" && \
			    reles[i] == "WILLRELE") {
				vppwillrele = "|VDESC_VPP_WILLRELE";
			}
		}

		if (!releflags)
			releflags = "0";
		printc("\t" releflags vppwillrele ",");

		# function to call
		printc("\t(vop_bypass_t *)" uname "_AP,");
		# vp offsets
		printc("\t" name "_vp_offsets,");
		# vpp (if any)
		printc("\t" find_arg_with_type("struct vnode **") ",");
		# cred (if any)
		printc("\t" find_arg_with_type("struct ucred *") ",");
		# thread (if any)
		printc("\t" find_arg_with_type("struct thread *") ",");
		# componentname
		printc("\t" find_arg_with_type("struct componentname *") ",");
		# transport layer information
		printc("};\n");
	}
}

if (pfile)
	printp("};")

if (hfile)
	close(hfile);
if (cfile)
	close(cfile);
if (pfile)
	close(pfile);
close(srcfile);

exit 0;

}

#!/usr/bin/awk -f

#-
# Copyright (c) 1992, 1993
#	The Regents of the University of California.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 4. Neither the name of the University nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

#
#	@(#)vnode_if.sh	8.1 (Berkeley) 6/10/93
# $FreeBSD$
#
# Script to produce VFS front-end sugar.
#
# usage: vnode_if.awk <srcfile> [-c | -h | -p | -q]
#	(where <srcfile> is currently /sys/kern/vnode_if.src)
#	The source file must have a .src extension
#

function usage()
{
	print "usage: vnode_if.awk <srcfile> [-c|-h|-p|-q]";
	exit 1;
}

function die(msg, what)
{
	printf srcfile "(" fnr "): " > "/dev/stderr";
	printf msg "\n", what > "/dev/stderr";
	exit 1;
}

function t_spc(type)
{
	# Append a space if the type is not a pointer
	return (type ~ /\*$/) ? type : type " ";
}

# These are just for convenience ...
function printc(s) {print s > cfile;}
function printh(s) {print s > hfile;}
function printp(s) {print s > pfile;}
function printq(s) {print s > qfile;}

function add_debug_code(name, arg, pos, ind)
{
	if (arg == "vpp")
		star = "*";
	else
		star = "";
	if (lockdata[name, arg, pos] && (lockdata[name, arg, pos] != "-")) {
		printc(ind"ASSERT_VI_UNLOCKED("star"a->a_"arg", \""uname"\");");
		# Add assertions for locking
		if (lockdata[name, arg, pos] == "L")
			printc(ind"ASSERT_VOP_LOCKED(" star "a->a_"arg", \""uname"\");");
		else if (lockdata[name, arg, pos] == "U")
			printc(ind"ASSERT_VOP_UNLOCKED(" star "a->a_"arg", \""uname"\");");
		else if (lockdata[name, arg, pos] == "E")
			printc(ind"ASSERT_VOP_ELOCKED(" star "a->a_"arg", \""uname"\");");
		else if (0) {
			# XXX More checks!
		}
	}
}

function add_pre(name)
{
	if (lockdata[name, "pre"]) {
		printc("\t"lockdata[name, "pre"]"(a);");
	}
}

function add_post(name)
{
	if (lockdata[name, "post"]) {
		printc("\t"lockdata[name, "post"]"(a, rc);");
	}
}

function find_arg_with_type (type)
{
	for (jj = 0; jj < numargs; jj++) {
		if (types[jj] == type) {
			return "VOPARG_OFFSETOF(struct " \
			    name "_args,a_" args[jj] ")";
		}
	}

	return "VDESC_NO_OFFSET";
}

BEGIN{

# Process the command line
for (i = 1; i < ARGC; i++) {
	arg = ARGV[i];
	if (arg !~ /^-[chpq]+$/ && arg !~ /\.src$/)
		usage();
	if (arg ~ /^-.*c/)
		cfile = "vnode_if.c";
	if (arg ~ /^-.*h/)
		hfile = "vnode_if.h";
	if (arg ~ /^-.*p/)
		pfile = "vnode_if_newproto.h";
	if (arg ~ /^-.*q/)
		qfile = "vnode_if_typedef.h";
	if (arg ~ /\.src$/)
		srcfile = arg;
}
ARGC = 1;

if (!cfile && !hfile && !pfile && !qfile)
	exit 0;

if (!srcfile)
	usage();

common_head = \
    "/*\n" \
    " * This file is produced automatically.\n" \
    " * Do not modify anything in here by hand.\n" \
    " *\n" \
    " * Created from $FreeBSD$\n" \
    " */\n" \
    "\n";

if (pfile) {
	printp(common_head)
	printp("struct vop_vector {")
	printp("\tstruct vop_vector\t*vop_default;")
	printp("\tvop_bypass_t\t*vop_bypass;")
}

if (qfile) {
	printq(common_head)
}

if (hfile) {
	printh(common_head "extern struct vnodeop_desc vop_default_desc;");
	printh("#include \"vnode_if_typedef.h\"")
	printh("#include \"vnode_if_newproto.h\"")
}

if (cfile) {
	printc(common_head \
	    "#include <sys/param.h>\n" \
	    "#include <sys/event.h>\n" \
	    "#include <sys/mount.h>\n" \
	    "#include <sys/systm.h>\n" \
	    "#include <sys/vnode.h>\n" \
	    "\n" \
	    "struct vnodeop_desc vop_default_desc = {\n" \
	    "	\"default\",\n" \
	    "	0,\n" \
	    "	(vop_bypass_t *)vop_panic,\n" \
	    "	NULL,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "};\n");
}

while ((getline < srcfile) > 0) {
	fnr++;
	if (NF == 0)
		continue;
	if ($1 ~ /^%%/) {
		if (NF != 6 ||
		    $2 !~ /^[a-z]+$/  ||  $3 !~ /^[a-z]+$/  ||
		    $4 !~ /^.$/  ||  $5 !~ /^.$/  ||  $6 !~ /^.$/) {
			die("Invalid %s construction", "%%");
			continue;
		}
		lockdata["vop_" $2, $3, "Entry"] = $4;
		lockdata["vop_" $2, $3, "OK"]    = $5;
		lockdata["vop_" $2, $3, "Error"] = $6;
		continue;
	}

	if ($1 ~ /^%!/) {
		if (NF != 4 ||
		    ($3 != "pre" && $3 != "post")) {
			die("Invalid %s construction", "%!");
			continue;
		}
		lockdata["vop_" $2, $3] = $4;
		continue;
	}
	if ($1 ~ /^#/)
		continue;

	# Get the function name.
	name = $1;
	uname = toupper(name);

	# Start constructing a ktrpoint string
	ctrstr = "\"" uname;
	# Get the function arguments.
	for (numargs = 0; ; ++numargs) {
		if ((getline < srcfile) <= 0) {
			die("Unable to read through the arguments for \"%s\"",
			    name);
		}
		fnr++;
		if ($1 ~ /^\};/)
			break;

		# Delete comments, if any.
		gsub (/\/\*.*\*\//, "");

		# Condense whitespace and delete leading/trailing space.
		gsub(/[[:space:]]+/, " ");
		sub(/^ /, "");
		sub(/ $/, "");

		# Pick off direction.
		if ($1 != "INOUT" && $1 != "IN" && $1 != "OUT")
			die("No IN/OUT direction for \"%s\".", $0);
		dirs[numargs] = $1;
		sub(/^[A-Z]* /, "");

		if ((reles[numargs] = $1) == "WILLRELE")
			sub(/^[A-Z]* /, "");
		else
			reles[numargs] = "WONTRELE";

		# kill trailing ;
		if (sub(/;$/, "") < 1)
			die("Missing end-of-line ; in \"%s\".", $0);

		# pick off variable name
		if ((argp = match($0, /[A-Za-z0-9_]+$/)) < 1)
			die("Missing var name \"a_foo\" in \"%s\".", $0);
		args[numargs] = substr($0, argp);
		$0 = substr($0, 1, argp - 1);

		# what is left must be type
		# remove trailing space (if any)
		sub(/ $/, "");
		types[numargs] = $0;

		# We can do a maximum of 6 arguments to CTR*
		if (numargs <= 6) {
			if (numargs == 0)
				ctrstr = ctrstr "(" args[numargs];
			else
				ctrstr = ctrstr ", " args[numargs];
			if (types[numargs] ~ /\*/)
				ctrstr = ctrstr " 0x%lX";
			else
				ctrstr = ctrstr " %ld";
		}
	}
	if (numargs > 6)
		ctrargs = 6;
	else
		ctrargs = numargs;
	ctrstr = "\tCTR" ctrargs "(KTR_VOP,\n\t    " ctrstr ")\",\n\t    ";
	ctrstr = ctrstr "a->a_" args[0];
	for (i = 1; i < ctrargs; ++i)
		ctrstr = ctrstr ", a->a_" args[i];
	ctrstr = ctrstr ");";

	if (pfile) {
		printp("\t"name"_t\t*"name";")
	}
	if (qfile) {
		printq("struct "name"_args;")
		printq("typedef int "name"_t(struct "name"_args *);\n")
	}

	if (hfile) {
		# Print out the vop_F_args structure.
		printh("struct "name"_args {\n\tstruct vop_generic_args a_gen;");
		for (i = 0; i < numargs; ++i)
			printh("\t" t_spc(types[i]) "a_" args[i] ";");
		printh("};");
		printh("");

		# Print out extern declaration.
		printh("extern struct vnodeop_desc " name "_desc;");
		printh("");

		# Print out function prototypes.
		printh("int " uname "_AP(struct " name "_args *);");
		printh("int " uname "_APV(struct vop_vector *vop, struct " name "_args *);");
		printh("");
		printh("static __inline int " uname "(");
		for (i = 0; i < numargs; ++i) {
			printh("\t" t_spc(types[i]) args[i] \
			    (i < numargs - 1 ? "," : ")"));
		}
		printh("{");
		printh("\tstruct " name "_args a;");
		printh("");
		printh("\ta.a_gen.a_desc = &" name "_desc;");
		for (i = 0; i < numargs; ++i)
			printh("\ta.a_" args[i] " = " args[i] ";");
		printh("\treturn (" uname "_APV("args[0]"->v_op, &a));");
		printh("}");

		printh("");
	}

	if (cfile) {
		# Print out the vop_F_vp_offsets structure.  This all depends
		# on naming conventions and nothing else.
		printc("static int " name "_vp_offsets[] = {");
		# as a side effect, figure out the releflags
		releflags = "";
		vpnum = 0;
		for (i = 0; i < numargs; i++) {
			if (types[i] == "struct vnode *") {
				printc("\tVOPARG_OFFSETOF(struct " name \
				    "_args,a_" args[i] "),");
				if (reles[i] == "WILLRELE") {
					releflags = releflags \
					    "|VDESC_VP" vpnum "_WILLRELE";
				}
				vpnum++;
			}
		}

		sub(/^\|/, "", releflags);
		printc("\tVDESC_NO_OFFSET");
		printc("};");

		# Print out function.
		printc("\nint\n" uname "_AP(struct " name "_args *a)");
		printc("{");
		printc("");
		printc("\treturn(" uname "_APV(a->a_" args[0] "->v_op, a));");
		printc("}");
		printc("\nint\n" uname "_APV(struct vop_vector *vop, struct " name "_args *a)");
		printc("{");
		printc("\tint rc;");
		printc("");
		printc("\tVNASSERT(a->a_gen.a_desc == &" name "_desc, a->a_" args[0]",");
		printc("\t    (\"Wrong a_desc in " name "(%p, %p)\", a->a_" args[0]", a));");
		printc("\twhile(vop != NULL && \\");
		printc("\t    vop->"name" == NULL && vop->vop_bypass == NULL)")
		printc("\t\tvop = vop->vop_default;")
		printc("\tVNASSERT(vop != NULL, a->a_" args[0]", (\"No "name"(%p, %p)\", a->a_" args[0]", a));")
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Entry", "\t");
		add_pre(name);
		printc("\tif (vop->"name" != NULL)")
		printc("\t\trc = vop->"name"(a);")
		printc("\telse")
		printc("\t\trc = vop->vop_bypass(&a->a_gen);")
		printc(ctrstr);
		printc("\tif (rc == 0) {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "OK", "\t\t");
		printc("\t} else {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Error", "\t\t");
		printc("\t}");
		add_post(name);
		printc("\treturn (rc);");
		printc("}\n");

		# Print out the vnodeop_desc structure.
		printc("struct vnodeop_desc " name "_desc = {");
		# printable name
		printc("\t\"" name "\",");
		# flags
		vppwillrele = "";
		for (i = 0; i < numargs; i++) {
			if (types[i] == "struct vnode **" && \
			    reles[i] == "WILLRELE") {
				vppwillrele = "|VDESC_VPP_WILLRELE";
			}
		}

		if (!releflags)
			releflags = "0";
		printc("\t" releflags vppwillrele ",");

		# function to call
		printc("\t(vop_bypass_t *)" uname "_AP,");
		# vp offsets
		printc("\t" name "_vp_offsets,");
		# vpp (if any)
		printc("\t" find_arg_with_type("struct vnode **") ",");
		# cred (if any)
		printc("\t" find_arg_with_type("struct ucred *") ",");
		# thread (if any)
		printc("\t" find_arg_with_type("struct thread *") ",");
		# componentname
		printc("\t" find_arg_with_type("struct componentname *") ",");
		# transport layer information
		printc("};\n");
	}
}

if (pfile)
	printp("};")

if (hfile)
	close(hfile);
if (cfile)
	close(cfile);
if (pfile)
	close(pfile);
close(srcfile);

exit 0;

}


inject a precisionbook keymap under us|machdep

#! /usr/bin/awk -f
#	$OpenBSD: makemap.awk,v 1.2 2003/05/27 15:31:52 mickey Exp $
#
# Copyright (c) 2003, Miodrag Vallat.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

#
# This script attempts to convert, with minimal hacks and losses, the
# regular PS/2 keyboard (pckbd) layout tables into GSC keyboard (gsckbd)
# layout tables, as almost all scancodes are completely different in the
# GSC world.
#

BEGIN {
	mapnum = 0

	# PS/2 id -> GSCKBD conversion table, or "sanity lossage 101"
	for (i = 0; i < 256; i++)
		conv[i] = -1

	conv[1] = 118
	conv[2] = 22
	conv[3] = 30
	conv[4] = 38
	conv[5] = 37
	conv[6] = 46
	conv[7] = 54
	conv[8] = 61
	conv[9] = 62
	conv[10] = 70
	conv[11] = 69
	conv[12] = 78
	conv[13] = 85
	conv[14] = 102
	conv[15] = 13
	conv[16] = 21
	conv[17] = 29
	conv[18] = 36
	conv[19] = 45
	conv[20] = 44
	conv[21] = 53
	conv[22] = 60
	conv[23] = 67
	conv[24] = 68
	conv[25] = 77
	conv[26] = 84
	conv[27] = 91
	conv[28] = 90
	conv[29] = 20
	conv[30] = 28
	conv[31] = 27
	conv[32] = 35
	conv[33] = 43
	conv[34] = 52
	conv[35] = 51
	conv[36] = 59
	conv[37] = 66
	conv[38] = 75
	conv[39] = 76
	conv[40] = 82
	conv[41] = 14
	conv[42] = 18
	conv[43] = 93
	conv[44] = 26
	conv[45] = 34
	conv[46] = 33
	conv[47] = 42
	conv[48] = 50
	conv[49] = 49
	conv[50] = 58
	conv[51] = 65
	conv[52] = 73
	conv[53] = 74
	conv[54] = 89
	conv[55] = 124
	conv[56] = 17
	conv[57] = 41
	conv[58] = 88
	conv[59] = 5
	conv[60] = 6
	conv[61] = 4
	conv[62] = 12
	conv[63] = 3
	conv[64] = 11
	conv[65] = 131
	conv[66] = 10
	conv[67] = 1
	conv[68] = 9
	conv[69] = 119
	conv[70] = 126
	conv[71] = 108
	conv[72] = 117
	conv[73] = 125
	conv[74] = 123
	conv[75] = 107
	conv[76] = 115
	conv[77] = 116
	conv[78] = 121
	conv[79] = 105
	conv[80] = 114
	conv[81] = 122
	conv[82] = 112
	conv[83] = 113
	conv[86] = 97
	conv[87] = 120
	conv[88] = 7
	# 112 used by jp
	# 115 used by jp and br
	# 121 used by jp
	# 123 used by jp
	# 125 used by jp
	conv[127] = 127
	conv[156] = 218
	conv[157] = 148
	# Print Screen produces E0 12 E0 7C when pressed, then E0 7C E0 12
	# when released.  Ignore the E0 12 code and match only on E0 7C
	conv[170] = 252
	conv[181] = 202
	conv[184] = 145
	conv[198] = 254
	conv[199] = 236
	conv[200] = 245
	conv[201] = 253
	conv[203] = 235
	conv[205] = 244
	conv[207] = 233
	conv[208] = 242
	conv[209] = 250
	conv[210] = 240
	conv[211] = 113
}
NR == 1 {
	VERSION = $0
	gsub("\\$", "", VERSION)

	printf("/*\t\$OpenBSD\$\t*/\n\n")
	printf("/*\n")
	printf(" * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.\n")
	printf(" *\n")
	printf(" * generated from:\n")
	printf(" */\n")
	print VERSION

	next
}
$1 == "#include" {
	if ($2 == "<dev/pckbc/wskbdmap_mfii.h>")
		print "#include <hppa/gsc/gsckbdmap.h>"
	else
		printf("#include %s\n", $2)

	next
}
$1 == "#define" || $1 == "#undef" {
	print $0
	next
}
/pckbd/ {
	gsub("pckbd", "gsckbd", $0)
	print $0
	next
}
/KC/ {
	sidx = substr($1, 4, length($1) - 5)
	orig = int(sidx)
	id = conv[orig]

	# 183 is another Print Screen...
	if (orig == 183)
		next

	if (id == -1) {
		printf("/* initially KC(%d),", orig)
		for (f = 2; f <= NF; f++) {
			if ($f != "/*" && $f != "*/")
				printf("\t%s", $f)
		}
		printf("\t*/\n")
	} else {
		printf("    KC(%d),", id)
		for (f = 2; f <= NF; f++) {
			printf("\t%s", $f)
		}
		printf("\n")
	}

	next
}
/};/ {
	if (mapnum == 0) {
		# Add 241 to the US map...
		print "    KC(241),\tKS_Delete"
		print $0
		print "\nstatic const keysym_t pckbd_keydesc_precisionbook[] = {"
		print "/*  pos      command\t\tnormal		shifted */"
		print "    KC(7),   KS_Cmd_Screen0,\tKS_f1,"
		print "    KC(15),  KS_Cmd_Screen1,\tKS_f2,"
		print "    KC(23),  KS_Cmd_Screen2,\tKS_f3,"
		print "    KC(31),  KS_Cmd_Screen3,\tKS_f4,"
		print "    KC(39),  KS_Cmd_Screen4,\tKS_f5,"
		print "    KC(47),  KS_Cmd_Screen5,\tKS_f6,"
		print "    KC(55),  KS_Cmd_Screen6,\tKS_f7,"
		print "    KC(63),  KS_Cmd_Screen7,\tKS_f8,"
		print "    KC(71),  KS_Cmd_Screen8,\tKS_f9,"
		print "    KC(79),  KS_Cmd_Screen9,\tKS_f10,"
		print "    KC(86),  KS_Cmd_Screen10,\tKS_f11,"
		print "    KC(94),  KS_Cmd_Screen11,\tKS_f12,"
		print "    KC(8),   KS_Cmd_Debugger,\tKS_Escape,"
		print "    KC(87),\t\t\tKS_Print_Screen,"
		print "    KC(92),\t\t\tKS_backslash,\tKS_bar,"
		print "    KC(96),\t\t\tKS_KP_Down,\tKS_KP_2,"
		print "    KC(95),\t\t\tKS_Hold_Screen,"
		print "    KC(97),\t\t\tKS_KP_Left,\tKS_KP_4,"
		print "    KC(98),\t\t\tKS_Pause, /* Break */"
		print "    KC(99),\t\t\tKS_KP_Up,\tKS_KP_8,"
		print "    KC(100),\t\t\tKS_KP_Delete,\tKS_KP_Decimal,"
		print "    KC(101),\t\t\tKS_KP_End,\tKS_KP_1,"
		print "    KC(103),\t\t\tKS_KP_Insert,\tKS_KP_0,"
		print "    KC(106),\t\t\tKS_KP_Right,\tKS_KP_6,"
		print "    KC(109),\t\t\tKS_KP_Next,\tKS_KP_3,"
		print "    KC(110),\t\t\tKS_KP_Home,\tKS_KP_7,"
		print "    KC(111),\t\t\tKS_KP_Prior,\tKS_KP_9,"
		print "    KC(20),\t\t\tKS_Caps_Lock,"
		print "    KC(17),  KS_Cmd1,\t\tKS_Control_L,"
		print "    KC(88),  KS_Cmd1,\t\tKS_Control_R,"
		print "    KC(25),  KS_Cmd2,\t\tKS_Alt_L,"
		print "    KC(57),  KS_Cmd2,\t\tKS_Alt_R,\tKS_Multi_key,"
		print "    KC(139),\t\t\tKS_Meta_L,"
		print "    KC(140),\t\t\tKS_Meta_R,"
	}
	mapnum++
}
/{0, 0, 0, 0}/ {
	printf("\tKBD_MAP(KB_US | KB_MACHDEP,\tKB_US,\tpckbd_keydesc_precisionbook),\n");
}
{
	print $0
}

#! /usr/bin/awk -f
#	$OpenBSD: makemap.awk,v 1.1 2003/02/16 01:42:49 miod Exp $
#
# Copyright (c) 2003, Miodrag Vallat.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

#
# This script attempts to convert, with minimal hacks and losses, the
# regular PS/2 keyboard (pckbd) layout tables into GSC keyboard (gsckbd)
# layout tables, as almost all scancodes are completely different in the
# GSC world.
#

BEGIN {
	mapnum = 0

	# PS/2 id -> GSCKBD conversion table, or "sanity lossage 101"
	for (i = 0; i < 256; i++)
		conv[i] = -1

	conv[1] = 118
	conv[2] = 22
	conv[3] = 30
	conv[4] = 38
	conv[5] = 37
	conv[6] = 46
	conv[7] = 54
	conv[8] = 61
	conv[9] = 62
	conv[10] = 70
	conv[11] = 69
	conv[12] = 78
	conv[13] = 85
	conv[14] = 102
	conv[15] = 13
	conv[16] = 21
	conv[17] = 29
	conv[18] = 36
	conv[19] = 45
	conv[20] = 44
	conv[21] = 53
	conv[22] = 60
	conv[23] = 67
	conv[24] = 68
	conv[25] = 77
	conv[26] = 84
	conv[27] = 91
	conv[28] = 90
	conv[29] = 20
	conv[30] = 28
	conv[31] = 27
	conv[32] = 35
	conv[33] = 43
	conv[34] = 52
	conv[35] = 51
	conv[36] = 59
	conv[37] = 66
	conv[38] = 75
	conv[39] = 76
	conv[40] = 82
	conv[41] = 14
	conv[42] = 18
	conv[43] = 93
	conv[44] = 26
	conv[45] = 34
	conv[46] = 33
	conv[47] = 42
	conv[48] = 50
	conv[49] = 49
	conv[50] = 58
	conv[51] = 65
	conv[52] = 73
	conv[53] = 74
	conv[54] = 89
	conv[55] = 124
	conv[56] = 17
	conv[57] = 41
	conv[58] = 88
	conv[59] = 5
	conv[60] = 6
	conv[61] = 4
	conv[62] = 12
	conv[63] = 3
	conv[64] = 11
	conv[65] = 131
	conv[66] = 10
	conv[67] = 1
	conv[68] = 9
	conv[69] = 119
	conv[70] = 126
	conv[71] = 108
	conv[72] = 117
	conv[73] = 125
	conv[74] = 123
	conv[75] = 107
	conv[76] = 115
	conv[77] = 116
	conv[78] = 121
	conv[79] = 105
	conv[80] = 114
	conv[81] = 122
	conv[82] = 112
	conv[83] = 113
	conv[86] = 97
	conv[87] = 120
	conv[88] = 7
	# 112 used by jp
	# 115 used by jp and br
	# 121 used by jp
	# 123 used by jp
	# 125 used by jp
	conv[127] = 127
	conv[156] = 218
	conv[157] = 148
	# Print Screen produces E0 12 E0 7C when pressed, then E0 7C E0 12
	# when released.  Ignore the E0 12 code and match only on E0 7C
	conv[170] = 252
	conv[181] = 202
	conv[184] = 145
	conv[198] = 254
	conv[199] = 236
	conv[200] = 245
	conv[201] = 253
	conv[203] = 235
	conv[205] = 244
	conv[207] = 233
	conv[208] = 242
	conv[209] = 250
	conv[210] = 240
	conv[211] = 113
}
NR == 1 {
	VERSION = $0
	gsub("\\$", "", VERSION)

	printf("/*\t\$OpenBSD\$\t*/\n\n")
	printf("/*\n")
	printf(" * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.\n")
	printf(" *\n")
	printf(" * generated from:\n")
	printf(" */\n")
	print VERSION

	next
}
$1 == "#include" {
	if ($2 == "<dev/pckbc/wskbdmap_mfii.h>")
		print "#include <hppa/gsc/gsckbdmap.h>"
	else
		printf("#include %s\n", $2)

	next
}
$1 == "#define" || $1 == "#undef" {
	print $0
	next
}
/pckbd/ {
	gsub("pckbd", "gsckbd", $0)
	print $0
	next
}
/KC/ {
	sidx = substr($1, 4, length($1) - 5)
	orig = int(sidx)
	id = conv[orig]

	# 183 is another Print Screen...
	if (orig == 183)
		next

	if (id == -1) {
		printf("/* initially KC(%d),", orig)
		for (f = 2; f <= NF; f++) {
			if ($f != "/*" && $f != "*/")
				printf("\t%s", $f)
		}
		printf("\t*/\n")
	} else {
		printf("    KC(%d),", id)
		for (f = 2; f <= NF; f++) {
			printf("\t%s", $f)
		}
		printf("\n")
	}

	next
}
/};/ {
	if (mapnum == 0) {
		# Add 241 to the US map...
		printf("    KC(241),\tKS_Delete,\n")
	}
	mapnum++
}
{
	print $0
}


version 0.8.20

version 0.8.20

BEGIN {
    Name        = "Translate Shell"
    Description = "Google Translate to serve as a command-line tool"
    Version     = "0.8.20"
    Command     = "trans"
    EntryPoint  = "translate.awk"
}

BEGIN {
    Name        = "Translate Shell"
    Description = "Google Translate to serve as a command-line tool"
    Version     = "0.8.19"
    Command     = "trans"
    EntryPoint  = "translate.awk"
}


Cleaned up Amazon URLs

Cleaned up Amazon URLs

# Helper for converting links copied and pasted from a webpage
#
# Since there is no way to paste an embedded hyperlink into a text file,
# paste the link and the title on two consecutive lines, e.g.
#
# https://www.netflix.com/title/80988955
# Death in Paradise, S06E01, Erupting in Murder
#
# https://www.netflix.com/title/80174814
# Borderliner: Season 1: "Milla’s Future"
#
# https://www.netflix.com/title/80203122
# Luxury Travel Show: Season 1: "Chiang Mai & Eze"
#
# Paste them in the same order they are in the Netflix "viewing activity" page

# https://www.amazon.com/gp/video/detail/B07XZ8G9WJ/ref=stream_prime_hd_ep?autoplay=1&t=0
/ref=stream_prime/ {
    sub (/\/ref=stream.*/,"")
}
/^https:/ {
    link = $0
    if ((getline title) > 0) {
        gsub (/"/,"\"\"",title)
        printf ("=HYPERLINK(\"%s\";\"%s\")\n",link,title)
    }
}

# Helper for converting links copied and pasted from a webpage
#
# Since there is no way to paste an embedded hyperlink into a text file,
# paste the link and the title on two consecutive lines, e.g.
#
# https://www.netflix.com/title/80988955
# Death in Paradise, S06E01, Erupting in Murder
#
# https://www.netflix.com/title/80174814
# Borderliner: Season 1: "Milla’s Future"
#
# https://www.netflix.com/title/80203122
# Luxury Travel Show: Season 1: "Chiang Mai & Eze"
#
# Paste them in the same order they are in the Netflix "viewing activity" page

/^https:/ {
    link = $0
    if ((getline title) > 0) {
        gsub (/"/,"\"\"",title)
        printf ("=HYPERLINK(\"%s\";\"%s\")\n",link,title)
    }
}


[bin] Add comment to parse-git-blame.awk to mention doesn't work with OSX awk

#!/usr/bin/env awk -f
# Parses `git blame -p` to produce a Github-like blame view, where consecutive lines affected
# by the same commit are collapsed.
#
# Doesn't work with OSX awk if there are accents and other interesting UTF-8 characters.
# Recommend using GNU awk.

BEGIN {
  # ensure lines is an array
  lines[0] = "test";
  delete lines[0];

  # First column width
  col_size = 32;
  if(ENVIRON["COLUMN_SIZE"]) {
    col_size = ENVIRON["COLUMN_SIZE"];
  }

  if(ENVIRON["COLORED"] != "0") {
    reset = "\033[0m";
    red = "\033[0;31m";
    green = "\033[0;32m";
    yellow = "\033[0;33m";
    blue = "\033[0;34m";
    gray = "\033[0;90m";
  }
}

$1 == "author" { author = $2 " " $3 }
$1 == "summary" { summary[sha] = substr($0, length($1) + 2) }
$1 ~ /[0-9a-f]{40}/ && $1 != sha {
  dump_lines();
  sha = $1;
}

substr($0, 1, 1) == "\t" {
  lines[length(lines) + 1] = substr($0, 2);
}

END { dump_lines() }

#-----------------------------------------------------------------------
# Supporting functions
#-----------------------------------------------------------------------
function max(a, b) {
  return a > b ? a : b;
}

function dump_lines() {
  if(length(lines) > 0) {
    for(idx = 1; idx <= max(4, length(lines)); ++idx) {
      initial = substr(summary[sha], 1 + col_size*(idx - 3));
      initial_color = green;
      if(idx == 1) {
        initial = sha;
        initial_color = yellow;
      } else if(idx == 2) {
        initial = author;
        initial_color = blue;
      }

      line_str = idx <= length(lines) ? ++curent_line : "";
      printf( \
        initial_color "%-*.*s" gray " │ %4s │ " reset "%s\n", \
        col_size, col_size, initial, line_str, lines[idx] \
      );
    }
    delete lines;
  }
}

#!/usr/bin/env awk -f
BEGIN {
  # ensure lines is an array
  lines[0] = "test";
  delete lines[0];

  # First column width
  col_size = 32;
  if(ENVIRON["COLUMN_SIZE"]) {
    col_size = ENVIRON["COLUMN_SIZE"];
  }

  if(ENVIRON["COLORED"] != "0") {
    reset = "\033[0m";
    red = "\033[0;31m";
    green = "\033[0;32m";
    yellow = "\033[0;33m";
    blue = "\033[0;34m";
    gray = "\033[0;90m";
  }
}

$1 == "author" { author = $2 " " $3 }
$1 == "summary" { summary[sha] = substr($0, length($1) + 2) }
$1 ~ /[0-9a-f]{40}/ && $1 != sha {
  dump_lines();
  sha = $1;
}

substr($0, 1, 1) == "\t" {
  lines[length(lines) + 1] = substr($0, 2);
}

END { dump_lines() }

#-----------------------------------------------------------------------
# Supporting functions
#-----------------------------------------------------------------------
function max(a, b) {
  return a > b ? a : b;
}

function dump_lines() {
  if(length(lines) > 0) {
    for(idx = 1; idx <= max(4, length(lines)); ++idx) {
      initial = substr(summary[sha], 1 + col_size*(idx - 3));
      initial_color = green;
      if(idx == 1) {
        initial = sha;
        initial_color = yellow;
      } else if(idx == 2) {
        initial = author;
        initial_color = blue;
      }

      line_str = idx <= length(lines) ? ++curent_line : "";
      printf( \
        initial_color "%-*.*s" gray " │ %4s │ " reset "%s\n", \
        col_size, col_size, initial, line_str, lines[idx] \
      );
    }
    delete lines;
  }
}


Fix valgrind script

Fix valgrind script

The script was not correctly handling the number in the form 3,120. The
',' was breaking the counter.

#!/bin/awk
BEGIN {
	leak = 0
	reachable = 0
}

$0 ~ /==.*==.*definitely lost: .* bytes in .* blocks/ {
	sub(",", "", $4)
	leak += $4
}

$0 ~ /==.*==.*indirectly lost: .* bytes in .* blocks/ {
	sub(",", "", $4)
	leak += $4
}

$0 ~ /==.*==.*possibly lost: .* bytes in .* blocks/ {
	sub(",", "", $4)
	leak += $4
}

$0 ~ /==.*==.*still reachable: .* bytes in .* blocks/ {
	sub(",", "", $4)
	reachable += $4
}

END {
	printf "%d;%d", leak, reachable
}

#!/bin/awk
BEGIN {
	leak = 0
	reachable = 0
}

$0 ~ /==.*==.*definitely lost: .* bytes in .* blocks/ {
	leak += $4
}

$0 ~ /==.*==.*indirectly lost: .* bytes in .* blocks/ {
	leak += $4
}

$0 ~ /==.*==.*possibly lost: .* bytes in .* blocks/ {
	leak += $4
}

$0 ~ /==.*==.*still reachable: .* bytes in .* blocks/ {
	reachable += $4
}

END {
	printf "%d;%d", leak, reachable
}


addr_utils.awk	-- deal with "delta" ranges, ie ranges where the 2d value is just new suffix of the 1st: 945-51 -> 945-951

function AU_parse(options, addr, result, states, towns, st_types, dirs, st_ords,   nf, ary, i, f_st, f_twn, nf2, ary2, b1, e1, name, street, quals, town, state, work) {

	result["status"] = "B"
	result["emsg"  ] = ""
	result["name"  ] = ""
	result["street"] = ""
	result["quals" ] = ""
	result["town"  ] = ""
	result["state" ] = ""

	nf = split(addr, ary, ",")
	for(i = 1; i <= nf; i++){
		sub(/^ */, "", ary[i])
		sub(/ *$/, "", ary[i])
	}

	# US only for now, so queries never have a country, but replies do
	if(options["rply"]){
		if(ary[nf] == "United States of America")
			nf--
	}

	# TODO: move this to a separate function, may AU_expand()
	if(!options["rply"]){
		# As a convenience to the user, given that nearly all the addresses
		# are in small set of nearby towns, the minimal query address can be
		#
		#	street, TOWN
		#
		# where TOWN is an abbreviation for town, state. Example: PA -> Palo Alto, CA
		if(nf < 2){
			result["emsg"] = "short.addr"
			return 1
		}
		# expand town if needed
		if(ary[nf] in towns){
			nf2 = split(towns[ary[nf]], ary2, ",")
			if(nf2 != 2){
				result["emsg"] = "bad.town"
				return  1
			}
			for(i = 1; i <= nf2; i++){
				sub(/^  */, "", ary2[i])
				sub(/  *$/, "", ary2[i])
			}
			ary[nf] = ary2[1]
			ary[nf+1] = ary2[2]
			nf++
		}
	}

	# At this point, all addresses must have at least three fields: street, town, state
	if(nf < 3){
		result["emsg"] = "short.addr"
		return 1
	}

	# check for a valid state, ignoring zip code
	if(!(substr(ary[nf], 1, 2) in states)){
		result["emsg"] = "not.US"
		return 1
	}else
		state = ary[nf]
	town = ary[nf-1]

	# find the street.
	# street is 1) 1st elt of ary[1:nf-2] that begins w/number & ends w/st_type or 2) last elt of ary[1:nf-2] that begins w/number
	f_st = 0
	for(i = 1; i < nf - 1; i++){
		if(ary[i] ~ /^[1-9]/){
			nf2 = split(ary[i], ary2, /  */)
			if(ary2[nf2] in st_types){
				f_st = i
				break
			}
			f_st = i
		}
	}
	if(f_st == 0){
		result["emsg"] = "no.street"
		return 1
	}

	# The name is what precedes f_st.  if f_st == 1, set name to user supplied default value
	if(f_st == 1)
		name = options["no_name"]
	else{
		name = ary[1]
		for(i = 2; i < f_st; i++)
			name = name ", " ary[i]
	}

	# Street qualifiers are those fields, if any, that follow f_st and precede nf
	if(nf - f_st > 2){
		quals = ary[f_st+1]
		for(i = f_st+2; i < nf - 1; i++)
			quals = quals ", " ary[i]
	}else
		quals = ""

	# clean up street
	nf2 = split(ary[f_st], ary2, /  */)
	if(nf2 < 2){
		result["emsg"] = "short.street"
		return 1
	}

	# Street Hacks: Probably should just compare ignore case but not ready for that step yet
	# change el Camino X to El Camino X
	# change el Monte X  to El Monte X
	if(options["do_subs"]){
		if(nf2 > 3){
			if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Camino")
				ary2[nf2-2] = "El"
			else if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Monte")
				ary2[nf2-2] =  "El"
		}
	}

	# Street should be num [ dir ] str [ st ] [ dir ]
	street = ary2[1]
	if(options["do_subs"]){
		# This is amusing.  What is South Court? Is is S. Court or South Ct.  No idea so leave such streets in long form
		if(nf2 == 3 && (ary2[2] in dirs) && (ary2[3] in st_types)){
			for(i = 2; i <= nf2; i++)
				street = street " " ary2[i]
		}else{
			# handle any leading direction
			if(ary2[2] in dirs){
				street = street " " dirs[ary2[2]]
				b1 = 3
			}else
				b1 = 2
			# detect trailing direction
			e1 = ary2[nf2] in dirs ? nf2-1 : nf2
			for(i = b1; i < e1; i++){
				if(i == b1)
					street = street ((ary2[i] in st_ords) ? (" " st_ords[ary2[i]]) : (" " ary2[i]))
				else
					street = street " " ary2[i]
			}
			street = street ((ary2[e1] in st_types) ? (" " st_types[ary2[e1]]) : (" " ary2[e1]))
			# handle any trailing direction
			if(e1 < nf2)
				street = street " " dirs[ary2[nf2]]
		}
	}else{
		for(i = 2; i <= nf2; i++)
			street = street " " ary2[i]
	}

	# TODO: this belongs in a separate function
	if(options["rply"]){
		if(town in towns)
			town = towns[town]
	}

	result["status"] = "G"
	result["name"  ] = name
	result["street"] = street
	result["quals" ] = quals
	result["town"  ] = town
	result["state" ] = state

	return 0
}
function AU_get_addr_data(addr_info, key, data,   k, keys, nk, i) {

	n_data = 0
	for(k in addr_info){
		nk = split(k, keys, SUBSEP)
		if(keys[1] == key){
			data[keys[2]] = addr_info[k]
			n_data++
		}
	}
	return n_data
}
function AU_match(options, cand, ref,   nc_fields, c_fields, nr_fields, r_fields, i, nc_rtab, c_rtab, nr_rtab, r_rtab, nr_nwords, r_nwords, nc_nwords, c_nwords) {

	if(options["verbose"]){
		printf("ref.name    = %s\n", ref["name"]) > "/dev/stderr"
		printf("ref.street  = %s\n", ref["street"]) > "/dev/stderr"
		printf("ref.town    = %s\n", ref["town"]) > "/dev/stderr"
		printf("ref.state   = %s\n", ref["state"]) > "/dev/stderr"
		printf("cand.name   = %s\n", cand["name"]) > "/dev/stderr"
		printf("cand.street = %s\n", cand["street"]) > "/dev/stderr"
		printf("cand.town   = %s\n", cand["town"]) > "/dev/stderr"
		printf("cand.state  = %s\n", cand["state"]) > "/dev/stderr"
	}

	if(cand["state"] != ref["state"]){
		if(options["ign_zip"]){
			if(substr(cand["state"], 1, 2) != substr(ref["state"], 1, 2))
				return 0
		}else
			return 0
	}

	if(cand["town"] != ref["town"])
		return 0

	# street numbers and/or ranges, etc is 1st field
	nc_fields = split(cand["street"], c_fields, /  */)
	nr_fields = split(ref["street"], r_fields, /  */)

	# check that non-number parts of streets agree
	if(nc_fields != nr_fields)
		return 0
	for(i = 2; i <= nc_fields; i++){
		if(c_fields[i] != r_fields[i])
			return 0
	}

	nc_rtab = AU_get_rtab(c_fields[1], c_rtab)
	if(nc_rtab == 0)
		return 0

	nr_rtab = AU_get_rtab(r_fields[1], r_rtab)
	if(nr_rtab == 0)
		return 0

	if(!AU_rtabs_intersect(nc_rtab, c_rtab, nr_rtab, r_rtab))
		return 0

	# deal with names
	if(cand["name"] == ref["name"]){
		return 3
	}else if(cand["name"] == ""){	# this is an unannotated address
		return 1
	}else if(ref["name"] == options["no_name"]){	# many destinations are just street, town, state
		return 1
	}else{	# check if 1st words match; but no matter what this is a match
		nc_nwords = split(cand["name"], c_nwords, /  */)
		nr_nwords = split(ref["name"], r_nwords, /  */)
		return c_nwords[1] == r_nwords[1] ? 2 : 1
	}
}
function AU_get_rtab(street, rtab,   n_rtab, i, nw, work, l_pfx, e_work2) {

	n_rtab = split(street, rtab, ";")
	for(i = 1; i <= n_rtab; i++){
		nw = split(rtab[i], work, "-")
		if(nw > 1){
			rtab[i, 1] = work[1] + 0	# force number
			rtab[i, 2] = work[2] + 0	# force number

			if(rtab[i,1] > rtab[i,2]){
				# Is this a "delta" range where the 2d value shows only the different address suffix?
				# If so try to fix it
				# For example 945-51 would be expanded to 945-951 which is good, but
				# 945-37 would be expanded to 945-937 which is still wrong.
				l_pfx = length(work[1]) - length(work[2])
				e_work2 = ((l_pfx > 0) ? ((substr(work[1], 1, l_pfx) work[2])) : work[2]) + 0
				# did we fix it?
				if(rtab[i, 1] > e_work2){
					printf("ERROR: bad range 1st precedes 2nd: %d-%d\n", rtab[i, 1], rtab[i, 2]) > "/dev/stderr"
					return 0
				}else
					rtab[i, 2] = e_work2
			}

			rtab[i, "rng"] = 1
			if(rtab[i, 1] % 2 != rtab[i, 2] % 2){
				printf("ERROR: bad range: odd/even %d-%d\n", rtab[i, 1], rtab[i, 2]) > "/dev/stderr"
				return 0
			}
		}else{
			rtab[i] = work[1] + 0	# force number
			rtab[i, "rng"] = 0
		}
	}
	return n_rtab
}
function AU_rtabs_intersect(n_rtab1, rtab1, n_rtab2, rtab2,   i, j) {

	# the most common case: number v number
	if(n_rtab1 == 1 && !rtab1[1, "rng"] && n_rtab2 == 1 && !rtab2[1, "rng"])
		return rtab1[1] == rtab2[1]

	for(i = 1; i <= n_rtab1; i++){
		for(j = 1; j <= n_rtab2; j++){
			if(!rtab1[i, "rng"]){
				if(!rtab2[j, "rng"]){	# number v number
					if(rtab1[i] == rtab2[j])
						return 1
				}else{			# number v range
					if(rtab1[i] >= rtab2[j, 1] && rtab1[i] <= rtab2[j, 2])
						return 1
				}
			}else if(!rtab2[j, "rng"]){	# range v number
				if(rtab2[j] >= rtab1[i, 1] && rtab2[j] <= rtab1[i, 2])
					return 1
			}else{				# range v range
				if(!(rtab1[i, 2] < rtab2[j, 1] || rtab1[i, 1] > rtab2[j, 2]))
					return 1
			}
		}
	}
	return 0
}
function AU_rtab_dump(file, n_rtab, rtab,   i) {

	printf("n_rtab = %d {\n", n_rtab) > file
	for(i = 1; i <= n_rtab; i++){
		if(rtab[i, "rng"])
			printf("\trtab[%d, 1-2] = %d-%d\n", i, rtab[i, 1], rtab[i, 2]) > file
		else
			printf("\trtab[%d     ] = %d\n", i, rtab[i]) > file
	}
	printf("}\n") > file
}

function AU_parse(options, addr, result, states, towns, st_types, dirs, st_ords,   nf, ary, i, f_st, f_twn, nf2, ary2, b1, e1, name, street, quals, town, state, work) {

	result["status"] = "B"
	result["emsg"  ] = ""
	result["name"  ] = ""
	result["street"] = ""
	result["quals" ] = ""
	result["town"  ] = ""
	result["state" ] = ""

	nf = split(addr, ary, ",")
	for(i = 1; i <= nf; i++){
		sub(/^ */, "", ary[i])
		sub(/ *$/, "", ary[i])
	}

	# US only for now, so queries never have a country, but replies do
	if(options["rply"]){
		if(ary[nf] == "United States of America")
			nf--
	}

	# TODO: move this to a separate function, may AU_expand()
	if(!options["rply"]){
		# As a convenience to the user, given that nearly all the addresses
		# are in small set of nearby towns, the minimal query address can be
		#
		#	street, TOWN
		#
		# where TOWN is an abbreviation for town, state. Example: PA -> Palo Alto, CA
		if(nf < 2){
			result["emsg"] = "short.addr"
			return 1
		}
		# expand town if needed
		if(ary[nf] in towns){
			nf2 = split(towns[ary[nf]], ary2, ",")
			if(nf2 != 2){
				result["emsg"] = "bad.town"
				return  1
			}
			for(i = 1; i <= nf2; i++){
				sub(/^  */, "", ary2[i])
				sub(/  *$/, "", ary2[i])
			}
			ary[nf] = ary2[1]
			ary[nf+1] = ary2[2]
			nf++
		}
	}

	# At this point, all addresses must have at least three fields: street, town, state
	if(nf < 3){
		result["emsg"] = "short.addr"
		return 1
	}

	# check for a valid state, ignoring zip code
	if(!(substr(ary[nf], 1, 2) in states)){
		result["emsg"] = "not.US"
		return 1
	}else
		state = ary[nf]
	town = ary[nf-1]

	# find the street.
	# street is 1) 1st elt of ary[1:nf-2] that begins w/number & ends w/st_type or 2) last elt of ary[1:nf-2] that begins w/number
	f_st = 0
	for(i = 1; i < nf - 1; i++){
		if(ary[i] ~ /^[1-9]/){
			nf2 = split(ary[i], ary2, /  */)
			if(ary2[nf2] in st_types){
				f_st = i
				break
			}
			f_st = i
		}
	}
	if(f_st == 0){
		result["emsg"] = "no.street"
		return 1
	}

	# The name is what precedes f_st.  if f_st == 1, set name to user supplied default value
	if(f_st == 1)
		name = options["no_name"]
	else{
		name = ary[1]
		for(i = 2; i < f_st; i++)
			name = name ", " ary[i]
	}

	# Street qualifiers are those fields, if any, that follow f_st and precede nf
	if(nf - f_st > 2){
		quals = ary[f_st+1]
		for(i = f_st+2; i < nf - 1; i++)
			quals = quals ", " ary[i]
	}else
		quals = ""

	# clean up street
	nf2 = split(ary[f_st], ary2, /  */)
	if(nf2 < 2){
		result["emsg"] = "short.street"
		return 1
	}

	# Street Hacks: Probably should just compare ignore case but not ready for that step yet
	# change el Camino X to El Camino X
	# change el Monte X  to El Monte X
	if(options["do_subs"]){
		if(nf2 > 3){
			if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Camino")
				ary2[nf2-2] = "El"
			else if(ary2[nf2-2] == "el" && ary2[nf2-1] == "Monte")
				ary2[nf2-2] =  "El"
		}
	}

	# Street should be num [ dir ] str [ st ] [ dir ]
	street = ary2[1]
	if(options["do_subs"]){
		# This is amusing.  What is South Court? Is is S. Court or South Ct.  No idea so leave such streets in long form
		if(nf2 == 3 && (ary2[2] in dirs) && (ary2[3] in st_types)){
			for(i = 2; i <= nf2; i++)
				street = street " " ary2[i]
		}else{
			# handle any leading direction
			if(ary2[2] in dirs){
				street = street " " dirs[ary2[2]]
				b1 = 3
			}else
				b1 = 2
			# detect trailing direction
			e1 = ary2[nf2] in dirs ? nf2-1 : nf2
			for(i = b1; i < e1; i++){
				if(i == b1)
					street = street ((ary2[i] in st_ords) ? (" " st_ords[ary2[i]]) : (" " ary2[i]))
				else
					street = street " " ary2[i]
			}
			street = street ((ary2[e1] in st_types) ? (" " st_types[ary2[e1]]) : (" " ary2[e1]))
			# handle any trailing direction
			if(e1 < nf2)
				street = street " " dirs[ary2[nf2]]
		}
	}else{
		for(i = 2; i <= nf2; i++)
			street = street " " ary2[i]
	}

	# TODO: this belongs in a separate function
	if(options["rply"]){
		if(town in towns)
			town = towns[town]
	}

	result["status"] = "G"
	result["name"  ] = name
	result["street"] = street
	result["quals" ] = quals
	result["town"  ] = town
	result["state" ] = state

	return 0
}
function AU_get_addr_data(addr_info, key, data,   k, keys, nk, i) {

	n_data = 0
	for(k in addr_info){
		nk = split(k, keys, SUBSEP)
		if(keys[1] == key){
			data[keys[2]] = addr_info[k]
			n_data++
		}
	}
	return n_data
}
function AU_match(options, cand, ref,   nc_fields, c_fields, nr_fields, r_fields, i, nc_rtab, c_rtab, nr_rtab, r_rtab, nr_nwords, r_nwords, nc_nwords, c_nwords) {

	if(options["verbose"]){
		printf("ref.name    = %s\n", ref["name"]) > "/dev/stderr"
		printf("ref.street  = %s\n", ref["street"]) > "/dev/stderr"
		printf("ref.town    = %s\n", ref["town"]) > "/dev/stderr"
		printf("ref.state   = %s\n", ref["state"]) > "/dev/stderr"
		printf("cand.name   = %s\n", cand["name"]) > "/dev/stderr"
		printf("cand.street = %s\n", cand["street"]) > "/dev/stderr"
		printf("cand.town   = %s\n", cand["town"]) > "/dev/stderr"
		printf("cand.state  = %s\n", cand["state"]) > "/dev/stderr"
	}

	if(cand["state"] != ref["state"]){
		if(options["ign_zip"]){
			if(substr(cand["state"], 1, 2) != substr(ref["state"], 1, 2))
				return 0
		}else
			return 0
	}

	if(cand["town"] != ref["town"])
		return 0

	# street numbers and/or ranges, etc is 1st field
	nc_fields = split(cand["street"], c_fields, /  */)
	nr_fields = split(ref["street"], r_fields, /  */)

	# check that non-number parts of streets agree
	if(nc_fields != nr_fields)
		return 0
	for(i = 2; i <= nc_fields; i++){
		if(c_fields[i] != r_fields[i])
			return 0
	}

	nc_rtab = AU_get_rtab(c_fields[1], c_rtab)
	if(nc_rtab == 0)
		return 0

	nr_rtab = AU_get_rtab(r_fields[1], r_rtab)
	if(nr_rtab == 0)
		return 0

	if(!AU_rtabs_intersect(nc_rtab, c_rtab, nr_rtab, r_rtab))
		return 0

	# deal with names
	if(cand["name"] == ref["name"]){
		return 3
	}else if(cand["name"] == ""){	# this is an unannotated address
		return 1
	}else if(ref["name"] == options["no_name"]){	# many destinations are just street, town, state
		return 1
	}else{	# check if 1st words match; but no matter what this is a match
		nc_nwords = split(cand["name"], c_nwords, /  */)
		nr_nwords = split(ref["name"], r_nwords, /  */)
		return c_nwords[1] == r_nwords[1] ? 2 : 1
	}
}
function AU_get_rtab(street, rtab,   n_rtab, i, nw, work) {

	n_rtab = split(street, rtab, ";")
	for(i = 1; i <= n_rtab; i++){
		nw = split(rtab[i], work, "-")
		if(nw > 1){
			rtab[i, 1] = work[1] + 0	# force number
			rtab[i, 2] = work[2] + 0	# force number
			rtab[i, "rng"] = 1
			if(rtab[i, 1] % 2 != rtab[i, 2] % 2){
				printf("ERROR: bad range: odd/even %d-%d\n", rtab[i, 1], rtab[i, 2]) > "/dev/stderr"
				return 0
			}
		}else{
			rtab[i] = work[1] + 0	# force number
			rtab[i, "rng"] = 0
		}
	}
	return n_rtab
}
function AU_rtabs_intersect(n_rtab1, rtab1, n_rtab2, rtab2,   i, j) {

	# the most common case: number v number
	if(n_rtab1 == 1 && !rtab1[1, "rng"] && n_rtab2 == 1 && !rtab2[1, "rng"])
		return rtab1[1] == rtab2[1]

	for(i = 1; i <= n_rtab1; i++){
		for(j = 1; j <= n_rtab2; j++){
			if(!rtab1[i, "rng"]){
				if(!rtab2[j, "rng"]){	# number v number
					if(rtab1[i] == rtab2[j])
						return 1
				}else{			# number v range
					if(rtab1[i] >= rtab2[j, 1] && rtab1[i] <= rtab2[j, 2])
						return 1
				}
			}else if(!rtab2[j, "rng"]){	# range v number
				if(rtab2[j] >= rtab1[i, 1] && rtab2[j] <= rtab1[i, 2])
					return 1
			}else{				# range v range
				if(!(rtab1[i, 2] < rtab2[j, 1] || rtab1[i, 1] > rtab2[j, 2]))
					return 1
			}
		}
	}
	return 0
}
function AU_rtab_dump(file, n_rtab, rtab,   i) {

	printf("n_rtab = %d {\n", n_rtab) > file
	for(i = 1; i <= n_rtab; i++){
		if(rtab[i, "rng"])
			printf("\trtab[%d, 1-2] = %d-%d\n", i, rtab[i, 1], rtab[i, 2]) > file
		else
			printf("\trtab[%d     ] = %d\n", i, rtab[i]) > file
	}
	printf("}\n") > file
}


Commons: add yn(string)

Commons: add yn(string)

####################################################################
# Commons.awk                                                      #
#                                                                  #
# Commonly used functions for string and array operations, logging.#
####################################################################

# Initialize `UrlEncoding`.
# See: <https://en.wikipedia.org/wiki/Percent-encoding>
function initUrlEncoding() {
    UrlEncoding["\n"] = "%0A"
    UrlEncoding[" "]  = "%20"
    UrlEncoding["!"]  = "%21"
    UrlEncoding["\""] = "%22"
    UrlEncoding["#"]  = "%23"
    UrlEncoding["$"]  = "%24"
    UrlEncoding["%"]  = "%25"
    UrlEncoding["&"]  = "%26"
    UrlEncoding["'"]  = "%27"
    UrlEncoding["("]  = "%28"
    UrlEncoding[")"]  = "%29"
    UrlEncoding["*"]  = "%2A"
    UrlEncoding["+"]  = "%2B"
    UrlEncoding[","]  = "%2C"
    UrlEncoding["-"]  = "%2D"
    UrlEncoding["."]  = "%2E"
    UrlEncoding["/"]  = "%2F"
    UrlEncoding[":"]  = "%3A"
    UrlEncoding[";"]  = "%3B"
    UrlEncoding["<"]  = "%3C"
    UrlEncoding["="]  = "%3D"
    UrlEncoding[">"]  = "%3E"
    UrlEncoding["?"]  = "%3F"
    UrlEncoding["@"]  = "%40"
    UrlEncoding["["]  = "%5B"
    UrlEncoding["\\"] = "%5C"
    UrlEncoding["]"]  = "%5D"
    UrlEncoding["^"]  = "%5E"
    UrlEncoding["_"]  = "%5F"
    UrlEncoding["`"]  = "%60"
    UrlEncoding["{"]  = "%7B"
    UrlEncoding["|"]  = "%7C"
    UrlEncoding["}"]  = "%7D"
    UrlEncoding["~"]  = "%7E"
}

# Return the real character represented by an escape sequence.
# Example: escapeChar("n") returns "\n".
# See: <https://en.wikipedia.org/wiki/Escape_character>
#      <https://en.wikipedia.org/wiki/Escape_sequences_in_C>
function escapeChar(char) {
    switch (char) {
    case "b":
        return "\b" # Backspace
    case "f":
        return "\f" # Formfeed
    case "n":
        return "\n" # Newline (Line Feed)
    case "r":
        return "\r" # Carriage Return
    case "t":
        return "\t" # Horizontal Tab
    case "v":
        return "\v" # Vertical Tab
    default:
        return char
    }
}

# Convert a literal-formatted string into its original string.
function literal(string,
                 ####
                 c, escaping, i, s) {
    if (string !~ /^".*"$/)
        return string

    split(string, s, "")
    string = ""
    escaping = 0
    for (i = 2; i < length(s); i++) {
        c = s[i]
        if (escaping) {
            string = string escapeChar(c)
            escaping = 0 # escape ends
        } else {
            if (c == "\\")
                escaping = 1 # escape begins
            else
                string = string c
        }
    }
    return string
}

# Return the escaped string.
function escape(string) {
    gsub(/"/, "\\\"", string)
    gsub(/\\/, "\\\\", string)
    return string
}

# Return the escaped, quoted string.
function parameterize(string, quotationMark) {
    if (!quotationMark)
        quotationMark = "'"

    if (quotationMark == "'") {
        gsub(/'/, "'\\''", string)
        return "'" string "'"
    } else {
        return "\"" escape(string) "\""
    }
}

# Return the URL-encoded string.
function quote(string,    i, r, s) {
    r = ""
    split(string, s, "")
    for (i = 1; i <= length(s); i++)
        r = r (s[i] in UrlEncoding ? UrlEncoding[s[i]] : s[i])
    return r
}

# Replicate a string.
function replicate(string, len,
                   ####
                   i, temp) {
    temp = ""
    for (i = 0; i < len; i++)
        temp = temp string
    return temp
}

# Squeeze a source line of AWK code.
function squeeze(line) {
    # Remove preceding spaces
    gsub(/^[[:space:]]+/, "", line)
    # Remove in-line comment
    gsub(/#[^"]*$/, "", line)
    # Remove trailing spaces
    gsub(/[[:space:]]+$/, "", line)

    return line
}

# Return 1 if the array contains anything; otherwise return 0.
function anything(array,
                  ####
                  i) {
    for (i in array)
        if (array[i]) return 1
    return 0
}

# Append an element into an array (zero-based).
function append(array, element) {
    array[anything(array) ? length(array) : 0] = element
}

# Return an element if it belongs to the array;
# Otherwise, return a null string.
function belongsTo(element, array,
                   ####
                   i) {
    for (i in array)
        if (element == array[i]) return element
    return ""
}

# Return one of the substrings if the string starts with it;
# Otherwise, return a null string.
function startsWithAny(string, substrings,
                       ####
                       i) {
    for (i in substrings)
        if (index(string, substrings[i]) == 1) return substrings[i]
    return ""
}

# Return one of the patterns if the string matches this pattern at the beginning;
# Otherwise, return a null string.
function matchesAny(string, patterns,
                    ####
                    i) {
    for (i in patterns)
        if (string ~ "^" patterns[i]) return patterns[i]
    return ""
}

# Join an array into one string;
# Return the string.
function join(array, separator, sortedIn, preserveNull,
              ####
              i, j, saveSortedIn, temp) {
    # Default parameters
    if (!separator)
        separator = " "
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    temp = ""
    j = 0
    saveSortedIn = PROCINFO["sorted_in"]
    if (length(array)) {
        PROCINFO["sorted_in"] = sortedIn
        for (i in array)
            if (preserveNull || array[i] != "")
                temp = j++ ? temp separator array[i] : array[i]
        PROCINFO["sorted_in"] = saveSortedIn
    } else
        temp = array # array == ""

    return temp
}

# Return 0 if the string starts with '0', 'f' or 'n';
# Otherwise, return 1.
function yn(string) {
    return (tolower(string) ~ /^[0fn]/) ? 0 : 1
}

# Initialize ANSI escape codes (ANSI X3.64 Standard Control Sequences).
# See: <https://en.wikipedia.org/wiki/ANSI_escape_code>
function initAnsiCode() {
    # Dumb terminal: no ANSI escape code whatsoever
    if (ENVIRON["TERM"] == "dumb") return

    AnsiCode["reset"]         = AnsiCode[0] = "\33[0m"

    AnsiCode["bold"]          = "\33[1m"
    AnsiCode["underline"]     = "\33[4m"
    AnsiCode["negative"]      = "\33[7m"
    AnsiCode["no bold"]       = "\33[22m" # SGR code 21 (bold off) not widely supported
    AnsiCode["no underline"]  = "\33[24m"
    AnsiCode["positive"]      = "\33[27m"

    AnsiCode["black"]         = "\33[30m"
    AnsiCode["red"]           = "\33[31m"
    AnsiCode["green"]         = "\33[32m"
    AnsiCode["yellow"]        = "\33[33m"
    AnsiCode["blue"]          = "\33[34m"
    AnsiCode["magenta"]       = "\33[35m"
    AnsiCode["cyan"]          = "\33[36m"
    AnsiCode["gray"]          = "\33[37m"

    AnsiCode["default"]       = "\33[39m"

    AnsiCode["dark gray"]     = "\33[90m"
    AnsiCode["light red"]     = "\33[91m"
    AnsiCode["light green"]   = "\33[92m"
    AnsiCode["light yellow"]  = "\33[93m"
    AnsiCode["light blue"]    = "\33[94m"
    AnsiCode["light magenta"] = "\33[95m"
    AnsiCode["light cyan"]    = "\33[96m"
    AnsiCode["white"]         = "\33[97m"
}

# Return ANSI escaped string.
function ansi(code, text) {
    switch (code) {
    case "bold":
        return AnsiCode[code] text AnsiCode["no bold"]
    case "underline":
        return AnsiCode[code] text AnsiCode["no underline"]
    case "negative":
        return AnsiCode[code] text AnsiCode["positive"]
    default:
        return AnsiCode[code] text AnsiCode[0]
    }
}

# Print warning message.
function w(text) {
    print ansi("yellow", text) > "/dev/stderr"
}

# Print error message.
function e(text) {
    print ansi("bold", ansi("red", text)) > "/dev/stderr"
}

# Print debugging message.
function d(text) {
    print ansi("gray", text) > "/dev/stderr"
}

# Log an array.
function da(array, formatString, sortedIn,
            ####
            i, j, saveSortedIn) {
    # Default parameters
    if (!formatString)
        formatString = "_[%s]='%s'"
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = sortedIn
    for (i in array) {
        split(i, j, SUBSEP)
        d(sprintf(formatString, join(j, ","), array[i]))
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

# Naive assertion.
function assert(x, message) {
    if (!message)
        message = "[ERROR] Assertion failed."

    if (x)
        return x
    else
        e(message)
}

# Return non-zero if file exists, otherwise return 0.
function fileExists(file) {
    return !system("test -f " file)
}

# Initialize `UriSchemes`.
function initUriSchemes() {
    UriSchemes[0] = "file://"
    UriSchemes[1] = "http://"
    UriSchemes[2] = "https://"
}

BEGIN {
    initUrlEncoding()
    initAnsiCode()
    initUriSchemes()
}

####################################################################
# Commons.awk                                                      #
#                                                                  #
# Commonly used functions for string and array operations, logging.#
####################################################################

# Initialize `UrlEncoding`.
# See: <https://en.wikipedia.org/wiki/Percent-encoding>
function initUrlEncoding() {
    UrlEncoding["\n"] = "%0A"
    UrlEncoding[" "]  = "%20"
    UrlEncoding["!"]  = "%21"
    UrlEncoding["\""] = "%22"
    UrlEncoding["#"]  = "%23"
    UrlEncoding["$"]  = "%24"
    UrlEncoding["%"]  = "%25"
    UrlEncoding["&"]  = "%26"
    UrlEncoding["'"]  = "%27"
    UrlEncoding["("]  = "%28"
    UrlEncoding[")"]  = "%29"
    UrlEncoding["*"]  = "%2A"
    UrlEncoding["+"]  = "%2B"
    UrlEncoding[","]  = "%2C"
    UrlEncoding["-"]  = "%2D"
    UrlEncoding["."]  = "%2E"
    UrlEncoding["/"]  = "%2F"
    UrlEncoding[":"]  = "%3A"
    UrlEncoding[";"]  = "%3B"
    UrlEncoding["<"]  = "%3C"
    UrlEncoding["="]  = "%3D"
    UrlEncoding[">"]  = "%3E"
    UrlEncoding["?"]  = "%3F"
    UrlEncoding["@"]  = "%40"
    UrlEncoding["["]  = "%5B"
    UrlEncoding["\\"] = "%5C"
    UrlEncoding["]"]  = "%5D"
    UrlEncoding["^"]  = "%5E"
    UrlEncoding["_"]  = "%5F"
    UrlEncoding["`"]  = "%60"
    UrlEncoding["{"]  = "%7B"
    UrlEncoding["|"]  = "%7C"
    UrlEncoding["}"]  = "%7D"
    UrlEncoding["~"]  = "%7E"
}

# Return the real character represented by an escape sequence.
# Example: escapeChar("n") returns "\n".
# See: <https://en.wikipedia.org/wiki/Escape_character>
#      <https://en.wikipedia.org/wiki/Escape_sequences_in_C>
function escapeChar(char) {
    switch (char) {
    case "b":
        return "\b" # Backspace
    case "f":
        return "\f" # Formfeed
    case "n":
        return "\n" # Newline (Line Feed)
    case "r":
        return "\r" # Carriage Return
    case "t":
        return "\t" # Horizontal Tab
    case "v":
        return "\v" # Vertical Tab
    default:
        return char
    }
}

# Convert a literal-formatted string into its original string.
function literal(string,
                 ####
                 c, escaping, i, s) {
    if (string !~ /^".*"$/)
        return string

    split(string, s, "")
    string = ""
    escaping = 0
    for (i = 2; i < length(s); i++) {
        c = s[i]
        if (escaping) {
            string = string escapeChar(c)
            escaping = 0 # escape ends
        } else {
            if (c == "\\")
                escaping = 1 # escape begins
            else
                string = string c
        }
    }
    return string
}

# Return the escaped string.
function escape(string) {
    gsub(/"/, "\\\"", string)
    gsub(/\\/, "\\\\", string)
    return string
}

# Return the escaped, quoted string.
function parameterize(string, quotationMark) {
    if (!quotationMark)
        quotationMark = "'"

    if (quotationMark == "'") {
        gsub(/'/, "'\\''", string)
        return "'" string "'"
    } else {
        return "\"" escape(string) "\""
    }
}

# Return the URL-encoded string.
function quote(string,    i, r, s) {
    r = ""
    split(string, s, "")
    for (i = 1; i <= length(s); i++)
        r = r (s[i] in UrlEncoding ? UrlEncoding[s[i]] : s[i])
    return r
}

# Replicate a string.
function replicate(string, len,
                   ####
                   i, temp) {
    temp = ""
    for (i = 0; i < len; i++)
        temp = temp string
    return temp
}

# Squeeze a source line of AWK code.
function squeeze(line) {
    # Remove preceding spaces
    gsub(/^[[:space:]]+/, "", line)
    # Remove in-line comment
    gsub(/#[^"]*$/, "", line)
    # Remove trailing spaces
    gsub(/[[:space:]]+$/, "", line)

    return line
}

# Return 1 if the array contains anything; otherwise return 0.
function anything(array,
                  ####
                  i) {
    for (i in array)
        if (array[i]) return 1
    return 0
}

# Append an element into an array (zero-based).
function append(array, element) {
    array[anything(array) ? length(array) : 0] = element
}

# Return an element if it belongs to the array;
# Otherwise, return a null string.
function belongsTo(element, array,
                   ####
                   i) {
    for (i in array)
        if (element == array[i]) return element
    return ""
}

# Return one of the substrings if the string starts with it;
# Otherwise, return a null string.
function startsWithAny(string, substrings,
                       ####
                       i) {
    for (i in substrings)
        if (index(string, substrings[i]) == 1) return substrings[i]
    return ""
}

# Return one of the patterns if the string matches this pattern at the beginning;
# Otherwise, return a null string.
function matchesAny(string, patterns,
                    ####
                    i) {
    for (i in patterns)
        if (string ~ "^" patterns[i]) return patterns[i]
    return ""
}

# Join an array into one string;
# Return the string.
function join(array, separator, sortedIn, preserveNull,
              ####
              i, j, saveSortedIn, temp) {
    # Default parameters
    if (!separator)
        separator = " "
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    temp = ""
    j = 0
    saveSortedIn = PROCINFO["sorted_in"]
    if (length(array)) {
        PROCINFO["sorted_in"] = sortedIn
        for (i in array)
            if (preserveNull || array[i] != "")
                temp = j++ ? temp separator array[i] : array[i]
        PROCINFO["sorted_in"] = saveSortedIn
    } else
        temp = array # array == ""

    return temp
}

# Initialize ANSI escape codes (ANSI X3.64 Standard Control Sequences).
# See: <https://en.wikipedia.org/wiki/ANSI_escape_code>
function initAnsiCode() {
    # Dumb terminal: no ANSI escape code whatsoever
    if (ENVIRON["TERM"] == "dumb") return

    AnsiCode["reset"]         = AnsiCode[0] = "\33[0m"

    AnsiCode["bold"]          = "\33[1m"
    AnsiCode["underline"]     = "\33[4m"
    AnsiCode["negative"]      = "\33[7m"
    AnsiCode["no bold"]       = "\33[22m" # SGR code 21 (bold off) not widely supported
    AnsiCode["no underline"]  = "\33[24m"
    AnsiCode["positive"]      = "\33[27m"

    AnsiCode["black"]         = "\33[30m"
    AnsiCode["red"]           = "\33[31m"
    AnsiCode["green"]         = "\33[32m"
    AnsiCode["yellow"]        = "\33[33m"
    AnsiCode["blue"]          = "\33[34m"
    AnsiCode["magenta"]       = "\33[35m"
    AnsiCode["cyan"]          = "\33[36m"
    AnsiCode["gray"]          = "\33[37m"

    AnsiCode["default"]       = "\33[39m"

    AnsiCode["dark gray"]     = "\33[90m"
    AnsiCode["light red"]     = "\33[91m"
    AnsiCode["light green"]   = "\33[92m"
    AnsiCode["light yellow"]  = "\33[93m"
    AnsiCode["light blue"]    = "\33[94m"
    AnsiCode["light magenta"] = "\33[95m"
    AnsiCode["light cyan"]    = "\33[96m"
    AnsiCode["white"]         = "\33[97m"
}

# Return ANSI escaped string.
function ansi(code, text) {
    switch (code) {
    case "bold":
        return AnsiCode[code] text AnsiCode["no bold"]
    case "underline":
        return AnsiCode[code] text AnsiCode["no underline"]
    case "negative":
        return AnsiCode[code] text AnsiCode["positive"]
    default:
        return AnsiCode[code] text AnsiCode[0]
    }
}

# Print warning message.
function w(text) {
    print ansi("yellow", text) > "/dev/stderr"
}

# Print error message.
function e(text) {
    print ansi("bold", ansi("red", text)) > "/dev/stderr"
}

# Print debugging message.
function d(text) {
    print ansi("gray", text) > "/dev/stderr"
}

# Log an array.
function da(array, formatString, sortedIn,
            ####
            i, j, saveSortedIn) {
    # Default parameters
    if (!formatString)
        formatString = "_[%s]='%s'"
    if (!sortedIn)
        sortedIn = "@ind_num_asc"

    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = sortedIn
    for (i in array) {
        split(i, j, SUBSEP)
        d(sprintf(formatString, join(j, ","), array[i]))
    }
    PROCINFO["sorted_in"] = saveSortedIn
}

# Naive assertion.
function assert(x, message) {
    if (!message)
        message = "[ERROR] Assertion failed."

    if (x)
        return x
    else
        e(message)
}

# Return non-zero if file exists, otherwise return 0.
function fileExists(file) {
    return !system("test -f " file)
}

# Initialize `UriSchemes`.
function initUriSchemes() {
    UriSchemes[0] = "file://"
    UriSchemes[1] = "http://"
    UriSchemes[2] = "https://"
}

BEGIN {
    initUrlEncoding()
    initAnsiCode()
    initUriSchemes()
}


Strip spaces and comments more agressively.

#!/usr/bin/awk -f
#
# Convert forth source files to a giant C string
#
# Joe Abley <jabley@patho.gen.nz>, 12 January 1999
#
# 02-oct-1999:  Cleaned up awk slightly; added some additional logic
#               suggested by dcs to compress the stored forth program.
#
# Note! This script uses strftime() which is a gawk-ism, and the
# POSIX [[:space:]] character class.
#
# $FreeBSD$

BEGIN \
{
  printf "/***************************************************************\n";
  printf "** s o f t c o r e . c\n";
  printf "** Forth Inspired Command Language -\n";
  printf "** Words from CORE set written in FICL\n";
  printf "** Author: John Sadler (john_sadler@alum.mit.edu)\n";
  printf "** Created: 27 December 1997\n";
  printf "** Last update: %s\n", strftime();
  printf "***************************************************************/\n";
  printf "\n/*\n";
  printf "** This file contains definitions that are compiled into the\n";
  printf "** system dictionary by the first virtual machine to be created.\n";
  printf "** Created automagically by ficl/softwords/softcore.awk\n";
  printf "*/\n";
  printf "\n#include \"ficl.h\"\n";
  printf "\nstatic char softWords[] =\n";

  commenting = 0;
}

# some general early substitutions
{
  gsub(/\t/, "    ");			# replace each tab with 4 spaces
  gsub(/\"/, "\\\"");			# escape quotes
  gsub(/\\[[:space:]]+$/, "");		# toss empty comments
}

# strip out empty lines
/^ *$/ \
{
  next;
}

# emit / ** lines as multi-line C comments
/^\\[[:space:]]\*\*/ \
{
  sub(/^\\[[:space:]]/, "");
  if (commenting == 0) printf "/*\n";
  printf "%s\n", $0;
  commenting = 1;
  next;
}

# strip blank lines
/^[[:space:]]*$/ \
{
  next;
}

# function to close a comment, used later
function end_comments()
{
  commenting = 0;
  printf "*/\n";
}

# pass commented preprocessor directives
/^\\[[:space:]]#/ \
{
  if (commenting) end_comments();
  sub(/^\\[[:space:]]/, "");
  printf "%s\n", $0;
  next;
}

# toss all other full-line \ comments
/^\\/ \
{
  if (commenting) end_comments();
  next;
}

# lop off trailing \ comments
/\\[[:space:]]+/ \
{
  sub(/\\[[:space:]]+.*$/, "");
}

# expunge ( ) comments
/[[:space:]]+\([[:space:]][^)]*\)/ \
{
  sub(/[[:space:]]+\([[:space:]][^)]*\)/, "");
}

# remove leading spaces
/^[[:space:]]+/ \
{
  sub(/^[[:space:]]+/, "");
}

# removing trailing spaces
/[[:space:]]+$/ \
{
  sub(/[[:space:]]+$/, "");
}

# strip out empty lines again (preceding rules may have generated some)
/^[[:space:]]*$/ \
{
  if (commenting) end_comments();
  next;
}

# emit all other lines as quoted string fragments
{
  if (commenting) end_comments();

  printf "    \"%s \"\n", $0;
  next;
}

END \
{
  if (commenting) end_comments();
  printf "    \"quit \";\n";
  printf "\n\nvoid ficlCompileSoftCore(FICL_VM *pVM)\n";
  printf "{\n";
  printf "    assert(ficlExec(pVM, softWords) != VM_ERREXIT);\n";
  printf "}\n";
}

#!/usr/bin/awk -f
# Convert forth source files to a giant C string
# Joe Abley <jabley@patho.gen.nz>, 12 January 1999
# $FreeBSD$

BEGIN \
{
  printf "/***************************************************************\n";
  printf "** s o f t c o r e . c\n";
  printf "** Forth Inspired Command Language -\n";
  printf "** Words from CORE set written in FICL\n";
  printf "** Author: John Sadler (john_sadler@alum.mit.edu)\n";
  printf "** Created: 27 December 1997\n";
  printf "** Last update: %s\n", strftime();
  printf "***************************************************************/\n";
  printf "\n/*\n";
  printf "** This file contains definitions that are compiled into the\n";
  printf "** system dictionary by the first virtual machine to be created.\n";
  printf "** Created automagically by ficl/softwords/softcore.awk\n";
  printf "*/\n";
  printf "\n#include \"ficl.h\"\n";
  printf "\nstatic char softWords[] =\n";

  commenting = 0;
}

# some general early substitutions
{
  gsub("\t", "    ");			# replace each tab with 4 spaces
  gsub("\"", "\\\"");			# escape quotes
  gsub("\\\\[[:space:]]+$", "");	# toss empty comments
}

# strip out empty lines
/^ *$/ \
{
  next;
}

# emit / ** lines as multi-line C comments
/^\\[[:space:]]\*\*/ && (commenting == 0) \
{
  sub("^\\\\[[:space:]]", "");
  printf "/*\n%s\n", $0;
  commenting = 1;
  next;
}

/^\\[[:space:]]\*\*/ \
{
  sub("^\\\\[[:space:]]", "");
  printf "%s\n", $0;
  next;
}

# function to close a comment, used later
function end_comments()
{
  commenting = 0;
  printf "*/\n";
}

# pass commented preprocessor directives
/^\\[[:space:]]#/ \
{
  if (commenting) end_comments();
  sub("^\\\\[[:space:]]", "");
  printf "%s\n", $0;
  next;
}

# toss all other full-line comments
/^\\/ \
{
  if (commenting) end_comments();
  next;
}

# emit all other lines as quoted string fragments
{
  if (commenting) end_comments();

  sub("\\\\[[:space:]]+.*$", "");	# lop off trailing \ comments
  sub("[[:space:]]+$", "");		# remove trailing spaces
  printf "    \"%s \\n\"\n", $0;
  next;
}

END \
{
  if (commenting) end_comments();
  printf "    \"quit \";\n";
  printf "\n\nvoid ficlCompileSoftCore(FICL_VM *pVM)\n";
  printf "{\n";
  printf "    assert(ficlExec(pVM, softWords) != VM_ERREXIT);\n";
  printf "}\n";
}


vector extraction script updated

vector extraction script updated

#!/usr/bin/awk -f

##################################################################
BEGIN {
    if (ARGC != 4) {
	printf("Incorrect number of arguments.") > "/sys/stderr"
	exit 1
    }

    ret = 0
    WCL = 0 + ARGV[1]
    # obtain words to fetch
    ifile = ARGV[2]
    while ((ret = (getline < ifile)) > 0) {
	if (!NF)
	    continue
	else if (NF != 2) {
	    printf("Incorrect line format: %s", $0) > "/sys/stderr"
	    exit 2
	}
        $1 = lower($1)
        if ($1 in WORD2SEEK)
          continue

	WORD2SEEK[$1] = ""
        ++w_cnt
    }
    ARGV[1] = ARGV[2] = ""
    # obtain random line numbers to fetch
    srand()
    wcl = WCL > 1? WCL - 1: 0
    for (i = 1; i < WCL; ++i) {
	randarr[i] = rand() * wcl + 1
    }
    asorti(randarr, tmparr)
    wcl = WCL / 500
    for (i = 1; i < wcl; ++i) {
	FNR2SEEK[tmparr[i]] = ""
    }
}

##################################################################
NF && FNR > 1 && ($1 in WORD2SEEK || FNR in FNR2SEEK)

#!/usr/bin/awk -f

##################################################################
BEGIN {
    if (ARGC != 4) {
	printf("Incorrect number of arguments.") > "/sys/stderr"
	exit 1
    }

    ret = 0
    WCL = 0 + ARGV[1]
    # obtain words to fetch
    ifile = ARGV[2]
    while ((ret = (getline < ifile)) > 0) {
	if (!NF)
	    continue
	else if (NF != 2) {
	    printf("Incorrect line format: %s", $0) > "/sys/stderr"
	    exit 2
	}
	WORD2SEEK[$1] = ""
    }
    ARGV[1] = ARGV[2] = ""
    # obtain random line numbers to fetch
    srand()
    wcl = WCL - 1
    for (i = 1; i < WCL; ++i) {
	randarr[i] = rand() * wcl + 1
    }
    asorti(randarr, tmparr)
    wcl = WCL / 1000
    for (i = 1; i < wcl; ++i) {
	FNR2SEEK[tmparr[i]] = ""
    }
}

##################################################################
NF && FNR > 1 && ($1 in WORD2SEEK || FNR in FNR2SEEK)


Add identifier KEY so we can see where we are in -v script

{
    title = $0
    if (match (title, /, The"$/)) {
        sub (/^"/,"\"The ", title)
        sub (/, The"$/,"\"", title)
    }

    if (FILENAME ~ /Acorn/)
        KEY = "A"
    if (FILENAME ~ /BBox/)
        KEY = "B"
    if (FILENAME ~ /MHz/)
        KEY = "M"
    if (FILENAME ~ /Watched/)
        KEY = "W"

    printf ("echo ==\\> %sT %3d: %s\n", KEY, NR, title) >> TITLES_SCRIPT
    printf ("./getIMDbInfoFrom-titles.py %s\n",title) >> TITLES_SCRIPT
    printf ("echo \n\n") >> TITLES_SCRIPT

    printf ("echo ==\\> %sI %3d: %s\n", KEY, NR, title) >> ID_SCRIPT
    printf ("./getIMDb_IDsFrom-titles.py %s | head -7\n",title) >> ID_SCRIPT
    printf ("echo \n\n") >> ID_SCRIPT
}

{
    title = $0
    if (match (title, /, The"$/)) {
        sub (/^"/,"\"The ", title)
        sub (/, The"$/,"\"", title)
    }
    printf ("echo ==\\> %3d: %s\n",NR,title) >> TITLES_SCRIPT
    printf ("./getIMDbInfoFrom-titles.py %s\n",title) >> TITLES_SCRIPT
    printf ("echo \n\n") >> TITLES_SCRIPT

    printf ("echo ==\\> %3d: %s\n",NR,title) >> ID_SCRIPT
    printf ("./getIMDb_IDsFrom-titles.py %s | head -7\n",title) >> ID_SCRIPT
    printf ("echo \n\n") >> ID_SCRIPT
}


update comment

update comment

#! /usr/bin/gawk -f
# distill_dot.awk <numbered.gv>  > <filtered_weighted.gv>

# I have dot files with edge lables that terminate with a number in parens `(nnn)`.
# the numbers may be simple counts (n > 0) or may be differences [nil neg zero pos ]
#
# I'm initally intrested in the case where the numbers are a difference
# and will filter out small differences and colorize/weight larger changes.

BEGIN {hi = -2^PREC; lo = 2^PREC; THRESHOLD = 10}

# there was an unfortunate case where the term label contains parens e.g.
# ZFIN -> LITERAL [label=<dc:title ! title (dce) (30783)>];
# AspGD -> GO [label=<RO:0002331 ! involved in (-12)>, color="pink"];

/.*label=<.*\(-?[0-9]+\).*;$/{

    n = split($0, a, /[()]/)
    # for(i in  a) print a[i]
    val = a[length(a)-1]
    sfx = a[length(a)]
    # print "Val=" val
    if(val > hi) hi = val
    if(val < lo) lo = val
    aval = (val < 0.0) ? -val : val
    if(aval > THRESHOLD){
        a[length(a)] = ""
        a[length(a)-1] = ""
        key=""
        for(x in a) key = key a[x]
        value[key] = val
        sufix[key] = substr(sfx,2)
    }
}

# not processed
!/.*label=<.*\(-?[0-9]+\).*;$/{
    pass_on[pss++] = $0
}

END {
    SCALE = (hi > -lo)? hi: -lo
    # Threshold can be extended as a fraction of scale
    # say... differences less than half a percent of the churn are supressed
    SLIDINGSCALE =  SCALE / 200

    if (SCALE < 1){
        # print "ERROR not to scale: " SCALE
        SCALE = 1
    }

    # precheck if there is any content before printing boilerplate
    content = 0
    for(key in value)   {
        val = value[key]
        aval = (val < 0.0) ? -val : val
        if(aval >= SLIDINGSCALE)
            content ++
    }

    for(i = 0; i < pss; i++)
        if( (pass_on[i]!="") && (pass_on[i]!="}") && content > 0)
            print pass_on[i]

    for(key in value)   {
        val = value[key]
        aval = (val < 0.0) ? -val : val
        if(aval >= SLIDINGSCALE)
            print key ">, penwidth = \""(aval/SCALE*5)"\", weight = \"" aval "\"" sufix[key]
    }

    if(content > 0)
        print "}"
}

#! /usr/bin/gawk -f
# distill_dot.awk <numbered.gv>  > <filtered_weighted.gv>

# I have dot files with edge lables that terminate with a number in parens `(nnn)`.
# the numbers may be simple counts (n > 0) or may be differences [nil neg zero pos ]
#
# I'm initally intrested in the case where the numbers are a difference
# and will filter out small differences and colorize/weight larger changes.

BEGIN {hi = -2^PREC; lo = 2^PREC; THRESHOLD = 10}

# there is an unfortunate case where the term label contains parens e.g.
# ZFIN -> LITERAL [label=<dc:title ! title (dce) (30783)>];
# AspGD -> GO [label=<RO:0002331 ! involved in (-12)>, color="pink"];

/.*label=<.*\(-?[0-9]+\).*;$/{

    n = split($0, a, /[()]/)
    # for(i in  a) print a[i]
    val = a[length(a)-1]
    sfx = a[length(a)]
    # print "Val=" val
    if(val > hi) hi = val
    if(val < lo) lo = val
    aval = (val < 0.0) ? -val : val
    if(aval > THRESHOLD){
        a[length(a)] = ""
        a[length(a)-1] = ""
        key=""
        for(x in a) key = key a[x]
        value[key] = val
        sufix[key] = substr(sfx,2)
    }
}

# not processed
!/.*label=<.*\(-?[0-9]+\).*;$/{
    pass_on[pss++] = $0
}

END {
    SCALE = (hi > -lo)? hi: -lo
    # Threshold can be extended as a fraction of scale
    # say... differences less than half a percent of the churn are supressed
    SLIDINGSCALE =  SCALE / 200

    if (SCALE < 1){
        # print "ERROR not to scale: " SCALE
        SCALE = 1
    }

    # precheck if there is any content before printing boilerplate
    content = 0
    for(key in value)   {
        val = value[key]
        aval = (val < 0.0) ? -val : val
        if(aval >= SLIDINGSCALE)
            content ++
    }

    for(i = 0; i < pss; i++)
        if( (pass_on[i]!="") && (pass_on[i]!="}") && content > 0)
            print pass_on[i]

    for(key in value)   {
        val = value[key]
        aval = (val < 0.0) ? -val : val
        if(aval >= SLIDINGSCALE)
            print key ">, penwidth = \""(aval/SCALE*5)"\", weight = \"" aval "\"" sufix[key]
    }

    if(content > 0)
        print "}"
}