Cast from the specific vop_*_args to vop_generic_args via a (void *)
pointer to eliminate the hundreds of warnings that we have in tree at
the moment.

# Chances are good that all the struct vop_*_args should have, as its
# first element, the struct vop_generic_args, and when necessary to
# reference it, we just take its address rather than going through
# this double case.

#!/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]
#	(where <srcfile> is currently /sys/kern/vnode_if.src)
#

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

function die(msg, what)
{
	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)
{
	if (arg == "vpp")
		arg = "*vpp";
	if (lockdata[name, arg, pos] && (lockdata[name, arg, pos] != "-")) {
		if (arg ~ /^\*/) {
			printh("\tif ("substr(arg, 2)" != NULL) {");
		}
		printh("\tASSERT_VI_UNLOCKED("arg", \""uname"\");");
		# Add assertions for locking
		if (lockdata[name, arg, pos] == "L")
			printh("\tASSERT_VOP_LOCKED("arg", \""uname"\");");
		else if (lockdata[name, arg, pos] == "U")
			printh("\tASSERT_VOP_UNLOCKED("arg", \""uname"\");");
		else if (0) {
			# XXX More checks!
		}
		if (arg ~ /^\*/) {
			printh("\t}");
		}
	}
}

function add_debug_pre(name)
{
	if (lockdata[name, "pre"]) {
		printh("#ifdef	DEBUG_VFS_LOCKS");
		printh("\t"lockdata[name, "pre"]"(&a);");
		printh("#endif");
	}
}

function add_debug_post(name)
{
	if (lockdata[name, "post"]) {
		printh("#ifdef	DEBUG_VFS_LOCKS");
		printh("\t"lockdata[name, "post"]"(&a, rc);");
		printh("#endif");
	}
}

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)
	printq("struct vop_generic_args;")
	printq("typedef int vop_bypass_t(struct vop_generic_args *);\n")
}

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/systm.h>\n" \
	    "#include <sys/vnode.h>\n" \
	    "\n" \
	    "struct vnodeop_desc vop_default_desc = {\n" \
	    "	1,\t\t\t/* special case, vop_default => 1 */\n" \
	    "	\"default\",\n" \
	    "	0,\n" \
	    "	NULL,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	NULL,\n" \
	    "};\n");
}

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

	if ($1 ~ /^#!/) {
		if (NF != 4 || $1 != "#!")
			continue;
		if ($3 != "pre" && $3 != "post")
			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);
		}
		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, " ctrstr ")\"";
	for (i = 0; i < ctrargs; ++i)
		ctrstr = ctrstr ", " 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 vnodeop_desc *a_desc;");
		for (i = 0; i < numargs; ++i)
			printh("\t" t_spc(types[i]) "a_" args[i] ";");
		printh("};");

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

		# Print out function.
		printh("static __inline int " uname "(");
		for (i = 0; i < numargs; ++i) {
			printh("\t" t_spc(types[i]) args[i] \
			    (i < numargs - 1 ? "," : ")"));
		}
		printh("{\n\tstruct " name "_args a;");
		printh("\tint rc;");
		printh("\ta.a_desc = VDESC(" name ");");
		for (i = 0; i < numargs; ++i)
			printh("\ta.a_" args[i] " = " args[i] ";");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Entry");
		add_debug_pre(name);
		printh("\t{")
		printh("\t\tstruct vop_vector *vop = "args[0]"->v_op;")
		printh("\t\twhile(vop != NULL && vop->"name" == NULL && vop->vop_bypass == NULL)")
		printh("\t\t\tvop = vop->vop_default;")
		printh("\t\tKASSERT(vop != NULL, (\"No "name"(%p...)\", "args[0]"));")
		printh("\t\tif (vop->"name" != NULL)")
		printh("\t\t\trc = vop->"name"(&a);")
		printh("\t\telse")
		printh("\t\t\trc = vop->vop_bypass((struct vop_generic_args *)(void *)&a);")
		printh("\t}")
		printh(ctrstr);
		printh("if (rc == 0) {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "OK");
		printh("} else {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Error");
		printh("}");
		add_debug_post(name);
		printh("\treturn (rc);\n}");
	}

	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 the vnodeop_desc structure.
		printc("struct vnodeop_desc " name "_desc = {");
		# offset
		printc("\toffsetof(struct vop_vector, "name"),");
		# 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 ",");

		# 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("\tNULL,\n};\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]
#	(where <srcfile> is currently /sys/kern/vnode_if.src)
#

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

function die(msg, what)
{
	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)
{
	if (arg == "vpp")
		arg = "*vpp";
	if (lockdata[name, arg, pos] && (lockdata[name, arg, pos] != "-")) {
		if (arg ~ /^\*/) {
			printh("\tif ("substr(arg, 2)" != NULL) {");
		}
		printh("\tASSERT_VI_UNLOCKED("arg", \""uname"\");");
		# Add assertions for locking
		if (lockdata[name, arg, pos] == "L")
			printh("\tASSERT_VOP_LOCKED("arg", \""uname"\");");
		else if (lockdata[name, arg, pos] == "U")
			printh("\tASSERT_VOP_UNLOCKED("arg", \""uname"\");");
		else if (0) {
			# XXX More checks!
		}
		if (arg ~ /^\*/) {
			printh("\t}");
		}
	}
}

function add_debug_pre(name)
{
	if (lockdata[name, "pre"]) {
		printh("#ifdef	DEBUG_VFS_LOCKS");
		printh("\t"lockdata[name, "pre"]"(&a);");
		printh("#endif");
	}
}

function add_debug_post(name)
{
	if (lockdata[name, "post"]) {
		printh("#ifdef	DEBUG_VFS_LOCKS");
		printh("\t"lockdata[name, "post"]"(&a, rc);");
		printh("#endif");
	}
}

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)
	printq("struct vop_generic_args;")
	printq("typedef int vop_bypass_t(struct vop_generic_args *);\n")
}

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/systm.h>\n" \
	    "#include <sys/vnode.h>\n" \
	    "\n" \
	    "struct vnodeop_desc vop_default_desc = {\n" \
	    "	1,\t\t\t/* special case, vop_default => 1 */\n" \
	    "	\"default\",\n" \
	    "	0,\n" \
	    "	NULL,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	VDESC_NO_OFFSET,\n" \
	    "	NULL,\n" \
	    "};\n");
}

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

	if ($1 ~ /^#!/) {
		if (NF != 4 || $1 != "#!")
			continue;
		if ($3 != "pre" && $3 != "post")
			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);
		}
		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, " ctrstr ")\"";
	for (i = 0; i < ctrargs; ++i)
		ctrstr = ctrstr ", " 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 vnodeop_desc *a_desc;");
		for (i = 0; i < numargs; ++i)
			printh("\t" t_spc(types[i]) "a_" args[i] ";");
		printh("};");

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

		# Print out function.
		printh("static __inline int " uname "(");
		for (i = 0; i < numargs; ++i) {
			printh("\t" t_spc(types[i]) args[i] \
			    (i < numargs - 1 ? "," : ")"));
		}
		printh("{\n\tstruct " name "_args a;");
		printh("\tint rc;");
		printh("\ta.a_desc = VDESC(" name ");");
		for (i = 0; i < numargs; ++i)
			printh("\ta.a_" args[i] " = " args[i] ";");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Entry");
		add_debug_pre(name);
		printh("\t{")
		printh("\t\tstruct vop_vector *vop = "args[0]"->v_op;")
		printh("\t\twhile(vop != NULL && vop->"name" == NULL && vop->vop_bypass == NULL)")
		printh("\t\t\tvop = vop->vop_default;")
		printh("\t\tKASSERT(vop != NULL, (\"No "name"(%p...)\", "args[0]"));")
		printh("\t\tif (vop->"name" != NULL)")
		printh("\t\t\trc = vop->"name"(&a);")
		printh("\t\telse")
		printh("\t\t\trc = vop->vop_bypass((struct vop_generic_args *)&a);")
		printh("\t}")
		printh(ctrstr);
		printh("if (rc == 0) {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "OK");
		printh("} else {");
		for (i = 0; i < numargs; ++i)
			add_debug_code(name, args[i], "Error");
		printh("}");
		add_debug_post(name);
		printh("\treturn (rc);\n}");
	}

	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 the vnodeop_desc structure.
		printc("struct vnodeop_desc " name "_desc = {");
		# offset
		printc("\toffsetof(struct vop_vector, "name"),");
		# 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 ",");

		# 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("\tNULL,\n};\n");
	}
}

if (pfile)
	printp("};")

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

exit 0;

}


Bix broken times on Acorn website

Bix broken times on Acorn website

# Create column files with lists of series titles, descriptions, number
# of seasons, and number of episodes, to be pasted into a spreadsheet.
# Note that IN_CANADA affects processing.
#
# Create a separate file with a line for each episode containing
# seriesNumber, episodeURL, seriesTitle, seasonNumber, episodeNumber,
# episodeTitle, & episodeDuration with the same columns as the primary
# spreadsheet so they can be combined into one.
#
# Create a separate file with a line for each series duration
#
# Return the total duration of all series
#
# INVOCATION:
#       curl -s https://acorn.tv/192/ \
#           | awk -v TITLE_FILE=$TITLE_FILE -v DESCRIPTION_FILE=$DESCRIPTION_FILE \
#           -v NUM_SEASONS_FILE=$NUM_SEASONS_FILE -v NUM_EPISODES_FILE=$NUM_EPISODES_FILE \
#           -v EPISODE_CURL_FILE=$EPISODE_CURL_FILE -v IN_CANADA=$IN_CANADA \
#           -v EPISODE_INFO_FILE=$EPISODE_INFO_FILE -v SERIES_NUMBER=$lastRow \
#           -v DURATION_FILE=$DURATION_FILE -v ERROR_FILE=$ERROR_FILE -f getAcornFrom-seriesPages.awk
#
# INPUT:
#        <title>Acorn TV | 19-2</title>
#  ---
#        19-2
#        <meta itemprop="numberOfEpisodes" content="30" />
#        <meta itemprop="numberOfSeasons" content="3" />
#  ---
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#  ---
#
"The writing is sublime" (New \
#        York Times) in this anything-but-a-procedural cop drama ... as reluctant partners \
#        patrolling the streets of Montreal. Not Available in Canada. CC Available.

#  ---
#
We're sorry, but Acorn TV is not available in this territory.

#  ---
#        <meta itemprop="seasonNumber" content="1" />
#  ---
#
#  ---
#        <meta itemprop="timeRequired" content="T44M06S" />
#  ---
#
Partners

#  ---
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

#
#
#
#  OUTPUT:
#       $EPISODE_INFO_FILE
#

# Extract the series title
/<title>/ {
    sub (/.*<title>Acorn TV \| /,"")
    sub (/<.*/,"")
    gsub (/&/,"\\&")
    gsub (/"/,"\"\"")
    # gsub (/'/,"'")
    if (match ($0, /^The /)) {
        $0 = substr($0, 5) ", The"
    }
    episodeLinesFound = 0
    numEpisodesStr = ""
    seriesTitle = $0
    print seriesTitle >> TITLE_FILE
    next
}

# Extract the number of episodes in the series
/itemprop="numberOfEpisodes"/ {
    split ($0,fld,"\"")
    episodeLinesFound += 1
    if (episodeLinesFound == 1)
        totalEpisodes = fld[4]
    if (episodeLinesFound != 1)
        numEpisodesStr = numEpisodesStr "+" fld[4]
    next
}

# Extract the number of seasons in the series
/itemprop="numberOfSeasons"/ {
    split ($0,fld,"\"")
    numSeasons = fld[4]
    if ((numSeasons + 0) == 0)
        printf ("==> No seasons: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    print numSeasons >> NUM_SEASONS_FILE
    next
}

/Acorn TV is not available in this territory/ {
    printf ("==> Not available here: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
}

# Extract the series description
/id="franchise-description"/ {
    # get rid of boilerplate
    split ($0,fld,"[<>]")
    description = fld[3]
    # get rid of unnecessary characters and text
    gsub (/\\/,"",description)
    if (IN_CANADA != "yes") {
        sub (/Series [[:digit:]]* not available in Canada\./,"",description)
        sub (/Not [Aa]vailable in Canada\./,"",description)
        sub (/NOT AVAILABLE IN CANADA\./,"",description)
    }
    sub (/CC Available\. CC Available/,"CC Available",description)
    # fix sloppy input spacing
    sub (/\.CC Available/,". CC Available",description)
    gsub (/ \./,".",description)
    gsub (/  */," ",description)
    sub (/^ */,"",description)
    sub (/ *$/,"",description)
    # fix funky HTML characters
    gsub (/'/,"'",description)
    # fix unmatched quotes
    numQuotes = gsub(/"/,"\"",description)
    if ((numQuotes % 2) == 1) {
        printf ("==> Corrected unmatched quote (%d): %d\t%s\n", numQuotes, SERIES_NUMBER, \
                seriesTitle) >> ERROR_FILE
        description = description " \""
    }
    print description >> DESCRIPTION_FILE
    next
}

# Extract the seasonNumber
# Note that Acorn "season" numbers may not correspond to actual season numbers.
# They simply refer to the number of seasons available on Acorn.
/<meta itemprop="seasonNumber" content="/ {
    split ($0,fld,"\"")
    seasonNumber = fld[4]
    next
}

# Extract the episode URL
/> EPISODE_CURL_FILE
    next
}

# Extract the episode duration
/<meta itemprop="timeRequired"/ {
    split ($0,fld,"\"")
    # broken times in Jamaica Inn (may be temporary)
    if (episodeURL ~ /\/jamaicainn\// && fld[4] == "T-1M57S") {
        printf ("==> Bad duration: %s  %s\n", episodeURL, fld[4]) >> ERROR_FILE
        fld[4] = "T59M18S"
    }
    if (episodeURL ~ /\/jamaicainn\/bonus\/cast-and-crew-interviews/ && fld[4] = "T09M08S") {
            printf ("==> Bad duration: %s  %s\n", episodeURL, fld[4]) >> ERROR_FILE
        fld[4] = "T25M54S"
    }
    split (fld[4],tm,/[TMS]/)
    secs = tm[3]
    mins = tm[2] + int(secs / 60)
    hrs =  int(mins / 60)
    secs %= 60; mins %= 60
    #
    seriesSecs += secs
    seriesMins += mins + int(seriesSecs / 60)
    seriesHrs += hrs + int(seriesMins / 60)
    seriesSecs %= 60; seriesMins %= 60
    #
    totalSecs += secs
    totalMins += mins + int(totalSecs / 60)
    totalHrs += hrs + int(totalMins / 60)
    totalSecs %= 60; totalMins %= 60
    #
    episodeDuration = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    if (episodeDuration == "00:00:00")
        printf ("==> No duration: %d\t%s  %s\n", SERIES_NUMBER, seriesTitle, episodeURL) >> ERROR_FILE
    next
}

# Extract the episode title
/
/ {
    split ($0,fld,"[<>]")
    episodeTitle = fld[3]
    gsub (/&/,"\\&", episodeTitle)
    next
}

# There are a number of variants in the "episodeNumber" string
#
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

# Extract episode number
/
.*span itemprop="episodeNumber">/ {
    # If show is a Prequel, use "P"
    if (episodeURL ~ /\/prequel/)
        showType = "P"
    # If show is a Miniseries, use "M"
    if (episodeURL ~ /\/miniseries/)
        showType = "M"
    # If we don't know what kind of show it is, use "S"
    if (showType == "")
        showType = "S"
    # Default episodeType to "E"
    episodeType = "E"
    # Default bonus, parttwo, and christmasspecials episodeType to "X"
    if (episodeURL ~ /bonus\/|[0-9]{1,2}parttwo\/|christmasspecial\//)
        episodeType = "X"
    # If episode is a Trailer, set episodeType to "T"
    if (episodeURL ~ /\/trailer/)
        episodeType = "T"
    # jamaicainn, newworlds & newtonslaw bonus or trailer seasonNumber should be 1
    if (episodeURL ~ \
        /\/jamaicainn\/bonus\/|\/jamaicainn\/trailer\/|\/newworlds\/bonus\/|\/newtonslaw\/bonus\// \
        && seasonNumber != 1) {
        split (episodeURL, part, "/")
        printf ("==> Corrected mismatch: https://acorn.tv/%s/%s was series %d\n", \
               part[4], part[5], seasonNumber) >> ERROR_FILE
        seasonNumber = 1
    }
    # Plain christmasspecial, seriesfinale don't increment seasonNumber
    if (episodeURL ~ /\/christmasspecial\/|\/seriesfinale\//) {
        split (episodeURL, part, "/")
        printf ("==> Corrected mismatch: https://acorn.tv/%s/%s was series %d\n", \
               part[4], part[5], seasonNumber) >> ERROR_FILE
        seasonNumber -= 1
    }
    #
    split ($0,fld,"[<>]")
    episodeNumber = fld[5]
    # Feature films don't have episodes
    if (episodeURL ~ /\/featurefilm\//)
        next
    # neither should single episode series
    if (totalEpisodes == 1)
        next
    # The season number should match that in the URL
    # Birds of a Feather, Doc Martin, Murdoch, Poirot, Rebus, Vera, and others have problems
    if (episodeURL ~ \
           /\/series[0-9]{1,2}\/|[0-9]{1,2}bonus\/|[0-9]{1,2}parttwo\/|\/murdoch\/season[0-9]{1,2}/) {
        split (episodeURL, part, "/")
        URLseasonNumber = part[5]
        sub (/bonus/,"",URLseasonNumber)
        # Foyle's War has sets: foyleswar/series9set8bonus/
        sub (/set[0-9]/,"",URLseasonNumber)
        # Midsomer Murders has two parts: midsomermurders/series19parttwo/
        sub (/parttwo/,"",URLseasonNumber)
        sub (/christmasspecial/,"",URLseasonNumber)
        sub (/[[:alpha:]]*/,"",URLseasonNumber)
        if (URLseasonNumber != seasonNumber) {
            printf ("==> Corrected mismatch: https://acorn.tv/%s/%s was series %d\n", \
               part[4], part[5], seasonNumber) >> ERROR_FILE
            seasonNumber = URLseasonNumber
        }
    }
    # Jamaica Inn has wrong episode numbers as of 180918 (may be temporary)
    if (episodeURL ~ /jamaicainn\/episode-[1-9]/) {
        split (episodeURL, episode, "-")
        if (episodeNumber != episode[2]) {
            printf ("==> Corrected mismatch: %s was episode %d\n", \
               episodeURL, episodeNumber) >> ERROR_FILE
            episodeNumber = episode[2]
        }
    }

    if ((episodeNumber + 0) == 0)
        print "==> Episode number is 00: " episodeURL >> ERROR_FILE
    printf ("%d\t=HYPERLINK(\"%s\";\"%s, %s%02d%s%02d, %s\"\)\t\t\t%s\n", \
        SERIES_NUMBER, episodeURL, seriesTitle, showType, seasonNumber, episodeType, episodeNumber, \
        episodeTitle, episodeDuration) >> EPISODE_INFO_FILE
    showType = ""
    next
}

/-- Viewers Also Watched --/ {
    print (episodeLinesFound == 1 ? "=0" : "=" numEpisodesStr) >> NUM_EPISODES_FILE
    if (description == "")
        printf ("==> No description: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    description = ""
    #
    seriesDuration = sprintf ("%02d:%02d:%02d", seriesHrs, seriesMins, seriesSecs)
    print seriesDuration >> DURATION_FILE
    if (seriesDuration == "00:00:00")
        printf ("==> No duration: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    seriesSecs = 0
    seriesMins = 0
    seriesHrs = 0
    seriesDuration = ""
    #
    SERIES_NUMBER += 1
}

END {
    # Return the total duration of all series
    printf ("%02d:%02d:%02d\n", totalHrs, totalMins, totalSecs)
}

# Create column files with lists of series titles, descriptions, number
# of seasons, and number of episodes, to be pasted into a spreadsheet.
# Note that IN_CANADA affects processing.
#
# Create a separate file with a line for each episode containing
# seriesNumber, episodeURL, seriesTitle, seasonNumber, episodeNumber,
# episodeTitle, & episodeDuration with the same columns as the primary
# spreadsheet so they can be combined into one.
#
# Create a separate file with a line for each series duration
#
# Return the total duration of all series
#
# INVOCATION:
#       curl -s https://acorn.tv/192/ \
#           | awk -v TITLE_FILE=$TITLE_FILE -v DESCRIPTION_FILE=$DESCRIPTION_FILE \
#           -v NUM_SEASONS_FILE=$NUM_SEASONS_FILE -v NUM_EPISODES_FILE=$NUM_EPISODES_FILE \
#           -v EPISODE_CURL_FILE=$EPISODE_CURL_FILE -v IN_CANADA=$IN_CANADA \
#           -v EPISODE_INFO_FILE=$EPISODE_INFO_FILE -v SERIES_NUMBER=$lastRow \
#           -v DURATION_FILE=$DURATION_FILE -v ERROR_FILE=$ERROR_FILE -f getAcornFrom-seriesPages.awk
#
# INPUT:
#        <title>Acorn TV | 19-2</title>
#  ---
#        19-2
#        <meta itemprop="numberOfEpisodes" content="30" />
#        <meta itemprop="numberOfSeasons" content="3" />
#  ---
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#        <meta itemprop="numberOfEpisodes" content="10" />
#  ---
#
"The writing is sublime" (New \
#        York Times) in this anything-but-a-procedural cop drama ... as reluctant partners \
#        patrolling the streets of Montreal. Not Available in Canada. CC Available.

#  ---
#
We're sorry, but Acorn TV is not available in this territory.

#  ---
#        <meta itemprop="seasonNumber" content="1" />
#  ---
#
#  ---
#        <meta itemprop="timeRequired" content="T44M06S" />
#  ---
#
Partners

#  ---
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

#
#
#
#  OUTPUT:
#       $EPISODE_INFO_FILE
#

# Extract the series title
/<title>/ {
    sub (/.*<title>Acorn TV \| /,"")
    sub (/<.*/,"")
    gsub (/&/,"\\&")
    gsub (/"/,"\"\"")
    # gsub (/'/,"'")
    if (match ($0, /^The /)) {
        $0 = substr($0, 5) ", The"
    }
    episodeLinesFound = 0
    numEpisodesStr = ""
    seriesTitle = $0
    print seriesTitle >> TITLE_FILE
    next
}

# Extract the number of episodes in the series
/itemprop="numberOfEpisodes"/ {
    split ($0,fld,"\"")
    episodeLinesFound += 1
    if (episodeLinesFound == 1)
        totalEpisodes = fld[4]
    if (episodeLinesFound != 1)
        numEpisodesStr = numEpisodesStr "+" fld[4]
    next
}

# Extract the number of seasons in the series
/itemprop="numberOfSeasons"/ {
    split ($0,fld,"\"")
    numSeasons = fld[4]
    if ((numSeasons + 0) == 0)
        printf ("==> No seasons: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    print numSeasons >> NUM_SEASONS_FILE
    next
}

/Acorn TV is not available in this territory/ {
    printf ("==> Not available here: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
}

# Extract the series description
/id="franchise-description"/ {
    # get rid of boilerplate
    split ($0,fld,"[<>]")
    description = fld[3]
    # get rid of unnecessary characters and text
    gsub (/\\/,"",description)
    if (IN_CANADA != "yes") {
        sub (/Series [[:digit:]]* not available in Canada\./,"",description)
        sub (/Not [Aa]vailable in Canada\./,"",description)
        sub (/NOT AVAILABLE IN CANADA\./,"",description)
    }
    sub (/CC Available\. CC Available/,"CC Available",description)
    # fix sloppy input spacing
    sub (/\.CC Available/,". CC Available",description)
    gsub (/ \./,".",description)
    gsub (/  */," ",description)
    sub (/^ */,"",description)
    sub (/ *$/,"",description)
    # fix funky HTML characters
    gsub (/'/,"'",description)
    # fix unmatched quotes
    numQuotes = gsub(/"/,"\"",description)
    if ((numQuotes % 2) == 1) {
        printf ("==> Corrected unmatched quote (%d): %d\t%s\n", numQuotes, SERIES_NUMBER, \
                seriesTitle) >> ERROR_FILE
        description = description " \""
    }
    print description >> DESCRIPTION_FILE
    next
}

# Extract the seasonNumber
# Note that Acorn "season" numbers may not correspond to actual season numbers.
# They simply refer to the number of seasons available on Acorn.
/<meta itemprop="seasonNumber" content="/ {
    split ($0,fld,"\"")
    seasonNumber = fld[4]
    next
}

# Extract the episode URL
/> EPISODE_CURL_FILE
    next
}

# Extract the episode duration
/<meta itemprop="timeRequired"/ {
    split ($0,fld,"\"")
    split (fld[4],tm,/[TMS]/)
    secs = tm[3]
    mins = tm[2] + int(secs / 60)
    hrs =  int(mins / 60)
    secs %= 60; mins %= 60
    #
    seriesSecs += secs
    seriesMins += mins + int(seriesSecs / 60)
    seriesHrs += hrs + int(seriesMins / 60)
    seriesSecs %= 60; seriesMins %= 60
    #
    totalSecs += secs
    totalMins += mins + int(totalSecs / 60)
    totalHrs += hrs + int(totalMins / 60)
    totalSecs %= 60; totalMins %= 60
    #
    episodeDuration = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    if (episodeDuration == "00:00:00")
        printf ("==> No duration: %d\t%s  %s\n", SERIES_NUMBER, seriesTitle, episodeURL) >> ERROR_FILE
    next
}

# Extract the episode title
/
/ {
    split ($0,fld,"[<>]")
    episodeTitle = fld[3]
    gsub (/&/,"\\&", episodeTitle)
    next
}

# There are a number of variants in the "episodeNumber" string
#
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

#
Series 10 (Sets 13 - 14): Episode 1

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

#
#
A Dance to the Music of Time: Episode 1

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

# Extract episode number
/
.*span itemprop="episodeNumber">/ {
    # If show is a Prequel, use "P"
    if (episodeURL ~ /\/prequel/)
        showType = "P"
    # If show is a Miniseries, use "M"
    if (episodeURL ~ /\/miniseries/)
        showType = "M"
    # If we don't know what kind of show it is, use "S"
    if (showType == "")
        showType = "S"
    # Default episodeType to "E"
    episodeType = "E"
    # Default bonus, parttwo, and christmasspecials episodeType to "X"
    if (episodeURL ~ /bonus\/|[0-9]{1,2}parttwo\/|christmasspecial\//)
        episodeType = "X"
    # jamaicainn, newworlds & newtonslaw bonus seasonNumber should be 1
    if (episodeURL ~ /\/jamaicainn\/bonus\/|\/newworlds\/bonus\/|\/newtonslaw\/bonus\// && seasonNumber != 1) {
        split (episodeURL, part, "/")
        printf ("==> Corrected mismatch: https://acorn.tv/%s/%s was series %d\n", \
               part[4], part[5], seasonNumber) >> ERROR_FILE
        seasonNumber = 1
    }
    # Plain christmasspecial, seriesfinale don't increment seasonNumber
    if (episodeURL ~ /\/christmasspecial\/|\/seriesfinale\//) {
        split (episodeURL, part, "/")
        printf ("==> Corrected mismatch: https://acorn.tv/%s/%s was series %d\n", \
               part[4], part[5], seasonNumber) >> ERROR_FILE
        seasonNumber -= 1
    }
    #
    split ($0,fld,"[<>]")
    episodeNumber = fld[5]
    # Feature films don't have episodes
    if (episodeURL ~ /\/featurefilm\//)
        next
    # neither should single episode series
    if (totalEpisodes == 1)
        next
    # The season number should match that in the URL
    # Birds of a Feather, Doc Martin, Murdoch, Poirot, Rebus, Vera, and others have problems
    if (episodeURL ~ \
           /\/series[0-9]{1,2}\/|[0-9]{1,2}bonus\/|[0-9]{1,2}parttwo\/|\/murdoch\/season[0-9]{1,2}/) {
        split (episodeURL, part, "/")
        URLseasonNumber = part[5]
        sub (/bonus/,"",URLseasonNumber)
        # Foyle's War has sets: foyleswar/series9set8bonus/
        sub (/set[0-9]/,"",URLseasonNumber)
        # Midsomer Murders has two parts: midsomermurders/series19parttwo/
        sub (/parttwo/,"",URLseasonNumber)
        sub (/christmasspecial/,"",URLseasonNumber)
        sub (/[[:alpha:]]*/,"",URLseasonNumber)
        if (URLseasonNumber != seasonNumber) {
            printf ("==> Corrected mismatch: https://acorn.tv/%s/%s was series %d\n", \
               part[4], part[5], seasonNumber) >> ERROR_FILE
            seasonNumber = URLseasonNumber
        }
    }
    # Jamaica Inn has wrong episode numbers as of 180918 (may be temporary)
    if (episodeURL ~ /jamaicainn\/episode-[1-9]/) {
        split (episodeURL, episode, "-")
        if (episodeNumber != episode[2]) {
            printf ("==> Corrected mismatch: %s was episode %d\n", \
               episodeURL, episodeNumber) >> ERROR_FILE
            episodeNumber = episode[2]
        }
    }

    if ((episodeNumber + 0) == 0)
        print "==> Episode number is 00: " episodeURL >> ERROR_FILE
    printf ("%d\t=HYPERLINK(\"%s\";\"%s, %s%02d%s%02d, %s\"\)\t\t\t%s\n", \
        SERIES_NUMBER, episodeURL, seriesTitle, showType, seasonNumber, episodeType, episodeNumber, \
        episodeTitle, episodeDuration) >> EPISODE_INFO_FILE
    showType = ""
    next
}

/-- Viewers Also Watched --/ {
    print (episodeLinesFound == 1 ? "=0" : "=" numEpisodesStr) >> NUM_EPISODES_FILE
    if (description == "")
        printf ("==> No description: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    description = ""
    #
    seriesDuration = sprintf ("%02d:%02d:%02d", seriesHrs, seriesMins, seriesSecs)
    print seriesDuration >> DURATION_FILE
    if (seriesDuration == "00:00:00")
        printf ("==> No duration: %d\t%s\n", SERIES_NUMBER, seriesTitle) >> ERROR_FILE
    seriesSecs = 0
    seriesMins = 0
    seriesHrs = 0
    seriesDuration = ""
    #
    SERIES_NUMBER += 1
}

END {
    # Return the total duration of all series
    printf ("%02d:%02d:%02d\n", totalHrs, totalMins, totalSecs)
}


tools: fix date detection

tools: fix date detection

The date detection assumed month format as MM but it is actually
M or MM.

Fixes: 7a3ed3640d65 ("tools: introduce log analyzer")
Signed-off-by: Jan Viktorin <3eab6ac54c005c4408f608556a6663b7f2abede7@fit.vutbr.cz>

#! /usr/bin/awk -f

function log_trim_left(line)
{
	sub(/^.+\[(Trace|Debug|Information|Notice|Warning|Error|Critical|Fatal)\] /, "", line)
	return line
}

function log_trim_right(line)
{
	sub(/\([^)]*:[0-9]*\)$/, "", line)
	return line
}

function log_extract_pid_tid(line)
{
	sub(/:\{/, "-", line)
	sub(/,.+$/, "", line)

	return line
}

function extract_message(line)
{
	line = log_trim_left(line)
	return log_trim_right(line)
}

function time2ms(time, part, result)
{
	split(time, part, "[:.]")

	result = part[1] * 60 * 60 * 1000
	result += part[2] * 60 * 1000
	result += part[3] * 1000
	result += part[4]

	return result
}

function time_stat(start, end)
{
	if (start == 0)
		return "??:??:??.???.." end " ?ms"

	return start ".." end " " (time2ms(end) - time2ms(start)) "ms"
}

function report_counts(title, counts)
{
	if (counts[0] > 0) {
		print title ":", counts[0]

		for (key in counts) {
			if (key == 0)
				continue

			print "*", key ":", counts[key]
		}
	}
}

function clear_array(array, first_zero) {
	for (key in array)
		delete array[key]

	if (first_zero)
		array[0] = 0
}

function count_array(array, key, count) {
	count = 0
	for (key in array)
		count += 1

	return count
}

function report_server_stats()
{
	report_counts("Criticals", criticals)
	report_counts("Fatals", fatals)

	report_counts("XML-UI clients", xmlui_clients)

	if (xmlui_requests[0] > 0) {
		print "Requests"

		for (key in xmlui_requests) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "ip")
				continue

			ip = xmlui_requests[multikey[1], "ip"]
			start = xmlui_requests[multikey[1], "start"]
			end = xmlui_requests[multikey[1], "end"]
			info = xmlui_requests[multikey[1], "info"]

			print "*", ip ":", time_stat(start, end), info
		}
	}

	report_counts("REST-UI clients", restui_clients)

	if (count_array(restui_requests_total) > 0) {
		print "REST-UI Requests"
		for (key in restui_requests_total) {
			print "*", key ":", restui_requests_total[key]
		}
	}

	if (count_array(restui_requests_uri) > 0) {
		print "REST-UI URIs"
		for (key in restui_requests_uri) {
			print "*", key ":", restui_requests_uri[key]
		}
	}

	report_counts("GWS clients", gws_clients)

	if (transactions[0] > 0) {
		print "Transactions"

		for (key in transactions) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "id")
				continue

			id = transactions[multikey[1], "id"]
			type = transactions[multikey[1], "type"]
			start = transactions[multikey[1], "start"]
			end = transactions[multikey[1], "end"]

			print "*", id ":", time_stat(start, end), type
		}
	}
}

function clear_stats()
{
	clear_array(fatals, 1)
	clear_array(criticals, 1)
	clear_array(xmlui_clients, 1)
	clear_array(xmlui_requests, 1)
	clear_array(restui_clients, 1)
	clear_array(restui_requests_total, 0)
	clear_array(restui_requests_uri, 0)
	clear_array(gws_clients, 1)
	clear_array(transactions, 1)
	clear_array(thread_pools, 1)

	types_count = 0
	enums_count = 0
	devs_count = 0
	queries_count = 0
	instances_count = 0
	rest_mappings = 0
}

BEGIN {
	clear_stats()
	HAS_DATE = 0
}

/ (19[0-9]{2}|20[0-9]{2})-[012]?[0-9]-[0-3][0-9] / {
	HAS_DATE = 1
}

!/ (19[0-9]{2}|20[0-9]{2})-[012]?[0-9]-[0-3][0-9] / {
	HAS_DATE = 0
}

/PermitAuthProvider/ && /SOME AUTHS/ {
	print
}

/PermitAuthProvider/ {
	next
}

/\[Fatal\]/ {
	_line = $0
	_msg = extract_message($0)

	if (fatals[_msg] == 0)
		fatals[0] += 1

	fatals[_msg] += 1
	if (fatals[_msg] == 1)
		print line
}

/\[Critical\]/ {
	_line = $0
	_msg = extract_message($0)

	if (criticals[_msg] == 0)
		criticals[0] += 1

	criticals[_msg] += 1
	if (criticals[_msg] == 1)
		print line
}

/^Application / && / version / {
	report_server_stats()
	clear_stats()

	if (HAS_DATE)
		_time = $2 " " $3
	else
		_time = $2

	print "# Server run report " _time
	print extract_message($0)
}
/^Application / && / Poco library / {
	print extract_message($0)
}

/IcuLocaleManager/ && /using ICU / {
	print extract_message($0)
}

/IcuLocaleManager/ && /resource dir: / {
	print extract_message($0)
}

/IcuLocaleManager/ && /detected locale: / {
	$0 = log_trim_left($0)
	print "locale: ", $3
}

/PocoDaoManager/ && /initialize database/ {
	print extract_message($0)
}

/PocoDaoManager/ && /Session / && /transact/ {
	print extract_message($0)
}

/PocoSqitchInitializer/ && /unsafe deploy / {
	print extract_message($0)
}

/PocoSimpleDBInitializer/ && /executed script / {
	print extract_message($0)
}

/TypeInfoProvider/ && /register type / {
	types_count += 1
}

/EnumInfoProvider/ && /register enum / {
	enums_count += 1
}

/DevicesSAXHandler/ && /Warning/ {
	print extract_message($0)
}

/DeviceInfoProvider/ && /register device / {
	devs_count += 1
}

/ loading query / {
	queries_count += 1
}

/DependencyInjector/ && /successfully created / {
	instances_count += 1
}

/RestRouter/ && /mapping / {
	rest_mappings += 1
}

/^Application/ && /starting runner/ {
	print extract_message($0)
}

/ creating thread pool / {
	$0 = log_trim_left($0)
	_curr = thread_pools[0] + 1
	thread_pools[0] = _curr
	thread_pools[_curr, "min"] += $5
	thread_pools[_curr, "max"] += $7
}

/LoopRunner/ && / started / && / loops / {
	print "Value types:   ", types_count
	print "Enum subtypes: ", enums_count
	print "Device types:  ", devs_count
	print "DB queries:    ", queries_count
	print "DI instances:  ", instances_count
	print "REST mappings: ", rest_mappings

	$0 = log_trim_left($0)
	print "Loops:         ", $2

	threads_min = 0
	threads_max = 0

	for (i = 0; i < thread_pools[0]; ++i) {
		threads_min += thread_pools[i + 1, "min"]
		threads_max += thread_pools[i + 1, "max"]
	}

	print "Thread pools:  ", thread_pools[0]
	print "Threads:       ", threads_min ".." threads_max
}

/XmlRequestHandlerFactory/ && / accepting client / {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (xmlui_clients[_ip] == 0)
		xmlui_clients[0] += 1

	xmlui_clients[_ip] += 1
	xmlui_requests_tmp[_id, "ip"] = _ip
}

/XmlRequestHandlerFactory/ && /document: <request/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	xmlui_requests_tmp[_id, "start"] = $(2 + HAS_DATE)

	_msg = extract_message($0)

	if (match(_msg, / ns="[^"]+" /) > 0) {
		_ns = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _ns)
		sub(/".*$/, "", _ns)
	}
	if (match(_msg, / type="[^"]+" /) > 0) {
		_type = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _type)
		sub(/".*$/, "", _type)
	}

	xmlui_requests_tmp[_id, "info"] = _ns ":" _type
}

/XmlRequestHandler/ && /<response/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	_curr = xmlui_requests[0] + 1
	xmlui_requests[0] = _curr

	xmlui_requests[_curr, "ip"] = xmlui_requests_tmp[_id, "ip"]
	delete xmlui_requests_tmp[_id, "ip"]
	xmlui_requests[_curr, "start"] = xmlui_requests_tmp[_id, "start"]
	delete xmlui_requests_tmp[_id, "start"]
	xmlui_requests[_curr, "info"] = xmlui_requests_tmp[_id, "info"]
	delete xmlui_requests_tmp[_id, "info"]

	xmlui_requests[_curr, "end"] = $(2 + HAS_DATE)
}

/PocoRestRequestFactory/ && /handling request/ {
	$0 = extract_message($0)
	restui_requests_total[$3] += 1
	restui_requests_uri[$4] += 1

	split($6, _addr, ":")
	_ip = _addr[1]

	if (restui_clients[_ip] == 0)
		restui_clients[0] += 1

	restui_clients[_ip] += 1
}

/WebSocketRequestHandler/ && /connection from / {
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (gws_clients[_ip] == 0)
		gws_clients[0] += 1

	gws_clients[_ip] += 1
}

/ transaction / && / started / {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	transactions_tmp[_id, "start"] = _time
}

/ transaction / && (/ commit / || / rollback /) {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	_type = $3

	_curr = transactions[0] + 1
	transactions[0] = _curr

	transactions[_curr, "start"] = transactions_tmp[_id, "start"]
	delete transactions_tmp[_id, "start"]

	transactions[_curr, "end"] = _time
	transactions[_curr, "type"] = _type
	transactions[_curr, "id"] = _id
}

END {
	report_server_stats()
	clear_stats()
}

#! /usr/bin/awk -f

function log_trim_left(line)
{
	sub(/^.+\[(Trace|Debug|Information|Notice|Warning|Error|Critical|Fatal)\] /, "", line)
	return line
}

function log_trim_right(line)
{
	sub(/\([^)]*:[0-9]*\)$/, "", line)
	return line
}

function log_extract_pid_tid(line)
{
	sub(/:\{/, "-", line)
	sub(/,.+$/, "", line)

	return line
}

function extract_message(line)
{
	line = log_trim_left(line)
	return log_trim_right(line)
}

function time2ms(time, part, result)
{
	split(time, part, "[:.]")

	result = part[1] * 60 * 60 * 1000
	result += part[2] * 60 * 1000
	result += part[3] * 1000
	result += part[4]

	return result
}

function time_stat(start, end)
{
	if (start == 0)
		return "??:??:??.???.." end " ?ms"

	return start ".." end " " (time2ms(end) - time2ms(start)) "ms"
}

function report_counts(title, counts)
{
	if (counts[0] > 0) {
		print title ":", counts[0]

		for (key in counts) {
			if (key == 0)
				continue

			print "*", key ":", counts[key]
		}
	}
}

function clear_array(array, first_zero) {
	for (key in array)
		delete array[key]

	if (first_zero)
		array[0] = 0
}

function count_array(array, key, count) {
	count = 0
	for (key in array)
		count += 1

	return count
}

function report_server_stats()
{
	report_counts("Criticals", criticals)
	report_counts("Fatals", fatals)

	report_counts("XML-UI clients", xmlui_clients)

	if (xmlui_requests[0] > 0) {
		print "Requests"

		for (key in xmlui_requests) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "ip")
				continue

			ip = xmlui_requests[multikey[1], "ip"]
			start = xmlui_requests[multikey[1], "start"]
			end = xmlui_requests[multikey[1], "end"]
			info = xmlui_requests[multikey[1], "info"]

			print "*", ip ":", time_stat(start, end), info
		}
	}

	report_counts("REST-UI clients", restui_clients)

	if (count_array(restui_requests_total) > 0) {
		print "REST-UI Requests"
		for (key in restui_requests_total) {
			print "*", key ":", restui_requests_total[key]
		}
	}

	if (count_array(restui_requests_uri) > 0) {
		print "REST-UI URIs"
		for (key in restui_requests_uri) {
			print "*", key ":", restui_requests_uri[key]
		}
	}

	report_counts("GWS clients", gws_clients)

	if (transactions[0] > 0) {
		print "Transactions"

		for (key in transactions) {
			if (key == 0)
				continue

			split(key, multikey, SUBSEP)
			if (multikey[2] != "id")
				continue

			id = transactions[multikey[1], "id"]
			type = transactions[multikey[1], "type"]
			start = transactions[multikey[1], "start"]
			end = transactions[multikey[1], "end"]

			print "*", id ":", time_stat(start, end), type
		}
	}
}

function clear_stats()
{
	clear_array(fatals, 1)
	clear_array(criticals, 1)
	clear_array(xmlui_clients, 1)
	clear_array(xmlui_requests, 1)
	clear_array(restui_clients, 1)
	clear_array(restui_requests_total, 0)
	clear_array(restui_requests_uri, 0)
	clear_array(gws_clients, 1)
	clear_array(transactions, 1)
	clear_array(thread_pools, 1)

	types_count = 0
	enums_count = 0
	devs_count = 0
	queries_count = 0
	instances_count = 0
	rest_mappings = 0
}

BEGIN {
	clear_stats()
	HAS_DATE = 0
}

/ (19[0-9]{2}|20[0-9]{2})-[012][0-9]-[0-3][0-9] / {
	HAS_DATE = 1
}

!/ (19[0-9]{2}|20[0-9]{2})-[012][0-9]-[0-3][0-9] / {
	HAS_DATE = 0
}

/PermitAuthProvider/ && /SOME AUTHS/ {
	print
}

/PermitAuthProvider/ {
	next
}

/\[Fatal\]/ {
	_line = $0
	_msg = extract_message($0)

	if (fatals[_msg] == 0)
		fatals[0] += 1

	fatals[_msg] += 1
	if (fatals[_msg] == 1)
		print line
}

/\[Critical\]/ {
	_line = $0
	_msg = extract_message($0)

	if (criticals[_msg] == 0)
		criticals[0] += 1

	criticals[_msg] += 1
	if (criticals[_msg] == 1)
		print line
}

/^Application / && / version / {
	report_server_stats()
	clear_stats()

	if (HAS_DATE)
		_time = $2 " " $3
	else
		_time = $2

	print "# Server run report " _time
	print extract_message($0)
}
/^Application / && / Poco library / {
	print extract_message($0)
}

/IcuLocaleManager/ && /using ICU / {
	print extract_message($0)
}

/IcuLocaleManager/ && /resource dir: / {
	print extract_message($0)
}

/IcuLocaleManager/ && /detected locale: / {
	$0 = log_trim_left($0)
	print "locale: ", $3
}

/PocoDaoManager/ && /initialize database/ {
	print extract_message($0)
}

/PocoDaoManager/ && /Session / && /transact/ {
	print extract_message($0)
}

/PocoSqitchInitializer/ && /unsafe deploy / {
	print extract_message($0)
}

/PocoSimpleDBInitializer/ && /executed script / {
	print extract_message($0)
}

/TypeInfoProvider/ && /register type / {
	types_count += 1
}

/EnumInfoProvider/ && /register enum / {
	enums_count += 1
}

/DevicesSAXHandler/ && /Warning/ {
	print extract_message($0)
}

/DeviceInfoProvider/ && /register device / {
	devs_count += 1
}

/ loading query / {
	queries_count += 1
}

/DependencyInjector/ && /successfully created / {
	instances_count += 1
}

/RestRouter/ && /mapping / {
	rest_mappings += 1
}

/^Application/ && /starting runner/ {
	print extract_message($0)
}

/ creating thread pool / {
	$0 = log_trim_left($0)
	_curr = thread_pools[0] + 1
	thread_pools[0] = _curr
	thread_pools[_curr, "min"] += $5
	thread_pools[_curr, "max"] += $7
}

/LoopRunner/ && / started / && / loops / {
	print "Value types:   ", types_count
	print "Enum subtypes: ", enums_count
	print "Device types:  ", devs_count
	print "DB queries:    ", queries_count
	print "DI instances:  ", instances_count
	print "REST mappings: ", rest_mappings

	$0 = log_trim_left($0)
	print "Loops:         ", $2

	threads_min = 0
	threads_max = 0

	for (i = 0; i < thread_pools[0]; ++i) {
		threads_min += thread_pools[i + 1, "min"]
		threads_max += thread_pools[i + 1, "max"]
	}

	print "Thread pools:  ", thread_pools[0]
	print "Threads:       ", threads_min ".." threads_max
}

/XmlRequestHandlerFactory/ && / accepting client / {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (xmlui_clients[_ip] == 0)
		xmlui_clients[0] += 1

	xmlui_clients[_ip] += 1
	xmlui_requests_tmp[_id, "ip"] = _ip
}

/XmlRequestHandlerFactory/ && /document: <request/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	xmlui_requests_tmp[_id, "start"] = $(2 + HAS_DATE)

	_msg = extract_message($0)

	if (match(_msg, / ns="[^"]+" /) > 0) {
		_ns = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _ns)
		sub(/".*$/, "", _ns)
	}
	if (match(_msg, / type="[^"]+" /) > 0) {
		_type = substr(_msg, RSTART, RLENGTH)
		sub(/^.*="/, "", _type)
		sub(/".*$/, "", _type)
	}

	xmlui_requests_tmp[_id, "info"] = _ns ":" _type
}

/XmlRequestHandler/ && /<response/ {
	_id = log_extract_pid_tid($(3 + HAS_DATE))
	_curr = xmlui_requests[0] + 1
	xmlui_requests[0] = _curr

	xmlui_requests[_curr, "ip"] = xmlui_requests_tmp[_id, "ip"]
	delete xmlui_requests_tmp[_id, "ip"]
	xmlui_requests[_curr, "start"] = xmlui_requests_tmp[_id, "start"]
	delete xmlui_requests_tmp[_id, "start"]
	xmlui_requests[_curr, "info"] = xmlui_requests_tmp[_id, "info"]
	delete xmlui_requests_tmp[_id, "info"]

	xmlui_requests[_curr, "end"] = $(2 + HAS_DATE)
}

/PocoRestRequestFactory/ && /handling request/ {
	$0 = extract_message($0)
	restui_requests_total[$3] += 1
	restui_requests_uri[$4] += 1

	split($6, _addr, ":")
	_ip = _addr[1]

	if (restui_clients[_ip] == 0)
		restui_clients[0] += 1

	restui_clients[_ip] += 1
}

/WebSocketRequestHandler/ && /connection from / {
	$0 = log_trim_left($0)

	split($3, _addr, ":")
	_ip = _addr[1]

	if (gws_clients[_ip] == 0)
		gws_clients[0] += 1

	gws_clients[_ip] += 1
}

/ transaction / && / started / {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	transactions_tmp[_id, "start"] = _time
}

/ transaction / && (/ commit / || / rollback /) {
	_time = $(2 + HAS_DATE)
	_thread = log_extract_pid_tid($(3 + HAS_DATE))
	$0 = extract_message($0)
	_id = $2 "-" _thread
	_type = $3

	_curr = transactions[0] + 1
	transactions[0] = _curr

	transactions[_curr, "start"] = transactions_tmp[_id, "start"]
	delete transactions_tmp[_id, "start"]

	transactions[_curr, "end"] = _time
	transactions[_curr, "type"] = _type
	transactions[_curr, "id"] = _id
}

END {
	report_server_stats()
	clear_stats()
}


Add <th> support to amdtbl.awk

Add <th> support to amdtbl.awk

#!/usr/bin/awk -f
BEGIN {
    FS="|"
    blank="[    ]"
    blanks=blank"+"
}

# Table head
/^\{\{.*\|.*\}\}$/ {
    if(! intable) print "
"
    intable=1
    print "
"

    $0 = substr($0, 2, length($0)-2)
    split($0, a)
    for(i in a) {
        align="center"
        if(match(a[i], "^" blank blanks)) {
            if(! match(a[i], blanks blank "$"))
                align="right"
        } else if(match(a[i], blanks blank "$"))
            align="left"
        print "" a[i] "	"
    }
    print "
"
    next
}

# Table lines
/^\{.*\|.*\}$/ {
    if(! intable) print "
"
    intable=1
    print "
"

    $0 = substr($0, 2, length($0)-2)
    split($0, a)
    for(i in a) {
        align="center"
        if(match(a[i], "^" blank blanks)) {
            if(! match(a[i], blanks blank "$"))
                align="right"
        } else if(match(a[i], blanks blank "$"))
            align="left"
        print "" a[i] "	"
    }
    print "
"
    next
}

# everything else
{
    if(intable) print "
"
    intable=0
    print
}

#!/usr/bin/awk -f
BEGIN {
    FS="|"
    blank="[    ]"
    blanks=blank"+"
}

# Table lines
/^\{.*\|.*\}$/ {
    if(! intable) print "
"
    intable=1
    print "
"

    $0 = substr($0, 2, length($0)-2)
    split($0, a)
    for(i in a) {
        align="center"
        if(match(a[i], "^" blank blanks)) {
            if(! match(a[i], blanks blank "$"))
                align="right"
        } else if(match(a[i], blanks blank "$"))
            align="left"
        print "" a[i] "	"
    }
    print "
"
    next
}

# everything else
{
    if(intable) print "
"
    intable=0
    print
}


build.awk: update wiki generator

build.awk: update wiki generator

#!/usr/bin/gawk -f

# Not all 4.x versions of gawk can handle @include without ".awk" extension
# But the build.awk script and the single build should support gawk 4.0+.
@include "include/Commons.awk"
@include "include/Utils.awk"
@include "include/LanguageData.awk"
@include "include/LanguageHelper.awk"
@include "metainfo.awk"

function init() {
    BuildPath            = "build/"
    Trans                = BuildPath Command
    TransAwk             = Trans ".awk"

    ManPath              = "man/"
    Man                  = ManPath Command ".1"
    ManTemplate          = Man ".template.md"
    ManMarkdown          = Man ".md"
    ManHtmlTemplate      = Man ".template.html"
    ManHtml              = Man ".html"

    PagesPath            = "gh-pages/"
    BadgeDownload        = PagesPath "images/badge-download"
    BadgeRelease         = PagesPath "images/badge-release"
    Index                = PagesPath "index.md"

    ReadmePath           = "./"
    ReadmeTemplate       = ReadmePath "README.template.md"
    Readme               = ReadmePath "README.md"

    WikiPath             = "wiki/"
    WikiHome             = WikiPath "Home.md"
    WikiLanguages        = WikiPath "Languages.md"
    WikiLanguagesHtml    = WikiLanguages ".html"

    RegistryPath         = "registry/"
    MainRegistryTemplate = RegistryPath "index.template.trans"
    MainRegistry         = RegistryPath "index.trans"
}

function man(    text) {
    text = readFrom(ManTemplate)
    gsub(/\$Version\$/, Version, text)
    gsub(/\$ReleaseDate\$/, ReleaseDate, text)
    writeTo(text, ManMarkdown)

    if (fileExists(ManHtmlTemplate))
        system("pandoc -s -f markdown-smart -t html --toc --toc-depth 1 --template " ManHtmlTemplate " " ManMarkdown " -o " ManHtml)
    return system("pandoc -s -f markdown-smart -t man " ManMarkdown " -o " Man)
}

function readme(    code, col, cols, content, group, i, iso, j, num, r, rows, text) {
    text = readFrom(ReadmeTemplate)

    content = getOutput("gawk -f translate.awk -- -no-ansi -h")
    gsub(/\$usage\$/, content, text)

    initBiDi(); initLocale()
    # number of language codes with stable support
    num = 0
    for (code in Locale)
        if (Locale[code]["support"] != "unstable")
            num++
    rows = int(num / 3) + (num % 3 ? 1 : 0)
    cols[0][0] = cols[1][0] = cols[2][0] = NULLSTR
    i = 0
    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "compName"
    for (code in Locale) {
        # Ignore unstable languages
        if (Locale[code]["support"] == "unstable") continue

        col = int(i / rows)
        append(cols[col], code)
        i++
    }
    PROCINFO["sorted_in"] = saveSortedIn
    r = "| Language | Code | Language | Code | Language | Code |" RS \
        "| :------: | :--: | :------: | :--: | :------: | :--: |" RS
    for (i = 0; i < rows; i++) {
        r = r "| "
        for (j = 0; j < 3; j++)
            if (cols[j][i]) {
                split(getISO(cols[j][i]), group, "-")
                iso = group[1]

                r = r "**[" getName(cols[j][i]) "](" "http://en.wikipedia.org/wiki/ISO_639:" iso \
                    ")**
 **" getEndonym(cols[j][i]) "** | **`" cols[j][i] "`** | "
            }
        r = r RS
    }
    gsub(/\$code-list\$/, r, text)

    content = readFrom(WikiHome)
    gsub(/\$wiki-home\$/, content, text)

    writeTo(text, Readme)
    return 0
}

function wiki(    code, group, iso, language, saveSortedIn) {
    initBiDi(); initLocale()

    #print "***" length(Locale) "*** *languages in total. "
    print "*Generated from the source code of Translate Shell " Version ".*\n" > WikiLanguages
    print "*Version: [English](https://github.com/soimort/translate-shell/wiki/Languages) " \
        "| [Chinese Simplified](https://github.com/soimort/translate-shell/wiki/Languages-%28%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%29)*\n" > WikiLanguages
    print "| Code | Name | Family | [Writing system](https://github.com/soimort/translate-shell/wiki/Writing-Systems-and-Fonts) | Is [RTL](http://en.wikipedia.org/wiki/Right-to-left)? | Has dictionary? |" > WikiLanguages
    print "| :--: | ---: | -----: | :------------: | :---------------------------------------------------: | :-------------: |" > WikiLanguages
    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (code in Locale) {
        # Ignore unstable languages and non-languages
        if (Locale[code]["support"] == "unstable") continue
        if (Locale[code]["status"] == "non-language") continue

        split(getISO(code), group, "-")
        iso = group[1]
        #split(getName(code), group, " ")
        #language = length(group) == 1 ? group[1] "_language" :
        #    group[2] ~ /^\(.*\)$/ ? group[1] "_language" : join(group, "_")
        print sprintf("| **`%s`**
 [`%s`](%s) | **[%s](%s)**
 **%s** | %s | `%s` | %s | %s |",
                      getCode(code), iso, "http://www.ethnologue.com/language/" iso,
                      getName(code), "http://en.wikipedia.org/wiki/ISO_639:" iso, getEndonym(code),
                      getFamily(code), getScript(code),
                      isRTL(code) ? "✓" : NULLSTR,
                      hasDictionary(code) ? "✓" : NULLSTR) > WikiLanguages
    }
    PROCINFO["sorted_in"] = saveSortedIn

    return system("pandoc -s -t html " WikiLanguages " -o " WikiLanguagesHtml)
}

function doc() {
    man()
    readme()
    wiki()
    return 0
}

function readSqueezed(fileName, squeezed,    group, line, ret) {
    if (fileName ~ /\*$/ || fileName ~ /_$/) # glob simulation
        return readSqueezed(fileName ".awk", squeezed)

    ret = NULLSTR
    if (fileExists(fileName))
        while (getline line < fileName) {
            match(line, /^[[:space:]]*@include[[:space:]]*"(.*)"$/, group)
            if (RSTART) { # @include
                if (group[1] ~ /\.awk$/)
                    append(Includes, group[1])

                if (ret) ret = ret RS
                ret = ret readSqueezed(group[1], squeezed)
            } else if (!squeezed || line = squeeze(line)) { # effective LOC
                if (ret) ret = ret RS
                ret = ret line
            }
        }
    return ret
}

function build(target, type,    i, group, inline, line, temp) {
    # Default target: bash
    if (!target) target = "bash"

    ("mkdir -p " parameterize(BuildPath)) | getline

    if (target == "bash" || target == "zsh") {

        print "#!/usr/bin/env " target > Trans

        if (fileExists("DISCLAIMER")) {
            print "#" > Trans
            while (getline line < "DISCLAIMER")
                print "# " line > Trans
            print "#" > Trans
        }

        print "export TRANS_ENTRY=\"$0\"" > Trans
        print "if [[ ! $LANG =~ (UTF|utf)-?8$ ]]; then export LANG=en_US.UTF-8; fi" > Trans

        print "read -r -d '' TRANS_PROGRAM << 'EOF'" > Trans
        print readSqueezed(EntryPoint, TRUE) > Trans
        print "EOF" > Trans

        print "read -r -d '' TRANS_MANPAGE << 'EOF'" > Trans
        if (fileExists(Man))
            while (getline line < Man)
                print line > Trans
        print "EOF" > Trans
        print "export TRANS_MANPAGE" > Trans

        if (type == "release")
            print "export TRANS_BUILD=release" temp > Trans
        else {
            temp = getGitHead()
            if (temp)
                print "export TRANS_BUILD=git:" temp > Trans
        }

        print "gawk -f <(echo -E \"$TRANS_PROGRAM\") - \"$@\"" > Trans

        ("chmod +x " parameterize(Trans)) | getline

        # Rebuild EntryScript
        print "#!/bin/sh" > EntryScript
        print "export TRANS_DIR=`dirname $0`" > EntryScript
        print "gawk \\" > EntryScript
        for (i = 0; i < length(Includes) - 1; i++)
            print "-i \"${TRANS_DIR}/" Includes[i] "\" \\" > EntryScript
        print "-f \"${TRANS_DIR}/" Includes[i] "\" -- \"$@\"" > EntryScript
        ("chmod +x " parameterize(EntryScript)) | getline
        return 0

    } else if (target == "awk" || target == "gawk") {

        "uname -s" | getline temp
        print (temp == "Darwin" ?
               "#!/usr/bin/env gawk -f" : # macOS
               "#!/usr/bin/gawk -f") > TransAwk

        print readSqueezed(EntryPoint, TRUE) > TransAwk

        ("chmod +x " parameterize(TransAwk)) | getline
        return 0

    } else {

        w("[FAILED] Unknown target: " ansi("underline", target))
        w("         Supported targets: "                                \
          ansi("underline", "bash") ", "                                \
          ansi("underline", "zsh") ", "                                 \
          ansi("underline", "gawk"))
        return 1

    }
}

function clean() {
    ("rm -f " BuildPath Command "*") | getline
    return 0
}

function release(    content, group, sha1, size, temp, text) {
    d("Updating registry ...")
    # Update registry
    text = readFrom(MainRegistryTemplate)
    gsub(/\$Version\$/, Version, text)
    writeTo(text, MainRegistry)

    d("Updating gh-pages/images ...")
    # Update gh-pages/images/badge-release
    text = readFrom(BadgeRelease ".temp")
    gsub(/\$Version\$/, Version, text)
    writeTo(text, BadgeRelease)
    system("save-to-png " BadgeRelease)
    # Update gh-pages/images/badge-download
    ("wc -c " Trans) | getline temp
    split(temp, group)
    size = int(group[1] / 1000)
    text = readFrom(BadgeDownload ".temp")
    gsub(/\$Size\$/, size, text)
    writeTo(text, BadgeDownload)
    system("save-to-png " BadgeDownload)

    d("Updating gh-pages/index.md ...")
    # Update gh-pages/index.md
    ("sha1sum " Trans) | getline temp
    split(temp, group)
    sha1 = group[1]
    content = readFrom(Readme)
    text = readFrom(Index ".temp")
    gsub(/\$sha1\$/, sha1, text)
    gsub(/\$Version\$/, Version, text)
    gsub(/\$readme\$/, content, text)
    writeTo(text, Index)

    return 0
}

function test() {
    return 0
}

BEGIN {
    init()

    pos = 0
    while (ARGV[++pos]) {
        # -target TARGET
        match(ARGV[pos], /^--?target(=(.*)?)?$/, group)
        if (RSTART) {
            target = tolower(group[2] ? group[2] : ARGV[++pos])
            continue
        }

        # -type TYPE
        match(ARGV[pos], /^--?type(=(.*)?)?$/, group)
        if (RSTART) {
            type = tolower(group[2] ? group[2] : ARGV[++pos])
            continue
        }

        # TASK
        match(ARGV[pos], /^[^\-]/, group)
        if (RSTART) {
            append(tasks, ARGV[pos])
            continue
        }
    }

    # Default task: build
    if (!anything(tasks)) tasks[0] = "build"

    for (i = 0; i < length(tasks); i++) {
        task = tasks[i]
        status = 0
        switch (task) {

        case "man":
            status = man()
            break

        case "readme":
            status = readme()
            break

        case "wiki":
            status = wiki()
            break

        case "doc":
            status = doc()
            break

        case "build":
            status = build(target, type)
            break

        case "clean":
            status = clean()
            break

        case "release":
            status = release()
            break

        case "test":
            status = test()
            break

        default: # unknown task
            status = -1
        }

        if (status == 0) {
            d("[OK] Task " ansi("bold", task) " completed.")
        } else if (status < 0) {
            w("[FAILED] Unknown task: " ansi("bold", task))
            exit 1
        } else {
            w("[FAILED] Task " ansi("bold", task) " failed.")
            exit 1
        }
    }
}

#!/usr/bin/gawk -f

# Not all 4.x versions of gawk can handle @include without ".awk" extension
# But the build.awk script and the single build should support gawk 4.0+.
@include "include/Commons.awk"
@include "include/Utils.awk"
@include "include/LanguageData.awk"
@include "include/LanguageHelper.awk"
@include "metainfo.awk"

function init() {
    BuildPath            = "build/"
    Trans                = BuildPath Command
    TransAwk             = Trans ".awk"

    ManPath              = "man/"
    Man                  = ManPath Command ".1"
    ManTemplate          = Man ".template.md"
    ManMarkdown          = Man ".md"
    ManHtmlTemplate      = Man ".template.html"
    ManHtml              = Man ".html"

    PagesPath            = "gh-pages/"
    BadgeDownload        = PagesPath "images/badge-download"
    BadgeRelease         = PagesPath "images/badge-release"
    Index                = PagesPath "index.md"

    ReadmePath           = "./"
    ReadmeTemplate       = ReadmePath "README.template.md"
    Readme               = ReadmePath "README.md"

    WikiPath             = "wiki/"
    WikiHome             = WikiPath "Home.md"
    WikiLanguages        = WikiPath "Languages.md"
    WikiLanguagesHtml    = WikiLanguages ".html"

    RegistryPath         = "registry/"
    MainRegistryTemplate = RegistryPath "index.template.trans"
    MainRegistry         = RegistryPath "index.trans"
}

function man(    text) {
    text = readFrom(ManTemplate)
    gsub(/\$Version\$/, Version, text)
    gsub(/\$ReleaseDate\$/, ReleaseDate, text)
    writeTo(text, ManMarkdown)

    if (fileExists(ManHtmlTemplate))
        system("pandoc -s -f markdown-smart -t html --toc --toc-depth 1 --template " ManHtmlTemplate " " ManMarkdown " -o " ManHtml)
    return system("pandoc -s -f markdown-smart -t man " ManMarkdown " -o " Man)
}

function readme(    code, col, cols, content, group, i, iso, j, num, r, rows, text) {
    text = readFrom(ReadmeTemplate)

    content = getOutput("gawk -f translate.awk -- -no-ansi -h")
    gsub(/\$usage\$/, content, text)

    initBiDi(); initLocale()
    # number of language codes with stable support
    num = 0
    for (code in Locale)
        if (Locale[code]["support"] != "unstable")
            num++
    rows = int(num / 3) + (num % 3 ? 1 : 0)
    cols[0][0] = cols[1][0] = cols[2][0] = NULLSTR
    i = 0
    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "compName"
    for (code in Locale) {
        # Ignore unstable languages
        if (Locale[code]["support"] == "unstable") continue

        col = int(i / rows)
        append(cols[col], code)
        i++
    }
    PROCINFO["sorted_in"] = saveSortedIn
    r = "| Language | Code | Language | Code | Language | Code |" RS \
        "| :------: | :--: | :------: | :--: | :------: | :--: |" RS
    for (i = 0; i < rows; i++) {
        r = r "| "
        for (j = 0; j < 3; j++)
            if (cols[j][i]) {
                split(getISO(cols[j][i]), group, "-")
                iso = group[1]

                r = r "**[" getName(cols[j][i]) "](" "http://en.wikipedia.org/wiki/ISO_639:" iso \
                    ")**
 **" getEndonym(cols[j][i]) "** | **`" cols[j][i] "`** | "
            }
        r = r RS
    }
    gsub(/\$code-list\$/, r, text)

    content = readFrom(WikiHome)
    gsub(/\$wiki-home\$/, content, text)

    writeTo(text, Readme)
    return 0
}

function wiki(    code, group, iso, language, saveSortedIn) {
    initBiDi(); initLocale()

    #print "***" length(Locale) "*** *languages in total. "
    print "*Generated from the source code of Translate Shell " Version ".*\n" > WikiLanguages
    print "*Version: [English](https://github.com/soimort/translate-shell/wiki/Languages) " \
        "| [Chinese Simplified](https://github.com/soimort/translate-shell/wiki/Languages-%28%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87%29)*\n" > WikiLanguages
    print "| Code | Name | Family | [Writing system](https://github.com/soimort/translate-shell/wiki/Writing-Systems-and-Fonts) | Is [RTL](http://en.wikipedia.org/wiki/Right-to-left)? | Has dictionary? |" > WikiLanguages
    print "| :--: | ---: | -----: | :------------: | :---------------------------------------------------: | :-------------: |" > WikiLanguages
    saveSortedIn = PROCINFO["sorted_in"]
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for (code in Locale) {
        # Ignore unstable languages and non-languages
        if (Locale[code]["support"] == "unstable") continue
        if (Locale[code]["status"] == "non-language") continue

        split(getISO(code), group, "-")
        iso = group[1]
        split(getName(code), group, " ")
        language = length(group) == 1 ? group[1] "_language" :
            group[2] ~ /^\(.*\)$/ ? group[1] "_language" : join(group, "_")
        print sprintf("| **`%s`**
 [`%s`](%s) | **[%s](%s)**
 **%s** | %s | `%s` | %s | %s |",
                      getCode(code), iso, "http://www.ethnologue.com/language/" iso,
                      getName(code), "http://en.wikipedia.org/wiki/" language, getEndonym(code),
                      getFamily(code), getScript(code),
                      isRTL(code) ? "✓" : NULLSTR,
                      hasDictionary(code) ? "✓" : NULLSTR) > WikiLanguages
    }
    PROCINFO["sorted_in"] = saveSortedIn

    return system("pandoc -s -t html " WikiLanguages " -o " WikiLanguagesHtml)
}

function doc() {
    man()
    readme()
    wiki()
    return 0
}

function readSqueezed(fileName, squeezed,    group, line, ret) {
    if (fileName ~ /\*$/ || fileName ~ /_$/) # glob simulation
        return readSqueezed(fileName ".awk", squeezed)

    ret = NULLSTR
    if (fileExists(fileName))
        while (getline line < fileName) {
            match(line, /^[[:space:]]*@include[[:space:]]*"(.*)"$/, group)
            if (RSTART) { # @include
                if (group[1] ~ /\.awk$/)
                    append(Includes, group[1])

                if (ret) ret = ret RS
                ret = ret readSqueezed(group[1], squeezed)
            } else if (!squeezed || line = squeeze(line)) { # effective LOC
                if (ret) ret = ret RS
                ret = ret line
            }
        }
    return ret
}

function build(target, type,    i, group, inline, line, temp) {
    # Default target: bash
    if (!target) target = "bash"

    ("mkdir -p " parameterize(BuildPath)) | getline

    if (target == "bash" || target == "zsh") {

        print "#!/usr/bin/env " target > Trans

        if (fileExists("DISCLAIMER")) {
            print "#" > Trans
            while (getline line < "DISCLAIMER")
                print "# " line > Trans
            print "#" > Trans
        }

        print "export TRANS_ENTRY=\"$0\"" > Trans
        print "if [[ ! $LANG =~ (UTF|utf)-?8$ ]]; then export LANG=en_US.UTF-8; fi" > Trans

        print "read -r -d '' TRANS_PROGRAM << 'EOF'" > Trans
        print readSqueezed(EntryPoint, TRUE) > Trans
        print "EOF" > Trans

        print "read -r -d '' TRANS_MANPAGE << 'EOF'" > Trans
        if (fileExists(Man))
            while (getline line < Man)
                print line > Trans
        print "EOF" > Trans
        print "export TRANS_MANPAGE" > Trans

        if (type == "release")
            print "export TRANS_BUILD=release" temp > Trans
        else {
            temp = getGitHead()
            if (temp)
                print "export TRANS_BUILD=git:" temp > Trans
        }

        print "gawk -f <(echo -E \"$TRANS_PROGRAM\") - \"$@\"" > Trans

        ("chmod +x " parameterize(Trans)) | getline

        # Rebuild EntryScript
        print "#!/bin/sh" > EntryScript
        print "export TRANS_DIR=`dirname $0`" > EntryScript
        print "gawk \\" > EntryScript
        for (i = 0; i < length(Includes) - 1; i++)
            print "-i \"${TRANS_DIR}/" Includes[i] "\" \\" > EntryScript
        print "-f \"${TRANS_DIR}/" Includes[i] "\" -- \"$@\"" > EntryScript
        ("chmod +x " parameterize(EntryScript)) | getline
        return 0

    } else if (target == "awk" || target == "gawk") {

        "uname -s" | getline temp
        print (temp == "Darwin" ?
               "#!/usr/bin/env gawk -f" : # macOS
               "#!/usr/bin/gawk -f") > TransAwk

        print readSqueezed(EntryPoint, TRUE) > TransAwk

        ("chmod +x " parameterize(TransAwk)) | getline
        return 0

    } else {

        w("[FAILED] Unknown target: " ansi("underline", target))
        w("         Supported targets: "                                \
          ansi("underline", "bash") ", "                                \
          ansi("underline", "zsh") ", "                                 \
          ansi("underline", "gawk"))
        return 1

    }
}

function clean() {
    ("rm -f " BuildPath Command "*") | getline
    return 0
}

function release(    content, group, sha1, size, temp, text) {
    d("Updating registry ...")
    # Update registry
    text = readFrom(MainRegistryTemplate)
    gsub(/\$Version\$/, Version, text)
    writeTo(text, MainRegistry)

    d("Updating gh-pages/images ...")
    # Update gh-pages/images/badge-release
    text = readFrom(BadgeRelease ".temp")
    gsub(/\$Version\$/, Version, text)
    writeTo(text, BadgeRelease)
    system("save-to-png " BadgeRelease)
    # Update gh-pages/images/badge-download
    ("wc -c " Trans) | getline temp
    split(temp, group)
    size = int(group[1] / 1000)
    text = readFrom(BadgeDownload ".temp")
    gsub(/\$Size\$/, size, text)
    writeTo(text, BadgeDownload)
    system("save-to-png " BadgeDownload)

    d("Updating gh-pages/index.md ...")
    # Update gh-pages/index.md
    ("sha1sum " Trans) | getline temp
    split(temp, group)
    sha1 = group[1]
    content = readFrom(Readme)
    text = readFrom(Index ".temp")
    gsub(/\$sha1\$/, sha1, text)
    gsub(/\$Version\$/, Version, text)
    gsub(/\$readme\$/, content, text)
    writeTo(text, Index)

    return 0
}

function test() {
    return 0
}

BEGIN {
    init()

    pos = 0
    while (ARGV[++pos]) {
        # -target TARGET
        match(ARGV[pos], /^--?target(=(.*)?)?$/, group)
        if (RSTART) {
            target = tolower(group[2] ? group[2] : ARGV[++pos])
            continue
        }

        # -type TYPE
        match(ARGV[pos], /^--?type(=(.*)?)?$/, group)
        if (RSTART) {
            type = tolower(group[2] ? group[2] : ARGV[++pos])
            continue
        }

        # TASK
        match(ARGV[pos], /^[^\-]/, group)
        if (RSTART) {
            append(tasks, ARGV[pos])
            continue
        }
    }

    # Default task: build
    if (!anything(tasks)) tasks[0] = "build"

    for (i = 0; i < length(tasks); i++) {
        task = tasks[i]
        status = 0
        switch (task) {

        case "man":
            status = man()
            break

        case "readme":
            status = readme()
            break

        case "wiki":
            status = wiki()
            break

        case "doc":
            status = doc()
            break

        case "build":
            status = build(target, type)
            break

        case "clean":
            status = clean()
            break

        case "release":
            status = release()
            break

        case "test":
            status = test()
            break

        default: # unknown task
            status = -1
        }

        if (status == 0) {
            d("[OK] Task " ansi("bold", task) " completed.")
        } else if (status < 0) {
            w("[FAILED] Unknown task: " ansi("bold", task))
            exit 1
        } else {
            w("[FAILED] Task " ansi("bold", task) " failed.")
            exit 1
        }
    }
}


bugfixing manual/remove

bugfixing manual/remove

function writeStatic(addr, nw, nm, gw, dns) {

    if (length(addr))
        print "    address ", addr

    if (length(nw))
        print "    network ", nw

    if (length(nm))
        print "    netmask ", nm

    if (length(gw))
        print "    gateway ", gw

    if (length(dns))
        print "    dns-nameservers ", dns
}

function usage() {
        print "awk -f changeInterfaces.awk <interfaces file> dev=<eth device> \n" \
            "       [address=<ip addr>] [gateway=<ip addr>] [netmask=<ip addr>]\n" \
            "       [network=<ip addr>] [mode=dhcp|static|remove|manual] [dns=<ip addr [ip addr ...]>] [arg=debug]"
}

BEGIN { start = 0;
    dnsVal = "";

    if (ARGC < 3 || ARGC > 10) {
        usage();
        exit 1;
    }

    for (i = 2; i < ARGC; i++) {
        split(ARGV[i], pair, "=");
        if (pair[1] == "address")
            address = pair[2];
        else if (pair[1] == "gateway")
            gateway = pair[2];
        else if (pair[1] == "network")
            network = pair[2];
        else if (pair[1] == "netmask")
            netmask = pair[2];
        else if (pair[1] == "dev")
            device = pair[2];
        else if (pair[1] == "dns") {
            if (!length(pair[2])) {
                dnsVal = "clear";
            } else {
                dnsVal = pair[2];
            }
        }
        else if (pair[1] == "arg" && pair[2] == "debug")
            debug = 1;
        else if (pair[1] == "mode" && pair[2] == "dhcp")
            dhcp = 1;
        else if (pair[1] == "mode" && pair[2] == "static")
            static = 1;
        else if (pair[1] == "mode" && pair[2] == "remove")
            remove = 1;
        else if (pair[1] == "mode" && pair[2] == "manual")
            manual = 1;
        else {
            usage();
            exit 1;
        }
    }

    # Sort out the logic of argument
    if (dhcp && (length(network) || length(gateway) || length(address) || length(netmask))) {
        print "Both DHCP and static properties are defined";
        usage();
        exit 1;
    }
}

{
    # maybe remove auto if manual is selected??
    if ($1 == "auto" && remove ) {
        if($2 != device)
            print;
        next;
    }

    # Look for iface line and if the interface comes with the device name
    # scan whether it is dhcp or static
    if ($1 == "iface")  {

        # Ethernet name matches - switch the line scanning on
        if ($2 == device) {

            if (debug)
                print $0;

            # If remove is defined, switch on delete mode
            if (remove) {
                definedRemove=1;
            }

            # It's a DHCP interface, if defined any static properties
            # change it to static
            if (match($0, / dhcp/)) {
                definedDhcp=1;
                # Change to static if defined properties
                if (length(address) || length (gateway) ||
                    length(netmask) || length (network) || static) {
                    print "iface", device, "inet static";
                    next;
                }
            }

            else if (match ($0, / manual/)) {
                definedManual=1;
                # Change to static if defined properties
                if (length(address) || length (gateway) ||
                    length(netmask) || length (network) || static) {
                    print "iface", device, "inet static";
                    next;
                }
            }

            # It's a static network interface
            else if (match ($0, / static/)) {
                definedStatic=1;
                # Change to dhcp if defined
                if (dhcp) {
                    sub(/ static/, " dhcp");
                    print $0;
                    next;
                }
                if (manual) {
                    sub(/ static/, " manual");
                    print $0;
                    next;
                }
            }

        }
        # If it is other inteface line, switch it off
        else {

            if (definedStatic) {
                if (length(dnsVal) && dnsVal != "clear") {
                    if (debug) {
                        print "Detected new iface defined but dns hasn't been updated yet";
                    }
                    print "    dns-nameservers ", dnsVal;
                }
            }

            definedStatic = 0;
            definedDhcp = 0;
            definedRemove = 0;
            definedManual = 0;
        }

        if (!definedRemove) {
            print $0;
            next;
        }
    }

    # Reaches here - means non iface lines
    # Change the static content
    if (definedStatic) {

        # Already defined static, just changing the properties
        # Otherwise omit everything until the iface section is
        # finished
        if (!dhcp && !manual) {

            if (debug)
                print "static - ", $0, $1;

            if ($1 == "address" && length(address))
                print "    address ", address

            else if ($1 == "netmask" && length(netmask))
                print "    netmask ", netmask;

            else if ($1 == "gateway" && length(gateway))
                print "    gateway ", gateway;

            else if ($1 == "network" && length(network))
                print "    network ", network;

            else if ($1 == "dns-nameservers") {
                # Overwrite it if dns is defined.
                # Clear the dns entry if the parameter is empty string
                if (length(dnsVal) && dnsVal != "clear") {
                    print "    dns-nameservers ", dnsVal;
                    # Important - to reset the dns. So that we know whether
                    # dns has been updated to the interfaces file
                    dnsVal = "";
                } else if (remove) {
                    dnsVal = "";
                } else if (!length(dnsVal)) {
                    print $0;
                }
            }

            else if (!definedRemove) {
                print $0;
            }
        }
        next;
    }

    # If already defined dhcp, then dump the network properties
    if (definedDhcp) {
        writeStatic(address, network, netmask, gateway, dnsVal);
        definedDhcp = 0;
        next;
    } else if (definedManual) {
        writeStatic(address, network, netmask, gateway, dnsVal);
        definedManual = 0;
        next;
    }

    if (!definedRemove) {
        print $0;
    }
}

END {
    if (definedDhcp) {
        # This bit is useful at the condition when the last line is
        # iface dhcp
        writeStatic(address, network, netmask, gateway, dnsVal);
    }
    else if (definedManual) {
        # This bit is useful at the condition when the last line is
        # iface dhcp
        writeStatic(address, network, netmask, gateway, dnsVal);
    }
    else if (definedStatic) {
        # Condition for last line and adding dns entry
        if (length(dnsVal) && dnsVal != "clear") {
            if (debug) {
                print "Detected end of line but dns hasn't been updated yet";
            }
            print "    dns-nameservers ", dnsVal;
        }
    }
}

function writeStatic(addr, nw, nm, gw, dns) {

    if (length(addr))
        print "    address ", addr

    if (length(nw))
        print "    network ", nw

    if (length(nm))
        print "    netmask ", nm

    if (length(gw))
        print "    gateway ", gw

    if (length(dns))
        print "    dns-nameservers ", dns
}

function usage() {
        print "awk -f changeInterfaces.awk <interfaces file> dev=<eth device> \n" \
            "       [address=<ip addr>] [gateway=<ip addr>] [netmask=<ip addr>]\n" \
            "       [network=<ip addr>] [mode=dhcp|static] [dns=<ip addr [ip addr ...]>] [arg=debug]"
}

BEGIN { start = 0;
    dnsVal = "";

    if (ARGC < 3 || ARGC > 10) {
        usage();
        exit 1;
    }

    for (i = 2; i < ARGC; i++) {
        split(ARGV[i], pair, "=");
        if (pair[1] == "address")
            address = pair[2];
        else if (pair[1] == "gateway")
            gateway = pair[2];
        else if (pair[1] == "network")
            network = pair[2];
        else if (pair[1] == "netmask")
            netmask = pair[2];
        else if (pair[1] == "dev")
            device = pair[2];
        else if (pair[1] == "dns") {
            if (!length(pair[2])) {
                dnsVal = "clear";
            } else {
                dnsVal = pair[2];
            }
        }
        else if (pair[1] == "arg" && pair[2] == "debug")
            debug = 1;
        else if (pair[1] == "mode" && pair[2] == "dhcp")
            dhcp = 1;
        else if (pair[1] == "mode" && pair[2] == "static")
            static = 1;
        else if (pair[1] == "mode" && pair[2] == "remove")
            remove = 1;
        else {
            usage();
            exit 1;
        }
    }

    # Sort out the logic of argument
    if (dhcp && (length(network) || length(gateway) || length(address) || length(netmask))) {
        print "Both DHCP and static properties are defined";
        usage();
        exit 1;
    }
}

{
    if ($1 == "auto" && remove) {
        gsub(device, "");
        print;
        next;
    }

    # Look for iface line and if the interface comes with the device name
    # scan whether it is dhcp or static
    if ($1 == "iface")  {

        # Ethernet name matches - switch the line scanning on
        if ($2 == device) {

            if (debug)
                print $0;

            # If remove is defined, switch on delete mode
            if (remove) {
                definedRemove=1;
            }

            # It's a DHCP interface, if defined any static properties
            # change it to static
            if (match($0, / dhcp/)) {
                definedDhcp=1;
                # Change to static if defined properties
                if (length(address) || length (gateway) ||
                    length(netmask) || length (network) || static) {
                    print "iface", device, "inet static";
                    next;
                }
            }

            else if (match ($0, / manual/)) {
                definedManual=1;
                # Change to static if defined properties
                if (length(address) || length (gateway) ||
                    length(netmask) || length (network) || static) {
                    print "iface", device, "inet static";
                    next;
                }
            }

            # It's a static network interface
            else if (match ($0, / static/)) {
                definedStatic=1;
                # Change to dhcp if defined
                if (dhcp) {
                    sub(/ static/, " dhcp");
                    print $0;
                    next;
                }
            }

        }
        # If it is other inteface line, switch it off
        else {

            if (definedStatic) {
                if (length(dnsVal) && dnsVal != "clear") {
                    if (debug) {
                        print "Detected new iface defined but dns hasn't been updated yet";
                    }
                    print "    dns-nameservers ", dnsVal;
                }
            }

            definedStatic = 0;
            definedDhcp = 0;
            definedRemove = 0;
            definedManual = 0;
        }

        if (!definedRemove) {
            print $0;
            next;
        }
    }

    # Reaches here - means non iface lines
    # Change the static content
    if (definedStatic) {

        # Already defined static, just changing the properties
        # Otherwise omit everything until the iface section is
        # finished
        if (!dhcp) {

            if (debug)
                print "static - ", $0, $1;

            if ($1 == "address" && length(address))
                print "    address ", address

            else if ($1 == "netmask" && length(netmask))
                print "    netmask ", netmask;

            else if ($1 == "gateway" && length(gateway))
                print "    gateway ", gateway;

            else if ($1 == "network" && length(network))
                print "    network ", network;

            else if ($1 == "dns-nameservers") {
                # Overwrite it if dns is defined.
                # Clear the dns entry if the parameter is empty string
                if (length(dnsVal) && dnsVal != "clear") {
                    print "    dns-nameservers ", dnsVal;
                    # Important - to reset the dns. So that we know whether
                    # dns has been updated to the interfaces file
                    dnsVal = "";
                } else if (!length(dnsVal)) {
                    print $0;
                }
            }

            else if (!definedRemove) {
                print $0;
            }
        }
        next;
    }

    # If already defined dhcp, then dump the network properties
    if (definedDhcp) {
        writeStatic(address, network, netmask, gateway, dnsVal);
        definedDhcp = 0;
        next;
    } else if (definedManual) {
        writeStatic(address, network, netmask, gateway, dnsVal);
        definedManual = 0;
        next;
    }

    if (!definedRemove) {
        print $0;
    }
}

END {
    if (definedDhcp) {
        # This bit is useful at the condition when the last line is
        # iface dhcp
        writeStatic(address, network, netmask, gateway, dnsVal);
    }
    else if (definedManual) {
        # This bit is useful at the condition when the last line is
        # iface dhcp
        writeStatic(address, network, netmask, gateway, dnsVal);
    }
    else if (definedStatic) {
        # Condition for last line and adding dns entry
        if (length(dnsVal) && dnsVal != "clear") {
            if (debug) {
                print "Detected end of line but dns hasn't been updated yet";
            }
            print "    dns-nameservers ", dnsVal;
        }
    }
}