#-- # rbmh/context.rb : rbmhshow 0.4.2 # # Copyright (C) 2004--2005 Merlin Hughes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #++ # RBMH context require 'rmail/parser' require 'rbmh/handler' require 'rbmh/rmail' require 'rbmh/pager' require 'rbmh/prompter' require 'rbmh/shower' require 'rbmh/suffixes' class MyStreamHandler < RMail::Parser::Handler def initialize(prefix, context) super() @prefix = prefix @context = context end attr_reader :prefix def preprocess_part @parts.last.context = @context @parts.last.parent = @parts[@parts.length - 2] if @parts.length > 1 # for headers during parse... # if count == 1 # emit_headers(part) # end end def body_begin preprocess_part # streaming toc # puts RBMH::ANSI.color("white") + section + RBMH::ANSI.color("bold") + name + RBMH::ANSI.uncolor + " (" + type + ")" super end def multipart_body_begin preprocess_part # streaming toc # puts RBMH::ANSI.color("white") + section + RBMH::ANSI.uncolor + type if @parts.last.header.content_type == "multipart/signed" @parts.last.raw = RBMH::Parser::capture_raw end super end end # class MyStreamHandler # TODO: make parse a method of @parser module RBMH class Exception < Exception end # class Exception class Context def initialize @profile = RBMH::Profile.new(self) @pager = RBMH::Pager.new(self) @prompter = RBMH::Prompter.new(self) @suffixes = RBMH::MimeSuffixes.new(self) @shower = RBMH::Shower.new(self) @handlers = RBMH::Handlers.new(self) @folder = @message = @range_str = @range = @range_index = nil @color_name = RBMH::ANSI.color(*(@profile['rbmh-color-name'] or '').split(' ')) @color_error = RBMH::ANSI.color(*(@profile['rbmh-color-error'] or '').split(' ')) @uncolor = RBMH::ANSI.uncolor @no_scissors = false # awful hack to suppress leading scissors end attr_reader :profile, :pager, :prompter, :suffixes, :handlers, :shower attr_reader :folder, :message, :range_str attr_accessor :no_scissors # sets $FOLDER and $MESSAGE; updates cur, folder, unseen; parses msg def cur() ENV['FOLDER'] = @folder ENV['MESSAGE'] = @message filename = profile.mhpath(@folder, @message) raise RBMH::Exception, "message #{@message} does not exist in folder #{@folder}" unless FileTest.file?(filename) # TODO: Don't redisplay for same message puts "(Message #{@color_name}#{@folder}:#{@message}#{@uncolor})" STDOUT.flush ; STDERR.flush fork { # mark message as seen and set current in child process system "mark #{@message} +#{@folder} -sequence #{profile.unseen_sequence} -delete /dev/null 2>&1" if profile.unseen_sequence exec "folder #{@message} +#{@folder} -nocreate -fast /dev/null 2>&1" } parse(filename) end def show(what) if @message.nil? || what !~ /^(first|prev|cur|next|last)$/ # TODO: show unseen:first shows range unseen with index -> first folder = nil messages = '' what.split.each { |str| if str =~ FolderRE raise RBMH::Exception, "multiple folders in #{what}" if folder folder = $1 else messages << (messages.empty? ? '' : ' ') << str end } folder = `folder -fast /dev/null`.chomp if folder.nil? messages = 'cur' if messages.empty? range = `pick #{messages} +#{folder} /dev/null`.split # TODO: utility fn that captures `` stderr so I can raise it s/pick: // raise RBMH::Exception, "pick #{messages} failed" if $?.exitstatus != 0 @folder = folder @message = range[0] if messages =~ SingleMessageRE @range = @range_index = @range_str = nil else @range = range; @range_index = 0; @range_str = messages end elsif @range.nil? # hack to reset cur so next/prev will work; will fail after rmm # but is only needed if another session changes cur so okay.. # race condition but wtf.. system "folder #{@message} +#{@folder} -nocreate -fast /dev/null 2>&1" if @message && @folder message = `pick #{what} /dev/null`.chomp raise RBMH::Exception, "no #{what}" if $?.exitstatus != 0 @message = message else delta = (what =~ /^(next|first)$/) ? 1 : (what == 'cur') ? 0 : -1 index = (what == 'first') ? -1 : (what == 'last') ? @range.length : @range_index loop do index = index + delta # if you reach the end of unseen, merge in any new ones. if (what == 'next') && (index >= @range.length) && profile.unseen_sequence && profile.unseen_sequence[@range_str] # TODO: match on word boundaries.. # backtrack to after previous extant message while (index > 0) && !FileTest.file?(profile.mhpath(@folder, @range[index - 1])) index = index - 1 end # merge in new unread messages; ignore errors (if no unseen) messages = `pick #{@range_str} +#{@folder} /dev/null`.split @range[index .. -1] = messages # nmh message sequences are always sorted @range.sort!.uniq! # will mess up if new unseen are added early, but wtf end indices = 0 ... @range.length break if indices.include?(index) && FileTest.file?(profile.mhpath(@folder, @range[index])) raise RBMH::Exception, "no #{what} in sequence" if !indices.include?(index) || (delta == 0) end @range_index = index @message = @range[index] end end def parse(source, prefix = "", mime = false) handler = MyStreamHandler.new(prefix, self) if source.is_a?(String) raise RBMH::Exception, "not a file - #{source}" unless FileTest.file?(source) raise RBMH::Exception, "not readable - #{source}" unless FileTest.readable?(source) File.open(source) { |file| parser = RMail::StreamParser.new(file, handler) mime ? parser.parse_mime : parser.parse } handler.message.path = source else parser = RMail::StreamParser.new(source, handler) mime ? parser.parse_mime : parser.parse end handler.message end def error(str, delay = 0.3) puts "#{@color_error}rbmhshow:#{@uncolor} #{str}" sleep(delay) if delay > 0 end FolderRE = /^\+(.+)$/ # should be alpha(alnum*) SingleMessageRE = /^(\d+|cur|(\S+:)?(first|last|next|prev))$/ # 1 | cur | unseen:first end # class Context end # module RBMH