Remove requirement for gawk in testsuite

switch is a GNU extension - not supported, for example, by the default
installation of awk on Ubuntu.

#########################################################################
#                                                                       #
#                                 OCaml                                 #
#                                                                       #
#         Damien Doligez, projet Gallium, INRIA Rocquencourt            #
#                                                                       #
#   Copyright 2013 Institut National de Recherche en Informatique et    #
#   en Automatique.  All rights reserved.  This file is distributed     #
#   under the terms of the Q Public License version 1.0.                #
#                                                                       #
#########################################################################

function check() {
    if (!in_test){
        printf("error at line %d: found test result without test start\n", NR);
        errored = 1;
    }
}

function clear() {
    curfile = "";
    in_test = 0;
}

function record_pass() {
    check();
    RESULTS[key] = "p";
    clear();
}

function record_skip() {
    check();
    RESULTS[key] = "s";
    clear();
}

# The output cares only if the test passes at least once so if a test passes,
# but then fails in a re-run triggered by a different test, ignore it.
function record_fail() {
    check();
    if (!(key in RESULTS)){
        RESULTS[key] = "f";
    }
    clear();
}

function record_unexp() {
    if (!(key in RESULTS)){
        RESULTS[key] = "e";
    }
    clear();
}

/Running tests from '[^']*'/ {
    if (in_test) record_unexp();
    match($0, /Running tests from '[^']*'/);
    curdir = substr($0, RSTART+20, RLENGTH - 21);
    key = curdir;
    DIRS[key] = key;
    curfile = "";
}

/ ... testing.* ... testing/ {
    printf("error at line %d: found two test results on the same line\n", NR);
    errored = 1;
}

/^ ... testing '[^']*'/ {
    if (in_test) record_unexp();
    match($0, /... testing '[^']*'/);
    curfile = substr($0, RSTART+13, RLENGTH-14);
    key = sprintf ("%s/%s", curdir, curfile);
    DIRS[key] = curdir;
    in_test = 1;
}

/^ ... testing with / {
    if (in_test) record_unexp();
    key = curdir;
    DIRS[key] = curdir;
    in_test = 1;
}

/=> passed/ {
    record_pass();
}

/=> skipped/ {
    record_skip();
}

/=> failed/ {
    record_fail();
}

/=> unexpected error/ {
    record_unexp();
}

/^re-ran / {
    if (in_test){
        printf("error at line %d: found re-ran inside a test\n", NR);
        errored = 1;
    }else{
        RERAN[substr($0, 8, length($0)-7)] += 1;
        ++ reran;
    }
}

# Not displaying "skipped" for the moment, as most of the skipped tests
# print nothing at all and are not counted.

END {
    if (errored){
        printf ("\n#### Some fatal error occurred during testing.\n\n");
        exit (3);
    }else{
        if (!retries){
            for (key in RESULTS){
                r = RESULTS[key];
                if (r == "p"){
                    ++ passed;
                }else if (r == "f"){
                    ++ failed;
                    fail[failidx++] = key;
                }else if (r == "e"){
                    ++ unexped;
                    unexp[unexpidx++] = key;
                }else if (r == "s"){
                    ++ skipped;
                    curdir = DIRS[key];
                    if (curdir in SKIPPED){
                        if (SKIPPED[curdir]){
                            SKIPPED[curdir] = 0;
                            skips[skipidx++] = curdir;
                        }
                    }else{
                        skips[skipidx++] = key;
                    }
                }
            }
            printf("\n");
            printf("Summary:\n");
            printf("  %3d test(s) passed\n", passed);
            printf("  %3d test(s) failed\n", failed);
            printf("  %3d unexpected error(s)\n", unexped);
            if (reran != 0){
                printf("  %3d test dir re-run(s)\n", reran);
            }
            if (failed != 0){
                printf("\nList of failed tests:\n");
                for (i=0; i < failed; i++) printf("    %s\n", fail[i]);
            }
            if (unexped != 0){
                printf("\nList of unexpected errors:\n");
                for (i=0; i < unexped; i++) printf("    %s\n", unexp[i]);
            }
            printf("\n");
            if (failed || unexped){
                printf("#### Some tests failed. Exiting with error status.\n\n");
                exit 4;
            }
        }else{
            for (key in RESULTS){
                if (RESULTS[key] == "f" || RESULTS[key] == "e"){
                    key = DIRS[key];
                    if (!(key in RERUNS)){
                        RERUNS[key] = 1;
                        if (RERAN[key] < max_retries){
                            printf("%s\n", key);
                        }
                    }
                }
            }
        }
    }
}

#########################################################################
#                                                                       #
#                                 OCaml                                 #
#                                                                       #
#         Damien Doligez, projet Gallium, INRIA Rocquencourt            #
#                                                                       #
#   Copyright 2013 Institut National de Recherche en Informatique et    #
#   en Automatique.  All rights reserved.  This file is distributed     #
#   under the terms of the Q Public License version 1.0.                #
#                                                                       #
#########################################################################

function check() {
    if (!in_test){
        printf("error at line %d: found test result without test start\n", NR);
        errored = 1;
    }
}

function clear() {
    curfile = "";
    in_test = 0;
}

function record_pass() {
    check();
    RESULTS[key] = "p";
    clear();
}

function record_skip() {
    check();
    RESULTS[key] = "s";
    clear();
}

# The output cares only if the test passes at least once so if a test passes,
# but then fails in a re-run triggered by a different test, ignore it.
function record_fail() {
    check();
    if (!(key in RESULTS)){
        RESULTS[key] = "f";
    }
    clear();
}

function record_unexp() {
    if (!(key in RESULTS)){
        RESULTS[key] = "e";
    }
    clear();
}

/Running tests from '[^']*'/ {
    if (in_test) record_unexp();
    match($0, /Running tests from '[^']*'/);
    curdir = substr($0, RSTART+20, RLENGTH - 21);
    key = curdir;
    DIRS[key] = key;
    curfile = "";
}

/ ... testing.* ... testing/ {
    printf("error at line %d: found two test results on the same line\n", NR);
    errored = 1;
}

/^ ... testing '[^']*'/ {
    if (in_test) record_unexp();
    match($0, /... testing '[^']*'/);
    curfile = substr($0, RSTART+13, RLENGTH-14);
    key = sprintf ("%s/%s", curdir, curfile);
    DIRS[key] = curdir;
    in_test = 1;
}

/^ ... testing with / {
    if (in_test) record_unexp();
    key = curdir;
    DIRS[key] = curdir;
    in_test = 1;
}

/=> passed/ {
    record_pass();
}

/=> skipped/ {
    record_skip();
}

/=> failed/ {
    record_fail();
}

/=> unexpected error/ {
    record_unexp();
}

/^re-ran / {
    if (in_test){
        printf("error at line %d: found re-ran inside a test\n", NR);
        errored = 1;
    }else{
        RERAN[substr($0, 8, length($0)-7)] += 1;
        ++ reran;
    }
}

# Not displaying "skipped" for the moment, as most of the skipped tests
# print nothing at all and are not counted.

END {
    if (errored){
        printf ("\n#### Some fatal error occurred during testing.\n\n");
        exit (3);
    }else{
        if (!retries){
            for (key in RESULTS){
                switch (RESULTS[key]) {
                case "p":
                    ++ passed;
                    break
                case "f":
                    ++ failed;
                    fail[failidx++] = key;
                    break
                case "e":
                    ++ unexped;
                    unexp[unexpidx++] = key;
                    break
                }
            }
            printf("\n");
            printf("Summary:\n");
            printf("  %3d test(s) passed\n", passed);
            printf("  %3d test(s) failed\n", failed);
            printf("  %3d unexpected error(s)\n", unexped);
            if (reran != 0){
                printf("  %3d test dir re-run(s)\n", reran);
            }
            if (failed != 0){
                printf("\nList of failed tests:\n");
                for (i=0; i < failed; i++) printf("    %s\n", fail[i]);
            }
            if (unexped != 0){
                printf("\nList of unexpected errors:\n");
                for (i=0; i < unexped; i++) printf("    %s\n", unexp[i]);
            }
            printf("\n");
            if (failed || unexped){
                printf("#### Some tests failed. Exiting with error status.\n\n");
                exit 4;
            }
        }else{
            for (key in RESULTS){
                if (RESULTS[key] == "f" || RESULTS[key] == "e"){
                    key = DIRS[key];
                    if (!(key in RERUNS)){
                        RERUNS[key] = 1;
                        if (RERAN[key] < max_retries){
                            printf("%s\n", key);
                        }
                    }
                }
            }
        }
    }
}


Filter successive - from labels

Filter successive - from labels

This is a workaround for a Doxygen bug.

#!/usr/bin/awk -f
#
# Adds github compatible labels to level 1 and 2 markdown headings
# in underline style.
#

#
# Get file name prefix for references.
#
# The prefix mimics the doxygen file naming scheme,
# e.g. `man/loadrec.1` becomse `man_1_loadrec`.
#
# Equivalently the prefix for `foo/bar.md` becomes the prefix `foo_md_bar_`.
# The trailing `_` exists as a separator to the internal reference label.
#
filename != FILENAME {
	filename = FILENAME
	# strip path
	"pwd -P" | getline path
	path = path "/"
	prefix = substr(filename, 1, length(path)) == path \
	         ? substr(filename, length(path) + 1) \
	         : filename
	# put the filename suffix behind the path but in front of
	# the file name
	sufx = prefix
	sub(/.*\./, "", sufx)               # get suffix
	sub(/\.[^.]*$/, "", prefix)         # strip suffix from filename
	sub(/[^\/]*$/, sufx "_&_", prefix)  # insert suffix
	# sanitise characters
	gsub(/[^_a-zA-Z0-9]+/, "_", prefix)
}

#
# Substitute github references with doxygen references.
#
/\(#[_a-z0-9-]*\)/ {
	gsub(/\[[^]]*\]\(#/, "&" prefix)
}

#
# If this line is underlining a heading, add a label to the previous
# line.
#
line && (/^===*$/ || /^---*$/) {
	id = tolower(line)
	gsub(/[^- \ta-z0-9]/, "", id)  # remove undesired characters
	gsub(/[ \t]/, "-", id)         # replace white space with -
	line = line " {#" prefix id "}"
}

#
# Doxygen up to at least 1.8.16 chokes on successive - in labels.
#
line ~ "#" prefix {
	cnt = split(line, a, "#" prefix)
	line = a[1]
	for (i = 2; i <= cnt; ++i) {
		while (a[i] ~ /^[-a-z0-9]*--[-a-z0-9]*/) {
			sub(/--+/, "-", a[i])
		}
		line = line "#" prefix a[i]
	}
}

#
# Print the previous line, after a label might have been added.
#
{print line}

#
# Remember this line when scanning the next one for underlining.
#
{line = $0}

#
# Print the last line.
#
END { print }

#!/usr/bin/awk -f
#
# Adds github compatible labels to level 1 and 2 markdown headings
# in underline style.
#

#
# Get file name prefix for references.
#
# The prefix mimics the doxygen file naming scheme,
# e.g. `man/loadrec.1` becomse `man_1_loadrec`.
#
# Equivalently the prefix for `foo/bar.md` becomes the prefix `foo_md_bar_`.
# The trailing `_` exists as a separator to the internal reference label.
#
filename != FILENAME {
	filename = FILENAME
	# strip path
	"pwd -P" | getline path
	path = path "/"
	prefix = substr(filename, 1, length(path)) == path \
	         ? substr(filename, length(path) + 1) \
	         : filename
	# put the filename suffix behind the path but in front of
	# the file name
	sufx = prefix
	sub(/.*\./, "", sufx)               # get suffix
	sub(/\.[^.]*$/, "", prefix)         # strip suffix from filename
	sub(/[^\/]*$/, sufx "_&_", prefix)  # insert suffix
	# sanitise characters
	gsub(/[^_a-zA-Z0-9]+/, "_", prefix)
}

#
# Substitute github references with doxygen references.
#
/\(#[_a-z0-9-]*\)/ {
	gsub(/\[[^]]*\]\(#/, "&" prefix)
}

#
# If this line is underlining a heading, add a label to the previous
# line.
#
line && (/^===*$/ || /^---*$/) {
	id = tolower(line)
	gsub(/[^- \ta-z0-9]/, "", id)  # remove undesired characters
	gsub(/[ \t]/, "-", id)         # replace white space with -
	line = line " {#" prefix id "}"
}

#
# Print the previous line, after a label might have been added.
#
{print line}

#
# Remember this line when scanning the next one for underlining.
#
{line = $0}

#
# Print the last line.
#
END { print }


reorder

reorder

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

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

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


simplify

simplify

BEGIN {
  print "%TCIDATA{Version=5.00.0.2606}";
  print "%TCIDATA{LaTeXparent=0,0,classes.tex}";
  print "                      ";
  print "";
  print "%%%%% BEGINNING OF DOCUMENT BODY %%%%%";
  print "% html: Beginning of file: `clean.html'";
  print "% DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"";
  print "%  This is a (PRE) block.  Make sure it's left aligned or your toc title will be off. ";
  print "";
  quoting = 0;
  itemizing = 0;
  coding = 0;
}

END {
  print "% html: End of file: `clean.html'";
  print "";
  print "%%%%% END OF DOCUMENT BODY %%%%%";
  print "% In the future, we might want to put some additional data here, such";
  print "% as when the documentation was converted from wiki to TeX.";
  print "%";
}

## HANDLE CODING
## make sure that control sequences are always at the front
/ *{{{/ { sub(/ */, "") }
/ *}}}/ { sub(/ */, "") }

!coding && /^{{{/ {
  print "\\begin{verbatim}";
  coding = 1;
  next;
}

coding && /^}}}/ {
  print "\\end{verbatim}";
  coding = 0;
  next;
}

coding {
  sub(/^/, "{\\small ");
  print;
  print "}";
  next;
}

## NO CODING BELOW THIS POINT
coding { print "###code???####"; next; }

## pragmas do not count
/\#labels/ {
  next;
}

## make sure that control sequences are always at the front
/ *=/ { sub(/ */, "") }

itemizing && !/^   *\*/ {
  print "\\end{itemize}";
  itemizing = 0;
}

quoting && /^  / {
  sub(/^  /, "");
  print;
  next;
}

quoting && !/^  / {
  print "\\end{quotation}";
  quoting = 0;
}

## empty lines do not count
/^ *$/ {
  next;
}

/`/ {
  line = $0;
  sub(/`/, "\\texttt{", line);
  sub(/`/, "}", line);
  $0 = line
}

/^==/ {
  sub(/^==/, "\\subsection*{");
  sub(/==/, "}");
  print;
  next;
}

/^=/ {
  sub(/^=/, "\\section*{");
  sub(/=/, "}");
  print;
  print "";
  print "\\label{f0}";
  next;
}

!itemizing && /^   *\*/ {
  print "\\begin{itemize}";
  itemizing = 1;
}

itemizing && /^   *\*/ {
  sub(/^   *\*/, "\\item");
  print;
  print "";
  next;
}

/^  / {
  print "\\begin{quotation}";
  quoting = 1;
  sub(/^  /, "");
  print;
  next;
}

{ print }

BEGIN {
  print "%TCIDATA{Version=5.00.0.2606}";
  print "%TCIDATA{LaTeXparent=0,0,classes.tex}";
  print "                      ";
  print "";
  print "%%%%% BEGINNING OF DOCUMENT BODY %%%%%";
  print "% html: Beginning of file: `clean.html'";
  print "% DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"";
  print "%  This is a (PRE) block.  Make sure it's left aligned or your toc title will be off. ";
  print "";
  quoting = 0;
  itemizing = 0;
  coding = 0;
}

END {
  print "% html: End of file: `clean.html'";
  print "";
  print "%%%%% END OF DOCUMENT BODY %%%%%";
  print "% In the future, we might want to put some additional data here, such";
  print "% as when the documentation was converted from wiki to TeX.";
  print "%";
}

## HANDLE CODING
## make sure that control sequences are always at the front
/ *{{{/ { sub(/ */, "") }
/ *}}}/ { sub(/ */, "") }

!coding && /^{{{/ {
  print "\\begin{verbatim}";
  coding = 1;
  next;
}

coding && /^}}}/ {
  print "\\end{verbatim}";
  coding = 0;
  next;
}

coding {
  sub(/^/, "{\\small ");
  print;
  print "}";
  next;
}

## NO CODING BELOW THIS POINT
coding { print "###code???####"; }

## pragmas do not count
!coding && /\#labels/ {
  next;
}

## make sure that control sequences are always at the front
/ *=/ { sub(/ */, "") }

itemizing && !/^   *\*/ {
  print "\\end{itemize}";
  itemizing = 0;
}

quoting && /^  / {
  sub(/^  /, "");
  print;
  next;
}

quoting && !/^  / {
  print "\\end{quotation}";
  quoting = 0;
}

## empty lines do not count
!coding && /^ *$/ {
  next;
}

/`/ {
  line = $0;
  sub(/`/, "\\texttt{", line);
  sub(/`/, "}", line);
  $0 = line
}

/^==/ {
  sub(/^==/, "\\subsection*{");
  sub(/==/, "}");
  print;
  next;
}

/^=/ {
  sub(/^=/, "\\section*{");
  sub(/=/, "}");
  print;
  print "";
  print "\\label{f0}";
  next;
}

!itemizing && /^   *\*/ {
  print "\\begin{itemize}";
  itemizing = 1;
}

itemizing && /^   *\*/ {
  sub(/^   *\*/, "\\item");
  print;
  print "";
  next;
}

!coding && /^  / {
  print "\\begin{quotation}";
  quoting = 1;
  sub(/^  /, "");
  print;
  next;
}

{ print }


Edit headers

Edit headers

git-svn-id: 293778eaa97c8c94097d610b1bd5133a8f478f36@17556 4f837ed2-42f5-46e7-a7a5-fa17313484d4

#!/usr/bin/awk -f
# input: publisher\tplugin\tcontract_year\tyear\tstatus

BEGIN {
  FS="\t"
  pn = 0
}

{
# add a loop to add line only if first 4 char or last 4 char of year are gt or eq to contract
if ((substr($4,0,4) >= $3) || ((length($4) == 9) && (substr($4,6,4) >= $3))) {
    nn = split($2,na,/\./)
    lp2 = na[nn]
  if (!(($1,lp2,$3,$4) in b)) {
    p[pn] = $1
    n[pn] = lp2
#    n[pn] = $2
    t[pn] = $3
    d[pn] = $4
    pn++
  }
  b[$1,lp2,$3,$4]++
  c[$1,lp2,$3,$4,$5]++
  x[$5]++
  tt++
}
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "crawling"
  s[5] = "testing"
  s[6] = "notReady"
  s[7] = "released"
  s[8] = "down"
  s[9] = "superseded"
  s[10] = "zapped"
  sn = 11

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "craw"
  sc[5] = "test"
  sc[6] = "notR"
  sc[7] = "rele"
  sc[8] = "down"
  sc[9] = "supe"
  sc[10] = "zapp"
  scn = 11

  printf "Publisher\tPlugin\tContr\tYear\tTotal"
  for (j = 0 ; j < scn ; j++) {
    printf "\t%s", sc[j]
  }
  printf "\n"

  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], d[i], b[p[i],n[i],t[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      printf "\t%d", c[p[i],n[i],t[i],d[i],s[j]]
    }
    printf "\n"
  }
    printf "Publisher\tPlugin\tContr\tYear\t%d", tt
    for (j = 0 ; j < sn ; j++) {
    	printf "\t%d", x[s[j]]
    }

  printf "\n"
}

#!/usr/bin/awk -f
# input: publisher\tplugin\tcontract_year\tyear\tstatus

BEGIN {
  FS="\t"
  pn = 0
}

{
# add a loop to add line only if first 4 char or last 4 char of year are gt or eq to contract
if ((substr($4,0,4) >= $3) || ((length($4) == 9) && (substr($4,6,4) >= $3))) {
    nn = split($2,na,/\./)
    lp2 = na[nn]
  if (!(($1,lp2,$3,$4) in b)) {
    p[pn] = $1
    n[pn] = lp2
#    n[pn] = $2
    t[pn] = $3
    d[pn] = $4
    pn++
  }
  b[$1,lp2,$3,$4]++
  c[$1,lp2,$3,$4,$5]++
  x[$5]++
  tt++
}
}

END {
  s[0] = "expected"
  s[1] = "exists"
  s[2] = "manifest"
  s[3] = "wanted"
  s[4] = "crawling"
  s[5] = "testing"
  s[6] = "notReady"
  s[7] = "released"
  s[8] = "down"
  s[9] = "superseded"
  s[10] = "zapped"
  sn = 11

  sc[0] = "expe"
  sc[1] = "exis"
  sc[2] = "mani"
  sc[3] = "want"
  sc[4] = "craw"
  sc[5] = "test"
  sc[6] = "notR"
  sc[7] = "rele"
  sc[8] = "down"
  sc[9] = "supe"
  sc[10] = "zapp"
  scn = 11

  printf "Publisher\tPlugin\tContr\tYear\tTotal"
  for (j = 0 ; j < scn ; j++) {
    printf "\t%s", sc[j]
  }
  printf "\n"

  for (i = 0 ; i < pn ; i++) {
    printf "%s\t%s\t%s\t%s\t%d", p[i], n[i], t[i], d[i], b[p[i],n[i],t[i],d[i]]
    for (j = 0 ; j < sn ; j++) {
      printf "\t%d", c[p[i],n[i],t[i],d[i],s[j]]
    }
    printf "\n"
  }
    printf "Publisher\tPlugin\tContract\tYear\t%d", tt
    for (j = 0 ; j < sn ; j++) {
    	printf "\t%d", x[s[j]]
    }

  printf "\n"
}


Add date to help sorting

Add date to help sorting

Shows with multiple seasons and no season numbers could appear in wrong order

# Print the processed "Season" lines from a WebScraper csv file saved in tsv format

# Field numbers
#    1 web-scraper-order  2 web-scraper-start-url  3 PgmURL            4 PgmURL-href       5 SsnURL
#    6 SsnURL-href        7 EpiURL                 8 EpiURL-href       9 Program_Title    10 Sn_Title
#   11 Sn_Years           12 Sn_Epis              13 Sn_Description   14 More_Description

BEGIN {
    FS="\t"
    print "Sortkey\tTitle\tYear(s)\tEpisodes\tDescription"
}

# Only print the preceding line if the current line has a different Program_Title
# or the preceding line had a non-blank SsnURL-href
NR > 1 {
    if (oldProgram_Title != $9 || oldSsnURL_href != "")
        print savedLine
}

# Save fields from the current line so they can be printed after checking the following line
{
    # Raw fields for comparison
    oldSsnURL_href = $6
    oldProgram_Title = $9

    # Fields to print from current line
    $6  == "" ? URL = $4 : URL = $6
    showTitle = $9
    $10 == "" ? seasonTitle = "(Only season)" : seasonTitle = $10
    Years = $11
    NumEpisodes = $12
    $14 == "" ? Description = $13 : Description = $14

    # Titles starting with "The" should not sort based on "The"
    if (match (showTitle, /^The /))
        showTitle = substr(showTitle, 5) ", The"

    # Extract sortkey from URL
    nflds = split (URL,fld,"_")
    if (URL ~ /_S[[:digit:]]*_[[:digit:]]*$/) {
        seasonNumber = substr(fld[nflds-1], 2)
        sortkey = sprintf ("S%02d", seasonNumber)
    } else {
        sortkey = sprintf ("S%05d", fld[nflds])
    }

    sub (/ Episodes?/,"",NumEpisodes)

    savedLine = sprintf \
        ("%s - %s %s\t=HYPERLINK(\"%s\";\"%s, %s, %s\"\)\t%s\t%s\t%s",\
         showTitle, Years, sortkey, URL, showTitle, sortkey, seasonTitle, Years,\
         NumEpisodes, Description)

    # Make sure line doesn't start with a single quote so it sorts correctly in Open Office
    sub (/^'/,"",savedLine)
}

END {
    print savedLine
}

# Print the processed "Season" lines from a WebScraper csv file saved in tsv format

# Field numbers
#    1 web-scraper-order  2 web-scraper-start-url  3 PgmURL            4 PgmURL-href       5 SsnURL
#    6 SsnURL-href        7 EpiURL                 8 EpiURL-href       9 Program_Title    10 Sn_Title
#   11 Sn_Years           12 Sn_Epis              13 Sn_Description   14 More_Description

BEGIN {
    FS="\t"
    print "Sortkey\tTitle\tYear(s)\tEpisodes\tDescription"
}

# Only print the preceding line if the current line has a different Program_Title
# or the preceding line had a non-blank SsnURL-href
NR > 1 {
    if (oldProgram_Title != $9 || oldSsnURL_href != "")
        print savedLine
}

# Save fields from the current line so they can be printed after checking the following line
{
    # Raw fields for comparison
    oldSsnURL_href = $6
    oldProgram_Title = $9

    # Fields to print from current line
    $6  == "" ? URL = $4 : URL = $6
    showTitle = $9
    $10 == "" ? seasonTitle = "(Only season)" : seasonTitle = $10
    Years = $11
    NumEpisodes = $12
    $14 == "" ? Description = $13 : Description = $14

    # Titles starting with "The" should not sort based on "The"
    if (match (showTitle, /^The /))
        showTitle = substr(showTitle, 5) ", The"

    # Extract sortkey from URL
    nflds = split (URL,fld,"_")
    if (URL ~ /_S[[:digit:]]*_[[:digit:]]*$/) {
        seasonNumber = substr(fld[nflds-1], 2)
        sortkey = sprintf ("S%02d", seasonNumber)
    } else {
        sortkey = sprintf ("S%05d", fld[nflds])
    }

    sub (/ Episodes?/,"",NumEpisodes)

    savedLine = sprintf \
        ("%s - %s\t=HYPERLINK(\"%s\";\"%s, %s, %s\"\)\t%s\t%s\t%s",\
         showTitle, sortkey, URL, showTitle, sortkey, seasonTitle, Years, NumEpisodes, Description)

    # Make sure line doesn't start with a single quote so it sorts correctly in Open Office
    sub (/^'/,"",savedLine)
}

END {
    print savedLine
}


Fix some typos in comments

Fix some typos in comments

#!/usr/bin/awk -f
#
# This script parses a configuration file and returns it as make syntax.
#

#
# Replaces something in a string, returns the new string.
#
# @param regexp
#	The regular expression to replace.
# @param replace
#	What to replace it with.
# @param haystack
#	The string to work on.
# @return
#	The processed string.
#
function substitute(regexp, replace, haystack) {
	sub(regexp, replace, haystack);
	return haystack;
}

##
# This selects the first string from haystack that matches regexp.
#
# @param regexp
#	The regular expression to match.
# @param haystack
#	The string to work on.
# @return
#	The string matching regexp.
#
function grep(regexp, haystack) {
	sub(regexp, "<<BEGIN>>&<<END>>", haystack);
	sub(".*<<BEGIN>>", "", haystack);
	sub("<<END>>.*", "", haystack);
	return haystack;
}

##
# This function checks whether a string contains useable data.
#
# @param haystack
#	The string to check.
# @return
#	1 if the string contains useable data.
#
function isset(haystack) {
	if (haystack ~ "[^[:space:]]")
		return 1;
}

##
# Resolves paths at the beginning of a location pattern as
# far as possible and prints the result. The string can
# contain several paths separated by '|' and '&'.
#
# @param path
#	The location pattern to resolve.
#
function resolvePath(path,
pattern) {
	# Find previous paths.
	# Take care of logical or connections.
	if (path ~ "\\|") {
		# Select all paths before this one.
		pattern = substitute("\\|[^\\|]*$", "", path);
		# Process the previous patterns.
		resolvePath(pattern);

		# Process the last one.
		path = grep("[^\\|]*$", path);
		printf("%s", " || ");
	}

	# Take care of logical and connections.
	if (path ~ "\\&") {
		# Select all paths before this one.
		pattern = substitute("\\&[^\\&]*$", "", path);
		# Process the previous patterns.
		resolvePath(pattern);

		# Process the last one.
		path = grep("[^\\&]*$", path);
		printf("%s", " && ");
	}

	# Trim the location pattern.
	sub("^[[:space:]]+", "", path);
	sub("[[:space:]]+$", "", path);

	# Check whether this is a pattern that should NOT be matched.
	if (path ~ "^!.*") {
		sub("^!", "", path);
		printf("%s", "!${.CURDIR:M");
	} else
		printf("%s", "${.CURDIR:M");

	# Paths that do not begin with '/' are never resolvable.
	if (path ~ "^[^/]") {
		printf("%s", path "}");
		return;
	}

	pattern = path;
	# Select the resolvable part of the given path.
	sub("[/]*[^/]*[?*].*$", "", path);
	# Remember the part that cannot be resolved.
	sub("^" path, "", pattern);

	# If there is a resolvable part resolve it.
	if (path) {
		# If path exists, resolve and print it, else print the
		# original string.
		print("printf `[ -d \"" path "\" ] && (cd \"" path "\";pwd -P) || printf \"" path "\"`") | "sh";
		close("sh");
	}

	# Append the unresolvable part.
	printf("%s", pattern "}");
}

##
# Prints the beginning of a new block.
#
# @param pattern
#	The location pattern for this block.
#
function beginBlock(pattern) {
	nesting[nesting_len++] = pattern;
	# Print the if.
	printf("%s", ".if ");

	# Print the if statement.
	resolvePath(pattern);

	# Append a newline.
	print("");
}

##
# Parses lines in quoted mode, dealing with things like ending quotes
# and single line quotes.
#
# @param line		The line to parse into a quoted string.
#
function parseQuoted(line) {
	# If the quote ends here, take care of it.
	if (line ~ "\"") {
		# Print the part that is still within the quote.
		print(substitute("\".*", "\"", line));

		# If present parse what is left of the line.
		sub("[^\"]*\"", "", line);
		if (isset(line))
			return parse(line);

		# Or end here, if there is nothing left to parse.
		return;
	}

	# Lines that are in a quoted block do not get parsed.
	print(line);
	return 1;
}

##
# Parses a line of buildflags.conf file.
#
# @param line		The line to parse.
# @param quoted		Set to true if the line is part of a
#			quoted string.
# @return		Whether the next line should be parsed in
#			quoted mode.
#
function parse(line, quoted,
block) {
	# Lines in a quoted block only get parsed for the end of the block.
	if (quoted)
		return parseQuoted(line);

	# Spaces at the beginning are never required.
	sub("^[[:space:]]+", "", line);

	# Deal with comments. Comments behind parsable data will end up
	# in the line in front of that data.
	if (line ~ "^[^\"]*#") {
		# Print the comment.
		print(grep("#.*", line));

		# Parse whatever was before the comment.
		sub("#.*", "", line);
		if (isset(line))
			parse(line);

		return;
	}

	# Deal with make native directives.
	if (line ~ "^\\.") {
		print(line);
		return;
	}

	# Deal with quotes.
	if (line ~ "\"") {
		# Check whether the assignment is properly formatted.
		if (line ~ "[^[:space:]{}]+=[[:space:]]*\"") {
			# Take whatever is in front of the assignment
			# and parse it.
			block = substitute("[^[:space:]{}]+=[[:space:]]*\".*", "", line);
			if (isset(block))
				parse(block);
		} else
			printf("%s", ".error ");

		# Print the variable assignment in front of the quoted string.
		block = grep("[^[:space:]{}]+=[[:space:]]*\"", line);
		printf("%s", block);

		# Parse the quoted string.
		block = substitute("[^\"]*\"", "", line);
		return parseQuoted(block);
	}

	# Close a block.
	if (line ~ "}") {
		# Parse whatever is before the end of the block.
		block = substitute("}.*", "", line);
		if (isset(block))
			parse(block);

		# End the block.
		print(".endif # " nesting[--nesting_len]);

		# Parse whatever is behind the end of the block.
		sub("[^}]*}", "", line);
		if (line ~ "[^[:space:]]")
			parse(line);

		return;
	}

	# Begin a new block.
	if (line ~ "{") {
		# Open the new block.
		beginBlock(substitute("{.*", "", line));

		# Parse whatever is inside.
		sub("[^{]*{", "", line);
		if (isset(line))
			parse(line);

		return;
	}

	# Deal with compact variable assignments.
	if (line ~ "^[^[:space:]=]+=[^[:space:]]+") {
		# Print the assignment.
		print(grep("^[^[:space:]=]+=[^[:space:]]+", line));

		# Parse what follows.
		block = substitute("^[^[:space:]=]+=[^[:space:]]+", "", line);
		if (isset(block))
			parse(block);

		return;
	}

	# Deal with long variable assignments.
	if (line ~ "^[^[:space:]]+=") {
		# Print the whole line.
		print(line);
		return;
	}

	# Deal with negated flags.
	if (line ~ "^![^[:space:]=]+") {
		# Remove the negation symbol.
		line = substitute("^!", "", line);
		# Print the undefined command.
		print(".undef " grep("^[^[:space:]=]+", line));

		# Parse what follows.
		block = substitute("^[^[:space:]=]+", "", line);
		if (isset(block))
			parse(block);

		return;
	}

	# Deal with flags.
	if (line ~ "^[^[:space:]=]+") {
		# Print the flag.
		printf("%-23s %s", grep("^[^[:space:]=]+", line) "=", "yes\n");

		# Parse what follows.
		block = substitute("^[^[:space:]=]+", "", line);
		if (isset(block))
			parse(block);

		return;
	}

	# Print empty lines.
	if (!isset(line))
		print("");
}

{
	# Parse the file by line.
	quoted = parse($0, quoted);
}

#!/usr/bin/awk -f
#
# This script parses a configuration file and returns it as make syntax.
#

#
# Replaces something in a string, returns the new string.
#
# @param regexp
#	The regular expression to replace.
# @param replace
#	What to replace it with.
# @param haystack
#	The string to work on.
# @return
#	The processed string.
#
function substitute(regexp, replace, haystack) {
	sub(regexp, replace, haystack);
	return haystack;
}

##
# This selects the first string from haystack that machthes regexp.
#
# @param regexp
#	The regular expression to match.
# @param haystack
#	The string to work on.
# @return
#	The string matching regexp.
#
function grep(regexp, haystack) {
	sub(regexp, "<<BEGIN>>&<<END>>", haystack);
	sub(".*<<BEGIN>>", "", haystack);
	sub("<<END>>.*", "", haystack);
	return haystack;
}

##
# This function checks whether a string contains useable data.
#
# @param haystack
#	The string to check.
# @return
#	1 if the string contains useable data.
#
function isset(haystack) {
	if (haystack ~ "[^[:space:]]")
		return 1;
}

##
# Resolves paths at the beginning of a location pattern as
# far as possible and prints the result. The string can
# contain several paths seperated by '|' and '&'.
#
# @param path
#	The location pattern to resolve.
#
function resolvePath(path,
pattern) {
	# Find previous paths.
	# Take care of logical or connections.
	if (path ~ "\\|") {
		# Select all paths before this one.
		pattern = substitute("\\|[^\\|]*$", "", path);
		# Process the previous patterns.
		resolvePath(pattern);

		# Process the last one.
		path = grep("[^\\|]*$", path);
		printf("%s", " || ");
	}

	# Take care of logical and connections.
	if (path ~ "\\&") {
		# Select all paths before this one.
		pattern = substitute("\\&[^\\&]*$", "", path);
		# Process the previous patterns.
		resolvePath(pattern);

		# Process the last one.
		path = grep("[^\\&]*$", path);
		printf("%s", " && ");
	}

	# Trim the location pattern.
	sub("^[[:space:]]+", "", path);
	sub("[[:space:]]+$", "", path);

	# Check whether this is a pattern that should NOT be matched.
	if (path ~ "^!.*") {
		sub("^!", "", path);
		printf("%s", "!${.CURDIR:M");
	} else
		printf("%s", "${.CURDIR:M");

	# Paths that do not begin with '/' are never resolveable.
	if (path ~ "^[^/]") {
		printf("%s", path "}");
		return;
	}

	pattern = path;
	# Select the resolvable part of the given path.
	sub("[/]*[^/]*[?*].*$", "", path);
	# Remember the part that cannot be resolved.
	sub("^" path, "", pattern);

	# If there is a resolveable part resolve it.
	if (path) {
		# If path exists, resolve and print it, else print the
		# original string.
		print("printf `[ -d \"" path "\" ] && (cd \"" path "\";pwd -P) || printf \"" path "\"`") | "sh";
		close("sh");
	}

	# Append the unresolvable part.
	printf("%s", pattern "}");
}

##
# Prints the beginning of a new block.
#
# @param pattern
#	The location pattern for this block.
#
function beginBlock(pattern) {
	nesting[nesting_len++] = pattern;
	# Print the if.
	printf("%s", ".if ");

	# Print the if statement.
	resolvePath(pattern);

	# Append a newline.
	print("");
}

##
# Parses lines in quoted mode, dealing with things like ending quotes
# and single line quotes.
#
# @param line		The line to parse into a quoted string.
#
function parseQuoted(line) {
	# If the quote ends here, take care of it.
	if (line ~ "\"") {
		# Print the part that is still within the quote.
		print(substitute("\".*", "\"", line));

		# If present parse what is left of the line.
		sub("[^\"]*\"", "", line);
		if (isset(line))
			return parse(line);

		# Or end here, if there is nothing left to parse.
		return;
	}

	# Lines that are in a quoted block do not get parsed.
	print(line);
	return 1;
}

##
# Parses a line of buildflags.conf file.
#
# @param line		The line to parse.
# @param quoted		Set to true if the line is part of a
#			quoted string.
# @return		Whether the next line should be parsed in
#			quoted mode.
#
function parse(line, quoted,
block) {
	# Lines in a quoted block only get parsed for the end of the block.
	if (quoted)
		return parseQuoted(line);

	# Spaces at the beginning are never required.
	sub("^[[:space:]]+", "", line);

	# Deal with comments. Comments behind parseable data will end up
	# in the line in front of that data.
	if (line ~ "^[^\"]*#") {
		# Print the comment.
		print(grep("#.*", line));

		# Parse whatever was before the comment.
		sub("#.*", "", line);
		if (isset(line))
			parse(line);

		return;
	}

	# Deal with make native directives.
	if (line ~ "^\\.") {
		print(line);
		return;
	}

	# Deal with quotes.
	if (line ~ "\"") {
		# Check whether the assignment is properly formated.
		if (line ~ "[^[:space:]{}]+=[[:space:]]*\"") {
			# Take whatever is in front of the assignment
			# and parse it.
			block = substitute("[^[:space:]{}]+=[[:space:]]*\".*", "", line);
			if (isset(block))
				parse(block);
		} else
			printf("%s", ".error ");

		# Print the variable assignment in front of the quoted string.
		block = grep("[^[:space:]{}]+=[[:space:]]*\"", line);
		printf("%s", block);

		# Parse the quoted string.
		block = substitute("[^\"]*\"", "", line);
		return parseQuoted(block);
	}

	# Close a block.
	if (line ~ "}") {
		# Parse whatever is before the end of the block.
		block = substitute("}.*", "", line);
		if (isset(block))
			parse(block);

		# End the block.
		print(".endif # " nesting[--nesting_len]);

		# Parse whatever is behind the end of the block.
		sub("[^}]*}", "", line);
		if (line ~ "[^[:space:]]")
			parse(line);

		return;
	}

	# Begin a new block.
	if (line ~ "{") {
		# Open the new block.
		beginBlock(substitute("{.*", "", line));

		# Pase whatever is inside.
		sub("[^{]*{", "", line);
		if (isset(line))
			parse(line);

		return;
	}

	# Deal with compact variable assignments.
	if (line ~ "^[^[:space:]=]+=[^[:space:]]+") {
		# Print the assignment.
		print(grep("^[^[:space:]=]+=[^[:space:]]+", line));

		# Parse what follows.
		block = substitute("^[^[:space:]=]+=[^[:space:]]+", "", line);
		if (isset(block))
			parse(block);

		return;
	}

	# Deal with long variable assignments.
	if (line ~ "^[^[:space:]]+=") {
		# Print the whole line.
		print(line);
		return;
	}

	# Deal with negated flags.
	if (line ~ "^![^[:space:]=]+") {
		# Remove the negation symbol.
		line = substitute("^!", "", line);
		# Print the undefine command.
		print(".undef " grep("^[^[:space:]=]+", line));

		# Parse what follows.
		block = substitute("^[^[:space:]=]+", "", line);
		if (isset(block))
			parse(block);

		return;
	}

	# Deal with flags.
	if (line ~ "^[^[:space:]=]+") {
		# Print the flag.
		printf("%-23s %s", grep("^[^[:space:]=]+", line) "=", "yes\n");

		# Parse what follows.
		block = substitute("^[^[:space:]=]+", "", line);
		if (isset(block))
			parse(block);

		return;
	}

	# Print empty lines.
	if (!isset(line))
		print("");
}

{
	# Parse the file by line.
	quoted = parse($0, quoted);
}


tests: handle the case when all 64 process capability bits are set

* tests/caps.awk: Handle the case when all bits of 64-bit capability
word are set.

BEGIN {
  fail = 0
  lines = 3
  cap = "(0|CAP_[A-Z_]+(\\|CAP_[A-Z_]+)*|CAP_[A-Z_]+(\\|CAP_[A-Z_]+){37}\\|0xffffffc0)"
  capget = "^capget\\(\\{_LINUX_CAPABILITY_VERSION_3, 0\\}, \\{" cap ", " cap ", " cap "\\}\\) = 0$"
}

NR == 1 {if (match($0, capget)) next}

NR == 2 && $0 == "capset({_LINUX_CAPABILITY_VERSION_3, 0}, {CAP_DAC_OVERRIDE|CAP_WAKE_ALARM, CAP_DAC_READ_SEARCH|CAP_BLOCK_SUSPEND, 0}) = -1 EPERM (Operation not permitted)" {next}

NR == lines && $0 == "+++ exited with 0 +++" {next}

{
  print "Line " NR " does not match."
  fail = 1
  exit 1
}

END {
  if (fail == 0 && NR != lines) {
    print "Expected " lines " lines, found " NR " line(s)."
    exit 1
  }
}

BEGIN {
  fail = 0
  lines = 3
  cap = "(0|CAP_[A-Z_]+(\\|CAP_[A-Z_]+)*)"
  capget = "^capget\\(\\{_LINUX_CAPABILITY_VERSION_3, 0\\}, \\{" cap ", " cap ", " cap "\\}\\) = 0$"
}

NR == 1 {if (match($0, capget)) next}

NR == 2 && $0 == "capset({_LINUX_CAPABILITY_VERSION_3, 0}, {CAP_DAC_OVERRIDE|CAP_WAKE_ALARM, CAP_DAC_READ_SEARCH|CAP_BLOCK_SUSPEND, 0}) = -1 EPERM (Operation not permitted)" {next}

NR == lines && $0 == "+++ exited with 0 +++" {next}

{
  print "Line " NR " does not match."
  fail = 1
  exit 1
}

END {
  if (fail == 0 && NR != lines) {
    print "Expected " lines " lines, found " NR " line(s)."
    exit 1
  }
}


INTEGRATION: CWS geordi2q12 (1.2.70); FILE MERGED 2004/01/06 14:18:33 hr 1.2.70.1: #111934#: merge CWS pp1numfor -> SRC680

#!/usr/bin/gawk -f
# Usage: gawk -f currency-check.awk *.xml
# Check any
# <FormatCode>...[$xxx-...]...</FormatCode>
# against every
# <CurrencySymbol>xxx</CurrencySymbol>
# definition of the same XML file and output symbols if no match was found.
# For formatindex="12" to formatindex="15" and for formatindex="17" it is
# checked if the used currency symbol is the usedInCompatibleFormatCodes
# currency symbol as it is needed by the number formatter.
# Also generates output if the generic currency symbol (UTF8 string 0xC2A4)
# is used instead of a real currency symbol.
# Author: Eike Rathke <er@openoffice.org>

BEGIN {
    file = ""
}

file != FILENAME {
    if ( file )
        checkIt()
    file = FILENAME
    line = 0
    nFormats = 0
    nSymbols = 0
    bSymbolDefault = 0
    bSymbolCompati = 0
    bFormatAuto = 0
    sReplaceFrom = ""
    sReplaceTo = ""
    sMatchReplace = ""
    sRefCurrencyFromLocale = ""
    crlf = 0
}

{
    ++line
    # If run under Unix a CrLf spoils ...$ line end checks. DOS line endings
    # are boo anyways.
    if ( /\x0D$/ )
    {
        print "Error: not Unix line ending in line " line
        crlf = 1
        exit(1)
    }
    if ( $1 ~ /^<LC_FORMAT$/ )
    {
        if ( $0 ~ /replaceFrom="\[CURRENCY\]"/ )
        {
            sReplaceFrom = "\\[CURRENCY\\]"
            sMatchReplace = "^<FormatCode>.*" sReplaceFrom
        }
        for ( j=2; j<=NF; ++j )
        {
            if ( $j ~ /^replaceTo="/ )
            {
                l = 12
                if ( $j ~ />$/ )
                    ++l
                if ( $j ~ /\/>$/ )
                    ++l
                sReplaceTo = substr( $j, 12, length($j)-l )
            }
        }
    }
    else if ( $1 ~ /^<FormatElement$/ )
    {
        if ( $0 ~ /usage="CURRENCY"/ )
        {
            if ( $0 ~ /formatindex="1[23457]"/ )
                bFormatAuto = 1
            else
                bFormatAuto = 0
        }
    }
    else if ( $0 ~ /^<FormatCode>.*\[\$.*-[0-9a-fA-F]+\]/ ||
            (sMatchReplace && $0 ~ sMatchReplace ) )
    {
        if ( sReplaceFrom )
            gsub( sReplaceFrom, sReplaceTo )
        split( $0, arr, /<|>/ )
        split( arr[3], code, /(\[\$)|(-[0-9a-fA-F]+\])/ )
        for ( j in code )
        {
            if ( code[j] && code[j] !~ /\#|0/ )
            {
                FormatLine[nFormats] = file " line " line
                FormatAuto[nFormats] = bFormatAuto
                Formats[nFormats++] = code[j]
            }
        }
        bFormatAuto = 0
    }
    else if ( $1 ~ /^<LC_CURRENCY$/ )
    {
        for ( j=2; j<=NF; ++j )
        {
            if ( $j ~ /^ref="/ )
            {
                l = 6
                if ( $j ~ />$/ )
                    ++l
                if ( $j ~ /\/>$/ )
                    ++l
                locale = substr( $j, 6, length($j)-l )
                sRefCurrencyFromLocale = file
                oldfile = file
                oldline = line
                file = locale ".xml"
                line = 0
                while ( (getline <file) > 0 )
                {
                    ++line
                    getCurrencyParams()
                }
                close( file )
                if ( !line )
                    print "ref locale not available: " file \
                        " (from " oldfile " line " oldline ")"
                file = oldfile
                line = oldline
                sRefCurrencyFromLocale = ""
            }
        }
    }
    else
        getCurrencyParams()
}

END {
    if ( file && !crlf )
        checkIt()
}

function getCurrencyParams() {
    if ( $1 ~ /^<Currency$/ )
    {
        if ( $0 ~ /default="true"/ )
            bSymbolDefault = 1
        else
            bSymbolDefault = 0
        if ( $0 ~ /usedInCompatibleFormatCodes="true"/ )
            bSymbolCompati = 1
        else
            bSymbolCompati = 0
    }
    else if ( $0 ~ /^<CurrencySymbol>/ )
    {
        split( $0, arr, /<|>/ )
        if ( sRefCurrencyFromLocale )
            SymbolLine[nSymbols] = file " line " line \
                " (referenced from " sRefCurrencyFromLocale ")"
        else
            SymbolLine[nSymbols] = file " line " line
        SymbolDefault[nSymbols] = bSymbolDefault
        SymbolCompati[nSymbols] = bSymbolCompati
        Symbols[nSymbols++] = arr[3]
        bSymbolDefault = 0
        bSymbolCompati = 0
    }
}

function checkIt() {
    bad = 0
    for ( j=0; j<nFormats; ++j )
    {
        state = FormatInSymbol( Formats[j] )
        if ( Formats[j] == "\xc2\xa4" )
        {
            bad = 1
            print "    bad: `" Formats[j] "'   (" FormatLine[j] ")"
        }
        else if ( state == 0 )
        {
            bad = 1
            print "unknown: `" Formats[j] "'   (" FormatLine[j] ")"
        }
        else if ( FormatAuto[j] && state < 2 )
        {
            bad = 1
            print "badauto: `" Formats[j] "'   (" FormatLine[j] ")"
        }
    }
    if ( bad )
    {
        for ( j=0; j<nSymbols; ++j )
        {
            bDef = 0
            if ( Symbols[j] == "\xc2\xa4" )
                print "def bad: `" Symbols[j] "'   (" SymbolLine[j] ")"
            if ( SymbolDefault[j] )
            {
                bDef = 1
                print "default: `" Symbols[j] "'   (" SymbolLine[j] ")"
            }
            if ( SymbolCompati[j] )
            {
                bDef = 1
                print "compati: `" Symbols[j] "'   (" SymbolLine[j] ")"
            }
            if ( !bDef )
                print "defined: `" Symbols[j] "'   (" SymbolLine[j] ")"
        }
    }
    else
    {
        bHasDefault = 0
        bHasCompati = 0
        for ( j=0; j<nSymbols; ++j )
        {
            if ( Symbols[j] == "\xc2\xa4" )
            {
                bad = 1
                print "def bad: `" Symbols[j] "'   (" SymbolLine[j] ")"
            }
            if ( SymbolDefault[j] )
            {
                if ( !bHasDefault )
                    bHasDefault = 1
                else
                {
                    bad = 1
                    print "dupe default: `" Symbols[j] "'   (" SymbolLine[j] ")"
                }
            }
            if ( SymbolCompati[j] )
            {
                if ( !bHasCompati )
                    bHasCompati = 1
                else
                {
                    bad = 1
                    print "dupe compati: `" Symbols[j] "'   (" SymbolLine[j] ")"
                }
            }
        }
        if ( !bHasDefault )
        {
            bad = 1
            print "  no default: (" file ")"
        }
        if ( !bHasCompati )
        {
            bad = 1
            print "  no compati: (" file ")"
        }
    }
    if ( bad )
        print ""
}

function FormatInSymbol( format ) {
    for ( nSym=0; nSym<nSymbols; ++nSym )
    {
        if ( format == Symbols[nSym] )
        {
            if ( SymbolCompati[nSym] )
                return 2
            else
                return 1
        }
    }
    return 0
}

# vim: ts=4 sw=4 expandtab

#!/usr/bin/gawk -f
# Usage: gawk -f currency-check.awk *.xml
# Check any
# <FormatCode>...[$xxx-...]...</FormatCode>
# against every
# <CurrencySymbol>xxx</CurrencySymbol>
# definition of the same XML file and output symbols if no match was found.
# For formatindex="12" to formatindex="15" and for formatindex="17" it is
# checked if the used currency symbol is the usedInCompatibleFormatCodes
# currency symbol as it is needed by the number formatter.
# Also generates output if the generic currency symbol (UTF8 string 0xC2A4)
# is used instead of a real currency symbol.
# Author: Eike Rathke <er@openoffice.org>

BEGIN {
    file = ""
}

file != FILENAME {
    if ( file )
        checkIt()
    file = FILENAME
    line = 0
    nFormats = 0
    nSymbols = 0
    bSymbolDefault = 0
    bSymbolCompati = 0
    bFormatAuto = 0
    sReplaceFrom = ""
    sReplaceTo = ""
    sMatchReplace = ""
    sRefCurrencyFromLocale = ""
}

{
    ++line
    if ( $1 ~ /^<LC_FORMAT$/ )
    {
        if ( $0 ~ /replaceFrom="\[CURRENCY\]"/ )
        {
            sReplaceFrom = "\\[CURRENCY\\]"
            sMatchReplace = "^<FormatCode>.*" sReplaceFrom
        }
        for ( j=2; j<=NF; ++j )
        {
            if ( $j ~ /^replaceTo="/ )
            {
                l = 12
                if ( $j ~ />$/ )
                    ++l
                if ( $j ~ /\/>$/ )
                    ++l
                sReplaceTo = substr( $j, 12, length($j)-l )
            }
        }
    }
    else if ( $1 ~ /^<FormatElement$/ )
    {
        if ( $0 ~ /usage="CURRENCY"/ )
        {
            if ( $0 ~ /formatindex="1[23457]"/ )
                bFormatAuto = 1
            else
                bFormatAuto = 0
        }
    }
    else if ( $0 ~ /^<FormatCode>.*\[\$.*-[0-9a-fA-F]+\]/ ||
            (sMatchReplace && $0 ~ sMatchReplace ) )
    {
        if ( sReplaceFrom )
            gsub( sReplaceFrom, sReplaceTo )
        split( $0, arr, /<|>/ )
        split( arr[3], code, /(\[\$)|(-[0-9a-fA-F]+\])/ )
        for ( j in code )
        {
            if ( code[j] && code[j] !~ /\#|0/ )
            {
                FormatLine[nFormats] = file " line " line
                FormatAuto[nFormats] = bFormatAuto
                Formats[nFormats++] = code[j]
            }
        }
        bFormatAuto = 0
    }
    else if ( $1 ~ /^<LC_CURRENCY$/ )
    {
        for ( j=2; j<=NF; ++j )
        {
            if ( $j ~ /^ref="/ )
            {
                l = 6
                if ( $j ~ />$/ )
                    ++l
                if ( $j ~ /\/>$/ )
                    ++l
                locale = substr( $j, 6, length($j)-l )
                sRefCurrencyFromLocale = file
                oldfile = file
                oldline = line
                file = locale ".xml"
                line = 0
                while ( (getline <file) > 0 )
                {
                    ++line
                    getCurrencyParams()
                }
                close( file )
                if ( !line )
                    print "ref locale not available: " file \
                        " (from " oldfile " line " oldline ")"
                file = oldfile
                line = oldline
                sRefCurrencyFromLocale = ""
            }
        }
    }
    else
        getCurrencyParams()
}

END {
    if ( file )
        checkIt()
}

function getCurrencyParams() {
    if ( $1 ~ /^<Currency$/ )
    {
        if ( $0 ~ /default="true"/ )
            bSymbolDefault = 1
        else
            bSymbolDefault = 0
        if ( $0 ~ /usedInCompatibleFormatCodes="true"/ )
            bSymbolCompati = 1
        else
            bSymbolCompati = 0
    }
    else if ( $0 ~ /^<CurrencySymbol>/ )
    {
        split( $0, arr, /<|>/ )
        if ( sRefCurrencyFromLocale )
            SymbolLine[nSymbols] = file " line " line \
                " (referenced from " sRefCurrencyFromLocale ")"
        else
            SymbolLine[nSymbols] = file " line " line
        SymbolDefault[nSymbols] = bSymbolDefault
        SymbolCompati[nSymbols] = bSymbolCompati
        Symbols[nSymbols++] = arr[3]
        bSymbolDefault = 0
        bSymbolCompati = 0
    }
}

function checkIt() {
    bad = 0
    for ( j=0; j<nFormats; ++j )
    {
        state = FormatInSymbol( Formats[j] )
        if ( Formats[j] == "\xc2\xa4" )
        {
            bad = 1
            print "    bad: `" Formats[j] "'   (" FormatLine[j] ")"
        }
        else if ( state == 0 )
        {
            bad = 1
            print "unknown: `" Formats[j] "'   (" FormatLine[j] ")"
        }
        else if ( FormatAuto[j] && state < 2 )
        {
            bad = 1
            print "badauto: `" Formats[j] "'   (" FormatLine[j] ")"
        }
    }
    if ( bad )
    {
        for ( j=0; j<nSymbols; ++j )
        {
            bDef = 0
            if ( Symbols[j] == "\xc2\xa4" )
                print "def bad: `" Symbols[j] "'   (" SymbolLine[j] ")"
            if ( SymbolDefault[j] )
            {
                bDef = 1
                print "default: `" Symbols[j] "'   (" SymbolLine[j] ")"
            }
            if ( SymbolCompati[j] )
            {
                bDef = 1
                print "compati: `" Symbols[j] "'   (" SymbolLine[j] ")"
            }
            if ( !bDef )
                print "defined: `" Symbols[j] "'   (" SymbolLine[j] ")"
        }
    }
    else
    {
        bHasDefault = 0
        bHasCompati = 0
        for ( j=0; j<nSymbols; ++j )
        {
            if ( Symbols[j] == "\xc2\xa4" )
            {
                bad = 1
                print "def bad: `" Symbols[j] "'   (" SymbolLine[j] ")"
            }
            if ( SymbolDefault[j] )
            {
                if ( !bHasDefault )
                    bHasDefault = 1
                else
                {
                    bad = 1
                    print "dupe default: `" Symbols[j] "'   (" SymbolLine[j] ")"
                }
            }
            if ( SymbolCompati[j] )
            {
                if ( !bHasCompati )
                    bHasCompati = 1
                else
                {
                    bad = 1
                    print "dupe compati: `" Symbols[j] "'   (" SymbolLine[j] ")"
                }
            }
        }
        if ( !bHasDefault )
        {
            bad = 1
            print "  no default: (" file ")"
        }
        if ( !bHasCompati )
        {
            bad = 1
            print "  no compati: (" file ")"
        }
    }
    if ( bad )
        print ""
}

function FormatInSymbol( format ) {
    for ( nSym=0; nSym<nSymbols; ++nSym )
    {
        if ( format == Symbols[nSym] )
        {
            if ( SymbolCompati[nSym] )
                return 2
            else
                return 1
        }
    }
    return 0
}

# vim: ts=4 sw=4 expandtab


Added Title to c1-4 output

Added Title to c1-4 output

# Print the processed "Episode" lines from a WebScraper csv file saved in tsv format

# INVOCATION:
#       awk -v EPISODE_INFO_FILE=$EPISODE_INFO_FILE -v ERROR_FILE=$ERROR_FILE \
#           -f getBBoxEpisodesFrom-webscraper.awk $EPISODES_SORTED_FILE >$EPISODES_SPREADSHEET_FILE

# Field numbers
#    1 web-scraper-start-url  2 SsnURL     3 SsnURL-href    4 URL        5 Program_Title
#    6 Episode_Title          7 Year       8 Duration       9 Rating    10 Description

BEGIN {
    FS="\t"
    OFS="\t"
    print "Sortkey\tTitle\tSeasons\tEpisodes\tDuration\tYear(s)\tRating\tDescription"
    # For debugging variations in seasons and episodes format
    str = "date +%y%m%d.%H%M%S"
    str | getline longdate
    close str
    for ( i = 1; i <= 4; i++ ) {
        outfile = "BBox-scrapes/c" i "-" longdate ".csv"
        print "Type\t#\tSortkey\tURL\tshowTitle\tepisodeTitle\tYear(s)" > outfile
    }
}

{
    for ( i = 1; i <= NF; i++ ) {
        if ($i == "null")
            $i = ""
    }
}

/\/us\/movie\/|\/us\/episode\// {
    baseURL = $1
    URL = $4
    showTitle = $5
    episodeTitle = $6
    Years = $7
    Duration = $8
    Rating = $9
    Description = $10
    showtype = "S"

    shortURL = URL
    sub (/^\/us\/episode\//,"",shortURL)
    episodeSeason = ""
    episodeNumber = ""
    # Season 3, Episode 8
    if (match (episodeTitle, /^Season [[:digit:]]+|^Series [[:digit:]]+/))
        episodeSeason = substr(episodeTitle,8,RLENGTH-7)

    # Extract sortkey from URL
    nflds = split (URL,fld,"_")
    if (URL ~ /_S[[:digit:]]*_E[[:digit:]]*_[[:digit:]]*$/) {
        # /us/episode/35_Days_S3_E8_23851
        seasonNumber = substr(fld[nflds-2], 2)
        episodeNumber = substr(fld[nflds-1], 2)
        if (baseURL ~ /The_Edinburgh_Show_23924$/) {
            episodeSeason = Years
        }
        # Give precedence to season number in episodeTitle over season number in URL
        if (episodeSeason != "" && seasonNumber+0 != episodeSeason+0) {
            revisedSeasons1 += 1
            printf ("==> Fixed c1 season: %02d-%02d\t%s\t%s\t%s\n", \
                    seasonNumber, episodeSeason, showTitle, shortURL, episodeTitle) >> ERROR_FILE
            seasonNumber = episodeSeason
        }
        sortkey = sprintf ("%s%02dE%03d", showtype, seasonNumber, episodeNumber)
        c1Num += 1
        outfile = "BBox-scrapes/c1-" longdate ".csv"
        printf ("c1\t%d\t%s\t%s\t%s\t%s\t%s\n",c1Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    } else if (URL ~ /_E[[:digit:]]*_[[:digit:]]*$/) {
        # /us/episode/35_Hours_E1_26129
        revisedSeasons2 += 1
        episodeSeason = Years
        if (episodeSeason != "" && seasonNumber+0 != episodeSeason+0) {
            printf ("==> Fixed c2 season: %02d-%02d\t%s\t%s\t%s\n", \
                    seasonNumber, episodeSeason, showTitle, shortURL, episodeTitle) >> ERROR_FILE
            seasonNumber = episodeSeason
        }
        episodeNumber = substr(fld[nflds-1], 2)
        sortkey = sprintf ("%s%02dE%03d", showtype, seasonNumber, episodeNumber)
        c2Num += 1
        outfile = "BBox-scrapes/c2-" longdate ".csv"
        printf ("c2\t%d\t%s\t%s\t%s\t%s\t%s\n",c2Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    } else if (URL ~ /Episode_[[:digit:]]*_[[:digit:]]*$/) {
        # /us/episode/Episode_5_26089
        revisedSeasons3 += 1
        seasonNumber = Years
        if (episodeSeason != "" && seasonNumber+0 != episodeSeason+0) {
            printf ("==> Fixed c3 season: %02d-%02d\t%s\t%s\t%s\n", \
                    seasonNumber, episodeSeason, showTitle, shortURL, episodeTitle) >> ERROR_FILE
            seasonNumber = episodeSeason
        }
        episodeNumber = substr(fld[nflds-1], 1)
        sortkey = sprintf ("%s%02dE%03d", showtype, seasonNumber, episodeNumber)
        c3Num += 1
        outfile = "BBox-scrapes/c3-" longdate ".csv"
        printf ("c3\t%d\t%s\t%s\t%s\t%s\t%s\n",c3Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    } else {
        revisedSeasons4 += 1
        URL ~ /^\/us\/movie\// ? showtype = "M" : showtype = "E"
        # /us/episode/Orkneys_Stone_Age_Temple_7144
        # A History of Ancient Britain Special, Orkney's Stone Age Temple
        sortkey = sprintf ("%s%05d", showtype, fld[nflds])
        if (episodeSeason != "") {
            showtype = "S"
            sortkey = sprintf ("%s%02dE%05d", showtype, episodeSeason, fld[nflds])
            printf ("==> Fixed c4 season: null-%02d\t%s\t%s\t%s\n", \
                    episodeSeason, showTitle, shortURL, episodeTitle) >> ERROR_FILE
        }
        c4Num += 1
        outfile = "BBox-scrapes/c4-" longdate ".csv"
        printf ("c4\t%d\t%s\t%s\t%s\t%s\t%s\n",c4Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    }

    # Convert duration from minutes to HMS
    sub( / min/,"",Duration)
    secs = 0
    mins = Duration % 60
    hrs = int(Duration / 60)
    HMS = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    if (HMS == "00:00:00")
        HMS = ""

    # Special processing for some shows is needed in all getBBox*From-webscraper.awk scripts.
    # "Changed" message and counting revisedTitles are only in getBBoxProgramsFrom-webscraper.awk
    # so they are not repeated for each episode and each season.
    if (baseURL ~ /Maigret_15974$/) {
        showTitle = "Maigret (2016)"
    }
    if (baseURL ~ /Maigret_\(1992\)_15928$/) {
        showTitle = "Maigret (1992)"
    }
    if (baseURL ~ /Porridge_9509$/) {
        showTitle = "Porridge (1974-1977)"
    }
    if (baseURL ~ /Porridge_14747$/) {
        showTitle = "Porridge (2016-2017)"
    }

    # Get rid of redundant "Series #" or "Series #" from episodeTitle
    if (match (episodeTitle, /^Season [[:digit:]]*, |^Series [[:digit:]]*, /))
        episodeTitle = substr(episodeTitle,RLENGTH+1)

    # Get rid of initial/trailing quotes in showTitle
    # "BBC Three's ""Murdered By..."" Collection Trailer"
    if (showTitle ~ /^".*"$/) {
        revisedTitles += 1
        printf ("==> Fixed quoted title: www.britbox.com%s\n", URL) >> ERROR_FILE
        # printf ("    %s\n", showTitle) >> ERROR_FILE
        sub (/^"/,"",showTitle)
        sub (/"$/,"",showTitle)
        # printf ("    %s\n", showTitle) >> ERROR_FILE
    }

    # Non-existent fields in movies or episodes
    if (URL ~ /^\/us\/movie\//) {
        depth = "1"
        numSeasons = 1
        numEpisodes = 1
        fullTitle = showTitle
    } else {
        depth = "2"
        numSeasons = ""
        numEpisodes = ""
        fullTitle = showTitle ", " sortkey ", " episodeTitle
    }

    # Fix two consecutive double quotes in showTitle, but leave in fullTitle
    gsub ("\"\"","\"",showTitle)

    # Fix HEX quotes in Description
    gsub ("\xc2\x91","\"",Description)
    gsub ("\xc2\x92","\"",Description)

    # Create spreadsheet row in common format
    savedLine = sprintf \
        ("%s (%s) %s %s\t=HYPERLINK(\"https://www.britbox.com%s\";\"%s\"\)\t%s\t%s\t%s\t%s\t%s\t%s",\
         showTitle, depth, Years, sortkey, URL, fullTitle, numSeasons, numEpisodes, \
         HMS, Years, Rating, Description)

    # Make sure line doesn't start with a single quote so it sorts correctly in Open Office
    sub (/^'/,"",savedLine)

    print savedLine
    next
}

/\/us\/show\// {
    # Shouldn't get here
    badEpisodes += 1
    URL = $4
    nflds = split (URL,URLflds,"/")
    printf ("==> Upstream error in %s\n", FILENAME)  >> ERROR_FILE
    print "==> Found /us/show/ in episodes: " URLflds[nflds]  >> ERROR_FILE
}

END {
    printf ("In getBBoxEpisodesFrom-webscraper.awk\n") > "/dev/stderr"
    if (revisedTitles > 0 ) {
        revisedTitles == 1 ? field = "title" : field = "titles"
        printf ("    %2d %s revised in %s\n", revisedTitles, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons1 > 0 ) {
        revisedSeasons1 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c1 %s revised in %s\n", revisedSeasons1, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons2 > 0 ) {
        revisedSeasons2 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c2 %s revised in %s\n", revisedSeasons2, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons3 > 0 ) {
        revisedSeasons3 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c3 %s revised in %s\n", revisedSeasons3, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons4 > 0 ) {
        revisedSeasons4 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c4 %s revised in %s\n", revisedSeasons4, field, FILENAME) > "/dev/stderr"
    }
    if (badEpisodes > 0 ) {
        badEpisodes == 1 ? field = "URL" : field = "URLs"
        printf ("    %2d extra /show/ %s in %s\n", badEpisodes, field, FILENAME) > "/dev/stderr"
    }
}

# Print the processed "Episode" lines from a WebScraper csv file saved in tsv format

# INVOCATION:
#       awk -v EPISODE_INFO_FILE=$EPISODE_INFO_FILE -v ERROR_FILE=$ERROR_FILE \
#           -f getBBoxEpisodesFrom-webscraper.awk $EPISODES_SORTED_FILE >$EPISODES_SPREADSHEET_FILE

# Field numbers
#    1 web-scraper-start-url  2 SsnURL     3 SsnURL-href    4 URL        5 Program_Title
#    6 Episode_Title          7 Year       8 Duration       9 Rating    10 Description

BEGIN {
    FS="\t"
    OFS="\t"
    print "Sortkey\tTitle\tSeasons\tEpisodes\tDuration\tYear(s)\tRating\tDescription"
    # For debugging variations in seasons and episodes format
    str = "date +%y%m%d.%H%M%S"
    str | getline longdate
    close str
    for ( i = 1; i <= 4; i++ ) {
        outfile = "BBox-scrapes/c" i "-" longdate ".csv"
        print "Type\t#\tSortkey\tURL\tshowTitle\tepisodeTitle\tYear(s)" > outfile
    }
}

{
    for ( i = 1; i <= NF; i++ ) {
        if ($i == "null")
            $i = ""
    }
}

/\/us\/movie\/|\/us\/episode\// {
    baseURL = $1
    URL = $4
    showTitle = $5
    episodeTitle = $6
    Years = $7
    Duration = $8
    Rating = $9
    Description = $10
    showtype = "S"

    shortURL = URL
    episodeSeason = ""
    # Season 3, Episode 8
    if (match (episodeTitle, /^Season [[:digit:]]+|^Series [[:digit:]]+/))
        episodeSeason = substr(episodeTitle,8,RLENGTH-7)

    # Extract sortkey from URL
    nflds = split (URL,fld,"_")
    if (URL ~ /_S[[:digit:]]*_E[[:digit:]]*_[[:digit:]]*$/) {
        # /us/episode/35_Days_S3_E8_23851
        seasonNumber = substr(fld[nflds-2], 2)
        episodeNumber = substr(fld[nflds-1], 2)
        if (baseURL ~ /The_Edinburgh_Show_23924$/) {
            episodeSeason = Years
        }
        # Give precedence to season number in episodeTitle over season number in URL
        if (episodeSeason != "" && seasonNumber+0 != episodeSeason+0) {
            revisedSeasons1 += 1
            sub (/^\/us\/episode\//,"",shortURL)
            printf ("==> Fixed c1 season: %02d-%02d\t%s\t%s\n", \
                    seasonNumber, episodeSeason, shortURL, episodeTitle) >> ERROR_FILE
            seasonNumber = episodeSeason
        }
        sortkey = sprintf ("%s%02dE%03d", showtype, seasonNumber, episodeNumber)
        c1Num += 1
        outfile = "BBox-scrapes/c1-" longdate ".csv"
        printf ("c1\t%d\t%s\t%s\t%s\t%s\t%s\n",c1Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    } else if (URL ~ /_E[[:digit:]]*_[[:digit:]]*$/) {
        # /us/episode/35_Hours_E1_26129
        revisedSeasons2 += 1
        episodeSeason = Years
        if (episodeSeason != "" && seasonNumber+0 != episodeSeason+0) {
            printf ("==> Fixed c2 season: %02d-%02d\t%s\t%s\n", \
                    seasonNumber, episodeSeason, shortURL, episodeTitle) >> ERROR_FILE
            seasonNumber = episodeSeason
        }
        episodeNumber = substr(fld[nflds-1], 2)
        sortkey = sprintf ("%s%02dE%03d", showtype, seasonNumber, episodeNumber)
        c2Num += 1
        outfile = "BBox-scrapes/c2-" longdate ".csv"
        printf ("c2\t%d\t%s\t%s\t%s\t%s\t%s\n",c2Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    } else if (URL ~ /Episode_[[:digit:]]*_[[:digit:]]*$/) {
        # /us/episode/Episode_5_26089
        revisedSeasons3 += 1
        seasonNumber = Years
        if (episodeSeason != "" && seasonNumber+0 != episodeSeason+0) {
            printf ("==> Fixed c3 season: %02d-%02d\t%s\t%s\n", \
                    seasonNumber, episodeSeason, shortURL, episodeTitle) >> ERROR_FILE
            seasonNumber = episodeSeason
        }
        episodeNumber = substr(fld[nflds-1], 1)
        sortkey = sprintf ("%s%02dE%03d", showtype, seasonNumber, episodeNumber)
        c3Num += 1
        outfile = "BBox-scrapes/c3-" longdate ".csv"
        printf ("c3\t%d\t%s\t%s\t%s\t%s\t%s\n",c3Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    } else {
        revisedSeasons4 += 1
        URL ~ /^\/us\/movie\// ? showtype = "M" : showtype = "E"
        # /us/episode/Orkneys_Stone_Age_Temple_7144
        # A History of Ancient Britain Special, Orkney's Stone Age Temple
        sortkey = sprintf ("%s%05d", showtype, fld[nflds])
        c4Num += 1
        outfile = "BBox-scrapes/c4-" longdate ".csv"
        printf ("c4\t%d\t%s\t%s\t%s\t%s\t%s\n",c4Num,sortkey,URL,showTitle,episodeTitle,Years) >> outfile
    }

    # Convert duration from minutes to HMS
    sub( / min/,"",Duration)
    secs = 0
    mins = Duration % 60
    hrs = int(Duration / 60)
    HMS = sprintf ("%02d:%02d:%02d", hrs, mins, secs)
    if (HMS == "00:00:00")
        HMS = ""

    # Special processing for some shows is needed in all getBBox*From-webscraper.awk scripts.
    # "Changed" message and counting revisedTitles are only in getBBoxProgramsFrom-webscraper.awk
    # so they are not repeated for each episode and each season.
    if (baseURL ~ /Maigret_15974$/) {
        showTitle = "Maigret (2016)"
    }
    if (baseURL ~ /Maigret_\(1992\)_15928$/) {
        showTitle = "Maigret (1992)"
    }
    if (baseURL ~ /Porridge_9509$/) {
        showTitle = "Porridge (1974-1977)"
    }
    if (baseURL ~ /Porridge_14747$/) {
        showTitle = "Porridge (2016-2017)"
    }

    # Get rid of redundant "Series #" or "Series #" from episodeTitle
    if (match (episodeTitle, /^Season [[:digit:]]*, |^Series [[:digit:]]*, /))
        episodeTitle = substr(episodeTitle,RLENGTH+1)

    # Get rid of initial/trailing quotes in showTitle
    # "BBC Three's ""Murdered By..."" Collection Trailer"
    if (showTitle ~ /^".*"$/) {
        revisedTitles += 1
        printf ("==> Fixed quoted title: www.britbox.com%s\n", URL) >> ERROR_FILE
        # printf ("    %s\n", showTitle) >> ERROR_FILE
        sub (/^"/,"",showTitle)
        sub (/"$/,"",showTitle)
        # printf ("    %s\n", showTitle) >> ERROR_FILE
    }

    # Non-existent fields in movies or episodes
    if (URL ~ /^\/us\/movie\//) {
        depth = "1"
        numSeasons = 1
        numEpisodes = 1
        fullTitle = showTitle
    } else {
        depth = "2"
        numSeasons = ""
        numEpisodes = ""
        fullTitle = showTitle ", " sortkey ", " episodeTitle
    }

    # Fix two consecutive double quotes in showTitle, but leave in fullTitle
    gsub ("\"\"","\"",showTitle)

    # Fix HEX quotes in Description
    gsub ("\xc2\x91","\"",Description)
    gsub ("\xc2\x92","\"",Description)

    # Create spreadsheet row in common format
    savedLine = sprintf \
        ("%s (%s) %s %s\t=HYPERLINK(\"https://www.britbox.com%s\";\"%s\"\)\t%s\t%s\t%s\t%s\t%s\t%s",\
         showTitle, depth, Years, sortkey, URL, fullTitle, numSeasons, numEpisodes, \
         HMS, Years, Rating, Description)

    # Make sure line doesn't start with a single quote so it sorts correctly in Open Office
    sub (/^'/,"",savedLine)

    print savedLine
    next
}

/\/us\/show\// {
    # Shouldn't get here
    badEpisodes += 1
    URL = $4
    nflds = split (URL,URLflds,"/")
    printf ("==> Upstream error in %s\n", FILENAME)  >> ERROR_FILE
    print "==> Found /us/show/ in episodes: " URLflds[nflds]  >> ERROR_FILE
}

END {
    printf ("In getBBoxEpisodesFrom-webscraper.awk\n") > "/dev/stderr"
    if (revisedTitles > 0 ) {
        revisedTitles == 1 ? field = "title" : field = "titles"
        printf ("    %2d %s revised in %s\n", revisedTitles, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons1 > 0 ) {
        revisedSeasons1 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c1 %s revised in %s\n", revisedSeasons1, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons2 > 0 ) {
        revisedSeasons2 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c2 %s revised in %s\n", revisedSeasons2, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons3 > 0 ) {
        revisedSeasons3 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c3 %s revised in %s\n", revisedSeasons3, field, FILENAME) > "/dev/stderr"
    }
    if (revisedSeasons4 > 0 ) {
        revisedSeasons4 == 1 ? field = "season" : field = "seasons"
        printf ("    %3d c4 %s revised in %s\n", revisedSeasons4, field, FILENAME) > "/dev/stderr"
    }
    if (badEpisodes > 0 ) {
        badEpisodes == 1 ? field = "URL" : field = "URLs"
        printf ("    %2d extra /show/ %s in %s\n", badEpisodes, field, FILENAME) > "/dev/stderr"
    }
}


ahrf.awk: Enable multi-space(s) after * for LIST_UL (validated)

#!/usr/bin/awk -f

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

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

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

# Paragraph
/^[A-Za-z0-9_]+/ {
	printf("
%s
\n\n", $0)
	next
}

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

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

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

#!/usr/bin/awk -f

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

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

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

# Paragraph
/^[A-Za-z0-9_]+/ {
	printf("
%s
\n\n", $0)
	next
}

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

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

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


ahrf.awk: Enable code blocks with more than 4 equals

#!/usr/bin/awk -f

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

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

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

# Paragraph
/^[A-Za-z0-9_]+/ {
	printf("
%s
\n\n", $0)
	next
}

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

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

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

#!/usr/bin/awk -f

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

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

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

# Paragraph
/^[A-Za-z0-9_]+/ {
	printf("
%s
\n\n", $0)
	next
}

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

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

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


Ensure check for episode 00 handles string episodeNumber

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

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

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

#  ---
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

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

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

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

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

#
#
#
#  OUTPUT:
#       $EPISODE_INFO_FILE
#

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

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

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

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

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

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

# Extract the episode URL
/> EPISODE_CURL_FILE
    next
}

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

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

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

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

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

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

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

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

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

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

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

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

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

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

#  ---
#
Season 1: Episode 1

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

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

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

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

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

#
#
#
#  OUTPUT:
#       $EPISODE_INFO_FILE
#

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

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

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

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

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

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

# Extract the episode URL
/> EPISODE_CURL_FILE
    next
}

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

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

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

#
Season 9 Christmas Special: Episode 1

#
#
Series 1: Episode 1

#
Series 1 (Set 1): Episode 1

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

#
Series 19 (Part One): Episode 1

#
#
Set 1: Episode 1

#
#
Mini Series: Episode 1

#
Miniseries: Episode 1

#
#
Black Widows Season 1: Episode 1

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

#
Christmas Special : Episode 1

#
Feature Film: Episode 1

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

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

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