#!@@MHC_RUBY_PATH@@ -Ke # -*- ruby -*- ## adb2mhc -- Convert ADB DB of HP200LX into MHC format. ## ## Author: Yoshinari Nomura ## ## Created: 1999/11/12 ## Revised: 2003/02/24 13:20:45 ## require 'mhc-date' require 'mhc-schedule' require 'kconv' class Appt WEEK = %w(Mon Tue Wed Thu Fri Sat Sun) MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) ORDER = %w(1st 2nd 3rd 4th last) REPEAT = %w(none daily weekly monthly yearly custom) def appt?; !todo? ; end def todo?; @appt_or_todo_flag & 16 != 0; end ################################################################ def initialize(raw_record) record_length, # 0 2 record length. category_offset, # 2 2 offset to category data in this record. location_offset, # 4 2 offset to location data in this record. repeat_offset, # 6 2 offset to repeat data in this record. @linked_note_number, # 8 2 linked note record no. previous_record_number, #10 2 always -1 (or 65535) ? next_record_number, #12 2 always -1 (or 65535) ? appt_or_todo_flag, #14 1 appointment or todo data. start_date, #15 3 packed 3 bytes (year-1900,month-1,day-1) difference, #18 8 used differently appoint from todo. repeat_status, #26 1 1:none, 2:daily, 4:monthly ... #27 * include description, category, location, # repeatinfo. trailer = raw_record .unpack("vvvvvvvCa3a8Ca*") @repeat_status = bit_to_string(repeat_status, REPEAT) @appt_or_todo_flag = appt_or_todo_flag @start_date = bit_to_date(start_date) @description, ## @category, ## @location, ## repeat_info = trailer .unpack(format("A%d A%d A%d a%d", category_offset - 27, location_offset - category_offset, repeat_offset - location_offset, record_length - repeat_offset)) # AppointOrTodoFlag is used like bellow # # Bit Appointment ToDo # # 0(LSB) Alarm Don't care # 1 MonthView Completed # 2 WeekView Carry forward # 3 # 4 This-record-is-TODO. # 5 This-record-is-Appointment. # if todo? ## The record is todo. @todo_priority , ## @todo_due_days, ## complete_date = difference .unpack("A2va3x") @todo_completed = (@appt_or_todo_flag & 2 != 0) @todo_complete_date = bit_to_date(complete_date) else ## The record is appointment start_time, #18 2 minute of the start time. hour is minute/60. consecutive, #20 2 no of consecutive days. end_time, #22 2 minute of the end time. hour is minute/60. #24 2 lead time in minute. appt_lead_time = difference .unpack("vvvv") @appt_lead_time = @appt_or_todo_flag & 1 != 0 ? appt_lead_time : nil @appt_start_time = bit_to_time(start_time) @appt_consecutive = consecutive + 1 @appt_end_time = bit_to_time(end_time) end ################################################################ ## unpack repeat info. if @repeat_status == 'none' return self end repeat_freqency, ## repeat_param1, ## repeat_param2, ## repeat_month, ## repeat_start_date, ## repeat_end_date, ## repeat_delete_count, ## repeat_delete_dates = repeat_info .unpack("cCCva3a3ca*") if @repeat_status != 'custom' @repeat_freqency = repeat_freqency end if repeat_param1 & 0x80 != 0 ## repeat by order and week. @repeat_day = bit_to_tag(repeat_param1, WEEK) @repeat_week = bit_to_tag(repeat_param2, ORDER) else ## repeat by day number. @repeat_day = repeat_param1 ## param2 is abandoned. end @repeat_month = bit_to_tag(repeat_month, MONTH) @repeat_start_date = bit_to_date(repeat_start_date) @repeat_end_date = bit_to_date(repeat_end_date) if repeat_delete_count > 0 len = repeat_delete_dates .length ary= repeat_delete_dates .unpack("a3x" * (len / 4)) @repeat_delete_dates = ary .collect{|a| bit_to_date(a)} end end def dump print "linked_note_number #{@linked_note_number} \n" ## Integer print "appt_or_todo_flag #{@appt_or_todo_flag} \n" ## Bitfield print "description #{@description} \n" ## String print "category #{@category} \n" ## String print "location #{@location} \n" ## String print "start_date #{@start_date} \n" ## MhcDate print "appt_consecutive #{@appt_consecutive} \n" ## Integer print "appt_start_time #{@appt_start_time} \n" ## MhcTime print "appt_end_time #{@appt_end_time} \n" ## MhcTime print "appt_lead_time #{@appt_lead_time} \n" ## Integer print "repeat_status #{@repeat_status} \n" ## String one of 'none', 'weekly' ... print "repeat_freqency #{@repeat_freqency} \n" ## Integer print "repeat_start_date #{@repeat_start_date} \n" ## MhcDate print "repeat_end_date #{@repeat_end_date} \n" ## MhcDate print "repeat_month #{@repeat_month} \n" ## Array of String 'Jan', 'Feb' ... print "repeat_week #{@repeat_week} \n" ## Array of String 'Sun', 'Mon' ... print "repeat_day #{@repeat_day} \n" ## Integer print "repeat_delete_dates #{@repeat_delete_dates} \n" ## Integer print "todo_complete_date #{@todo_complete_date} \n" ## MhcDate print "todo_due_days #{@todo_due_days} \n" ## Integer print "todo_priority #{@todo_priority} \n" ## String print "\n\n" end def to_mhc if todo? STDERR .print "Warn: Todo records are not supported... ignored.\n" return nil end if (@repeat_status && @repeat_status != 'daily') and (@repeat_freqency && @repeat_freqency > 1) STDERR .print "Warn: Records such as freqency > 1 are not supported... ignored.\n" return nil end if @description =~ /\|/ STDERR .print "Warn: Cron job is not supported... ignored.\n" return nil end ## X-SC-Subject: @description ## X-SC-Category: @category ## X-SC-Location: @location ## X-SC-Day: !@repeat_delete_date + ## @start_date if @repeat_status = 'none' ## X-SC-Time: @appt_start_time - @appt_end_time or '' ## X-SC-Cond: @repeat_month @repeat_week @repeat_day ## X-SC-Duration: @repeat_start_date - @repeat_end_date or '' x_sc_subject = Kconv::tojis(@description) x_sc_category = Kconv::tojis(@category) x_sc_alarm = @appt_lead_time ? @appt_lead_time .to_s + ' minute' : '' x_sc_time = @appt_start_time ? [@appt_start_time, @appt_end_time] .join('-') : '' x_sc_day = '' if @repeat_status == 'daily' && @repeat_freqency i = @repeat_start_date while (i <= @repeat_end_date) x_sc_day += "#{i} " i = i .succ(@repeat_freqency) end elsif @repeat_status == 'none' x_sc_day = @start_date .to_s end x_sc_day += ' ' + (@repeat_delete_dates ? @repeat_delete_dates .collect{|date| '!' + date .to_s} .join(' ') : '') x_sc_cond = ((@repeat_month || []) + (@repeat_week || []) + (@repeat_day || [])) .join(' ') x_sc_duration = (@repeat_start_date || @repeat_end_date) ? "#{@repeat_start_date}-#{@repeat_end_date}" : '' header = [ "X-SC-Subject: " + x_sc_subject, "X-SC-Category: " + x_sc_category, "X-SC-Day: " + x_sc_day, "X-SC-Time: " + x_sc_time, "X-SC-Cond: " + x_sc_cond, "X-SC-Alarm: " + x_sc_alarm, "X-SC-Duration: " + x_sc_duration ] .join("\n") + "\n" MhcScheduleItem .new(header, false) end ################################################################ ## private private def bit_to_tag(bit, tag) ret, i = [], 1 tag .each{|str| if bit & i != 0 ret << str end i <<= 1 } return ret end def bit_to_string(bit, tag) ret, i = [], 1 tag .each{|str| return str if bit & i != 0 i <<= 1 } return nil end def bit_to_time(bit) if bit == 65535 return nil else return MhcTime .new(bit / 60, bit % 60) end end def bit_to_date(bit) y, m, d = bit .unpack("CCC") return MhcDate .new(y + 1900, m + 1, d + 1) end end def usage(do_exit = true) STDERR .print "usage: adb2mhc [options] adb_files... Convert ADB DB of HP200LX into MHC format. --help show this message. --mhc-dir set repository dir of MHC. it is good idea to set empty dir. default: ~/Mail/schedule\n" exit if do_exit end ################################################################ ## main while ARGV[0] =~ /^-/ case (ARGV[0]) when /^--mhc-dir=(.+)/ mhc_dir = $1 ARGV .shift else usage() end end mhc_db = MhcScheduleDB .new(mhc_dir || File .expand_path("~/Mail/schedule")) adb_file = File .open(ARGV[0]) db_type = adb_file .read(4) .chop if db_type != 'hcD' STDERR .print "Error: Unknown file type #{db_type}... aborted.\n" exit end while (header = adb_file .read(6)) type, status, length, no = header .unpack("CCvv") exit if length <= 6 if ((field = adb_file .read(length - 6)) .length != length - 6) STDERR .print "Error: ADB file format error... aborted.\n" exit end next if status & 0x01 != 0 # This field is obsolete. ## print "#{type}, #{status}, #{length}, #{no}\n" case type when 9 STDERR .print "Warn: Attached note records are not supported... ignored.\n" ## Note reocrd. next when 11 appt_item = Appt .new(field) mhc_sch = appt_item .to_mhc if mhc_sch print "----------------------------------------------\n" print mhc_sch .dump mhc_db .add_sch(mhc_sch) end else next end end ### Copyright Notice: ## Copyright (C) 1999, 2000 Yoshinari Nomura. All rights reserved. ## Copyright (C) 2000 MHC developing team. All rights reserved. ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions ## are met: ## ## 1. Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## 2. 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. ## 3. Neither the name of the team 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 TEAM 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 TEAM 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. ### adb2mhc ends here