Distinguish the cases when a dup symbol is in different versions
(evil, needs a error) or in the same version (harmless, deserves
a mere warning).

Noticed by:     grehan@, tinderbox

#
# Copyright (C) 2006 Daniel M. Eischen.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
#

#
# Make a list of all the library versions listed in the master file.
#
#   versions[] - array indexed by version name, contains number
#                of symbols (+ 1) found for each version.
#   successors[] - array index by version name, contains successor
#                  version name.
#   symbols[][] - array index by [version name, symbol index], contains
#                 names of symbols defined for each version.
#   names[] - array index is symbol name and value is its first version seen,
#	      used to check for duplicate symbols and warn about them.
#
BEGIN {
	brackets = 0;
	errors = warns = 0;
	version_count = 0;
	current_version = "";
	stderr = "/dev/stderr";
	while (getline < vfile) {
		# Strip comments.
		sub("#.*$", "", $0);

		# Strip leading and trailing whitespace.
		sub("^[ \t]+", "", $0);
		sub("[ \t]+$", "", $0);

		if (/^[a-zA-Z0-9._]+[ \t]*{$/) {
			# Strip brace.
			sub("{", "", $1);
			brackets++;
			symver = $1;
			versions[symver] = 1;
			successors[symver] = "";
			generated[symver] = 0;
			version_count++;
		}
		else if (/^}[ \t]*[a-zA-Z0-9._]+[ \t]*;$/) {
			v = $1 != "}" ? $1 : $2;
			# Strip brace.
			sub("}", "", v);
			# Strip semicolon.
			sub(";", "", v);
			if (symver == "") {
				printf("File %s: Unmatched bracket.\n",
				vfile) > stderr;
				errors++;
			}
			else if (versions[v] != 1) {
				printf("File %s: `%s' has unknown " \
				    "successor `%s'.\n",
				    vfile, symver, v) > stderr;
				errors++;
			}
			else
				successors[symver] = v;
			brackets--;
		}
		else if (/^}[ \t]*;$/) {
			if (symver == "") {
				printf("File %s: Unmatched bracket.\n",
				    vfile) > stderr;
				errors++;
			}
			# No successor
			brackets--;
		}
		else if (/^}$/) {
			printf("File %s: Missing final semicolon.\n",
			    vfile) > stderr;
			errors++;
		}
		else if (/^$/)
			;  # Ignore blank lines.
		else {
			printf("File %s: Unknown directive: `%s'.\n",
			    vfile, $0) > stderr;
			errors++;
		}
	}
	brackets = 0;
}

{
	# Set meaningful filename for diagnostics.
	filename = FILENAME != "" ? FILENAME : "<stdin>";

	# Delete comments, preceding and trailing whitespace, then
	# consume blank lines.
	sub("#.*$", "", $0);
	sub("^[ \t]+", "", $0);
	sub("[ \t]+$", "", $0);
	if ($0 == "")
		next;
}

/^[a-zA-Z0-9._]+[ \t]*{$/ {
	# Strip bracket from version name.
	sub("{", "", $1);
	if (current_version != "") {
		printf("File %s, line %d: Illegal nesting detected.\n",
		    filename, FNR) > stderr;
		errors++;
	}
	else if (versions[$1] == 0) {
		printf("File %s, line %d: Undefined " \
		    "library version `%s'.\n", filename, FNR, $1) > stderr;
		errors++;
		# Remove this entry from the versions.
		delete versions[$1];
	}
	else
		current_version = $1;
	brackets++;
	next;
}

/^[a-zA-Z0-9._]+[ \t]*;$/ {
	# Strip semicolon.
	sub(";", "", $1);
	if (current_version != "") {
		count = versions[current_version];
		versions[current_version]++;
		symbols[current_version, count] = $1;
		if ($1 in names && names[$1] != current_version) {
			#
			# A graver case when a dup symbol appears under
			# different versions in the map.  That can result
			# in subtle problems with the library later.
			#
			printf("File %s, line %d: Duplicated symbol `%s' " \
			    "in version `%s', first seen in `%s'. " \
			    "Did you forget to move it to ObsoleteVersions?\n",
			    filename, FNR, $1,
			    current_version, names[$1]) > stderr;
			errors++;
		}
		else if (names[$1] == current_version) {
			#
			# A harmless case: a dup symbol with the same version.
			#
			printf("File %s, line %d: warning: " \
			    "Duplicated symbol `%s' in version `%s'.\n",
			    filename, FNR, $1, current_version) > stderr;
			warns++;
		}
		else
			names[$1] = current_version;
	}
	else {
		printf("File %s, line %d: Symbol `%s' outside version scope.\n",
		    filename, FNR, $1) > stderr;
		errors++;
	}
	next;
}

/^}[ \t]*;$/ {
	brackets--;
	if (brackets < 0) {
		printf("File %s, line %d: Unmatched bracket.\n",
		    filename, FNR, $1) > stderr;
		errors++;
		brackets = 0;	# Reset
	}
	current_version = "";
	next;
}

{
	printf("File %s, line %d: Unknown directive: `%s'.\n",
	    filename, FNR, $0) > stderr;
	errors++;
}

function print_version(v)
{
	# This function is recursive, so return if this version
	# has already been printed.  Otherwise, if there is an
	# ancestral version, recursively print its symbols before
	# printing the symbols for this version.
	#
	if (generated[v] == 1)
		return;
	if (successors[v] != "")
		print_version(successors[v]);

	printf("%s {\n", v);

	# The version count is always one more that actual,
	# so the loop ranges from 1 to n-1.
	#
	for (i = 1; i < versions[v]; i++) {
		if (i == 1)
			printf("global:\n");
		printf("\t%s;\n", symbols[v, i]);
	}

	version_count--;
	if (version_count == 0) {
		printf("local:\n");
		printf("\t*;\n");
	}
	if (successors[v] == "")
		printf("};\n");
	else
		printf("} %s;\n", successors[v]);
	printf("\n");

	generated[v] = 1;
    }

END {
	if (errors) {
		printf("%d error(s) total.\n", errors) > stderr;
		exit(1);
	}
	# OK, no errors.
	for (v in versions) {
		print_version(v);
	}
}

#
# Copyright (C) 2006 Daniel M. Eischen.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
#

#
# Make a list of all the library versions listed in the master file.
#
#   versions[] - array indexed by version name, contains number
#                of symbols (+ 1) found for each version.
#   successors[] - array index by version name, contains successor
#                  version name.
#   symbols[][] - array index by [version name, symbol index], contains
#                 names of symbols defined for each version.
#   names[] - array index is symbol name and value is count,
#	      used to check for duplicate symbols and warn about them.
#
BEGIN {
	brackets = 0;
	errors = 0;
	version_count = 0;
	current_version = "";
	stderr = "/dev/stderr";
	while (getline < vfile) {
		# Strip comments.
		sub("#.*$", "", $0);

		# Strip leading and trailing whitespace.
		sub("^[ \t]+", "", $0);
		sub("[ \t]+$", "", $0);

		if (/^[a-zA-Z0-9._]+[ \t]*{$/) {
			# Strip brace.
			sub("{", "", $1);
			brackets++;
			symver = $1;
			versions[symver] = 1;
			successors[symver] = "";
			generated[symver] = 0;
			version_count++;
		}
		else if (/^}[ \t]*[a-zA-Z0-9._]+[ \t]*;$/) {
			v = $1 != "}" ? $1 : $2;
			# Strip brace.
			sub("}", "", v);
			# Strip semicolon.
			sub(";", "", v);
			if (symver == "") {
				printf("File %s: Unmatched bracket.\n",
				vfile) > stderr;
				errors++;
			}
			else if (versions[v] != 1) {
				printf("File %s: `%s' has unknown " \
				    "successor `%s'.\n",
				    vfile, symver, v) > stderr;
				errors++;
			}
			else
				successors[symver] = v;
			brackets--;
		}
		else if (/^}[ \t]*;$/) {
			if (symver == "") {
				printf("File %s: Unmatched bracket.\n",
				    vfile) > stderr;
				errors++;
			}
			# No successor
			brackets--;
		}
		else if (/^}$/) {
			printf("File %s: Missing final semicolon.\n",
			    vfile) > stderr;
			errors++;
		}
		else if (/^$/)
			;  # Ignore blank lines.
		else {
			printf("File %s: Unknown directive: `%s'.\n",
			    vfile, $0) > stderr;
			errors++;
		}
	}
	brackets = 0;
}

{
	# Set meaningful filename for diagnostics.
	filename = FILENAME != "" ? FILENAME : "<stdin>";

	# Delete comments, preceding and trailing whitespace, then
	# consume blank lines.
	sub("#.*$", "", $0);
	sub("^[ \t]+", "", $0);
	sub("[ \t]+$", "", $0);
	if ($0 == "")
		next;
}

/^[a-zA-Z0-9._]+[ \t]*{$/ {
	# Strip bracket from version name.
	sub("{", "", $1);
	if (current_version != "") {
		printf("File %s, line %d: Illegal nesting detected.\n",
		    filename, FNR) > stderr;
		errors++;
	}
	else if (versions[$1] == 0) {
		printf("File %s, line %d: Undefined " \
		    "library version `%s'.\n", filename, FNR, $1) > stderr;
		errors++;
		# Remove this entry from the versions.
		delete versions[$1];
	}
	else
		current_version = $1;
	brackets++;
	next;
}

/^[a-zA-Z0-9._]+[ \t]*;$/ {
	# Strip semicolon.
	sub(";", "", $1);
	if (current_version != "") {
		count = versions[current_version];
		versions[current_version]++;
		symbols[current_version, count] = $1;
		if (names[$1]++) {
			printf("File %s, line %d: Duplicated symbol `%s'. " \
			    "Did you forget to move it to ObsoleteVersions?\n",
			    filename, FNR, $1) > stderr;
			errors++;
		}
	}
	else {
		printf("File %s, line %d: Symbol `%s' outside version scope.\n",
		    filename, FNR, $1) > stderr;
		errors++;
	}
	next;
}

/^}[ \t]*;$/ {
	brackets--;
	if (brackets < 0) {
		printf("File %s, line %d: Unmatched bracket.\n",
		    filename, FNR, $1) > stderr;
		errors++;
		brackets = 0;	# Reset
	}
	current_version = "";
	next;
}

{
	printf("File %s, line %d: Unknown directive: `%s'.\n",
	    filename, FNR, $0) > stderr;
	errors++;
}

function print_version(v)
{
	# This function is recursive, so return if this version
	# has already been printed.  Otherwise, if there is an
	# ancestral version, recursively print its symbols before
	# printing the symbols for this version.
	#
	if (generated[v] == 1)
		return;
	if (successors[v] != "")
		print_version(successors[v]);

	printf("%s {\n", v);

	# The version count is always one more that actual,
	# so the loop ranges from 1 to n-1.
	#
	for (i = 1; i < versions[v]; i++) {
		if (i == 1)
			printf("global:\n");
		printf("\t%s;\n", symbols[v, i]);
	}

	version_count--;
	if (version_count == 0) {
		printf("local:\n");
		printf("\t*;\n");
	}
	if (successors[v] == "")
		printf("};\n");
	else
		printf("} %s;\n", successors[v]);
	printf("\n");

	generated[v] = 1;
    }

END {
	if (errors) {
		printf("%d errors total.\n", errors) > stderr;
		exit(1);
	}
	# OK, no errors.
	for (v in versions) {
		print_version(v);
	}
}


Comment fixes from Larry Rosenman" <ler@lerctr.org>.

# $Id: ouiparse.awk,v 1.2 2000/08/23 13:44:14 thomas Exp $
#
# ouiparse.awk
# Author: Lawrence E. Rosenman <ler@lerctr.org>
# Original Date: 30 July 2000 (in this form).
# This AWK script takes the IEEE's oui.txt file and creates insert
# statements to populate a SQL table with the following attributes:
# create table oui (
#        oui macaddr primary key,
#        manufacturer text);
# the table name is set by setting the AWK variable TABLE
#
# we translate the character apostrophe (') to double apostrophe ('') inside
# the company name to avoid SQL errors.
#

BEGIN {
	TABLE="macoui";
	printf "DELETE FROM %s;",TABLE;
	printf "BEGIN TRANSACTION;";
	nrec=0;
}

END {
#	if (nrec > 0)
	printf "COMMIT TRANSACTION;";
}

# match ONLY lines that begin with 2 hex numbers, -, and another hex number
/^[0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F]/ {
#	if (nrec >= 100) {
#		printf "COMMIT TRANSACTION;";
#		printf "BEGIN TRANSACTION;";
#		nrec=0;
#	} else {
#		nrec++;
#	}
	# Get the OUI
	OUI=$1;
	# Skip the (hex) tag to get to Company Name
	Company=$3;
	# make the OUI look like a macaddr
	gsub("-",":",OUI);
	OUI=OUI ":00:00:00"
	# Pick up the rest of the company name
	for (i=4;i<=NF;i++)
		Company=Company " " $i;
	# Modify any apostrophes (') to avoid grief below.
	gsub("'","''",Company);
	# Print out for the SQL table insert
	printf "INSERT INTO %s (addr, name) VALUES (trunc(macaddr \'%s\'),\'%s\');\n",
		TABLE,OUI,Company;
}

# $Id: ouiparse.awk,v 1.1 2000/08/23 06:02:23 thomas Exp $
#
# ouiparse.awk
# Author: Lawrence E. Rosenman <ler@lerctr.org>
# Original Date: 30 July 2000 (in this form).
# This AWK script takes the IEEE's oui.txt file and creates insert
# statements to populate a SQL table with the following attributes:
# create table oui (
#        oui macaddr primary key,
#        manufacturer text);
# the table name is set by setting the AWK variable TABLE
#
# we translate the character apostrophe (') to space inside the company name
# to avoid SQL errors.
#
# match ONLY lines that begin with 2 hex numbers, -, and another hex number

BEGIN {
	TABLE="macoui";
	printf "DELETE FROM %s;",TABLE;
	printf "BEGIN TRANSACTION;";
	nrec=0;
}

END {
#	if (nrec > 0)
	printf "COMMIT TRANSACTION;";
}

/^[0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F]/ {
#	if (nrec >= 100) {
#		printf "COMMIT TRANSACTION;";
#		printf "BEGIN TRANSACTION;";
#		nrec=0;
#	} else {
#		nrec++;
#	}
	# Get the OUI
	OUI=$1;
	# Skip the (hex) tag to get to Company Name
	Company=$3;
	# make the OUI look like a macaddr
	gsub("-",":",OUI);
	OUI=OUI ":00:00:00"
	# Pick up the rest of the company name
	for (i=4;i<=NF;i++)
		Company=Company " " $i;
	# Modify any apostrophes (') to avoid grief below.
	gsub("'","''",Company);
	# Print out for the 'C' structure in mac.c
	printf "INSERT INTO %s (addr, name) VALUES (trunc(macaddr \'%s\'),\'%s\');\n",
		TABLE,OUI,Company;
}


Minor cleanup of instructions

Minor cleanup of instructions

# Helper for converting links copied and pasted from a webpage
#
# Since there is no way to paste an embedded hyperlink into a text file,
# paste the link and the title on two consecutive lines, e.g.
#
# https://www.netflix.com/title/80988955
# Death in Paradise, S06E01, Erupting in Murder
#
# https://www.netflix.com/title/80174814
# Borderliner: Season 1: "Milla’s Future"
#
# https://www.netflix.com/title/80203122
# Luxury Travel Show: Season 1: "Chiang Mai & Eze"
#
# https://www.amazon.com/gp/video/detail/B07QBFM357/ref=stream_prime_hd_ep?autoplay=1&t=3128
# Silent Witness, S16E001, Change, Part One
#
# Paste them in the same order they are in the Netflix or Amazon "viewing activity" page
#
# INVOCATION:
#    awk -f generateLinkFrom-text.awk watched.txt

/ref=stream_prime/ {
    sub (/\/ref=stream.*/,"")
}

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

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

# https://www.amazon.com/gp/video/detail/B07XZ8G9WJ/ref=stream_prime_hd_ep?autoplay=1&t=0
/ref=stream_prime/ {
    sub (/\/ref=stream.*/,"")
}

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