]> git.cworth.org Git - sup/commitdiff
Merge branch 'ncurses-fixes' into next
authorWilliam Morgan <wmorgan-sup@masanjin.net>
Sun, 16 Aug 2009 22:00:10 +0000 (18:00 -0400)
committerWilliam Morgan <wmorgan-sup@masanjin.net>
Sun, 16 Aug 2009 22:00:10 +0000 (18:00 -0400)
18 files changed:
Manifest.txt
bin/sup
bin/sup-add
bin/sup-sync
bin/sup-sync-back
bin/sup-tweak-labels
lib/sup.rb
lib/sup/buffer.rb
lib/sup/ferret_index.rb
lib/sup/hook.rb
lib/sup/index.rb
lib/sup/interactive-lock.rb [new file with mode: 0644]
lib/sup/mbox/loader.rb
lib/sup/message-chunks.rb
lib/sup/message.rb
lib/sup/suicide.rb [deleted file]
lib/sup/util.rb
lib/sup/xapian_index.rb

index be633d776e2b1e5165edeac4d7631cf714471207..09d867eb47595a3ad8a1a7e693734e9f201cb983 100644 (file)
@@ -32,6 +32,7 @@ lib/sup/index.rb
 lib/sup/keymap.rb
 lib/sup/label.rb
 lib/sup/logger.rb
+lib/sup/interactive-lock.rb
 lib/sup/maildir.rb
 lib/sup/mbox.rb
 lib/sup/mbox/loader.rb
@@ -67,7 +68,6 @@ lib/sup/poll.rb
 lib/sup/rfc2047.rb
 lib/sup/sent.rb
 lib/sup/source.rb
-lib/sup/suicide.rb
 lib/sup/tagger.rb
 lib/sup/textfield.rb
 lib/sup/thread.rb
diff --git a/bin/sup b/bin/sup
index b87c11223342aefe977b34e99f053ba548a4fc6d..a9f0b95686d567067e7ce0f873d0b372779da91c 100755 (executable)
--- a/bin/sup
+++ b/bin/sup
@@ -130,37 +130,14 @@ end
 module_function :start_cursing, :stop_cursing
 
 Index.new
-begin
-  Index.lock
-rescue Index::LockError => e
-  require 'highline'
-
-  h = HighLine.new
-  h.wrap_at = :auto
-  h.say Index.fancy_lock_error_message_for(e)
-
-  case h.ask("Should I ask that process to kill itself? ")
-  when /^\s*y(es)?\s*$/i
-    h.say "Ok, suggesting seppuku..."
-    FileUtils.touch Redwood::SUICIDE_FN
-    sleep SuicideManager::DELAY * 2
-    FileUtils.rm_f Redwood::SUICIDE_FN
-    h.say "Let's try that again."
-    retry
-  else
-    h.say <<EOS
-Ok, giving up. If the process crashed and left a stale lockfile, you
-can fix this by manually deleting #{Index.lockfile}.
-EOS
-    exit
-  end
-end
+Index.lock_interactively or exit
 
 begin
   Redwood::start
   Index.load
 
-  trap("TERM") { |x| SuicideManager.please_die! }
+  $die = false
+  trap("TERM") { |x| $die = true }
   trap("WINCH") { |x| BufferManager.sigwinch_happened! }
 
   if(s = Redwood::SourceManager.source_for DraftManager.source_name)
@@ -212,7 +189,6 @@ begin
 
   unless $opts[:no_threads]
     PollManager.start
-    SuicideManager.start
     Index.start_lock_update_thread
   end
 
@@ -220,7 +196,7 @@ begin
     SearchResultsMode.spawn_from_query $opts[:search]
   end
 
-  until Redwood::exceptions.nonempty? || SuicideManager.die?
+  until Redwood::exceptions.nonempty? || $die
     c = begin
       Ncurses.nonblocking_getch
     rescue Interrupt => e
@@ -245,15 +221,16 @@ begin
 
     bm.erase_flash
 
-    action = begin
-      if bm.handle_input c
+    action =
+      begin
+        if bm.handle_input c
+          :nothing
+        else
+          bm.resolve_input_with_keymap c, global_keymap
+        end
+      rescue InputSequenceAborted
         :nothing
-      else
-        bm.resolve_input_with_keymap c, global_keymap
       end
-    rescue InputSequenceAborted
-      :nothing
-    end
     case action
     when :quit_now
       break if bm.kill_all_buffers_safely
@@ -319,13 +296,12 @@ begin
     bm.draw_screen
   end
 
-  bm.kill_all_buffers if SuicideManager.die?
+  bm.kill_all_buffers if $die
 rescue Exception => e
   Redwood::record_exception e, "main"
 ensure
   unless $opts[:no_threads]
     PollManager.stop if PollManager.instantiated?
-    SuicideManager.stop if PollManager.instantiated?
     Index.stop_lock_update_thread
   end
 
@@ -335,7 +311,7 @@ ensure
   stop_cursing
   Redwood::log "stopped cursing"
 
-  if SuicideManager.instantiated? && SuicideManager.die?
+  if $die
     Redwood::log "I've been ordered to commit seppuku. I obey!"
   end
 
index 3ab7c4db0a18bf4bdb07bf2b9b7f9af375172e78..64d2890596cd4d5465d57bbc7e8aafdeb2c0c2a5 100755 (executable)
@@ -79,7 +79,7 @@ $terminal.wrap_at = :auto
 Redwood::start
 index = Redwood::Index.new
 
-index.lock_or_die
+index.lock_interactively or exit
 
 begin
   Redwood::SourceManager.load_sources
index 8e37c745873865ccb710ca4bdea68694f02bc590..6ac2429f45c633660c054c2165cc51c0994b39dd 100755 (executable)
@@ -57,7 +57,7 @@ EOS
   opt :changed, "Scan over the entire source for messages that have been deleted, altered, or moved from another source. (In the case of mbox sources, this includes all messages AFTER an altered message.)"
   opt :restored, "Operate only on those messages included in a dump file as specified by --restore which have changed state."
   opt :all, "Operate on all messages in the source, regardless of newness or changedness."
-  opt :start_at, "For --changed and --all, start at a particular offset.", :type => :int
+  opt :start_at, "For --changed, --restored and --all, start at a particular offset.", :type => :int
 
 text <<EOS
 
@@ -86,7 +86,7 @@ end
 Trollop::die :restored, "requires --restore" if opts[:restored] unless opts[:restore]
 if opts[:start_at]
   Trollop::die :start_at, "must be non-negative" if opts[:start_at] < 0
-  Trollop::die :start_at, "requires either --changed or --all" unless opts[:changed] || opts[:all]
+  Trollop::die :start_at, "requires either --changed, --restored or --all" unless opts[:changed] || opts[:restored] || opts[:all]
 end
 
 target = [:new, :changed, :all, :restored].find { |x| opts[x] } || :new
@@ -111,7 +111,7 @@ restored_state =
   end
 
 seen = {}
-index.lock_or_die
+index.lock_interactively or exit
 begin
   index.load
 
index 56ac4eb51c05545e8879c976fcbf8b9c4d695c65..6e66c6b646519afb1b5865a40e336520768ce64d 100755 (executable)
@@ -66,7 +66,7 @@ end
 
 Redwood::start
 index = Redwood::Index.new
-index.lock_or_die
+index.lock_interactively or exit
 
 deleted_fp, spam_fp = nil
 unless opts[:dry_run]
index 8ae5c26f9054c841ad612bdaf29c79f300cab753..eac300ebf866e90ef1f7ac1328713e87126331d6 100755 (executable)
@@ -58,10 +58,12 @@ add_labels = (opts[:add] || "").split(",").map { |l| l.intern }.uniq
 remove_labels = (opts[:remove] || "").split(",").map { |l| l.intern }.uniq
 
 Trollop::die "nothing to do: no labels to add or remove" if add_labels.empty? && remove_labels.empty?
+Trollop::die "no sources specified" if ARGV.empty?
 
 Redwood::start
+index = Redwood::Index.new
+index.lock_interactively or exit
 begin
-  index = Redwood::Index.new
   index.load
 
   source_ids = 
index 54de73f5597f1e2f752f07a00694d39de547880b..cfa93fc6608c13c6c0dd508c0c99267f6bdcb1f6 100644 (file)
@@ -114,7 +114,6 @@ module Redwood
     Redwood::DraftManager.new Redwood::DRAFT_DIR
     Redwood::UpdateManager.new
     Redwood::PollManager.new
-    Redwood::SuicideManager.new Redwood::SUICIDE_FN
     Redwood::CryptoManager.new
     Redwood::UndoManager.new
     Redwood::SourceManager.new
@@ -256,7 +255,6 @@ end
 
 ## now everything else (which can feel free to call Redwood::log at load time)
 require "sup/update"
-require "sup/suicide"
 require "sup/message-chunks"
 require "sup/message"
 require "sup/source"
@@ -266,6 +264,7 @@ require "sup/imap"
 require "sup/person"
 require "sup/account"
 require "sup/thread"
+require "sup/interactive-lock"
 require "sup/index"
 require "sup/textfield"
 require "sup/colormap"
index 7605e52d9275ec3e713d4faeb47c3c5d25ee581c..09281e9f5823bf99f609a103a949a5cf98c2c7b7 100644 (file)
@@ -25,6 +25,8 @@ module Ncurses
   def mutex; @mutex ||= Mutex.new; end
   def sync &b; mutex.synchronize(&b); end
 
+  ## magically, this stuff seems to work now. i could swear it didn't
+  ## before. hm.
   def nonblocking_getch
     ## INSANTIY
     ## it is NECESSARY to wrap Ncurses.getch in a select() otherwise all
@@ -70,7 +72,7 @@ class Buffer
   def content_height; @height - 1; end
   def content_width; @width; end
 
-  def resize rows, cols
+  def resize rows, cols 
     return if cols == @width && rows == @height
     @width = cols
     @height = rows
index f3d414770212e090d0ce03ff9b58a5a07df45233..98ea9b571806167cd5d3db69ad169714405278a0 100644 (file)
@@ -4,6 +4,16 @@ module Redwood
 
 class FerretIndex < BaseIndex
 
+  HookManager.register "custom-search", <<EOS
+Executes before a string search is applied to the index,
+returning a new search string.
+Variables:
+  subs: The string being searched. Be careful about shadowing:
+    this variable is actually a method, so use a temporary variable
+    or explicitly call self.subs; the substitutions in index.rb
+    don't actually work.
+EOS
+
   def initialize dir=BASE_DIR
     super
 
@@ -327,7 +337,9 @@ class FerretIndex < BaseIndex
   def parse_query s
     query = {}
 
-    subs = s.gsub(/\b(to|from):(\S+)\b/) do
+    subs = HookManager.run("custom-search", :subs => s) || s
+
+    subs = subs.gsub(/\b(to|from):(\S+)\b/) do
       field, name = $1, $2
       if(p = ContactManager.contact_for(name))
         [field, p.email]
index 0a0a2f672fca775e4bb792896537d2ec2535fac3..33a97b26063bffceb9f3d600962918b46106aa34 100644 (file)
@@ -19,6 +19,11 @@ class HookManager
 
     attr_writer :__locals
 
+    ## an annoying gotcha here is that if you try something
+    ## like var = var.foo(), var will magically get allocated
+    ## to Nil and method_missing will never get called.  You
+    ## can work around this by calling self.var or simply
+    ## not assigning it to itself.
     def method_missing m, *a
       case @__locals[m]
       when Proc
index fb46eb053a02b871923e93281021441414e11683..54ec84325224bce3a0ff75dbd695daf42c3e2b98 100644 (file)
@@ -13,6 +13,8 @@ end
 module Redwood
 
 class BaseIndex
+  include InteractiveLock
+
   class LockError < StandardError
     def initialize h
       @h = h
@@ -54,42 +56,6 @@ class BaseIndex
     @lock_update_thread = nil
   end
 
-  def possibly_pluralize number_of, kind
-    "#{number_of} #{kind}" +
-        if number_of == 1 then "" else "s" end
-  end
-
-  def fancy_lock_error_message_for e
-    secs = (Time.now - e.mtime).to_i
-    mins = secs / 60
-    time =
-      if mins == 0
-        possibly_pluralize secs , "second"
-      else
-        possibly_pluralize mins, "minute"
-      end
-
-    <<EOS
-Error: the sup index is locked by another process! User '#{e.user}' on
-host '#{e.host}' is running #{e.pname} with pid #{e.pid}. The process was alive
-as of #{time} ago.
-EOS
-  end
-
-  def lock_or_die
-    begin
-      lock
-    rescue LockError => e
-      $stderr.puts fancy_lock_error_message_for(e)
-      $stderr.puts <<EOS
-
-You can wait for the process to finish, or, if it crashed and left a
-stale lock file behind, you can manually delete #{@lock.path}.
-EOS
-      exit
-    end
-  end
-
   def unlock
     if @lock && @lock.locked?
       Redwood::log "unlocking #{lockfile}..."
diff --git a/lib/sup/interactive-lock.rb b/lib/sup/interactive-lock.rb
new file mode 100644 (file)
index 0000000..92a5ead
--- /dev/null
@@ -0,0 +1,74 @@
+require 'fileutils'
+
+module Redwood
+
+## wrap a nice interactive layer on top of anything that has a #lock method
+## which throws a LockError which responds to #user, #host, #mtim, #pname, and
+## #pid.
+
+module InteractiveLock
+  def pluralize number_of, kind; "#{number_of} #{kind}" + (number_of == 1 ? "" : "s") end
+
+  def time_ago_in_words time
+    secs = (Time.now - time).to_i
+    mins = secs / 60
+    time = if mins == 0
+      pluralize secs, "second"
+    else
+      pluralize mins, "minute"
+    end
+  end
+
+  DELAY = 5 # seconds
+
+  def lock_interactively stream=$stderr
+    begin
+      Index.lock
+    rescue Index::LockError => e
+      stream.puts <<EOS
+Error: the index is locked by another process! User '#{e.user}' on
+host '#{e.host}' is running #{e.pname} with pid #{e.pid}.
+The process was alive as of at least #{time_ago_in_words e.mtime} ago.
+
+EOS
+      stream.print "Should I ask that process to kill itself (y/n)? "
+      stream.flush
+
+      success = if $stdin.gets =~ /^\s*y(es)?\s*$/i
+        stream.puts "Ok, trying to kill process..."
+
+        begin
+          Process.kill "TERM", e.pid.to_i
+          sleep DELAY
+        rescue Errno::ESRCH # no such process
+          stream.puts "Hm, I couldn't kill it."
+        end
+
+        stream.puts "Let's try that again."
+        begin
+          Index.lock
+        rescue Index::LockError => e
+          stream.puts "I couldn't lock the index. The lockfile might just be stale."
+          stream.print "Should I just remove it and continue? (y/n) "
+          stream.flush
+
+          if $stdin.gets =~ /^\s*y(es)?\s*$/i
+            FileUtils.rm e.path
+
+            stream.puts "Let's try that one more time."
+            begin
+              Index.lock
+              true
+            rescue Index::LockError => e
+            end
+          end
+        end
+      end
+
+      stream.puts "Sorry, couldn't unlock the index." unless success
+      success
+    end
+  end
+end
+
+end
index 831c71705ed11f8dab9a781c323ff2e5b2fd04d3..ea277cf0f80ffa41397484a92a6940cb468dd0a2 100644 (file)
@@ -113,7 +113,7 @@ class Loader < Source
     need_blank = File.exists?(@filename) && !File.zero?(@filename)
     File.open(@filename, "a") do |f|
       f.puts if need_blank
-      f.puts "From #{from_email} #{date}"
+      f.puts "From #{from_email} #{date.utc}"
       yield f
     end
   end
index 0d742d99e746a9ce96a4c11eddcd57506fcae22f..705f15eb4b0ccd152811a65e8d1dbe4761962cf4 100644 (file)
@@ -131,9 +131,9 @@ EOS
     def initial_state; :open end
     def viewable?; @lines.nil? end
     def view_default! path
-      cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}' 2>/dev/null"
+      cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}'"
       Redwood::log "running: #{cmd.inspect}"
-      system cmd
+      BufferManager.shell_out(cmd)
       $? == 0
     end
 
@@ -208,13 +208,25 @@ EOS
 
   class EnclosedMessage
     attr_reader :lines
-    def initialize from, body
-      @from = from
-      @lines = body.split "\n"
-    end
+    def initialize from, to, cc, date, subj
+      @from = from ? "unknown sender" : from.full_adress
+      @to = to ? "" : to.map { |p| p.full_address }.join(", ")
+      @cc = cc ? "" : cc.map { |p| p.full_address }.join(", ")
+      if date
+        @date = date.rfc822
+      else
+        @date = ""
+      end
 
-    def from
-      @from ? @from.longname : "unknown sender"
+      @subj = subj
+
+      @lines = "\nFrom: #{from}\n"
+      @lines += "To: #{to}\n"
+      if !cc.empty?
+        @lines += "Cc: #{cc}\n"
+      end
+      @lines += "Date: #{date}\n"
+      @lines += "Subject: #{subj}\n\n"
     end
 
     def inlineable?; false end
@@ -224,7 +236,7 @@ EOS
     def viewable?; false end
 
     def patina_color; :generic_notice_patina_color end
-    def patina_text; "Begin enclosed message from #{from} (#{@lines.length} lines)" end
+    def patina_text; "Begin enclosed message sent on #{@date}" end
 
     def color; :quote_color end
   end
index 7c6374651e379ea0947caf7072390bba6d981eb3..29999863ebbfa8bf73eb2ce1e26a6a7e838ce99d 100644 (file)
@@ -404,10 +404,19 @@ private
       chunks
     elsif m.header.content_type == "message/rfc822"
       payload = RMail::Parser.read(m.body)
-      from = payload.header.from.first
-      from_person = from ? Person.from_address(from.format) : nil
-      [Chunk::EnclosedMessage.new(from_person, payload.to_s)] +
-        message_to_chunks(payload, encrypted)
+      from = payload.header.from.first ? payload.header.from.first.format : ""
+      to = payload.header.to.map { |p| p.format }.join(", ")
+      cc = payload.header.cc.map { |p| p.format }.join(", ")
+      subj = payload.header.subject
+      subj = subj ? Message.normalize_subj(payload.header.subject.gsub(/\s+/, " ").gsub(/\s+$/, "")) : subj
+      if Rfc2047.is_encoded? subj
+        subj = Rfc2047.decode_to $encoding, subj
+      end
+      msgdate = payload.header.date
+      from_person = from ? Person.from_address(from) : nil
+      to_people = to ? Person.from_address_list(to) : nil
+      cc_people = cc ? Person.from_address_list(cc) : nil
+      [Chunk::EnclosedMessage.new(from_person, to_people, cc_people, msgdate, subj)] + message_to_chunks(payload, encrypted)
     else
       filename =
         ## first, paw through the headers looking for a filename
diff --git a/lib/sup/suicide.rb b/lib/sup/suicide.rb
deleted file mode 100644 (file)
index 53f31a5..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-module Redwood
-
-class SuicideManager
-  include Singleton
-
-  DELAY = 5
-
-  def initialize fn
-    @fn = fn
-    @die = false
-    @thread = nil
-    self.class.i_am_the_instance self
-    FileUtils.rm_f @fn
-  end
-
-  bool_reader :die
-  def please_die!; @die = true end
-
-  def start
-    @thread = Redwood::reporting_thread("suicide watch") do
-      while true
-        sleep DELAY
-        if File.exists? @fn
-          FileUtils.rm_f @fn
-          @die = true
-        end
-      end
-    end
-  end
-
-  def stop
-    @thread.kill if @thread
-    @thread = nil
-  end
-end
-
-end
index 3f2c901a160823c4229d922fd1c052fa42e6a428..f71094e4d8120bba2d649abc4e3f2465a8977d8d 100644 (file)
@@ -24,6 +24,7 @@ class Lockfile
   def lockinfo_on_disk
     h = load_lock_id IO.read(path)
     h['mtime'] = File.mtime path
+    h['path'] = path
     h
   end
 
index 5a5dfc1225f1580019fe6a8ff032113a7e364b0f..861c2a3a7fc1618343da4f8ac51d62e36bd4993f 100644 (file)
@@ -20,14 +20,16 @@ class XapianIndex < BaseIndex
     super
 
     @index_mutex = Monitor.new
+  end
 
-    @entries = MarshalledGDBM.new File.join(dir, "entries.db")
-    @docids = MarshalledGDBM.new File.join(dir, "docids.db")
-    @thread_members = MarshalledGDBM.new File.join(dir, "thread_members.db")
-    @thread_ids = MarshalledGDBM.new File.join(dir, "thread_ids.db")
-    @assigned_docids = GDBM.new File.join(dir, "assigned_docids.db")
+  def load_index
+    @entries = MarshalledGDBM.new File.join(@dir, "entries.db")
+    @docids = MarshalledGDBM.new File.join(@dir, "docids.db")
+    @thread_members = MarshalledGDBM.new File.join(@dir, "thread_members.db")
+    @thread_ids = MarshalledGDBM.new File.join(@dir, "thread_ids.db")
+    @assigned_docids = GDBM.new File.join(@dir, "assigned_docids.db")
 
-    @xapian = Xapian::WritableDatabase.new(File.join(dir, "xapian"), Xapian::DB_CREATE_OR_OPEN)
+    @xapian = Xapian::WritableDatabase.new(File.join(@dir, "xapian"), Xapian::DB_CREATE_OR_OPEN)
     @term_generator = Xapian::TermGenerator.new()
     @term_generator.stemmer = Xapian::Stem.new(STEM_LANGUAGE)
     @enquire = Xapian::Enquire.new @xapian
@@ -35,9 +37,6 @@ class XapianIndex < BaseIndex
     @enquire.docid_order = Xapian::Enquire::ASCENDING
   end
 
-  def load_index
-  end
-
   def save_index
   end