#!/usr/bin/perl -w # constants my $kKeywordsPath = "../search.pl"; my $kBookmarksPath = "/Users/mihai/Library/Safari/Bookmarks.plist"; my $kBookmarksBarTitle = "BookmarksBar"; my $kCacheLocalPath = "/Library/WebServer/Documents/bookmarks.html"; my $kCacheWebPath = "/bookmarks.html"; my %kIgnoredTitles = ("Address Book" => 1, "Rendezvous" => 1, "History" => 1, "BookmarksMenu" => 1); use strict; use XML::DOM; require "$kKeywordsPath"; use vars qw(%keywords); if (FilesModified()) { RegenerateFile(); } print "Location: $kCacheWebPath\n\n"; sub FilesModified { my $bookmarksModDate = GetModDate($kBookmarksPath); my $keywordsModDate = GetModDate($kKeywordsPath); open(DATES, "< dates"); my $savedBookmarksModDate = ; chomp $savedBookmarksModDate; my $savedKeywordsModDate = ; chomp $savedKeywordsModDate; close(DATES); return 0 if ($savedBookmarksModDate == $bookmarksModDate && $savedKeywordsModDate == $keywordsModDate); open(DATES, "> dates"); print DATES "$bookmarksModDate\n$keywordsModDate\n"; close(DATES); return 1; } sub GetModDate { my ($file) = @_; my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($file); return ($mtime); } sub RegenerateFile { open(BOOKMARKS, "> $kCacheLocalPath"); # print header print BOOKMARKS < Bookmarks

Bookmarks

HEADER # begin parsing my $parser = new XML::DOM::Parser; my $bookmarks = $parser->parsefile($kBookmarksPath); my $plistRoot = $bookmarks->getFirstChild()->getNextSibling(); foreach my $child ($plistRoot->getChildNodes()) { if ($child->getNodeName eq 'dict') { ParseNode($child); last; } } $bookmarks->dispose(); # import keywords too print BOOKMARKS <

Keywords

KEYWORDSHEADER foreach my $keyword (keys %keywords) { print BOOKMARKS "

$keyword\n"; } print BOOKMARKS <

KEYWORDSFOOTER # print footer print BOOKMARKS <

FOOTER close(BOOKMARKS); } sub ParseNode { my ($node) = @_; my %dict = DictToHash($node); if ($dict{Title} && !$kIgnoredTitles{$dict{Title}}) { if ($dict{Title} eq $kBookmarksBarTitle) { print BOOKMARKS "

Bookmarks Toolbar Folder

\n"; } else { print BOOKMARKS "

$dict{Title}

\n"; } print BOOKMARKS "

\n"; } foreach my $child ($node->getChildNodes()) { next if $child->getNodeType != ELEMENT_NODE; if ($child->getNodeName() eq 'key') { if (GetInnerText($child) eq 'Children') { my $sibling = GetNextRealSibling($child->getNextSibling()); CheckNodeName($sibling, 'array'); foreach my $arrayElement ($sibling->getChildNodes()) { next if ($arrayElement->getNodeType != ELEMENT_NODE); CheckNodeName($arrayElement, 'dict'); ParseNode($arrayElement); } } elsif (GetInnerText($child) eq 'URIDictionary') { my $bookmarkDict = GetNextRealSibling($child->getNextSibling()); CheckNodeName($bookmarkDict, 'dict'); my ($url, $title) = GetBookmarkDictInfo($bookmarkDict); print BOOKMARKS "

$title\n"; } } } if ($dict{Title} && !$kIgnoredTitles{$dict{Title}}) { print BOOKMARKS "

\n"; } } sub CheckNodeName { my ($node, $name) = @_; die('unexepected node name: ' . $node->getNodeName . ' wanted ' . $name . "\n" . 'node contents:' . $node->toString) if ($node->getNodeName ne $name); } sub GetNextRealSibling { my ($node) = @_; for (; $node && $node->getNodeType() != ELEMENT_NODE; $node = $node->getNextSibling) {;} return $node; } sub GetInnerText { my ($node) = @_; my $text = ''; foreach my $child ($node->getChildNodes()) { next if ($child->getNodeType() != TEXT_NODE); my $childText = $child->toString; $childText =~ s/^\s*(.*[^\s])\s*$/$1/; $text .= $childText; } return $text; } sub GetBookmarkDictInfo { my ($dict) = @_; my %info = DictToHash($dict); $info{''} =~ s/&/&/g; return ($info{''}, $info{'title'}); } sub DictToHash { my ($dict) = @_; my %hash = (); my ($key, $value) = (GetNextRealSibling($dict->getFirstChild()), ''); do { CheckNodeName($key, 'key'); $value = GetNextRealSibling($key->getNextSibling); $hash{GetInnerText($key)} = GetInnerText($value); $key = GetNextRealSibling($value->getNextSibling); } while ($key && $value); return %hash; }