/usr/bin/sup-tweak-labels is in sup-mail 0.22.1-2.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | #!/usr/bin/ruby
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
require 'trollop'
require "sup"
class Float
def to_s; sprintf '%.2f', self; end
def to_time_s
infinite? ? "unknown" : super
end
end
class Numeric
def to_time_s
i = to_i
sprintf "%d:%02d:%02d", i / 3600, (i / 60) % 60, i % 60
end
end
def time
startt = Time.now
yield
Time.now - startt
end
opts = Trollop::options do
version "sup-tweak-labels (sup #{Redwood::VERSION})"
banner <<EOS
Batch modification of message state for messages already in the index.
Usage:
sup-tweak-labels [options] <source>*
where <source>* is zero or more source URIs. Supported source URI schemes can
be seen by running "sup-add --help".
Options:
EOS
opt :add, "One or more labels (comma-separated) to add to every message from the specified sources", :default => ""
opt :remove, "One or more labels (comma-separated) to remove from every message from the specified sources, if those labels are present", :default => ""
opt :query, "A Sup search query", :type => String
text <<EOS
Other options:
EOS
opt :verbose, "Print message ids as they're processed."
opt :very_verbose, "Print message names and subjects as they're processed."
opt :all_sources, "Scan over all sources.", :short => :none
opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
opt :no_sync_back, "Do not sync back to the original Maildir."
opt :version, "Show version information", :short => :none
end
opts[:verbose] = true if opts[:very_verbose]
add_labels = opts[:add].to_set_of_symbols ","
remove_labels = opts[:remove].to_set_of_symbols ","
Trollop::die "nothing to do: no labels to add or remove" if add_labels.empty? && remove_labels.empty?
Redwood::start
index = Redwood::Index.init
index.lock_interactively or exit
begin
index.load
source_ids = if opts[:all_sources]
Redwood::SourceManager.sources
else
ARGV.map do |uri|
Redwood::SourceManager.source_for uri or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?"
end
end.map { |s| s.id }
Trollop::die "nothing to do: no sources" if source_ids.empty?
query = "(" + source_ids.map { |id| "source_id:#{id}" }.join(" OR ") + ")"
if add_labels.empty?
## if all we're doing is removing labels, we can further restrict the
## query to only messages with those labels
query += " (" + remove_labels.map { |l| "label:#{l}" }.join(" OR ") + ")"
end
query += ' AND ' + opts[:query] if opts[:query]
parsed_query = index.parse_query query
parsed_query.merge! :load_spam => true, :load_deleted => true, :load_killed => true
ids = index.to_enum(:each_id, parsed_query)
num_total = index.num_results_for parsed_query
$stderr.puts "Found #{num_total} documents across #{source_ids.length} sources. Scanning..."
num_changed = num_scanned = 0
last_info_time = start_time = Time.now
ids.each do |id|
num_scanned += 1
m = index.build_message id
old_labels = m.labels.dup
m.labels += add_labels
m.labels -= remove_labels
unless m.labels == old_labels
num_changed += 1
puts "From #{m.from}, subject: #{m.subj}" if opts[:very_verbose]
puts "#{m.id}: {#{old_labels.to_a.join ','}} => {#{m.labels.to_a.join ','}}" if opts[:verbose]
puts if opts[:very_verbose]
unless opts[:dry_run]
index.update_message_state [m, false]
m.sync_back unless opts[:no_sync_back]
end
end
if Time.now - last_info_time > 60
last_info_time = Time.now
elapsed = last_info_time - start_time
pctdone = 100.0 * num_scanned.to_f / num_total.to_f
remaining = (100.0 - pctdone) * (elapsed.to_f / pctdone)
$stderr.puts "## #{num_scanned} (#{pctdone}%) read; #{elapsed.to_time_s} elapsed; #{remaining.to_time_s} remaining"
end
end
$stderr.puts "Scanned #{num_scanned} / #{num_total} messages and changed #{num_changed}."
unless num_changed == 0
$stderr.puts "Optimizing index..."
index.optimize unless opts[:dry_run]
end
rescue Exception => e
File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
raise
ensure
index.save
Redwood::finish
index.unlock
end
|