はてなダイアリーライター 0.5.0 のパッチ

id:hyuki:20040825にて、はてダラのバージョンアップ報告があったので、昨日自サイトの日記にアップしたパッチを再度適用。あとエントリのロード機能ではtouch.txtを更新しないように修正。パッチは以下に貼り付けときます。

--- hw050.pl	Wed Aug 25 12:07:56 2004
+++ hw050p1.pl	Wed Aug 25 18:58:10 2004
@@ -15,7 +15,7 @@
 # modify it under the same terms as Perl itself.
 #
 use strict;
-my $VERSION = "0.5.0";
+my $VERSION = "0.5.0-patch1";
 
 use LWP::UserAgent;
 use HTTP::Request::Common;
@@ -23,11 +23,17 @@
 use File::Basename;
 use Getopt::Std;
 
+my $enable_encode = eval('use Encode; 1');
+
 # Prototypes.
 sub login();
 sub logout();
+sub update_mode();
+sub load_mode();
+sub load_diary_entry($$$);
 sub update_diary_entry($$$$$$);
 sub delete_diary_entry($);
+sub load_it($$$);
 sub create_it($$$);
 sub delete_it($);
 sub post_it($$$$$$);
@@ -58,6 +64,13 @@
 # where %s is filename, output is stdout.
 my $filter_command = '';
 
+# Proxy setting.
+my $httpproxy = '';
+
+# Encoding.
+my $c_code = 'euc-jp';
+my $s_code = 'euc-jp';
+
 my $hatena_url = 'http://d.hatena.ne.jp';
 
 my %ua_option = (
@@ -81,10 +94,12 @@
     'g' => "",  # "groupname" option.
     'f' => "",  # "file" option.
     'M' => 0,   # "no timestamp" flag.
+    'l' => "",  # "load" option.
+    'n' => "",  # "coNfig file" option.
 );
 
 $Getopt::Std::STANDARD_HELP_VERSION = 1;
-getopts("tdu:p:a:T:cg:f:M", \%cmd_opt);
+getopts("tdu:p:a:T:cg:f:Ml:n:", \%cmd_opt);
 
 if ($cmd_opt{d}) {
     print_debug("Debug flag on.");
@@ -93,6 +108,8 @@
     &VERSION_MESSAGE();
 }
 
+$config_file = $cmd_opt{n} if $cmd_opt{n};
+
 # Override global vars with config file.
 load_config() if -e($config_file);
 
@@ -117,6 +134,16 @@
 
 # Main sequence.
 sub main {
+    if($cmd_opt{l}) {
+        load_mode();
+    }
+    else {
+        update_mode();
+    }
+}
+
+# Update mode.
+sub update_mode() {
     my $count = 0;
     my @files;
 
@@ -189,10 +216,30 @@
     }
 }
 
+# Load mode
+sub load_mode() {
+
+    # Check -s option format.
+    error_exit("Illegal -l option format.") if $cmd_opt{l} !~ /(\d\d\d\d)-(\d\d)-(\d\d)/;
+    my ($year, $month, $day) = ($1, $2, $3);
+    
+    # Login if necessary.
+    login unless ($user_agent);
+
+    # Load
+    print_message("Load $year-$month-$day.");
+    load_diary_entry($year,$month,$day);
+    print_message("Load OK.");
+
+    # Logout if necessary.
+    logout if ($user_agent);
+}
+
 # Login.
 sub login() {
     $user_agent = LWP::UserAgent->new(%ua_option);
     $user_agent->env_proxy;
+    $user_agent->proxy( 'http', $httpproxy ) if $httpproxy;
 
     # Ask username if not set.
     unless ($username) {
@@ -276,6 +323,31 @@
     print_message("Logout OK.");
 }
 
+# Load entry.
+sub load_diary_entry($$$) {
+    my ($year, $month, $day) = @_;
+    my $load_retry = 0;
+    my $ok = 0;
+
+LOAD_RETRY:
+    while ($load_retry < 2) {
+        # Load.
+        $ok = load_it($year, $month, $day);
+        if ($ok or not $cmd_opt{c}) {
+            last;
+        }
+        print_debug("load_diary_entry: LOAD_RETRY.");
+        unlink($cookie_file);
+        print_message("Old cookie. Retry login.");
+        login();
+        $load_retry++;
+    }
+
+    if (not $ok) {
+        error_exit("load_diary_entry: get: Check username/password.");
+    }
+}
+
 # Update entry.
 sub update_diary_entry($$$$$$) {
     my ($year, $month, $day, $title, $body, $imgfile) = @_;
@@ -381,6 +453,70 @@
     }
 }
 
+# Load.
+sub load_it($$$) {
+    my ($year, $month, $day) = @_;
+
+    print_debug("load_it: $year-$month-$day.");
+
+    $user_agent->cookie_jar($cookie_jar);
+
+    my $r = $user_agent->simple_request(
+        HTTP::Request::Common::GET("$hatena_url/$username/edit?date=$year$month$day"));
+
+    print_debug("load_it: " . $r->status_line);
+
+    if (not $r->is_success()) {
+        error_exit("Load: Unexpected response: ", $r->status_line);
+    }
+
+    # Check entry exist.
+    $r->content =~ /<form method="post" action="\.\/edit" .*?>(.*<\/textarea>)/s;
+    my $form_data = $1;
+
+    $form_data =~ /<input type="hidden" name="date" value="(\d\d\d\d\d\d\d\d)">/;
+    my $resp_date = $1;
+
+    if($resp_date ne "$year$month$day") {
+        error_exit("Load: Not exist entry.");
+    }
+    
+    $form_data =~ /<input class="field" name="title" .*?value="(.*?)">/;
+    my $title = $1 . "\n";
+    $form_data =~ /<textarea .*?>(.*)<\/textarea>/s;
+    my $body = $1;
+    
+    if ($enable_encode and ($c_code ne $s_code)) {
+        Encode::from_to($title,$s_code,$c_code);
+        Encode::from_to($body,$s_code,$c_code);
+    }
+    # Save entry to file.
+    my $datename = "$year-$month-$day";
+    
+    # Check if file is exist.
+    if(-f "$datename.txt") {
+        my $bakext = 0;
+        while(-f "$datename.$bakext") {
+            $bakext++;
+        }
+        if (not rename("$datename.txt", "$datename.$bakext")) {
+            error_exit("$!:$datename.txt");
+        }
+    }
+    
+    if (not open(OUT, ">$datename.txt")) {
+        error_exit("$!:$datename.txt");
+    }
+    
+    print OUT $title;
+    print OUT $body;
+    close(OUT);
+    
+    print_debug("load_it: returns 1 (OK).");
+    return 1;
+}
+
+
 sub create_it($$$) {
     my ($year, $month, $day) = @_;
 
@@ -521,10 +657,12 @@
 # Read title and body.
 sub read_title_body($) {
     my ($file) = @_;
+    my $title;
+    my $body;
 
     # Execute filter command, if any.
     my $input = $file;
-    if ($filter_command) {
+    if ( (!$enable_encode) and $filter_command) {
         $input = sprintf("$filter_command |", $file);
     }
     print_debug("read_title_body: input: $input");
@@ -535,6 +673,11 @@
     chomp($title);
     my $body = join('', <FILE>); # rest of all.
     close(FILE);
+    
+    if($enable_encode and ($c_code ne $s_code)) {
+        Encode::from_to($title,$c_code,$s_code);
+        Encode::from_to($body,$c_code,$s_code);
+    }
     return($title, $body);
 }
 
@@ -601,6 +744,8 @@
     -g groupname    Groupname. Specify groupname.
     -f filename     File. Send only this file without checking timestamp.
     -M              Do NOT replace *t* with current time.
+    -l date         Load entry. Specify date.
+    -n conffile     Config file. Specify conffile.
 
 Config file example:
 #
@@ -610,6 +755,9 @@
 password:yourpassword
 cookie:cookie.txt
 # g:yourgroup
+# httpproxy:proxy
+# c_code:client_encoding
+# s_code:server_encoding
 ## for Unix.
 # filter:iconv -f euc-jp -t utf-8 %s
 EOD
@@ -640,6 +788,15 @@
         } elsif (/^filter:(.*)$/) {
             $filter_command = $1;
             print_debug("load_config: filter:$filter_command");
+        } elsif (/^proxy:(.*)$/) {
+            $httpproxy = $1;
+            print_debug("load_config: proxy:$httpproxy");
+        } elsif (/^c_code:(.*)$/) {
+            $c_code = $1;
+            print_debug("load_config: c_code:$c_code");
+        } elsif (/^s_code:(.*)$/) {
+            $s_code = $1;
+            print_debug("load_config: s_code:$s_code");
         } else {
             error_exit("Unknown option '$_' in $config_file.");
         }

あ〜っ、ほんとにperlは組むの楽だ…。本気でhatena-diary-modeのエンジンとして使っちゃおうかと考えてしまう。form-data形式のPOSTデータを構築するライブラリ作っててキレそうなんだもの…。
だれかAPEL/FLIM/SEMIをxyzzyに移植してやろうという猛者はいないものか…。