#!/usr/bin/perl
# Filename:	?
# Author:	David Ljung Madison <DaveSource.com>
# See License:	http://MarginalHacks.com/License
# Description:	Reads in netscape bookmark files
use strict;

##################################################
# Setup the variables
##################################################
my $PROGNAME = $0;
$PROGNAME =~ s|.*/||;

my $UMASK = 0755;

my $NETSCAPE = "netscape";
my $ICON = "";
my $ICON_FOLDER = "gnome-folder.png";

my @IGNORE = ("Personal Toolbar Folder");

##################################################
# Usage
##################################################
sub usage {
  my $msg;
  foreach $msg (@_) { print "ERROR:  $msg\n"; }
  print "\n";
  print "Usage:\t$PROGNAME [-d] <file>\n";
  print "\tParses a netscape bookmark file\n";
  print "\t-d\tSet debug mode\n";
  print "\t-rev\tWrite bookmarks in reverse order\n";
  print "\n";
  exit -1;
}

sub parse_args {
  my $file;
  my $arg;
  while ($#ARGV>=0) {
    $arg=shift(@ARGV);
    if ($arg =~ /^-h$/) { usage(); }
    if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; }
    if ($arg =~ /^-rev(erse)?$/) { $MAIN::REVERSE=1; next; }
    if ($arg =~ /^-/) { usage("Unknown option: $arg"); }
    usage("Too many files specified [$arg and $file]") if (defined($file));
    $file=$arg;
  }
  usage("No file defined") if (!defined($file));

  $file;
}

sub ignore {
  my ($name) = @_;

  my $ignore;
  foreach $ignore ( @IGNORE ) {
    return 1 if ($name =~ /$ignore/);
  }
  0;
}

sub parse_netscape {
  my ($file) = @_;

  # Open the file and prep it
  open(FILE,$file) || usage("Couldn't open file: $file");
  # Read up to the first <dl> tag
  while(<FILE>) { last if (/<dl>/i); }

  my %bm;		# Bookmark data structure
  my $bm_H = \%bm;	# The pointer
  $bm_H->{'parent'}=$bm_H;

  # Parse the bookmarks
  while(<FILE>) {
    # Start a list (should always follow <dt><h3>folder..)
    next if (/<dl>/i);	# We actually just ignore these

    # Bookmark
    if (/<dt><a href="([^"]+)" ADD_DATE="(\d+)" LAST_VISIT="(\d+)" LAST_MODIFIED="(\d+)">(.*)<\/a>/i) {
      my $next = $MAIN::REVERSE ? 0 : ($#{$bm_H->{'bookmarks_L'}}+1);
      unshift(@{$bm_H->{'bookmarks_L'}},undef) unless ($next);
      $bm_H->{'bookmarks_L'}[$next]{'href'}=$1;
      $bm_H->{'bookmarks_L'}[$next]{'add_date'}=$2;
      $bm_H->{'bookmarks_L'}[$next]{'last_visit'}=$3;
      $bm_H->{'bookmarks_L'}[$next]{'last_modified'}=$4;
      $bm_H->{'bookmarks_L'}[$next]{'name'}=$5;

    # Bookmark description
    } elsif (/<dd>(.*)/i) {
      next unless $1;
      my $current = $MAIN::REVERSE ? 0 : $#{$bm_H->{'bookmarks_L'}};
      $bm_H->{'bookmarks_L'}[$current]{'comment'}=$1 if ($current>=0);

    # Folder
    } elsif (/<dt><h3 (folded )?add_date="(\d+)">(.*)<\/h3>/i) {
      my ($name,$add_date,$folded) = ($3, $2, $1 ? 1 : 0);

      if (ignore($name)) {
        while(<FILE>) { last if (/<\/dl>/i); }
        next;
      }

      my $next = $MAIN::REVERSE ? 0 : ($#{$bm_H->{'bookmarks_L'}}+1);
      unshift(@{$bm_H->{'bookmarks_L'}},undef) unless ($next);
      $bm_H->{'bookmarks_L'}[$next]{'name'}=$name;
      $bm_H->{'bookmarks_L'}[$next]{'add_date'}=$add_date;
      $bm_H->{'bookmarks_L'}[$next]{'folded'}=$folded;
      $bm_H->{'bookmarks_L'}[$next]{'folder'}{'parent'}=$bm_H;
      $bm_H=$bm_H->{'bookmarks_L'}[$next]{'folder'};

    # End folder
    } elsif (/<\/dl>/i) {
      $bm_H=$bm_H->{'parent'};

    # More of a comment
    } else {
      my $current = $MAIN::REVERSE ? 0 : $#{$bm_H->{'bookmarks_L'}};

      if ($bm_H->{'bookmarks_L'}[$current]{'comment'} =~ /<br>$/i) {
        $bm_H->{'bookmarks_L'}[$current]{'comment'}="$`\n$_";
      } else {
        print STDERR "Can't parse [$.]: $_\n";
      }
    }
  }

  close(FILE);

  \%bm;
}

# Make a name safe for the filesystem
sub safe_name {
  my ($name) = @_;

  $name =~ s|/|\\|g;		# Take '/' out of directory names
  $name =~ s|/^./|dot|;		# Don't hide directories
  $name = "noname" unless ($name);
  return $name unless (-r $name);
  my $count=1;
  while (1) {
    $count++;
    return "$name.$count" unless (-r $name);
  }
}

sub print_kde {
  my ($bm_H, $name) = @_;

  mkdir($name,$UMASK) || die("[$PROGNAME]  Couldn't make directory [$name]\n");
  chdir($name);
  open(DIRECTORY,">.directory") || die("[$PROGNAME]  Couldn't write [.directory]\n");
  print DIRECTORY "# KDE Config File [written by $PROGNAME]\n";
  print DIRECTORY "[Desktop Entry]\n";
  print DIRECTORY "Icon=$ICON_FOLDER\n";
  print DIRECTORY "Name=$name\n";
  print DIRECTORY "Type=Directory\n";
      #  Rats.  We don't have this data anymore
      #$bm_H->{'bookmarks_L'}[$next]{'folded'}=$1 ? 1 : 0;
      #$bm_H->{'bookmarks_L'}[$next]{'add_date'}=$2;
      #$bm_H->{'bookmarks_L'}[$next]{'name'}=$3;
  close(DIRECTORY);

  my $entry_H;
  my @order;
  foreach $entry_H ( @{$bm_H->{'bookmarks_L'}} ) {
    if ($entry_H->{'folder'}) {
      my $name = safe_name($entry_H->{'name'});
      push(@order,$name);
      print_kde($entry_H->{'folder'}, $name);
      chdir("..");
    } else {
      my $file = safe_name($entry_H->{'name'}) . ".desktop";
      push(@order,$file);
      open(ENTRY, ">$file") || die("[$PROGNAME]  Couldn't write [$file]\n");;
      print ENTRY "[Desktop Entry]\n";
      print ENTRY "Name=$entry_H->{'name'}\n";
      my $comment = $entry_H->{'comment'} || $entry_H->{'name'};
      print ENTRY "Comment=$comment\n";
      print ENTRY "Exec=$NETSCAPE $entry_H->{'href'}\n";
      print ENTRY "Icon=$ICON\n";
      print ENTRY "Terminal=0\n";
      print ENTRY "Type=Application\n";
      print ENTRY "Add_Date=$entry_H->{'add_date'}\n";
      print ENTRY "Last_Visit=$entry_H->{'last_visit'}\n";
      print ENTRY "Last_Modified=$entry_H->{'last_modified'}\n";
      close(ENTRY);
    }
  }
  open(ORDER,">.order") || die("[$PROGNAME]  Couldn't write [.order]\n");
  print ORDER join("\n",@order);
  close(ORDER);
}

sub print_bm {
  my ($bm_H, $lvl) = @_;

  my $entry_H;
  foreach $entry_H ( @{$bm_H->{'bookmarks_L'}} ) {
    if ($entry_H->{'folder'}) {
      print "\n";
      print "  "x($lvl+2), "$entry_H->{'name'}\n";
      print "  "x($lvl+2), "-"x(length($entry_H->{'name'})), "\n";
      print_bm($entry_H->{'folder'}, $lvl+2);
      print "\n";
    } else {
      #print "  "x$lvl, "$entry_H->{'name'} -> $entry_H->{'href'}\n";
      print "  "x$lvl, "$entry_H->{'name'}\n";
    }
  }
}

##################################################
# Main code
##################################################
sub main {
  my $file = parse_args();

  my $bm_H=parse_netscape($file);

  print_kde($bm_H, "bookmarks");
  #print_bm($bm_H);
}
main();
