We are OpenBSD

We are OpenBSD

#! /usr/bin/awk -f
#	$OpenBSD: devlist2h.awk,v 1.2 2000/05/15 06:29:09 niklas Exp $
#	$NetBSD: devlist2h.awk,v 1.1 1999/10/15 06:07:22 haya Exp $
#
# Copyright (c) 1995, 1996 Christopher G. Demetriou
# 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.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#      This product includes software developed by Christopher G. Demetriou.
# 4. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
BEGIN {
	nproducts = nvendors = 0
	dfile="cardbusdevs_data.h"
	hfile="cardbusdevs.h"
}
NR == 1 {
	VERSION = $0
	gsub("\\$", "", VERSION)

	printf("/*\t\$OpenBSD\$\t*/\n\n") > dfile
	printf("/*\n") > dfile
	printf(" * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.\n") \
	    > dfile
	printf(" *\n") > dfile
	printf(" * generated from:\n") > dfile
	printf(" *\t%s\n", VERSION) > dfile
	printf(" */\n") > dfile

	printf("/*\t\OpenBSD\$\t*/\n\n") > hfile
	printf("/*\n") > hfile
	printf(" * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.\n") \
	    > hfile
	printf(" *\n") > hfile
	printf(" * generated from:\n") > hfile
	printf(" *\t%s\n", VERSION) > hfile
	printf(" */\n") > hfile

	next
}
$1 == "vendor" {
	nvendors++

	vendorindex[$2] = nvendors;		# record index for this name, for later.
	vendors[nvendors, 1] = $2;		# name
	vendors[nvendors, 2] = $3;		# id
	printf("#define\tCARDBUS_VENDOR_%s\t%s\t", vendors[nvendors, 1],
	    vendors[nvendors, 2]) > hfile

	i = 3; f = 4;

	# comments
	ocomment = oparen = 0
	if (f <= NF) {
		printf("\t/* ") > hfile
		ocomment = 1;
	}
	while (f <= NF) {
		if ($f == "#") {
			printf("(") > hfile
			oparen = 1
			f++
			continue
		}
		if (oparen) {
			printf("%s", $f) > hfile
			if (f < NF)
				printf(" ") > hfile
			f++
			continue
		}
		vendors[nvendors, i] = $f
		printf("%s", vendors[nvendors, i]) > hfile
		if (f < NF)
			printf(" ") > hfile
		i++; f++;
	}
	if (oparen)
		printf(")") > hfile
	if (ocomment)
		printf(" */") > hfile
	printf("\n") > hfile

	next
}
$1 == "product" {
	nproducts++

	products[nproducts, 1] = $2;		# vendor name
	products[nproducts, 2] = $3;		# product id
	products[nproducts, 3] = $4;		# id
	printf("#define\tCARDBUS_PRODUCT_%s_%s\t%s\t", products[nproducts, 1],
	    products[nproducts, 2], products[nproducts, 3]) > hfile

	i=4; f = 5;

	# comments
	ocomment = oparen = 0
	if (f <= NF) {
		printf("\t/* ") > hfile
		ocomment = 1;
	}
	while (f <= NF) {
		if ($f == "#") {
			printf("(") > hfile
			oparen = 1
			f++
			continue
		}
		if (oparen) {
			printf("%s", $f) > hfile
			if (f < NF)
				printf(" ") > hfile
			f++
			continue
		}
		products[nproducts, i] = $f
		printf("%s", products[nproducts, i]) > hfile
		if (f < NF)
			printf(" ") > hfile
		i++; f++;
	}
	if (oparen)
		printf(")") > hfile
	if (ocomment)
		printf(" */") > hfile
	printf("\n") > hfile

	next
}
{
	if ($0 == "")
		blanklines++
	print $0 > hfile
	if (blanklines < 2)
		print $0 > dfile
}
END {
	# print out the match tables

	printf("\n") > dfile

	printf("struct cardbus_knowndev cardbus_knowndevs[] = {\n") > dfile
	for (i = 1; i <= nproducts; i++) {
		printf("\t{\n") > dfile
		printf("\t    CARDBUS_VENDOR_%s, CARDBUS_PRODUCT_%s_%s,\n",
		    products[i, 1], products[i, 1], products[i, 2]) \
		    > dfile
		printf("\t    ") > dfile
		printf("0") > dfile
		printf(",\n") > dfile

		vendi = vendorindex[products[i, 1]];
		printf("\t    \"") > dfile
		j = 3;
		needspace = 0;
		while (vendors[vendi, j] != "") {
			if (needspace)
				printf(" ") > dfile
			printf("%s", vendors[vendi, j]) > dfile
			needspace = 1
			j++
		}
		printf("\",\n") > dfile

		printf("\t    \"") > dfile
		j = 4;
		needspace = 0;
		while (products[i, j] != "") {
			if (needspace)
				printf(" ") > dfile
			printf("%s", products[i, j]) > dfile
			needspace = 1
			j++
		}
		printf("\",\n") > dfile
		printf("\t},\n") > dfile
	}
	for (i = 1; i <= nvendors; i++) {
		printf("\t{\n") > dfile
		printf("\t    CARDBUS_VENDOR_%s, 0,\n", vendors[i, 1]) \
		    > dfile
		printf("\t    CARDBUS_KNOWNDEV_NOPROD,\n") \
		    > dfile
		printf("\t    \"") > dfile
		j = 3;
		needspace = 0;
		while (vendors[i, j] != "") {
			if (needspace)
				printf(" ") > dfile
			printf("%s", vendors[i, j]) > dfile
			needspace = 1
			j++
		}
		printf("\",\n") > dfile
		printf("\t    NULL,\n") > dfile
		printf("\t},\n") > dfile
	}
	printf("\t{ 0, 0, 0, NULL, NULL, }\n") > dfile
	printf("};\n") > dfile
}

#! /usr/bin/awk -f
#	$OpenBSD: devlist2h.awk,v 1.1 2000/03/02 23:03:26 aaron Exp $
#	$NetBSD: devlist2h.awk,v 1.1 1999/10/15 06:07:22 haya Exp $
#
# Copyright (c) 1995, 1996 Christopher G. Demetriou
# 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.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#      This product includes software developed by Christopher G. Demetriou.
# 4. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
BEGIN {
	nproducts = nvendors = 0
	dfile="cardbusdevs_data.h"
	hfile="cardbusdevs.h"
}
NR == 1 {
	VERSION = $0
	gsub("\\$", "", VERSION)

	printf("/*\t\$NetBSD\$\t*/\n\n") > dfile
	printf("/*\n") > dfile
	printf(" * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.\n") \
	    > dfile
	printf(" *\n") > dfile
	printf(" * generated from:\n") > dfile
	printf(" *\t%s\n", VERSION) > dfile
	printf(" */\n") > dfile

	printf("/*\t\$NetBSD\$\t*/\n\n") > hfile
	printf("/*\n") > hfile
	printf(" * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.\n") \
	    > hfile
	printf(" *\n") > hfile
	printf(" * generated from:\n") > hfile
	printf(" *\t%s\n", VERSION) > hfile
	printf(" */\n") > hfile

	next
}
$1 == "vendor" {
	nvendors++

	vendorindex[$2] = nvendors;		# record index for this name, for later.
	vendors[nvendors, 1] = $2;		# name
	vendors[nvendors, 2] = $3;		# id
	printf("#define\tCARDBUS_VENDOR_%s\t%s\t", vendors[nvendors, 1],
	    vendors[nvendors, 2]) > hfile

	i = 3; f = 4;

	# comments
	ocomment = oparen = 0
	if (f <= NF) {
		printf("\t/* ") > hfile
		ocomment = 1;
	}
	while (f <= NF) {
		if ($f == "#") {
			printf("(") > hfile
			oparen = 1
			f++
			continue
		}
		if (oparen) {
			printf("%s", $f) > hfile
			if (f < NF)
				printf(" ") > hfile
			f++
			continue
		}
		vendors[nvendors, i] = $f
		printf("%s", vendors[nvendors, i]) > hfile
		if (f < NF)
			printf(" ") > hfile
		i++; f++;
	}
	if (oparen)
		printf(")") > hfile
	if (ocomment)
		printf(" */") > hfile
	printf("\n") > hfile

	next
}
$1 == "product" {
	nproducts++

	products[nproducts, 1] = $2;		# vendor name
	products[nproducts, 2] = $3;		# product id
	products[nproducts, 3] = $4;		# id
	printf("#define\tCARDBUS_PRODUCT_%s_%s\t%s\t", products[nproducts, 1],
	    products[nproducts, 2], products[nproducts, 3]) > hfile

	i=4; f = 5;

	# comments
	ocomment = oparen = 0
	if (f <= NF) {
		printf("\t/* ") > hfile
		ocomment = 1;
	}
	while (f <= NF) {
		if ($f == "#") {
			printf("(") > hfile
			oparen = 1
			f++
			continue
		}
		if (oparen) {
			printf("%s", $f) > hfile
			if (f < NF)
				printf(" ") > hfile
			f++
			continue
		}
		products[nproducts, i] = $f
		printf("%s", products[nproducts, i]) > hfile
		if (f < NF)
			printf(" ") > hfile
		i++; f++;
	}
	if (oparen)
		printf(")") > hfile
	if (ocomment)
		printf(" */") > hfile
	printf("\n") > hfile

	next
}
{
	if ($0 == "")
		blanklines++
	print $0 > hfile
	if (blanklines < 2)
		print $0 > dfile
}
END {
	# print out the match tables

	printf("\n") > dfile

	printf("struct cardbus_knowndev cardbus_knowndevs[] = {\n") > dfile
	for (i = 1; i <= nproducts; i++) {
		printf("\t{\n") > dfile
		printf("\t    CARDBUS_VENDOR_%s, CARDBUS_PRODUCT_%s_%s,\n",
		    products[i, 1], products[i, 1], products[i, 2]) \
		    > dfile
		printf("\t    ") > dfile
		printf("0") > dfile
		printf(",\n") > dfile

		vendi = vendorindex[products[i, 1]];
		printf("\t    \"") > dfile
		j = 3;
		needspace = 0;
		while (vendors[vendi, j] != "") {
			if (needspace)
				printf(" ") > dfile
			printf("%s", vendors[vendi, j]) > dfile
			needspace = 1
			j++
		}
		printf("\",\n") > dfile

		printf("\t    \"") > dfile
		j = 4;
		needspace = 0;
		while (products[i, j] != "") {
			if (needspace)
				printf(" ") > dfile
			printf("%s", products[i, j]) > dfile
			needspace = 1
			j++
		}
		printf("\",\n") > dfile
		printf("\t},\n") > dfile
	}
	for (i = 1; i <= nvendors; i++) {
		printf("\t{\n") > dfile
		printf("\t    CARDBUS_VENDOR_%s, 0,\n", vendors[i, 1]) \
		    > dfile
		printf("\t    CARDBUS_KNOWNDEV_NOPROD,\n") \
		    > dfile
		printf("\t    \"") > dfile
		j = 3;
		needspace = 0;
		while (vendors[i, j] != "") {
			if (needspace)
				printf(" ") > dfile
			printf("%s", vendors[i, j]) > dfile
			needspace = 1
			j++
		}
		printf("\",\n") > dfile
		printf("\t    NULL,\n") > dfile
		printf("\t},\n") > dfile
	}
	printf("\t{ 0, 0, 0, NULL, NULL, }\n") > dfile
	printf("};\n") > dfile
}


Removed debugging feature from msg_parser.awk making it fail with BSD awk.

darcs-hash:20060224081416-65a35-16d5d37c9d56c34559483d63d0be6d9c0d59e36b.gz

#! /usr/bin/env awk
#
# This script recreates C files containing header-specific boilerplate stuff
# using the given list of headers (usually obtained from the master structure).
#
# It can also create a parser table.
#
# --------------------------------------------------------------------
#
# This file is part of the Sofia-SIP package
#
# Copyright (C) 2005 Nokia Corporation.
#
# Contact: Pekka Pessi <pekka.pessi@nokia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# --------------------------------------------------------------------
#
# Contributor(s): Pekka.Pessi@nokia.com.
#
# Created: Fri Apr  6 12:59:59 2001 ppessi
#

BEGIN {
  "date '+%a %b %e %H:%M:%S %Y'" | getline date;

  ascii =			       \
     "                               " \
    " !\"#$%&'()*+,-./0123456789:;<=>?" \
    "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" \
    "`abcdefghijklmnopqrstuvwxyz{|}~";
  lower_case = "abcdefghijklmnopqrstuvwxyz";

  N=0;
  # Initialize these as arrays
  split("", symbols);
  split("", names);
  split("", comments);
  split("", hashes);
  split("", NAMES);
  split("", Comments);
  split("", COMMENTS);

  template="";
  template1="";
  template2="";
  template3=="";
}

function name_hash (name)
{
  hash = 0;

  len = split(name, chars, "");

  for (i = 1; i <= len; i++) {
    c = tolower(chars[i]);
    hash = (38501 * (hash + index(ascii, c))) % 65536;
  }

  if (0) {
    # Test that hash algorithm above agrees with the C version
    pipe = ("../msg/msg_name_hash " name);
    pipe | getline;
    close(pipe);
    if (hash != $0) {
      print name ": " hash " != " $0 > "/dev/stderr";
    }
  }

  return hash "";
}

#
# Replace magic patterns in template p with header-specific values
#
function protos (name, comment, hash)
{
  NAME=toupper(name);
  sub(/.*[\/][*][*][<][ 	]*/, "", comment);
  sub(/[ 	]*[*][\/].*/, "", comment);
  sub(/[ 	]+/, " ", comment);

  short = match(comment, /[(][a-z][)]/);
  if (short) {
    short = substr(comment, short + 1, 1);
    sub(/ *[(][a-z][)]/, "", comment);
    shorts[index(lower_case, short)] = name;
  }

  do_hash = hash == 0;

  if (do_hash) {
    split(comment, parts, " ");
    name2 = tolower(parts[1]);
    gsub(/-/, "_", name2);
    if (name2 != name) {
      print name " mismatch with " comment " (" real ")" > "/dev/stderr";
    }

    hash = name_hash(parts[1]);

    hashed[name] = hash;

    if (comment !~ /header/) {
      comment = comment " header";
    }
  }

  Comment = comment;
  if (!do_hash) {
    comment = tolower(comment);
  }
  COMMENT = toupper(comment);

  # Store the various forms into an array for the footer processing
  N++;
  hashes[N] = hash;
  names[N] = name;
  NAMES[N] = NAME;
  comments[N] = comment;
  Comments[N] = comment;
  COMMENTS[N] = COMMENT;

  symbols[name] = comment;

  if (PR) {
    replace(template, hash, name, NAME, comment, Comment, COMMENT);
    replace(template1, hash, name, NAME, comment, Comment, COMMENT);
    replace(template2, hash, name, NAME, comment, Comment, COMMENT);
    replace(template3, hash, name, NAME, comment, Comment, COMMENT);
  }
}

function replace (p, hash, name, NAME, comment, Comment, COMMENT)
{
  if (p) {
    gsub(/#hash#/, hash, p);
    gsub(/#xxxxxx#/, name, p);
    gsub(/#XXXXXX#/, NAME, p);
    gsub(/#xxxxxxx_xxxxxxx#/, comment, p);
    gsub(/#Xxxxxxx_Xxxxxxx#/, Comment, p);
    gsub(/#XXXXXXX_XXXXXXX#/, COMMENT, p);

    print p > PR;
  }
}

#
# Repeat each line in the footer containing the magic replacement
# pattern with a instance of all headers
#
function process_footer (text)
{
  if (!match(tolower(text), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) {
    n = length(text);
    while (substr(text, n) == "\n") {
      n = n - 1;
      text = substr(text, 1, n);
    }
    if (n > 0)
      print text > PR;
    return;
  }

  n = split(text, lines, RS);

  for (i = 1; i <= n; i++) {
    l = lines[i];
    if (match(tolower(l), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) {
      for (j = 1; j <= N; j++) {
	l = lines[i];
	gsub(/#hash#/, hashes[j], l);
	gsub(/#xxxxxxx_xxxxxxx#/, comments[j], l);
	gsub(/#Xxxxxxx_Xxxxxxx#/, Comments[j], l);
	gsub(/#XXXXXXX_XXXXXXX#/, COMMENTS[j], l);
	gsub(/#xxxxxx#/, names[j], l);
	gsub(/#XXXXXX#/, NAMES[j], l);
	print l > PR;
      }
    } else {
      print l > PR;
    }
  }
}

#
# Read flags used with headers
#
function read_header_flags (flagfile,    line, tokens, name, value)
{
  while ((getline line < flagfile) > 0) {
    sub(/^[ \t]+/, "", line);
    sub(/[ \t]+$/, "", line);
    if (line ~ /^#/ || line ~ /^$/)
      continue;

    split(line, tokens,  /[ \t]*=[ \t]*/);
    name = tolower(tokens[1]);
    gsub(/-/, "_", name);
    gsub(/,/, " ", name);
    # print "FLAG: " name " = " tokens[2]

    if (header_flags[name]) {
      print flagfile ": already defined " tokens[1];
    }
    else if (!symbols[name]) {
      print flagfile ": unknown header \"" tokens[1] "\"";
    }
    else {
      header_flags[name] = tokens[2];
    }
  }
  close(flagfile);
}

/^ *\/\* === Headers start here \*\// {

  in_header_list=1;

  if (!auto) {
    auto = FILENAME;
    sub(/.*\//, "", auto);
    auto = "This file is automatically generated from <" auto "> by msg_parser.awk.";

    if (PR) {
      if (TEMPLATE == "") { TEMPLATE = PR ".in"; }
      RS0=RS; RS="\f\n";
      getline theader < TEMPLATE;
      getline header < TEMPLATE;
      getline template < TEMPLATE;
      getline footer < TEMPLATE;

      if (TEMPLATE1) {
	getline dummy < TEMPLATE1;
	getline dummy < TEMPLATE1;
	getline template1 < TEMPLATE1;
	getline dummy < TEMPLATE1;
      }

      if (TEMPLATE2) {
	getline dummy < TEMPLATE2;
	getline dummy < TEMPLATE2;
	getline template2 < TEMPLATE2;
	getline dummy < TEMPLATE2;
      }

      if (TEMPLATE3) {
	getline dummy < TEMPLATE3;
	getline dummy < TEMPLATE3;
	getline template3 < TEMPLATE3;
	getline dummy < TEMPLATE3;
      }

      sub(/.*[\/]/, "", TEMPLATE);
      gsub(/#AUTO#/, auto, header);
      gsub(/#DATE#/, "@date Generated: " date, header);
      if (PACKAGE_NAME) gsub(/#PACKAGE_NAME#/, PACKAGE_NAME, header);
      if (PACKAGE_VERSION) gsub(/#PACKAGE_VERSION#/, PACKAGE_VERSION, header);
      print header > PR;

      RS=RS0;
    }

    if (!NO_FIRST) {
      protos("request", "/**< Request line */", -1);
      protos("status", "/**< Status line */", -2);
    }
  }
}

in_header_list && /^ *\/\* === Headers end here \*\// { in_header_list=0;}

in_header_list && PT && /^ *\/\* === Hash headers end here \*\// { in_header_list=0;}

in_header_list && /^  (sip|rtsp|http|msg|mp)_[a-z_0-9]+_t/ {
  n=$0
  sub(/;.*$/, "", n);
  sub(/^ *(sip|rtsp|http|msg|mp)_[a-z0-9_]*_t[ 	]*/, "", n);
  sub(/^[*](sip|rtsp|http|msg|mp)_/, "", n);

  if ($0 !~ /[\/][*][*][<]/) {
    getline;
  }
  if ($0 !~ /[\/][*][*][<]/) {
    printf "msg_protos.awk: header %s is malformed\n", n;
    exit 1;
  }

  if (!NO_MIDDLE)
    protos(n, $0, 0);

  headers[total++] = n;
}

END {
  if (!prefix) { prefix = module; }
  if (!tprefix) { tprefix = prefix; }

  if (!NO_LAST) {
    protos("unknown", "/**< Unknown headers */", -3);
    protos("error", "/**< Erroneous headers */", -4);
    protos("separator", "/**< Separator line between headers and payload */", -5);
    protos("payload", "/**< Message payload */", -6);
    if (multipart)
      protos("multipart", "/**< Multipart payload */", -7);
  }

  if (PR) {
    process_footer(footer);
  }
  else if (PT) {
    if (FLAGFILE)
      read_header_flags(FLAGFILE);

    if (TEMPLATE == "") { TEMPLATE = PT ".in"; }
    RS0=RS; RS="\n";
    getline theader < TEMPLATE;
    getline header < TEMPLATE;
    getline template < TEMPLATE;
    getline footer < TEMPLATE;
    RS=RS0;

    sub(/.*[\/]/, "", TEMPLATE);
    gsub(/#AUTO#/, auto, header);
    gsub(/#DATE#/, "@date Generated: " date, header);
    print header > PT;

    if (MC_SHORT_SIZE) {
      printf("static msg_href_t const " \
	     "%s_short_forms[MC_SHORT_SIZE] = \n{\n",
	     module) > PT;

      for (i = 1; i <= MC_SHORT_SIZE; i = i + 1) {
	c = (i == MC_SHORT_SIZE) ? "" : ",";
	if (i in shorts) {
	  n = shorts[i];
        flags = header_flags[n]; if (flags) flags = ",\n      " flags;

	  printf("  { /* %s */ %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n",
		 substr(lower_case, i, 1),
		 tprefix, n, module, prefix, n, flags, c)	\
	    > PT;
	}
	else {
	  printf("  { NULL }%s\n", c) \
	    > PT;
	}
      }
      printf("};\n\n") > PT;
    }

    # printf("extern msg_hclass_t msg_multipart_class[];\n\n") > PT;

    printf("msg_mclass_t const %s_mclass[1] = \n{{\n", module) > PT;
    printf("# if defined (%s_HCLASS)\n", toupper(module)) > PT;
    printf("  %s_HCLASS,\n", toupper(module)) > PT;
    printf("#else\n") > PT;
    printf("  {{ 0 }},\n") > PT;
    printf("#endif\n") > PT;
    printf("  %s_VERSION_CURRENT,\n", toupper(module)) > PT;
    printf("  %s_PROTOCOL_TAG,\n", toupper(module)) > PT;
    printf("#if defined (%s_PARSER_FLAGS)\n", toupper(module)) > PT;
    printf("  %s_PARSER_FLAGS,\n", toupper(module)) > PT;
    printf("#else\n") > PT;
    printf("  0,\n") > PT;
    printf("#endif\n") > PT;
    printf("  sizeof(%s_t),\n", module) > PT;
    printf("  %s_extract_body,\n", module) > PT;

    len = split("request status separator payload unknown error", unnamed, " ");

    for (i = 1; i <= len; i++) {
      printf("  {{ %s_%s_class, offsetof(%s_t, %s_%s) }},\n",
	     tprefix, unnamed[i], module, prefix, unnamed[i]) > PT;
    }
    if (multipart) {
      printf("  {{ %s_class, offsetof(%s_t, %s_multipart) }},\n",
	     multipart, module, prefix) > PT;
    } else {
      printf("  {{ NULL, 0 }},\n") > PT;
    }
    if (MC_SHORT_SIZE) {
      printf("  %s_short_forms, \n", module) > PT;
    }
    else {
      printf("  NULL, \n") > PT;
    }
    printf("  %d, %d, \n", MC_HASH_SIZE, total) > PT;
    printf("  {\n") > PT;

    for (i = 0; i < total; i++) {
      n = headers[i];
      h = hashed[n];

      if (h < 0)
	continue;

      for (j = h % MC_HASH_SIZE; j in header_hash;) {
	if (++j == MC_HASH_SIZE) {
	  j = 0;
	}
      }

      header_hash[j] = n;
    }

    for (i = 0; i < MC_HASH_SIZE; i++) {
      c = (i + 1 == MC_HASH_SIZE) ? "" : ",";
      if (i in header_hash) {
	n = header_hash[i];
        flags = header_flags[n]; if (flags) flags = ",\n      " flags;

	printf("    { %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n",
	       tprefix, n, module, prefix, n, flags, c) > PT;
      }
      else {
	printf("    { NULL, 0 }%s\n", c) > PT;
      }
    }
    printf("  }\n}};\n\n") > PT;
  }
}

#! /usr/bin/env awk
#
# This script recreates C files containing header-specific boilerplate stuff
# using the given list of headers (usually obtained from the master structure).
#
# It can also create a parser table.
#
# --------------------------------------------------------------------
#
# This file is part of the Sofia-SIP package
#
# Copyright (C) 2005 Nokia Corporation.
#
# Contact: Pekka Pessi <pekka.pessi@nokia.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# --------------------------------------------------------------------
#
# Contributor(s): Pekka.Pessi@nokia.com.
#
# Created: Fri Apr  6 12:59:59 2001 ppessi
#

BEGIN {
  "date '+%a %b %e %H:%M:%S %Y'" | getline date;

  ascii =			       \
     "                               " \
    " !\"#$%&'()*+,-./0123456789:;<=>?" \
    "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" \
    "`abcdefghijklmnopqrstuvwxyz{|}~";
  lower_case = "abcdefghijklmnopqrstuvwxyz";

  N=0;
  # Initialize these as arrays
  split("", symbols);
  split("", names);
  split("", comments);
  split("", hashes);
  split("", NAMES);
  split("", Comments);
  split("", COMMENTS);

  template="";
  template1="";
  template2="";
  template3=="";
}

function name_hash (name)
{
  hash = 0;

  len = split(name, chars, "");

  for (i = 1; i <= len; i++) {
    c = tolower(chars[i]);
    hash = (38501 * (hash + index(ascii, c))) % 65536;
  }

  if (0) {
    # Test that hash algorithm above agrees with the C version
    pipe = ("../msg/msg_name_hash " name);
    pipe | getline;
    close(pipe);
    if (hash != $0) {
      print name ": " hash " != " $0 > "/dev/stderr";
    }
  }

  return hash "";
}

#
# Replace magic patterns in template p with header-specific values
#
function protos (name, comment, hash)
{
  NAME=toupper(name);
  sub(/.*[\/][*][*][<][ 	]*/, "", comment);
  sub(/[ 	]*[*][\/].*/, "", comment);
  sub(/[ 	]+/, " ", comment);

  short = match(comment, /[(][a-z][)]/);
  if (short) {
    short = substr(comment, short + 1, 1);
    sub(/ *[(][a-z][)]/, "", comment);
    shorts[index(lower_case, short)] = name;
  }

  do_hash = hash == 0;

  if (do_hash) {
    split(comment, parts, " ");
    name2 = tolower(parts[1]);
    gsub(/-/, "_", name2);
    if (name2 != name) {
      print name " mismatch with " comment " (" real ")" > "/dev/stderr";
    }

    hash = name_hash(parts[1]);

    hashed[name] = hash;

    if (comment !~ /header/) {
      comment = comment " header";
    }
  }

  Comment = comment;
  if (!do_hash) {
    comment = tolower(comment);
  }
  COMMENT = toupper(comment);

  # Store the various forms into an array for the footer processing
  N++;
  hashes[N] = hash;
  names[N] = name;
  NAMES[N] = NAME;
  comments[N] = comment;
  Comments[N] = comment;
  COMMENTS[N] = COMMENT;

  symbols[name] = comment;

  if (PR) {
    replace(template, hash, name, NAME, comment, Comment, COMMENT);
    replace(template1, hash, name, NAME, comment, Comment, COMMENT);
    replace(template2, hash, name, NAME, comment, Comment, COMMENT);
    replace(template3, hash, name, NAME, comment, Comment, COMMENT);
  }
}

function replace (p, hash, name, NAME, comment, Comment, COMMENT)
{
  if (p) {
    gsub(/#hash#/, hash, p);
    gsub(/#xxxxxx#/, name, p);
    gsub(/#XXXXXX#/, NAME, p);
    gsub(/#xxxxxxx_xxxxxxx#/, comment, p);
    gsub(/#Xxxxxxx_Xxxxxxx#/, Comment, p);
    gsub(/#XXXXXXX_XXXXXXX#/, COMMENT, p);

    print p > PR NEW;
  }
}

#
# Repeat each line in the footer containing the magic replacement
# pattern with a instance of all headers
#
function process_footer (text)
{
  if (!match(tolower(text), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) {
    n = length(text);
    while (substr(text, n) == "\n") {
      n = n - 1;
      text = substr(text, 1, n);
    }
    if (n > 0)
      print text > PR NEW;
    return;
  }

  n = split(text, lines, RS);

  for (i = 1; i <= n; i++) {
    l = lines[i];
    if (match(tolower(l), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) {
      for (j = 1; j <= N; j++) {
	l = lines[i];
	gsub(/#hash#/, hashes[j], l);
	gsub(/#xxxxxxx_xxxxxxx#/, comments[j], l);
	gsub(/#Xxxxxxx_Xxxxxxx#/, Comments[j], l);
	gsub(/#XXXXXXX_XXXXXXX#/, COMMENTS[j], l);
	gsub(/#xxxxxx#/, names[j], l);
	gsub(/#XXXXXX#/, NAMES[j], l);
	print l > PR NEW;
      }
    } else {
      print l > PR NEW;
    }
  }
}

#
# Read flags used with headers
#
function read_header_flags (flagfile,    line, tokens, name, value)
{
  while ((getline line < flagfile) > 0) {
    sub(/^[ \t]+/, "", line);
    sub(/[ \t]+$/, "", line);
    if (line ~ /^#/ || line ~ /^$/)
      continue;

    split(line, tokens,  /[ \t]*=[ \t]*/);
    name = tolower(tokens[1]);
    gsub(/-/, "_", name);
    gsub(/,/, " ", name);
    # print "FLAG: " name " = " tokens[2]

    if (header_flags[name]) {
      print flagfile ": already defined " tokens[1];
    }
    else if (!symbols[name]) {
      print flagfile ": unknown header \"" tokens[1] "\"";
    }
    else {
      header_flags[name] = tokens[2];
    }
  }
  close(flagfile);
}

/^ *\/\* === Headers start here \*\// {

  in_header_list=1;

  if (!auto) {
    auto = FILENAME;
    sub(/.*\//, "", auto);
    auto = "This file is automatically generated from <" auto "> by msg_parser.awk.";

    if (PR) {
      if (TEMPLATE == "") { TEMPLATE = PR ".in"; }
      RS0=RS; RS="\f\n";
      getline theader < TEMPLATE;
      getline header < TEMPLATE;
      getline template < TEMPLATE;
      getline footer < TEMPLATE;

      if (TEMPLATE1) {
	getline dummy < TEMPLATE1;
	getline dummy < TEMPLATE1;
	getline template1 < TEMPLATE1;
	getline dummy < TEMPLATE1;
      }

      if (TEMPLATE2) {
	getline dummy < TEMPLATE2;
	getline dummy < TEMPLATE2;
	getline template2 < TEMPLATE2;
	getline dummy < TEMPLATE2;
      }

      if (TEMPLATE3) {
	getline dummy < TEMPLATE3;
	getline dummy < TEMPLATE3;
	getline template3 < TEMPLATE3;
	getline dummy < TEMPLATE3;
      }

      sub(/.*[\/]/, "", TEMPLATE);
      gsub(/#AUTO#/, auto, header);
      gsub(/#DATE#/, "@date Generated: " date, header);
      if (PACKAGE_NAME) gsub(/#PACKAGE_NAME#/, PACKAGE_NAME, header);
      if (PACKAGE_VERSION) gsub(/#PACKAGE_VERSION#/, PACKAGE_VERSION, header);
      print header > PR NEW;

      RS=RS0;
    }

    if (!NO_FIRST) {
      protos("request", "/**< Request line */", -1);
      protos("status", "/**< Status line */", -2);
    }
  }
}

in_header_list && /^ *\/\* === Headers end here \*\// { in_header_list=0;}

in_header_list && PT && /^ *\/\* === Hash headers end here \*\// { in_header_list=0;}

in_header_list && /^  (sip|rtsp|http|msg|mp)_[a-z_0-9]+_t/ {
  n=$0
  sub(/;.*$/, "", n);
  sub(/^ *(sip|rtsp|http|msg|mp)_[a-z0-9_]*_t[ 	]*/, "", n);
  sub(/^[*](sip|rtsp|http|msg|mp)_/, "", n);

  if ($0 !~ /[\/][*][*][<]/) {
    getline;
  }
  if ($0 !~ /[\/][*][*][<]/) {
    printf "msg_protos.awk: header %s is malformed\n", n;
    exit 1;
  }

  if (!NO_MIDDLE)
    protos(n, $0, 0);

  headers[total++] = n;
}

END {
  if (!prefix) { prefix = module; }
  if (!tprefix) { tprefix = prefix; }

  if (!NO_LAST) {
    protos("unknown", "/**< Unknown headers */", -3);
    protos("error", "/**< Erroneous headers */", -4);
    protos("separator", "/**< Separator line between headers and payload */", -5);
    protos("payload", "/**< Message payload */", -6);
    if (multipart)
      protos("multipart", "/**< Multipart payload */", -7);
  }

  if (PR) {
    process_footer(footer);
  }
  else if (PT) {
    if (FLAGFILE)
      read_header_flags(FLAGFILE);

    if (TEMPLATE == "") { TEMPLATE = PT ".in"; }
    RS0=RS; RS="\n";
    getline theader < TEMPLATE;
    getline header < TEMPLATE;
    getline template < TEMPLATE;
    getline footer < TEMPLATE;
    RS=RS0;

    sub(/.*[\/]/, "", TEMPLATE);
    gsub(/#AUTO#/, auto, header);
    gsub(/#DATE#/, "@date Generated: " date, header);
    print header > PT NEW;

    if (MC_SHORT_SIZE) {
      printf("static msg_href_t const " \
	     "%s_short_forms[MC_SHORT_SIZE] = \n{\n",
	     module) > PT NEW;

      for (i = 1; i <= MC_SHORT_SIZE; i = i + 1) {
	c = (i == MC_SHORT_SIZE) ? "" : ",";
	if (i in shorts) {
	  n = shorts[i];
        flags = header_flags[n]; if (flags) flags = ",\n      " flags;

	  printf("  { /* %s */ %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n",
		 substr(lower_case, i, 1),
		 tprefix, n, module, prefix, n, flags, c)	\
	    > PT NEW;
	}
	else {
	  printf("  { NULL }%s\n", c) \
	    > PT NEW;
	}
      }
      printf("};\n\n") > PT NEW;
    }

    # printf("extern msg_hclass_t msg_multipart_class[];\n\n") > PT NEW;

    printf("msg_mclass_t const %s_mclass[1] = \n{{\n", module) > PT NEW;
    printf("# if defined (%s_HCLASS)\n", toupper(module)) > PT NEW;
    printf("  %s_HCLASS,\n", toupper(module)) > PT NEW;
    printf("#else\n") > PT NEW;
    printf("  {{ 0 }},\n") > PT NEW;
    printf("#endif\n") > PT NEW;
    printf("  %s_VERSION_CURRENT,\n", toupper(module)) > PT NEW;
    printf("  %s_PROTOCOL_TAG,\n", toupper(module)) > PT NEW;
    printf("#if defined (%s_PARSER_FLAGS)\n", toupper(module)) > PT NEW;
    printf("  %s_PARSER_FLAGS,\n", toupper(module)) > PT NEW;
    printf("#else\n") > PT NEW;
    printf("  0,\n") > PT NEW;
    printf("#endif\n") > PT NEW;
    printf("  sizeof(%s_t),\n", module) > PT NEW;
    printf("  %s_extract_body,\n", module) > PT NEW;

    len = split("request status separator payload unknown error", unnamed, " ");

    for (i = 1; i <= len; i++) {
      printf("  {{ %s_%s_class, offsetof(%s_t, %s_%s) }},\n",
	     tprefix, unnamed[i], module, prefix, unnamed[i]) > PT NEW;
    }
    if (multipart) {
      printf("  {{ %s_class, offsetof(%s_t, %s_multipart) }},\n",
	     multipart, module, prefix) > PT NEW;
    } else {
      printf("  {{ NULL, 0 }},\n") > PT NEW;
    }
    if (MC_SHORT_SIZE) {
      printf("  %s_short_forms, \n", module) > PT NEW;
    }
    else {
      printf("  NULL, \n") > PT NEW;
    }
    printf("  %d, %d, \n", MC_HASH_SIZE, total) > PT NEW;
    printf("  {\n") > PT NEW;

    for (i = 0; i < total; i++) {
      n = headers[i];
      h = hashed[n];

      if (h < 0)
	continue;

      for (j = h % MC_HASH_SIZE; j in header_hash;) {
	if (++j == MC_HASH_SIZE) {
	  j = 0;
	}
      }

      header_hash[j] = n;
    }

    for (i = 0; i < MC_HASH_SIZE; i++) {
      c = (i + 1 == MC_HASH_SIZE) ? "" : ",";
      if (i in header_hash) {
	n = header_hash[i];
        flags = header_flags[n]; if (flags) flags = ",\n      " flags;

	printf("    { %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n",
	       tprefix, n, module, prefix, n, flags, c) > PT NEW;
      }
      else {
	printf("    { NULL, 0 }%s\n", c) > PT NEW;
      }
    }
    printf("  }\n}};\n\n") > PT NEW;
  }
}


2.13.awk

#!/usr/bin/awk -f
BEGIN	{
	printf("#include \"apue.h\"\n")
	printf("#include <erron.h>\n")
	printf("#include <limits.h>\n")
	printf("\n")
	printf("static void pr_sysconf(char *, int);\n")
	printf("static void pr_pathconf(char *, char *, int);\n")
	printf("\n")
	printf("int\n")
	printf("main(int argc, char *argv[])\n")
	printf("{\n")
	printf("\tif (argc != 2)\n")
	printf("\t\terr_quit{\"usage: a.out <dirname>\";\n\n")
	FS="\t+"
	while (getline <"sysconf.sym" > 0) {
		printf("#ifdef %s\n", $1)
		printf("\tprintf(\"%s defined to be %%ld\\n\", (long)%s+0);\n", $1, $1)
		printf("#else\n")
		printf("\tprintf(\"no symbol for %s\\n\");\n", $1)
		printf("#endif\n")
		printf("#ifdef %s\n", $2)
		printf("\tpr_sysconf(\"%s = \", %s);\n", $1, $2)
		printf("#else\n")
		printf("\tprintf(\"no symbol for %s\\n\");\n", $2)
		printf("#endif\n")
	}
	close("sysconf.sym")
	while (getline <"pathconf.sym" > 0) {
		printf("#ifdef %s\n", $1)
        printf(" printf(\"%s defined to be %%d\\n\", %s+0);\n", $1, $1)
        printf("#else\n")
        printf(" printf(\"no symbol for %s\\n\");\n", $1)
        printf("#endif\n")
        printf("#ifdef %s\n", $2)
        printf(" pr_pathconf(\"%s =\", argv[1], %s);\n", $1, $2)
        printf("#else\n")
        printf(" printf(\"no symbol for %s\\n\");\n", $2)
        printf("#endif\n")
    }
    close("pathconf.sym")
    exit
	}
END {
    printf(" exit(0);\n")
    printf("}\n\n")
    printf("static void\n")
    printf("pr_sysconf(char *mesg, int name)\n")
    printf("{\n")
    printf(" long val;\n\n")
    printf(" fputs(mesg, stdout);\n")
    printf(" errno = 0;\n")
    printf(" if ((val = sysconf(name)) < 0) {\n")
    printf("  if (errno != 0) {\n")
    printf("   if (errno == EINVAL)\n")
    printf("    fputs(\" (not supported)\\n\", stdout);\n")
    printf("   else\n")
    printf("    err_sys(\"sysconf error\");\n")
    printf("  } else {\n")
    printf("   fputs(\" (no limit)\\n\", stdout);\n")
    printf("  }\n")
    printf(" } else {\n")
    printf("  printf(\" %%ld\\n\", val);\n")
    printf(" }\n")
    printf("}\n\n")
    printf("static void\n")
    printf("pr_pathconf(char *mesg, char *path, int name)\n")
    printf("{\n")
    printf(" long val;\n")
    printf("\n")
    printf(" fputs(mesg, stdout);\n")
    printf(" errno = 0;\n")
    printf(" if ((val = pathconf(path, name)) < 0) {\n")
    printf("  if (errno != 0) {\n")
    printf("   if (errno == EINVAL)\n")
    printf("    fputs(\" (not supported)\\n\", stdout);\n")
    printf("   else\n")
    printf("    err_sys(\"pathconf error, path = %%s\", path);\n")
    printf("  } else {\n")
    printf("   fputs(\" (no limit)\\n\", stdout);\n")
    printf("  }\n")
    printf(" } else {\n")
    printf("  printf(\" %%ld\\n\", val);\n")
    printf(" }\n")
    printf("}\n")
}
}


Add "print to stderr" in awk #chunks

Add "print to stderr" in awk #chunks

match($0, /^mix ([^ ]+) +# +(.*)/, arr) { print arr[1] "\t" arr[2] }

# https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html
#
# sub(regexp, replacement [, target])

print "something" > "/dev/stderr"


TestUtils: add test case for curlPost() (thanks to httpbin.org)

@include "include/Utils"

BEGIN {
    START_TEST("Utils.awk")

    T("GawkVersion", 1)
    {
        initGawk()
        assertTrue(GawkVersion ~ "^4.")
    }

    T("Rlwrap", 1)
    {
        initRlwrap()
        assertEqual(Rlwrap, "rlwrap")
    }

    T("Emacs", 1)
    {
        initEmacs()
        assertEqual(Emacs, "emacs")
    }

    T("newerVersion()", 5)
    {
        assertTrue(newerVersion("0.9", "0.8"))
        assertTrue(newerVersion("0.9.0.1", "0.9.0"))
        assertTrue(newerVersion("1.0", "0.9.9999"))
        assertTrue(newerVersion("1.9.9999", "1.9.10"))
        assertTrue(newerVersion("2", "1.9.9999"))
    }

    T("curl()", 1)
    {
        delete tokens; delete ast
        tokenize(tokens, curl("https://httpbin.org/get"))
        parseJson(ast, tokens)
        assertEqual(unparameterize(ast[0 SUBSEP "url"]),
                    "https://httpbin.org/get")
    }

    T("curlPost()", 1)
    {
        delete tokens; delete ast
        tokenize(tokens, curlPost("https://httpbin.org/post", "fizz=buzz"))
        parseJson(ast, tokens)
        assertEqual(unparameterize(ast[0 SUBSEP "url"]),
                    "https://httpbin.org/post")
    }

    T("dump()", 3)
    {
        delete group
        assertEqual(dump("a", group), 1)

        delete group
        assertEqual(dump("Århus", group), 6)

        delete group
        assertEqual(dump("안녕하세요 세계", group), 22)
    }

    T("base64()", 1)
    {
        assertEqual(base64("ninja"), "bmluamE=")
    }

    #T("uprintf", 1)
    #{
    #    assertEqual(uprintf("Ma\\u00f1ana"), "Mañana")
    #}

    END_TEST()
}

@include "include/Utils"

BEGIN {
    START_TEST("Utils.awk")

    T("GawkVersion", 1)
    {
        initGawk()
        assertTrue(GawkVersion ~ "^4.")
    }

    T("Rlwrap", 1)
    {
        initRlwrap()
        assertEqual(Rlwrap, "rlwrap")
    }

    T("Emacs", 1)
    {
        initEmacs()
        assertEqual(Emacs, "emacs")
    }

    T("newerVersion()", 5)
    {
        assertTrue(newerVersion("0.9", "0.8"))
        assertTrue(newerVersion("0.9.0.1", "0.9.0"))
        assertTrue(newerVersion("1.0", "0.9.9999"))
        assertTrue(newerVersion("1.9.9999", "1.9.10"))
        assertTrue(newerVersion("2", "1.9.9999"))
    }

    T("curl()", 1)
    {
        delete tokens; delete ast
        tokenize(tokens, curl("https://httpbin.org/get"))
        parseJson(ast, tokens)
        assertEqual(unparameterize(ast[0 SUBSEP "url"]),
                    "https://httpbin.org/get")
    }

    T("dump()", 3)
    {
        delete group
        assertEqual(dump("a", group), 1)

        delete group
        assertEqual(dump("Århus", group), 6)

        delete group
        assertEqual(dump("안녕하세요 세계", group), 22)
    }

    T("base64()", 1)
    {
        assertEqual(base64("ninja"), "bmluamE=")
    }

    #T("uprintf", 1)
    #{
    #    assertEqual(uprintf("Ma\\u00f1ana"), "Mañana")
    #}

    END_TEST()
}


rng2rnc.awk: Various improvements

rng2rnc.awk: Various improvements

#!/bin/awk
# -*- indent-tabs-mode: nil; c-basic-offset: 2 -*-
# rnc2rng.awk - Convert RELAX NG Compact Syntax to XML Syntax
# Copyright (C) 2007 Shaun McCance <shaunm@gnome.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# This program is free software, but that doesn't mean you should use it.
# I wanted to write and maintain the Mallard schema in RNC in code blocks
# within the specification, but I needed to distribute the schema in RNG.
# Since xmllint (libxml2) does not currently support RNC, and since I'm
# not willing to introduce more dependencies, awk was my only option.
#
# This awk script is *NOT* a complete implementation, and probably never
# will be.  Its sole purpose is to handle the Mallard schema, and it is
# sufficient for that task.
#
# You are, of course, free to use any of this.  If you do proliferate this
# hack, it is requested (though not required, that would be non-free) that
# you atone for your actions.  A good atonement would be contributing to
# free software.

function parse_define (line) {
  nameix = index(line, "=");
  combine = "";
  if (nameix < 1) next;
  if (substr(line, nameix - 1, 1) == "|") {
    combine = "choice";
    name = substr(line, 1, nameix - 2);
  }
  else if (substr(line, nameix - 1, 1) == "&") {
    combine = "interleave";
    name = substr(line, 1, nameix - 2);
  }
  else {
    name = substr(line, 1, nameix - 1);
  }
  sub(/^ */, "", name);
  sub(/ *$/, "", name);
  if (name == "")
    name = maybe_define;
  if (name == "start")
    define = "<start"
  else
    define = sprintf("<define name='%s'", name);
  if (combine != "")
    define = define " combine='" combine "'"
  define = define ">"
  stack[++stack_i] = define;
  mode = "pattern";
  maybe_define = "";
  if (length(line) >= nameix + 1)
    parse_pattern(substr(line, nameix + 1))
}
function parse_pattern (line) {
  sub(/^ */, "", line)
  if (length(line) == 0) return;
  c = substr(line, 1, 1);
  if (c == "(" || c == "{") {
    stack[++stack_i] = substr(line, 1, 1);
    paren[++paren_i] = stack_i;
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == ")" || c == "}") {
    open = stack[paren[paren_i]];
    oc = substr(open, 1, 1) c;
    if (oc != "()" && oc != "{}") {
      print "Mismatched parentheses on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }

    tag = "";
    if (length(open) > 1 && substr(open, 2, 1) == "&") {
      tag = "interleave";
    }
    else if (length(open) > 1 && substr(open, 2, 1) == "|") {
      tag = "choice";
    }
    else if (oc == "()") {
      tag = "group";
    }

    tmp = "";
    if (tag != "") {
      tmp = "<" tag ">";
    }
    for (i = paren[paren_i] + 1; i <= stack_i; i++) {
      tmp = tmp stack[i] "\n";
    }
    if (tag != "") {
      tmp = tmp "</" tag ">";
    }
    stack_i = paren[paren_i];
    stack[stack_i] = tmp;
    paren_i--;

    if (oc == "{}") {
      if (match(stack[stack_i - 1], "^<element")) {
        tmp = stack[stack_i - 1] "\n";
        if (stack[stack_i] != "") {
          tmp = tmp stack[stack_i] "\n";
        } else {
          tmp = tmp "<empty/>\n";
        }
        tmp = tmp "</element>";
        stack[--stack_i] = tmp;
      }
      else if (match(stack[stack_i - 1], "^<attribute")) {
        tmp = stack[stack_i - 1] "\n";
        if (stack[stack_i] != "") {
          tmp = tmp stack[stack_i] "\n";
        } else {
          tmp = tmp "<empty/>\n";
        }
        tmp = tmp "</attribute>";
        stack[--stack_i] = tmp;
      }
      else if (stack[stack_i - 1] == "<list>") {
        tmp = stack[stack_i - 1] "\n";
        tmp = tmp stack[stack_i] "\n";
        tmp = tmp "</list>";
        stack[--stack_i] = tmp;
      }
      else if (stack[stack_i - 1] == "<mixed>") {
        tmp = stack[stack_i - 1] "\n";
        tmp = tmp stack[stack_i] "\n";
        tmp = tmp "</mixed>";
        stack[--stack_i] = tmp;
      }
    }
    if (paren_i == 0) {
      mode = "grammar";
    }
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "|" || c == "&" || c == ",") {
    if (length(stack[paren[paren_i]]) == 1) {
      stack[paren[paren_i]] = stack[paren[paren_i]] c;
    }
    else if (length(stack[paren[paren_i]]) < 2 || substr(stack[paren[paren_i]], 2) != c) {
      if (paren_i == 0) {
        tmp = stack[stack_i];
        stack[stack_i] = c c;
        paren[++paren_i] = stack_i;
        stack[++stack_i] = tmp;
      }
      else {
        print "Mismatched infix operators on line " FNR | "cat 1>&2";
        error = 1;
        exit 1
      }
    }
    postop = 1;
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "?") {
    stack[stack_i] = "<optional>" stack[stack_i] "</optional>"
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "*") {
    stack[stack_i] = "<zeroOrMore>" stack[stack_i] "</zeroOrMore>"
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "+") {
    stack[stack_i] = "<oneOrMore>" stack[stack_i] "</oneOrMore>"
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "\"") {
    txt = substr(line, 2);
    sub(/".*/, "", txt)
    stack[++stack_i] = "<value>" txt "</value>";
    if (length(line) >= length(txt) + 3)
      parse_pattern(substr(line, length(txt) + 3));
  }
  else if (match(line, "^element ")) {
    aft = substr(line, 8);
    sub(/^ */, "", aft);
    name = aft;
    stack[++stack_i] = "<element";
    mode = "name_class";
    name_class_i = stack_i;
    postop = 0;
    parse_name_class(name);
  }
  else if (match(line, "^attribute ")) {
    aft = substr(line, 10);
    sub(/^ */, "", aft);
    name = aft;
    stack[++stack_i] = "<attribute";
    mode = "name_class";
    name_class_i = stack_i;
    postop = 0;
    parse_name_class(name);
  }
  else if (match(line, "^list ")) {
    aft = substr(line, 5);
    sub(/^ */, "", aft);
    stack[++stack_i] = "<list>";
    if (aft != "") { parse_pattern(aft); }
  }
  else if (match(line, "^mixed ")) {
    aft = substr(line, 6);
    sub(/^ */, "", aft);
    stack[++stack_i] = "<mixed>";
    if (aft != "") { parse_pattern(aft); }
  }
  else if (match(line, /^text[^A-Za-z]/) || match(line, /^text$/)) {
    stack[++stack_i] = "<text/>";
    aft = substr(line, 5);
    sub(/^ */, "", aft);
    if (aft != "") { parse_pattern(aft); }
  }
  else if (match(line, "^default namespace ")) {
    print "default namespace appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, "^namespace ")) {
    print "namespace appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, "^start ")) {
    print "start appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, "^include ")) {
    print "include appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, /^xsd:[A-Za-z_.-]/)) {
    name = substr(line, 1);
    sub(/^xsd:/, "", name);
    sub(/[^A-Za-z_.-]+.*/, "", name);
    stack[++stack_i] = sprintf("<data type='%s' datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes'/>",
                               name);
    postop = 0;
    if (length(line) >= length(name) + 5) {
      aft = substr(line, length(name) + 5);
      parse_pattern(aft);
    }
  }
  else if (match(line, /^[A-Za-z_.-]/)) {
    if (popop(line)) {
      next;
    }
    name = substr(line, 1);
    sub(/[^A-Za-z_.-]+.*/, "", name);
    if (name == "empty") {
      stack[++stack_i] = "<empty/>";
    }
    else if (name == "notAllowed") {
      stack[++stack_i] = "<notAllowed/>";
    }
    else {
      stack[++stack_i] = sprintf("<ref name='%s'/>", name);
    }
    postop = 0;
    if (length(line) >= length(name) + 1) {
      aft = substr(line, length(name) + 1);
      parse_pattern(aft);
    }
  }
}
function popop (line) {
  op = stack[paren[paren_i]];
  if (postop == 0 && (op == "||" || op == "&&" || op == ",,")) {
    stack[paren[paren_i]] = "(" substr(op, 1, 1);
    parse_pattern(")");
    mode = "grammar";
    if (line != "") {
      parse_define(line);
    }
    return 1;
  }
  return 0;
}
function parse_name_class (line) {
  sub(/^ */, "", line)
  if (length(line) == 0) return;
  c = substr(line, 1, 1);
  if (c == "{") {
    if (stack_i != name_class_i) {
      tmp = "";
      for (i = stack_i; i >= name_class_i; i--) {
        if (stack[i] == "<except>") {
          tmp = "<except>" tmp "</except>";
        }
        else if (stack[i] == "<anyName>") {
          tmp = "<anyName>" tmp "</anyName>";
        }
        else {
          tmp = stack[i] tmp
        }
      }
      stack[name_class_i] = tmp;
      stack_i = name_class_i;
    }
    mode = "pattern";
    parse_pattern(line);
  }
  else if (c == "*") {
    if (stack[stack_i] == "<element" || stack[stack_i] == "<attribute") {
      stack[stack_i] = stack[stack_i] ">";
    }
    stack[++stack_i] = "<anyName>";
    parse_name_class(substr(line, 2));
  }
  else if (c == "-" && stack[stack_i] == "<anyName>") {
    stack[++stack_i] = "<except>"
    parse_name_class(substr(line, 2));
  }
  else if (c == "|") {
    if (length(stack[paren[paren_i]]) == 1) {
      stack[paren[paren_i]] = stack[paren[paren_i]] c;
    }
    else if (substr(stack[paren[paren_i]], 2) != "|") {
      print "Mismatched infix operators on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }
    parse_name_class(substr(line, 2));
  }
  else if (c == "(") {
    stack[++stack_i] = "(";
    paren[++paren_i] = stack_i;
    parse_name_class(substr(line, 2));
  }
  else if (c == ")") {
    open = stack[paren[paren_i]];
    if (substr(open, 1, 1) != "(") {
      print "Mismatched parentheses on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }
    if (length(open) == 1 || substr(open, 2, 1) != "|") {
      print "Unknown name class pattern on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }
    tmp = ""
    for (i = paren[paren_i] + 1; i <= stack_i; i++) {
      tmp = tmp stack[i] "\n";
    }
    stack_i = paren[paren_i];
    stack[stack_i] = tmp;
    paren_i--;
    parse_name_class(substr(line, 2));
  }
  else if (match(line, /^[A-Za-z_.-]/)) {
    name = substr(line, 1);
    sub(/[^A-Za-z_.-]+.*/, "", name);
    if (length(line) >= length(name) + 1)
      aft = substr(line, length(name) + 1);
    else
      aft = "";
    if (length(aft) >= 2 && substr(aft, 1, 2) == ":*") {
      namespace_uri = "";
      if (name == "xml") {
        namespace_uri = "http://www.w3.org/XML/1998/namespace";
      }
      else {
        for (i = 1; i <= namespaces_i; i++) {
          if (nsnames[i] == name) {
            namespace_uri = namespaces[i];
            break;
          }
        }
      }
      if (namespace_uri == "")
        stack[++stack_i] = "<nsName/>";
      else
        stack[++stack_i] = sprintf("<nsName ns='%s'/>", namespace_uri);
      if (length(aft) >= 3)
        aft = substr(aft, 3);
      else
        aft = "";
    }
    else {
      if (length(aft) >= 2 && substr(aft, 1, 1) == ":") {
        checkns = "";
        pos = 1;
        while (pos <= namespaces_i) {
          if (namespaces[pos] != "" && nsnames[pos] == name) {
            checkns = namespaces[pos];
            break;
          }
          pos++;
        }
        if (name != "xml" && checkns == "") {
          print "Unknown namespace prefix " name " on line " FNR | "cat 1>&2";
          error = 1;
          exit 1
        }
        localname = substr(aft, 2);
        sub(/[^A-Za-z_.-]+.*/, "", localname);
        if (length(aft) >= length(localname) + 2)
          aft = substr(aft, length(localname) + 2);
        else
          aft = "";
        name = name ":" localname;
      }
      if (stack[stack_i] == "<element" || stack[stack_i] == "<attribute") {
        stack[stack_i] = stack[stack_i] sprintf(" name='%s'>", name);
      }
      else {
        stack[++stack_i] = sprintf("<name>%s</name>", name);
      }
    }
    parse_name_class(aft);
  }
}

function printstack () {
  pos = 1;
  while (pos <= stack_i) {
    printstackone();
  }
}
function printstackone () {
  if (substr(stack[pos], 1, 1) == "#") {
    print "<!--";
    while (substr(stack[pos], 1, 1) == "#") {
      cmt = substr(stack[pos], 2);
      sub(/^ */, "", cmt);
      print cmt;
      pos++;
    }
    print "-->";
  }
  else if (substr(stack[pos], 1, 6) == "<start") {
    print stack[pos];
    pos++;
    printstackone();
    print "</start>"
  }
  else if (substr(stack[pos], 1, 7) == "<define") {
    print stack[pos];
    pos++;
    printstackone();
    print "</define>"
  }
  else {
    print stack[pos];
    pos++;
  }
}

BEGIN {
  mode = "grammar";
  stack_i = 0;
  paren_i = 0;
  namespaces_i = 0;
  include_i = 0;
  postop = 0;
  error = 0;
  maybe_define = "";
}

END {
  popop("");
  if (!error) {
    print "<grammar xmlns='http://relaxng.org/ns/structure/1.0'";
    pos = 1;
    while (pos <= namespaces_i) {
      if (namespaces[pos] != "") {
        printf " xmlns:%s='%s'", nsnames[pos], namespaces[pos];
      }
      pos++;
    }
    if (default_namespace != "") {
      printf " ns='%s'>\n", default_namespace;
    }
    else {
      print ">";
    }
    printstack()
    print "</grammar>"
  }
}
/^[^#]/ || /^$/ {
  if (substr(stack[stack_i], 1, 1) == "#") {
    stack[++stack_i] = " "
  }
}

mode == "pattern" && paren_i == 0 && /.*=/ { mode = "grammar"; }
mode == "grammar" && /.*=/ {
  if (match($0, "^default namespace ")) {
    namespace = substr($0, index($0, "=") + 1);
    nsname = substr($0, 19, index($0, "=") - 19);
    sub(/^ */, "", nsname);
    sub(/ *$/, "", nsname);
    sub(/^ *"/, "", namespace);
    sub(/" *$/, "", namespace);
    if (nsname != "") {
      nsnames[++namespaces_i] = nsname;
      namespaces[namespaces_i] = namespace;
    }
    default_namespace = namespace
  }
  else if (match($0, "^namespace ")) {
    namespace = substr($0, index($0, "=") + 1);
    nsname = substr($0, 11, index($0, "=") - 11);
    sub(/^ */, "", nsname);
    sub(/ *$/, "", nsname);
    sub(/^ *"/, "", namespace);
    sub(/" *$/, "", namespace);
    if (nsname != "") {
      nsnames[++namespaces_i] = nsname;
      namespaces[namespaces_i] = namespace;
    }
  }
  else {
    parse_define($0);
  }
  next;
}
mode == "grammar" && /^include / {
  href = substr($0, index($0, "\"") + 1);
  brace = substr(href, index(href, "\"") + 1);
  href = substr(href, 1, index(href, "\"") - 1);
  sub(/^ */, "", brace);
  sub(/ *$/, "", brace);
  sub(/\.rnc$/, ".rng", href);
  if (brace == "{") {
    stack[++stack_i] = "<include href=\"" href "\">";
    include_i = stack_i;
  }
  else {
    stack[++stack_i] = "<include href=\"" href "\"/>";
  }
}
mode == "grammar" && /^ *[A-Za-z_.-]/ {
  maybe_define = substr($0, 1, length($0));
  sub(/^ */, "", maybe_define);
  sub(/ *$/, "", maybe_define);
}
include_i != 0 && paren_i == 0 && /}/ {
  if (substr(stack[include_i], 1, 8) == "<include") {
    stack[++stack_i] = "</include>";
  }
  else {
    print "Mismatched parentheses on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  mode = "grammar";
  include_i = 0;
  next;
}
mode == "pattern" && /^[^#]/ {
  parse_pattern($0);
  next;
}
mode == "name_class" && /^[^#]/ {
  parse_name_class($0);
  next;
}
# Doesn't handle all comments. That would require modifying
# parse_pattern and parse_name_class and generally being a lot
# more clever about output. But it handles comments that start
# a line outside a pattern. Enough for me.
/#.*/ {
  popop("");
  stack[++stack_i] = $0
  next;
}

#!/bin/awk
# -*- indent-tabs-mode: nil; c-basic-offset: 2 -*-
# rnc2rng.awk - Convert RELAX NG Compact Syntax to XML Syntax
# Copyright (C) 2007 Shaun McCance <shaunm@gnome.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# This program is free software, but that doesn't mean you should use it.
# I wanted to write and maintain the Mallard schema in RNC in code blocks
# within the specification, but I needed to distribute the schema in RNG.
# Since xmllint (libxml2) does not currently support RNC, and since I'm
# not willing to introduce more dependencies, awk was my only option.
#
# This awk script is *NOT* a complete implementation, and probably never
# will be.  Its sole purpose is to handle the Mallard schema, and it is
# sufficient for that task.
#
# You are, of course, free to use any of this.  If you do proliferate this
# hack, it is requested (though not required, that would be non-free) that
# you atone for your actions.  A good atonement would be contributing to
# free software.

function parse_define (line) {
  nameix = index(line, "=");
  combine = "";
  if (nameix < 1) next;
  if (substr(line, nameix - 1, 1) == "|") {
    combine = "choice";
    name = substr(line, 1, nameix - 2);
  }
  else if (substr(line, nameix - 1, 1) == "&") {
    combine = "interleave";
    name = substr(line, 1, nameix - 2);
  }
  else {
    name = substr(line, 1, nameix - 1);
  }
  sub(/^ */, "", name);
  sub(/ *$/, "", name);
  if (name == "start")
    define = "<start"
  else
    define = sprintf("<define name='%s'", name);
  if (combine != "")
    define = define " combine='" combine "'"
  define = define ">"
  stack[++stack_i] = define;
  mode = "pattern";
  if (length(line) >= nameix + 1)
    parse_pattern(substr(line, nameix + 1))
}
function parse_pattern (line) {
  sub(/^ */, "", line)
  if (length(line) == 0) return;
  c = substr(line, 1, 1);
  if (c == "(" || c == "{") {
    stack[++stack_i] = substr(line, 1, 1);
    paren[++paren_i] = stack_i;
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == ")" || c == "}") {
    open = stack[paren[paren_i]];
    oc = substr(open, 1, 1) c;
    if (oc != "()" && oc != "{}") {
      print "Mismatched parentheses on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }

    tag = "";
    if (length(open) > 1 && substr(open, 2, 1) == "&") {
      tag = "interleave";
    }
    else if (length(open) > 1 && substr(open, 2, 1) == "|") {
      tag = "choice";
    }
    else if (oc == "()") {
      tag = "group";
    }

    tmp = "";
    if (tag != "") {
      tmp = "<" tag ">";
    }
    for (i = paren[paren_i] + 1; i <= stack_i; i++) {
      tmp = tmp stack[i] "\n";
    }
    if (tag != "") {
      tmp = tmp "</" tag ">";
    }
    stack_i = paren[paren_i];
    stack[stack_i] = tmp;
    paren_i--;

    if (oc == "{}") {
      if (match(stack[stack_i - 1], "^<element")) {
        tmp = stack[stack_i - 1] "\n";
        if (stack[stack_i] != "") {
          tmp = tmp stack[stack_i] "\n";
        } else {
          tmp = tmp "<empty/>\n";
        }
        tmp = tmp "</element>";
        stack[--stack_i] = tmp;
      }
      else if (match(stack[stack_i - 1], "^<attribute")) {
        tmp = stack[stack_i - 1] "\n";
        if (stack[stack_i] != "") {
          tmp = tmp stack[stack_i] "\n";
        } else {
          tmp = tmp "<empty/>\n";
        }
        tmp = tmp "</attribute>";
        stack[--stack_i] = tmp;
      }
      else if (stack[stack_i - 1] == "<list>") {
        tmp = stack[stack_i - 1] "\n";
        tmp = tmp stack[stack_i] "\n";
        tmp = tmp "</list>";
        stack[--stack_i] = tmp;
      }
      else if (stack[stack_i - 1] == "<mixed>") {
        tmp = stack[stack_i - 1] "\n";
        tmp = tmp stack[stack_i] "\n";
        tmp = tmp "</mixed>";
        stack[--stack_i] = tmp;
      }
    }
    if (paren_i == 0) {
      mode = "grammar";
    }
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "|" || c == "&" || c == ",") {
    if (length(stack[paren[paren_i]]) == 1) {
      stack[paren[paren_i]] = stack[paren[paren_i]] c;
    }
    else if (length(stack[paren[paren_i]]) < 2 || substr(stack[paren[paren_i]], 2) != c) {
      if (paren_i == 0) {
        tmp = stack[stack_i];
        stack[stack_i] = c c;
        paren[++paren_i] = stack_i;
        stack[++stack_i] = tmp;
      }
      else {
        print "Mismatched infix operators on line " FNR | "cat 1>&2";
        error = 1;
        exit 1
      }
    }
    postop = 1;
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "?") {
    stack[stack_i] = "<optional>" stack[stack_i] "</optional>"
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "*") {
    stack[stack_i] = "<zeroOrMore>" stack[stack_i] "</zeroOrMore>"
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "+") {
    stack[stack_i] = "<oneOrMore>" stack[stack_i] "</oneOrMore>"
    if (length(line) >= 2)
      parse_pattern(substr(line, 2));
  }
  else if (c == "\"") {
    txt = substr(line, 2);
    sub(/".*/, "", txt)
    stack[++stack_i] = "<value>" txt "</value>";
    if (length(line) >= length(txt) + 3)
      parse_pattern(substr(line, length(txt) + 3));
  }
  else if (match(line, "^element ")) {
    aft = substr(line, 8);
    sub(/^ */, "", aft);
    name = aft;
    stack[++stack_i] = "<element";
    mode = "name_class";
    name_class_i = stack_i;
    postop = 0;
    parse_name_class(name);
  }
  else if (match(line, "^attribute ")) {
    aft = substr(line, 10);
    sub(/^ */, "", aft);
    name = aft;
    stack[++stack_i] = "<attribute";
    mode = "name_class";
    name_class_i = stack_i;
    postop = 0;
    parse_name_class(name);
  }
  else if (match(line, "^list ")) {
    aft = substr(line, 5);
    sub(/^ */, "", aft);
    stack[++stack_i] = "<list>";
    if (aft != "") { parse_pattern(aft); }
  }
  else if (match(line, "^mixed ")) {
    aft = substr(line, 6);
    sub(/^ */, "", aft);
    stack[++stack_i] = "<mixed>";
    if (aft != "") { parse_pattern(aft); }
  }
  else if (match(line, /^text[^A-Za-z]/) || match(line, /^text$/)) {
    stack[++stack_i] = "<text/>";
    aft = substr(line, 5);
    sub(/^ */, "", aft);
    if (aft != "") { parse_pattern(aft); }
  }
  else if (match(line, "^default namespace ")) {
    print "default namespace appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, "^namespace ")) {
    print "namespace appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, "^start ")) {
    print "start appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, "^include ")) {
    print "include appeared out of context on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  else if (match(line, /^xsd:[A-Za-z_.-]/)) {
    name = substr(line, 1);
    sub(/^xsd:/, "", name);
    sub(/[^A-Za-z_.-]+.*/, "", name);
    stack[++stack_i] = sprintf("<data type='%s' datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes'/>",
                               name);
    postop = 0;
    if (length(line) >= length(name) + 5) {
      aft = substr(line, length(name) + 5);
      parse_pattern(aft);
    }
  }
  else if (match(line, /^[A-Za-z_.-]/)) {
    c = stack[paren[paren_i]];
    if (postop == 0 && (c == "||" || c == "&&" || c == ",,")) {
      stack[paren[paren_i]] = "(" substr(c, 1, 1);
      parse_pattern(")");
      mode = "grammar";
      parse_define(line);
      next;
    }
    name = substr(line, 1);
    sub(/[^A-Za-z_.-]+.*/, "", name);
    if (name == "empty") {
      stack[++stack_i] = "<empty/>";
    }
    else if (name == "notAllowed") {
      stack[++stack_i] = "<notAllowed/>";
    }
    else {
      stack[++stack_i] = sprintf("<ref name='%s'/>", name);
    }
    postop = 0;
    if (length(line) >= length(name) + 1) {
      aft = substr(line, length(name) + 1);
      parse_pattern(aft);
    }
  }
}

function parse_name_class (line) {
  sub(/^ */, "", line)
  if (length(line) == 0) return;
  c = substr(line, 1, 1);
  if (c == "{") {
    if (stack_i != name_class_i) {
      tmp = "";
      for (i = stack_i; i >= name_class_i; i--) {
        if (stack[i] == "<except>") {
          tmp = "<except>" tmp "</except>";
        }
        else if (stack[i] == "<anyName>") {
          tmp = "<anyName>" tmp "</anyName>";
        }
        else {
          tmp = stack[i] tmp
        }
      }
      stack[name_class_i] = tmp;
      stack_i = name_class_i;
    }
    mode = "pattern";
    parse_pattern(line);
  }
  else if (c == "*") {
    if (stack[stack_i] == "<element" || stack[stack_i] == "<attribute") {
      stack[stack_i] = stack[stack_i] ">";
    }
    stack[++stack_i] = "<anyName>";
    parse_name_class(substr(line, 2));
  }
  else if (c == "-" && stack[stack_i] == "<anyName>") {
    stack[++stack_i] = "<except>"
    parse_name_class(substr(line, 2));
  }
  else if (c == "|") {
    if (length(stack[paren[paren_i]]) == 1) {
      stack[paren[paren_i]] = stack[paren[paren_i]] c;
    }
    else if (substr(stack[paren[paren_i]], 2) != "|") {
      print "Mismatched infix operators on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }
    parse_name_class(substr(line, 2));
  }
  else if (c == "(") {
    stack[++stack_i] = "(";
    paren[++paren_i] = stack_i;
    parse_name_class(substr(line, 2));
  }
  else if (c == ")") {
    open = stack[paren[paren_i]];
    if (substr(open, 1, 1) != "(") {
      print "Mismatched parentheses on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }
    if (length(open) == 1 || substr(open, 2, 1) != "|") {
      print "Unknown name class pattern on line " FNR | "cat 1>&2";
      error = 1;
      exit 1
    }
    tmp = ""
    for (i = paren[paren_i] + 1; i <= stack_i; i++) {
      tmp = tmp stack[i] "\n";
    }
    stack_i = paren[paren_i];
    stack[stack_i] = tmp;
    paren_i--;
    parse_name_class(substr(line, 2));
  }
  else if (match(line, /^[A-Za-z_.-]/)) {
    name = substr(line, 1);
    sub(/[^A-Za-z_.-]+.*/, "", name);
    if (length(line) >= length(name) + 1)
      aft = substr(line, length(name) + 1);
    else
      aft = "";
    if (length(aft) >= 2 && substr(aft, 1, 2) == ":*") {
      for (i = 1; i <= namespaces_i; i++) {
        if (nsnames[i] == name) {
          stack[++stack_i] = sprintf("<nsName ns='%s'/>", namespaces[i])
          break;
        }
      }
      if (length(aft) >= 3)
        aft = substr(aft, 3);
      else
        aft = "";
    }
    else {
      if (length(aft) >= 2 && substr(aft, 1, 1) == ":") {
        checkns = "";
        pos = 1;
        while (pos <= namespaces_i) {
          if (namespaces[pos] != "" && nsnames[pos] == name) {
            checkns = namespaces[pos];
            break;
          }
          pos++;
        }
        if (name != "xml" && checkns == "") {
          print "Unknown namespace prefix " name " on line " FNR | "cat 1>&2";
          error = 1;
          exit 1
        }
        localname = substr(aft, 2);
        sub(/[^A-Za-z_.-]+.*/, "", localname);
        if (length(aft) >= length(localname) + 2)
          aft = substr(aft, length(localname) + 2);
        else
          aft = "";
        name = name ":" localname;
      }
      if (stack[stack_i] == "<element" || stack[stack_i] == "<attribute") {
        stack[stack_i] = stack[stack_i] sprintf(" name='%s'>", name);
      }
      else {
        stack[++stack_i] = sprintf("<name>%s</name>", name);
      }
    }
    parse_name_class(aft);
  }
}

function printstack () {
  pos = 1;
  while (pos <= stack_i) {
    printstackone();
  }
}
function printstackone () {
  if (substr(stack[pos], 1, 1) == "#") {
    print "<!--";
    while (substr(stack[pos], 1, 1) == "#") {
      cmt = substr(stack[pos], 2);
      sub(/^ */, "", cmt);
      print cmt;
      pos++;
    }
    print "-->";
  }
  if (substr(stack[pos], 1, 6) == "<start") {
    print stack[pos];
    pos++;
    printstackone();
    print "</start>"
  }
  else if (substr(stack[pos], 1, 7) == "<define") {
    print stack[pos];
    pos++;
    printstackone();
    print "</define>"
  }
  else {
    print stack[pos];
    pos++;
  }
}

BEGIN {
  mode = "grammar";
  stack_i = 0;
  paren_i = 0;
  namespaces_i = 0;
  include_i = 0;
  postop = 0;
  error = 0;
}

END {
  if (!error) {
    print "<grammar xmlns='http://relaxng.org/ns/structure/1.0'";
    pos = 1;
    while (pos <= namespaces_i) {
      if (namespaces[pos] != "") {
        printf " xmlns:%s='%s'", nsnames[pos], namespaces[pos];
      }
      pos++;
    }
    printf " ns='%s'>\n", default_namespace;
    printstack()
    print "</grammar>"
  }
}
/^[^#]/ {
  if (substr(stack[stack_i], 1, 1) == "#") {
    stack[++stack_i] = " "
  }
}

mode == "pattern" && paren_i == 0 && /.*=/ { mode = "grammar"; }
mode == "grammar" && /.*=/ {
  if (match($0, "^default namespace ")) {
    namespace = substr($0, index($0, "=") + 1);
    nsname = substr($0, 19, index($0, "=") - 19);
    sub(/^ */, "", nsname);
    sub(/ *$/, "", nsname);
    sub(/^ *"/, "", namespace);
    sub(/" *$/, "", namespace);
    if (nsname != "") {
      nsnames[++namespaces_i] = nsname;
      namespaces[namespaces_i] = namespace;
    }
    default_namespace = namespace
  }
  else if (match($0, "^namespace ")) {
    namespace = substr($0, index($0, "=") + 1);
    nsname = substr($0, 11, index($0, "=") - 11);
    sub(/^ */, "", nsname);
    sub(/ *$/, "", nsname);
    sub(/^ *"/, "", namespace);
    sub(/" *$/, "", namespace);
    if (nsname != "") {
      nsnames[++namespaces_i] = nsname;
      namespaces[namespaces_i] = namespace;
    }
  }
  else {
    parse_define($0);
  }
  next;
}
mode == "grammar" && /^include / {
  href = substr($0, index($0, "\"") + 1);
  brace = substr(href, index(href, "\"") + 1);
  href = substr(href, 1, index(href, "\"") - 1);
  sub(/^ */, "", brace);
  sub(/ *$/, "", brace);
  sub(/\.rnc$/, ".rng", href);
  if (brace == "{") {
    stack[++stack_i] = "<include href=\"" href "\">";
    include_i = stack_i;
  }
  else {
    stack[++stack_i] = "<include href=\"" href "\"/>";
  }
}
include_i != 0 && paren_i == 0 && /}/ {
  if (substr(stack[include_i], 1, 8) == "<include") {
    stack[++stack_i] = "</include>";
  }
  else {
    print "Mismatched parentheses on line " FNR | "cat 1>&2";
    error = 1;
    exit 1
  }
  mode = "grammar";
  include_i = 0;
  next;
}
mode == "pattern" {
  parse_pattern($0);
  next;
}
mode == "name_class" {
  parse_name_class($0);
  next;
}
# Doesn't handle all comments. That would require modifying
# parse_pattern and parse_name_class and generally being a lot
# more clever about output. But it handles comments that start
# a line outside a pattern. Enough for me.
/#.*/ {
  stack[++stack_i] = $0
}


Also repair zeroEpisodes shows

Also repair zeroEpisodes shows

# WebScraper has problems getting incomplete data.
#
# Crosscheck the info in EPISODES with the info in SEASONS. Count up episodes
# and compare with the number of episodes listed in the seasons file.

# For now these seem more likely a problem in scraping SEASONS than EPISODES
# so it could be these are false positives in generating spreadsheets.
# Problems listed in BBox_anomalies files are more likely to be real.

# Generate a list of shows that might assist in repair

# INVOCATION
#    awk -v REPAIR_FILE=$REPAIR_FILE -f verifyBBoxInfoFrom-webscraper.awk \
#        $EPISODE_INFO_FILE >>$ERROR_FILE

/ movie / {
    if (NF < 5) {
        print "    Bad input line " NR ":\n          " $0
        next
    }
    gsub ("'", "", $3)
    title = $3
    numEpisodes = $5
    if (numEpisodes != 1)
        print "    " substr ($0,11)
}

/ show / {
    numShows += 1
    gsub ("'", "", $3)
    title = $3
    numEpisodes = $5
    numSeasons = $8
    if (NF < 8) {
        print "    Bad input line " NR ":\n          " $0
        next
    }

    if (numEpisodes == 0) {
        zeroEpisodes += 1
        print "    " substr ($0,11)
        print title >> REPAIR_FILE
    }

    showTitle[numShows] = title
    seas[numShows] = numSeasons
    shouldHave[numShows] = numEpisodes
    doesHave[numShows] = 0
}

/^         / {
    epis  = NF-1
    if ($epis !~ /^[[:digit:]]*$/)
        print "    Bad input line " NR ":\n" $0
    else
        doesHave[numShows] += $epis
}

END {
    if (zeroEpisodes > 0 ) {
        printf ("==> %2d shows with 0 episodes in %s\n", zeroEpisodes, FILENAME) > "/dev/stderr"
        print ""
    }
    for ( i = 1; i <= numShows; i++ ) {
        if (seas[i] != 1 && shouldHave[i] != doesHave[i]) {
            badEpisodes += 1
            print "    "showTitle[i] " has " doesHave[i] " instead of " \
                shouldHave[i] " episodes."
            print showTitle[i] >> REPAIR_FILE
        }
    }
    if (badEpisodes > 0 ) {
        badEpisodes == 1 ? field = "URL" : field = "URLs"
        printf ("==> %2d shows with wrong number of episodes in %s\n", badEpisodes, FILENAME) \
            > "/dev/stderr"
    }
}

# WebScraper has problems getting incomplete data.
#
# Crosscheck the info in EPISODES with the info in SEASONS. Count up episodes
# and compare with the number of episodes listed in the seasons file.

# For now these seem more likely a problem in scraping SEASONS than EPISODES
# so it could be these are false positives in generating spreadsheets.
# Problems listed in BBox_anomalies files are more likely to be real.

# Generate a list of shows that might assist in repair

# INVOCATION
#    awk -v REPAIR_FILE=$REPAIR_FILE -f verifyBBoxInfoFrom-webscraper.awk \
#        $EPISODE_INFO_FILE >>$ERROR_FILE

/ movie / {
    if (NF < 5) {
        print "    Bad input line " NR ":\n          " $0
        next
    }
    title = $3
    numEpisodes = $5
    gsub ("'", "", $0)
    if (numEpisodes != 1)
        print "    " substr ($0,11)
}

/ show / {
    numShows += 1
    title = $3
    numEpisodes = $5
    numSeasons = $8
    if (NF < 8) {
        print "    Bad input line " NR ":\n          " $0
        next
    }
    gsub ("'", "", $0)
    if (numEpisodes == 0) {
        zeroEpisodes += 1
        print "    " substr ($0,11)
    }

    gsub ("'", "", title)
    showTitle[numShows] = title
    seas[numShows] = numSeasons
    shouldHave[numShows] = numEpisodes
    doesHave[numShows] = 0
}

/^         / {
    epis  = NF-1
    if ($epis !~ /^[[:digit:]]*$/)
        print "    Bad input line " NR ":\n" $0
    else
        doesHave[numShows] += $epis
}

END {
    if (zeroEpisodes > 0 ) {
        printf ("==> %2d shows with 0 episodes in %s\n", zeroEpisodes, FILENAME) > "/dev/stderr"
        print ""
    }
    for ( i = 1; i <= numShows; i++ ) {
        if (seas[i] != 1 && shouldHave[i] != doesHave[i]) {
            badEpisodes += 1
            print "    "showTitle[i] " has " doesHave[i] " instead of " \
                shouldHave[i] " episodes."
            print showTitle[i] >> REPAIR_FILE
        }
    }
    if (badEpisodes > 0 ) {
        badEpisodes == 1 ? field = "URL" : field = "URLs"
        printf ("==> %2d shows with wrong number of episodes in %s\n", badEpisodes, FILENAME) \
            > "/dev/stderr"
    }
}


Utils: add initCurl() and curl(url)

Utils: add initCurl() and curl(url)

####################################################################
# Utils.awk                                                        #
####################################################################

# Detect gawk version.
function initGawk(    group) {
    Gawk = "gawk"
    GawkVersion = PROCINFO["version"]

    split(PROCINFO["version"], group, ".")
    if (group[1] < 4) {
        e("[ERROR] Oops! Your gawk (version " GawkVersion ") "          \
          "appears to be too old.\n"                                    \
          "        You need at least gawk 4.0.0 to run this program.")
        exit 1
    }
}

# Detect external bidirectional algorithm utility (fribidi);
# Fallback to Unix `rev` if not found.
function initBiDi() {
    FriBidi = detectProgram("fribidi", "--version", 1)
    BiDiNoPad = FriBidi ? "fribidi --nopad" : "rev"
    BiDi = FriBidi ? "fribidi --width %s" :
        "rev | sed \"s/'/\\\\\\'/\" | xargs printf '%%s '"
}

# Detect external readline wrapper (rlwrap).
function initRlwrap() {
    Rlwrap = detectProgram("rlwrap", "--version")
}

# Detect emacs.
function initEmacs() {
    Emacs = detectProgram("emacs", "--version")
}

# Detect curl.
function initCurl() {
    Curl = detectProgram("curl", "--version")
}

# Log any value if debugging is enabled.
function l(value, name, inline, heredoc, valOnly, numSub, sortedIn) {
    if (Option["debug"]) {
        if (name)
            da(value, name, inline, heredoc, valOnly, numSub, sortedIn)
        else
            d(value)
    }
}

# Return a log message if debugging is enabled.
function m(string) {
    if (Option["debug"])
        return ansi("cyan", string) RS
}

# Fork a rlwrap process as the wrapper. Return non-zero if failed,
function rlwrapMe(    i, command) {
    initRlwrap()

    if (!Rlwrap) {
        l(">> not found: rlwrap")
        return 1
    } else if (!(ENVIRON["TRANS_PROGRAM"] || fileExists(EntryPoint))) {
        l(">> not found: $TRANS_PROGRAM or EntryPoint")
        return 1
    } else {
        command = Rlwrap " " Gawk " " (ENVIRON["TRANS_PROGRAM"] ?
                                       "\"${TRANS_PROGRAM}\"" :
                                       "-f " EntryPoint)                \
            " - " parameterize("-no-rlwrap") # never fork rlwrap again!
        for (i = 1; i < length(ARGV); i++)
            if (ARGV[i])
                command = command " " parameterize(ARGV[i])

        l(">> forking: " command)
        if (!system(command)) {
            l(">> process exited with code 0")
            exit ExitCode
        } else {
            l(">> process exited with non-zero return code")
            return 1
        }
    }
}

# Fork an emacs process as the front-end. Return non-zero if failed,
function emacsMe(    i, params, el, command) {
    initEmacs()

    if (!Emacs) {
        l(">> not found: emacs")
        return 1
    } else if (!(ENVIRON["TRANS_PROGRAM"] || fileExists(EntryPoint))) {
        l(">> not found: $TRANS_PROGRAM or EntryPoint")
        return 1
    } else {
        params = ""
        for (i = 1; i < length(ARGV); i++)
            if (ARGV[i])
                params = params " " parameterize(ARGV[i], "\"")
        if (ENVIRON["TRANS_PROGRAM"]) {
            el = "(progn (setq trans-program (getenv \"TRANS_PROGRAM\")) " \
                "(setq explicit-shell-file-name \"" Gawk "\") "         \
                "(setq explicit-" Gawk "-args (cons trans-program '(\"-\" \"-I\" \"-no-rlwrap\"" params "))) " \
                "(command-execute 'shell) (rename-buffer \"" Name "\"))"
        } else {
            el = "(progn (setq explicit-shell-file-name \"" Gawk "\") " \
                "(setq explicit-" Gawk "-args '(\"-f\" \"" EntryPoint "\" \"--\" \"-I\" \"-no-rlwrap\"" params ")) " \
                "(command-execute 'shell) (rename-buffer \"" Name "\"))"
        }
        command = Emacs " --eval " parameterize(el)

        l(">> forking: " command)
        if (!system(command)) {
            l(">> process exited with code 0")
            exit ExitCode
        } else {
            l(">> process exited with non-zero return code")
            return 1
        }
    }
}

# Fetch the content of a URL. Return a null string if failed.
function curl(url,    command, content, line) {
    initCurl()

    if (!Curl) {
        l(">> not found: curl")
        w("[WARNING] curl is not found.")
        return NULLSTR
    } else {
        command = Curl " --location --silent " url
        content = NULLSTR
        while ((command |& getline line) > 0)
            content = content line "\n"
        return content
    }
}

####################################################################
# Utils.awk                                                        #
####################################################################

# Detect gawk version.
function initGawk(    group) {
    Gawk = "gawk"
    GawkVersion = PROCINFO["version"]

    split(PROCINFO["version"], group, ".")
    if (group[1] < 4) {
        e("[ERROR] Oops! Your gawk (version " GawkVersion ") "          \
          "appears to be too old.\n"                                    \
          "        You need at least gawk 4.0.0 to run this program.")
        exit 1
    }
}

# Detect external bidirectional algorithm utility (fribidi);
# Fallback to Unix `rev` if not found.
function initBiDi() {
    FriBidi = detectProgram("fribidi", "--version", 1)
    BiDiNoPad = FriBidi ? "fribidi --nopad" : "rev"
    BiDi = FriBidi ? "fribidi --width %s" :
        "rev | sed \"s/'/\\\\\\'/\" | xargs printf '%%s '"
}

# Detect external readline wrapper (rlwrap).
function initRlwrap() {
    Rlwrap = detectProgram("rlwrap", "--version")
}

# Detect emacs.
function initEmacs() {
    Emacs = detectProgram("emacs", "--version")
}

# Log any value if debugging is enabled.
function l(value, name, inline, heredoc, valOnly, numSub, sortedIn) {
    if (Option["debug"]) {
        if (name)
            da(value, name, inline, heredoc, valOnly, numSub, sortedIn)
        else
            d(value)
    }
}

# Return a log message if debugging is enabled.
function m(string) {
    if (Option["debug"])
        return ansi("cyan", string) RS
}

# Fork a rlwrap process as the wrapper. Return non-zero if failed,
function rlwrapMe(    i, command) {
    initRlwrap()

    if (!Rlwrap) {
        l(">> not found: rlwrap")
        return 1
    } else if (!(ENVIRON["TRANS_PROGRAM"] || fileExists(EntryPoint))) {
        l(">> not found: $TRANS_PROGRAM or EntryPoint")
        return 1
    } else {
        command = Rlwrap " " Gawk " " (ENVIRON["TRANS_PROGRAM"] ?
                                       "\"${TRANS_PROGRAM}\"" :
                                       "-f " EntryPoint)                \
            " - " parameterize("-no-rlwrap") # never fork rlwrap again!
        for (i = 1; i < length(ARGV); i++)
            if (ARGV[i])
                command = command " " parameterize(ARGV[i])

        l(">> forking: " command)
        if (!system(command)) {
            l(">> process exited with code 0")
            exit ExitCode
        } else {
            l(">> process exited with non-zero return code")
            return 1
        }
    }
}

# Fork an emacs process as the front-end. Return non-zero if failed,
function emacsMe(    i, params, el, command) {
    initEmacs()

    if (!Emacs) {
        l(">> not found: emacs")
        return 1
    } else if (!(ENVIRON["TRANS_PROGRAM"] || fileExists(EntryPoint))) {
        l(">> not found: $TRANS_PROGRAM or EntryPoint")
        return 1
    } else {
        params = ""
        for (i = 1; i < length(ARGV); i++)
            if (ARGV[i])
                params = params " " parameterize(ARGV[i], "\"")
        if (ENVIRON["TRANS_PROGRAM"]) {
            el = "(progn (setq trans-program (getenv \"TRANS_PROGRAM\")) " \
                "(setq explicit-shell-file-name \"" Gawk "\") "         \
                "(setq explicit-" Gawk "-args (cons trans-program '(\"-\" \"-I\" \"-no-rlwrap\"" params "))) " \
                "(command-execute 'shell) (rename-buffer \"" Name "\"))"
        } else {
            el = "(progn (setq explicit-shell-file-name \"" Gawk "\") " \
                "(setq explicit-" Gawk "-args '(\"-f\" \"" EntryPoint "\" \"--\" \"-I\" \"-no-rlwrap\"" params ")) " \
                "(command-execute 'shell) (rename-buffer \"" Name "\"))"
        }
        command = Emacs " --eval " parameterize(el)

        l(">> forking: " command)
        if (!system(command)) {
            l(">> process exited with code 0")
            exit ExitCode
        } else {
            l(">> process exited with non-zero return code")
            return 1
        }
    }
}