First add of ripnews to cvs
This commit is contained in:
parent
bde9d157e2
commit
05e19814d6
3 changed files with 1750 additions and 0 deletions
305
trunk/ripnews/news/newsrc.rb
Normal file
305
trunk/ripnews/news/newsrc.rb
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
#################################
|
||||
#
|
||||
# newsrc.rb
|
||||
# ported from Perl code by Ward Wouts
|
||||
# this software is released under the terms of
|
||||
# the GNU Library General Public License
|
||||
#
|
||||
# (C) 2001, Ward Wouts
|
||||
#
|
||||
#################################
|
||||
|
||||
require "set/intspan"
|
||||
|
||||
module News
|
||||
|
||||
class Newsrc
|
||||
|
||||
def initialize(file=nil)
|
||||
@newsrc = { "group" => Hash.new, "list" => Array.new }
|
||||
if file
|
||||
unless load(file)
|
||||
print "Can't load #{file}\n"
|
||||
exit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load(file=nil)
|
||||
file = "#{ENV['HOME']}/.newsrc" unless file
|
||||
@newsrc["file"] = file
|
||||
@newsrc["group"] = {}
|
||||
@newsrc["list"] = []
|
||||
|
||||
lines = IO.readlines("#{file}")
|
||||
import_rc(lines)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def import_rc(lines)
|
||||
@newsrc["group"] = {}
|
||||
@newsrc["list"] = []
|
||||
linenumber = 1
|
||||
for line in lines
|
||||
parse(line)
|
||||
end
|
||||
end
|
||||
|
||||
def parse(line)
|
||||
unless line =~ /^([^!:]+)([!:])\s(.*)$/x
|
||||
print "Newsrc.parse: Bad newsrc line: #{line}\n"
|
||||
exit
|
||||
end
|
||||
|
||||
name = $1
|
||||
mark = $2
|
||||
articles = $3
|
||||
|
||||
unless Set::IntSpan.valid(articles)
|
||||
print "Newsrc.parse: Bad article list: #{line}\n"
|
||||
end
|
||||
|
||||
|
||||
group = { "name" => name, "subscribed" => (mark == ":"),
|
||||
"articles" => Set::IntSpan.new(articles)}
|
||||
|
||||
@newsrc["group"][name] = group
|
||||
@newsrc["list"] += [ group ]
|
||||
end
|
||||
|
||||
def save
|
||||
unless @newsrc.has_key?("file")
|
||||
@newsrc["file"] = "#{$ENV['HOME']}/.newsrc"
|
||||
end
|
||||
save_as(@newsrc["file"])
|
||||
end
|
||||
|
||||
def save_as(file)
|
||||
if FileTest.exists?("#{file}")
|
||||
begin
|
||||
File.rename(file, "#{file}.bak")
|
||||
rescue
|
||||
print "Can't rename #{file}, #{file}.bak: #{$!}\n"
|
||||
exit
|
||||
end
|
||||
end
|
||||
begin
|
||||
newsrc = File.new(file, "w")
|
||||
rescue
|
||||
print "Can't open #{file}: #{$!}\n"
|
||||
exit
|
||||
end
|
||||
@newsrc["file"] = file
|
||||
for group in @newsrc["list"]
|
||||
newsrc.print format(group)
|
||||
end
|
||||
end
|
||||
|
||||
def format(group)
|
||||
name = group["name"]
|
||||
sub = group["subscribed"] ? ':' : '!'
|
||||
articles = group["articles"].run_list
|
||||
space = articles ? ' ' : ''
|
||||
return "#{name}#{sub}#{space}#{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" => 1,
|
||||
"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.type.to_s == "Array"
|
||||
|
||||
case where.to_s
|
||||
when "first"
|
||||
@newsrc["list"].unshift(group)
|
||||
when "last"
|
||||
@newsrc["list"] += [ group ]
|
||||
when ""
|
||||
@newsrc["list"] += [ group ] # default
|
||||
when "alpha"
|
||||
alpha(group)
|
||||
when "before"
|
||||
print "metsj\n"
|
||||
before(group, arg)
|
||||
when "after"
|
||||
after(group, arg)
|
||||
when "number"
|
||||
number(group, arg)
|
||||
end
|
||||
end
|
||||
|
||||
def alpha (group)
|
||||
name = group["name"]
|
||||
for i in (0...@newsrc["list"].length)
|
||||
print @newsrc["list"][i]["name"],"\n"
|
||||
if ((name <=> @newsrc["list"][i]["name"]) == -1)
|
||||
# splice @$list, $i, 0, $group
|
||||
upper = @newsrc["list"].slice!(i..@newsrc["list"].length)
|
||||
@newsrc["list"] += [ group ]
|
||||
@newsrc["list"] += upper
|
||||
return;
|
||||
end
|
||||
end
|
||||
@newsrc["list"] += [group]
|
||||
end
|
||||
|
||||
def before(group, before)
|
||||
print "before\n"
|
||||
name = group["name"]
|
||||
|
||||
|
||||
for i in (0...@newsrc["list"].length)
|
||||
print @newsrc["list"][i]["name"],"\n"
|
||||
if (@newsrc["list"][i]["name"] == before.to_s)
|
||||
print @newsrc["list"][i]["name"],"\n"
|
||||
upper = @newsrc["list"].slice!(i..@newsrc["list"].length)
|
||||
@newsrc["list"] += [ group ]
|
||||
@newsrc["list"] += upper
|
||||
|
||||
return;
|
||||
end
|
||||
end
|
||||
|
||||
@newsrc["list"] += [group]
|
||||
end
|
||||
|
||||
def after(group, after)
|
||||
name = group["name"]
|
||||
|
||||
for i in (0...@newsrc["list"].length)
|
||||
if (@newsrc["list"][i]["name"] == after.to_s)
|
||||
print @newsrc["list"][i+1]["name"],"\n"
|
||||
upper = @newsrc["list"].slice!((i+1)..@newsrc["list"].length)
|
||||
@newsrc["list"] += [ group ]
|
||||
@newsrc["list"] += upper
|
||||
return;
|
||||
end
|
||||
end
|
||||
|
||||
@newsrc["list"] += [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"] += [ group ]
|
||||
@newsrc["list"] += 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
|
||||
|
||||
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
|
||||
# [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
|
||||
# [ ] mark
|
||||
# [ ] mark_list
|
||||
# [ ] mark_range
|
||||
# [ ] unmark
|
||||
# [ ] unmark_list
|
||||
# [ ] unmark_range
|
||||
# [ ] exists
|
||||
# [ ] subscribed
|
||||
# [ ] marked
|
||||
# [ ] num_groups
|
||||
# [ ] groups
|
||||
# [ ] sub_groups
|
||||
# [ ] unsub_groups
|
||||
# [ ] marked_articles
|
||||
# [ ] unmarked_articles
|
||||
# [ ] get_articles
|
||||
# [ ] set_articles
|
||||
519
trunk/ripnews/ripnews.rb
Executable file
519
trunk/ripnews/ripnews.rb
Executable file
|
|
@ -0,0 +1,519 @@
|
|||
#!/usr/local/bin/ruby
|
||||
|
||||
require 'net/nntp'
|
||||
require 'news/newsrc'
|
||||
require 'date'
|
||||
require 'getoptlong'
|
||||
|
||||
class Articles
|
||||
|
||||
Debuglevel = 1
|
||||
|
||||
def initialize(server)
|
||||
@ids = []
|
||||
@subjects = []
|
||||
@sorted = false
|
||||
@grouped = false
|
||||
@agroups = {}
|
||||
@nntp = Net::NNTP.new(server)
|
||||
end
|
||||
|
||||
def add(id, subject)
|
||||
@ids += [id]
|
||||
@subjects += [subject]
|
||||
@sorted = false
|
||||
@grouped = false
|
||||
end
|
||||
|
||||
def get_articles(group)
|
||||
resp, count, first, last,name = @nntp.group(group)
|
||||
for i in (first.to_i..last.to_i)
|
||||
begin
|
||||
@nntp.stat(i)
|
||||
resp, nr, messid, list = @nntp.head(i)
|
||||
for j in list
|
||||
if j =~ /Subject: (.*)/
|
||||
subj=$1
|
||||
end
|
||||
end
|
||||
add(messid, subj)
|
||||
rescue Net::NNTP::RuntimeError
|
||||
print "whoopsie couldn't stat #{i}\n" if Debuglevel > 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_groups
|
||||
group_subjects unless @grouped
|
||||
return @agroups
|
||||
end
|
||||
|
||||
def get_group_body(subj)
|
||||
result = []
|
||||
for i in @agroups[subj][1..@agroups[subj].length]
|
||||
resp, nr, id, list = @nntp.body(i)
|
||||
result = list
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
def get_group_body_first(subj)
|
||||
resp, nr, id, list = @nntp.body(@agroups[subj][1])
|
||||
print "getting article: #{subj}\n" if Debuglevel > 0
|
||||
print "article id: #{id}\n" if Debuglevel > 0
|
||||
return list
|
||||
end
|
||||
|
||||
def get_group_body_rest(subj, file=nil)
|
||||
result = []
|
||||
for i in @agroups[subj][2..@agroups[subj].length]
|
||||
print "getting article: #{i}\n" if Debuglevel > 0
|
||||
resp, nr, id, list = @nntp.body(i)
|
||||
if file
|
||||
for line in list
|
||||
file.print "#{line}\n"
|
||||
end
|
||||
else
|
||||
result += list
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
def get_group_subjects
|
||||
group_subjects unless @grouped
|
||||
return @agroups.keys
|
||||
end
|
||||
|
||||
def group_complete(subj)
|
||||
group_subjects unless @grouped
|
||||
print "length: #{@agroups[subj].length} total: #{@agroups[subj][0].to_i}\n" if Debuglevel > 0
|
||||
if (@agroups[subj].length - 1 ) >= @agroups[subj][0].to_i
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def get_ids
|
||||
return @ids
|
||||
end
|
||||
|
||||
def get_subjects
|
||||
return @subjects
|
||||
end
|
||||
|
||||
def group_subjects
|
||||
@agroups = {}
|
||||
subject_sort unless @sorted
|
||||
prev_subj = ""
|
||||
for i in (0..@subjects.length)
|
||||
if @subjects[i] =~ /(.*)\((\d+)\/(\d+)\)(.*)/ || @subjects[i] =~ /(.*)\[(\d+)\/(\d+)\](.*)/
|
||||
j = "#{$1}#{$4}"
|
||||
number = $2
|
||||
total = $3
|
||||
else
|
||||
j = @subjects[i]
|
||||
number = 1
|
||||
total = 1
|
||||
end
|
||||
if j == prev_subj
|
||||
@agroups[j] += [ @ids[i] ]
|
||||
else
|
||||
unless number == 0
|
||||
prev_subj = j
|
||||
@agroups[j] = [ total, @ids[i] ]
|
||||
end
|
||||
end
|
||||
end
|
||||
@grouped = true
|
||||
end
|
||||
|
||||
def uudecode(data, outfile=nil)
|
||||
case data.type.to_s
|
||||
when "Array"
|
||||
print "Calling _uudecode_array\n" if Debuglevel>0
|
||||
mode, file, body = _uudecode_array(data)
|
||||
when "File"
|
||||
unless outfile
|
||||
print "uudecode: need outfile\n"
|
||||
exit
|
||||
end
|
||||
print "Calling _uudecode_file\n" if Debuglevel>0
|
||||
mode, file, body = _uudecode_file(data, outfile)
|
||||
end
|
||||
return mode, file, body
|
||||
end
|
||||
|
||||
def _uudecode_file(file, outfile)
|
||||
mode = 0600
|
||||
filename = "unknown"
|
||||
c = 0
|
||||
lines = file.pos
|
||||
percent = 0
|
||||
mark = lines/100
|
||||
file.pos=0
|
||||
|
||||
while (! file.eof)
|
||||
line = file.gets
|
||||
print "line: #{line}" if Debuglevel > 0
|
||||
if line =~ /^begin(.*)/
|
||||
m = $1
|
||||
print "beginning matched; rest: #{m}\n"
|
||||
if m =~ /^(\s+(\d+))?(\s+(.*?\S))?\s*\Z/
|
||||
mode = $2
|
||||
filename = $4
|
||||
print "found beginning\n" if Debuglevel > 0
|
||||
else
|
||||
print "mode, file set to defaults: #{m}\n"
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
print "not uuencoded!\n" if file.eof
|
||||
print "c: #{c} mark: #{mark} lines: #{lines}\n" if Debuglevel > 1
|
||||
|
||||
print "UUdecoding...\n"
|
||||
|
||||
while (! file.eof)
|
||||
if Debuglevel > 1
|
||||
c = file.pos
|
||||
if c > mark
|
||||
print "#{percent}%\n"
|
||||
print "c: #{c} mark: #{mark} lines: #{lines}\n" if Debuglevel > 1
|
||||
percent += 1
|
||||
mark = (lines/100)*(percent+1)
|
||||
end
|
||||
end
|
||||
line = file.gets
|
||||
print "line: #{line}" if Debuglevel > 1
|
||||
return mode, filename if line =~ /^end/
|
||||
next if line =~ /[a-z]/
|
||||
next if line == nil
|
||||
next unless ((((line[0] - 32) & 077) + 2) / 3).to_i ==
|
||||
(line.length/4).to_i
|
||||
outfile.print line.unpack("u")
|
||||
end
|
||||
|
||||
print "No \"end\" found!!!\n"
|
||||
return mode, file, outfile
|
||||
|
||||
end
|
||||
|
||||
# gaat volgens mij niet verder als er meerdere uuencoded blocks zijn...
|
||||
# zal dan meerdere keren aangeroepen moeten worden, grmbl...
|
||||
# tis getting a mess as we speak...
|
||||
# toch maar een keer aparte class van maken...
|
||||
def _uudecode_array(data)
|
||||
decode = []
|
||||
# begun = false
|
||||
mode = 0600
|
||||
file = "unknown"
|
||||
c = 0
|
||||
lines = data.length
|
||||
percent = 0
|
||||
mark = lines/100
|
||||
|
||||
i = 0
|
||||
print "data.length #{data.length}\n"
|
||||
while (i < data.length)
|
||||
print "i #{i}\n"
|
||||
if data[i] =~ /^begin(.*)/
|
||||
m = $1
|
||||
print "beginning matched; rest: #{m}\n"
|
||||
if m =~ /^(\s+(\d+))?(\s+(.*?\S))?\s*\Z/
|
||||
mode = $2
|
||||
file = $4
|
||||
print "found beginning\n" if Debuglevel > 0
|
||||
else
|
||||
print "mode, file set to defaults: #{m}\n"
|
||||
end
|
||||
break
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
|
||||
unless (i < data.length)
|
||||
print "not uuencoded!\n"
|
||||
end
|
||||
|
||||
print "UUdecoding...\n"
|
||||
|
||||
while (i < data.length)
|
||||
if Debuglevel > 1
|
||||
if c > mark
|
||||
print "#{percent}%\n"
|
||||
print "c: #{c} mark: #{mark} lines: #{lines} i: #{i}\n" if Debuglevel > 1
|
||||
percent += 1
|
||||
mark = (lines/100)*(percent+1)
|
||||
end
|
||||
c += 1
|
||||
end
|
||||
line = data[i]
|
||||
i += 1
|
||||
return mode, file, decode if line =~ /^end/
|
||||
next if line =~ /[a-z]/
|
||||
next if line == nil
|
||||
next unless ((((line[0] - 32) & 077) + 2) / 3).to_i ==
|
||||
(line.length/4).to_i
|
||||
decode += line.unpack("u")
|
||||
end
|
||||
|
||||
print "No \"end\" found!!!\n"
|
||||
return mode, file, decode
|
||||
end
|
||||
|
||||
def uudecode_group(subj, file=nil, outfile=nil)
|
||||
group_subjects unless @grouped
|
||||
|
||||
body = get_group_body_first(subj)
|
||||
if body.to_s =~ /begin/
|
||||
print "uuencoded!\n" if Debuglevel > 0
|
||||
if (file and outfile)
|
||||
for i in body
|
||||
file.print "#{i}\n"
|
||||
end
|
||||
get_group_body_rest(subj, file)
|
||||
mode, filename, result = uudecode(file, outfile)
|
||||
else
|
||||
body += get_group_body_rest(subj)
|
||||
mode, filename, result = uudecode(body)
|
||||
end
|
||||
return mode, filename, result
|
||||
else
|
||||
print "Not uuencoded!\n" if Debuglevel > 0
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
###############################################################
|
||||
|
||||
def subject_sort
|
||||
sort_arr = []
|
||||
for i in (0..@subjects.length)
|
||||
sort_arr += ["#{@subjects[i]} #{@ids[i]}"]
|
||||
end
|
||||
sort_arr.sort!{|a,b| ward_sort(a, b)}
|
||||
@ids = []
|
||||
@subjects = []
|
||||
for i in sort_arr
|
||||
i =~ /^(.*) (<[^<]*>)$/ || i =~ /^(.*) \[<[^<]*>\]$/
|
||||
@ids += [$2]
|
||||
@subjects += [$1]
|
||||
end
|
||||
@sorted = true
|
||||
end
|
||||
|
||||
def ward_sort(a, b)
|
||||
a =~ /^(.*) (<[^<]*>)$/
|
||||
c = $1.to_s.split(/([0-9]+)/)
|
||||
b =~ /^(.*) (<[^<]*>)$/
|
||||
d = $1.to_s.split(/([0-9]+)/)
|
||||
|
||||
for x in c
|
||||
y = d.shift
|
||||
r = ((x.to_s =~ /^[0-9]+$/) && (y.to_s =~ /^[0-9]+$/)) ?
|
||||
(x.to_i <=> y.to_i) :
|
||||
(x.to_s <=> y.to_s)
|
||||
if r != 0
|
||||
return r
|
||||
end
|
||||
end
|
||||
return -1 if (d)
|
||||
return 0
|
||||
end
|
||||
|
||||
def quit
|
||||
@nntp.quit
|
||||
end
|
||||
|
||||
private :ward_sort
|
||||
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
###########################################################################
|
||||
|
||||
Debuglevel = 1
|
||||
|
||||
def save_file(dir, name, data)
|
||||
print "savename: #{name}\n" if Debuglevel > 1
|
||||
nname = name.gsub(/\//, "-")
|
||||
print "nname: #{nname}\n" if Debuglevel > 1
|
||||
newname = nname
|
||||
count = 1
|
||||
d = Date.today
|
||||
date = "#{d.year}#{d.month}#{d.mday}"
|
||||
while FileTest.exists?("#{dir}/#{newname}")
|
||||
newname = "#{nname}-<#{date}.#{count}>"
|
||||
count += 1
|
||||
end
|
||||
print "name: #{newname}\n" if Debuglevel > 1
|
||||
|
||||
case data.type.to_s
|
||||
when "String"
|
||||
if File.rename(data, "#{dir}/#{newname}")
|
||||
print "Saving: #{newname}\n"
|
||||
else
|
||||
print "couldn't rename tempfile\n"
|
||||
end
|
||||
when "Array"
|
||||
if file = File.new("#{dir}/#{newname}", "w", "0644")
|
||||
print "Saving: #{newname}\n"
|
||||
for i in data
|
||||
file.print "#{i}"
|
||||
end
|
||||
else
|
||||
print "couldn't open file for writeing\n"
|
||||
end
|
||||
else
|
||||
print "EEEEPS\n"
|
||||
end
|
||||
end
|
||||
|
||||
def tmp_file(dir)
|
||||
name = "riptmp"
|
||||
print "tmpname: #{name}\n" if Debuglevel > 1
|
||||
nname = name.gsub(/\//, "-")
|
||||
print "nname: #{nname}\n" if Debuglevel > 1
|
||||
newname = nname
|
||||
count = 1
|
||||
|
||||
while FileTest.exists?("#{dir}/#{newname}")
|
||||
newname = "#{nname}-#{count}"
|
||||
count += 1
|
||||
end
|
||||
print "name: #{newname}\n" if Debuglevel > 1
|
||||
fullname ="#{dir}/#{newname}"
|
||||
if file = File.new(fullname, "w+", "0644")
|
||||
return file, fullname
|
||||
else
|
||||
print "couldn't open tempfile for writeing\n"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def parse_options
|
||||
options = {}
|
||||
|
||||
begin
|
||||
opts = GetoptLong.new(
|
||||
[ "-I", "--include", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "-c", "--configfile", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "-L", "--longname", GetoptLong::NO_ARGUMENT ],
|
||||
[ "-S", "--singlepart", GetoptLong::NO_ARGUMENT ],
|
||||
[ "-g", "--greedy", GetoptLong::NO_ARGUMENT ]
|
||||
)
|
||||
opts.quiet=true
|
||||
|
||||
opts.each do |opt, arg|
|
||||
options[opt] = arg
|
||||
end
|
||||
|
||||
rescue
|
||||
print "#{opts.error_message}\n"
|
||||
print "\nUsage:\n"
|
||||
exit
|
||||
end
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
def read_config(options)
|
||||
unless options.has_key?("-c")
|
||||
options["-c"]=".ripnewsrc"
|
||||
end
|
||||
begin
|
||||
config = IO.readlines("#{options[\"-c\"]}")
|
||||
for i in config
|
||||
if i =~ /^OPT_(.)=?(.*)$/
|
||||
options["-#{$1}"]=$2 unless
|
||||
options.has_key?("-#{$1}")
|
||||
else
|
||||
i =~ /([^=]*)=(.*)/
|
||||
options["#{$1}"]=$2
|
||||
end
|
||||
print "#{$1}=", options["#{$1}"], "\n" if Debuglevel > 1
|
||||
end
|
||||
rescue
|
||||
print "Coudn't open config file: #{options[\"-c\"]}\n"
|
||||
exit
|
||||
end
|
||||
return options
|
||||
end
|
||||
|
||||
def check_options(options)
|
||||
if (Debuglevel > 1)
|
||||
for i in options.keys
|
||||
print "Opt: #{i} Value: #{options[i]}\n"
|
||||
end
|
||||
end
|
||||
unless options.has_key?("-I")
|
||||
print "No inclusions given. Won't match anything.\n"
|
||||
exit
|
||||
end
|
||||
unless options.has_key?("DATADIR")
|
||||
options["DATADIR"] ="."
|
||||
end
|
||||
# exit # zeer tijdelijk voor snel testen
|
||||
end
|
||||
|
||||
options = {}
|
||||
options = parse_options
|
||||
options = read_config(options)
|
||||
check_options(options)
|
||||
|
||||
if Debuglevel > 1
|
||||
for i in options.keys
|
||||
print "Opt: #{i} val: #{options[i]}\n"
|
||||
end
|
||||
end
|
||||
|
||||
articles = Articles.new(options["NNTPSERVER"])
|
||||
articles.get_articles("alt.binaries.e-book.flood")
|
||||
|
||||
|
||||
for i in articles.get_group_subjects
|
||||
if i =~ /#{options["-I"]}/i
|
||||
print "Match: #{i}\n" if Debuglevel > 0
|
||||
if articles.group_complete(i)
|
||||
print "Complete: #{i}\n" if Debuglevel > 0
|
||||
if options.has_key?("TMPDIR")
|
||||
file, fullname = tmp_file(options["TMPDIR"])
|
||||
fileout, fullnameout = tmp_file(options["TMPDIR"])
|
||||
else
|
||||
file=nil
|
||||
fullname = ""
|
||||
fileout=nil
|
||||
fullnameout = ""
|
||||
end
|
||||
mode, filename, body = articles.uudecode_group(i, file, fileout)
|
||||
|
||||
if file
|
||||
file.close
|
||||
fileout.close
|
||||
body = fullnameout
|
||||
end
|
||||
|
||||
if mode
|
||||
print "mode: #{mode}\n" if Debuglevel > 0
|
||||
print "filename: #{filename}\n"
|
||||
if options.has_key?("-L")
|
||||
print "longname\n" if Debuglevel > 1
|
||||
save_file(options["DATADIR"], i, body)
|
||||
else
|
||||
print "shortname\n" if Debuglevel > 1
|
||||
save_file(options["DATADIR"], filename, body)
|
||||
end
|
||||
end
|
||||
# rm file & fileout
|
||||
File.delete(fullname)
|
||||
else
|
||||
print "Not complete: #{i}\n"
|
||||
end
|
||||
print "\n"
|
||||
end
|
||||
end
|
||||
|
||||
articles.quit
|
||||
926
trunk/ripnews/set/intspan.rb
Normal file
926
trunk/ripnews/set/intspan.rb
Normal file
|
|
@ -0,0 +1,926 @@
|
|||
#################################
|
||||
#
|
||||
# intspan.rb
|
||||
# ported from Perl code by Ward Wouts
|
||||
# this software is released under the terms of
|
||||
# the GNU Library General Public License
|
||||
#
|
||||
# (C) 2001, Ward Wouts
|
||||
#
|
||||
#################################
|
||||
|
||||
module Set
|
||||
|
||||
class IntSpan
|
||||
|
||||
Empty_String = '-'
|
||||
Debuglevel = 0
|
||||
|
||||
def initialize(setspec=nil)
|
||||
@set = { "empty_string" => Empty_String }
|
||||
print "initialize: Calling copy\n" if Debuglevel > 0
|
||||
copy(setspec)
|
||||
end
|
||||
|
||||
def IntSpan.valid(run_list)
|
||||
testset = new
|
||||
begin
|
||||
testset._copy_run_list(run_list)
|
||||
rescue SystemExit
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def copy(set_spec)
|
||||
print "Copy #{set_spec.type.to_s}\n" if Debuglevel > 0
|
||||
case set_spec.type.to_s
|
||||
when "NilClass"
|
||||
print "copy: Calling _copy_empty\n" if Debuglevel > 0
|
||||
_copy_empty
|
||||
when "String"
|
||||
print "copy: Calling _copy_run_list\n" if Debuglevel > 0
|
||||
_copy_run_list(set_spec)
|
||||
when "Array"
|
||||
print "copy: Calling _copy_array\n" if Debuglevel > 0
|
||||
_copy_array(set_spec)
|
||||
when "Set::Intspan"
|
||||
print "copy: Calling _copy_set\n"
|
||||
_copy_set(set_spec)
|
||||
when "Hash"
|
||||
print "copy: Calling _copy_set\n"
|
||||
_copy_set(set_spec)
|
||||
else
|
||||
print "eeps\n"
|
||||
end
|
||||
end
|
||||
|
||||
def _copy_empty # makes @set the empty set
|
||||
@set = { "negInf" => false }
|
||||
@set["posInf"] = false
|
||||
@set["edges"] = []
|
||||
@set["run"] = []
|
||||
end
|
||||
|
||||
def _copy_array(array) # copies an array into @set
|
||||
@set["negInf"] = false
|
||||
@set["posInf"] = false
|
||||
|
||||
print "scary thingy gets called!!!\n"
|
||||
edges = []
|
||||
for element in array.sort
|
||||
next if edges and edges[-1] == element; # skip duplicates
|
||||
|
||||
if (edges and edges[-1] == $element-1)
|
||||
edges[-1] = $element;
|
||||
else
|
||||
push @edges, $element-1, $element;
|
||||
end
|
||||
end
|
||||
|
||||
@set["edges"] = edges
|
||||
@set["run"] = []
|
||||
end
|
||||
|
||||
def _copy_set(src) # copies one set to another
|
||||
@set["negInf"] = src.neg_inf
|
||||
@set["posInf"] = src.pos_inf
|
||||
@set["edges"] = src.edges
|
||||
@set["run"] = []
|
||||
end
|
||||
|
||||
def _copy_run_list(runlist)
|
||||
|
||||
_copy_empty
|
||||
|
||||
runlist.gsub!(/\s|_/, '')
|
||||
return true if runlist == ""
|
||||
|
||||
|
||||
print "copy run list...\n" if Debuglevel > 0
|
||||
|
||||
first = true
|
||||
last = false
|
||||
|
||||
edges = []
|
||||
|
||||
for i in runlist.split(/,/)
|
||||
print "#{i}\n" if Debuglevel > 0
|
||||
begin
|
||||
if i =~ /^(-?\d+)$/x
|
||||
edges += [ ($1.to_i-1), $1.to_i ]
|
||||
next
|
||||
end
|
||||
|
||||
if i =~ /^ (-?\d+) - (-?\d+) $/x
|
||||
if $1.to_i > $2.to_i
|
||||
print "match rule 1 #{$1} > #{$2}\n"
|
||||
print "Set::IntSpan::_copy_run_list: Bad order: #{runlist}\n"
|
||||
exit
|
||||
else
|
||||
edges += [ ($1.to_i-1), $2.to_i ]
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
if i =~ /^\(-(-?\d+)$/x
|
||||
unless first
|
||||
print "match rule 2\n"
|
||||
print "Set::IntSpan::_copy_run_list: Bad order: #{runlist}\n"
|
||||
exit
|
||||
end
|
||||
@set = {"negInf" => true}
|
||||
edges += [ $1.to_i ]
|
||||
next
|
||||
end
|
||||
|
||||
if i =~ /^(-?\d+)-\)$/x
|
||||
print "match rule 3\n"
|
||||
edges += [ ($1.to_i-1) ]
|
||||
@set = {"posInf" => true}
|
||||
last = true
|
||||
next
|
||||
end
|
||||
|
||||
if i =~ /^\(-\)$/x
|
||||
unless first
|
||||
print "match rule 4\n"
|
||||
print "Set::IntSpan::_copy_run_list: Bad order: #{runlist}\n"
|
||||
exit
|
||||
end
|
||||
@set = {"negInf" => true}
|
||||
@set = {"posInf" => true}
|
||||
last = true
|
||||
next
|
||||
end
|
||||
|
||||
print "no match! \"#{i}\"\n"
|
||||
print "Set::IntSpan::_copy_run_list: Bad syntax: #{runlist}\n"
|
||||
end
|
||||
first = false
|
||||
end
|
||||
|
||||
@set["edges"] = edges
|
||||
@set["run"] = []
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# check for overlapping runs
|
||||
# delete duplicate edges
|
||||
def _cleanup
|
||||
edges = @set["edges"]
|
||||
|
||||
for i in (0..(edges.length-1))
|
||||
cmp = edges[i] <=> edges[i+1];
|
||||
begin
|
||||
case cmp
|
||||
when -1
|
||||
i = i + 1
|
||||
break
|
||||
when 0
|
||||
edges.slice!(i..(i+1))
|
||||
break
|
||||
when 1
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
1
|
||||
end
|
||||
|
||||
#def splice(array, offset, length=nil, list=[])
|
||||
# if offset >= 0
|
||||
# length = array.length-offset unless length
|
||||
# leftarray = array.slice(0, offset)
|
||||
# rightarray = array.slice(offset+length, (array.length - offset))
|
||||
# else
|
||||
# length = array.length+offset unless length
|
||||
# leftarray = array.slice(0, (array.length+offset))
|
||||
# rightarray = array.slice(array.length+length+offset, array.length+offset)
|
||||
# end
|
||||
#
|
||||
# array = leftarray
|
||||
# array += list
|
||||
# array += rightarray if rightarray
|
||||
#
|
||||
# return array
|
||||
#end
|
||||
|
||||
def run_list
|
||||
if empty
|
||||
return @set["empty_string"]
|
||||
end
|
||||
|
||||
print "edges leng: ", @set["edges"].length, "\n" if Debuglevel > 0
|
||||
edges = []
|
||||
edges = @set["edges"]
|
||||
runs = []
|
||||
|
||||
if edges.length > 0
|
||||
edges = ['(', edges] if @set["negInf"]
|
||||
edges += [')'] if @set["posInf"]
|
||||
|
||||
print edges.join("/"),"\n" if Debuglevel > 0
|
||||
|
||||
while(edges.length>0)
|
||||
print "edges leng: ", @set["edges"].length, "\n" if Debuglevel > 0
|
||||
lower = edges[0]
|
||||
upper = edges[1]
|
||||
print "Lower: \"#{lower}\" Upper: \"#{upper}\"\n" if Debuglevel > 0
|
||||
edges = edges.slice(2..edges.length)
|
||||
|
||||
if ((lower.to_s <=> '(')!=0 and
|
||||
(upper.to_s <=> ')')!=0 and
|
||||
((lower+1) == upper))
|
||||
print "#{upper}\n" if Debuglevel > 0
|
||||
runs += [ "#{upper}" ]
|
||||
else
|
||||
lower += 1 if (lower.to_s <=> "(")!=0
|
||||
print "#{lower}-#{upper}\n" if Debuglevel > 0
|
||||
runs += [ "#{lower}-#{upper}" ]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print "edges leng: ", @set["edges"].length, "\n" if Debuglevel > 0
|
||||
|
||||
return runs.join(',')
|
||||
end
|
||||
|
||||
def elements
|
||||
if (@set["negInf"] == true or @set["posInf"] == true)
|
||||
print "Set::IntSpan::elements: infinite set\n"
|
||||
exit
|
||||
end
|
||||
|
||||
elements = []
|
||||
edges = @set["edges"]
|
||||
while (edges.length>0)
|
||||
lower, upper = edges.slice!(0..1)
|
||||
elements += (lower+1 .. upper).to_a
|
||||
end
|
||||
|
||||
return elements
|
||||
end
|
||||
|
||||
def _real_set(set_spec=nil) # converts a set specification into a set
|
||||
(set_spec != nil and set_spec.type.to_s == "Set::IntSpan") ?
|
||||
set_spec :
|
||||
IntSpan.new(set_spec)
|
||||
end
|
||||
|
||||
def union(set_spec)
|
||||
b = _real_set(set_spec)
|
||||
s = IntSpan.new
|
||||
|
||||
s.set_neg_inf(@set["negInf"] || b.neg_inf)
|
||||
|
||||
eA = @set["edges"]
|
||||
eB = b.edges
|
||||
eS = s.edges
|
||||
|
||||
inA = @set["negInf"]
|
||||
inB = b.neg_inf
|
||||
|
||||
iA = 0
|
||||
iB = 0
|
||||
|
||||
while (iA < eA.length and iB < eB.length)
|
||||
xA = eA[iA]
|
||||
xB = eB[iB]
|
||||
|
||||
if (xA < xB)
|
||||
iA += 1
|
||||
inA = ! inA
|
||||
not inB and eS += [ xA ]
|
||||
elsif (xB < xA)
|
||||
iB += 1
|
||||
inB = ! inB
|
||||
not inA and eS += [ xB ]
|
||||
else
|
||||
iA += 1
|
||||
iB += 1
|
||||
inA = ! inA
|
||||
inB = ! inB
|
||||
inA == inB and eS += [ xA ]
|
||||
end
|
||||
end
|
||||
|
||||
iA < eA.length and (! inB) and eS += eA[iA..eA.length]
|
||||
iB < eB.length and (! inA) and eS += eB[iB..eB.length]
|
||||
|
||||
s.set_pos_inf(@set["posInf"] || b.pos_inf)
|
||||
s.set_edges(eS)
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
def intersect(set_spec)
|
||||
b = _real_set(set_spec)
|
||||
s = IntSpan.new
|
||||
|
||||
s.set_neg_inf(@set["negInf"] && b.neg_inf)
|
||||
|
||||
eA = @set["edges"]
|
||||
eB = b.edges
|
||||
eS = s.edges
|
||||
|
||||
inA = @set["negInf"]
|
||||
inB = b.neg_inf
|
||||
|
||||
iA = 0
|
||||
iB = 0
|
||||
|
||||
while (iA < eA.length and iB < eB.length)
|
||||
xA = eA[iA]
|
||||
xB = eB[iB]
|
||||
|
||||
if (xA < xB)
|
||||
iA += 1
|
||||
inA = ! inA
|
||||
inB and eS += [ xA ]
|
||||
elsif (xB < xA)
|
||||
iB += 1
|
||||
inB = ! inB
|
||||
inA and eS += [ xB ]
|
||||
else
|
||||
iA += 1
|
||||
iB += 1
|
||||
inA = ! inA
|
||||
inB = ! inB
|
||||
inA == inB and eS += [ xA ]
|
||||
end
|
||||
end
|
||||
|
||||
iA < eA.length and inB and eS += eA[iA..eA.length]
|
||||
iB < eB.length and inA and eS += eB[iB..eB.length]
|
||||
|
||||
s.set_neg_inf(@set["posInf"] && b.pos_inf)
|
||||
s.set_edges(eS)
|
||||
return s
|
||||
end
|
||||
|
||||
def diff (set_spec)
|
||||
b = _real_set(set_spec)
|
||||
s = IntSpan.new
|
||||
|
||||
s.set_neg_inf(@set["negInf"] && ! b.neg_inf)
|
||||
|
||||
eA = @set["edges"]
|
||||
eB = b.edges
|
||||
eS = s.edges
|
||||
|
||||
inA = @set["negInf"]
|
||||
inB = b.neg_inf
|
||||
|
||||
iA = 0
|
||||
iB = 0
|
||||
|
||||
while (iA < eA.length and iB < eB.length)
|
||||
xA = eA[iA]
|
||||
xB = eB[iB]
|
||||
|
||||
if (xA < xB)
|
||||
iA += 1
|
||||
inA = ! inA
|
||||
not inB and eS += [ xA ]
|
||||
elsif (xB < xA)
|
||||
iB += 1
|
||||
inB = ! inB
|
||||
inA and eS += [ xB ]
|
||||
else
|
||||
iA += 1
|
||||
iB += 1
|
||||
inA = ! inA
|
||||
inB = ! inB
|
||||
inA != inB and eS += xA
|
||||
end
|
||||
end
|
||||
|
||||
iA < eA.length and not inB and eS += eA[iA..eA.length]
|
||||
iB < eB.length and inA and eS += eB[iB..eB.length]
|
||||
|
||||
s.set_edges(eS)
|
||||
|
||||
s.set_pos_inf(@set["posInf"] && ! b.pos_inf)
|
||||
return s
|
||||
end
|
||||
|
||||
def xor(set_spec)
|
||||
b = _real_set(set_spec)
|
||||
s = IntSpan.new
|
||||
|
||||
s.set_neg_inf(@set["negInf"] ^ b.neg_inf)
|
||||
|
||||
eA = @set["edges"]
|
||||
eB = b.edges
|
||||
eS = s.edges
|
||||
|
||||
iA = 0
|
||||
iB = 0
|
||||
|
||||
while (iA < eA.length and iB < eB.length)
|
||||
xA = eA[iA]
|
||||
xB = eB[iB]
|
||||
|
||||
if (xA < xB)
|
||||
iA += 1
|
||||
eS += [ xA ]
|
||||
elsif (xB < xA)
|
||||
iB += 1
|
||||
eS += [ xB ]
|
||||
else
|
||||
iA += 1
|
||||
iB += 1
|
||||
end
|
||||
end
|
||||
|
||||
iA < eA.length and eS += eA[iA..eA.length]
|
||||
iB < eB.length and eS += eB[iB..eB.length]
|
||||
|
||||
s.set_pos_inf(@set["posInf"] ^ b.pos_inf)
|
||||
s.set_edges(eS)
|
||||
return s
|
||||
end
|
||||
|
||||
def complement
|
||||
# complement is inverse set; dit klopt hier dus niet
|
||||
a = first
|
||||
b = last
|
||||
|
||||
print "first #{a} last #{b}\n" if Debuglevel > 0
|
||||
if a!=b
|
||||
s = IntSpan.new("#{a}-#{b}")
|
||||
comp = xor(s)
|
||||
else
|
||||
comp = IntSpan.new("#{a}")
|
||||
end
|
||||
|
||||
if Debuglevel > 0
|
||||
while i = comp.next
|
||||
print "#{i}\n"
|
||||
end
|
||||
end
|
||||
|
||||
comp.set_neg_inf(! comp.neg_inf)
|
||||
comp.set_pos_inf(! comp.pos_inf)
|
||||
return comp
|
||||
end
|
||||
|
||||
|
||||
def superset(set_spec)
|
||||
b = _real_set(set_spec)
|
||||
|
||||
# $b->diff($a)->empty
|
||||
s = b.diff(self)
|
||||
return s.empty
|
||||
end
|
||||
|
||||
|
||||
def subset(set_spec)
|
||||
b = _real_set(set_spec)
|
||||
|
||||
# $a->diff($b)->empty
|
||||
s = diff(b)
|
||||
return s.empty
|
||||
end
|
||||
|
||||
|
||||
def equal(set_spec)
|
||||
b = _real_set(set_spec)
|
||||
|
||||
print "a\n"
|
||||
@set["negInf"] == b.neg_inf or return false
|
||||
print "b\n"
|
||||
@set["posInf"] == b.pos_inf or return false
|
||||
|
||||
aEdge = @set["edges"]
|
||||
bEdge = b.edges
|
||||
print "aEdge #{aEdge.length} bEdge #{bEdge.length}\n"
|
||||
aEdge.length == bEdge.length or return false
|
||||
print "c\n"
|
||||
|
||||
for i in (0...aEdge.length)
|
||||
aEdge[i] == bEdge[i] or return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def equivalent(set_spec)
|
||||
b = _real_set(set_spec)
|
||||
|
||||
cardinality == b.cardinality
|
||||
end
|
||||
|
||||
|
||||
def cardinality
|
||||
(@set["negInf"] or @set["posInf"]) and return -1
|
||||
|
||||
car = 0
|
||||
edges = @set["edges"]
|
||||
i=0
|
||||
while (i < edges.length)
|
||||
lower = edges[i]
|
||||
upper = edges[i+1]
|
||||
car += upper - lower
|
||||
i += 2
|
||||
end
|
||||
|
||||
return car
|
||||
end
|
||||
|
||||
def empty
|
||||
if @set["negInf"] == false and @set["edges"].length > 0 and
|
||||
@set["posInf"] == false
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def finite
|
||||
if @set["negInf"] == false and @set["posInf"] == false
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def edges
|
||||
return @set["edges"]
|
||||
end
|
||||
|
||||
def set_edges(edges)
|
||||
@set["edges"] = edges
|
||||
end
|
||||
|
||||
def neg_inf
|
||||
return @set["negInf"]
|
||||
end
|
||||
|
||||
def set_neg_inf(negInf)
|
||||
@set["negInf"] = negInf
|
||||
end
|
||||
|
||||
def pos_inf
|
||||
return @set["posInf"]
|
||||
end
|
||||
|
||||
def set_pos_inf(posInf)
|
||||
@set["posInf"] = posInf
|
||||
end
|
||||
|
||||
def infinite
|
||||
@set["negInf"] or @set["posInf"]
|
||||
end
|
||||
|
||||
def universal
|
||||
@set["negInf"] and not @set["edges"].length > 0 and @set["posInf"]
|
||||
end
|
||||
|
||||
def member(n)
|
||||
inSet = @set["negInf"]
|
||||
edge = @set["edges"]
|
||||
|
||||
for i in (0...edge.length)
|
||||
if inSet
|
||||
return true if n <= edge[i]
|
||||
inSet = false
|
||||
else
|
||||
return false if n <= edge[i]
|
||||
inSet = true
|
||||
end
|
||||
end
|
||||
|
||||
inSet
|
||||
end
|
||||
|
||||
def insert(n)
|
||||
n or return
|
||||
|
||||
inSet = @set["negInf"]
|
||||
edge = @set["edges"]
|
||||
|
||||
if n > edge[-1]+1
|
||||
@set["edges"] += [n-1, n]
|
||||
return
|
||||
elsif n > edge[-1]
|
||||
@set["edges"][-1] += 1
|
||||
return
|
||||
end
|
||||
|
||||
for i in (0...edge.length)
|
||||
if (inSet)
|
||||
n <= edge[i] and return
|
||||
inSet = false
|
||||
else
|
||||
n <=edge[i] and break
|
||||
inSet = true
|
||||
end
|
||||
end
|
||||
|
||||
inSet and return
|
||||
|
||||
lGap = i == 0 || n-1 - edge[i-1]
|
||||
lGap = false if lGap == 0
|
||||
|
||||
rGap = i == edge.length-1 ? i : edge[i] - n
|
||||
rGap = false if rGap == 0
|
||||
|
||||
if ( lGap and rGap)
|
||||
lower = edge[0...i]
|
||||
upper = edge[i...edge.length]
|
||||
edge = lower
|
||||
edge += [n-1, n]
|
||||
edge += upper
|
||||
elsif (not lGap and rGap)
|
||||
edge[i-1] += 1
|
||||
elsif ( lGap and not rGap)
|
||||
edge[i] -= 1
|
||||
else
|
||||
lower = edge[0...i-1]
|
||||
upper = edge[i+1..edge.length]
|
||||
edge = lower
|
||||
edge += upper
|
||||
end
|
||||
|
||||
@set["edges"] = edge
|
||||
end
|
||||
|
||||
def remove(n)
|
||||
n or return
|
||||
|
||||
inSet = @set["negInf"]
|
||||
edge = @set["edges"]
|
||||
|
||||
for i in (0...edge.length)
|
||||
if (inSet)
|
||||
break if n <= edge[i]
|
||||
inSet = false
|
||||
else
|
||||
return if n <= edge[i]
|
||||
inSet = true
|
||||
end
|
||||
end
|
||||
|
||||
return unless inSet
|
||||
|
||||
for i in (0...edge.length)
|
||||
if edge[i] == n-1 and edge[i+1] == n
|
||||
lower = edge[0...i]
|
||||
upper = edge[i+2..edge.length]
|
||||
edge = lower + upper
|
||||
break
|
||||
elsif edge[i] == n-1
|
||||
edge[i] += 1
|
||||
break
|
||||
elsif edge[i] == n
|
||||
edge[i] += 1
|
||||
break
|
||||
elsif edge[i+1] == n
|
||||
edge[i+1] -= 1
|
||||
break
|
||||
elsif edge[i]<n and edge[i+1]>n
|
||||
lower = edge[0..i]
|
||||
upper = edge[i+1..edge.length]
|
||||
edge = lower + [n-1, n] +upper
|
||||
break
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
|
||||
@set["edges"] = edge
|
||||
end
|
||||
|
||||
def min
|
||||
empty and return nil
|
||||
neg_inf and return nil
|
||||
@set["edges"][0]+1
|
||||
end
|
||||
|
||||
|
||||
def max
|
||||
empty and return nil
|
||||
pos_inf and return nil
|
||||
@set["edges"][-1]
|
||||
end
|
||||
|
||||
def grep_set(block)
|
||||
return nil if @set["negInf"] or @set["posInf"]
|
||||
|
||||
edges = @set["edges"]
|
||||
sub_edges = []
|
||||
|
||||
while (edges.length > 0)
|
||||
lower = edges[0]
|
||||
upper = edges[1]
|
||||
edges = edges.slice(2..edges.length)
|
||||
|
||||
for i in (lower+1..upper)
|
||||
# local $_ = i
|
||||
# &$block() or next # definately wrong, must eval block
|
||||
|
||||
if (sub_edges.length > 0 and sub_edges[-1] == i-1)
|
||||
sub_edges[-1] = i
|
||||
else
|
||||
sub_edges += [ i-1, i ]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sub_set = new
|
||||
sub_set["edges"] = sub_edges
|
||||
sub_set
|
||||
end
|
||||
|
||||
def map_set(block)
|
||||
return nil if @set["negInf"] or @set["posInf"]
|
||||
|
||||
map_set = new
|
||||
|
||||
edges = @set["edges"]
|
||||
while (edges.length > 0)
|
||||
lower = edges[0]
|
||||
upper = edges[1]
|
||||
edges = edges.slice(2..edges.length)
|
||||
|
||||
for domain in (lower+1..upper)
|
||||
local $_ = domain;
|
||||
|
||||
# for range (&$block()) # definately wrong, must eval block
|
||||
# map_set.insert(range)
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
map_set
|
||||
end
|
||||
|
||||
def first
|
||||
@set["iterator"] = min
|
||||
@set["run"] = []
|
||||
@set["run"][0] = 0
|
||||
@set["run"][1] = @set["edges"].length > 0 ? 1 : nil
|
||||
|
||||
@set["iterator"]
|
||||
end
|
||||
|
||||
|
||||
def last
|
||||
lastEdge = @set["edges"].length - 1
|
||||
@set["iterator"] = max
|
||||
@set["run"][0] = lastEdge > 0 ? lastEdge-1 : nil
|
||||
@set["run"][1] = lastEdge
|
||||
|
||||
@set["iterator"]
|
||||
end
|
||||
|
||||
def start(startval)
|
||||
set["iterator"] = nil
|
||||
startval or return nil
|
||||
|
||||
inSet = @set["negInf"]
|
||||
edges = @set["edges"]
|
||||
|
||||
for i in (0...edges.length)
|
||||
if (inSet)
|
||||
if (startval <= edges[i])
|
||||
@set["iterator"] = startval
|
||||
@set["run"][0] = i ? i-1 : nil
|
||||
@set["run"][1] = i
|
||||
return $startval
|
||||
end
|
||||
inSet = false
|
||||
else
|
||||
if (startval <= edges[i])
|
||||
return nil
|
||||
end
|
||||
inSet = true
|
||||
end
|
||||
end
|
||||
|
||||
if (inSet)
|
||||
@set["iterator"] = startval
|
||||
@set["run"][0] = edges.length > 0 ? edges.length: nil
|
||||
@set["run"][1] = nil
|
||||
end
|
||||
|
||||
@set["iterator"]
|
||||
end
|
||||
|
||||
def current
|
||||
@set["iterator"]
|
||||
end
|
||||
|
||||
|
||||
def next
|
||||
@set["iterator"] or return first
|
||||
|
||||
run1 = @set["run"][1]
|
||||
run1 or return ++@set["iterator"]
|
||||
|
||||
edges = @set["edges"]
|
||||
if (@set["iterator"] < edges[run1])
|
||||
@set["iterator"] += 1
|
||||
return @set["iterator"]
|
||||
end
|
||||
|
||||
if (run1 < edges.length-2)
|
||||
run0 = run1 + 1
|
||||
@set["run"] = [run0, run0+1]
|
||||
@set["iterator"] = edges[run0]+1
|
||||
elsif (run1 < edges.length-1)
|
||||
run0 = run1 + 1
|
||||
@set["run"] = [run0, nil]
|
||||
@set["iterator"] = edges[run0]+1
|
||||
else
|
||||
@set["iterator"] = nil
|
||||
end
|
||||
|
||||
@set["iterator"]
|
||||
end
|
||||
|
||||
def prev
|
||||
@set["iterator"] or return last
|
||||
|
||||
run0 = @set["run"][0]
|
||||
run0 or return --@set["iterator"]
|
||||
|
||||
edges = @set["edges"]
|
||||
|
||||
if (@set["iterator"] > edges[run0]+1)
|
||||
@set["iterator"] -= 1
|
||||
return @set["iterator"]
|
||||
end
|
||||
|
||||
if (run0 > 1)
|
||||
run1 = run0 - 1
|
||||
@set["run"] = [run1-1, run1]
|
||||
@set["iterator"] = edges[run1]
|
||||
elsif (run0 > 0)
|
||||
run1 = run0 - 1
|
||||
@set["run"] = [nil, run1]
|
||||
@set["iterator"] = edges[run1]
|
||||
else
|
||||
@set["iterator"] = nil
|
||||
end
|
||||
|
||||
@set["iterator"]
|
||||
end
|
||||
|
||||
|
||||
end # class
|
||||
|
||||
end # module
|
||||
|
||||
|
||||
# TODO
|
||||
# Do not kill an item until it's tested!
|
||||
|
||||
# [x] new
|
||||
# [x] valid
|
||||
# [ ] copy
|
||||
# [ ] _copy_empty # makes $set the empty set
|
||||
# [ ] _copy_array # copies an array into a set
|
||||
# [ ] _copy_set # copies one set to another
|
||||
# [ ] _copy_run_list # parses a run list
|
||||
# [ ] _cleanup
|
||||
# [x] run_list
|
||||
# [x] elements
|
||||
# [x] _real_set # converts a set specification into a set
|
||||
# [x] union
|
||||
# [x] intersect
|
||||
# [x] diff
|
||||
# [x] xor
|
||||
# [ ] complement
|
||||
# [x] superset
|
||||
# [x] subset
|
||||
# [x] equal
|
||||
# [x] equivalent
|
||||
# [x] cardinality
|
||||
# [x] empty
|
||||
# [x] finite
|
||||
# [x] neg_inf { shift->{negInf} }
|
||||
# [x] pos_inf { shift->{posInf} }
|
||||
# [x] infinite
|
||||
# [ ] universal
|
||||
# [x] member
|
||||
# [x] insert
|
||||
# [x] remove
|
||||
# [x] min
|
||||
# [x] max
|
||||
# [ ] grep_set(&$)
|
||||
# [ ] map_set(&$)
|
||||
# [x] first($)
|
||||
# [x] last($)
|
||||
# [ ] start($$)
|
||||
# [x] current($) { shift->{iterator} }
|
||||
# [x] next($)
|
||||
# [x] prev($)
|
||||
|
||||
# New methods
|
||||
# [x] set_neg_inf
|
||||
# [x] set_pos_inf
|
||||
# [x] set_edges
|
||||
# [x] edges
|
||||
Loading…
Add table
Add a link
Reference in a new issue