ruby-ogginfo-0.7.2/0000755000004100000410000000000012261213656014163 5ustar www-datawww-dataruby-ogginfo-0.7.2/Rakefile0000644000004100000410000000200112261213656015621 0ustar www-datawww-data# -*- ruby -*- require 'hoe' Hoe.plugin :yard Hoe.plugin :git Hoe.plugin :rcov Hoe.plugin :gemspec Hoe.spec('ruby-ogginfo') do developer('Guillaume Pierronnet','guillaume.pierronnet@gmail.com') developer('Grant Gardner','grant@lastweekend.com.au') #summary = 'ruby-ogginfo is a pure-ruby library that gives low level informations on ogg files' remote_rdoc_dir = '' rdoc_locations << "rubyforge.org:/var/www/gforge-projects/ruby-ogginfo/" end desc "generate audio fixtures" task :generate_fixtures do ffmpeg = "ffmpeg -f u16le -i /dev/urandom -t 3 -ar 48000 -ac 2 -f wav -y - 2>/dev/null" files = { :ogg => "oggenc -q0 --raw --artist=artist -o - -", :opus => "opusenc --bitrate 64 --artist=artist - -", :speex => "speexenc --rate 48000 --stereo --author spxinfotest --title SpxInfoTest --comment test=\"hello\303\251\" - -" }.each_with_object({}) do |(codec, cmd), hash| hash[codec] = `#{ffmpeg} | #{cmd}` end File.binwrite("test/fixtures.yml", files.to_yaml) end # vim: syntax=Ruby ruby-ogginfo-0.7.2/.gemtest0000644000004100000410000000000012261213656015622 0ustar www-datawww-dataruby-ogginfo-0.7.2/Manifest.txt0000644000004100000410000000050512261213656016472 0ustar www-datawww-dataHistory.txt Manifest.txt README.txt Rakefile lib/ogg/codecs/comments.rb lib/ogg/codecs/speex.rb lib/ogg/codecs/vorbis.rb lib/ogg/codecs/opus.rb lib/ogg/page.rb lib/ogg/reader.rb lib/ogg/writer.rb lib/ogg.rb lib/ogginfo.rb setup.rb test/fixtures.yml test/test_helper.rb test/test_ruby-ogginfo.rb test/test_ruby-othercodecs.rb ruby-ogginfo-0.7.2/README.txt0000644000004100000410000000113612261213656015662 0ustar www-datawww-data= ruby-ogginfo http://ruby-ogginfo.rubyforge.org/ https://github.com/moumar/ruby-ogginfo == DESCRIPTION: ruby-ogginfo gives you access to low level information on ogg files (bitrate, length, samplerate, encoder, etc... ), as well as tag. Supported codecs are: vorbis, speex and opus. It is written in pure ruby. == FEATURES/PROBLEMS * writing tags is now pure ruby == SYNOPSIS: require "ogginfo" OggInfo.open("toto.ogg") do |ogg| puts ogg.bitrate puts ogg.artist puts ogg end == INSTALL: sudo gem install ruby-ogginfo == TODO: * writing tags in pure-ruby == LICENSE: GPL v3 ruby-ogginfo-0.7.2/History.txt0000644000004100000410000000345412261213656016373 0ustar www-datawww-data=== 0.7.2 / 2013-11-20 * set licence to GPL === 0.7.1 / 2013-10-27 * fixed UTF-8 bug === 0.7 / 2013-10-27 * opus codec added * OggInfo#picture read accessor added === 0.6.13 / 2013-10-25 * added OggInfo#picture=(filepath) method to add picture to OggFile with METADATA_BLOCK_PICTURE tag === 0.6.12 / 2013-09-05 * avoid potential race condition on temp file creation * remove warnings on unused variables === 0.6.11 / 2012-07-16 * more robust compute_length() method (works on broken OGGs) === 0.6.10 / 2012-05-18 * doesn't show binary dump in raise messages anymore === 0.6.9 / 2012-05-18 * more robust Ogg pages parsing. (doesn't fail on truncated Ogg files) === 0.6.8 / 2012-02-28 * removed :encoding parameter on OggInfo#new * utf8 strings are correctly written now in ruby > 1.9 === 0.6.7 / 2012-02-27 * fixes for ruby 1.9 (again) === 0.6.6 / 2011-12-23 * fixes for ruby 1.9 (thanks to gwolf) === 0.6.5 / 2011-04-07 * internal reorganization, leading to more robust and faster library === 0.6 / 2011-03-01 * pure ruby tag writing (thanks to Grant Gardner) === 0.5 / 2011-01-13 * speex support (thanks to Grant Gardner) === 0.4.2 / 2010-03-13 * bugfix on frame parsing === 0.4.1 / 2010-03-13 * bugfix on file parsing === 0.4 / 2009-12-04 * new ogg frame reading implementation * better tag reading (thanks to Sven) * now assume utf-8 by default to decode tags === 0.3.2 / 2009-06-22 * added setup.rb for tarball distribution * added license on top of lib/ogginfo.rb === 0.3.1 / 2008-03-16 * bug fixed #18852 "OggInfo#close(): close @ic only if not nil" * bug fixed on encoding handling === 0.3 / 2008-03-15 * write support through "vorbiscomment" binary * correct encoding handling === 0.2 / 2005-07-11 * tag["key"] is accessible with tag.key === 0.1 / 2004-06-20 * first public version ruby-ogginfo-0.7.2/lib/0000755000004100000410000000000012261213656014731 5ustar www-datawww-dataruby-ogginfo-0.7.2/lib/ogg/0000755000004100000410000000000012261213656015505 5ustar www-datawww-dataruby-ogginfo-0.7.2/lib/ogg/codecs/0000755000004100000410000000000012261213656016745 5ustar www-datawww-dataruby-ogginfo-0.7.2/lib/ogg/codecs/vorbis.rb0000644000004100000410000000304612261213656020601 0ustar www-datawww-datamodule Ogg::Codecs class Vorbis class << self include VorbisComments # return true/false based on whether the header packet belongs to us def match?(header_packet) header_packet.start_with?("\001vorbis") end #consume header and tag pages, return array of two hashes, info and tags def decode_headers(reader) init_pkt, tag_pkt, _ = reader.read_packets(3) # init_pkt, tag_pkt, setup_pkt info = extract_info(init_pkt) info[:tag], info[:tag_vendor] = unpack_comments(tag_pkt, "\003vorbis") info end # consume pages with old tags/setup packets and rewrite newtags,setup packets # return the number of pages written def replace_tags(reader, writer, new_tags, vendor) _, setup_pkt = reader.read_packets(2) # tag_pkt, setup_pkt writer.write_packets(0, pack_comments(new_tags, vendor, "\003vorbis"), setup_pkt) end def extract_info(packet) _, #vorbis_string, _, # vorbis_version, channels, samplerate, upper_bitrate, nominal_bitrate, lower_bitrate = packet.unpack("a7VCV4") if nominal_bitrate == 0 if (upper_bitrate == 2**32 - 1) || (lower_bitrate == 2**32 - 1) nominal_bitrate = 0 else nominal_bitrate = ( upper_bitrate + lower_bitrate) / 2 end end return { :channels => channels, :samplerate => samplerate, :nominal_bitrate => nominal_bitrate } end end end end ruby-ogginfo-0.7.2/lib/ogg/codecs/opus.rb0000644000004100000410000000232712261213656020264 0ustar www-datawww-datamodule Ogg::Codecs class Opus class << self include VorbisComments # return true/false based on whether the header packet belongs to us def match?(header_packet) header_packet.start_with?("OpusHead") end #consume header and tag pages, return array of two hashes, info and tags def decode_headers(reader) init_pkt, tag_pkt = reader.read_packets(2) # init_pkt, tag_pkt info = extract_info(init_pkt) info[:tag], info[:tag_vendor] = unpack_comments(tag_pkt, "OpusTags") info end # consume pages with old tags/setup packets and rewrite newtags,setup packets # return the number of pages written def replace_tags(reader, writer, new_tags, vendor) _ = reader.read_packets(1) # tag_packet writer.write_packets(0, pack_comments(new_tags, vendor, "OpusTags")) end def extract_info(packet) _, # opus magic signature _, # opus_version, channels, _, # pre skip _, # samplerate, _, # output gain _ = packet.unpack("a8CCvVvC") # channel map return { :channels => channels, :samplerate => 48000 } end end end end ruby-ogginfo-0.7.2/lib/ogg/codecs/comments.rb0000644000004100000410000000340412261213656021120 0ustar www-datawww-datarequire "stringio" module Ogg::Codecs # See http://www.xiph.org/vorbis/doc/v-comment.html # Methods to pack/unpack vorbis comment packets # intended to be included into Codec classes module VorbisComments # unpack a packet, skipping the preamble # returns a 2 element array being a Hash of tag/value pairs and the vendor string def unpack_comments(packet, preamble="") pio = StringIO.new(packet) pio.read(preamble.length) vendor_length = pio.read(4).unpack("V").first vendor = pio.read(vendor_length) tag = {} tag_size = pio.read(4).unpack("V")[0] tag_size.times do |i| size = pio.read(4).unpack("V")[0] comment = pio.read(size) unless RUBY_VERSION[0..2] == "1.8" comment.force_encoding("UTF-8") end key, val = comment.split(/=/, 2) tag[key.downcase] = val end #framing bit = pio.read(1).unpack("C")[0] [ tag, vendor ] end # Pack tag Hash and vendor string into an ogg packet. def pack_comments(tag, vendor, preamble="") packet_data = "".force_encoding("binary") packet_data << preamble packet_data << [ vendor.length ].pack("V") packet_data << vendor packet_data << [tag.size].pack("V") tag.each do |k,v| tag_data = "#{ k }=#{ v }" unless RUBY_VERSION[0..2] == "1.8" tag_data.force_encoding("binary") end packet_data << [ binary_size(tag_data) ].pack("V") packet_data << tag_data end packet_data << "\001" packet_data end def binary_size(s) if RUBY_VERSION[0..2] == "1.8" s.size else s.dup.force_encoding("BINARY").size end end end end ruby-ogginfo-0.7.2/lib/ogg/codecs/speex.rb0000644000004100000410000000221412261213656020415 0ustar www-datawww-datamodule Ogg::Codecs class Speex class << self include VorbisComments def match?(header_packet) header_packet.start_with?("Speex") end def decode_headers(reader) init_packet, tag_packet = reader.read_packets(2) info = extract_info(init_packet) info[:tag], info[:tag_vendor] = unpack_comments(tag_packet) return info end def replace_tags(reader, writer, new_tags, vendor) _ = reader.read_packets(1) # tag_packet writer.write_packets(0, pack_comments(new_tags, vendor)) end def extract_info(info_packet) _, #speex_string, _, #speex_version, _, #speex_version_id, _, #header_size, samplerate, _, #mode, _, #mode_bitstream_version, channels, nominal_bitrate, #framesize, vbr _, _ = info_packet.unpack("A8A20VVVVVVVVV") #not sure how to make sense of the bitrate info,picard doesn't show it either... return { :channels => channels, :samplerate => samplerate, :nominal_bitrate => nominal_bitrate } end end end end ruby-ogginfo-0.7.2/lib/ogg/writer.rb0000644000004100000410000000254112261213656017350 0ustar www-datawww-datarequire 'stringio' module Ogg # Writes pages or packets to an output io class Writer attr_reader :output def initialize(bitstream_serial_no, output) @output = output @page_sequence = 0 @bitstream_serial_no = bitstream_serial_no end # Writes a page to the output, the serial number and page sequence are # are overwritten to be appropriate for this stream. def write_page(page) page.sequence_no = @page_sequence @output << page.pack @page_sequence += 1 end def write_packets(granule_pos, *packets) written_pages_count = 1 page = Page.new(@bitstream_serial_no, granule_pos) packets.each do |packet| io = StringIO.new(packet) while !io.eof? do page.segments << io.read(255) if (page.segments.length == 255) page.granule_pos = -1 write_page(page) page = Page.new(@bitstream_serial_no, granule_pos) written_pages_count += 1 end end #If our packet was an exact multiple of 255 we need to put in an empty closing segment if (page.segments.length == 0 || page.segments.last.length == 255) page.segments << "" end end #we always need to flush the final page. write_page(page) written_pages_count end end end ruby-ogginfo-0.7.2/lib/ogg/page.rb0000644000004100000410000000414212261213656016747 0ustar www-datawww-datamodule Ogg class Page attr_accessor :granule_pos, :bitstream_serial_no, :sequence_no, :segments, :header attr_reader :checksum # read an ogg frame from the +file+ # file must be positioned at end of frame after this loop # options - :skip_body = seek to end of frame rather than reading in the data def self.read(io, options = {}) return nil if io.eof? chunk = io.read(27) capture_pattern, _, # version header, granule_pos, bitstream_serial_no, sequence_no, @checksum, segments = chunk.unpack("a4CCQVVVC") #a4CCQNNNC if capture_pattern != "OggS" raise(StreamError, "bad magic number") end page = Page.new(bitstream_serial_no, granule_pos) page.header = header page.sequence_no = sequence_no unless io.eof? segment_sizes = io.read(segments).unpack("C*") if options[:skip_body] body_size = segment_sizes.inject(0) { |sum, i| sum + i } io.seek(body_size, IO::SEEK_CUR) else segment_sizes.each do |size| break if io.eof? page.segments << io.read(size) end if options[:checksum] if @checksum != Ogg.compute_checksum(page.pack) raise(StreamError, "bad checksum: expected #{ @checksum }, got #{ page.checksum }") end end end end page end def initialize(bitstream_serial_no = 0, granule_pos = 0) @bitstream_serial_no = bitstream_serial_no @granule_pos = granule_pos @segments = [] @header = 0 end def pack packed = [ "OggS", 0, #version @header, @granule_pos, @bitstream_serial_no, @sequence_no, 0, #checksum @segments.length ].pack("a4CCQVVVC") packed << @segments.collect { |segment| segment.length }.pack("C*") packed << @segments.join crc = Ogg.compute_checksum(packed) packed[22..25] = [crc].pack("V") packed end end end ruby-ogginfo-0.7.2/lib/ogg/reader.rb0000644000004100000410000000153612261213656017301 0ustar www-datawww-datamodule Ogg #Reads pages and packets from an ogg stream class Reader attr_reader :input def initialize(input) @input = input end def each_pages(options = {}) until @input.eof? yield Page.read(@input, options) end end def read_packets(max_packets) result = [] partial_packet = "" each_pages do |page| partial_packet = page.segments.inject(partial_packet) do |packet,segment| packet << segment if segment.length < 255 #end of packet result << packet return result if result.length == max_packets "" else packet end end end # We expect packets to reach page boundaries, consider raising exception if partial_packet here. result end end end ruby-ogginfo-0.7.2/lib/ogg.rb0000644000004100000410000001150712261213656016036 0ustar www-datawww-data#Ogg framing # see http://www.xiph.org/ogg/vorbis/docs.html for documentation on vorbis format # http://www.xiph.org/vorbis/doc/framing.html %w{page reader writer codecs/comments codecs/vorbis codecs/speex codecs/opus}.each do |file| require File.join(File.dirname(__FILE__), "ogg", file) end module Ogg # Raised on any kind of Ogg parsing/writing error class StreamError < StandardError; end CHECKSUM_TABLE = [ 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4 ] class << self def detect_codec(input) if input.kind_of?(Page) first_page = input else first_page = Page.read(input) input.rewind end codecs = Ogg::Codecs.constants.map { |module_name| Ogg::Codecs.class_eval(module_name.to_s) }.select { |c| c.is_a?(Class) } codec = codecs.detect { |c| c.match?(first_page.segments.first) } unless codec raise(StreamError,"unknown codec") end return codec end # Calculate the checksum from the page (or the pre packed data) # If data it supplied it will be updated to record the checksum value def compute_checksum(data_) data = data_.dup data[22..25] = [0].pack("V") crc = 0 data.each_byte do |byte| crc = (crc << 8)^CHECKSUM_TABLE[((crc >> 24)&0xff) ^ byte] crc = crc & 0xffffffff end crc end end end if __FILE__ == $0 require 'pp' infile = ARGV[0] outfile = ARGV[1] vendor = "" tags = { } File.open(infile,"r") do |input| info = Ogg.read_headers(input) pp info vendor = info[:tag_vendor] end File.open(infile,"r") do | input | File.open(outfile,"w") do | output | Ogg.replace_tags(input,output,tags,vendor) end end File.open(outfile,"r") do | output | pp Ogg.read_headers(output) end end ruby-ogginfo-0.7.2/lib/ogginfo.rb0000644000004100000410000001430112261213656016705 0ustar www-datawww-data# see http://www.xiph.org/ogg/vorbis/docs.html for documentation on vorbis format # http://www.xiph.org/vorbis/doc/v-comment.html # http://www.xiph.org/vorbis/doc/framing.html # # License: ruby require 'forwardable' require "tempfile" require File.join(File.dirname(__FILE__), 'ogg.rb') class Hash ### lets you specify hash["key"] as hash.key ### this came from CodingInRuby on RubyGarden ### http://www.rubygarden.org/ruby?CodingInRuby def method_missing(meth,*args) if /=$/=~(meth=meth.id2name) then self[meth[0...-1]] = (args.length<2 ? args[0] : args) else self[meth] end end end # Raised on any kind of error related to ruby-ogginfo class OggInfoError < StandardError ; end class OggInfo VERSION = "0.7.2" extend Forwardable include Ogg attr_reader :channels, :samplerate, :nominal_bitrate # +tag+ is a hash containing the vorbis tag like "Artist", "Title", and the like attr_reader :tag # create new instance of OggInfo # use of charset is deprecated! please use utf-8 encoded strings and leave +charset+ to nil") def initialize(filename, charset = nil) if charset warn("use of charset is deprecated! please use utf-8 encoded tags") end @filename = filename @length = nil @bitrate = nil File.open(@filename, 'rb') do |file| begin info = read_headers(file) @samplerate = info[:samplerate] @nominal_bitrate = info[:nominal_bitrate] @channels = info[:channels] @tag = info[:tag] # filesize is used to calculate bitrate # but we don't want to include the headers @filesize = file.stat.size - file.pos rescue Ogg::StreamError => se raise(OggInfoError, se.message, se.backtrace) end end @original_tag = @tag.dup end # The length in seconds of the track # since this requires reading the whole file we only get it # if called def length unless @length File.open(@filename) do |file| @length = compute_length(file) end end return @length end # Calculated bit rate, also lazily loaded # since we depend on the length def bitrate @bitrate ||= (@filesize * 8).to_f / length() end # set a picture (.jpg or .png) on the ogg file def picture=(filepath) ext = File.extname(filepath) mime_type = { ".jpg" => "image/jpeg", ".png" => "image/png" }[ext] description = "folder#{ext}" raw_string = [3, # picture type mime_type.size, mime_type, description.size, description, 0, # width 0, # height 0, # color depth 0, # number of colors used File.size(filepath), File.binread(filepath) ].pack("NNa*Na*NNNNNa*") @tag["METADATA_BLOCK_PICTURE"] = [raw_string].pack("m*").strip end # get the picture as an array of [extension, file_content] # or nil if not existent def picture extensions = { "image/jpeg" => ".jpg", "image/png" => ".png" } if content = tag["metadata_block_picture"] _, #type, # picture type _, # mime_type size mime_type, _, # description size _, # description, _, # width _, # height _, # color depth _, # number of color used _, #file_content_size, file_content = content.unpack("m*").first.unpack("NNa10Na10NNNNNa*") return [extensions[mime_type], file_content] end nil end # "block version" of ::new() def self.open(*args) m = self.new(*args) ret = nil if block_given? begin ret = yield(m) ensure m.close end else ret = m end ret end # commits any tags to file def close if tag != @original_tag Tempfile.open(["ruby-ogginfo", ".ogg"]) do |tempfile| tempfile.close tempfile = File.new(tempfile.path, "wb") File.open(@filename, "rb") do | input | replace_tags(input, tempfile, tag) end tempfile.close FileUtils.cp(tempfile.path, @filename) end end end # check the presence of a tag def hastag? !tag.empty? end def to_s "channels #{channels} samplerate #{samplerate} bitrate #{nominal_bitrate} #{tag.inspect}" end private def read_headers(input) reader = Reader.new(input) codec = Ogg.detect_codec(input) codec.decode_headers(reader) end # For both Vorbis and Speex, the granule_pos is the number of samples # strictly this should be a codec function. def compute_length(input) reader = Reader.new(input) last_page = nil begin reader.each_pages(:skip_body => true, :skip_checksum => true) do |page| if page.granule_pos last_page = page end end rescue Ogg::StreamError end if last_page return last_page.granule_pos.to_f / @samplerate else return 0 end end # Pipe input to output transforming tags along the way # input/output must be open streams reading for reading/writing def replace_tags(input, output, new_tags, vendor = "ruby-ogginfo") # use the same serial number... first_page = Page.read(input) codec = Ogg.detect_codec(first_page) bitstream_serial_no = first_page.bitstream_serial_no reader = Reader.new(input) writer = Writer.new(bitstream_serial_no, output) # Write the first page as is (including presumably the b_o_s header) writer.write_page(first_page) upcased_tags = new_tags.inject({}) do |memo, (k, v)| memo[k.upcase] = v memo end # The codecs we know about put comments etc in following pages # as suggested by the spec written_pages_count = codec.replace_tags(reader, writer, upcased_tags, vendor) if written_pages_count > 1 # Write the rest of the pages. We have to do page at a time # because our tag replacement may have changed the number of # pages and thus every subsequent page needs to have its # sequence_no updated. reader.each_pages(:skip_checksum => true) do |page| writer.write_page(page) end else FileUtils.copy_stream(reader.input, writer.output) end end end ruby-ogginfo-0.7.2/metadata.yml0000644000004100000410000000543712261213656016477 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: ruby-ogginfo version: !ruby/object:Gem::Version version: 0.7.2 platform: ruby authors: - Guillaume Pierronnet - Grant Gardner autorequire: bindir: bin cert_chain: [] date: 2013-11-20 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rdoc requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.10' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.10' - !ruby/object:Gem::Dependency name: rcov requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.9' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.9' - !ruby/object:Gem::Dependency name: hoe requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.5' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '3.5' description: |- ruby-ogginfo gives you access to low level information on ogg files (bitrate, length, samplerate, encoder, etc... ), as well as tag. Supported codecs are: vorbis, speex and opus. It is written in pure ruby. email: - guillaume.pierronnet@gmail.com - grant@lastweekend.com.au executables: [] extensions: [] extra_rdoc_files: - History.txt - Manifest.txt - README.txt files: - History.txt - Manifest.txt - README.txt - Rakefile - lib/ogg/codecs/comments.rb - lib/ogg/codecs/speex.rb - lib/ogg/codecs/vorbis.rb - lib/ogg/codecs/opus.rb - lib/ogg/page.rb - lib/ogg/reader.rb - lib/ogg/writer.rb - lib/ogg.rb - lib/ogginfo.rb - setup.rb - test/fixtures.yml - test/test_helper.rb - test/test_ruby-ogginfo.rb - test/test_ruby-othercodecs.rb - .gemtest homepage: http://ruby-ogginfo.rubyforge.org/ licenses: [] metadata: {} post_install_message: rdoc_options: - --main - README.txt require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: ruby-ogginfo rubygems_version: 2.1.5 signing_key: specification_version: 4 summary: ruby-ogginfo gives you access to low level information on ogg files (bitrate, length, samplerate, encoder, etc.. test_files: - test/test_ruby-othercodecs.rb - test/test_ruby-ogginfo.rb - test/test_helper.rb ruby-ogginfo-0.7.2/test/0000755000004100000410000000000012261213656015142 5ustar www-datawww-dataruby-ogginfo-0.7.2/test/test_ruby-ogginfo.rb0000644000004100000410000000523112261213656021136 0ustar www-datawww-data#!/usr/bin/ruby -w # encoding: utf-8 $:.unshift("lib/") require "test/unit" require "ogginfo" require "fileutils" require "tempfile" require File.join(File.dirname(__FILE__), "test_helper") class OggInfoTest < Test::Unit::TestCase def setup @tempfile = load_fixtures[:ogg] end def teardown FileUtils.rm_f(@tempfile) end def test_infos OggInfo.open(@tempfile) do |ogg| assert_equal 64000, ogg.nominal_bitrate assert_equal 2, ogg.channels assert_equal 44100, ogg.samplerate assert_in_delta(3, ogg.length, 0.5) end end def test_length OggInfo.open(@tempfile) do |ogg| assert_in_delta(3, ogg.length, 0.3, "length has not been correctly guessed") assert_in_delta(64000.0, ogg.bitrate, 10000, "bitrate has not been correctly guessed") end end def test_should_not_fail_when_input_is_truncated ogg_length = nil OggInfo.open(@tempfile) do |ogg| ogg_length = ogg.length end tf = generate_truncated_ogg OggInfo.open(tf.path) do |truncated_ogg| assert ogg_length != truncated_ogg.length end reader = Ogg::Reader.new(open(tf.path, "r")) last_page = nil reader.each_pages do |page| last_page = page end assert_not_equal Ogg.compute_checksum(last_page.pack), last_page.checksum end def test_checksum tf = generate_truncated_ogg reader = Ogg::Reader.new(open(tf.path)) assert_raises(Ogg::StreamError) do reader.each_pages(:checksum => true) do |page| page end end end def test_picture tf = Tempfile.new(["ruby-ogginfo", ".jpg"]) jpg_content = (0...1000).collect { rand(256).chr }.join("") tf.write(jpg_content) tf.close OggInfo.open(@tempfile) do |ogg| ogg.picture = tf.path end OggInfo.open(@tempfile) do |ogg| assert ogg.tag.has_key?("metadata_block_picture") type, # picture type _, # mime_type size mime_type, _, # description size description, _, # width _, # height _, # color depth _, # number of color used file_content_size, file_content = ogg.tag["metadata_block_picture"].unpack("m*").first.unpack("NNa10Na10NNNNNa*") assert_equal 3, type assert_equal "image/jpeg", mime_type assert_equal "folder.jpg", description assert_equal jpg_content.size, file_content_size assert_equal jpg_content, file_content assert_equal [".jpg", jpg_content], ogg.picture end end protected def generate_truncated_ogg tf = Tempfile.new("ruby-ogginfo") s = File.size(@tempfile) data = File.read(@tempfile, (s - s*0.75).to_i) tf.write(data) tf.close tf end end ruby-ogginfo-0.7.2/test/test_ruby-othercodecs.rb0000644000004100000410000000524012261213656022010 0ustar www-datawww-data#!/usr/bin/ruby -w $:.unshift("lib/") require "test/unit" require "ogginfo" require "fileutils" require "tempfile" require File.join(File.dirname(__FILE__), "test_helper") class OtherCodecsInfoTest < Test::Unit::TestCase def setup @fixtures = load_fixtures end def test_generated_info @fixtures.each do |codec, tempfile| OggInfo.open(tempfile) do |ogg| assert_equal 2, ogg.channels case codec when :speex assert_equal "spxinfotest", ogg.tag.author when :opus assert_in_delta(3, ogg.length, 0.2, "length has not been correctly guessed for codec \"#{codec}\"") assert_in_delta 64000, ogg.bitrate, 2000 assert_equal "artist", ogg.tag.artist assert_equal 48000, ogg.samplerate when :ogg assert_in_delta(3, ogg.length, 0.5, "length has not been correctly guessed for codec \"#{codec}\"") assert_in_delta 64000, ogg.bitrate, 10000 assert_equal "artist", ogg.tag.artist assert_equal 44100, ogg.samplerate end end end end def test_tag_writing @fixtures.each do |codec, tempfile| tag = {"title" => "mytitle", "test" => "myartist" } OggInfo.open(tempfile) do |ogg| ogg.tag.clear tag.each { |k,v| ogg.tag[k] = v } end OggInfo.open(@fixtures[codec]) do |ogg| assert_equal tag, ogg.tag end end end def test_good_writing_of_utf8_strings tag = { "title" => "this is a éé utf8 string", "artist" => "and è another one à"} tag_test("tag_writing", tag) end def test_tag_writing data = "a"*256 tag_test("tag_writing", "title" => data, "artist" => data ) end def test_big_tags data = "a"*60000 tag_test("big_tags", "title" => data, "artist" => data ) end def tag_test(test_name, tag) @fixtures.each do |codec, tempfile| OggInfo.open(tempfile) do |ogg| ogg.tag.clear tag.each { |k,v| ogg.tag[k] = v } end OggInfo.open(tempfile) do |ogg| assert_equal tag, ogg.tag end FileUtils.cp(tempfile, "/tmp/test_#{RUBY_VERSION}_#{test_name}.ogg") assert_nothing_raised do io = open(tempfile) reader = Ogg::Reader.new(io) reader.each_pages do |page| page end end end end def test_unicode_support @fixtures.each do |codec, tempfile| filename = "fichier éé.#{codec}" FileUtils.cp(tempfile, filename) begin OggInfo.open(tempfile) do |ogg| ogg.tag.artist = "artistéoo" ogg.tag.title = "a"*200 end ensure FileUtils.rm_f(filename) end end end end ruby-ogginfo-0.7.2/test/test_helper.rb0000644000004100000410000000047212261213656020010 0ustar www-datawww-datarequire "yaml" require "tempfile" def load_fixtures fixtures = YAML::load_file(File.join(File.dirname(__FILE__), "fixtures.yml")) fixtures.each_with_object({}) do |(codec, content), hash| f = File.join(Dir.tmpdir, "/test.ruby-ogginfo.#{codec}") File.binwrite(f, content) hash[codec] = f end end ruby-ogginfo-0.7.2/test/fixtures.yml0000644000004100000410000021364212261213656017546 0ustar www-datawww-data--- :ogg: !binary |- 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 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAIAAAIAABwCAAAuh0JAVAUCcAIDBcSwLAAAcSdIsAABwJEvTAADA 0jRRBAAAS9NEEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAADAgAMAQIAJZaDQkJUA QBQAgEExPA1gWQDLAmgaQNMAngfwPIAoAgABAAAFDgAAATZoSiwOUGjISgAg CgDAoCiWZVmeB03TNFGEpmmaKELTNE8UoWmaJooQRc8zTXii55kmTFMUTROI omkKAAAocAAACLBBU2JxgEJDVgIAIQEABkexLE/zPM8TRdNUVWia54miKIqm aaoqNM3zRFEUTdM0VRWa5nmiKIqmqaqqCk3zPFEURdNUVVWF54miKJqmaaqq 68LzRFEUTdM0VdV1IYqiaJqmqaqq67pAFE3TNFVVVV0XiKJpmqaquq4sA1E0 TdNUVdeVZWCaqqqqquu6sgxQTVVVVdeVZYCquqrruq4sA1RVdV3XlWUZ4Lqu 68qybNsAXNd1Zdm2BQAAHDgAAAQYQScZVRZhowkXHoBCQ1YEAFEAAIAxTCmm lGFMQighNIpJCCmETEpKqZVUQUglpVIqCKmkVEpGpaWUUsoglFJSKhWEVEoq pQAAsAMHALADC6HQkJUAQB4AAEGIUowxxpyUUinGnHNOSqkUY845J6VkjDHn nJNSMsaYc85JKR1zzjnnpJSMOeecc1JK55xzzjkppZTOOeeclFJKCJ1zTkop pXPOOScAAKjAAQAgwEaRzQlGggoNWQkApAIAGBzHsjRN0zxPFDVJ0jTP8zxR NE1NsjTN8zxPFE2T53meKIqiaaoqz/M8URRF01RVriuKpmmaqqqqZFkURdE0 VVV1YZqmqaqq6rowTVFUVdV1Xciyaaqq68oybNs0VdV1ZRmoqqrKriwD11VV 15VlAQDgCQ4AQAU2rI5wUjQWWGjISgAgAwCAIAQhpRRCSimElFIIKaUQEgAA MOAAABBgQhkoNGRFABAnAAAgJKWCTkoloZRSSimllFJKKaWUUkoppZRSSiml lFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSiml lFJKKaWTUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSiml lFJKKaWUklJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJJKaWUUkop pZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkop pZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkop pZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkop pZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkop pZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkop pZQKANCNcADQfTChDBQashIASAUAAIxRijEIqcVWIcSYcxJaa61CiDHnJLSU Ys+YcxBKaS22njHHIJSSWou9lM5JSa21GHsqHaOSUksx9t5LKSWl2GLsvaeQ Qo4txth7zzGlFlursfdeY0qx1Rhj7733GGOrsdbee+8xtlZrjgUAYDY4AEAk 2LA6wknRWGChISsBgJAAAMIYpRhjzDnnnHNOSskYc85BCCGEEEopGWPMOQgh hBBCKSVjzjkHIYRQQiilZMw56CCEUEIopZTOOQcdhBBCCaWUkjHnIIQQQgml lFI65yCEEEIopYRUSimdgxBCKCGEUkpJKYQQQgihhFBSKSmFEEIIIYRQQkol pRBCCCGEEEpIpaSUUgghhBBCCKWUlFIKJZQQQiihpJJKKaWEEEoIoaRUUiqp lBJCCCWEkkpKKZVUSighhFIAAMCBAwBAgBF0klFlETaacOEBKDRkJQAQBQAA GQcdlJYbgJBy1FqHHIQUWwuRQwxajJ1yjEFKKWSQMcaklZJCxxik1GJLoYMU e8+5ldQCAAAgCAAIMAEEBggKvhACYgwAQBAiM0RCYRUsMCiDBod5APAAESER ACQmKNIuLjDLABd0cdeBEIIQhKDyBlBAAg5OuOGJNzzhRibRFIUxEAAAAACA BQB4AABAKICIiGauwuICI0Njg6PD4wNEAAAAAACsAOADAAAJASIimrkKiwuM DI0Njg6PD5AAAEAAAQAAAAAQQAACAgIAAAAAAAEAAAACAk9nZ1MAAMBmAAAA AAAA7s5oPAIAAADKyJ0XHDguqp+alZeen5+ek5iXl5+Xl5SemZ6ckpObjZ5c eQjidQeC1RVlPYu2lM0Qg6FsplVzGoPT0rOrbGd+cUKMn0+HjiKT6/nNYyVq mIYoYfd9N9vwOzx56lBv8tRZrp6eURFSKkGQWCYo4Y1+l+rQaRyndNR2nvr2 35zoeKp+L2NPPgD6G+cS5kMACsBvnEuYDwEoAH6ISjTWiqIsM4vsse1Jp1D5 /w+iTAQUCRE5dMREokqEkaRIGEtKMnIbmZUZzZCRIW1uk7REtKKfeH/1Nblv utjnmLVWOZn/Ilfo5g2tcDj/5aTbXvE4CYNezDZev8x5uicqprTf7mQmj/Zz Rm/3fW7fPIz243TxOPArd5ukbFf+9jpfMS557VfccJdS1Wp1L0GqwQBqgOKG C/5rJxxlZQSFQAj4tROOsjKCQiAEOC1Cf1k+niIiSaJ56YA5+dIk0SRDmq8Z Gdl8Ia+mTGMztB3mcUxGkopUdCpEF2rqLfZiemz3ynt22vZNHcV9rts9ND5l Mm+ou8edNn8Wa/VON+9jrduV8is1a/d+J9Pr++StNObnO2q2L1LD37hxMT1X JzORG8e0a6CuHIZ5Wfs/IlItaWmzAvIMFl4rtzUDAAColduaAQAAOB3mP/tP zaaZiWy+JEmaTXIiiQxpbpLkS+QzQGTTjPw5IpuRoCLpJKYOaTtQaeZEjYrc SH9DJpdamoHxvstYxebPcijdc43/O3nOMuqFcIdMM6bQ/2Ln+NnFsDF+Ru/e sXGZR69xb2lYk1wN6REaTxIeL2N9z3k3KeLYfe4Q47WugKrSkFwruJDQJgC+ K+cMpmEAMAa7cs5gGgYAY+B0yP9/DBoiTQdIc7JtEnk5zdc88lcqTTNSGZHX NM0GmYnKDOOUzEDV4s+IqHNb6uw577TL9A6NXEyC7XJT2aWQzz3C/mH6xo2V 5k126/yiu2+SDb9biuzGTbNYD+diKJviH9Oau5ROj/ipQuJ1OC/x4T7RNa41 Eq3EjarXsayKEqhJAX47VyKF0QCAl9u5EimMBgC8OK0Lydn/ccqItJnN3zZf mtNsc/NkZJMkokmOnNwkS2Zukm0lLd5uxKTjZI2sO9myo8O1vZfkU5794mm1 p6NSp2aPm2oMBvVjnd0x27/7opVqKaZrvMv/kkk/byjZ5J8xxdpcn5jwCc23 gp13Z8+YYjXN9NWUS+MWQtMqXMn0iKgLpCQldwEeXCcNYyIdNEIFwXXSMCbS QSNU4HSwy/L/iUjIlD9fZrZpbr5MchsZ2XwM0MhrKiN/IppKWhGq5oixg3i0 fI+8j7xYd16ya6ypWumEZTV36TzPZ9sKR7dzrmejtod0n4Z+Ik2PGBpix6P7 Mo7xczVTCuF7rp0OLKzzdGfd+CbU8bb81b63kFmLuNbKphXrEmRLFHEKUUmB USipKiMqAP4LJwygZ8AAYcEvnDCAngEDhAVeWMMhVJaSxf8JoUTUYVLMiTB0 NCNJUpnZAUMzksxIm+a22YxWtlFSOpFSbdbtS98LYUVkVybXuSHt+2fd3nYu 59c4SbF/MGxgbf56b5h85EL5bKWDza5bbm8MjNvtfXH5R2vzX1bpxYK8ct1m XWvUcLHUcm4I0yJLHIL+Oi2IcQVKuAXP64iiIEi1AP4b5z3jIAAIC37jvGcc BABhgdMhfzn7pwpZ6YDZnCSNHNkBksxsE/ky05ARSW42ctPIpmlzqjK6IOmg ohn0iS82Ro89uy6x7zZztczHWq8pdLxZ7ThiWpMVtk+P1GgcyE1SX9WaDq32 dCWT3nOtDXQ6/MjP0w3/vTyyxPVsbo5+sJrvRlas0ZFU6jLJriraliJf2FJL Kj2zJKVkUCG6LL4bVyRlWqABYDeuSMq0QAOA01C+lP9PqSTJS3My8udkI2Tm REZm/nx5bZptmk1yM7qQhanDOJgnJPXzdtJFFGVEd55+/D0/4/md7MndbUjX 0jTC7duNTOuuX7BtIuIXo0SKEMnXcGpWlXx+V1+W6atxf1bd7cv0avJr6MmI 3J3jgbE2mI511G4NcyEw3BafssagihOdJVYsqBYPRKgAviu3HKMAAGBXbjlG AQCA0yH/f8RISUbatEmSlyvJH5Eh8rK5sjlpNjKyms2MNCPNlyZJyJAZpJOM Go3I3iv+mZOlbK6pWcpos24dwxj1fzi8ikefmG4tKU6PtF3JlUT7baD2k12Y bnd7rTRvfEzl3C4NpXu8WufxnPJY0Q7d87hqLFXvBhrMuoSyimvJFikkhMEA Piu3NSZyCY0EMCu3NSZyCY0E4LQu5eX/B5qZNpubTbKSgbS5STZN07wBMiLN y0izMqKpjCQ3ERUZUuMpZT+qndt2mnLLE9lLTtKbN8XLVfa++vms7MF8cuBU rIWWOaGmtb2pqr2Gbi+mv9lxS/hLPvdqUmtoGPlyh3lpxlTqCpRj/kEsK26t r7Ip3g/EHKQrjcDCUEEpCwIeLCc95eRAA+GDYDnpKScHGggfuBhgfba8/A8S QVKSwkBEwIKQodl8WXltTv7oABmRGW2SGZFoVM2yCNezw8YUxjjvEfXzu1vK OTw7HLaKNwm1Mysc3y6XCPvx4u6Jagx/V5Luu/B6bFxWPmXr2bjFxlD3TiZC 0+PeSZLPVbku/Wbv3iEejyjzdQeBavcZhntIgthD4BKovhtXNaMjAAB246pm dAQAgNO6+Wf/g0jlZOTky2nSIOkA6YA5muQ0p9HcyjZyc9oImWmkjb1q5yR0 ylAaAa/nL9et1srvTCuk23O39aHD7xjuyxJn9j7ub/uz1lb5md+rxODT10+v e7u5gvo4h9HLZpq91f1k6tjW7pA047sUX+wzzLzq/1bJn6OIlhrFElVFvcSd oBBUCf4bJzykDQAQGvzGCQ9pAwCEBk7DQlIom/1PbTZpEgOm0mZzOpDIzMpJ mi/yMtLKScJiaxhi6kgxzfdX7j5D9wt6ry3R8pN29Jw2akwW01OFzqT7F91K nGoPjRrGrOjvRBpd6uZ2szUy74Ln+4Z8/kWdMn3FfhGpO2bTvbfnccffZ5kH dyVBUzzqXUwP3B/SXBxJkTg/Ja4qyXiFoY1EAh485wzlRgAaYUHwnDOUGwFo hAVOi5Sz2T9hiuY1kZkk2QyZEWkk6UCymhf5m9emhs4G5qbVxWbOmBilY6pX lmLxdJM2lDLrxe19zmpGr/HNp6fzNjsJWWohp/aXlZ6HPqnZztV7vKmluiLD l/3j9Noolf2H8kYrzeV7tWXi+N6wVjNyrYxUzGOsr05rgrislAwxsS3gqlT+ K+c1ZbdAA6GCXzmvKbsFGggVOC3y/z8SBhgwLzcyKicjN5vma5KRN2CGyMmJ jGbkm6cxxhiHjIOMaopRCmoLXclm9+OZknDgvqForxRCiGmNozBdctMhiKn2 SkYqRSznEm6uxGbDcZfu4mplWu2xOX3ZGMdStq4Q4n4xvX+/jCsvDeue69mt i5KERAoppjQESiGyqmkA/hoPKYUEDBLAazykFBIwSABOQ/b/T0lGtnlym2Rk pOTPaUZ0ILmZaZqbkdFEMkyzMRYNUZr9vtwc18r1ajkg5I9lcH53jqu9paTR cCiPSb4fgllc/lFWDZL7OllLLNIGYcqtV0yxW9+/fI7jjQ5OfZAt08OapZhC c5KfIuaLdIUY02MmHatCSkWqNWJ1iVZNFm6IAB4cJyOlDwAQFgTHyUjpAwCE BU7D/+w/hSTNl5HbNDebZHPkzxigkS+j2Wzky8nQkHZxGNPBWIaR7/Pp0fnD +//xnOjf7AbnZvd1W1cZv0hdf3xf5aOD8CuvpV0f8Zk78lPGW61Jvhx+0ft+ 42rvQtg1BbY2IzPKTaFfp9x4UO+WFQ7U7hI3K+PVH1op1cqgbmNetXx1FQKU a40EEU8AvmtXBGMiCiRMsGtXBGMiCiRM4HQs9P80kmbTnHx5GTlJkkrCgGk2 TQ2YyMkm1UhEXjMSIdrSJPMsRmXw7uSSMRtdkwPbXnd71naZXSNLbFBL/7Re M/79pXZt/Gt0yLZTOb69K4pl8+1bZWUzv9f+bHBiw+Gws3VNsWa/JY4xUt36 1OuSwmRIUUOCoyXTuK+wxDsvvCbcUlABnitXNWUBAECuXNWUBQAATof8/5+k EZHNHGAgkdc2h6ZpGnIzm5GTEQNmGUhGkmY0lZVodO6QYUzEoI35GGSun+v7 PDsmuXW7f9xe+tUgtOtOo861GaZfk1jqXXILIx7I5OqvfbftEZq34fvYtPk1 c7jj5pRKan86+rzGkcbhMrUZ4y5vOZhcVZp0fsbp1BNjqCJ3jC2oQ6RIg6iQ CgCeG1c1RpGARDDIjasao0hAIhg4DYWWX/4xyUhlM6RpJOlA5UsigfyZOaTS NJ9Mi1kcYxosdB7LW2ZsmtbrvHI69izyiWsUYRJmK53KjGc2fNEjsvx6uxH7 2W2okCkzG5wqDdTWBtTvJqt0qJ+XB4a4rjBN72vJvh5X/P6J0R0lfn5dm6rM 9FglUce2VvKGUBI3KQ1hQnMR3VExqAB+Cs8BAAAAcArPAQAAAOC0LuX/fyCb Jpl5uZk5Oc2Xm5lERl7ITHPy509SaTaNVBhoEnIbkZKMIed7P+Nk/nt7g1yr G9vEvjbGxh2ydx4yVSvbcU2tudA1MrWzdUcoGn+SGGOa74pbyvobDXc18ysZ qad6XEo2cyZyLF/gc+tai2WZpm0kqMnwoITEZZRqqSwUF94rVzWjIwAToFeu akZHACaA0yKzL/+nprLZNM3MGMgAeRma5sjMS+VJ82VEKs3sZCGTSoeRzhka SUgZo5WPiHFDQ037Vn2bNDvcoHl03XNLp5f2jFgGz5ZmzSkq47F1XA/9n8/o 142CZU8+Hvgoub5Vq8f6qvf0G93SojcrLWONRD+shnOKq2AdYG1BEF0oLgkC El48R4WycpQBwoLiOSqUlaMMEBY4rUuZyf8faBIDpnnZbE6GNKdpEvnSzKZp mheamzQvj7bZrEDqvLZ8bV6IvsUb1RghMuN7fc2Om6bL7Tpa03VsAdVo6aaZ ivTvpzU7RkFDXFXz+Foaprdf73B7kxWyTsXXqY+Uv627+61RV41Bp8yqGQ/r 1TN1uoP/mpjWu5yp8LskRM80MAoDHgwnHKABACEgGE44QAMAQoDT8H/2J0oz JDlpXpLNkBs52WyabUYkaWbzZWUTOnRxcR6G6jRYsCa1Pvt9MfSmtcLsWcq+ c9FdDdbu/GIFYfPvxt3zr0ymdIiBe8dgNUx/vKkpMSkpN1nd4xnSSDW8M+Xe hfzofW54UiQMRV2D0oCGForCCASJaUKBpEgkHownvWRCAIQGwXjSSyYEQGjg RabheCb/n8iRKKGCMGFwUkCSmZuZptlGMyIniYy8jFSTNNJWpjTmZEw7JlGG TlMy/W/rrsk9C6n/d7qdpLElhnwc4t5+yicxzcQWoaoz+4nd0LK3G3MeMXUz 89PxrenJVzFf5s043zV4k5srfu1muTFoFAkc817LpqOGpSRbaSBUq8lT1EC8 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 0GRN9O+r+0ZXpicxOTxm27Z6rLmtMkK+l8vsHW9j/7yOq3RYJnfjGKP1q9Nn a4MDf8wnU4Wfw7sNDppEzGfPcJdkOTgeYcHHMqJFhXIjo14AHhxHDnMMYIGw RRAcRw5zDGCBsEXgNCwEL+X/A2EA2Zw0IydP5oDZXDmR28yMnDR/NokBJMPY YYhpMGQcMqTIyebKTza1h7NXht5V89Zb7oNTU4A01Bjupj832Hj6hu1u0u5f XyTJ3sNiKQx6fPll4t7nr66wm6fXf07uS3X+2tXonudOUGb5O0JKrpBqtbGG qf1uoXk+z6vLqFchVlKiCLAkBioeLFeSZiIAg2AQLFeSZiIAg2DgdMj//0lk ZObLyV+pJCciiTSVT5KRL8mfFU0z8kgzJS3aDp2nYUhI0sj9t/o2OuWN5mU9 HyOsUWaP/5+3IRVCcgzDY1921lF8el/vLX27g9rS+UB+fFbJEOvdlDK3tR2/ xp89p2d3Oe9HyJbHYyzNBrFOZwuP29xF0bbcsq6yGJRmA1UQ0mUV3KsUvhtX JIUMmADYjSuSQgZMADgt8uX/E2U0kpwkM6MZ+SMfzZ+mkjTJHCAnIyPbSMah mcd0mEmbUSchoUT49ogSNq9xKiuOexx7nzUed6uhRNsRNsWbLYJ4FKlzbNyu nZWcV6b3dZyMjeYxhRrqvvaNpsNR3dxwVyYaj7hWJoaaORBSEMdyTTJqvqRV kPIloa0YKCojcA232Av+K+c9ZSEAhdDgV857ykIACqGB08LO5P8xVRppTnNi wOZmIyMVIsmJzObky5/IQJrFpuOUwWRiSMYMialDHvdlvp+MKxDf1XXD9PeQ /zRMUq31YF0azay/hz8rEOiCUttphc+2f27HrCkb95S19b1Xj5nZ577Mfdm0 NxoNfxyPd62ht4L8HbpXHDFWMg3x9QqyoSRVHCD1wG2I2F4MAF78hg0wtwAQ AorfsAHmFgBCgNO6lOzs/0jRyCebk6m50nyKjHyJGKi0A2REiJzIyxCpRNDY ybD56+jrFJlx++dje5Sdmc/3zUmYjfGs3sizUkljt/74+pv0xy2jIXv6vqZe /N09Jiv4Rr6+Pt31apYNJSRLNvU0jH+LUtMdRyJ7HIuV5rE8YlVz7+8aiRBD CTfSTUiUzJBE4CVjAb4bD5KgEYACsBsPkqARgALgRQprXV7+PyVFQgICR8PQ xInkNvIy2tw0V2YkaZomkTYzN09kG9nEfTop+8oa0eF3LQ9SvI2GsZsO2U8p v3Ou35r2uPhs7g1H4cXo2NDco7TvqG/r7Ov0pKF0iCX8QrN5OrtdpkNKDQdl 7/tzGON7Ms+p5WhozcLraglxKBYErHEDytA3NwZYKlkAXvwGFbAGAAgLit+g AtYAAGGB01AKL//4mCiSbJqbK18kzZ+RmW1GpPJnZrNpNE0jHejIOI60UxPm qMVmnOxwCtHLCVFjW6FkBo00JXvPjbSy+20Tefy9eVXaB9XXXHaVo5Ts7jGJ JcXJWiXtI3Cn7M+rRqFlzMpa4s5ueiaz/zEEN66Qfqp75Iow0xPdIOGui2C5 AP4L5xzgAAChwS+cc4ADAIQGTuts9h/xSNFErsyB5qbNCM3Jl4b8mblNk7w0 V6KSbJpEkpeTpplNM7ev4dritTsdduvl6wzVs5u2Leed8uZpB8/F1fFjl0pZ Rbq2uGXQNF20JrJr0naraMYIkjXKUgvbG24dbEKoq3yiMQ8uCrhBAEFyhSwq /hsnHKYNEtARGvzGCYdpgwR0hAZO60L//1Mi0bT5K39GTjRf5OXvgJqRzYjM HNk0NxVJthmFpqIo9h975blzxTEJ9boc3yY6BqFBI7W0p9cvpTIeYXwdrZOx of9sRK/5CVVNdZ0ex/Ncc9Jc36fO9ZvPX9Q4NuRe1Y27Ei+vzdT89PPd35KL wuhv2zRrzESh1t5362C20uoTVTG6FhcukQF+G1cFhkxCIwDcxlWBIZPQCABO 69mf/SPISHTA0IyMfJIkI6eZeUmkuZoVSXOTNG1+mqbSNBEU1++7XCe3t9d7 1qZchqtSEGv/7QnrvMkNuXlKfNtekz2x7ueoQz2+6m5K/0uK6X8uRtOfkmnf oj/r1tZq4Yn5+npqqv5TCu1fpxWn7DnUDxpuec0EI4QyC11dUFbsG7JjKEnV ZuVjG148B4WyBgAIDYrnoFDWAAChgdPCCs2Ws5+AJDNJs7mRRDbNkNO8ATLz a6Z8ifzNqsW0jJMkHTS0LZMaarRzLtpEmjUb+7PlSvPq4HGYTG9jL9vhvPv+ SdOHXyfjt2Lmo0N3Zlzxa3f3zCWxHJcehet8I/sZ76dHXu5wXM1yOo6olHBE Rxm5iKFTRQpisBhWTouhcsE5PGGBAt4r5wqmbSQgAfTKuYJpGwlIAF4QGJYs /f9EEHWYiBJGwjBKQOTk5ObkRl6IbGZeZnYhC7o4ppHEWNtTY/vxZqzD/Yat c7rmLabfT73/ruleOMLkN9r7x44xNXo2hKp3WPPWJJcvaa1ZfrE002u3vptY ocMSNz1HCc3PL3P1SqorN50cdznuzlXHGWF6sqSoqFhJJFGRV0EpSAB+XEeV cRAOQhQCjuuoMg7CQYhCgBcLFqHZ5f8JBSYikQgiIZBNI0mbkS+vkX+AbMSc aR6nxXFssjgbpB1pxSzS/3KtEVGMzG3oUD+yNx/uGM1fyvq8pu3CyhG3lJ+K mKuxHptFWyaeea1x1cydEmP5M7Zfs6Vp09+0ro53hcZXXwmzKjuG3pru7WRH 7Uq122gQ21VOqK9mtViJcg4DN6OgJFQeHCcdpQOgAATHSUfpACgATkOZsc/K P0UjN5uZxEBoVkbzZxggGxEZBkjSzCSyaIhhoZPFsR1fmdVd7iW8im2XGEN5 TWPedPgYU40a5M32Ec+Jk5TZMUq91/tVlMGIX0h3jNw8NSdNe0z/Dtd48lQ+ tBWKEZCmvJv5ZSNETChx9t+YM6nbzyqk/mD1RpiGowSlGyHFisKsXwIaAN4b 5xxm5sAEoUFvnHOYmQMThAYuARgyVmZn/ynqMOZE6Gg0DBORMJY20vw5lVPZ /DKz42I6zMNocahmvLqoXt3jOZ3R/+4UaXq2o6SKmq9r9ZUzPTmEXnNzCPu3 Ui0vt8dfwzZb/8RtDfvxj0e393zsWQeeJb0doT2dCfcNa/FJYfJiH1rp39cb fWxKK0cNcRuVVNMa9RNMLPUprBBEKl16462IuMwnAE9nZ1MAAMA+AQAAAAAA 7s5oPAQAAACfQ3axG6CXmpGjm5mbmZ2UlpGimZmTkZmSkJyVo5Obm/4r5ysj BgCBkBl+5XxlxAAgEDKD05Rfsv9Tmk1zsjmRishL8ufLDJmJ5s9mJM3JZuSl 49BxHqeFjCRj+/uZzr+JuYPotJ9j/Jwgd6/YfR6MB2PQWV392XRps/IxfV5v jN9jWpOQUIjfM2ajmzrvm4TSDWP3qEOj7MFrbKXcc0pu17DakzKE8FshTjcL rkMLo0cOqf0HA2UeoqVHlfxWxQVeHEcVM0eQGwgVFMdRxcwR5AZCBU7rz/5/ ShuRki+SNv8AkS/JppUjN/JypamcaGaSibyGpEk86ke5s8nNdW/oJN6Vra/l 1Q1NnXLhWOSnS/yHR8N0O2QIpapZu6R3sHkTD5MNp19Hs/yd7h1Xfm7Mdmn2 tTX6WR0baLWuMCu5MHQVZyWssKxiEJf6WiuucxFg2gOekBkYHjznvWCigEGo IHjOe8FEAYNQgdO6UCYvVP4TCGleZiKJjIFEcyOSjEibmRMDNidthJyMJEmT PJlpmqZpIu61T30z/HoUJ/x1UrP+4niN/XWym90M0WanTJ15t5OotdIJafqJ emnX5lPtjilDzOH6doAJxp+RbCqEotRfKHdz/6ZbbymHpJuNuKtYjaI4DClX QsXFYjhC2wrMAP4bJw2kAQChwW+cNJAGAIQGTgs7/qcnOZLmpbkikjTJl+bI jBzZSpMkc8A0HRbGeTB2qMy1MBjSSTIqxIZz2J4vsEuq8+83siu9PbcsBx+b hHm/9zl7Ir/Z6tv0d3desQTPpxjPB5cS86HZTKOU/WvJF6E/Nvhorm4Rl2yp 2fvbEsSoNIgugyq1LWaoJGkIEQr+CycNpo8SGoVQx+AXThpMHyU0CqGOgdNQ sv//lKTZbP7MSNJmSAZsmm2aM0CkkZcbGalspoydFqui4ZVXjTrOdRMho/4x mXTTqusb2fWM6u84ENaffci12mE7XZaUUsP5UZnSSvV7rfkRdIVf+Z6i0J6y pt8/zDuFrBpLOR/HbuKgv/n+M/ZWrtbXLw5F9fhVrVauqno4E3cuXKmWSb1g a7YAHisPNQMAFIBYeagZAKAAOA2FZv9/knbAZjOb5mYkSUZmZUTWgAPmZvPJ lBkZ6bgwptOYYU5j+m67xdh9u33FNLLcJn/57TE2O+91S8Hbv+59Bg2laji3 8+FR/jhkTSYuK6wXf0kx+Hiz3NL0vLnymSJzMo5QaPhNvSmWOG4c2fo9ZjeO 0U1KaKVQb2QMNdW4qFFUG65ClSokQwGeK7c0oxAAALlySzMKAQDgtH7Jlj8+ aJpNMwaS5KWaNM2XL81JM7IhMzebmebPyUY2LyONZtOMZjUjVd/P+XzHOjie SUO1pm0dHe82P2vRQd6RbjZmZGR8Vpbq3CDGpke3TFYut4ijV+spPv/vXuZD 8D2zZ2PMFx29jq6V0mr/I9Nwyx3aDm+lOhNvs1eJsQoLFUGUqC48JAU+HCc6 JkYJCISeYThOdEyMEhAIPYPTevZ/+UFlkzSbDNhIIzM3cjIzZMqXRpIvr0Lk ZCN/Ns3KNlJlJ/WfNTt17PcnD4c735tJnUXzYp04f/Li8BdmRrffjm7HOmQD q/pFd+Mk7P2HZVilY/veZt1N3tex9vSaoonqBSkOB4JLEWfZ4xGlkLMRvUgl 1LKGZFlWkGodsRQM+lhiAn4L5w2ADQAAt3DeANgAAOA0lFn5yx8pm6S5GSqb ZCOj+SMnjUgkadI8kZuRP0PnoePYDIMmr4JnO8qLz+bdrme72Xm6cfk8uo3b newfZzOfz2pmh9xexaffjmnpXCZ269Xy7/TtfMq5wduMNzG4B3r9UErQPxsj x3RIK0b86eZmaScU0dZr9K4qQRrUjUZirCu+QoioJB6KAh4c5yNjIgCJsDME x/nImAhAIuwMTkP5z/6IJE0lqTS/JHIiLzNXRmUjY0DNkOSTYXFuhzEGwzA3 t76dGDvd1zC6vtz+Uc59Vv7Obt2tF/NsDOFvmFLi8s5XrQ4heQ3qfd3/de2h 8y6slElbb+YN3RDHiFnH7/XoLTvrTMimkc9uYNuULWl6O8qNaY3xk/omgRJc wuqPGA0oiuIulQVePCcqo+VAAk01KJ4TldFyIIGmGrgCYH75f0yOKFSMIOEw CGIEbZIR2bym8mnzZ3Yc2oV5IXNCTNlm/kZ7Rm2XkWTL5zae7fRq9oasrRjH 9Kjh+uSK4Ns2Tjn0jk83/m3Yc7t13+c43cx0b7bhOF3mn0nphhgsod/o/CDe ECPW2o1wtFSyqDtRCobesCpawDsDWhgA/msnNeNAAIQPfu2kZhwIgPCB07r8 /6cUmpvRJLKRP5uXmy/SnCSamZlm5Uub0USSl9toqk3BL735Xv8rv4wiO1dj XJPz1HisrZBf8+HLW+erzn4aY+92PvzBOB9/6RbFs24weuFQsVYIDd3dbSq9 5tT1LqXSpC2tMiopFPlwah65tcrfEAieo0J/Z1odkMZK1ohVPpAAHjsPKAMJ TBJA7DygDCQwSQBOh9D//0SzuSUbuUmakdPManPEgNlsKts0O0AaZEUJFQuS JiWtLK64Lps9SpF+4uH8/TrPW6MbrbVrvY4YnCO3hlxppa3X1p3vKf38vu32 tt+J+XnVfZxYVh1KudPndHbuRvRaKzI2ITw96h3ZRDXuqz4y0uJBTeQLJMSh MFK1VJ4bt40AAtAAcuO2EUAAGoAL2rDIcnb5nwICIlKYcNRBxESSijQjJ1+a m5kTrQzzNA5z1FDDMGmHTEMqOraR5mtrXA2pISVhZTtC3afeyciXR0M4VL/f 7vY2m3+s8eXU+We7mE+G18O9AyO/n7CNlDir4+HlfJ3lLSNqd9mFNY3w67tE 13n09v2Cr5/2xH0Ju52IFUtN5dzqB5Jop4jcgbFQKz4cJz1lAiARdoHhOOkp EwCJsAs4LdnL/wlJZHNEkkS+AdM0aQyQleTPJtkk0jbJSMZpWJg6TjFRGc1J MnZAxuHT7PD6S/9lb3qIYvuqV1Oj9PZ/t9Ttqda4rvG/1lAzEuLtJqOQzEuo ZRdiCM09Xc6f/+WZ2OBj3mGcpraCNdQxhMZETdRSFZRxVysF0avHcMdailuC GtYyAP4qz1AGCACAV3mGMkAAADgdQuyX/wORGjCjkshmJElmkpPbNDPbVCN/ RhIiI00js0Fes5G0MRs1pKHv6epsk16Vmmm/irb5hUloDmoS/3IghNsL23mZ pPCs87eeKa2aHi2f0CmmW698NKZ0DiWfC7vvdZuJx+doviiZqTufpvg4ndTg ktoEZ+xGNqHfQkl5ZYRqlmqRVAb0AX5KzwMGAABwSs8DBgAA4CUsrL9c/h/g MErcAQkpApE0yWbmNTfJyMnQyGiSkY1EXjQropI3ZVm788T7O2DNnrRk0ojx 3vE1G0q7e/ontgOFPvkMS3f+h7g5hLhyGqeV+Xdu3Y4Oe6beYw3/KPqHc7jF +d508/X2VYzYLOcr1RvTYklu6VrBzYXIHPLUWK1IwBCLCz4sJx1lAqAQFgzL SUeZACiEBVZJgEP+/09BGEVEJYMVoxZGkaFTF4c0f8k2MzIjkEbSIUnUrHR6 ry+7NG3XLqXZmh92Quza0p5zvNNxtDV01bt9rtqoTMeUPb/IezvGo4QwGb9f Qzn+t5+Q+j/p9Y4bhX/E1/30Ai6FV8f1mlafIhO2LUYlKYgjVSzkpQoFFQU+ DEcFsgYACA2G4ahA1gAAoYEV6zJbXv40DRnnhYxjx4Vpcazm5MsnSSIyIol0 ADIiL5pBVkk7Ru57Uhfqf1OHER/raynT5XC3ObpfkMHpbab0YphSM79ojILI X6d29IV47r7QmmyzZarVMEbNGjdTR6WnkWOdS7qpV2NY0vg7xJtqUbaqqKfM RiWOqFMMhQJhuRDOpWDgrgr+Gyc9pOUAEBr8xkkPaTkAhAauQMK6lP//KbQU V4yEQzsgjKycvCTbNJtGGjkZA0STSFNkVeUrykNHl8ak2Y+bsypEu537lJ5f tnWct+JNixImj5p3wwEzY8+NL/M3NJ4yLyXkrHOrhPF+8T6r2TDidjoowZBS PuwwmprHZYlo+5Vk/AJhoF6lFzEGoVbGYFFdA9775hygJWAAoPfNOUBLwACA 0zBj0/+JALI5DJibm5e2+WSkmubLTdP8ZKQZidk4lLFTZqL4inzW+v7vo/vv XHn0X/bVRcSmzL6GttcTcgXRqq2wQpzEfP58IGQ84qQh7RxHGs2jkW483FkZ aRVxvaFue7KrpDhSEhXJb7JmalXfaIYQkr+Qiuhcwhi4YymSCP4bJw2mYQAI GcBvnDSYhgEgZABOh8n+/5giEQNk5KlsRp78GZmhGUlaaU42V0bkBZWkbSoi m8gwD6g2YW5EijIMK4X0UlKfiMxSbn0f3WKdT+c3mOX3W0KsMcQ7nF2fw8aV c9O+O542JnOhP3YekzLLzZNezUuPe6c9He/IfGq/2+vHm8TlflqxGt29kbvo TrxKz52LatV71SspAF4cJxOjo4BEyAiK42RidBSQCBmB0yL0LPufqDVg/iSJ zMhpZh6RivwZTXKakZkXwZwMYmJqxnashQ5z2xp15JccW0Q2oTltjJFS6nD/ WSa5OEY+1g1XWEO7tE+fyy8PheQo23uZb8bwlkzk5F1sCb3Rvz7+yMrqz+NW 36pFtobqXvpGEK8tFXmlCANGSQFWUoZET4gE3ivnDaUBIBF2CHrlvKE0ACTC DoErARZZztj/EwmSwriCBJEogkhyQiJDNvJlZmQkmYdpIRZmTUdZTCOVJIhj Oqmje5syJvrxM0QbRf3sL9bzZNPr9eI+asxr/Xqxe8Xe+OkRns05yK+7I5SZ lcumqQ0nPzVJtbzJhw2KXvN0DG9Gs/FsBQida2vsJIl1R+a2JixLECKyuquo +ka5rw0lEkkjAR4c5x1joYBEhBMEx3nHWCggEeEETkMp//I/NZpNQw2QTSPN yJIhZBqgGdJKZuPikDGGIcyTWgjnm+hYsccSXzNc9ul06A/PVl0lHorja+/O X2aMEGJWJpSChodXG/TN+srLF8v0c2XSejh9p/eLcOj6rY9+7Ajd26Ba1aZG vdNZi1KTm+RIlBAhU5GobhUvEiqLCr4rVyRlIgEJYFeuSMpEAhKAFyFxCC3L /0eKmACH0XgoQYxUZpIxYE5GtrlNcppTWblpBFGNdKCZxojoKPu49j/GJPNc 7aHKxpJthXk7TG9X9KuzSqEejOlGS9EmyNqWbUnyvhfPXS3zXENX2PVj5w6+ Jq30tOZZu80p8AgxOLBKw9L7x41yaklrSzGudUcS4riXhmCFWAJWJSgAXiu3 JIUMAIBauSUpZAAAOG3yz85+StJGNrO5SeSrzKykkc3N6QBpTpKmkYi8UBqR FxFR/8weemKj7hrO7ddn6uVDWXvHaE7G7k5M9xtIx2znm17FXMNr825N/vhH jc8xfVQ+f9ToHGJ2jJA7ylF7Kx5nk/xrJD9q6W6N7RqNH1O/1szQSq8Lod2W qMKxruIO92KEKMCNgcslUAFPZ2dTAADAqgEAAAAAAO7OaDwFAAAAlOzSfxuh l5qnlaCWmpaVnZqcoZSjmpyenZygl5qfmp/+Kg8opSwwAfAqDyilLDAB4IVY rJeFZuX/BIqEspMSYZCIK6K52STJnxdpbkamnHyZcpuRaFaSCMabq37yG5N9 wxdaKZmgp70YrSmjGfovQ7bfJE+uRCFN5t3s67uWm8qzttffarPIf8px19q2 jtsDmS2f9CIYrax1Vjt2p0fdfoaNlVE+Wuq8PNQ7CP21hrD6o6nHY0xLETWm WmVEICoJBd4b5xxkZUABQG+cc5CVAQUAXkwspsz+p0/CYeB4mBSLJpyIK5GT NGfApJlpNo1sM9S0MEzDSBhOkF7smZ/i1X/ZKZZs+ymbvQ3tEdv1um0Y2376 6y4pvt1w868dy/G6XdQau53vntRaL0afME6PY5O+MtqlvhOareNqYP6IN93p PVvUFd5HzzRwhQV59SNSI46XhViWRAIeLCcMZQEAISBYThjKAgBCgNMiVH72 xwHRjGxmcwww0NDKC5HmSDLyRb6M5k1Gk3FqOw81dFpMja0aoifcourSctE+ H1L+nnOxKcWzeV2j/i/OoaTY3+AZ/ti4etfaju/TpRsns9+bs7fvYya7Gptl Ov+2h3konzp1vjxXXkiHo8S6pDiuqOnpqS8mfGYB8/FKuMOyuCuhYyoAfivn DYYcGCsAt3LeYMiBsQLgtF7Kkv1/isgTrTSbZOXPZg4kkoxsTjYnt5GbISNN IlMS0Qx5CUm7tNl0jrMas5KfYpt5L6UsiGtrs9lxzNb/3KdZ06drdPZ8U5p6 6PxM/YqzcjS3Gzy/m7y4/dGt/Rdj/0y2NDq5p0/rtXuW9j0/JU66MWTD6sWa hJmok1GkX78Z1YYwEkV41G1kqeEO7iDprqWgIgLeC+c0oAMAYUEvnNOADgCE BQ4Lu5T/dKJskJFmB1KpbL4YcBqnhcXMXZwW0ixmzjB1HrLYGjuMqZrMNQ4d DOt7P/TqE0U01Lj/oVvDdLesA2k6uQ5kQ+cSEUzO7hkVzihwwuOer9tKLJnS sPX0E4IyTg+pzLN6pr29GDWUdVPyqoRQjs22EGKNd8UhLw2GEAYECwwXRD4c Rx5zjIRGJvQAhuPIY46R0MiEHoDTuiz//6lJZv5mZrORkYbMJNI0J7+MnKSy kaaZmUlenlCh2SRNYsPdGoq8tuc8Tvmo06N8ZTN6P11s7zzGozq1lT94D4VO q9/MxJJ8xtsY1t/5PJm73bJqerv7/VrT6Wy3Sl2vq9H30kvzsm4YP6GfHwTX stb61Ps6uTSQ3rpUQ1kEqbcq2gGSgAr+G+cKpkYFIBj8xrmCqVEBCAZOQ/kv /1No/ozEQCRJZk6zuRlpQnNzI3Iz07zqpIsZFkdjQHgzbeazI37h+Ad3+nZX 5z3ZrGXVqz+dD0p5zc13jLy2tb2tMGaP9bnHKfPY21Naqdc9XcIs1YpVDPJd rTi99qQ6jmNaCFG+qHdamZfBpNYSSZftStGK6qxESM8Fg0/yhAD+a+edpIGD QNg1/Np5J2ngIBB2DU6HZP/HT5FNkySbKSLNyUxy8mfz5EsjSTPS0Ig8SW5a MtMMGRrCLMnAlMrSl1sUqzveTctwbRkRrzRRWpPj6SQ0UGMRcqGcG9w2K1N9 1l2apavh/aQ+C8xfrsMxuy+M9Nk0O3in3P/GCk9luBubmy358pmMO9aCXBHJ uFGgr94lEQzZQXEB3hsnBWAZYIBQQW+cFIBlgAFCBU7rUsr/f6Jp/mY2yc1r RjJQkZM/N3/+ZjaNRlY6YI5I5MiUigj/q+fN3PjRxWaH9f+ejOpSjjr1dh6Z ipRfybdgqa3+tjj8bOAzxS7+TDdXCJvD/sknvVCMpdEsl3yNupGUn9ZJGebb sEY8UpzOtJWhS7FUQkgFI1pGDDwxFgwG1woAfixHhbIAgLDgWI4KZQEAYYHT Ii//P1E1V/78zc2RmT9ym+ZFIrPyS5KcRkZ1XuicIUmGiDFdyKDR0vyT3P+x 3dVS2rcVx/46rXjXsJ2+u72l8AjZs3RjXSV+ihvupMTpt/f+NKcHk+1E9zz9 a7WDcPd7pz8xtuKjdD1v00pi6vZC7akrqivFDCnRH1JQsKqEinHdAhZ+LCeq 4CAAjdDgWE5UwUEAGqGBi0WwlIwt/+PkIBoPEwgrYhSI3LTkz0nbjDYZp2HR 1Ek6FxYGo6EGEcke7jW3/0g2uHds0rl3eJ3/sH3LJLuBSSplt0P19jYhVLoa xqMRl9pJt7lOjTLU6dq885HqKbvb7Xy8Q3aN2hBTk3WTesv0p13rM9MffiXO hzLGDS2F2JvLXUMZREIyVEIC/ivnDWVHAYHwwa+cN5QdBQTCB05DofJnf0pk 0sycZiRpMtAk2zSbzchLk2wkMpsYzQuaKcNsGLJ4+yvv+XaUapMRX+tdafIp IY5OvbkZ40nfuwfmk3YidB6L6bfN3Gqn5kPbrhZW3TxLPS/6aeobo8SpI3v9 uhd3QWrqjRLG0Uc3mBIT503d4S7NFNwRVVZgNyjBgmdhMlQKAx4cJx1mZQIS hdAgOE46zMoEJAqhgdO6/Oz/qeRmZuZPsvmTZsqVyk0N0HTAJMmURCMvJ0+a RBva+r9GGXEVV7tL+dneeREy7V5ucscLpdl4VGlMORXfjdv4AM12s8ZJSIpZ uPDY8dvw8bo+yecPlnFCyWR7pzSsXS1m4fNMitaeSd4EMT2FdUqyUucqFZlH uIWakqtCrNQGCpQrAR4cJx1lAQChguA46SgLAAgVOC0yY/+fkuiAeRI5eZGZ zcknjXwZGfkjIyPNSdNs23meFjsvRgyLGZq5U2Ic1NjWV0dHZ37IpeN0/cMo JfbmuSO7TIfdMZjoWIcaYmnmxRDdkH37JuPpNuTq9n8cTH1m5uqY0vzjphTH 77xiqcF4nb6ht3j+ekXQz5xrHYO9yriUbpjrl05CGixqN4gMmFMW/joPMQUE oAC8zkNMAQEoAE6HLOXlp3HSvGpm/kQ2MkJO5OWmyUAjo7KVJZvNS4NIyUGb JpmHStv2CC1/3dDs/oxXN20ou+tvbbwjiKuLTA2zrrFkp59vZv1Nj8yPJGr9 Y5eJvRHctydtLu/Waqhf++fQPvT6fvds11r/DryhqpnSKwPHXrl0JWPBK9Z1 r3stBASgAD4sJxKmDwJ1UAgNhuVEwvRBoA4KoYHTumQs+/9EaRpJIzM3I1fa tFkDzcyf5mUkzchmRpIRiTQnm0Y2t1HVx4m350Rcfr2icr/rmF+lXu5vfD0f 4/ByX9z8CBtbLz9CH1U+tCcvJnvH455t8vmlRrqz+tmeEvYJu259fY57LPln CVl/k7Y4azfYljVuyK4RQ7cKYcElI8SIknuiSq4wIxTXDQFeTMcVsg9GQSQs KKbjCtkHoyASFjgtjP3yf4pI0iQ3zZ9kM5sVMWB0gCTSzFRGKn90RDNNBlF0 1AxYaCrzxr/arpf5EK6cRtL7WCmT7uVOJu9vHPd1daeff62wbYVLerEbNx4K Vlqd5CPrZru+fk+jjg6dSmWNEGd1aMg/tJtl3jqI/LNWoj+goX2la0Y99jFC UaVwB0W0KuQB/jtXvKQBIGCG37niJQ0AATM4LULln/1TExmaIRv5B5INkYCc NH9GyEgiI51j6LQ4DIak0zhmrkHGQdvI+z0zinKFzhit38o/1/g1gVKXXjNx 8xvVlUYa5D+yMdUQ4ia+xV02aj3jdLgT79ml8XXTfq0NmIe77MD9/I9/pKTG 9XmGRy8wPX2Wldra7Yt4fm/YJauGVJ5qtnJdaFoXfiu3tIBMoQHgVm5pAZlC A4DT8ezZfwq5Ii8zibS5zWma5mYzDTSbzWgjXyaSnJz8ERJpmtBJZKhhDGk1 /M9Oucf23/Gm1319jUU3l3bR6bI6dj+lvvHFnuq1Ifsz4VfFKhcaX6fuWrnx +vul4tl/nqC9q1ftiPXsbHNTux/bnG/ffXZ6HWJSywjls3n9cYNGqGeqgVqZ 4BorUIuEsQA+LCccpidggPDBsJxwmJ6AAcIHTuvyGcv+MTWy+XIzM8gmyYAD Rkb+JMnmNaNpbvMym4jIa25kVBNE490YYa6Zj4iIcDyw/27K6dsvcdfrKa/h c+tP+h1LCda9p260y2c3J7W9XI+XL9TsrnfqL+4taZY07NOcuPSaNSR76y2x 2Th0nu9gdKt6WQRTWoNVHovnaLksQWUtikQkFrAAvltXnaShDgrAbl11koY6 KABe6CWHUMb+/2SCIADFg2gYiZGURCKjksxE/ohmM7KRtDJk5DWywWBORzqO ICK/KkwWNkz/+r4HMV/HtEHmPh7pQ/bzGQxhfVZVU5kat+FX/U4/6pBpbhK0 8vNTrftWWDr2j3eNML3yymO8joar2KBSZ9vC6q7Q2oURxnJz22iF6Sh9rIE7 SZUFMRREniu3jaCjgECoC+TKbSPoKCAQ6gJOiyzly/IfCPkz0wgGkpeX5skv I83Jl83NRiXNGo1ZmBY1jaRz5o7zMCWppjq4dz82OLbanzUzYvapYxevh7Js 0B7ls/Jqv6GxuDqF7Fj1Ob+3jmMab8RDbvradwu3XX+tWKU4fdT7CaN+G+qY PlXrs1Rr3MzYVjHVVR9FyAfbWSJxFQrVWIplVTGyAN4qDyEGCzQAtMpDiMEC DQBO63L5/yMQUbkif2QOEPmr+ZIYMCIzSSrymiSSnEaOJrkaerWMqxheR4v+ 88Bca7r0Xmts1TepQ8/UumLeGtbNdvjyarrWWumOhrtnfZLjlH3H/CMcKONz 02RT7tf4btkTlJrs6fdy6kqZ82S9SbfU6Y0FQYzh3mF4mNfSYCFcNR0SrIAE 6wI+LAcR0nMACAuG5SBCeg4AYYHXwjIUmi3ZPybCCkxIKOyIyKZpOkBO5FW0 GCx2zMI4kUSMq+9h61TkCNhCKrHDed6rh9dttfIN20kpteblqYPDn9hosqJ8 bo0S3azdsVGU82tD+Efu2I8TtvdfsT1702s2btfxzISOt/TKqb33dRWE+moZ H7kbWsRzlKqIx+UoiXcCwg6X/iIC3ivnDGUNoG4QGvTKOUNZA6gbhAZO69n/ P1HlJM3Iy0bTzETkyw6YkZlImiQZ0czcbEZzc5tEhiaNphGv2jF89v3dlH53 euozlmSvf2W1++16Q+4+3t3TDh16HwK9OJSGZufJ6fKn8b3XzUjav1HOTWXy R12v/+sGvyBTzjHs/ijtrpGfRYGHFFKvmOj7zGMRMC1KfvoxqvS4cG/hLjwB /hsnDeQAAGHBb5w0kAMAhAVOy2x59icCIjIyMpPsgPkzJRlJNn9eRpIr0ubk 5E/zpIZRujDNSYYOw9B2EJnLzO8E21PdKcHptqRs/m1wCfWwX+NY4jZtyPS4 yIaHq32CsLnXDpzwrV9a7VXXs5VCS5rJzmfYb0NMMi/ihm0pT+hYH5+PW1qD ry33DlEZayVzz5oJMXApAtsQJP4bJx2k5QAQKviNkw7ScgAIFTgN5Yz9/xRJ RJLkNjKkmfkyc/NlDBDRyElz5KaRGjqNY+YOzYju6Entjq8LsezZlfhrn6fX rSU13j3u959utiHYjdDTNcLPNDby2iCdvvO4+Wv05v3IzcaJ9bb6pZGY3lzN e9dI6XWK7hJTeWa6tdhusExNvTvUdUUrDR+7sW5Bw0p+SwguSYQ0iVgIA09n Z1MAAMAWAgAAAAAA7s5oPAYAAAAhUebWG56blp2PnpyXmaKWopuio5eVoJuZ j5ubmI6enl4chwkzcwAIFRTHYcLMHABCBU5LVpZl+R+IyMsOICMnjWykmQM2 IyMR2YzMNK9yM2MYzOZmaJukiai5GcGeL4zzoyG1TV7PSZfr2hufTxkHnrC6 y9zvk0nxNTjx3e36SKGUR5pixEm5T+Kk347Vit/97u1B5z3bds/tsP9uytqN FKaudu1UJ7uBV8qqP6gqtSYiSs2sHOqyLGVECawI/gtXHKOjAIQKfuGKY3QU gFCB0yL//xMlicwkSXPSHGKgiZxso2lkxkCyJTIzZlgYxmmIsaXGYYZBZEje 59Tq3GuKxxFTJicu/RD7j1JT6N1aYrPapVrX6y+EPStpcrrdeM135eu+/8J0 /mNbtxPj7W3KpzS6zelPp/reoRS91iBbL2s8+Wyl+vKGUSq/+biiX2n8qK7A Eq4qMADeK7e1oKOACdArt7Wgo4AJ4CKBRWbs8v9EKAeKEMpBGAauyDYzHWjb nDTNiXHumHahRlRNMWuJYPxye1cpJZTp6Wyc3mzUkj8nZZR71id3j9eKrRqj Z83F6fOzzML08pt8OkaUwuU5u6/jymVzrRX6+V0cr2K4V1khT7i13eAOiU+N tV+sYBlJSWwri+iQZSXqsGOAgAjeOg8gRmgG6SEUQOs8gBihGaSHUABeIMYw Y5fZ/0TcBARBNAxjIUlRubIhI01yIs1L0oEaxjkd5qS0prtXraXb6G5a2mPP LpaWoe99V25G4hH2PI/CLs6a4RUrOa9f6+Y2bCE9ulXuhKZ76Q+zEfePfkRQ SrqNYpQ71OrhZ4OSLeStBitOXkvJrTtmqb8410SkJzZaSiUKYIwLUcUF3gvn DGABAKFBL5wzgAUAhAZOi5T//wiSjGZzk0rlJhnZppWRm9tkwAwy8iLJP47T aFFjSLUZI5kNRQc91qzz1GtHuOyT9+O30suodtrbb4+GcxzKRp/QvOFYWhkr tWTS+LXS7RpFGVPuCH9I0ZXuWa8ahDe5KMxrnyggxsh1z5QSi2ysVOk2VV3O LroERQU+HCcdYyUBk0JoMBwnHWMlAZNCaOBCgEV+9v8ApGgkjBIk4mEYBEFe jpxcmXl5eTnSeWFhbKqjmZiTdgiJDGxfoQtvxpr8Wqm9dXpS3OzBcvKzOw/d fsXee6q5mvNcyFtrfscqFSG/28nQ74US34f0+unafNyWtD6huec51tsbxvg8 P5+6NojBumuWYog7d1hVYYjqIurlBiQ1BhIGAJ5rt6WkEQUaQK7dlpJGFGgA TouUQuyz/yTJymeAXJFmicjIlzYzmxc52ZAdsGmmdjRkkpgm49iYMkho5Ckl babAMpe1Rnb35bxybYhy3PxwBSaONm1OJdfdHQ8IYa+Jsf3+fLvncDOlU+M1 fR1hPLfzE76szSK1Nq2Gg7XHygqt1kes9ybxjnutzkkOT9IKrCC9urMhLLIs 4rQAKj4btyWGXAAAzMZtiSEXAABO60Kzs+w/kJPNRqQq8mXkNTKymYlmpAM2 n2iSNjdPmyDSNEnJssapvvCRl2+qau42jXdWzEmxWdlAfruzSLHx5r6ye319 2dJq51fx3bG+jrEfadf4am8cnw3mO2Gs1fWMSH1K5hw1quF42XnMc+MQNATL svloJCmtpiG4fZfkVikakeMA6wKeK+c1BiwAQK6c1xiwAABO60Is+/+TnNxo k6Tyt5HNS3KyuWmTvJzMJHKTbLMJ+fM1FWmaNE3+5fNtcW2XnV0+HYIyjJ+H Xd/ffmS8HzGWK9PnOVq3doeNdkNOzY9Usn/UeedUUu3v+lV8E0MhxPn0L8aw 58oGvZua7xrzZBtE8ygGFow+SVgp3mJ1UMYjuVeSWLp1EKa5LC4ebCcipWUE E4SAYDsRKS0jmCAEOK0Lvfz/lCY0N6+RZg40g0hkRE5m/ozIaGQmSZrkRUY2 I0lSiUjSiH28i3Eib1xDTwRDja2GXDc0vd66O1xKdN5AUWeP4Hy269z40bn7 1C7XY3ryau+d4hheHJs1ZrsHn+qsf+xUS3Y4OGt1Y4Oo+TrMjXSbaxUHVlmN QqJcY1+SZ0VlIGxatfzChEvigQT+WycTpUYUIAT81slEqREFCAFO6/L/T5gk zUjSdiAy8nKyaciL5ktyKzdDZirJisx8yKZJm0RLuZljr5iEMiJs/MzKdJz7 TUI3xREm2fD/2Ffflge3lJhehTv7dGr9bcj4vV1Pm7Pz3Se05cYJunR0sPXe kpZoZGwOgttM63V20yoiBCUcP16VlcXaLdJ8UkxUBNa6iQLeKycNZmVAAyGg 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 :opus: !binary |- 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 LaNlfuw5gpRYuoJVQ2n4Y9AqUjjQOO7N+VjyXIft21CBTSBBKaCr7lNnN5+e iXspR1BfFzqIPz2UQ42wetZY32AXjJqOBs4lqufab682Y9VKh474U/yhgvAJ //ag/6//8Ar8LfehjfnstdCj+MPVgzFIcghq5d4cK7e/CT6FUC8QnW0tAqsV ozYRV1ie/wwD5f9v1Ld2rMWWnwGgq6pjbYK3cH/BJbFc7Nq4JGoh5BtkHZXd aRSjUkDJkvgnWeQgqAT/82BouUD2rf9j+qeNc1ZE3o1wXbpEGyka+SBfFdPx QUzGMWTWVa8vry0115KhMuv/7W1/9f+qAzMK/AKEBY1w+riyyCO1je1IiP4B xrz/26fvdw4Mb1Az+Ota/5HqmaaBaTm3VVa/ouJkCAxr9/bbDcDVCAsq+Xgo 9uhHPJVYf9d6tCzKIXKQ50mC1UyM83S+58QtwyINseHabuxavKkNitt/dOzJ 4UR2YMwJuZOdwvI8RtzAkVlgcTxDfP5XOA0K4Vw2eZHRNcxO2PAkAAn6Clrz PwD8LoBdcqMIgPoFKEYc5xHRuYPCF9aV2s0LBrw4nftEenArNN9XmX2CAJvg ZVlvu7+6p2gZ2Bo3sS1K7N5UcKqZVzMAWO5Hy8JqurlHQQOchCyIVsjEdVvm LO7YHg3ZM9axbrNkIRwtEFoqiR6akgJicvBqiHUNC0kB2xOQNZ0GrGys3Mk4 glYfMivHdaMFa7TwJJAt9f+gADCg/C33oe4FvkigEN6++JPdSchO9e3GAIqW AUgdultCe/wHZO2RjPYUM+UJWlo3iavxf4CatCtVcAf5Dklxr7eZVt1fuI21 rO1Ds6nBftbrbbLYo86n3uAkt2XtqRVRo68cldY5Rim++n0KWJvnJPPiYsVQ kFSUuM0i/08ca3VsPh5WilQ8iV476kVI+KZFTfxv47AkJDZVD/9TDPr8LoDN YtOqz2bZg37QNj1ABwqE5MAIwtIjAUqUUUK/nzCHNQK+k38S4HRv5o+QeHiy QgmCCGECImfGTD4gfhUWURRbekiHSfp7XZwdBt6t8PyjsYWliUktZBX8sBpf Kd2ozgirs604EM6HGm1/sMnH/Tq1mWXgixU1ri/zhMunozg5Sh4dSaK0FvCC eI3ZD+FRJ7/tbAkACl/zAAX8AoKoxEPpdlpKgzr49S5xoT+Uadty9NLJ4XKh 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 mBky2XPIMIDQiZOIgDYlvbYCRICgDwUKX1A= :speex: !binary |- T2dnUwACAAAAAAAAAAAZNfsyAAAAAHkWS1UBUFNwZWV4ICAgMS4ycmMxAAAA AAAAAAAAAAAAAAABAAAAUAAAAIC7AAACAAAABAAAAAIAAAD/////gAIAAAAA AAABAAAAAAAAAAAAAAAAAAAAT2dnUwAAAAAAAAAAAAAZNfsyAQAAAEPQyZEB XBkAAABFbmNvZGVkIHdpdGggU3BlZXggMS4ycmMxAwAAABIAAABhdXRob3I9 c3B4aW5mb3Rlc3QRAAAAdGl0bGU9U3B4SW5mb1Rlc3QMAAAAdGVzdD1oZWxs b8OpT2dnUwAEAAAAAAAAAAAZNfsyAgAAADtmIIoBXXTAH06NzRAAAL////// /+22222220SAX///////9tttttttoQAv///////7bbbbbbbQgBf///////22 2222222dsFXV1dXVhV1dXV1YVdXV1dWFXV1dXVydsAAAPw== ruby-ogginfo-0.7.2/setup.rb0000644000004100000410000010713612261213656015660 0ustar www-datawww-data# # setup.rb # # Copyright (c) 2000-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read } end end unless Errno.const_defined?(:ENOTEMPTY) # Windows? module Errno class ENOTEMPTY # We do not raise this exception, implementation is not needed. end end end def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted Windows' stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end class ConfigTable include Enumerable def initialize(rbconfig) @rbconfig = rbconfig @items = [] @table = {} # options @install_prefix = nil @config_opt = nil @verbose = true @no_harm = false end attr_accessor :install_prefix attr_accessor :config_opt attr_writer :verbose def verbose? @verbose end attr_writer :no_harm def no_harm? @no_harm end def [](key) lookup(key).resolve(self) end def []=(key, val) lookup(key).set val end def names @items.map {|i| i.name } end def each(&block) @items.each(&block) end def key?(name) @table.key?(name) end def lookup(name) @table[name] or setup_rb_error "no such config item: #{name}" end def add(item) @items.push item @table[item.name] = item end def remove(name) item = lookup(name) @items.delete_if {|i| i.name == name } @table.delete_if {|name, i| i.name == name } item end def load_script(path, inst = nil) if File.file?(path) MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path end end def savefile '.config' end def load_savefile begin File.foreach(savefile()) do |line| k, v = *line.split(/=/, 2) self[k] = v.strip end rescue Errno::ENOENT setup_rb_error $!.message + "\n#{File.basename($0)} config first" end end def save @items.each {|i| i.value } File.open(savefile(), 'w') {|f| @items.each do |i| f.printf "%s=%s\n", i.name, i.value if i.value? and i.value end } end def load_standard_entries standard_entries(@rbconfig).each do |ent| add ent end end def standard_entries(rbconfig) c = rbconfig rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] # V > 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = c['rubylibdir'] librubyverarch = c['archdir'] siteruby = c['sitedir'] siterubyver = c['sitelibdir'] siterubyverarch = c['sitearchdir'] elsif newpath_p # 1.4.4 <= V <= 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = c['sitedir'] siterubyver = "$siteruby/#{version}" siterubyverarch = "$siterubyver/#{c['arch']}" else # V < 1.4.4 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" siterubyver = siteruby siterubyverarch = "$siterubyver/#{c['arch']}" end parameterize = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') } if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end [ ExecItem.new('installdirs', 'std/site/home', 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ {|val, table| case val when 'std' table['rbdir'] = '$librubyver' table['sodir'] = '$librubyverarch' when 'site' table['rbdir'] = '$siterubyver' table['sodir'] = '$siterubyverarch' when 'home' setup_rb_error '$HOME was not set' unless ENV['HOME'] table['prefix'] = ENV['HOME'] table['rbdir'] = '$libdir/ruby' table['sodir'] = '$libdir/ruby' end }, PathItem.new('prefix', 'path', c['prefix'], 'path prefix of target environment'), PathItem.new('bindir', 'path', parameterize.call(c['bindir']), 'the directory for commands'), PathItem.new('libdir', 'path', parameterize.call(c['libdir']), 'the directory for libraries'), PathItem.new('datadir', 'path', parameterize.call(c['datadir']), 'the directory for shared data'), PathItem.new('mandir', 'path', parameterize.call(c['mandir']), 'the directory for man pages'), PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), 'the directory for system configuration files'), PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), 'the directory for local state data'), PathItem.new('libruby', 'path', libruby, 'the directory for ruby libraries'), PathItem.new('librubyver', 'path', librubyver, 'the directory for standard ruby libraries'), PathItem.new('librubyverarch', 'path', librubyverarch, 'the directory for standard ruby extensions'), PathItem.new('siteruby', 'path', siteruby, 'the directory for version-independent aux ruby libraries'), PathItem.new('siterubyver', 'path', siterubyver, 'the directory for aux ruby libraries'), PathItem.new('siterubyverarch', 'path', siterubyverarch, 'the directory for aux ruby binaries'), PathItem.new('rbdir', 'path', '$siterubyver', 'the directory for ruby scripts'), PathItem.new('sodir', 'path', '$siterubyverarch', 'the directory for ruby extentions'), PathItem.new('rubypath', 'path', rubypath, 'the path to set to #! line'), ProgramItem.new('rubyprog', 'name', rubypath, 'the ruby program using for installation'), ProgramItem.new('makeprog', 'name', makeprog, 'the make program to compile ruby extentions'), SelectItem.new('shebang', 'all/ruby/never', 'ruby', 'shebang line (#!) editing mode'), BoolItem.new('without-ext', 'yes/no', 'no', 'does not compile/install ruby extentions') ] end private :standard_entries def load_multipackage_entries multipackage_entries().each do |ent| add ent end end def multipackage_entries [ PackageSelectionItem.new('with', 'name,name...', '', 'ALL', 'package names that you want to install'), PackageSelectionItem.new('without', 'name,name...', '', 'NONE', 'package names that you do not want to install') ] end private :multipackage_entries ALIASES = { 'std-ruby' => 'librubyver', 'stdruby' => 'librubyver', 'rubylibdir' => 'librubyver', 'archdir' => 'librubyverarch', 'site-ruby-common' => 'siteruby', # For backward compatibility 'site-ruby' => 'siterubyver', # For backward compatibility 'bin-dir' => 'bindir', 'bin-dir' => 'bindir', 'rb-dir' => 'rbdir', 'so-dir' => 'sodir', 'data-dir' => 'datadir', 'ruby-path' => 'rubypath', 'ruby-prog' => 'rubyprog', 'ruby' => 'rubyprog', 'make-prog' => 'makeprog', 'make' => 'makeprog' } def fixup ALIASES.each do |ali, name| @table[ali] = @table[name] end end def options_re /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ end def parse_opt(opt) m = options_re().match(opt) or setup_rb_error "config: unknown option #{opt}" m.to_a[1,2] end def dllext @rbconfig['DLEXT'] end def value_config?(name) lookup(name).value? end class Item def initialize(name, template, default, desc) @name = name.freeze @template = template @value = default @default = default @description = desc end attr_reader :name attr_reader :description attr_accessor :default alias help_default default def help_opt "--#{@name}=#{@template}" end def value? true end def value @value end def resolve(table) @value.gsub(%r<\$([^/]+)>) { table[$1] } end def set(val) @value = check(val) end private def check(val) setup_rb_error "config: --#{name} requires argument" unless val val end end class BoolItem < Item def config_type 'bool' end def help_opt "--#{@name}" end private def check(val) return 'yes' unless val case val when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' else setup_rb_error "config: --#{@name} accepts only yes/no for argument" end end end class PathItem < Item def config_type 'path' end private def check(path) setup_rb_error "config: --#{@name} requires argument" unless path path[0,1] == '$' ? path : File.expand_path(path) end end class ProgramItem < Item def config_type 'program' end end class SelectItem < Item def initialize(name, selection, default, desc) super @ok = selection.split('/') end def config_type 'select' end private def check(val) unless @ok.include?(val.strip) setup_rb_error "config: use --#{@name}=#{@template} (#{val})" end val.strip end end class ExecItem < Item def initialize(name, selection, desc, &block) super name, selection, nil, desc @ok = selection.split('/') @action = block end def config_type 'exec' end def value? false end def resolve(table) setup_rb_error "$#{name()} wrongly used as option value" end undef set def evaluate(val, table) v = val.strip.downcase unless @ok.include?(v) setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" end @action.call v, table end end class PackageSelectionItem < Item def initialize(name, template, default, help_default, desc) super name, template, default, desc @help_default = help_default end attr_reader :help_default def config_type 'package' end private def check(val) unless File.dir?("packages/#{val}") setup_rb_error "config: no such package: #{val}" end val end end class MetaConfigEnvironment def initialize(config, installer) @config = config @installer = installer end def config_names @config.names end def config?(name) @config.key?(name) end def bool_config?(name) @config.lookup(name).config_type == 'bool' end def path_config?(name) @config.lookup(name).config_type == 'path' end def value_config?(name) @config.lookup(name).config_type != 'exec' end def add_config(item) @config.add item end def add_bool_config(name, default, desc) @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) end def add_path_config(name, default, desc) @config.add PathItem.new(name, 'path', default, desc) end def set_config_default(name, default) @config.lookup(name).default = default end def remove_config(name) @config.remove(name) end # For only multipackage def packages raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer @installer.packages end # For only multipackage def declare_packages(list) raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer @installer.packages = list end end end # class ConfigTable # This module requires: #verbose?, #no_harm? module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # Does not check '/', it's too abnormal. dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(path) $stderr.puts "rm -f #{path}" if verbose? return if no_harm? force_remove_file path end def rm_rf(path) $stderr.puts "rm -rf #{path}" if verbose? return if no_harm? remove_tree path end def remove_tree(path) if File.symlink?(path) remove_file path elsif File.dir?(path) remove_tree0 path else force_remove_file path end end def remove_tree0(path) Dir.foreach(path) do |ent| next if ent == '.' next if ent == '..' entpath = "#{path}/#{ent}" if File.symlink?(entpath) remove_file entpath elsif File.dir?(entpath) remove_tree0 entpath else force_remove_file entpath end end begin Dir.rmdir path rescue Errno::ENOTEMPTY # directory may not be empty end end def move_file(src, dest) force_remove_file dest begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def force_remove_file(path) begin remove_file path rescue end end def remove_file(path) File.chmod 0777, path File.unlink path end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(*args) $stderr.puts args.join(' ') if verbose? system(*args) or raise RuntimeError, "system(#{args.map{|a| a.inspect }.join(' ')}) failed" end def ruby(*args) command config('rubyprog'), *args end def make(task = nil) command(*[config('makeprog'), task].compact) end def extdir?(dir) File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") end def files_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.file?("#{dir}/#{ent}") } } end DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) def directories_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT } end end # This module requires: #srcdir_root, #objdir_root, #relpath module HookScriptAPI def get_config(key) @config[key] end alias config get_config # obsolete: use metaconfig to change configuration def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file?(srcfile(path)) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.4.1' Copyright = 'Copyright (c) 2000-2006 Minero Aoki' TASKS = [ [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'test', 'run all tests in test/' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke config = ConfigTable.new(load_rbconfig()) config.load_standard_entries config.load_multipackage_entries if multipackage? config.fixup klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) klass.new(File.dirname($0), config).invoke end def ToplevelInstaller.multipackage? File.dir?(File.dirname($0) + '/packages') end def ToplevelInstaller.load_rbconfig if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) load File.expand_path(arg.split(/=/, 2)[1]) $".push 'rbconfig.rb' else require 'rbconfig' end ::Config::CONFIG end def initialize(ardir_root, config) @ardir = File.expand_path(ardir_root) @config = config # cache @valid_task_re = nil end def config(key) @config[key] end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs case task = parsearg_global() when nil, 'all' parsearg_config init_installers exec_config exec_setup exec_install else case task when 'config', 'test' ; when 'clean', 'distclean' @config.load_savefile if File.exist?(@config.savefile) else @config.load_savefile end __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig" end def init_installers @installer = Installer.new(@config, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global while arg = ARGV.shift case arg when /\A\w+\z/ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) return arg when '-q', '--quiet' @config.verbose = false when '--verbose' @config.verbose = true when '--help' print_usage $stdout exit 0 when '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else setup_rb_error "unknown global option '#{arg}'" end end nil end def valid_task?(t) valid_task_re() =~ t end def valid_task_re @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ end def parsearg_no_options unless ARGV.empty? task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options alias parsearg_test parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config evalopt = [] set = [] @config.config_opt = [] while i = ARGV.shift if /\A--?\z/ =~ i @config.config_opt = ARGV.dup break end name, value = *@config.parse_opt(i) if @config.value_config?(name) @config[name] = value else evalopt.push [name, value] end set.push name end evalopt.each do |name, value| @config.lookup(name).evaluate value, @config end # Check if configuration is valid set.each do |n| @config[n] if @config.value_config?(n) end end def parsearg_install @config.no_harm = false @config.install_prefix = '' while a = ARGV.shift case a when '--no-harm' @config.no_harm = true when /\A--prefix=/ path = a.split(/=/, 2)[1] path = File.expand_path(path) unless path[0,1] == '/' @config.install_prefix = path else setup_rb_error "install: unknown option #{a}" end end end def print_usage(out) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, ' --help', 'print this message' out.printf fmt, ' --version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf fmt, name, desc end fmt = " %-24s %s [%s]\n" out.puts out.puts 'Options for CONFIG or ALL:' @config.each do |item| out.printf fmt, item.help_opt, item.description, item.help_default end out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts out.puts 'Options for INSTALL:' out.printf fmt, '--no-harm', 'only display what to do if given', 'off' out.printf fmt, '--prefix=path', 'install path prefix', '' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_test @installer.exec_test end def exec_show @config.each do |i| printf "%-20s %s\n", i.name, i.value if i.value? end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end # class ToplevelInstaller class ToplevelInstallerMulti < ToplevelInstaller include FileOperations def initialize(ardir_root, config) super @packages = directories_of("#{@ardir}/packages") raise 'no package exists' if @packages.empty? @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig", self @packages.each do |name| @config.load_script "#{@ardir}/packages/#{name}/metaconfig" end end attr_reader :packages def packages=(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_test run_hook 'pre-test' each_selected_installers {|inst| inst.exec_test } run_hook 'post-test' end def exec_clean rm_f @config.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f @config.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if verbose? Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def run_hook(id) @root_installer.run_hook id end # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end end # class ToplevelInstallerMulti class Installer FILETYPES = %w( bin lib ext data conf man ) include FileOperations include HookScriptAPI def initialize(config, srcroot, objroot) @config = config @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end def noop(rel) end # # Hook Script API base methods # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # Config Access # # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end def verbose_off begin save, @config.verbose = @config.verbose?, false yield ensure @config.verbose = save end end # # TASK config # def exec_config exec_task_traverse 'config' end alias config_dir_bin noop alias config_dir_lib noop def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end alias config_dir_data noop alias config_dir_conf noop alias config_dir_man noop def extconf ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) files_of(curr_srcdir()).each do |fname| update_shebang_line "#{curr_srcdir()}/#{fname}" end end alias setup_dir_lib noop def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end alias setup_dir_data noop alias setup_dir_conf noop alias setup_dir_man noop def update_shebang_line(path) return if no_harm? return if config('shebang') == 'never' old = Shebang.load(path) if old $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 new = new_shebang(old) return if new.to_s == old.to_s else return unless config('shebang') == 'all' new = Shebang.new(config('rubypath')) end $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? open_atomic_writer(path) {|output| File.open(path, 'rb') {|f| f.gets if old # discard output.puts new.to_s output.print f.read } } end def new_shebang(old) if /\Aruby/ =~ File.basename(old.cmd) Shebang.new(config('rubypath'), old.args) elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' Shebang.new(config('rubypath'), old.args[1..-1]) else return old unless config('shebang') == 'all' Shebang.new(config('rubypath')) end end def open_atomic_writer(path, &block) tmpfile = File.basename(path) + '.tmp' begin File.open(tmpfile, 'wb', &block) File.rename tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end class Shebang def Shebang.load(path) line = nil File.open(path) {|f| line = f.gets } return nil unless /\A#!/ =~ line parse(line) end def Shebang.parse(line) cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') new(cmd, args) end def initialize(cmd, args = []) @cmd = cmd @args = args end attr_reader :cmd attr_reader :args def to_s "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") end end # # TASK install # def exec_install rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755, strip_ext? end def strip_ext? /mswin|mingw/ !~ RUBY_PLATFORM end def install_dir_lib(rel) install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files rubyextentions('.'), "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 end def install_dir_conf(rel) # FIXME: should not remove current config files # (rename previous file to .old/.org) install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 end def install_dir_man(rel) install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 end def install_files(list, dest, mode, stripext = false) mkdir_p dest, @config.install_prefix list.each do |fname| if stripext install fname, "#{dest}/#{File.basename(fname, '.*')}", mode, @config.install_prefix else install fname, dest, mode, @config.install_prefix end end end def libfiles glob_reject(%w(*.y *.output), targetfiles()) end def rubyextentions(dir) ents = glob_select("*.#{@config.dllext}", targetfiles()) if ents.empty? setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" end ents end def targetfiles mapdir(existfiles() - hookfiles()) end def mapdir(ents) ents.map {|ent| if File.exist?(ent) then ent # objdir else "#{curr_srcdir()}/#{ent}" # srcdir end } end # picked up many entries from cvs-1.11.1/src/ignore.c JUNK_FILES = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) def existfiles glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean distclean ).map {|t| sprintf(fmt, t) } }.flatten end def glob_select(pat, ents) re = globs2re([pat]) ents.select {|ent| re =~ ent } end def glob_reject(pats, ents) re = globs2re(pats) ents.reject {|ent| re =~ ent } end GLOB2REGEX = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } def globs2re(pats) /\A(?:#{ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') })\z/ end # # TASK test # TESTDIR = 'test' def exec_test unless File.directory?('test') $stderr.puts 'no test in this package' if verbose? return end $stderr.puts 'Running tests...' if verbose? begin require 'test/unit' rescue LoadError setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' end runner = Test::Unit::AutoRunner.new(true) runner.to_run << TESTDIR runner.run end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f @config.savefile rm_f 'InstalledFiles' end alias clean_dir_bin noop alias clean_dir_lib noop alias clean_dir_data noop alias clean_dir_conf noop alias clean_dir_man noop def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f @config.savefile rm_f 'InstalledFiles' end alias distclean_dir_bin noop alias distclean_dir_lib noop def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end alias distclean_dir_data noop alias distclean_dir_conf noop alias distclean_dir_man noop # # Traversing # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if type == 'ext' and config('without-ext') == 'yes' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') directories_of(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end def run_hook(id) path = [ "#{curr_srcdir()}/#{id}", "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } return unless path $stderr.puts "invoking hook script #{path}" if verbose? begin instance_eval File.read(path), path, 1 rescue raise if $DEBUG setup_rb_error "hook #{path} failed:\n" + $!.message end end end # class Installer class SetupError < StandardError; end def setup_rb_error(msg) raise SetupError, msg end if $0 == __FILE__ begin ToplevelInstaller.invoke rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end ruby-ogginfo-0.7.2/checksums.yaml.gz0000444000004100000410000000041512261213656017451 0ustar www-datawww-dataQRe9N@ E"xMGG hP*Nτ ˖n+/~x_>W@8 k`emfE%6pÿ~-ývsL-KFQT%t55qIYioTȱR%Pn#ꚫͬ"$g<{Znl,\;uh!Ը]n`4g {7W@^c0)yZdSeK_~T'