#!/usr/bin/perl

use strict;
use warnings;

use Cwd;
use File::Basename;
use File::Glob;
use Getopt::Long;

my $version =   'v3.14';
my $signature = " (hs $version)";            # Added to (some of) the html files
my $prog =      fileparse($0, '\.pl');
my $rc =        'bcfix.ini';                 # Control file
my $nl =        "\n    ";
   $nl =        "";


# Standard values
# Change the following constants to tailor the script to your club:
my $defCharset =    '  <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">';
my $defResSeq =     'sap';


# Following values can be set in the configuration file, $rc
my $fSub =           0;                                 # No link for substitutes
my $submail =        '';
my $sub =            'Substitutter';
my $subtip =         'Send e-mail om substitutter';

my $fCorr =          0;                                 # No links for corrections
my $corrmail =       '';
my $corr =           'Korrektioner';
my $corrtip =        'Send e-mail med korrektioner';

my $fNext =          0;                                 # No link to next section
my $nextweek =       'Næste gang';
my $nextweektip =    'Startlisten næste gang';

my $fTable =         0;                                 # No table symbol in the diagrams

my $fTNumbers =      0;                                 # Retain table numbers (2Å and 3L)

my $fScoreSequence = 0;                                 # Show results in different sequences
my ($resseq, @resseq);
# End of values that can be set in the configuration file

# Values modifiable by options:
my $fCss =   1;        # Insert CSS commands in the html files
my $fDel =   1;        # Delete superfluous SpilRes files
my $fList =  0;        # 
my $fShow =  0;        # 
my $fTest =  0;        # 
my $fTime =  0;        # 
my $fIgnore = 0;       # Ignore missing files
# End of values modifiable by options:



# I would like to control the page breaks when the boards are printed,
# but I haven't found the way with current browsers.

sub usage();                  # print usage info and quit
sub version();                # print version info and quit
sub list();                   # list the start files and their status and quit
sub showtime($);              #
sub move($$);                 #
sub state($);                 # returns the state of a presumed start file
sub makeISOdate($$$$);        # 
sub makePairsResult($$);      # makes an html-section with a pairs' result table
sub makeTeamsTable($$$$);     # makes an html-section with a teams' score table
sub makeBoard($$$);           # 
sub getTournaments($);        # make a hash with tournament dates and corresponding section numbers
sub getNextSec($);            # determine the next section number
sub isOK($);                  # 
sub extendResultat($);        # add differently ordered result lists
sub extendSpilRes($);         # add differently orderer board results
sub extendKampresultat($);    # add team table order by scores
sub handle($);                # handle a single html-file
sub getconf();                # read the configuration file





sub usage() {
  print <<"USAGE";

Synopsis:
 $prog [options] [section ...]
  --help            show this help and quit
  --?               show this help and quit
  --version         show the version of $prog and quit
  --list            show the start files in the current directory and quit
  --[no]css         insert (or don't insert) the CSS modifications in the html-files (default css)
  --[no]delete      delete (or don't delete) the superfluous html files (default delete)
  --[no]show        show (or don't show) the names of the files handled (default noshow)
  --[no]table       show (or don't show) a table in the diagrams (default notable)
  --[no]time        show (or don't show) the time used (default notime)

 If no section is specified, all new (i.e. unhandled) sections are fixed.

 This script modifies the html-files produces by BridgeCentral for the specified sections
 in the current directory (the sections may be specified by their number (e.g. 48) or
 by the name of their start file (e.g. Startliste48.html)).
 It corrects a few formal errors, it ensures that the links from the personal result files
 refer to the actual boards in the SpilRes file rather than to the top,
 and it inserts (optionally) a number of mailto links for corrections.

 The script assumes the existence of a start list (Startliste#.html), and all files referred here
 or in any of the referred files (except those only referred in ../KlubTurn.html) are fixed.

                             Pairs   Teams
   ../KlubTurn.html            x       x
   Startliste#.html            x       x
   Fordeling#.html             x       x
   Resultat#.html              x       x
   PRegnskab_S#_P€.html        x
   SpilRes_S#_CG~.html         x
   Frekvens#.html              x
   Stilling#.html              x
   Slutstilling#.html          x
   KampResultat#.html                  x
   MultiStilling#.html                 x
   MultiBord§.html                     x
   MultiResultat§.html                 x

 where #  is the section number,
       €  is the pair number,
       ~  is the round number and
       §  is some sort of sequence number.

 Depending on the options it further deletes any file named SpilRes_S#_CG~.html unless it is
 referred from another file. This cleans up a strange behaviour of BridgeCentral.

 The script currently works with the template files from BridgeCentral 1.8.1, but the CSS file
 (standard.css) has to be modified as explained in the source (unless you use the --css option,
 which is the default).
USAGE
  exit 0;
} # usage()


sub version() {
  printf "%s %s\n", $prog, $version;
  exit 0;
} # version()


sub list() {
  foreach my $fn (glob "Startliste*.html") {
    print $fn, state($fn);
  }

  exit 0;
} # list()



sub showtime($) {
  my ($t) = @_;
  printf "\ttime used: %.3f secs\n", $t   if $fTime;
} # showtime()


# The script makes the following modifications to the html-files:
#  Cosmetics
#   All tags and attributes are converted to lower case
#   Table numbers like 3Å and 4L are (optionally) shown
#   as if the tables were numbered 1, 2, 3, ...
#
#  Errors corrected
#   The character set is defined
#   A missing </tr> is inserted
#   Missing <tr>...</tr> inserted around <td colspan="6" height="1" bgcolor="#ffffff"></td>
#   align="left" valign="top" align="right" is replaced by align="left" valign="top"
#   Missing </center>, </body> and </html> inserted
#   align="+center" is replaced by align="center"
#   &nbsp< is replaced by &nbsp;<
#   A score of 0 is shown as 0 (not as blank)
#
#  Deprecated features removed
#     <td height="1" bgcolor="#FFFFFF"></td>                 replaced by  <td class="horwhite"></td>
#     <td height="1" colspan="9" bgcolor="#FFFFFF"></td>     replaced by  <td class="horwhite" colspan="9"></td>
#     <td width="1" bgcolor="#FFFFFF"></td>                  replaced by  <td class="verwhite"></td>
#     width="(\d+)"                                generally replaced by  style="width:$1px"
#     bgcolor="                                    generally replaced by  style="background-color:
#     <i><font color="gray">...</font></i>                   replaced by  <span class="legs">...</span>
#     NOWRAP                                                 removed
#
#  Conveniences added
#   A link to next week's start list is included in the start list
#   A mail-to link for mails regarding substitutes is included in the start list page
#   A mail-to link for corrections is included in the result pages
#   The board links link directly to the actual board (Røhl's complaint) rather than to the first board
#   Insert a meta tag with the date in ISO-format to ease links to latest result
#   The result file for pairs is now presented in three different orderings rather than by total score.
#   The board results for pairs are now ordered by pair numbers (one list by NS, one by EW),
#
#  Miscellaneous
#   All references to a target are removed
#   A remark (Bemærkning) is centered [No, I'm looking for a non-deprecated way]
#   Superfluous output files removed


# Unless called with the --css option, the script assumes that the file standard.css
# contains the following definitions:
#    !!! To be supplied ... !!!
# and that the errors, a zillion missing px's, have been corrected.
# Note that the existing standard.css at the server is not overwritten when BridgeCentral
# uploads the files.

# Program sketch:
# For each arg
#   If the indicated start file is invalid
#     Give an error message (not exist, not text file, already processed)
#   else
#     Handle the files


my @handled;

my (%files, %fixed, %allfiles);
my ($reccorr, $recsub);
my ($substitutes, $corrections, $rescorr, $boardcorr, $date);
my $teams;                # Number of teams from the start list
                          # Used to convert table numbers like 2Å and 3L to pure numbers
                          # In HCØ we use 1-8 rather than 1Å-4Å and 1L-4L
my $t5;                   # Number of columns in the team result table


my $charset = "$defCharset\n";

my $nextweektag = '';
my $isHandled = 100000;    # must be sufficiently large ...

my $average = '';

my $t0 = (times)[0];


END {
  if (keys %allfiles > 1) {
    if ($fShow) {
      foreach (sort keys %allfiles) {
        next  if /^\w+:/;
        printf "%s occurs %d times: %s\n" , $_, ($allfiles{$_} % $isHandled), (exists $fixed{$_} ? "fixed" : "unchanged");
      }
    }

    if ($fDel) {
     # Delete superfluous files.
      foreach my $sec (@handled) {
        foreach (glob "SpilRes_S${sec}_CG*.html") {
          unlink  unless exists $allfiles{$_};
        }
      }
    }

    showtime((times)[0] - $t0);
  }
} # END


# The following kludge seems to remedy a rather strange timing problem,
# probably a slow virus scanner ...
sub move($$) {
  my ($outfile, $infile) = @_;
  unless (rename($outfile, $infile)) {
    sleep(1);
    unless (rename($outfile, $infile)) {
      sleep(1);
      rename($outfile, $infile)
        or warn "Could not rename $outfile to $infile: $!\n";
    }
  }
} # move()


# state(file) is used in connection with the --list option to tell the user about
# the state of a start file.
# It takes a quick look at the file and decides whether the file
#   does not exist,
#   is not a readable file,
#   can't be opened,
#   has already been fixed,
#   has not been fixed, or
#   is invalid.
# The decision is returned as a string.

sub state($) {
  my ($startfile) = @_;

  return " does not exist\n"          unless -e $startfile;
  return " is not a readable file\n"  unless -s _ && -f _ && -r _;
  return " can't be opened: $!\n"     unless open(IN, "<", $startfile);


  while (<IN>) {
    if (/<meta name="Dato"/ || /<!DOCTYPE html PUBLIC/) {
      close IN;
      return " has already been fixed\n";
    }
    if (/;Startliste/) {
      close(IN);
      return " has not been fixed\n";
    }
  }

  close(IN);
  return " is invalid\n";
} # state()


# Rather primitive ... and the suffix has nothing to do with ISO
my %MN = (januar => 1, februar => 2, marts     => 3, april   =>  4, maj      =>  5, juni     =>  6,
          juli   => 7, august  => 8, september => 9, oktober => 10, november => 11, december => 12,
         );
my %SF = (formiddag => 'a', eftermiddag => 'b', aften => 'c');

sub makeISOdate($$$$) {
  my ($d, $m, $y, $t) = @_;
  $t = 'aften'  unless defined $t;

  my $iso = sprintf("%04d-%02d-%02d%s", $y, $MN{$m}, $d, $SF{$t});
  return $iso;
} # makeISOdate()



my @resline;       # Used in
                   #  extendResultat() and extendSpilRes()
                   #   to collect the lines in the right sequence
                   #  makePairsResult() and makeBoard()
                   #   to output the lines

sub makePairsResult($$) {
  my ($rh, $heading) = @_;

  print OUT  <<"HEAD";
<table class="tbMain">
<tr>
  <th>$heading</th>
</tr>
<tr>
  <td class="horwhite"></td>
</tr>
<tr>
  <td align="center">
    <table class="tbFrekvens X1">
    <tr>
      <td rowspan="2" colspan="2">$average&nbsp;</td>
      <td align="center" colspan="3">&nbsp;&nbsp;&nbsp;Sektion</td>
      <td align="center" colspan="2">&nbsp;&nbsp;&nbsp;Turnering</td>
    </tr>
    <tr>
      <td align="right">&nbsp;&nbsp;Score</td>
      <td align="right">&nbsp;&nbsp;MP&nbsp;</td>
      <td align="center" valign="bottom">Plac.</td>
      <td align="right">&nbsp;&nbsp;&nbsp;Score</td>
      <td align="center">&nbsp;Plac.&nbsp;</td>
    </tr>
HEAD

  my $q = 0;
  foreach my $p (sort {$a <=> $b} keys %$rh) {
    printf OUT  $resline[$$rh{$p}], (++$q % 2 ? " class='alt'" : "");
  }

print OUT  <<"TAIL";
    </table>
  </td>
</tr>
</table>
<br>

TAIL
} # makePairsResult()


sub makeTeamsTable($$$$) {
  my ($rh, $all, $fNonIntSum, $round) = @_;

 # Insert new result table
  print OUT  <<"HEAD";
  <td class="horwhite"></td>
</tr>
<tr>
  <td class="horwhite"></td>
</tr>
<tr>
  <th>Stilling efter $round</th>
</tr>
<tr>
  <td class="horwhite"></td>
</tr>
<tr>
  <td align="center">
    <table class="tbFrekvens X1">
HEAD

  my @qq = sort {$a <=> $b} keys %$rh;

  print OUT  "    <tr align='center'>\n";
  print OUT  "      <td>Plac.</td>\n";
  print OUT  "      <td>&nbsp;</td>\n";
  for my $pn (0..$#qq) {
    print OUT  "      <td>&nbsp;&nbsp;$$all[$$rh{$qq[$pn]}-1][0]&nbsp;</td>\n";
  }
  print OUT  "      <td>&nbsp;&nbsp;Korr.&nbsp;</td>\n";
  if ($fNonIntSum) {
    print OUT  "      <td colspan='2'>&nbsp;&nbsp;&nbsp;Sum&nbsp;&nbsp;&nbsp;</td>\n";
  } else {
    print OUT  "      <td>&nbsp;&nbsp;&nbsp;Sum&nbsp;&nbsp;</td>\n";
  }
  print OUT  "      <td>&nbsp;&nbsp;Hold</td>\n";
  print OUT  "    </tr>\n";

  for my $pn (0..$#qq) {
    my $tn = $$rh{$qq[$pn]};
    printf OUT  "    <tr align='right' class='tdKryds%s'>\n", ($pn % 2? '' : ' alt');
    print  OUT  "      <td>$$all[$tn-1][0]&nbsp;&nbsp;</td>\n";
    print  OUT  "      <td align='left'>$$all[$tn-1][2]</td>\n";
    for my $t (0..$#qq) {
      my $val = $$all[$tn-1][4+$$rh{$qq[$t]}];
      if ($val =~ /@@/) {
        print OUT  "      <td><span class='na'>@@</span></td>\n"
      } else {
        print OUT  "      <td>&nbsp;$val</td>\n"
      }
    }
    print OUT  "      <td>$$all[$tn-1][3]&nbsp;&nbsp;</td>\n";
    my ($int, $rest) = $$all[$tn-1][4] =~ /^(-?\d+)(.*)$/;
    if ($fNonIntSum) {
      print OUT  "      <td class='norb'>$int</td>\n";
      print OUT  "      <td  class='nolb' align='left'>$rest</td>\n";
    } else {
      print OUT  "      <td>$int&nbsp;&nbsp;&nbsp;</td>\n";
    }
    print OUT  "      <td>$$all[$tn-1][1]&nbsp;&nbsp;</td>\n";
    print OUT  "    </tr>\n";
  }

  print OUT  <<"TAIL";
    </table>
  </td>
</tr>
</table>
<table class="tbMain">
<tr>
TAIL
} # makeTeamsTable()


sub makeBoard($$$) {
  my ($board, $rh, $order) = @_;
  my $NS = $order eq 'NS' ? '<span class="order">NS</span>' : 'NS';
  my $EW = $order eq 'EW' ? '<span class="order">ØV</span>' : 'ØV';
  my $class = defined $order ? $order : 'SC';

  print OUT  <<"HEAD";
<tr id="$class$board" class="$class">
  <td align="center">
    <table class="tbFrekvens X0">
    <tr>
      <td align="left" colspan="2" rowspan="2">$NS&nbsp;</td>
      <td align="left" colspan="2" rowspan="2">$EW&nbsp;</td>
      <td align="left" rowspan="2">Kontrakt&nbsp;</td>
      <td align="right" rowspan="2">Stik&nbsp;</td>
      <td align="center" colspan="2">Score</td>
      <td align="center" colspan="2">Point</td>
    </tr>
    <tr align="right">
      <td>&nbsp;&nbsp;NS&nbsp;&nbsp;</td>
      <td>&nbsp;&nbsp;ØV&nbsp;&nbsp;</td>
      <td>&nbsp;&nbsp;NS&nbsp;&nbsp;</td>
      <td>&nbsp;&nbsp;ØV&nbsp;&nbsp;</td>
    </tr>
HEAD

  my $q = 0;
  foreach my $p (sort {$a <=> $b} keys %$rh) {
    printf OUT  $resline[$$rh{$p}], (++$q % 2 ? " class='alt'" : "");
  }

print OUT  <<"TAIL";
    </table>
  </td>
</tr>
TAIL
} # makeBoard()



my %link;       # After initialization by getTournaments(), %link gives the link
                # to the tournament file for a specific date and heat.
                # The tournament file may be a start file or a result file.
                # Example:
                #   $link{'2009-02-17c'}{'*'}   Resultat73
                #   $link{'2009-03-24c'}{'*'}   Startliste78

sub getTournaments($) {
  my ($test) = @_;
  my $infile = '../KlubTurn.html';
  open(IN, '<', $infile)
    or die "Could not open file $infile: $!";

  my ($tour, $date, $heat, $sect, $link);
  my ($d, $m, $y, $t, $r);

  while (<IN>) {
    next  unless /tdTurn/;

    if (/>&nbsp;&nbsp;([^&]+)&nbsp;&nbsp;</i) {
      $date = $1;
      $heat = $1  if /rowspan="(\d+)"/i;
      if ($date =~ /(\d+)\. (\w+) (\d+)(, (\w+))?/i) {
        ($d, $m, $y, $t) = ($1, $2, $3, $5);
        $date = makeISOdate($d, $m, $y, $t);
      }
    } elsif (/rowspan="(\d+)"/i) {
      if (/>&nbsp;(.+):&nbsp;</) {
        $tour = $1;
      }
    } else {
      if (/>([^>]+) <em>\((.+)\)<\/em>/i) {
        ($heat, $sect) = ($1, $2);
        $heat =~ s/^&nbsp;//;
      } elsif (/>([^<]+)<\/a>/i) {
        ($heat, $sect) = ('*', $1);
      }
      if (/Turneringer\/([^"]+)\.html"/i) {
        $link = $1;
      } else {
        $link = 'no link';
      }
      $link{$date}{$heat} = $link;
    }
  }

  close(IN)
    or warn "$infile not closed: $!\n";

  if ($test) {
    print STDERR "\n\n";
    for my $d (sort keys %link) {
      for my $h (sort keys %{$link{$d}}) {
        printf STDERR  "%-15s %s => %s\n", $d, $h, $link{$d}{$h};
      }
    }
  }
} # getTournaments()


sub getNextSec($) {
  my ($sec) = @_;
  my ($dd, $hh);
  my $next;

 # Find the date and time of the current heat ($sec)
 SEARCH:
  for my $d (sort keys %link) {
    for my $h (sort keys %{$link{$d}}) {
     # printf "%-15s %s => %s\n", $d, $h, $link{$d}{$h};
      if ($link{$d}{$h} =~ /^[a-z]+$sec$/i) {
        ($dd, $hh) = ($d, $h);
        last SEARCH;
      }
    }
  }

  if (defined $dd && defined $hh) {
    for my $d (sort keys %link) {
      next  unless $d gt $dd;

      for my $h (sort keys %{$link{$d}}) {
       # printf "%-15s %s => %s\n", $d, $h, $link{$d}{$h};
        if ($h eq $hh or $h eq '*' or $hh eq '*') {
          $next = $link{$d}{$h};
          $next =~ tr/a-zA-Z//d;
          return $next;
        }
      }
    }
  }

 # Well, no info in the tournament table, so don't make a link
  return undef;
} # getNextSec()



# isOK(file) looks at the file and returns true (1) if the file is a valid start file that has
# not been fixed yet and false (0) otherwise. When it returns true, it has also defined the variables
# $substitutes and $corrections with the correct date, and when it returns false, it has printed
# an appropriate error message.

sub isOK($) {
  # Verify that the input files have not been modified;
  # obtain the date and format the links.
  my $startfile = shift;

  print STDERR  "*** isOK($startfile)\n"  if $fTest;
  ($substitutes, $corrections, $rescorr, $boardcorr, $date, $teams, $t5) = ();

  unless (-e $startfile)             {warn "$startfile does not exist\n";          return 0;}
  unless (-s _ && -f _ && -r _)      {warn "$startfile is not a readable file\n";  return 0;}
  unless (open(IN, "<", $startfile)) {warn "Could not open file $startfile: $!\n"; return 0;}

  while (<IN>) {
    if (/<meta name="Dato"/ || /<!DOCTYPE html PUBLIC/) {
      warn "$startfile has already been fixed\n";
      close IN;
      return 0;
    }

    next  unless /<TH>/;

   # Extract information on the current tournament from the source.
   # Usually the identifying line looks like:
   #  <TH>Parfinale, 25. marts 2009, 4. sektion, A-række (hvid)</TH>
   #      *********  **************  **********  **************
   #      tournament date            section     heat
   # But for a club that consists of several sub-clubs the sub-club name is also shown:
   #  <TH>BK HCØ - Toafteners vinterferieturnering, 10. februar 2009, 1. sektion</TH>
   #      ******   *******************************  ****************  **********
   #
   #  <TH>Tirsdagsklub - Parturnering 1-5 /  Rækkemester, 17. marts 2009, 3. sektion, A-rækken</TH>
   #
   # It is unfortunately possible to use strange character sequences in the various fields,
   # e.g. </TH>, further dates and commas, and this makes the parsing and extraction of
   # relevant information error prone. Consider this, admittedly strange, example:
   #  <TH>Sub-club - test med to rækker, starter 20. juni 2009, slutter senere, 27. juni 2009, 2. sektion, 1. række, hurtige</TH>
   #      ********   *********************************************************  *************  **********  *****************
   # Strange values may defeat the RE below. It assumes that the sequence /, \d\d?\. \w+ \d\d\d\d, \d+\. sektion/ is present.
   # (As I don't reaaly know when BridgeCentral uses sektion and when it uses runde, I match /\w+/.

    unless (defined $substitutes) {
      my ($club, $tour, $d, $m, $y, $part, $heat);

##      ($club, $tour, $d, $m, $y, $part, $heat) = /<TH>(.* - )?(.*), (\d{1,2}). (\w+) (\d{4}), (?=\d+\.)([^,]+)(, .+)?<\/TH>/;
      ($club, $tour, $d, $m, $y, $part, $heat) = /<TH>(.* - )?(.*), (\d{1,2}). (\w+) (\d{4}), (\d+\. \w+)(, .+)?<\/TH>/;
      $club = ''  unless defined $club;
      $heat = ''  unless defined $heat;
     # Note that $club is '' or something like 'BK HCØ - ',
     # and that $heat is '' or something like ', A-rækken'.

     $date = makeISOdate($d, $m, $y, undef);
      my $info = sprintf "%s%s, %.10s, %s%s", $club, $tour, $date, $part, $heat;
      $info =~ s/ /%20/g;

      $substitutes = "<a class='noPrint' href='$recsub?subject=Substitutter%20$info' title='$subtip'>$sub</a>";
      $corrections = "<a class='noPrint' href='$reccorr?subject=Korrektioner%20$info' title='$corrtip'>$corr</a>";
      $info =~ s/%/%%/g;

      $rescorr = "<a class='noPrint' href='$reccorr?subject=Korrektioner%%20$info,%%20bord%%20%s%s' title='$corrtip'>%s</a>";
      # Multihold resultater                                                                  table                  table
      #                                                                                         leg
      $boardcorr = "<a class='noLink' href='$reccorr?subject=Korrektioner%%20$info,%%20bord%%20%s,%%20spil%%20%s' title='$corrtip'>%s</a>";
      # Multihold bordresultater                                                               table          spil                 table

      next;
    }

    if (/Startliste/) {
     # Find the number of teams
      my @teams = grep /ROWSPAN="/, <IN>;
      close(IN);
      $teams = ($#teams+1) / 3;
      $t5 = $teams+5;
    }

    return 1;
  }

  warn "$startfile is not a valid start file\n";
  close(IN);
  return 0;
} # isOK()


sub handle($) {
  my $tf0 = (times)[0];

  my $infile = shift;
  my $seq = 0;   $seq = $1  if $infile =~ /(\d+)\.html$/;

  my $isPairsStart =  0;
  my $isPairsResult = 0;

 # Determine the section number from the file name
  my $sec = $infile;
  $sec =~ s/.+_S(\d+)_.*/$1/;
  $sec =~ s/.+\D(\d+)\.html$/$1/;

  my $outfile = $infile; chop $outfile;
  print STDERR "*** handle: $infile => $outfile\n"  if $fTest;
  
  if ($fIgnore && ! -x $infile) {
    print STDERR "File $infile not found -- ignored\n";
    return;
  }

  open(IN, '<', $infile)
    or die "Could not open file $infile: $!";
  open(OUT, '>', $outfile)
    or die "Could not create file $outfile: $!";

  my ($klubturn, $startliste, $fordeling,
      $resultat, $pregnskab, $spilres, $frekvens, $rundestilling, $slutstilling,
      $kampresultat, $multistilling, $multibord, $multiresultat);
  if    ($infile =~ /KlubTurn/)       {$klubturn =      1;}
  elsif ($infile =~ /^Startliste/)    {$startliste =    1;}
  elsif ($infile =~ /^Fordeling/)     {$fordeling =     1;}
  elsif ($infile =~ /^Resultat/)      {$resultat =      1;}
  elsif ($infile =~ /^PRegnskab/)     {$pregnskab =     1;}
  elsif ($infile =~ /^SpilRes/)       {$spilres =       1;}
  elsif ($infile =~ /^Frekvens/)      {$frekvens =      1;}
  elsif ($infile =~ /^Stilling/)      {$rundestilling = 1;}
  elsif ($infile =~ /^Slutstilling/)  {$slutstilling =  1;}
  elsif ($infile =~ /^KampResultat/)  {$kampresultat =  1;}
  elsif ($infile =~ /^MultiStilling/) {$multistilling = 1;}
  elsif ($infile =~ /^MultiBord/)     {$multibord =     1;}
  elsif ($infile =~ /^MultiResultat/) {$multiresultat = 1;}

  my $n =        0;
  my $bn =       0;
  my $rowspan4 = 0;
  my $extra =    '';
  my $tr =       0;
  my $fixtr =    0;
  my ($fix, $line0) = (0, 0);
  my $delColspan = 0;

  my $fInsertTable = $fTable && ($spilres || $fordeling || $frekvens || $rundestilling || $multibord);

  my ($line, $leg, $board, $noPrintNext) = (100000, '', 0, 0);

  my ($html, $body, $center);

  while (<IN>) {
   # Drop already modified files (this should only happen while testing and only for KlubTurn)
    if (/class="noPrint"/) {
      close(IN);
      close(OUT);
      unlink($outfile);
      print STDERR  "$infile has already been fixed\n";
      return;
    }

   # Something rather strange ...
   # A tool tip displays the score for each of the boards in the current round, but there are a few hiccups:
   #  - white space are indicated by LF CR rather than the canonical CR LF
   #  - a score of 0 (the result of a passed out board) is shown as blank
    if ($rundestilling) {
      while (/[^>]\n$/s) {
        my $first = $_; chomp($first);
        my $next = <IN>; chomp($next);
        $_ = $first . '&nbsp;' . $next;
        s/[\x0A\x0D]/&nbsp;/g;
        $_ .= "\n";
      }
      s/Spil\s+(\d+):\s+([^-\d\s])/Spil $1: 0$2/g;
      s/Spil\s+(\d+):\s+([-\d]+)/Spil $1: $2/g;
    }

   # Lower case all tags and attributes
    s/(<\/?\w+)/\L$1/ig;
    s/( [-\w]+=)/\L$1/ig;                     # assumes that / \w+=/ is an attribute!

   # Quote (some) attributes
    s/span=(\d+)/span="$1"/g;

   # Insert the date in a common format
    if ($startliste && /<head>/) {
      $extra = "  <meta name=\"Dato\" content=\"$date\">\n";
      next;
    }

   # Check for pairs
    $isPairsStart = 1   if $startliste && /Frekvenstavler/;
    $isPairsResult = 1  if $resultat   && /^  <th>Resultat<\/th>$/;

   # Sign
    next  if s/^  <td align="left" class="tdTimeStamp">BridgeCentral,/  <td align="left" class="tdTimeStamp">BridgeCentral$signature,/;

   # Define the character set
    next  if /^  <meta http-equiv="Content-Style-Type" content="text\/css">/ and $extra .= $charset;

   # Currently BridgeCentral "forgets" to close certain tags. I count the occurrences and supply the missing end tags.
   # It would be cheaper to count only center, as this is sufficient right now.
    $html   += s|<html|<html|g;
    $body   += s|<body|<body|g;
    $center += s|<center|<center|g;
    $center -= s|</center|</center|g;
    $body   -= s|</body|</body|g;
    $html   -= s|</html|</html|g;

   # Replace <center> and </center>
    next  if s/^<center>/<div class="main">/ or s/^<\/center>/<\/div>/;

   # Remember referenced files, but not from all tournaments!
    $files{$1}++  if !$klubturn && /<a href="([^"]+)"/;

   # Link to the specific board
    s[^(.+)<a href="SpilRes_S(\d+)_CG1\.html">(\d+)</a>(.*)]    [$1<a href=\"SpilRes_S$2_CG1.html#spil$3\">$3</a>$4]   if $pregnskab;

   # Remove superfluous info and decoration in prints.
   #  noPrint suppresses print
   #  noLink renders links black with no underlining in print.
       s[<table class="tbFrekvens">]  [<table class="noPrint tbFrekvens">]
    or s[href="PRegnskab]             [class="noLink" href="PRegnskab]
    or s[href="SpilRes]               [class="noLink" href="SpilRes]
    or s[href="]                      [class="noPrint" href="];

   # Insert CSS declarations in the individual files (and, tentatively, in KlubTurn.html)
    if ($fCss && /<\/head>/) {
     # Notes to the @media print section in the CSS below
     # Adding  .X4{page-break-before:always}  causes Firefox to print 
     # the Fordeling nicely, but it is a disaster for IE8. As it is now 
     # Firefox inserts a page break in the middle of the boards.

      print OUT <<'CSS';
  <style type="text/css">
    table{border-collapse:collapse}
    .main{text-align:center}
    .horwhite{color:red;background-color:white;height:3px}
    .verwhite{color:red;background-color:white;width:3px}
    .dim{color:#808080;background-color:inherit}
    .alt{color:black;background-color:white}
    .na{color:silver;background-color:silver}
    .legs{color:gray;background-color:inherit;font-style:italic}
    .tbMain{text-align:center;border:none}
    .tbMain td{padding:2px}
    .tbFrekvens{border:none}
    .tdKryds,.tdKryds td,.tdKryds2{border:1px solid silver}
    .tdKryds td.norb{border-right:none;padding-right:0}
    .tdKryds td.nolb{border-left:none;padding-left:0}
    .X0 td{padding:0 2px}
    .X4{}
    .X100{width:100%}
    @media print {
    body{font-size:6pt !important;color:black !important;background-color:white !important}
    a{text-decoration:none}
    .tbMain{width:100%}
/*    th,.tbMain,.tbFrekvens{font-size:6pt} */
/*    .tbFrekvens,.tbMain{color:black;background-color:white} */
/*    @page land{size:landscape} */
    .noPrint{display:none}
    .noLink{color:black;background-color:inherit;text-decoration:none}
    .tbBoard{page-break-before:always}
    .X4{page-break-inside:avoid;widows:15;orphans:15}
    .alt{color:blue;background-color:#F2F2F2}
    .horwhite,.verwhite{color:red;background-color:white;width:3px}
    }
  </style>
CSS
    }

   # Insert link to next week
    $extra .= $nextweektag  if $fNext && !$klubturn && /Startliste[&<]/ && !/<th>Startliste/;

   # Remove all references to a target
    s/ target="HovedRamme"//;

   # Avoid inline styles
    s/style="color:#808080;"/class="dim"/;

   # Add missing end tag in line containing <tr> immediately after line with ...rowspan="4"...
    s[^    <tr>$] [   </tr><tr>]  if $rowspan4;
    $rowspan4 = /rowspan="4"/;

   # Remove a superfluous <tr>
    next  if $tr && s/^<tr>$/<!-- tr -->/;
    $tr = /^<tr>$/;

   # Eliminate second align
    s/align="left" valign="top" align="right"/align="left" valign="top"/             if $startliste;

   # Fix the bad colspans in connection with sdjusted ("reguleret") score
    s[^     <td colspan="2">] [     <td colspan="$t5" align="left">]                 if $kampresultat;

   # Fix typos
    s/nbsp</nbsp;</                                                                  if $spilres || $pregnskab;
    s/align="\+center"/align="center"/                                               if $resultat;
    s/cellpadding=""/cellpadding="0"/                                                if $rundestilling;
    s/:  &#10;/: 0&#10;/g                                                            if $rundestilling;
    s/&#10;&#13;/&nbsp;&nbsp;/g                                                      if $rundestilling;

    s[^    <tr>$] [    </tr>]                                                        if $fixtr;
    $fixtr = m[&nbsp;\d+&nbsp;&nbsp;&nbsp;</td>]                                     if $rundestilling;

   # Change capitalization
    s/(align="[A-Z]+")/\L$1/                                                         if $spilres || $pregnskab;

   # A score of 0 is not shown as a zero
       s[(<td align="right"( bgcolor="#F7F7F7")?>)&nbsp;</td>]  [${1}0&nbsp;</td>]
    or s[>&nbsp;(\d+)-&nbsp;</td>]                              [>&nbsp;$1-0&nbsp;</td>]
    or s[>&nbsp;-(\d+)&nbsp;</td>]                              [>&nbsp;0-$1&nbsp;</td>]  if $resultat;

    s[^      <td>&nbsp;</td>] [      <td colspan="3">&nbsp;</td>]                    if $klubturn;

    if ($fSub) {
     # Replace self ref with a substitute link
      s/;Startliste&/;$substitutes&/                                                 if $startliste;
    }

    if ($fCorr) {
     # replace self ref with a correction link
      s/;Resultat&/;$corrections&/                                                   if $pregnskab;
      s/;Bordresultater&/;$corrections&/                                             if $multibord;
    }

   # Insert the missing link to the results
    s[&nbsp;Resultat&nbsp;] [&nbsp;<a href="Resultat$sec.html">Resultat</a>&nbsp;]   if -e "Resultat$sec.html" && ! $resultat;

   # Drop deprecated features
    s/ NOWRAP//                                                                      if $multiresultat;
    s[<b>([^<]+)</b>] [<strong>$1</strong>]                                          if $kampresultat;

   # Make an anchor at the specific board for teams
    if ($multibord) {
      if (/<td height="1" bgcolor="#FFFFFF"><\/td>/) {
        ++$n;
        s/></><a name="spil$n">&nbsp;<\/a></;    # The anchor doesn't work consistently if empty
      }
    }

   # Link to the specific board in MultiBord
    if ($multiresultat) {
      if (m[<td align="center" class="tdR">(\d+)</td>]) {
        $bn++;
        s[<td align="center" class="tdR">(\d+)</td>]
         [<td align="center" class="tdR"><a href="MultiBord$seq.html#spil$bn" title="Se spil $1">$1</a></td>];
      }
    }

   # Make an anchor at the specific board for pairs
    if ($spilres) {
      if (/border="0" cellspacing="0" cellpadding="2" class="tbMain/) {  # Note that this line is modified shortly
        if ($n > 0) {
          print OUT  "<a name=\"spil$n\">&nbsp;</a>\n";    # The anchor doesn't work consistently if empty
          s/tbMain/tbMain tbBoard rel/;
        }
        ++$n;
      }
     # Insert a correction link
      if ($fCorr && /<th>Spil (\d+)<\/th>/) {
        my $s = $1;
        my $lcorr = $corrections;
        $lcorr =~ s/' title/,%20spil%20$s\' title/;
        s[<th>Spil (\d+)</th>] [<th>Spil $1&nbsp;&nbsp;$lcorr</th>];
      }
    }

   # Use CSS ...
   # The X00 is used in SpilRes (where it is reasonable) and in PRegnskab (where it is not).
   # But X00 is only defined in SpilRes.
       s/^<table(?: border="0")? cellspacing="0" cellpadding="2" class="tbMain/<table class="tbMain/
    or s/<table border="0" cellspacing="0" cellpadding="0" class="tbFrekvens">/<table class="tbFrekvens X0">/
    or s/<table border="0" cellspacing="0" class="tbFrekvens">/<table class="tbFrekvens X00">/
    or s/<table border="0" cellspacing="0" cellpadding="0">/<table class="X0">/
    or s/<table border="0" cellspacing="0" cellpadding="4" class="tbFrekvens">/<table class="tbFrekvens X4">/
    or s/<table border="0" cellspacing="0" cellpadding="0" width="100%" class="tbFrekvens">/<table class="tbFrekvens X100">/
    or s/<table border="0" cellspacing="0" cellpadding="0" width="100%" class="tbFrekvens">/<table class="tbFrekvens X100">/
    ;

   # Don't use deprecated features ...
   # Note that one of the <td>'s occur without <tr>
   #   #1  first horizontal white band in Frekvens and Fordeling, all horizontal white bands in Multibord
   #   #2  other horizontal white bands in Fordeling (and fix the colspan value)
   #   #3  other horizontal white bands in Frekvens
   #   #4  vertical white band in Frekvens and Fordeling
       s[<td height="1" bgcolor="#FFFFFF">]                       [<td class="horwhite">]
    or s[<td height="1" colspan="9" bgcolor="#FFFFFF"></td>]      [<td class="horwhite" colspan="7"></td>]
    or s[<td colspan="(\d+)" height="1" bgcolor="#FFFFFF"></td>]  [<tr><td class="horwhite" colspan="$1"></td></tr>]
    or s[<td width="1" bgcolor="#FFFFFF"></td>]                   [<td class="verwhite"></td>]
    ;

   # Eliminate any remaining width=...
    s/width="(\d+)"/style="width:$1px"/;

   # Eliminate any remaining BGCOLORs
   # Every other line with names and/or scores (and an attempt to cater for print without background)
       s[<td class="tdKryds" bgcolor="silver">&nbsp;]             [<td class="tdKryds"><span class="na">@@</span>]
    or s[<td(.*) class="([^"]+)"(.*) bgcolor="#F7F7F7"(.*)>]      [<td class="$2 alt"$1$3$4>]
    or s[<td(.*) bgcolor="#F7F7F7"(.*) class="([^"]+)">]          [<td class="$3 alt"$1$2>]
    or s[<td(.*) bgcolor="#F7F7F7"]                               [<td$1 class="alt"];

   # Eliminate <font>
    s/<i><font color="gray">/<span class="legs">/ && s[</font></i>] [</span>];

   # Change table name to the corresponding number with a correction link in the multiresultat file
    if ($fCorr && $multiresultat) {
      $leg = $1    if m[^      <th>Multihold resultater efter \d+. runde(, \d. halvleg)&nbsp;&nbsp;&nbsp;</th>];
      if (m[^      <td [^>]+>\D+(\d+[ÅL])&nbsp;&nbsp;</td>]) {
        my $table = $1;
        my $t = $table;
        if ($fTNumbers) {
          $t =~ s/Å//;
          $t =~ s[(\d+)L] [$1+$teams/2]e;
        }
        my $replacement = sprintf "$rescorr", $t, $leg, $t;
        s[$table]  [$replacement];
      }
    }

   # Change table number to a correction link in the multibord file, and don't print leg selectors
    if ($multibord) {
      s/<td>/<td class="noPrint">/  if $noPrintNext-- > 0;
      $noPrintNext = 2  if /^   <th>Multihold bordresultater/;

     # Change table number to a correction link in the multibord file
      if ($fCorr) {
       # Note that the following match is against a modified line
        $board = $1   if m[^       <td align="left" style="width:45px" class="tdF">(\d+)</td>]
                      or m[^       <td align="center" class="tdR">(\d+)</td>];
        if (m[^       <td [^>]+>(\d+)&nbsp;&nbsp;&nbsp;</td>]) {
          my $table = $1;
          my $replacement = sprintf "$boardcorr", $table, $board, $table;
          s[>\d+] [>$replacement];
        }
      }
    }

   # Right-justify pair numbers in the rundestilling file
    s[<td align="left"(.*)>&nbsp;&nbsp;(\d+)</td>$] [<td align="right"$1>$2&nbsp;&nbsp;</td>]     if $rundestilling;

   # Right-justify the pair numbers in the spilres file
    s/>&nbsp;(\d+)&nbsp;</>$1&nbsp;</                                    && s/"left"/"right"/     if $spilres;

   # Right-justify the pair numbers in the start file for pairs
    s/>&nbsp;&nbsp;(\d+)</>$1&nbsp;</                                    && s/"left"/"right"/     if $isPairsStart;

   # Right-justify the pair numbers and the score per round
    s[>(-?\d+)</a>]   [>$1</a>&nbsp;&nbsp;]                              && s/"center"/"right"/   if $pregnskab;
    s[>(-?\d+)</td>]  [>$1&nbsp;&nbsp;&nbsp;</td>]                       && s/"center"/"right"/   if $pregnskab;
    s/Runderesultat&nbsp;/&nbsp;Runde/                                                            if $pregnskab;

   # Right-justify the pair numbers and rank in the resultat file
    s[>&nbsp;&nbsp;(\d+)<]   [>$1<]                                      && s/"left"/"right"/     if $resultat;
                                                         s/"center"([^>]*)>&nbsp;/"right"$1>/     if $resultat;


   # Insert a NESW in the diagrams
    if ($fInsertTable) {
      if (s[<td style="width:\d+px" class="tdF"></td>] [<td rowspan="4" class="tdF"><br>&nbsp;<img src="table.gif" alt="">&nbsp;</td>]) {
        $delColspan = 3;
      } else {
        if (/ colspan="2"/ && $delColspan > 0) {
          s/ colspan="2"//;  --$delColspan;
        }
      }
    }

  } continue {
   # Print the (modified) line
    print OUT;

   # Print a possible extra line
    if ($extra ne "") {
      print OUT  $extra;
      $extra = "";
    }
  } # while(<IN>)

 # Supply the missing closing tags
  print OUT "</div>\n"   if $center;
  print OUT "</body>\n"  if $body;
  print OUT "</html>\n"  if $html;

  if ($html > 1 || $body > 1 || $center > 1) {
    print STDERR  "Invalid $infile: $html/$body/$center\n";
  }


  close(IN)
    or warn "$infile not closed: $!\n";
  close(OUT)
    or warn "$outfile not closed: $!\n";

  showtime((times)[0] - $tf0)   if $fTest;
  move($outfile, $infile);
  ++$fixed{$infile};

 # Modify the result file
  extendResultat($infile)        if $fScoreSequence && $isPairsResult;

 # Modify the spilres file to show the individual board results ordered
 # by pair numbers ... (undocumented feature)
  extendSpilRes($infile)         if $fScoreSequence && $spilres;

 # Modify the kampresultat file
  extendKampresultat($infile)    if $fScoreSequence && $kampresultat;
} # handle()


sub extendResultat($) {
 # Modify the result file to display one or more of the three result lists:
 #   ordered by the section scores
 #   ordered by the accumulated scores
 #   ordered by pair numbers

  my ($infile) = @_;
  my $tf0 = (times)[0];

  my $nLine = 0;
  my ($pn, $sc, $plac, $mp, $tsc, $tplac, $pair);
  my (%bynr, %byplac, %bytplac);

 # Collect the score lines ...
  open(IN, '<', $infile)   or return;

##################################################################################################
#<TR>
#  <TD ALIGN="center">
#    <TABLE CELLSPACING="0" CELLPADDING="1" CLASS="tbFrekvens">
#    <TR>
#      <TD ALIGN="left" ROWSPAN="2" COLSPAN="2"><I>(Middelscore: 165)</I></TD>
#      <TD ALIGN="center" COLSPAN="3">&nbsp;&nbsp;&nbsp;Sektion</TD>
#      <TD ALIGN="center" COLSPAN="2">&nbsp;&nbsp;&nbsp;Turnering</TD>
#    </TR>
#    <TR>
#      <TD ALIGN="right">&nbsp;&nbsp;Score</TD>                                 ##  /Score<\/TD>/
#      <TD ALIGN="right">&nbsp;&nbsp;MP&nbsp;</TD>
#
#      <TD ALIGN="center" VALIGN="bottom">Plac.</TD>
#      <TD ALIGN="right">&nbsp;&nbsp;&nbsp;Score</TD>
#      <TD ALIGN="center">&nbsp;Plac.&nbsp;</TD>
#    </TR>
#    <TR>
#     <TD ALIGN="left" BGCOLOR="#F7F7F7">&nbsp;&nbsp;2</TD>
#     <TD ALIGN="left" BGCOLOR="#F7F7F7">&nbsp;<A HREF="PRegnskab_S72_P2.html">Jens Brix Christiansen - Peter Krogh</A>&nbsp;</TD>
#     <TD ALIGN="right" BGCOLOR="#F7F7F7">175&nbsp;</TD>
#     <TD ALIGN="right" BGCOLOR="#F7F7F7">22&nbsp;&nbsp;</TD>
#     <TD ALIGN="center" BGCOLOR="#F7F7F7">&nbsp;1&nbsp;</TD>
#     <TD ALIGN="right" BGCOLOR="#F7F7F7">175&nbsp;</TD>
#     <TD ALIGN="center" BGCOLOR="#F7F7F7">&nbsp;1&nbsp;</TD>
#   </TR>
##################################################################################################

  $average = '';
  while (<IN>) {
    if (/(\(Middelscore: \d+\))/) {
      $average = "<i>$1</i>";
      next;
    }
    if (m[Score</td>]) {
      $nLine = $. + 7   unless $nLine;
      next;
    }
    if ($nLine && $nLine <= $. && $. <= $nLine + 7) {
     # get info
      if    ($. == $nLine && m[^\s*</td>$]) {last; $nLine = 1000000;}
      elsif ($. == $nLine + 0) {         $pn = $1     if m[>(\d+)<];}
      elsif ($. == $nLine + 1) {         $pair = $1   if m[[^>]+>(.+)</td>.*];}
      elsif ($. == $nLine + 2) {         $sc = $1     if m[>([-\d]+).*];}
      elsif ($. == $nLine + 3) {$mp = 0; $mp = $1     if m[>(\d+).*];}
      elsif ($. == $nLine + 4) {         $plac = $1   if m[>(\d+).*];}
      elsif ($. == $nLine + 5) {         $tsc = $1    if m[>([-\d]+).*];}
      elsif ($. == $nLine + 6) {         $tplac = $1  if m[>(\d+).*];
        $nLine = $. + 3;
        my $m = $mp ? $mp : ' ';
        $resline[$pn] = "    <tr align='right'%s>$nl"
                            . "<td>$pn</td>$nl"
                            . "<td align='left'>$pair</td>$nl"
                            . "<td>$sc&nbsp;&nbsp;&nbsp;</td>$nl"
                            . "<td>$m&nbsp;&nbsp;</td>$nl"
                            . "<td>&nbsp;$plac&nbsp;&nbsp;&nbsp;</td>$nl"
                            . "<td>$tsc&nbsp;&nbsp;&nbsp;</td>$nl"
                            . "<td>$tplac&nbsp;&nbsp;&nbsp;</td>$nl"
                          . "</tr>\n";
        $bynr{$pn} = $pn;
        $byplac{$plac*1000+$pn} = $pn;
        $bytplac{$tplac*1000+$pn} = $pn;
      }
    }
  } # while()
  close(IN);

  my $outfile = $infile; chop $outfile;
  print STDERR "*** order results: $infile => $outfile\n"  if $fTest;

  open(IN, '<',  $infile)
    or die "Could not open file $infile: $!";
  open(OUT, '>', $outfile)
    or die "Could not create file $outfile: $!";

 # Copy first part: Stop at second occurrence of <table class="tbMain">
  my $tableseen = 0;
  while (<IN>) {
    if (/^<table class="tbMain">$/) {
      last  if $tableseen;
      $tableseen = 1;
    }
    print OUT;
  }

 # Insert new result lists
  foreach my $t (@resseq) {
    if    ($t eq 'a') {makePairsResult(\%byplac,  'Denne sektion')}
    elsif ($t eq 'p') {makePairsResult(\%bynr,    'Efter parnummer')}
    elsif ($t eq 's') {makePairsResult(\%bytplac, 'Samlet stilling')}
  }

 # Skip old list
  while (<IN>) {
    last  if m[^</table>$];
  }

 # Copy last part
  while (<IN>) {
    print OUT;
  }

  close(IN)                 or warn "$infile not closed: $!\n";
  close(OUT)                or warn "$outfile not closed: $!\n";

  showtime((times)[0] - $tf0)   if $fTest;
  move($outfile, $infile);
} # extendResultat()


sub extendSpilRes($) {
 # Modify the spilres file
  my ($infile) = @_;
  my $tf0 = (times)[0];

  my $nLine = 0;
  my ($cont, $tricks);
  my (%byscore);
  my ($nspn, $nspair, $nsscore, $nspt, %bynspn);
  my ($ewpn, $ewpair, $ewscore, $ewpt, %byewpn);
  my $bye = 1000;
  my $fCopy = 1;
  my $stopcopy = 0;
  my $startcopy = 0;
  my $seq = 0;
  my $board = 0;

  my $outfile = $infile; chop $outfile;
  print STDERR "*** order board results: $infile => $outfile\n"  if $fTest;

  open(IN, '<',  $infile)
    or die "Could not open file $infile: $!";
  open(OUT, '>', $outfile)
    or die "Could not create file $outfile: $!";

  while (<IN>) {
   # Insert necessary CSS and JavaScript definitions in the SpilRes file
    if (/<\/head>/) {
      print OUT <<'CSS';
  <style type="text/css">
    .order{font-weight:bold}
    .rel{position:relative}
    .NS,.EW{position:absolute;visibility:hidden}
    .SC{position:relative}
    @media print {
    .X00{margin-top:2cm}
    .X4{page-break-inside:avoid;widows:15;orphans:15}
    .NS,.EW{left:-6cm}
    }
  </style>
CSS
      print OUT <<'JS';
  <script type="text/javascript">
  function showByKind(tp, ml) {
    var n = 1;
    while (true) {
      var obj = document.getElementById(tp + n)
    if (obj == null) return
      obj.style.visibility = "visible"
      obj.style["marginLeft"] = ml
      if (tp != "NS") document.getElementById("NS" + n).style.visibility = "hidden"
      if (tp != "EW") document.getElementById("EW" + n).style.visibility = "hidden"
      if (tp != "SC") document.getElementById("SC" + n).style.visibility = "hidden"
      ++n
    }
  }
  function showNext(c) {
    c = c.parentNode
    var innerW = c.offsetWidth
    while (c.id == '') {c = c.parentNode}
    var outerW = c.parentNode.offsetWidth
    var tp = c.id.substr(0,2)
    var b = c.id.substr(2)
    if      (tp == "NS") {tp = "EW"}
    else if (tp == "EW") {tp = "SC"}
    else                 {tp = "NS"}
    showByKind(tp, "" + (outerW - innerW) / 2 + "px")
  }
  </script>
JS
    }

    if ($. == $startcopy) {
      $fCopy = 1;
      $nLine = 0;
      $seq = 0;
     # Insert new result lists
      ++$board;
      makeBoard($board, \%byewpn, 'EW');
      makeBoard($board, \%bynspn, 'NS');
      makeBoard($board, \%byscore, 'SC');
    }

    print OUT      if $fCopy;

    if ($. == $stopcopy) {
      $fCopy = 0;
      undef %bynspn;
      undef %byewpn;
      undef %byscore;
      $nLine = 0;
      $bye = 1000;
    } elsif ($. == $startcopy) {
      $fCopy = 1;
      $nLine = 0;
    } elsif (m[^  <table class="tbFrekvens X100">$]) {
      $stopcopy = $. + 9;
    } elsif (m[^      <td align="center" colspan="2">Point</td>$]) {
      $nLine = $. + 9   unless $nLine;
    } elsif ($nLine && $nLine <= $. && $. < $nLine + 10) {
     # get info
     # pair numbers are usually /\d+&nbsp;/, but they might be /<em>&nbsp;*\d+&nbsp;</em>/
      if ($. == $nLine && m[^  </td>$]) {$startcopy = $. + 2;}
      elsif ($. == $nLine + 0) {if (/>(\d+)/) {$nspn = $1;}
                                elsif (/>&nbsp;(\*?\d+)&nbsp;</) {$nspn = $1;}
                                else {$nspn = 0; $nspair = ''; --$nLine;}}
      elsif ($. == $nLine + 1) {               $nspair = $1   if m[[^>]+>(.+)</td>];}
      elsif ($. == $nLine + 2) {if (/>(\d+)/) {$ewpn = $1;}
                                elsif (/>&nbsp;(\*?\d+)&nbsp;</) {$ewpn = $1;}
                                else {$ewpn = 0; $ewpair = ''; --$nLine;}}
      elsif ($. == $nLine + 3) {               $ewpair = $1   if m[[^>]+>(.+)</td>];}
      elsif ($. == $nLine + 4) {
                                if (/colspan="2"/) {
                                  ($cont, $tricks) = ('&nbsp;','&nbsp;');
                                  $nLine--;
                                } else {
                                               $cont = $1     if m[>(.+)<];}}
      elsif ($. == $nLine + 5) {               $tricks = $1   if m[>(.+)<];}
      elsif ($. == $nLine + 6) {               $nsscore = $1  if m[>(.+)</td];}
      elsif ($. == $nLine + 7) {               $ewscore = $1  if m[>(.+)</td];}
      elsif ($. == $nLine + 8) {               $nspt = $1     if m[([-\d]+)];}
      elsif ($. == $nLine + 9) {               $ewpt = $1     if m[([-\d]+)];
        $nLine = $. + 3;
        my ($ns, $ew) = ($nspn, $ewpn);
        $nspn =~ tr/*//d;
        $ewpn =~ tr/*//d;
        unless ($ns) {$ns = ''; $nspn = ++$bye;}
        unless ($ew) {$ew = ''; $ewpn = ++$bye;}
        $nspair =~ s/ /&nbsp;/g; $nspair =~ s/&nbsp;-&nbsp;/ - /;
        $ewpair =~ s/ /&nbsp;/g; $ewpair =~ s/&nbsp;-&nbsp;/ - /;
        $nsscore =~ s/([\%])/$1$1/g;
        $ewscore =~ s/([\%])/$1$1/g;
        $cont =~ s/ /&nbsp;/g;
        $resline[$nspn] = "    <tr align='right'%s onclick='showNext(this)'>$nl"
                              . "<td>$ns&nbsp;&nbsp;</td>$nl"
                              . "<td align='left'>$nspair</td>$nl"
                              . "<td>&nbsp;$ew&nbsp;&nbsp;</td>$nl"
                              . "<td align='left'>$ewpair</td>$nl"
                              . "<td align='left'>&nbsp;&nbsp;$cont</td>$nl"
                              . "<td>$tricks</td>$nl"
                              . "<td>$nsscore</td>$nl"
                              . "<td>$ewscore</td>$nl"
                              . "<td>$nspt&nbsp;&nbsp;</td>$nl"
                              . "<td>$ewpt&nbsp;&nbsp;</td>$nl"
                            . "</tr>\n";
        $bynspn{$nspn} = $nspn;
        $byewpn{$ewpn} = $nspn;
        $byscore{$seq++} = $nspn;
      }
    }
  }

  close(IN)                 or warn "$infile not closed: $!\n";
  close(OUT)                or warn "$outfile not closed: $!\n";

  showtime((times)[0] - $tf0)   if $fTest;
  move($outfile, $infile);
} # extendSpilRes()


sub extendKampresultat($) {
 # Modify the kampresultat file to show the table ordered by total scores
  my ($infile) = @_;
  my $tf0 = (times)[0];

  my $nLine = 0;
  my (%bynr, %byplac);
  my ($nTeams, $tn, $team, $corr, $sum, $plac, $pn);
  my $fNonIntSum = 0;
  my (@score, @team, @all);

 # Collect the score lines ...
  open(IN, '<', $infile)   or return;

  while (<IN>) {
    if (m[<td class="tdKryds3" align="center">(&nbsp;)?&nbsp;(\d+)&nbsp;(&nbsp;)?</td>]) {
      $nTeams = $2;
    } elsif (/&nbsp;Plac.&nbsp;/) {
      $nLine = $. + 3   unless $nLine;
    } elsif ($nLine && $nLine <= $. && $. <= $nLine + 4 + $nTeams) {
     # get info
      if    (m[^  </td>$])               {last;}
      elsif ($. == $nLine + 0)           {$tn = $1          if />&nbsp;(\d+)&nbsp;</;
                                          undef @score;}
      elsif ($. == $nLine + 1)           {$team = $1        if />&nbsp;(.+)&nbsp;&nbsp;</;}
      elsif ($. <= $nLine + 1 + $nTeams) {push @score, $1   if /">(.+)</;}
      elsif ($. == $nLine + 2 + $nTeams) {$corr = $1        if />(.+)</;}
      elsif ($. == $nLine + 3 + $nTeams) {$sum = $1         if />([\d,*]+).*/;
                                          $fNonIntSum = 1   if $sum =~ /[^-\d]/;}
      elsif ($. == $nLine + 4 + $nTeams) {$plac = $1        if />(\d+).*/;
        $nLine = $. + 3;
        if    ($sum =~ /^\d+$/)    {$sum .= '&nbsp;&nbsp;&nbsp;';}
        elsif ($sum =~ /^[\d*]+$/) {$sum .= '&nbsp;&nbsp;';}
        @team = ($plac, $tn, $team, $corr, $sum, @score);
        $bynr{$tn} = $tn;
        $byplac{$plac*1000+$tn} = $tn;
        push @all, [@team];
      }
    }
  }

  close(IN);

  my $outfile = $infile; chop $outfile;
  print STDERR "*** order team results: $infile => $outfile\n"  if $fTest;

  open(IN, '<',  $infile)
    or die "Could not open file $infile: $!";
  open(OUT, '>', $outfile)
    or die "Could not create file $outfile: $!";

 # Copy first part
  my $round = 0;
  while (<IN>) {
    #last  if m[^  <th>Resultater efter ([^<]+)</th>];
    $round = $1  if m[^  <th>Resultater efter ([^<]+)</th>];
    last  if $round;
    print OUT;
  }

  makeTeamsTable(\%byplac, \@all, $fNonIntSum, $round);

 # Copy last part
  print OUT  <<"WHITE";
  <td class="horwhite"></td>
</tr>
<tr>
  <td class="horwhite"></td>
</tr>
<tr>
  <th>Efter holdnummer</th>
WHITE

  while (<IN>) {
   # Explain column (Röhl, 2009-03-06)
    print OUT  "      <td>Hold</td>\n"   if s/ colspan="2"//;
    print OUT;
  }

  close(IN)                 or warn "$infile not closed: $!\n";
  close(OUT)                or warn "$outfile not closed: $!\n";

  showtime((times)[0] - $tf0)   if $fTest;
  move($outfile, $infile);
} # extendKampresultat()


sub getconf() {
  unless (open(IN, "<", $rc)) {
    warn "Could not open $rc: $!\nUsing standard values\n";
    return;
  }

  my %rc;
  while (<IN>) {
    next          if /^$/;
    next          if /^\s*#/;
    $rc{$1} = $2  if /^\s*(\w+)\s*(.*)/;
  }
  close(IN);

  if (exists $rc{corrmail})       {$fCorr = 1;  $corrmail =    $rc{corrmail};    delete $rc{corrmail};}
  if (exists $rc{corr})           {             $corr =        $rc{corr};        delete $rc{corr};}
  if (exists $rc{corrtip})        {             $corrtip =     $rc{corrtip};     delete $rc{corrtip};}

  if (exists $rc{submail})        {$fSub =  1;  $submail =     $rc{submail};     delete $rc{submail};}
  if (exists $rc{sub})            {             $sub =         $rc{sub};         delete $rc{sub};}
  if (exists $rc{subtip})         {             $subtip =      $rc{subtip};      delete $rc{subtip};}

  if (exists $rc{nextweek})       {$fNext = 1;  $nextweek =    $rc{nextweek};    delete $rc{nextweek};}
  if (exists $rc{nextweektip})    {             $nextweektip = $rc{nextweektip}; delete $rc{nextweektip};}

  if (exists $rc{table})          {$fTable = 1;                                  delete $rc{table};}
  if (exists $rc{tablenumbers})   {$fTNumbers = 1;                               delete $rc{tablenumbers};}

  if (exists $rc{scoresequence})  {$fScoreSequence = 1;
    $resseq = $rc{scoresequence};
    $resseq = $defResSeq  unless $resseq ne '';
    if (   length $resseq > 3
        or  $resseq =~ /[^asp]/
        or ($resseq =~ tr/a//) > 1
        or ($resseq =~ tr/p//) > 1
        or ($resseq =~ tr/s//) > 1) {
      print STDERR  "Sequence '$resseq' not valid; '$defResSeq' used\n";
      $resseq = $defResSeq;
    }
    @resseq = split //, $resseq;
    delete $rc{scoresequence};
  }

  if (%rc) {
    for my $k (sort keys %rc) {
      print STDERR  "Unknown option: $k -- ignored\n";
    }
  }
} # getconf()



# ===== Here we go =====

# Read the configuration
getconf();
$reccorr = "mailto:$corrmail";
$recsub =  "mailto:$submail";

my $optErc = GetOptions (
     'help|?'         => \&usage,
     'version'        => \&version,
     'list'           => \&list,
     'css!'           => \$fCss,
     'del!'           => \$fDel,
     'ignore!'        => \$fIgnore,
     'show!'          => \$fShow,
     'table!'         => \$fTable,
     'test!'          => \$fTest,
     'time!'          => \$fTime,
   );


# Fill an empty @ARGV with all unhandled start files
# A start file is recognized by its name, Startliste99.html, where 99 represents a number.
# A start file has already been handled by bcfix if either
#   -  the first line matches / html / (a convention used in the early versions of bcfix), or
#   -  the first line with a <meta> tag matches /name="Dato"/ (the current convention).
unless (@ARGV) {
  if (opendir DIR, '.') {
   FILE:
    foreach my $fn (readdir DIR) {
      next  unless -e $fn;
      next  unless $fn =~ /^Startliste\d+\.html$/;
      next  unless -s _ && -f _ && -r _;
      next  unless open(IN, '<', $fn);

      while (<IN>) {
        last  if $. == 1 && / html /;
        last  if /^  <meta/i;
      }
      close(IN);

      push @ARGV, $fn  unless /name="Dato"/ || / html /;
    }
    closedir DIR;
  }
  unless (@ARGV) {
    warn "No unhandled start files in ", cwd(), "\n";
   # getTournaments(1);
    exit 0;
  }
}

die "$prog must be called with at least one argument\n"  unless @ARGV;

# Convert any start file name to a section number
map s/^Startliste(\d+)\.html$/$1/, @ARGV;

# Check for invalid arguments
my @invalid = grep !/^\d+$/, @ARGV;
die "Following arguments are not valid: @invalid\n"  if @invalid;

getTournaments(0);

foreach my $sec (@ARGV) {
  my $startfile = "Startliste$sec.html";
  print "Handle $startfile\n";

  if (isOK($startfile)) {
    push @handled, $sec;
    if ($fNext) {
      my $nw = getNextSec($sec);
      $nextweektag = defined $nw ? "      <td align='center'>&nbsp;<a class='noPrint' href='Startliste${nw}.html' title='$nextweektip'>$nextweek</a>&nbsp;</td>\n"
                                 : '';
    }
    undef %files;
    $files{$startfile} = 1;
    my $mod;
    do {
      $mod = 0;
      foreach my $file (keys %files) {
        if ($file !~ /^\w+:/ && $files{$file} < $isHandled) {
          handle($file);
          $files{$file} += $isHandled;
          $mod = 1;
        }
      }
    } while $mod;
    foreach (keys %files) {
      $allfiles{$_} += $files{$_};
    }
  }
}

##################################### Further info #############################################
#
#
# A single board result looks like this when the contracts are included
#      <TR>
#       <TD ALIGN="right" ROWSPAN="2" BGCOLOR="#F7F7F7" CLASS="tdR">1&nbsp;&nbsp;&nbsp;</TD>
#       <TD ALIGN="left" ROWSPAN="2" CLASS="tdR" BGCOLOR="#F7F7F7">Fink&nbsp;</TD>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Jens Hult Hansen&nbsp;</I></TD>
#       <TD ALIGN="left" ROWSPAN="2" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;Ricard&nbsp;</TD>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Jørgen Flensholt&nbsp;</I></TD>
#        <TD ALIGN="left" ROWSPAN="2" BGCOLOR="#F7F7F7">Ø 4HJ &nbsp;&nbsp;</TD>
#        <TD ALIGN="right" ROWSPAN="2" BGCOLOR="#F7F7F7">8&nbsp;</TD>
#       <TD ALIGN="right" ROWSPAN="2" BGCOLOR="#F7F7F7" CLASS="tdR">100&nbsp;</TD>
#       <TD ALIGN="right" ROWSPAN="2" BGCOLOR="#F7F7F7" CLASS="tdR">5&nbsp;&nbsp;</TD>
#      </TR>
#      <TR>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Jesper Dybdal&nbsp;</I></TD>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Peter Krogh&nbsp;</I></TD>
#      </TR>
#
#       +---+---+---+---+---+---+---+---+---+
#       |   |   | x |   | x |   |   |   |   |
#       + T + N +---+ N +---+ C + T + S + I +
#       |   |   | x |   | x |   |   |   |   |
#       +---+---+---+---+---+---+---+---+---+
#         Table       team Name       Score
#             team Name       Contract    Imp
#                 player  player  Tricks 
#
# and like this without the distribution and the contracts
#      <TR>
#       <TD ALIGN="center" CLASS="tdR">1</TD>
#       <TD ALIGN="right" ROWSPAN="2" BGCOLOR="#F7F7F7" CLASS="tdR">1&nbsp;&nbsp;&nbsp;</TD>
#       <TD ALIGN="left" ROWSPAN="2" CLASS="tdR" BGCOLOR="#F7F7F7">Allan Gaardbo&nbsp;</TD>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Allan Gaardbo&nbsp;</I></TD>
#       <TD ALIGN="left" ROWSPAN="2" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;Mette Staugaard&nbsp;</TD>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Mette Staugaard&nbsp;</I></TD>
#       <TD ALIGN="right" ROWSPAN="2" BGCOLOR="#F7F7F7" CLASS="tdR">600&nbsp;</TD>
#       <TD ALIGN="right" ROWSPAN="2" BGCOLOR="#F7F7F7" CLASS="tdR">4&nbsp;&nbsp;</TD>
#      </TR>
#      <TR>
#       <TD>&nbsp;</TD>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Bernhard Kolerus&nbsp;</I></TD>
#       <TD ALIGN="left" CLASS="tdR" BGCOLOR="#F7F7F7">&nbsp;<I>Olaf Eisenhardt&nbsp;</I></TD>
#      </TR>
#
#       +---+---+---+---+---+---+---+---+
#       | B |   |   | x |   |   |   |   |
#       +---+ T + N +---+ N +---+ S + I +
#       | _ |   |   | x |   | x |   |   |
#       +---+---+---+---+---+---+---+---+
#         Board   team Name       Score
#             Table       team Name   Imp
#                     player  player
#
