#!/usr/bin/env perl

# Copyright 2005-2024 (c) Faisal N. Jawdat
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer. Redistributions in
# binary form must reproduce the above copyright notice, this list of
# conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution. Neither the name of
# Faisal N. Jawdat nor the names of its contributors may be used to
# endorse or promote products derived from this software without specific
# prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
# HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Cases:
  # called without path:  fail
  # called with blogdir:  create post, then file
  # called with blogdir and postpath in drafts dir:  file
  # called with blogdir and postname in blog dir:  recreate indices and otherwise ignore


use strict;
use warnings;
use FindBin qw($Bin);
use Fcntl ':mode';
use File::Copy;
use File::Basename;
use Cwd;
use Cwd 'abs_path';
use Time::Local 'timelocal_nocheck';
use DateTime;

our $VERSION = '0.21';
our $DEBUG = 0;
our ($renderpath, $stagingpath, $blogpath, $draftspath, $postpath, $editor, %support, @renderhistory);

my %argv_options;
my $usage_error = "usage $0 source-path [post-path]\n";

my %prefs;

eval "use Text::MultiMarkdown 'markdown';";
if ($@) {
  die "MultiMarkdown support not loaded: Install Text::MultiMarkdown.\n";
} else {
  $support{'markdown'} = 1;
}

eval "use XML::Atom::SimpleFeed;";
if ($@) {
  die "Atom support not loaded: install XML::Atom::SimpleFeed.\n";
} else {
  $support{'atom'} = 1;
};

eval "use DateTime::Format::W3CDTF;";
if ($@) {
  die "WCDTF support not loaded: install DateTime::Format::W3CDTF.\n";
} else {
  $support{'w3cdtf'} = 1;
}

use Data::Dumper;
eval "use XML::Simple;";
if ($@) {
  die "XML support not loaded: install XML::Simple.\n";
} else {
  $support{'parser'} = 1;
}

if (defined $ENV{'BEAR_EDITOR'}) {
  $editor = $ENV{'BEAR_EDITOR'}
} else {
  $editor = 'mate -w';
}

if ($ENV{'EMBEDDED_IN_BEAR'}) {

} else {

  local $ENV{'EMBEDDED_IN_BEARBLOG'} = 1;
  eval "require '$Bin/bear';";

  if ((@ARGV) && (!(defined $ARGV[2]))) {
    $renderpath = abs_path($ARGV[0]);
    print "Source path: $renderpath\n";

    $stagingpath = $renderpath . '/staging';

    if (!(-e $renderpath)) {
      die "ERROR - Source path does not exist: $renderpath\n";
    }

    if (!(-e $stagingpath)) {
      die "ERROR - Staging path does not exist: $stagingpath\n";
    }

    if (!(-e "$stagingpath/%prefs.txt")) {
      die "ERROR - %prefs.txt does not exist: $stagingpath/%prefs.txt\n";
    }

    chdir($stagingpath);
    %prefs = get_prefs(%prefs);
    chdir($ENV{'PWD'});

    if (defined $prefs{'BEAR_blog_path'}) {
      $blogpath = $stagingpath . '/' . $prefs{'BEAR_blog_path'};
      print "Blog path: $blogpath\n";
    } else {
      die "%BEAR_blog_path undefined in $stagingpath/%prefs";
    }

    if (defined $prefs{'BEAR_blog_drafts_path'}) {
      $draftspath = $stagingpath . '/' . $prefs{'BEAR_blog_drafts_path'};
      print "Drafts path: $draftspath\n";
    } else {
      die "%BEAR_blog_drafts_path undefined in $stagingpath/%prefs";
    }

    if (defined $ARGV[1]) {
      $postpath = $ARGV[1];
      if (-e $postpath) {
        $postpath = abs_path($ARGV[1]);
      } elsif (-e "$draftspath/$ARGV[1]") {
        $postpath = "$draftspath/$ARGV[1]";
      } else {
        die "No such file:  $ARGV[1]\n";
      }
    }

  } else {
    die $usage_error;
  }

  if (defined $ARGV[1]) {
    if (post_in_draftspath($postpath)) {
      install_post_for_publication($postpath);
    } else {
      create_date_indices_from_postpath();
    }
  } else {
    # If no input -- create a new post
    $postpath = create_draft_post();
    system($editor . ' ' . $postpath);
    install_post_for_publication($postpath);
  }
}

1;

sub create_draft_post {
  my $post = $draftspath . '/' . time . '.txt';
  if (-e $post) {
    die "$post exists.\n";
  } else {
    open(POST, "> $post") or die "Failed to open $postpath for editing.\n";
    print(POST "\n");
    close(POST);
  }
  return $post;
}

sub install_post_for_publication {
  my ($post) = @_;

  my $time = time;

  my ($sec,  $min,  $hour,  $day,  $month,  $year,  $wday,  $yday,  $isdst) = localtime($time);
  $year += 1900;
  $month++;

  if (1 == length($month)) {
    $month = "0$month";
  }

  if (1 == length($day)) {
    $day = "0$day";
  }

  my $yearmonthday = $year . '/' . $month . '/' . $day;

  create_date_indices($year, $month, $day);

  open(POST, $post) or die "couldn't open $post\n";
  # The first line is the title
  my $title = <POST>;
  close(POST);
  if (!(defined $title)) {
    $title = '';
  }

  # Make version of title for final path.
  $title =~ tr/[A-Z]/[a-z]/;      # lowercase
  $title =~ s/^\s+//;             # strip leading spaces
  $title =~ s/\s+$//;             # strip trailing spaces
  $title =~ s/[^a-z0-9\-_\s]//g;   # strip non alphanumeric, -, _
  $title =~ s/\s+/-/g;            # spaces to -s
  $title =~ s/-+/-/g;             # - runs to single -s
  if ($title eq  '') {
    # If nothing left, make it the current time.
    $title = $time;
  }

  my $target_dir = $blogpath . '/' . $yearmonthday;
  my @target_dir_files = <$target_dir/*>;

  # The first file in the dir is the index file, the second is post 1, the
  # third is post 2, etc. Therefore we do not increment the count. Note: This
  # assumes the index already exists
  my $file_increment = @target_dir_files;

  my $targetpath = $target_dir . '/' . $file_increment . '-' . $title . '.txt';

  # The file_increment should ensure this is a unique filename. If we are not
  # putting the count at the beginning then we'll need to check the uniqueness
  # and possibly fail the save.
  move($post, $targetpath);

  print "Posting: $post\n";
  print "Saving in: $targetpath\n";
}

sub create_date_indices_from_postpath {
  $postpath =~ m/$blogpath\/(\d\d\d\d)\/(\d\d)\/(\d\d)\/(.*)/;
  my $year = $1;
  my $month = $2;
  my $day = $3;
  print "Post exists in: $year-$month-$day \n";
  create_date_indices($year, $month, $day);
}

sub create_date_indices {
  my ($year,  $month,  $day) = @_;

  my $year_dir = $blogpath . '/' . $year;
  mkdir($year_dir) unless (-d $year_dir);
  create_year_index($year);

  my $yearmonth = $year. '/' . $month;
  my $yearmonth_dir = $blogpath . '/' . $yearmonth;
  mkdir($yearmonth_dir) unless (-d $yearmonth_dir);
  create_yearmonth_index($year, $month);

  my $yearmonthday = $yearmonth . '/' . $day;
  my $yearmonthday_dir = $blogpath . '/' . $yearmonthday;
  mkdir($yearmonthday_dir) unless (-d $yearmonthday_dir);
  create_yearmonthday_index($year, $month, $day);
}


sub create_year_index {
  my ($year) = @_;

  my $year_index_path = $blogpath . '/' . '%index_year.txt';

  if (-e $year_index_path) {
    print "Creating index for $year.\n";

    my $year_index_buffer;
    open(YEAR_INDEX_TEMPLATE, $year_index_path) or die "Failed to open $year_index_path.\n";
    while (my $year_index = <YEAR_INDEX_TEMPLATE>) {
      $year_index_buffer .= $year_index;
    }
    close(YEAR_INDEX_TEMPLATE);

    $year_index_buffer =~ s/<%BEAR_blog_post_year%>/$year/g;

    open(YEAR_INDEX, "> $blogpath/$year/index.txt") or die "Failed to open $blogpath/$year/index.txt.\n";
    print(YEAR_INDEX $year_index_buffer);
    close(YEAR_INDEX);

  } else {
    print "No %index_year.txt file found to create index for $year.\n";
  }
}

sub create_yearmonth_index {
  my ($year,  $month) = @_;

  my $month_name = month_name($month);

  my $month_index_path = $blogpath . '/' . '%index_month.txt';

  if (-e $month_index_path) {
    print "Creating index for $month_name, $year.\n";

    my $month_index_buffer;
    open(MONTH_INDEX_TEMPLATE, $month_index_path) or die "Failed to open $month_index_path.\n";
    while (my $month_index = <MONTH_INDEX_TEMPLATE>) {
      $month_index_buffer .= $month_index;
    }
    close(MONTH_INDEX_TEMPLATE);

    $month_index_buffer =~ s/<%BEAR_blog_post_year%>/$year/g;
    $month_index_buffer =~ s/<%BEAR_blog_post_month%>/$month_name/g;

    open(MONTH_INDEX, "> $blogpath/$year/$month/index.txt") or die "Failed to open $blogpath/$year/$month/index.txt.\n";
    print(MONTH_INDEX $month_index_buffer);
    close(MONTH_INDEX);

  } else {
    print "No %index_month.txt file found to create index for $year/$month.\n";
  }
}

sub create_yearmonthday_index {
  my ($year,  $month,  $day) = @_;

  my $month_name = month_name($month);

  my $day_index_path = $blogpath . '/' . '%index_day.txt';

  my $day_of_week = day_of_week($year, $month, $day);

  if (-e $day_index_path) {
    print "Creating index for $month_name $day, $year.\n";

    my $day_index_buffer;
    open(DAY_INDEX_TEMPLATE, $day_index_path) or die "Failed to open $day_index_path.\n";
    while (my $day_index = <DAY_INDEX_TEMPLATE>) {
      $day_index_buffer .= $day_index;
    }
    close(DAY_INDEX_TEMPLATE);

    $day_index_buffer =~ s/<%BEAR_blog_post_year%>/$year/g;
    $day_index_buffer =~ s/<%BEAR_blog_post_month%>/$month_name/g;
    $day_index_buffer =~ s/<%BEAR_blog_post_day%>/$day/g;
    $day_index_buffer =~ s/<%BEAR_blog_day_of_week%>/$day_of_week/g;

    open(DAY_INDEX, "> $blogpath/$year/$month/$day/index.txt") or die "Failed to open $blogpath/$year/$month/$day/index.txt.\n";
    print(DAY_INDEX $day_index_buffer);
    close(DAY_INDEX);

  } else {
    print "No %index_day.txt file found to create index for $year/$month/$day.\n";
  }
}

sub post_in_draftspath {
  my ($post) = @_;

  # If the post path doesn't start in the blogpath, fail.
  if ($post =~ m/^$draftspath/) {
    return 1;
  } else {
    return 0;
  }
}

sub transform_blogindex {
  my (%prefs) = @_;

  my $index_buffer;

  # @renderhistory is constructed by the (ordered) traversal of the tree, so
  # the order is guaranteed and we can just pop stuff off the top.

  my $historysize = @renderhistory;
  my $indexsize;
  if ($historysize < $prefs{'BEAR_blog_index_days'}) {
    $indexsize = $historysize;
  } else {
    $indexsize = $prefs{'BEAR_blog_index_days'};
  }

  my $array_count = 1;
  my $day_count = 0;

  until ($day_count == $indexsize) {
    # This could be $renderhistory[-$array_count++]. It isn't.
    # This could also be a pop(@renderhistory), but we want it around for
    # any rss generation.
    my $path = $renderhistory[-$array_count];

    # Indices are the only files we know we'll find in every blog dir, so look for
    # one and extract the date from there. Otherwise ignore it and move on.
    if ($path =~ /(.*)\/(\d\d\d\d)\/(\d\d)\/(\d\d)\/index.txt/) {
      my $year = $2;
      my $month = $3;
      my $day = $4;

      $path =~ s/index.txt$//;
      chdir($path);

      $index_buffer .= '<h2>' . full_date($year, $month, $day) . "</h2>\n";
      for my $post (sort_blog(<*.txt>)) {
        unless ($post eq 'index.txt') {
          $index_buffer .= blog_post($post, 0, %prefs);
        }
      }
      $day_count++;
    }
    $array_count++;
  }

  if ((defined $prefs{'BEAR_blog_rss_path'}) and ($support{'atom'})) {
    blog_rss(%prefs);
  }

  return $index_buffer;
}

sub transform_blog {
  my ($sourcefile, %prefs) = @_;

  $sourcefile =~ m/^(.*)\//;
  my $sourcedir = $1;

  if ($sourcefile =~ m/$blogpath\/(\d\d\d\d)\/(\d\d)\/(\d\d)\/(.*?)/) {
    # this is a day index
    my $year = $1;
    my $month = $2;
    my $day = $3;
    unless ($sourcefile =~ m/index.txt$/) {
      # This is a post. Sack the input sourcebuffer and regenerate
      # from %template_post.txt.
      return blog_post($sourcefile, 1, %prefs);
    } else {
      # This is an index. Work with the files in the dir to render the post.
      return blog_day($sourcedir, %prefs);
    }
  } elsif ($sourcefile =~ m/$blogpath\/(\d\d\d\d)\/(\d\d)\/index.txt/) {
    # This is a month index.
    my $year = $1;
    my $month = $2;
    return blog_month($year, $month, %prefs);
  } elsif ($sourcefile =~ m/$blogpath\/(\d\d\d\d)\/index.txt/) {
    # This is a year index.
    my $year = $1;
    return blog_year($year, %prefs);
  } elsif ($sourcefile eq "$blogpath/index.txt") {
    # This is the top level index.
    return blog_all(%prefs);
  }
  # For all other cases, do nothing.
  return ''
}

sub blog_rss {
  my (%prefs) = @_;
  my ($feed_dt, %feed_history) = read_feed(%prefs);
  my $wc3now = DateTime::Format::W3CDTF->new->format_datetime(DateTime->now);

  # Finding it ugly, fragile, and/or impossible to update the feed once
  # created, so we create two and decide which to keep after the fact.
  # It's a hack.
  my $old_feed = XML::Atom::SimpleFeed->new(
    id => $prefs{'BEAR_site_url'},
    title => $prefs{'BEAR_blog_rss_title'},
    link => $prefs{'BEAR_site_url'},
    link => { rel => 'self', href => "$prefs{'BEAR_site_url'}$prefs{'BEAR_blog_rss_path'}", },
    updated => $feed_dt
  );
  my $new_feed = XML::Atom::SimpleFeed->new(
    id => $prefs{'BEAR_site_url'},
    title => $prefs{'BEAR_blog_rss_title'},
    link => $prefs{'BEAR_site_url'},
    link => { rel => 'self', href => "$prefs{'BEAR_site_url'}$prefs{'BEAR_blog_rss_path'}", },
    updated => $wc3now
  );

  my $array_count = 1;
  my $post_count = 0;
  my $feed_updated = 0;
  until ($post_count == $prefs{'BEAR_blog_rss_items'}) {
    my $path = $renderhistory[-$array_count];
    if (($path =~ /(.*)\/(\d\d\d\d)\/(\d\d)\/(\d\d)\/(.*)/) and (!('index.txt' eq $5))) {
      my $post_id = "$prefs{'BEAR_site_url'}$prefs{'BEAR_blog_path'}/$2/$3/$4/$5";
      $post_id =~ s/\.txt$//;
      open(POST, $path);
      my $posttitle = <POST>;
      chomp($posttitle);
      my $postcontent;
      while (my $postline = <POST>) {
        $postcontent .= $postline;
      }
      close(POST);
      $postcontent = markdown($postcontent);

      my $w3cdt;
      if (exists $feed_history{$post_id}) {
        $w3cdt = $feed_history{$post_id};
      } else {
        $w3cdt = $wc3now;
        $feed_updated = 1;
      }

      # Now populate both feeds
      $old_feed->add_entry(
        id => $post_id,
        title => $posttitle,
        link => $post_id,
        author => $prefs{'BEAR_blog_author'},
        summary => $postcontent,
        updated => $w3cdt
      );
      $new_feed->add_entry(
        id => $post_id,
        title => $posttitle,
        link => $post_id,
        author => $prefs{'BEAR_blog_author'},
        summary => $postcontent,
        updated => $w3cdt
      );

      $post_count++;
    }
    $array_count++;
  }

  my $rsspath = $renderpath . '/' . $prefs{'BEAR_blog_rss_path'};
  my $rss;
  if ($feed_updated) {
    $rss = $new_feed->as_string;
  } else {
    $rss = $old_feed->as_string;
  }
  open(RSS, "> $rsspath");
  print(RSS $rss);
  close(RSS);
}

sub read_feed {
  my (%prefs) = @_;
  my $feed_dt;
  my %feed_history;
  my $rsspath = $renderpath . '/' . $prefs{'BEAR_blog_rss_path'};

  if ($support{'parser'} and (-f $rsspath)) {
    my $parser = XML::Simple->new;
    my $feed_data = $parser->XMLin($rsspath);

    $feed_dt = $$feed_data{updated};

    my $entries = $$feed_data{entry};
    for my $key (keys(%$entries)) {
      $feed_history{$key} = $entries->{$key}->{updated};
    }
  }

  return ($feed_dt, %feed_history);
}

sub blog_raw_post {
  my ($filename, %prefs) = @_;

  my $title;
  my $post_buffer;
  my $templatefile = "$stagingpath/$prefs{'BEAR_blog_path'}/%template_post.txt";
  open(POST_TEMPLATE, $templatefile) or die "Failed to open $templatefile.\n";
  while (my $line = <POST_TEMPLATE>) {
    $post_buffer .= $line;
  }
  close(POST_TEMPLATE);

  my $source_buffer;
  open(POST, $filename) or die "Failed to open $templatefile.\n";
  $title = <POST>;
  chomp($title);
  while (my $line = <POST>) {
    $source_buffer .= $line;
  }
  close(POST);
  $source_buffer = markdown($source_buffer);

  $post_buffer =~ s/<%BEAR_blog_post%>/$source_buffer/;

  my $permalink = abs_path($filename);
  $permalink =~ s/$stagingpath//;
  $permalink = "$prefs{'BEAR_site_url'}/$permalink";
  # Kill spare //s
  $permalink =~ s/\/(\/+)/\//g;
  # ... but put http:// back
  $permalink =~ s/^http:\//http:\/\//;
  if (has_pref('BEAR_create_xhtml', %prefs)) {
    $permalink =~ s/\.txt$/.xhtml/g;
  } else {
    $permalink =~ s/\.txt$/.html/g;
  }

  $post_buffer =~ s/<%BEAR_blog_title%>/$title/;

  $post_buffer =~ s/<%BEAR_blog_permalink%>/$permalink/;

  return ($title, $post_buffer);
}

sub blog_post {
  my ($sourcefile, $touch, %prefs) = @_;

  my ($scratch, $post_buffer) = blog_raw_post($sourcefile, %prefs);

  if ($touch) {
    my $tmpdir = dirname($sourcefile);
    touch(abs_path("$tmpdir/index.txt")); # update day
    touch(abs_path("$tmpdir/../index.txt")); # update month
    touch(abs_path("$tmpdir/../../index.txt")); #update year
    touch(abs_path("$tmpdir/../../../index.txt")); #update everything
    touch(abs_path("$tmpdir/../../../../index.txt")); #update site index (should be configurable?)
  }

  return $post_buffer;
}

sub blog_day {
  my ($dir, %prefs) = @_;

  my $day_buffer = '';

  chdir($dir);
  for my $file (sort_blog(<*.txt>)) {
    unless ($file eq 'index.txt') {
      my ($title, $post_contents) = blog_raw_post($file, %prefs);
      $day_buffer .= $post_contents;
    }
  }

  return $day_buffer;
}

sub blog_month {
  my ($year, $month, %prefs) = @_;

  my $month_buffer;

  my $monthdir = "$blogpath/$year/$month";
  chdir($monthdir);

  for my $day (sort_blog(<*>)) {
    unless ($day eq 'index.txt') {
      $month_buffer .= '<p><strong>' . full_date($year, $month, $day) . "</strong></p>\n";
      chdir("$monthdir/$day");
      for my $post (sort_blog(<*.txt>)) {
        unless ($post eq 'index.txt') {
          $month_buffer .= blog_post($post, 0, %prefs);
        }
      }
    }
  }

  return $month_buffer;
}

sub blog_year {
  my ($year, %prefs) = @_;

  my $year_buffer = "<ul>\n";

  chdir("$blogpath/$year");

  for my $month (sort_blog(<*>)) {
    unless ($month eq 'index.txt') {
      my $name_of_month = month_name($month);
      my $permalink = "$prefs{'BEAR_site_url'}/$prefs{'BEAR_blog_path'}/$year/$month/";
      if (has_pref('BEAR_create_xhtml', %prefs)) {
        $permalink .= 'index.xhtml';
      } else {
        $permalink .= 'index.html';
      }
      # Kill spare //s
      $permalink =~ s/\/(\/+)/\//g;
      # ... but put http:// back
      $permalink =~ s/^http:\//http:\/\//;
      $year_buffer .= '<li><a href="' . $permalink . '">' . $name_of_month . "</a></li>\n";
    }
  }
  $year_buffer .= "</ul>\n";

  return $year_buffer;
}

sub blog_all {
  my (%prefs) = @_;

  my $top_buffer = "<ul>\n";

  my $topdir = "$blogpath";
  chdir($topdir);

  my @years;
  for my $file (<*>) {
    if ($file =~ m/\d\d\d\d/) {
      push @years, $file;
    }
  }
  for my $year (sort_blog(@years)) {
    my $permalink = "$prefs{'BEAR_site_url'}/$prefs{'BEAR_blog_path'}/$year/";
    if (has_pref('BEAR_create_xhtml', %prefs)) {
      $permalink .= 'index.xhtml';
    } else {
      $permalink .= 'index.html';
    }
    # Kill spare //s
    $permalink =~ s/\/(\/+)/\//g;
    # ... but put http:// back
    $permalink =~ s/^http:\//http:\/\//;
    $top_buffer .= "<li><a href=\"$permalink\">$year</a>\n";
    $top_buffer .= blog_year($year, %prefs);
    $top_buffer .= "</li>\n";
  }

  $top_buffer .= "</ul>\n";

  return $top_buffer;
}

sub sort_blog {
  my @files = @_;
  @files = sort {
    # Grab the numbers from each file's name.
    $a =~ m/^(\d+)/;
    my $anum = $1;
    $b =~ m/^(\d+)/;
    my $bnum = $1;

    # Sort the files by the reverse order of the numbers within.
    if ($anum > $bnum) {
      return -1;
    } else {
      return 1;
    }

  } @files;

  return @files;
}

sub date_elements_from_path {
  my ($file) = @_;
  $file =~ /(.*)\/(\d\d\d\d)\/(\d\d)\/(\d\d)/;
  return ($2, $3, $4);
}

sub get_stub_title_from_path {
  my ($file) = @_;
  $file =~ /(.*)\/(\d\d\d\d)\/(\d\d)\/(\d\d)/;
  my $fulldate = full_date($2, $3, $4);
  return "Blog post: $fulldate"
}

sub touch {
  my ($file) = @_;
  utime(undef, undef, $file);
}

sub full_date {
  my ($year, $month, $day) = @_;
  my $fulldate;
  $fulldate .= day_of_week($year, $month, $day);
  $fulldate .= ', ';
  $fulldate .= month_name($month);
  $fulldate .= " $day, $year";
  return $fulldate;
}

sub day_of_week {
  my ($year,  $month,  $day) = @_;

  my @weekday = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);
  my $epochtime = timelocal_nocheck(0, 0, 0, $day, $month-1, $year);
  my $date = (localtime($epochtime))[6];
  return $weekday[$date];
}


sub month_name {
  my ($month) = @_;

  my @month = ('', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');

  return $month[$month];
}
