#!/usr/bin/perl -w
# - Add facility for images alongside papers (like jeffe, and in Gardner5)
# - Better naming of files than paper.ps, talk.ps
# - FIELDS for things like xxx number (which autogenerates text instead of
#   having to copy it every time) and ditto for electronic proceedings.
# - Related Papers section could say how the papers relate, e.g.,
#   "full version," "short version," "old version," "new version," ...
#   - Ideally even have them appear on a common webpage
#     e.g. ids of Linkage-Full and Linkage-FOCS intrinsically linked by Linkage
# - editors
# - add facility for linking to short versions of papers, like CCCG'99
#   papers... (electronic proceedings vs. printed proceedings)
# - add BibTeX entry to each page
use strict;
use YAML::Loader;
use File::Basename qw(dirname);

my $root_dir = "$ENV{'HOME'}/public_html";
#my $coauthor_index = "$root_dir/index.html";
my $coauthor_index = "$root_dir/papers/coauthors.html";
my $coauthor_yaml = "$root_dir/papers/coauthors.yaml";
my ($indexfile, $ctrfile, $logfile, $abstractfile) =
    ("index.html", "index.ctr", "index.log", "abstract.html");
my ($myname, $myname_re) = ("Erik D. Demaine", "E.* Demaine");
my $bibtex_file = "edemaine.bib";
my $debugging = 0;
if ($debugging) {
    $indexfile = "new-index.html";
}

my %suffixes = (
    "" => "HTML",
    ".ps" => "PostScript",
    ".ps.gz" => "gzipped PostScript",
    ".pdf" => "PDF",
    ".pdf.gz" => "gzipped PDF",
    ".pdf.zip" => "ZIPped PDF"
);
my @suffix_order = ("", ".ps", ".ps.gz", ".pdf", ".pdf.gz", ".pdf.zip");

my %complement = ( "(" => '\)', "{" => '}', "\"" => '"' );

# The following describes what the reference for a particular kind of paper
# (specified with an @at sign in the BibTeX) looks like.
# See use_form for details.
my %reference_style = (
  "article" => ["Journal articles", 0,
    'author, title, journal, volume, number, date, pages, publisher. note'
  ],
  "book" => ["Books", 0,
    'author, title, publisher, date. note'
  ],
  "inproceedings" => ["Conference papers", 0,
    'author, title, "in" booktitle, editor, series, volume, number, address, date, pages, publisher. note'
  ],
  "incollection" => ["Book chapters", 0,
    'author, title, "in" booktitle, editor, series, edition "Edition", volume, number, address, date, "chapter" chapter, pages, publisher. note'
  ],
  "misc" => ["Manuscripts", 0,
    'author, title, howpublished, date. note'
  ],
  "phdthesis" => ["PhD thesis", 0,
    'author, title, "PhD thesis", school, address, date. note',
    undef, 'thesis'
  ],
  "mastersthesis" => ["Master's thesis", 0,
    'author, title, "Master\'s thesis", school, address, date. note',
    undef, 'thesis'
  ],
  "techreport" => ["Technical reports", 0,
    'author, title, "Technical Report" number, series, institution, address, date. note'
  ],
  "conferencetalk" => ["Conference talks", 1,
    'author, title, series, conference, address, date. note',
    "This section only lists talks at conferences without proceedings.  See also <a href=\"#inproceedings\">conference papers</a>."
  ],
  "invitedtalk" => ["Invited talks", 1,
    'author, title, "Invited talk", series, institution/conference, address, date. note'
  ],
  "plenarytalk" => ["Plenary talks", 1,
    'author, title, "Plenary talk", series, institution/conference, address, date. note'
  ],
);
my @kinds = (
    { # Papers
        "name"  => "Paper",
        "dir"   => "papers",
        "file"  => "paper",
        "order" => ["book", "article", "incollection", "inproceedings",
                    "techreport", "misc", "phdthesis", "mastersthesis"]
    },
    { # Talk
        "name"  => "Talk",
        "dir"   => "talks",
        "file"  => "talk",
        "order" => ["plenarytalk", "invitedtalk", "conferencetalk"]
    }
);

my %url_key = (
  'booktitle'   => 'bookurl',
  'journal'     => 'journalurl',
  'series'      => 'seriesurl',
  'school'      => 'schoolurl',
  'institution' => 'institutionurl',
  'conference'  => 'conferenceurl',
  'number'      => 'numberurl',
  'publisher'   => 'publisherurl'
);

# This is a hash table, whose keys are strings like "Technical report"
# in the reference styles, and whose values are the BibTeX entries that would
# give a URL to link from these strings.
my %string_url_key = (
  "Technical report" => "techreporturl",
);

my $abbrev_volno = 0;  # use vol., no., pp. instead of volume, number, pages
my %parts = (
    "booktitle" => sub { pad_part ($_[0], "<I>", "booktitle", "</I>") },
    "conference" => sub { return pad_part ($_[0], "<I>", "conference", "</I>") },
    "date" => sub {
        my ($month, $year) = (just_part ($_[0], "month"),
                              just_part ($_[0], "year"));
        if ($month =~ /^\w+\.?$/) {
            return $month . " " . $year;
        } elsif ($month) {
            return $month . ", " . $year;
        } else {
            return $year;
        }
    },
    "editor" => sub { exists $_[0]->{'editor'} ?
                        "edited by " . author_text ($_[0], "editor", 1) : "" },
    "journal" => sub { pad_part ($_[0], "<I>", "journal", "</I>") },
    "number" => sub {
      substr ($_[1], 0, 1) eq '"' ?
        just_part ($_[0], "number") :
        pad_part ($_[0], ($abbrev_volno ? "no. " : "number "), "number", "")
      },
    "pages" => sub {
      if (!($_[0]->{'pages'})) {
        return "";
      } elsif ($_[0]->{'pages'} =~ /^[-0-9,\s]+$/) {
        return pad_part ($_[0], ($abbrev_volno ? "pp. " : "pages "),
                         "pages", "");
      } else {
        return just_part ($_[0], "pages");
      }
    },
    "author" => \&author_links,
    "title" => sub { pad_part ($_[0], "&ldquo;", "title", "&rdquo;"); },
    #"title" => sub { pad_part ($_[0], "``<A HREF=\"$_[0]->{'name'}\">",
    #                     "title", "</A>''") },
    "volume" => sub { pad_part ($_[0], ($abbrev_volno ? "vol. " : "volume "),
                                "volume", "") }
);

my $coauthor_homepages = coauthor_homepages ();
print "You have ", scalar (keys (%$coauthor_homepages)), " co-authors (counting aliases).\n";

################################## CODE ##################################

chdir "$root_dir/$kinds[0]->{'dir'}";
my ($bytype, $byname) = read_bibtex ($bibtex_file);
for my $type (keys %$bytype) {  ## for ports
  for my $e (grep { ! am_an_author ($_) } @{$bytype->{$type}}) {
    print STDOUT "Warning: Ignoring paper $e->{'name'} of which you are not an author.\n";
  }
  $bytype->{$type} = [ grep { am_an_author ($_) } @{$bytype->{$type}} ];
  delete $bytype->{$type} unless @{$bytype->{$type}};
}

my $kind;
for $kind (0, 1) {
  if (-d "$root_dir/$kinds[$kind]->{'dir'}") {
    chdir "$root_dir/$kinds[$kind]->{'dir'}";
    write_html ($kind);
  }
}

#system ("../bin/ctrs");

################################ ROUTINES ################################

sub read_bibtex {
    my ($filename) = @_;

    my ($type, $name, %entry);
    my $brace = "";
    my $bibtex = "";
    my %bytype = ();
    my %byname = ();

    open BIBTEX, $filename or die "Couldn't open $filename for reading";

    while (<BIBTEX>) {
        chomp;
        if (/^\s*@/) {
            warn "$type $name wasn't finished (discarding)" if $brace;
            unless (/^\s*@(\w+)\s*([({])\s*(\w+)\s*,\s*$/) {
                warn "Bad bibentry format";
                next;
            }
            ($type, $brace, $name) = (lc $1, $2, $3);
            %entry = ("name" => $name, "type" => $type);
            $bibtex = $_ . "\n";
        } elsif ($brace) {
            $bibtex .= $_ . "\n";
            if (/^\s*$complement{$brace}\s*$/) {
                warn "Unrecognized \@-type $type (ignoring)"
                    unless exists $reference_style{$type};
                $bytype{$type} = [] unless exists $bytype{$type};
                $entry{'bibtex'} = $bibtex;
                $byname{$entry{'name'}} = { %entry };
                push @{$bytype{$type}}, $byname{$entry{'name'}};
                $brace = "";
            } else {
                if (/^\s*\w+\s*=\s*(\d+)/) {
                    substr ($_, length ($&) - length ($1), length ($1))
                        = '"' . $1 . '"';
                }
                if (/^\s*(\w+)\s*=\s*([{"])(.*)$/) {
                    my ($key, $val_brace, $val) = (lc $1, $2, $3);
                    my $endbrace = $complement{$val_brace};
                    my $start_line = $.;
                    until ($val =~ /${endbrace}\s*(,?)\s*$/) {
                        die "EOF while looking for $endbrace (starting at line $start_line)" unless defined ($_ = <BIBTEX>);
                        $bibtex .= $_;
                        chomp;
                        $val .= $_;
                    }
                    #warn "Missing comma after $type ${name}'s $key" unless $key;
                    $val =~ s/${endbrace}\s*(,?)\s*$//;
                    $entry{$key} = $val;
                }
            }
        }
    }

    close BIBTEX;

    return (\%bytype, \%byname);
}

# Convert some of the LaTeX parts of a value to the corresponding HTML.
sub latex2html {
    my ($text) = @_;
    $text =~ s/\s\s+/ /g;
    $text =~ s#\\(?:emph|textit|mathit)\{([^{}]*)\}#<I>$1</I>#g;
    $text =~ s#\\(?:mathbb|mathbf|textbf)\{([^{}]*)\}#<B>$1</B>#g;
    $text =~ s#\$\(([a-zA-Z]+)\s*,\s*([a-zA-Z]+)\)\$#(<I>$1</I>,&nbsp;<I>$2</I>)#g;
    $text =~ s#\$([a-zA-Z]+)\$#<I>$1</I>#g;
    $text =~ s#\$([a-zA-Z]+)_\{([0-9,]+)\}\$#<I>$1</I><SUB STYLE="font-style: normal">$2</SUB>#g;
    $text =~ s#\$([a-zA-Z]+)_([0-9,])\$#<I>$1</I><SUB STYLE="font-style: normal">$2</SUB>#g;
    $text =~ s#\$<B>([a-zA-Z]+)</B>\^\{([0-9,]+)\}\$#<SPAN STYLE="font-style: normal"><B>$1</B><SUP>$2</SUP></SPAN>#g;
    $text =~ s#\$<B>([a-zA-Z]+)</B>\^([0-9,])\$#<SPAN STYLE="font-style: normal"><B>$1</B><SUP>$2</SUP></SPAN>#g;
    $text =~ s#\$\^([0-9])\$#<SUP>$1</SUP>#g;
    $text =~ s#\$\^\\circ\$#&deg;#g;
    $text =~ s#\$([a-zA-Z]+)\+([a-zA-Z]+)\$#<I>$1</I>&nbsp;+&nbsp;<I>$2</I>#g;
    $text =~ s#\$([a-zA-Z]+)\s*\(\s*([0-9]+)\s*\)\s*\$#<I>$1</I>($2)#g;
    $text =~ s#\$([a-zA-Z]+)\s*\(\\log\^([0-9])\s*([a-zA-Z]+)\s*\)\s*\$#<I>$1</I>(log<SUP>$2</SUP>&nbsp;<I>$3</I>)#g;
    $text =~ s#\$([a-zA-Z]+)\s*\(\\log\s*([a-zA-Z]+)\s*\)\s*\$#<I>$1</I>(log&nbsp;<I>$2</I>)#g;
    $text =~ s#\$\(([a-zA-Z]+)\s*\^([0-9])\s*-([0-9]+)\)\$#(<I>$1</I><SUP>$2</SUP>&nbsp;&minus;&nbsp;$3)#g;
    $text =~ s#\$\s*\\ell_([0-9])\s*\$#&\#8467;<SUB>$1</SUB>#g;
    $text =~ s#\$\s*([0-9]+)\s*\\times\s+([a-zA-Z]+)\$#$1&nbsp;&times;&nbsp;<I>$2</I>#g;
    $text =~ s#\$\s*([0-9]+)\s*\\times\s+([0-9]+)\$#$1&nbsp;&times;&nbsp;$2#g;
    $text =~ s/---/&mdash;/g;
    $text =~ s/--/&ndash;/g;
    # Unicode accents
    $text =~ s/\\'c/&#263;/g;
    $text =~ s/\\'n/&#324;/g;
    $text =~ s/\\v\{C\}/&#268;/g;
    $text =~ s/\\c\{s\}/&#351;/g;
    $text =~ s/\\c\{z\}/z/g;  ## doesn't exist
    $text =~ s/\\v\{s\}/&#353;/g;
    $text =~ s/\\v\{n\}/&#328;/g;
    $text =~ s/\\v\{r\}/&#345;/g;
    $text =~ s/\\u\{a\}/&#259;/g;
    $text =~ s/\\v\{a\}/&#462;/g;
    $text =~ s/\\H\{o\}/&#337;/g;
    #$text =~ s/\\v{a}/&#259;/g;  ## HACK: Mihai Patrascu says it looks better
    # end
    $text =~ s/\{([^{}]*)\}/$1/g;
    $text =~ s/\\&/&amp;/g;
    $text =~ s/\\#/#/g;
    $text =~ s/\\'a/&aacute;/g;
    $text =~ s/\\'e/&eacute;/g;
    $text =~ s/\\'E/&Eacute;/g;
    $text =~ s/\\`e/&egrave;/g;
    $text =~ s/\\'o/&oacute;/g;
    $text =~ s/\\'\\i/&iacute;/g;
    $text =~ s/\\'\{\\i\}/&iacute;/g;
    $text =~ s/\\"([aeuoO])/&$1uml;/g;
    $text =~ s/\\\^o/&ocirc;/g;
    $text =~ s/\\o/&oslash;/g;
    $text =~ s/\\copyright/&copy;/g;
    $text =~ s/\\dots\s*/&hellip;/g;
    $text =~ s/``/&ldquo;/g;
    $text =~ s/''/&rdquo;/g;
    $text =~ s/\\\s/ /g;
    $text =~ s#~#&nbsp;#g;
    # Excess braces
    $text =~ s#(^|\s)\{([^{}]*)\}#$1$2#g;
    return $text;
}

sub html_escape {
    my ($text) = @_;
    return "" unless defined $text;
    $text =~ s/&/&amp;/g;
    $text =~ s/</&lt;/g;
    $text =~ s/>/&gt;/g;
    $text =~ s/"/&quot;/g;
    return $text;
}

################################ ROUTINES ################################

sub write_html {
    my ($kind) = @_;
    my ($this, $other) = ($kinds[$kind], $kinds[1-$kind]);

    open INDEX, ">$indexfile" or die "Couldn't open $indexfile for writing";
    select INDEX; html_head ("Erik Demaine's $this->{'name'}s");
    #--- Removed now that talks are gone:
    ##print INDEX "You may also be interested in Erik's ",
    ##  "<A HREF=\"../$other->{'dir'}/\">", lc ($other->{'name'}), "s</A>.\n";

    print INDEX "$this->{'name'}s are grouped into\n";
    my $type;
    my @types = ();
    foreach $type (@{$this->{'order'}}) {
      next unless exists $bytype->{$type};
      #my $pluralize = \&pluralize;
      #$pluralize = sub { return @_; } if scalar @{$bytype->{$type}} < 2;
      push @types, "<A HREF=\"#$type\">" . $reference_style{$type}->[0]
        . "</A>";
    }
    print INDEX and_list (@types), ".\n";

    my %form_ignore = ('author' => 1, 'title' => 1);

    my @unavailable_list = ();
    my @no_abstract_list = ();
    my @no_length_list = ();

    foreach $type (@{$this->{'order'}}) {
        next unless exists $bytype->{$type};
        my ($title, $actual_kind, $form, $comment, $kind_text) = @{$reference_style{$type}};
        next unless $actual_kind == $kind;
        print INDEX "\n<H2 CLASS=\"section\"> <A NAME=\"$type\">$title",
            (substr ($title, -1, 1) eq "s" ? "" : "s"), "</A> </H2>\n\n";
        print INDEX "$comment\n\n" if defined $comment;
        print INDEX "<UL>\n";

        print STDOUT "You have ", scalar (@{$bytype->{$type}}),
                     " ", lc ($title), ".\n";

        my $e;
        foreach $e (@{$bytype->{$type}}) {
          print INDEX "<LI> <B><A HREF=\"$e->{'name'}/\">",
            latex2html ($e->{'title'}), "</A></B> <BR>\n";
          my $other_authors = other_authors_links ($e);
          print INDEX "     (",
            (author_key ($e) eq 'editor' ? 'edited' : 'joint work'),
            " with $other_authors) <BR>\n" unless $other_authors eq '';
          print INDEX "     ", use_form ($form, $e, \%form_ignore), " <P>\n";

            mkdir $e->{name}, 0755 unless -d $e->{name};
            chmod 0755, $e->{name};
            ##symlink "../../cgi-bin", "$e->{name}/cgi-bin"
            ##    unless -l "$e->{name}/cgi-bin";
            ##touch ("$e->{name}/$ctrfile") unless -f "$e->{name}/$ctrfile";
            ##touch ("$e->{name}/$logfile") unless -f "$e->{name}/$logfile";
            if (open PAPER, ">$e->{name}/$indexfile") {
              select PAPER;
              html_head (author_text ($e) . ": $e->{'title'}",
                $this->{'name'} . " by $myname");
              print PAPER "<DL>\n";

              print PAPER "<DT><B>Reference</B>:</DT>\n";
              my $reference = use_form ($form, $e);
              # Kill the first link
              #$reference =~ s#<A HREF=".*?">(.*?)</A>#$1#;
              print PAPER "<DD> $reference\n";
              if ($e->{'bibtex'}) {
                my $bibtex = html_escape ($e->{'bibtex'});
                print PAPER "<details><summary>BibTeX</summary>",
                  "<pre>$bibtex</pre></details>\n";
              }
              print PAPER "</DD> <P>\n";

              if (open ABSTRACT, "$e->{name}/$abstractfile") {
                print PAPER "<DT><B>Abstract</B>:</DT> <DD>\n";
                while (<ABSTRACT>) {
                  print PAPER $_;
                }
                close ABSTRACT;
                print PAPER "</DD> <P>\n";
                chmod 0644, "$e->{name}/$abstractfile";  ## for ports
              } else {
                push @no_abstract_list, $e->{name};
              }

              my $note;
              foreach $note (qw(comments updates copyright)) {
                if (exists $e->{$note}) {
                  print PAPER "<DT><B>", ucfirst ($note), "</B>:</DT>\n";
                  print PAPER "<DD> $e->{$note} </DD> <P>\n";
                }
              }

              my @kindname = (lc $this->{'name'}, lc $other->{'name'});
              if ($type eq 'book') {
                $kindname[0] = $type;
              }
              $kindname[$kind] = $kind_text if defined $kind_text;
              $kindname[0] = $e->{"$kindname[0]kind"}
                if exists $e->{"$kindname[0]kind"};
              $kindname[1] = $e->{"$kindname[1]kind"}
                if exists $e->{"$kindname[1]kind"};

              if (exists $e->{'length'}) {
                print PAPER "<DT><B>Length</B>:</DT>\n";
                my @parts = split (/\s*;\s*/, $e->{'length'});
                print "<DD> The $kindname[0] is $parts[0]";
                print " and the $kindname[1] is $parts[1]"
                    if $#parts >= 1;
                print ".</DD> <P>\n";
              } else {
                push @no_length_list, $e->{name};
              }

              print PAPER "<DT><B>Availability</B>:\n";
              my $something = 0;
              my @which = ($this, $other);
              my $k;
              for $k (0, 1) {
                my @files = find_file ("$e->{name}/$which[$k]->{'file'}");
                print PAPER "<DD> The $kindname[$k] is",
                  ($k > 0 ? " also" : ""),
                  " available in ", and_list (map {
                    "<A HREF=\"$which[$k]->{'file'}$_\">$suffixes{$_}</A>" .
                    file_size ("$e->{name}/$which[$k]->{'file'}$_")
                  } @files) . ".</DD>\n" if @files;
                $something = 1 if @files;
              }
              if ($e->{availability}) {
                warn "'availability' set for something that's available"
                  if $something;
                print PAPER "<DD> $e->{availability}</DD>\n";
              } elsif ($something) {
                print PAPER "<DD> See <a href=\"../../formats.html\">",
                  "information on file formats</a>.</DD>\n";
              } else {
                push @unavailable_list, $e->{name};
                print PAPER "<DD> Currently unavailable.  ",
                  "If you are in a rush for copies,\n",
                  '<A HREF="mailto:edemaine@mit.edu">contact me</A>',
                  ".</DD>\n";
              }
              my $query = "allintitle:\"$e->{'title'}\" " .
                          join (" ", map { 'author:' . author_last_name ($_) }
                                         author_list ($e));
              $query =~ s/\s\s+/ /g;
              $query =~ s/:/%3A/g; $query =~ s/ /+/g; $query =~ s/"/%22/g;
              print "<DD>[<A HREF=\"http://scholar.google.com/scholar?q=$query&ie=UTF-8&oe=UTF-8&hl=en&btnG=Search\">Google Scholar search</A>]</DD>\n";
              print "<P>\n";

              if (exists $e->{'software'}) {
                print PAPER "<DT><B>Software</B>:</DT>\n";
                my $project;
                foreach $project (split /\s*;\s*/, $e->{'software'}) {
                  print PAPER "<DD> <A HREF=\"../../$project\">$project",
                    "</A> </DD> <P>\n";
                }
              }

              for $k (0, 1) {
                my $w = $kinds[$k];
                my $label = lc ($w->{'name'}) . "s";
                if (exists $e->{$label}) {
                  my @related = grep { am_an_author ($_) }
                    map { $byname->{$_} } grep { exists $byname->{$_} }
                    split /\s*;\s*/, $e->{$label};
                  if (@related) {
                    print PAPER "<DT><B>Related $label</B>:</DT>\n";
                    my $obj;
                    foreach $obj (@related) {
                      print PAPER "<DD> <A HREF=\"../../$w->{'dir'}/$obj->{'name'}",
                        "\">$obj->{'name'}</A> (" . latex2html ($obj->{'title'}) .
                        ") </DD>\n";
                    }
                    print "<P>\n";
                  }
                }
              }

              if (exists $e->{'webpages'}) {
                print PAPER "<DT><B>Related webpages</B>:</DT>\n";
                my @pages;
                foreach my $page (split /\s*;\s*/, $e->{'webpages'}) {
                  my $file = "$root_dir/$page";
                  $file .= "/index.html" if -d $file;
                  my $title = $page;
                  if (open PAGE, $file) {
                    while (<PAGE>) {
                      if (m#<TITLE>(.*?)</TITLE>#i) {
                        $title = $1;
                        $title =~ s/Erik(\s*Demaine\s*)'s\s*//;
                        $title =~ s/\s*\(Erik Demaine\)\s*$//;
                        last;
                      }
                    }
                    close PAGE;
                  } else {
                    warn "Couldn't open $file to extract title";
                  }
                  push @pages, "<A HREF=\"../../$page\">$title</A>\n"
                }
                print PAPER "<DD> ", join ("<BR>\n", @pages), "<P>\n";
              }

#               my @related = ();
#               push @related,
#                 "The corresponding <A HREF=\"../../$other->{'dir'}/$e->{name}\">"
#                 if exists $e->{"Has " . $type};
#               if (@related) {
#                 print PAPER "<DT><B>Related information</B>:\n";
#                 print PAPER "<DD><UL>\n<LI> ";
#                 print PAPER join ("\n<LI> ", @related);
#                 print PAPER "</UL>\n"
#               }

              print PAPER "</DL>\n<HR>\nSee also other ";
              print PAPER and_list (map {
                "<A HREF=\"../../$kinds[$_]->{'dir'}/\">" .
                lc ($kinds[$_]->{'name'}) . "s</A>";
              } (0,)); ##(0,1)); -- removed talks
              print PAPER " by <A HREF=\"../../\">Erik Demaine</A>.";
              html_foot (1);
              close PAPER;
              chmod 0644, "$e->{name}/$indexfile";
            } else {
              warn "Couldn't write to $e->{name}/$indexfile";
            }
        }
        print INDEX "</UL>\n";
    }
    select INDEX; html_foot (0);
    close INDEX;
    chmod 0644, $indexfile;

    print STDOUT "Unavailable ", lc ($this->{name}), "s: ",
      join (", ", @unavailable_list), "\n";
    if ($this->{name} eq 'Paper') {
      my $unavailable_re = join ("|", @unavailable_list);
      print STDOUT "Abstractless but available ", lc ($this->{name}), "s: ",
        join (", ", grep { ! /^$unavailable_re$/ } @no_abstract_list), "\n";
      print STDOUT "Lengthless but available ", lc ($this->{name}), "s: ",
        join (", ", grep { ! /^$unavailable_re$/ } @no_length_list), "\n";
    } else {
      print STDOUT "Abstractless ", lc ($this->{name}), "s: ",
        join (", ", @no_abstract_list), "\n";
      print STDOUT "Lengthless ", lc ($this->{name}), "s: ",
        join (", ", @no_length_list), "\n";
    }
}

sub touch {
    my ($file) = @_;
    if (open TOUCHTEMP, ">>$file") {
        close TOUCHTEMP;
    } else {
        warn "Couldn't touch $file";
    }
}

sub html_head {
    my ($title, $head) = @_;
    $head = $title unless defined $head;
    print <<END;
<HTML>
<HEAD>
  <TITLE> $title </TITLE>
  <META NAME="keywords" CONTENT="Erik Demaine, paper, papers, publication, publications, research" charset="UTF-8">
  <STYLE><!--#include virtual="/style.css"--></STYLE>
  <!--#include virtual="/analytics.html"-->
</HEAD>
<BODY>
<H1> $head </H1>
END
}

sub html_foot {
    my ($level) = @_;
    my $dots = '../' x $level;
    print <<END;

<HR>

<I>
These pages are generated automagically from a
<A HREF=\"${dots}$bibtex_file\">BibTeX</A> file.
<BR>
<!--#config errmsg="[?]" -->
<!--#config timefmt="%B %e, %Y" -->
Last updated <!--#flastmod file="$indexfile" --> by
<A HREF="../$dots">Erik Demaine</A>.
<!--You are the-->
<!--config errmsg="<I>n</I>th visitor for some <I>n</I>." -->
<!--include virtual="/cgi-bin/cgiwrap/~eddemain/ecounter.cgi"-->

</BODY>
</HTML>
END
}

sub find_file {
    my ($base) = @_;

    my $suffix;
    my @result = ();
    foreach $suffix (@suffix_order) {
        if (-r $base . $suffix) {
            push @result, $suffix;
            if (-d $base . $suffix) {
                chmod 0755, $base . $suffix;
            } else {
                chmod 0644, $base . $suffix;
            }
        }
    }

    return @result;
}

sub file_size {
  my ($filename) = @_;
  return "" if -d $filename;
  my @stats = (stat _);
  if ($#stats >= 7) {
    return "&nbsp;(" . int ($stats[7] / 1024) . "k)";
  }
}

sub and_list {
  my @list = (@_);
  if (scalar @list <= 0) {
    return "";
  } elsif (scalar @list == 1) {
    return $list[0];
  } elsif (scalar @list == 2) {
    return "$list[0] and $list[1]";
  } else {
    my $last = pop @list;
    return (join ", ", @list) . ", and $last";
  }
}

sub author_key {
  my ($e) = @_;
  if (exists $e->{'author'}) {
    return "author";
  } elsif (exists $e->{'editor'} && $e->{'type'} eq 'book') {
    return "editor";
  } else {
    warn "No author/editor for $e->{'type'} $e->{'name'}";
    return undef;
  }
}

sub author_suffix {
  my ($e, $key) = @_;
  return "" if ref $e eq 'ARRAY';
  $key = author_key ($e) unless defined $key;
  if ($key eq 'editor') {
    if (scalar (author_list ($e, $key)) == 1) {
      return " (editor)";
    } else {
      return " (editors)";
    }
  } else {
    return "";
  }
}

sub author_list {
  my ($e, $key) = @_;
  if (ref $e eq 'ARRAY') {
    return @$e;
  } else {
    $key = author_key ($e) unless defined $key;
    return split /\s+and\s+/, latex2html ($e->{$key});
  }
}

sub author_text {
  my ($e, $key, $nosuffix) = @_;
  if ($nosuffix) {
    return and_list (author_list ($e, $key));
  } else {
    return and_list (author_list ($e, $key)) . author_suffix ($e, $key);
  }
}

sub author_links {
  my ($e, $key) = @_;
  return and_list (map {
    my $url = coauthor_homepage ($coauthor_homepages, $_);
    defined $url ? "<A HREF=\"$url\">$_</A>" : $_
  } author_list ($e, $key)) . author_suffix ($e, $key);
}

sub am_an_author {
  return scalar grep { $_ =~ /$myname_re/ } author_list (@_);
}

sub other_authors_text {
  return and_list (map { $_ =~ /$myname_re/ ? () : ($_) } author_list (@_));
}

sub other_authors_links {
  return author_links ([map { $_ =~ /$myname_re/ ? () : ($_) } author_list (@_)]);
}

sub author_last_name {
  my ($name) = @_;
  while ($name =~ /\s/) {
    $name = $';
  }
  return $name;
}

################################ ROUTINES ################################

# $entry is the BibTeX entry.
#
# $ignore is a hash reference.  Each key in it specifies a BibTeX field to
# ignore, that is, pretend that it's empty.
sub use_form {
  my ($form, $entry, $ignore) = @_;
  my ($text, $subtext, $punct, $nvar, $nnovar) = ("", "", "", 0, 0);
  my $prev;

  while ($form =~ /(\w+)(\/\w+)?|[,. ]|"[^"]*"/) { #"
    my $c = substr ($&, 0, 1);
    if ($c eq "," || $c eq ".") {
      if ($nvar > 0 || $nnovar == 0) {
        # We've seen something since the last punctuation, so 
        if (length ($text) >= 2 &&
            substr ($text, length ($text) - 2) eq "''") {

          # Stick punctuation before the end quotes
          substr ($text, length ($text) - 2, 0) = $punct;
        } else {

          # Add punctuation to the end of the string
          $text .= $punct;
          #$prev_punct = $punct unless $punct eq "";
        }
        $text .= $subtext;
        $punct = $c;
      } else {
        # Upgrade our punctuation.  Periods rule: if we ever pass one, that's
        # what we should use for our punctuation.
        $punct = $c unless $punct eq ".";
      }
      $subtext = "";
      $nvar = $nnovar = 0;
      $prev = $punct;
    } elsif ($c eq " ") {
      $subtext .= $c;
    } elsif ($c eq '"') {
      $prev = $&;
      my $string = substr ($&, 1, -1);
      if (exists $string_url_key{$string}) {
        $string = "<A HREF=\"$entry->{$string_url_key{$string}}\">$string</A>"
          if exists $entry->{$string_url_key{$string}};
      }
      $subtext .= $string;
    } else {
      my ($var, $key);
      $key = $1;
      $key = substr ($2, 1) if defined $2 && !exists $entry->{$1};
      if (exists $ignore->{$key}) {
        $var = "";
      } else {
        if (exists $parts{$key}) {
          $var = &{$parts{$key}} ($entry, $prev);
        } else {
          $var = just_part ($entry, $key);
        }
      }
      if ($var ne '') {
        $subtext .= $var;
        $nvar++;
      } else {
        $nnovar++;
      }
    }
    $form = substr ($form, length ($&));
  }

  $text = $text . $punct . $subtext;
  # Remove any leading punctuation.
  $text =~ s/^[,. ]*//;
  return $text;
}

################################ ROUTINES ################################

sub just_part {
    my ($e, $key) = @_;

    if (exists $e->{$key}) {
        return make_link ($e, $key);
    } else {
        return "";
    }
}

sub pad_part {
    my ($e, $left, $key, $right) = @_;

    if (exists $e->{$key}) {
        return $left . make_link ($e, $key) . $right;
    } else {
        return "";
    }
}

sub make_link {
    my ($e, $key) = @_;
    my $val = latex2html ($e->{$key});

    return $val unless exists $url_key{$key};

    my $url = $url_key{$key};
    if (exists $e->{$url}) {
        if ($val =~ /\(([^()]+)\)$/) {  # Put link around abbreviation
            $val = $` . "(<A HREF=\"$e->{$url}\">" . $1 . "</A>)";
        } else {                        # Put link around everything
            $val = "<A HREF=\"$e->{$url}\">$val</A>";
        }
    }

    return $val;
}

# Give a reasonable attempt at pluralizing the given word or phrase,
# by simply looking blindly at a suffix.
sub pluralize {
  my ($x) = @_;
  if ($x =~ /is$/) {
    $x =~ s/is$/es/;
  } elsif ($x =~ /s$/) {
    $x =~ s/$/es/;
  } else {
    $x =~ s/$/s/;
  }
  return $x;
}

############################# COAUTHOR HOMEPAGE #############################

sub canonical_name {
  my $name = $_[0];
  $name =~ s/&(.)acute;/$1/g;
  $name =~ s/&(.)uml;/$1/g;
  $name =~ s/\\['`"](.)/$1/g;
  my @parts = split (" ", $name);
  ## This seems to be a rule for e.g. J. Ian Munro, but it fails on e.g.
  ## E. D. Demaine and isn't necessary for J. Ian Munro anymore.
  #if (scalar @parts > 2 && $parts[0] =~ /^\S\.$/) {
  #  return lc (substr ($parts[1], 0, 1) . $parts[$#parts]);
  #} else {
    return lc (substr ($parts[0], 0, 1) . $parts[$#parts]);
  #}
}

sub coauthor_homepages_HTML {
  my %homepages = (canonical_name ($myname) => '../../');

  unless (open INDEX, $coauthor_index) {
    warn "coauthor_homepage: Couldn't open $coauthor_index\n";
    return \%homepages;
  }

  my $on = 1;  ## this is because of the new Python/YAML-based coauthor system
  while (<INDEX>) {
    if (/My co-authors:/) {
      $on = 1;
    } elsif (/<LI>/) {
      $on = 0;
    } elsif ($on) {
      if (m#<A HREF="([^"]*)">\s*([^<]*?)\s*</A>[,.]#) {
        my ($url, $name) = ($1, $2);
        my $canon = canonical_name ($name);
        warn "Multiple homepages for $name" if exists $homepages{$canon};
        $homepages{$canon} = $url;
      } elsif (m#^\s*([-\w.]+ [-\w\s.]+)[,.]\s*$#) {
        my $name = $1;
        my $canon = canonical_name ($name);
        warn "Multiple homepages for $name" if exists $homepages{$canon};
        $homepages{$canon} = undef;
      }
    }
  }

  close INDEX;

  return \%homepages;
}

sub coauthor_homepages {
  my %homepages = (canonical_name ($myname) => '../../');
  open YAML, $coauthor_yaml or die "No YAML file found";
  my $yaml = join "", <YAML>;
  close YAML;
  $yaml = YAML::Loader->new->load ($yaml);
  for my $author (@$yaml) {
    $homepages{canonical_name (latex2html ($author->{'name'}))} = $author->{'url'}
      if exists $author->{'name'};
    if (exists $author->{'alias'}) {
      if (ref $author->{'alias'} eq 'ARRAY') {
        for my $alias (@{$author->{'alias'}}) {
          $homepages{canonical_name (latex2html ($alias))} = $author->{'url'};
        }
      } else {
        $homepages{canonical_name (latex2html ($author->{'alias'}))} = $author->{'url'};
      }
    }
  }
  return \%homepages;
}

sub coauthor_homepage {
  my ($homepages, $queryname) = @_;
  my $canon = canonical_name ($queryname);
  if (exists $homepages->{$canon}) {
    return $homepages->{$canon};
  } else {
    warn "coauthor_homepage: Co-author `$queryname' isn't in the co-author list!\n";
    return undef;
  }
}
