get rid of old SVN structure
This commit is contained in:
parent
64eaf7c89f
commit
08e83501be
324 changed files with 0 additions and 70245 deletions
529
news/newsrc.rb
Normal file
529
news/newsrc.rb
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
# $Dwarf: newsrc.rb,v 1.13 2004/06/16 08:16:58 ward Exp $
|
||||
# $Source$
|
||||
|
||||
#
|
||||
# Copyright (c) 2002, 2003 Ward Wouts <ward@wouts.nl>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
require Pathname.new(__FILE__).dirname + "../set/intspan"
|
||||
#require "thread"
|
||||
|
||||
module News
|
||||
|
||||
class Newsrc
|
||||
|
||||
#@@save_lock = Mutex.new
|
||||
|
||||
def initialize(file=nil)
|
||||
@newsrc = { "group" => Hash.new, "list" => Array.new }
|
||||
if file
|
||||
unless load(file)
|
||||
puts "Can't load #{file}"
|
||||
exit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load(file=nil)
|
||||
file = "#{ENV['HOME']}/.newsrc" unless file
|
||||
@newsrc["file"] = file
|
||||
@newsrc["group"] = {}
|
||||
@newsrc["list"] = []
|
||||
|
||||
if FileTest.file?( "#{file}" ) and FileTest.readable?( "#{file}" )
|
||||
lines = IO.readlines("#{file}")
|
||||
import_rc(lines)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def import_rc(lines)
|
||||
@newsrc["group"] = {}
|
||||
@newsrc["list"] = []
|
||||
linenumber = 1
|
||||
lines.each{|line|
|
||||
parse(line)
|
||||
}
|
||||
end
|
||||
|
||||
def parse(line)
|
||||
unless line =~ /^([^!:]+)([!:])\s(.*)$/x
|
||||
puts "Newsrc.parse: Bad newsrc line: #{line}"
|
||||
exit
|
||||
end
|
||||
|
||||
name = $1
|
||||
mark = $2
|
||||
articles = $3
|
||||
|
||||
unless Set::IntSpan.valid(articles)
|
||||
puts "Newsrc.parse: Bad article list: #{line}"
|
||||
end
|
||||
|
||||
|
||||
group = { "name" => name, "subscribed" => (mark == ":"),
|
||||
"articles" => Set::IntSpan.new(articles)}
|
||||
|
||||
@newsrc["group"][name] = group
|
||||
@newsrc["list"].push(group)
|
||||
end
|
||||
|
||||
def save
|
||||
unless @newsrc.has_key?("file")
|
||||
@newsrc["file"] = "#{$ENV['HOME']}/.newsrc"
|
||||
end
|
||||
save_as(@newsrc["file"])
|
||||
end
|
||||
|
||||
# this is not thread safe! (well, it should be now)
|
||||
def save_as(file)
|
||||
# @@save_lock.synchronize{
|
||||
if FileTest.exists?("#{file}")
|
||||
begin
|
||||
FileUtils.mv(file, "#{file}.bak")
|
||||
rescue
|
||||
puts "Can't rename #{file}, #{file}.bak: #{$!}"
|
||||
exit
|
||||
end
|
||||
end
|
||||
begin
|
||||
newsrc = File.new(file, "w")
|
||||
newsrc.flock(File::LOCK_EX)
|
||||
rescue
|
||||
puts "Can't open #{file}: #{$!}"
|
||||
exit
|
||||
end
|
||||
@newsrc["file"] = file
|
||||
@newsrc["list"].each{|group|
|
||||
newsrc.print format(group)
|
||||
}
|
||||
newsrc.sync
|
||||
newsrc.flock(File::LOCK_UN) # what's the right order here?
|
||||
newsrc.close
|
||||
# }
|
||||
end
|
||||
|
||||
# Here 'group' is a group structure. It'd probably be much more useful if
|
||||
# it could just be a group_name_; which it can now.
|
||||
def save_group(group)
|
||||
unless @newsrc.has_key?("file")
|
||||
@newsrc["file"] = "#{$ENV['HOME']}/.newsrc"
|
||||
end
|
||||
if group.class.to_s == "String"
|
||||
groupname = group.dup
|
||||
@newsrc["list"].each{|g|
|
||||
if g["name"] == groupname
|
||||
group = g.dup
|
||||
break
|
||||
end
|
||||
}
|
||||
|
||||
end
|
||||
save_group_as(@newsrc["file"], group)
|
||||
end
|
||||
|
||||
# This should be thread safe, but may not be. It needs testing!
|
||||
# If not, mutexes are needed.
|
||||
def save_group_as(file, group)
|
||||
# @@save_lock.synchronize{
|
||||
#p Time.now
|
||||
#p "copy file"
|
||||
if FileTest.exists?("#{file}")
|
||||
begin
|
||||
FileUtils.copy(file, "#{file}.bak")
|
||||
rescue
|
||||
puts "Can't copy #{file} to #{file}.bak: #{$!}"
|
||||
end
|
||||
end
|
||||
#p Time.now
|
||||
#p "open & lock file"
|
||||
begin
|
||||
if FileTest.exists?("#{file}")
|
||||
newsrc = File.new(file, "r+")
|
||||
else
|
||||
newsrc = File.new(file, "w")
|
||||
end
|
||||
newsrc.flock(File::LOCK_EX)
|
||||
rescue
|
||||
puts "Can't open #{file}: #{$!}"
|
||||
exit
|
||||
end
|
||||
#p Time.now
|
||||
#p "opened & locked"
|
||||
|
||||
# read file
|
||||
lines = newsrc.readlines
|
||||
|
||||
# pointer -> 0
|
||||
newsrc.rewind
|
||||
|
||||
group_saved = false
|
||||
# write read stuff & replace group
|
||||
lines.each{|line|
|
||||
# same parsing as the parse method uses
|
||||
unless line =~ /^([^!:]+)([!:])\s(.*)$/x
|
||||
puts "Newsrc.parse: Bad newsrc line: #{line}"
|
||||
# restore backup on failure, it'll contain the flaw too, but it'll
|
||||
# be complete
|
||||
begin
|
||||
FileUtils.copy("#{file}.bak", file)
|
||||
rescue
|
||||
puts "Can't copy #{file}.bak to #{file}: #{$!}"
|
||||
end
|
||||
exit
|
||||
end
|
||||
linegroup = $1
|
||||
if linegroup == group["name"]
|
||||
newsrc.print format(group)
|
||||
group_saved = true
|
||||
else
|
||||
newsrc.print line
|
||||
end
|
||||
}
|
||||
if ! group_saved
|
||||
newsrc.print format(group)
|
||||
end
|
||||
|
||||
#p Time.now
|
||||
#p "truncate, sync, unlock & close file"
|
||||
# sometimes the file grows and then shrinks
|
||||
# this is because a 'read' line van become shorter when more
|
||||
# articles have been read (1,3,5 vs 1-5)
|
||||
# when this happens the file needs to be truncated
|
||||
pos = newsrc.pos
|
||||
newsrc.truncate(pos)
|
||||
newsrc.sync
|
||||
newsrc.flock(File::LOCK_UN) # what's the right order here?
|
||||
newsrc.close
|
||||
#p Time.now
|
||||
#p "garbage collect"
|
||||
#p Time.now
|
||||
GC.start
|
||||
# }
|
||||
end
|
||||
|
||||
def format(group)
|
||||
name = group["name"]
|
||||
sub = group["subscribed"] ? ':' : '!'
|
||||
articles = group["articles"].run_list
|
||||
return "#{name}#{sub} #{articles}\n"
|
||||
end
|
||||
|
||||
def export_rc
|
||||
lines = @newsrc["list"].collect{ |group|
|
||||
name = group["name"]
|
||||
sub = group["subscribed"] ? ':' : '!'
|
||||
articles = group["articles"].run_list
|
||||
space = articles ? ' ' : ''
|
||||
"#{name}#{sub}#{space}#{articles}\n" }
|
||||
return lines
|
||||
end
|
||||
|
||||
def add_group(name, options)
|
||||
|
||||
if @newsrc["group"].has_key?(name)
|
||||
options.has_key?("replace") or return false
|
||||
del_group(name)
|
||||
end
|
||||
group = {"name" => name,
|
||||
"subscribed" => true,
|
||||
"articles" => Set::IntSpan.new }
|
||||
|
||||
@newsrc["group"][name] = group
|
||||
_insert(group, options)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def move_group(name, options)
|
||||
if @newsrc["group"].has_key?(name)
|
||||
group = @newsrc["group"][name]
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
@newsrc["list"] = @newsrc["list"].delete_if{|x| x["name"] == name}
|
||||
|
||||
_insert(group, options)
|
||||
return true
|
||||
end
|
||||
|
||||
def _insert(group, options)
|
||||
list = @newsrc["list"]
|
||||
|
||||
where = ""
|
||||
arg = ""
|
||||
if options.has_key?("where")
|
||||
where = options["where"]
|
||||
end
|
||||
arg = where.slice!(1) if where.class.to_s == "Array"
|
||||
|
||||
case where.to_s
|
||||
when "first"
|
||||
@newsrc["list"].unshift(group)
|
||||
when "last"
|
||||
@newsrc["list"].push(group)
|
||||
when ""
|
||||
@newsrc["list"].push(group) # default
|
||||
when "alpha"
|
||||
alpha(group)
|
||||
when "before"
|
||||
before(group, arg)
|
||||
when "after"
|
||||
after(group, arg)
|
||||
when "number"
|
||||
number(group, arg)
|
||||
end
|
||||
end
|
||||
|
||||
def alpha (group)
|
||||
name = group["name"]
|
||||
(0...@newsrc["list"].length).each{|i|
|
||||
if ((name <=> @newsrc["list"][i]["name"]) == -1)
|
||||
upper = @newsrc["list"].slice!(i..@newsrc["list"].length)
|
||||
@newsrc["list"].push(group)
|
||||
@newsrc["list"].push(upper)
|
||||
return;
|
||||
end
|
||||
}
|
||||
@newsrc["list"].push(group)
|
||||
end
|
||||
|
||||
def before(group, before)
|
||||
name = group["name"]
|
||||
(0...@newsrc["list"].length).each{|i|
|
||||
if (@newsrc["list"][i]["name"] == before.to_s)
|
||||
upper = @newsrc["list"].slice!(i..@newsrc["list"].length)
|
||||
@newsrc["list"].push(group)
|
||||
@newsrc["list"].push(upper)
|
||||
|
||||
return;
|
||||
end
|
||||
}
|
||||
|
||||
@newsrc["list"].push(group)
|
||||
end
|
||||
|
||||
def after(group, after)
|
||||
name = group["name"]
|
||||
|
||||
(0...@newsrc["list"].length).each{|i|
|
||||
if (@newsrc["list"][i]["name"] == after.to_s)
|
||||
upper = @newsrc["list"].slice!((i+1)..@newsrc["list"].length)
|
||||
@newsrc["list"].push(group)
|
||||
@newsrc["list"].push(upper)
|
||||
return;
|
||||
end
|
||||
}
|
||||
|
||||
@newsrc["list"].push(group)
|
||||
end
|
||||
|
||||
def number(group, offset)
|
||||
offset = @newsrc["list"].length if offset[0] > @newsrc["list"].length
|
||||
upper = @newsrc["list"].slice!(offset..@newsrc["list"].length)
|
||||
@newsrc["list"].push(group)
|
||||
@newsrc["list"].push(upper)
|
||||
end
|
||||
|
||||
def del_group(name)
|
||||
if @newsrc["group"].has_key?(name)
|
||||
group = @newsrc["group"][name]
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
@newsrc["group"].delete(name)
|
||||
@newsrc["list"] = @newsrc["list"].delete_if{|x| x["name"] == name}
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def subscribe(name, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
@newsrc["group"][name]["subscribed"] = true
|
||||
end
|
||||
|
||||
def unsubscribe(name, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
@newsrc["group"][name]["subscribed"] = false
|
||||
end
|
||||
|
||||
def mark(name, article, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
@newsrc["group"][name]["articles"].insert!(article)
|
||||
end
|
||||
|
||||
def mark_list(name, list, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
articles = @newsrc["group"][name]["articles"].union(list)
|
||||
@newsrc["group"][name]["articles"] = articles
|
||||
end
|
||||
|
||||
def mark_range(name, from, to, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
range = Set::IntSpan.new("#{from}-#{to}")
|
||||
articles = @newsrc["group"][name]["articles"].union(range)
|
||||
@newsrc["group"][name]["articles"] = articles
|
||||
end
|
||||
|
||||
def unmark(name, article, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
@newsrc["group"][name]["articles"].remove(article)
|
||||
end
|
||||
|
||||
def unmark_list(name, list, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
articles = @newsrc["group"][name]["articles"].diff(list)
|
||||
@newsrc["group"][name]["articles"] = articles
|
||||
end
|
||||
|
||||
def unmark_range(name, from, to, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
range = Set::IntSpan.new("#{from}-#{to}")
|
||||
articles = @newsrc["group"][name]["articles"].diff(range)
|
||||
@newsrc["group"][name]["articles"] = articles
|
||||
end
|
||||
|
||||
def exists(name)
|
||||
return @newsrc["group"].has_key?(name) ? true : false
|
||||
end
|
||||
|
||||
def subscribed(name)
|
||||
exists(name) and @newsrc["group"][name]["subscribed"]
|
||||
end
|
||||
|
||||
def marked(name, article)
|
||||
exists(name) and @newsrc["group"][name]["articles"].member?(article)
|
||||
end
|
||||
|
||||
def num_groups
|
||||
return @newsrc["list"].length
|
||||
end
|
||||
|
||||
def groups
|
||||
list = @newsrc["list"].dup
|
||||
list.collect!{|x| x["name"]}
|
||||
end
|
||||
|
||||
def sub_groups
|
||||
list = @newsrc["list"].dup
|
||||
list.collect!{|x| x["subscribed"] ? x["name"] : nil}.compact!
|
||||
end
|
||||
|
||||
def unsub_groups
|
||||
list = @newsrc["list"].dup
|
||||
list.collect!{|x| x["subscribed"] ? nil : x["name"]}.compact!
|
||||
end
|
||||
|
||||
def marked_articles(name, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
return @newsrc["group"][name]["articles"].elements
|
||||
end
|
||||
|
||||
def unmarked_articles(name, from, to, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
range = Set::IntSpan.new("#{from}-#{to}")
|
||||
return range.diff(@newsrc["group"][name]["articles"]).elements
|
||||
end
|
||||
|
||||
def get_articles(name, options = {"where" => ""})
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
@newsrc["group"][name]["articles"].run_list
|
||||
end
|
||||
|
||||
def set_articles(name, articles, options = {"where" => ""})
|
||||
Set::IntSpan.valid(articles) or return false
|
||||
set = Set::IntSpan.new(articles)
|
||||
set.finite or return false
|
||||
min = set.min
|
||||
min != nil and min < 0 and return false
|
||||
unless @newsrc["group"].has_key?(name)
|
||||
add_group(name, options)
|
||||
end
|
||||
@newsrc["group"][name]["articles"] = set
|
||||
return true
|
||||
end
|
||||
|
||||
end # class
|
||||
|
||||
end # module
|
||||
|
||||
|
||||
# TODO
|
||||
# Do not kill an item until it's tested!
|
||||
|
||||
# [x] new
|
||||
# [x] load
|
||||
# [ ] _scan # Initializes a Newsrc object from a string. Used for testing.
|
||||
# [x] import_rc
|
||||
# [x] parse # parses a single line from a newsrc file
|
||||
# [x] save
|
||||
# [x] save_as
|
||||
# [ ] save_group
|
||||
# [ ] save_group_as
|
||||
# [x] format
|
||||
# [x] export_rc
|
||||
# [ ] _dump # Formats a Newsrc object to a string. Used for testing
|
||||
# [x] add_group
|
||||
# [x] move_group
|
||||
# [x] Splice(\@$$@) # heet nu number en is simpeler
|
||||
# [x] _insert
|
||||
# [x] Alpha
|
||||
# [x] Before
|
||||
# [x] After
|
||||
# [x] del_group
|
||||
# [x] subscribe
|
||||
# [x] unsubscribe
|
||||
# [x] mark
|
||||
# [x] mark_list
|
||||
# [x] mark_range
|
||||
# [x] unmark
|
||||
# [x] unmark_list
|
||||
# [x] unmark_range
|
||||
# [x] exists
|
||||
# [x] subscribed
|
||||
# [x] marked
|
||||
# [x] num_groups
|
||||
# [x] groups
|
||||
# [x] sub_groups
|
||||
# [x] unsub_groups
|
||||
# [x] marked_articles
|
||||
# [x] unmarked_articles
|
||||
# [x] get_articles
|
||||
# [x] set_articles
|
||||
Loading…
Add table
Add a link
Reference in a new issue