Codebase list feed2imap / a3269e0
New upstream version 1.3.2 Antonio Terceiro 1 year, 6 months ago
8 changed file(s) with 56 addition(s) and 1594 deletion(s). Raw diff Collapse all Expand all
88
99 PKG_NAME = 'feed2imap'
1010 PKG_VERSION = Feed2Imap::VERSION
11 PKG_FILES = [ 'ChangeLog', 'README', 'COPYING', 'setup.rb', 'Rakefile']
11 PKG_FILES = [ 'ChangeLog', 'README', 'COPYING', 'Rakefile']
1212 Find.find('bin/', 'lib/', 'test/', 'data/') do |f|
1313 if FileTest.directory?(f) and f =~ /\.svn/
1414 Find.prune
11 # This file has been automatically generated by gem2tgz #
22 #########################################################
33 # -*- encoding: utf-8 -*-
4 # stub: feed2imap 1.3.1 ruby lib
4 # stub: feed2imap 1.3.2 ruby lib
55
66 Gem::Specification.new do |s|
77 s.name = "feed2imap".freeze
8 s.version = "1.3.1"
8 s.version = "1.3.2"
99
1010 s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
1111 s.require_paths = ["lib".freeze]
1212 s.authors = ["Lucas Nussbaum".freeze]
13 s.date = "2022-02-15"
13 s.date = "2022-09-13"
1414 s.description = "RSS/Atom feed aggregator".freeze
1515 s.executables = ["feed2imap".freeze, "feed2imap-cleaner".freeze, "feed2imap-dumpconfig".freeze, "feed2imap-opmlimport".freeze]
16 s.files = ["COPYING".freeze, "ChangeLog".freeze, "README".freeze, "Rakefile".freeze, "bin/feed2imap".freeze, "bin/feed2imap-cleaner".freeze, "bin/feed2imap-dumpconfig".freeze, "bin/feed2imap-opmlimport".freeze, "data/doc/feed2imap/examples/feed2imaprc".freeze, "data/man/man1/feed2imap-cleaner.1".freeze, "data/man/man1/feed2imap-dumpconfig.1".freeze, "data/man/man1/feed2imap-opmlimport.1".freeze, "data/man/man1/feed2imap.1".freeze, "data/man/man5/feed2imaprc.5".freeze, "lib/feed2imap.rb".freeze, "lib/feed2imap/cache.rb".freeze, "lib/feed2imap/config.rb".freeze, "lib/feed2imap/feed2imap.rb".freeze, "lib/feed2imap/html2text-parser.rb".freeze, "lib/feed2imap/httpfetcher.rb".freeze, "lib/feed2imap/imap.rb".freeze, "lib/feed2imap/itemtomail.rb".freeze, "lib/feed2imap/maildir.rb".freeze, "lib/feed2imap/rexml_settings.rb".freeze, "lib/feed2imap/sgml-parser.rb".freeze, "lib/feed2imap/version.rb".freeze, "setup.rb".freeze, "test/maildir/cur/1376317520.15784_1.debian:2,S".freeze, "test/maildir/cur/1376317520.15789_1.debian:2,S".freeze, "test/maildir/cur/1376319137.17850_1.debian:2,".freeze, "test/maildir/cur/1376320022.18396_5.debian:2,FS".freeze, "test/maildir/new/1376320099.18396_7.debian".freeze, "test/tc_cache.rb".freeze, "test/tc_config.rb".freeze, "test/tc_httpfetcher.rb".freeze, "test/tc_maildir.rb".freeze]
16 s.files = ["COPYING".freeze, "ChangeLog".freeze, "README".freeze, "Rakefile".freeze, "bin/feed2imap".freeze, "bin/feed2imap-cleaner".freeze, "bin/feed2imap-dumpconfig".freeze, "bin/feed2imap-opmlimport".freeze, "data/doc/feed2imap/examples/feed2imaprc".freeze, "data/man/man1/feed2imap-cleaner.1".freeze, "data/man/man1/feed2imap-dumpconfig.1".freeze, "data/man/man1/feed2imap-opmlimport.1".freeze, "data/man/man1/feed2imap.1".freeze, "data/man/man5/feed2imaprc.5".freeze, "lib/feed2imap.rb".freeze, "lib/feed2imap/cache.rb".freeze, "lib/feed2imap/config.rb".freeze, "lib/feed2imap/feed2imap.rb".freeze, "lib/feed2imap/html2text-parser.rb".freeze, "lib/feed2imap/httpfetcher.rb".freeze, "lib/feed2imap/imap.rb".freeze, "lib/feed2imap/itemtomail.rb".freeze, "lib/feed2imap/maildir.rb".freeze, "lib/feed2imap/rexml_settings.rb".freeze, "lib/feed2imap/sgml-parser.rb".freeze, "lib/feed2imap/version.rb".freeze, "test/maildir/cur/1376317520.15784_1.debian:2,S".freeze, "test/maildir/cur/1376317520.15789_1.debian:2,S".freeze, "test/maildir/cur/1376319137.17850_1.debian:2,".freeze, "test/maildir/cur/1376320022.18396_5.debian:2,FS".freeze, "test/maildir/new/1376320099.18396_7.debian".freeze, "test/tc_cache.rb".freeze, "test/tc_config.rb".freeze, "test/tc_httpfetcher.rb".freeze, "test/tc_itemtomail.rb".freeze, "test/tc_maildir.rb".freeze]
1717 s.rubygems_version = "3.2.5".freeze
1818 s.summary = "RSS/Atom feed aggregator".freeze
1919
3939 # Load the configuration from the IO stream
4040 # TODO should do some sanity check on the data read.
4141 def initialize(io)
42 @conf = YAML::load(io)
42 @conf = YAML::load(io, aliases: true)
4343 @cache = @conf['cache'] || DEFCACHE
4444 @dumpdir = @conf['dumpdir'] || nil
4545 @conf['feeds'] ||= []
152152 outh = Thread::new do
153153 output = stdout.read
154154 end
155 err = nil
156 errth = Thread::new do
157 err = stderr.read.chomp
158 end
155159 inth.join
156160 outh.join
161 errth.join
162 err.lines do |e|
163 @logger.warn(e.chomp)
164 end
157165 s = output
158166 if $? && $?.exitstatus != 0
159167 @logger.warn("Filter command for #{feed.name} exited with status #{$?.exitstatus}. Output might be corrupted !")
9595 # inline images as attachments
9696 imgs = []
9797 if inline_images
98 cids = []
9998 htmlpart.body.gsub!(/(<img[^>]+)src="(\S+?\/([^\/]+?\.(png|gif|jpe?g)))"([^>]*>)/i) do |match|
10099 # $2 contains url, $3 the image name, $4 the image extension
101100 begin
00 class Feed2Imap
1 VERSION = "1.3.1"
1 VERSION = "1.3.2"
22 end
+0
-1586
setup.rb less more
0 #!/usr/bin/ruby
1 #
2 # setup.rb
3 #
4 # Copyright (c) 2000-2005 Minero Aoki
5 #
6 # This program is free software.
7 # You can distribute/modify this program under the terms of
8 # the GNU LGPL, Lesser General Public License version 2.1.
9 #
10
11 unless Enumerable.method_defined?(:map) # Ruby 1.4.6
12 module Enumerable
13 alias map collect
14 end
15 end
16
17 unless File.respond_to?(:read) # Ruby 1.6
18 def File.read(fname)
19 open(fname) {|f|
20 return f.read
21 }
22 end
23 end
24
25 unless Errno.const_defined?(:ENOTEMPTY) # Windows?
26 module Errno
27 class ENOTEMPTY
28 # We do not raise this exception, implementation is not needed.
29 end
30 end
31 end
32
33 def File.binread(fname)
34 open(fname, 'rb') {|f|
35 return f.read
36 }
37 end
38
39 # for corrupted Windows' stat(2)
40 def File.dir?(path)
41 File.directory?((path[-1,1] == '/') ? path : path + '/')
42 end
43
44
45 class ConfigTable
46
47 include Enumerable
48
49 def initialize(rbconfig)
50 @rbconfig = rbconfig
51 @items = []
52 @table = {}
53 # options
54 @install_prefix = nil
55 @config_opt = nil
56 @verbose = true
57 @no_harm = false
58 end
59
60 attr_accessor :install_prefix
61 attr_accessor :config_opt
62
63 attr_writer :verbose
64
65 def verbose?
66 @verbose
67 end
68
69 attr_writer :no_harm
70
71 def no_harm?
72 @no_harm
73 end
74
75 def [](key)
76 lookup(key).resolve(self)
77 end
78
79 def []=(key, val)
80 lookup(key).set val
81 end
82
83 def names
84 @items.map {|i| i.name }
85 end
86
87 def each(&block)
88 @items.each(&block)
89 end
90
91 def key?(name)
92 @table.key?(name)
93 end
94
95 def lookup(name)
96 @table[name] or setup_rb_error "no such config item: #{name}"
97 end
98
99 def add(item)
100 @items.push item
101 @table[item.name] = item
102 end
103
104 def remove(name)
105 item = lookup(name)
106 @items.delete_if {|i| i.name == name }
107 @table.delete_if {|name, i| i.name == name }
108 item
109 end
110
111 def load_script(path, inst = nil)
112 if File.file?(path)
113 MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
114 end
115 end
116
117 def savefile
118 '.config'
119 end
120
121 def load_savefile
122 begin
123 File.foreach(savefile()) do |line|
124 k, v = *line.split(/=/, 2)
125 self[k] = v.strip
126 end
127 rescue Errno::ENOENT
128 setup_rb_error $!.message + "\n#{File.basename($0)} config first"
129 end
130 end
131
132 def save
133 @items.each {|i| i.value }
134 File.open(savefile(), 'w') {|f|
135 @items.each do |i|
136 f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
137 end
138 }
139 end
140
141 def load_standard_entries
142 standard_entries(@rbconfig).each do |ent|
143 add ent
144 end
145 end
146
147 def standard_entries(rbconfig)
148 c = rbconfig
149
150 rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
151
152 major = c['MAJOR'].to_i
153 minor = c['MINOR'].to_i
154 teeny = c['TEENY'].to_i
155 version = "#{major}.#{minor}"
156
157 # ruby ver. >= 1.4.4?
158 newpath_p = ((major >= 2) or
159 ((major == 1) and
160 ((minor >= 5) or
161 ((minor == 4) and (teeny >= 4)))))
162
163 if c['rubylibdir']
164 # V > 1.6.3
165 libruby = "#{c['prefix']}/lib/ruby"
166 librubyver = c['rubylibdir']
167 librubyverarch = c['archdir']
168 siteruby = c['sitedir']
169 siterubyver = c['sitelibdir']
170 siterubyverarch = c['sitearchdir']
171 elsif newpath_p
172 # 1.4.4 <= V <= 1.6.3
173 libruby = "#{c['prefix']}/lib/ruby"
174 librubyver = "#{c['prefix']}/lib/ruby/#{version}"
175 librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
176 siteruby = c['sitedir']
177 siterubyver = "$siteruby/#{version}"
178 siterubyverarch = "$siterubyver/#{c['arch']}"
179 else
180 # V < 1.4.4
181 libruby = "#{c['prefix']}/lib/ruby"
182 librubyver = "#{c['prefix']}/lib/ruby/#{version}"
183 librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
184 siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
185 siterubyver = siteruby
186 siterubyverarch = "$siterubyver/#{c['arch']}"
187 end
188 parameterize = lambda {|path|
189 path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
190 }
191
192 if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
193 makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
194 else
195 makeprog = 'make'
196 end
197
198 [
199 ExecItem.new('installdirs', 'std/site/home',
200 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
201 {|val, table|
202 case val
203 when 'std'
204 table['rbdir'] = '$librubyver'
205 table['sodir'] = '$librubyverarch'
206 when 'site'
207 table['rbdir'] = '$siterubyver'
208 table['sodir'] = '$siterubyverarch'
209 when 'home'
210 setup_rb_error '$HOME was not set' unless ENV['HOME']
211 table['prefix'] = ENV['HOME']
212 table['rbdir'] = '$libdir/ruby'
213 table['sodir'] = '$libdir/ruby'
214 end
215 },
216 PathItem.new('prefix', 'path', c['prefix'],
217 'path prefix of target environment'),
218 PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
219 'the directory for commands'),
220 PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
221 'the directory for libraries'),
222 PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
223 'the directory for shared data'),
224 PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
225 'the directory for man pages'),
226 PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
227 'the directory for system configuration files'),
228 PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
229 'the directory for local state data'),
230 PathItem.new('libruby', 'path', libruby,
231 'the directory for ruby libraries'),
232 PathItem.new('librubyver', 'path', librubyver,
233 'the directory for standard ruby libraries'),
234 PathItem.new('librubyverarch', 'path', librubyverarch,
235 'the directory for standard ruby extensions'),
236 PathItem.new('siteruby', 'path', siteruby,
237 'the directory for version-independent aux ruby libraries'),
238 PathItem.new('siterubyver', 'path', siterubyver,
239 'the directory for aux ruby libraries'),
240 PathItem.new('siterubyverarch', 'path', siterubyverarch,
241 'the directory for aux ruby binaries'),
242 PathItem.new('rbdir', 'path', '$siterubyver',
243 'the directory for ruby scripts'),
244 PathItem.new('sodir', 'path', '$siterubyverarch',
245 'the directory for ruby extentions'),
246 PathItem.new('rubypath', 'path', rubypath,
247 'the path to set to #! line'),
248 ProgramItem.new('rubyprog', 'name', rubypath,
249 'the ruby program using for installation'),
250 ProgramItem.new('makeprog', 'name', makeprog,
251 'the make program to compile ruby extentions'),
252 SelectItem.new('shebang', 'all/ruby/never', 'never',
253 'shebang line (#!) editing mode'),
254 BoolItem.new('without-ext', 'yes/no', 'no',
255 'does not compile/install ruby extentions')
256 ]
257 end
258 private :standard_entries
259
260 def load_multipackage_entries
261 multipackage_entries().each do |ent|
262 add ent
263 end
264 end
265
266 def multipackage_entries
267 [
268 PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
269 'package names that you want to install'),
270 PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
271 'package names that you do not want to install')
272 ]
273 end
274 private :multipackage_entries
275
276 ALIASES = {
277 'std-ruby' => 'librubyver',
278 'stdruby' => 'librubyver',
279 'rubylibdir' => 'librubyver',
280 'archdir' => 'librubyverarch',
281 'site-ruby-common' => 'siteruby', # For backward compatibility
282 'site-ruby' => 'siterubyver', # For backward compatibility
283 'bin-dir' => 'bindir',
284 'bin-dir' => 'bindir',
285 'rb-dir' => 'rbdir',
286 'so-dir' => 'sodir',
287 'data-dir' => 'datadir',
288 'ruby-path' => 'rubypath',
289 'ruby-prog' => 'rubyprog',
290 'ruby' => 'rubyprog',
291 'make-prog' => 'makeprog',
292 'make' => 'makeprog'
293 }
294
295 def fixup
296 ALIASES.each do |ali, name|
297 @table[ali] = @table[name]
298 end
299 @items.freeze
300 @table.freeze
301 @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
302 end
303
304 def parse_opt(opt)
305 m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
306 m.to_a[1,2]
307 end
308
309 def dllext
310 @rbconfig['DLEXT']
311 end
312
313 def value_config?(name)
314 lookup(name).value?
315 end
316
317 class Item
318 def initialize(name, template, default, desc)
319 @name = name.freeze
320 @template = template
321 @value = default
322 @default = default
323 @description = desc
324 end
325
326 attr_reader :name
327 attr_reader :description
328
329 attr_accessor :default
330 alias help_default default
331
332 def help_opt
333 "--#{@name}=#{@template}"
334 end
335
336 def value?
337 true
338 end
339
340 def value
341 @value
342 end
343
344 def resolve(table)
345 @value.gsub(%r<\$([^/]+)>) { table[$1] }
346 end
347
348 def set(val)
349 @value = check(val)
350 end
351
352 private
353
354 def check(val)
355 setup_rb_error "config: --#{name} requires argument" unless val
356 val
357 end
358 end
359
360 class BoolItem < Item
361 def config_type
362 'bool'
363 end
364
365 def help_opt
366 "--#{@name}"
367 end
368
369 private
370
371 def check(val)
372 return 'yes' unless val
373 case val
374 when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
375 when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
376 else
377 setup_rb_error "config: --#{@name} accepts only yes/no for argument"
378 end
379 end
380 end
381
382 class PathItem < Item
383 def config_type
384 'path'
385 end
386
387 private
388
389 def check(path)
390 setup_rb_error "config: --#{@name} requires argument" unless path
391 path[0,1] == '$' ? path : File.expand_path(path)
392 end
393 end
394
395 class ProgramItem < Item
396 def config_type
397 'program'
398 end
399 end
400
401 class SelectItem < Item
402 def initialize(name, selection, default, desc)
403 super
404 @ok = selection.split('/')
405 end
406
407 def config_type
408 'select'
409 end
410
411 private
412
413 def check(val)
414 unless @ok.include?(val.strip)
415 setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
416 end
417 val.strip
418 end
419 end
420
421 class ExecItem < Item
422 def initialize(name, selection, desc, &block)
423 super name, selection, nil, desc
424 @ok = selection.split('/')
425 @action = block
426 end
427
428 def config_type
429 'exec'
430 end
431
432 def value?
433 false
434 end
435
436 def resolve(table)
437 setup_rb_error "$#{name()} wrongly used as option value"
438 end
439
440 undef set
441
442 def evaluate(val, table)
443 v = val.strip.downcase
444 unless @ok.include?(v)
445 setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
446 end
447 @action.call v, table
448 end
449 end
450
451 class PackageSelectionItem < Item
452 def initialize(name, template, default, help_default, desc)
453 super name, template, default, desc
454 @help_default = help_default
455 end
456
457 attr_reader :help_default
458
459 def config_type
460 'package'
461 end
462
463 private
464
465 def check(val)
466 unless File.dir?("packages/#{val}")
467 setup_rb_error "config: no such package: #{val}"
468 end
469 val
470 end
471 end
472
473 class MetaConfigEnvironment
474 def initialize(config, installer)
475 @config = config
476 @installer = installer
477 end
478
479 def config_names
480 @config.names
481 end
482
483 def config?(name)
484 @config.key?(name)
485 end
486
487 def bool_config?(name)
488 @config.lookup(name).config_type == 'bool'
489 end
490
491 def path_config?(name)
492 @config.lookup(name).config_type == 'path'
493 end
494
495 def value_config?(name)
496 @config.lookup(name).config_type != 'exec'
497 end
498
499 def add_config(item)
500 @config.add item
501 end
502
503 def add_bool_config(name, default, desc)
504 @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
505 end
506
507 def add_path_config(name, default, desc)
508 @config.add PathItem.new(name, 'path', default, desc)
509 end
510
511 def set_config_default(name, default)
512 @config.lookup(name).default = default
513 end
514
515 def remove_config(name)
516 @config.remove(name)
517 end
518
519 # For only multipackage
520 def packages
521 raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
522 @installer.packages
523 end
524
525 # For only multipackage
526 def declare_packages(list)
527 raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
528 @installer.packages = list
529 end
530 end
531
532 end # class ConfigTable
533
534
535 # This module requires: #verbose?, #no_harm?
536 module FileOperations
537
538 def mkdir_p(dirname, prefix = nil)
539 dirname = prefix + File.expand_path(dirname) if prefix
540 $stderr.puts "mkdir -p #{dirname}" if verbose?
541 return if no_harm?
542
543 # Does not check '/', it's too abnormal.
544 dirs = File.expand_path(dirname).split(%r<(?=/)>)
545 if /\A[a-z]:\z/i =~ dirs[0]
546 disk = dirs.shift
547 dirs[0] = disk + dirs[0]
548 end
549 dirs.each_index do |idx|
550 path = dirs[0..idx].join('')
551 Dir.mkdir path unless File.dir?(path)
552 end
553 end
554
555 def rm_f(path)
556 $stderr.puts "rm -f #{path}" if verbose?
557 return if no_harm?
558 force_remove_file path
559 end
560
561 def rm_rf(path)
562 $stderr.puts "rm -rf #{path}" if verbose?
563 return if no_harm?
564 remove_tree path
565 end
566
567 def remove_tree(path)
568 if File.symlink?(path)
569 remove_file path
570 elsif File.dir?(path)
571 remove_tree0 path
572 else
573 force_remove_file path
574 end
575 end
576
577 def remove_tree0(path)
578 Dir.foreach(path) do |ent|
579 next if ent == '.'
580 next if ent == '..'
581 entpath = "#{path}/#{ent}"
582 if File.symlink?(entpath)
583 remove_file entpath
584 elsif File.dir?(entpath)
585 remove_tree0 entpath
586 else
587 force_remove_file entpath
588 end
589 end
590 begin
591 Dir.rmdir path
592 rescue Errno::ENOTEMPTY
593 # directory may not be empty
594 end
595 end
596
597 def move_file(src, dest)
598 force_remove_file dest
599 begin
600 File.rename src, dest
601 rescue
602 File.open(dest, 'wb') {|f|
603 f.write File.binread(src)
604 }
605 File.chmod File.stat(src).mode, dest
606 File.unlink src
607 end
608 end
609
610 def force_remove_file(path)
611 begin
612 remove_file path
613 rescue
614 end
615 end
616
617 def remove_file(path)
618 File.chmod 0777, path
619 File.unlink path
620 end
621
622 def install(from, dest, mode, prefix = nil)
623 $stderr.puts "install #{from} #{dest}" if verbose?
624 return if no_harm?
625
626 realdest = prefix ? prefix + File.expand_path(dest) : dest
627 realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
628 str = File.binread(from)
629 if diff?(str, realdest)
630 verbose_off {
631 rm_f realdest if File.exist?(realdest)
632 }
633 File.open(realdest, 'wb') {|f|
634 f.write str
635 }
636 File.chmod mode, realdest
637
638 File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
639 if prefix
640 f.puts realdest.sub(prefix, '')
641 else
642 f.puts realdest
643 end
644 }
645 end
646 end
647
648 def diff?(new_content, path)
649 return true unless File.exist?(path)
650 new_content != File.binread(path)
651 end
652
653 def command(*args)
654 $stderr.puts args.join(' ') if verbose?
655 system(*args) or raise RuntimeError,
656 "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
657 end
658
659 def ruby(*args)
660 command config('rubyprog'), *args
661 end
662
663 def make(task = nil)
664 command(*[config('makeprog'), task].compact)
665 end
666
667 def extdir?(dir)
668 File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
669 end
670
671 def files_of(dir)
672 Dir.open(dir) {|d|
673 return d.select {|ent| File.file?("#{dir}/#{ent}") }
674 }
675 end
676
677 DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
678
679 def directories_of(dir)
680 Dir.open(dir) {|d|
681 return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
682 }
683 end
684
685 end
686
687
688 # This module requires: #srcdir_root, #objdir_root, #relpath
689 module HookScriptAPI
690
691 def get_config(key)
692 @config[key]
693 end
694
695 alias config get_config
696
697 # obsolete: use metaconfig to change configuration
698 def set_config(key, val)
699 @config[key] = val
700 end
701
702 #
703 # srcdir/objdir (works only in the package directory)
704 #
705
706 def curr_srcdir
707 "#{srcdir_root()}/#{relpath()}"
708 end
709
710 def curr_objdir
711 "#{objdir_root()}/#{relpath()}"
712 end
713
714 def srcfile(path)
715 "#{curr_srcdir()}/#{path}"
716 end
717
718 def srcexist?(path)
719 File.exist?(srcfile(path))
720 end
721
722 def srcdirectory?(path)
723 File.dir?(srcfile(path))
724 end
725
726 def srcfile?(path)
727 File.file?(srcfile(path))
728 end
729
730 def srcentries(path = '.')
731 Dir.open("#{curr_srcdir()}/#{path}") {|d|
732 return d.to_a - %w(. ..)
733 }
734 end
735
736 def srcfiles(path = '.')
737 srcentries(path).select {|fname|
738 File.file?(File.join(curr_srcdir(), path, fname))
739 }
740 end
741
742 def srcdirectories(path = '.')
743 srcentries(path).select {|fname|
744 File.dir?(File.join(curr_srcdir(), path, fname))
745 }
746 end
747
748 end
749
750
751 class ToplevelInstaller
752
753 Version = '3.4.1'
754 Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
755
756 TASKS = [
757 [ 'all', 'do config, setup, then install' ],
758 [ 'config', 'saves your configurations' ],
759 [ 'show', 'shows current configuration' ],
760 [ 'setup', 'compiles ruby extentions and others' ],
761 [ 'install', 'installs files' ],
762 [ 'test', 'run all tests in test/' ],
763 [ 'clean', "does `make clean' for each extention" ],
764 [ 'distclean',"does `make distclean' for each extention" ]
765 ]
766
767 def ToplevelInstaller.invoke
768 config = ConfigTable.new(load_rbconfig())
769 config.load_standard_entries
770 config.load_multipackage_entries if multipackage?
771 config.fixup
772 klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
773 klass.new(File.dirname($0), config).invoke
774 end
775
776 def ToplevelInstaller.multipackage?
777 File.dir?(File.dirname($0) + '/packages')
778 end
779
780 def ToplevelInstaller.load_rbconfig
781 if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
782 ARGV.delete(arg)
783 load File.expand_path(arg.split(/=/, 2)[1])
784 $".push 'rbconfig.rb'
785 else
786 require 'rbconfig'
787 end
788 ::Config::CONFIG
789 end
790
791 def initialize(ardir_root, config)
792 @ardir = File.expand_path(ardir_root)
793 @config = config
794 # cache
795 @valid_task_re = nil
796 end
797
798 def config(key)
799 @config[key]
800 end
801
802 def inspect
803 "#<#{self.class} #{__id__()}>"
804 end
805
806 def invoke
807 run_metaconfigs
808 case task = parsearg_global()
809 when nil, 'all'
810 parsearg_config
811 init_installers
812 exec_config
813 exec_setup
814 exec_install
815 else
816 case task
817 when 'config', 'test'
818 ;
819 when 'clean', 'distclean'
820 @config.load_savefile if File.exist?(@config.savefile)
821 else
822 @config.load_savefile
823 end
824 __send__ "parsearg_#{task}"
825 init_installers
826 __send__ "exec_#{task}"
827 end
828 end
829
830 def run_metaconfigs
831 @config.load_script "#{@ardir}/metaconfig"
832 end
833
834 def init_installers
835 @installer = Installer.new(@config, @ardir, File.expand_path('.'))
836 end
837
838 #
839 # Hook Script API bases
840 #
841
842 def srcdir_root
843 @ardir
844 end
845
846 def objdir_root
847 '.'
848 end
849
850 def relpath
851 '.'
852 end
853
854 #
855 # Option Parsing
856 #
857
858 def parsearg_global
859 while arg = ARGV.shift
860 case arg
861 when /\A\w+\z/
862 setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
863 return arg
864 when '-q', '--quiet'
865 @config.verbose = false
866 when '--verbose'
867 @config.verbose = true
868 when '--help'
869 print_usage $stdout
870 exit 0
871 when '--version'
872 puts "#{File.basename($0)} version #{Version}"
873 exit 0
874 when '--copyright'
875 puts Copyright
876 exit 0
877 else
878 setup_rb_error "unknown global option '#{arg}'"
879 end
880 end
881 nil
882 end
883
884 def valid_task?(t)
885 valid_task_re() =~ t
886 end
887
888 def valid_task_re
889 @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
890 end
891
892 def parsearg_no_options
893 unless ARGV.empty?
894 task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
895 setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
896 end
897 end
898
899 alias parsearg_show parsearg_no_options
900 alias parsearg_setup parsearg_no_options
901 alias parsearg_test parsearg_no_options
902 alias parsearg_clean parsearg_no_options
903 alias parsearg_distclean parsearg_no_options
904
905 def parsearg_config
906 evalopt = []
907 set = []
908 @config.config_opt = []
909 while i = ARGV.shift
910 if /\A--?\z/ =~ i
911 @config.config_opt = ARGV.dup
912 break
913 end
914 name, value = *@config.parse_opt(i)
915 if @config.value_config?(name)
916 @config[name] = value
917 else
918 evalopt.push [name, value]
919 end
920 set.push name
921 end
922 evalopt.each do |name, value|
923 @config.lookup(name).evaluate value, @config
924 end
925 # Check if configuration is valid
926 set.each do |n|
927 @config[n] if @config.value_config?(n)
928 end
929 end
930
931 def parsearg_install
932 @config.no_harm = false
933 @config.install_prefix = ''
934 while a = ARGV.shift
935 case a
936 when '--no-harm'
937 @config.no_harm = true
938 when /\A--prefix=/
939 path = a.split(/=/, 2)[1]
940 path = File.expand_path(path) unless path[0,1] == '/'
941 @config.install_prefix = path
942 else
943 setup_rb_error "install: unknown option #{a}"
944 end
945 end
946 end
947
948 def print_usage(out)
949 out.puts 'Typical Installation Procedure:'
950 out.puts " $ ruby #{File.basename $0} config"
951 out.puts " $ ruby #{File.basename $0} setup"
952 out.puts " # ruby #{File.basename $0} install (may require root privilege)"
953 out.puts
954 out.puts 'Detailed Usage:'
955 out.puts " ruby #{File.basename $0} <global option>"
956 out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
957
958 fmt = " %-24s %s\n"
959 out.puts
960 out.puts 'Global options:'
961 out.printf fmt, '-q,--quiet', 'suppress message outputs'
962 out.printf fmt, ' --verbose', 'output messages verbosely'
963 out.printf fmt, ' --help', 'print this message'
964 out.printf fmt, ' --version', 'print version and quit'
965 out.printf fmt, ' --copyright', 'print copyright and quit'
966 out.puts
967 out.puts 'Tasks:'
968 TASKS.each do |name, desc|
969 out.printf fmt, name, desc
970 end
971
972 fmt = " %-24s %s [%s]\n"
973 out.puts
974 out.puts 'Options for CONFIG or ALL:'
975 @config.each do |item|
976 out.printf fmt, item.help_opt, item.description, item.help_default
977 end
978 out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
979 out.puts
980 out.puts 'Options for INSTALL:'
981 out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
982 out.printf fmt, '--prefix=path', 'install path prefix', ''
983 out.puts
984 end
985
986 #
987 # Task Handlers
988 #
989
990 def exec_config
991 @installer.exec_config
992 @config.save # must be final
993 end
994
995 def exec_setup
996 @installer.exec_setup
997 end
998
999 def exec_install
1000 @installer.exec_install
1001 end
1002
1003 def exec_test
1004 @installer.exec_test
1005 end
1006
1007 def exec_show
1008 @config.each do |i|
1009 printf "%-20s %s\n", i.name, i.value if i.value?
1010 end
1011 end
1012
1013 def exec_clean
1014 @installer.exec_clean
1015 end
1016
1017 def exec_distclean
1018 @installer.exec_distclean
1019 end
1020
1021 end # class ToplevelInstaller
1022
1023
1024 class ToplevelInstallerMulti < ToplevelInstaller
1025
1026 include FileOperations
1027
1028 def initialize(ardir_root, config)
1029 super
1030 @packages = directories_of("#{@ardir}/packages")
1031 raise 'no package exists' if @packages.empty?
1032 @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
1033 end
1034
1035 def run_metaconfigs
1036 @config.load_script "#{@ardir}/metaconfig", self
1037 @packages.each do |name|
1038 @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
1039 end
1040 end
1041
1042 attr_reader :packages
1043
1044 def packages=(list)
1045 raise 'package list is empty' if list.empty?
1046 list.each do |name|
1047 raise "directory packages/#{name} does not exist"\
1048 unless File.dir?("#{@ardir}/packages/#{name}")
1049 end
1050 @packages = list
1051 end
1052
1053 def init_installers
1054 @installers = {}
1055 @packages.each do |pack|
1056 @installers[pack] = Installer.new(@config,
1057 "#{@ardir}/packages/#{pack}",
1058 "packages/#{pack}")
1059 end
1060 with = extract_selection(config('with'))
1061 without = extract_selection(config('without'))
1062 @selected = @installers.keys.select {|name|
1063 (with.empty? or with.include?(name)) \
1064 and not without.include?(name)
1065 }
1066 end
1067
1068 def extract_selection(list)
1069 a = list.split(/,/)
1070 a.each do |name|
1071 setup_rb_error "no such package: #{name}" unless @installers.key?(name)
1072 end
1073 a
1074 end
1075
1076 def print_usage(f)
1077 super
1078 f.puts 'Inluded packages:'
1079 f.puts ' ' + @packages.sort.join(' ')
1080 f.puts
1081 end
1082
1083 #
1084 # Task Handlers
1085 #
1086
1087 def exec_config
1088 run_hook 'pre-config'
1089 each_selected_installers {|inst| inst.exec_config }
1090 run_hook 'post-config'
1091 @config.save # must be final
1092 end
1093
1094 def exec_setup
1095 run_hook 'pre-setup'
1096 each_selected_installers {|inst| inst.exec_setup }
1097 run_hook 'post-setup'
1098 end
1099
1100 def exec_install
1101 run_hook 'pre-install'
1102 each_selected_installers {|inst| inst.exec_install }
1103 run_hook 'post-install'
1104 end
1105
1106 def exec_test
1107 run_hook 'pre-test'
1108 each_selected_installers {|inst| inst.exec_test }
1109 run_hook 'post-test'
1110 end
1111
1112 def exec_clean
1113 rm_f @config.savefile
1114 run_hook 'pre-clean'
1115 each_selected_installers {|inst| inst.exec_clean }
1116 run_hook 'post-clean'
1117 end
1118
1119 def exec_distclean
1120 rm_f @config.savefile
1121 run_hook 'pre-distclean'
1122 each_selected_installers {|inst| inst.exec_distclean }
1123 run_hook 'post-distclean'
1124 end
1125
1126 #
1127 # lib
1128 #
1129
1130 def each_selected_installers
1131 Dir.mkdir 'packages' unless File.dir?('packages')
1132 @selected.each do |pack|
1133 $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1134 Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1135 Dir.chdir "packages/#{pack}"
1136 yield @installers[pack]
1137 Dir.chdir '../..'
1138 end
1139 end
1140
1141 def run_hook(id)
1142 @root_installer.run_hook id
1143 end
1144
1145 # module FileOperations requires this
1146 def verbose?
1147 @config.verbose?
1148 end
1149
1150 # module FileOperations requires this
1151 def no_harm?
1152 @config.no_harm?
1153 end
1154
1155 end # class ToplevelInstallerMulti
1156
1157
1158 class Installer
1159
1160 FILETYPES = %w( bin lib ext data conf man )
1161
1162 include FileOperations
1163 include HookScriptAPI
1164
1165 def initialize(config, srcroot, objroot)
1166 @config = config
1167 @srcdir = File.expand_path(srcroot)
1168 @objdir = File.expand_path(objroot)
1169 @currdir = '.'
1170 end
1171
1172 def inspect
1173 "#<#{self.class} #{File.basename(@srcdir)}>"
1174 end
1175
1176 def noop(rel)
1177 end
1178
1179 #
1180 # Hook Script API base methods
1181 #
1182
1183 def srcdir_root
1184 @srcdir
1185 end
1186
1187 def objdir_root
1188 @objdir
1189 end
1190
1191 def relpath
1192 @currdir
1193 end
1194
1195 #
1196 # Config Access
1197 #
1198
1199 # module FileOperations requires this
1200 def verbose?
1201 @config.verbose?
1202 end
1203
1204 # module FileOperations requires this
1205 def no_harm?
1206 @config.no_harm?
1207 end
1208
1209 def verbose_off
1210 begin
1211 save, @config.verbose = @config.verbose?, false
1212 yield
1213 ensure
1214 @config.verbose = save
1215 end
1216 end
1217
1218 #
1219 # TASK config
1220 #
1221
1222 def exec_config
1223 exec_task_traverse 'config'
1224 end
1225
1226 alias config_dir_bin noop
1227 alias config_dir_lib noop
1228
1229 def config_dir_ext(rel)
1230 extconf if extdir?(curr_srcdir())
1231 end
1232
1233 alias config_dir_data noop
1234 alias config_dir_conf noop
1235 alias config_dir_man noop
1236
1237 def extconf
1238 ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1239 end
1240
1241 #
1242 # TASK setup
1243 #
1244
1245 def exec_setup
1246 exec_task_traverse 'setup'
1247 end
1248
1249 def setup_dir_bin(rel)
1250 files_of(curr_srcdir()).each do |fname|
1251 update_shebang_line "#{curr_srcdir()}/#{fname}"
1252 end
1253 end
1254
1255 alias setup_dir_lib noop
1256
1257 def setup_dir_ext(rel)
1258 make if extdir?(curr_srcdir())
1259 end
1260
1261 alias setup_dir_data noop
1262 alias setup_dir_conf noop
1263 alias setup_dir_man noop
1264
1265 def update_shebang_line(path)
1266 return if no_harm?
1267 return if config('shebang') == 'never'
1268 old = Shebang.load(path)
1269 if old
1270 $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
1271 new = new_shebang(old)
1272 return if new.to_s == old.to_s
1273 else
1274 return unless config('shebang') == 'all'
1275 new = Shebang.new(config('rubypath'))
1276 end
1277 $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
1278 open_atomic_writer(path) {|output|
1279 File.open(path, 'rb') {|f|
1280 f.gets if old # discard
1281 output.puts new.to_s
1282 output.print f.read
1283 }
1284 }
1285 end
1286
1287 def new_shebang(old)
1288 if /\Aruby/ =~ File.basename(old.cmd)
1289 Shebang.new(config('rubypath'), old.args)
1290 elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
1291 Shebang.new(config('rubypath'), old.args[1..-1])
1292 else
1293 return old unless config('shebang') == 'all'
1294 Shebang.new(config('rubypath'))
1295 end
1296 end
1297
1298 def open_atomic_writer(path, &block)
1299 tmpfile = File.basename(path) + '.tmp'
1300 begin
1301 File.open(tmpfile, 'wb', &block)
1302 File.rename tmpfile, File.basename(path)
1303 ensure
1304 File.unlink tmpfile if File.exist?(tmpfile)
1305 end
1306 end
1307
1308 class Shebang
1309 def Shebang.load(path)
1310 line = nil
1311 File.open(path) {|f|
1312 line = f.gets
1313 }
1314 return nil unless /\A#!/ =~ line
1315 parse(line)
1316 end
1317
1318 def Shebang.parse(line)
1319 cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
1320 new(cmd, args)
1321 end
1322
1323 def initialize(cmd, args = [])
1324 @cmd = cmd
1325 @args = args
1326 end
1327
1328 attr_reader :cmd
1329 attr_reader :args
1330
1331 def to_s
1332 "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1333 end
1334 end
1335
1336 #
1337 # TASK install
1338 #
1339
1340 def exec_install
1341 rm_f 'InstalledFiles'
1342 exec_task_traverse 'install'
1343 end
1344
1345 def install_dir_bin(rel)
1346 install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1347 end
1348
1349 def install_dir_lib(rel)
1350 install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1351 end
1352
1353 def install_dir_ext(rel)
1354 return unless extdir?(curr_srcdir())
1355 install_files rubyextentions('.'),
1356 "#{config('sodir')}/#{File.dirname(rel)}",
1357 0555
1358 end
1359
1360 def install_dir_data(rel)
1361 install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1362 end
1363
1364 def install_dir_conf(rel)
1365 # FIXME: should not remove current config files
1366 # (rename previous file to .old/.org)
1367 install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1368 end
1369
1370 def install_dir_man(rel)
1371 install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1372 end
1373
1374 def install_files(list, dest, mode)
1375 mkdir_p dest, @config.install_prefix
1376 list.each do |fname|
1377 install fname, dest, mode, @config.install_prefix
1378 end
1379 end
1380
1381 def libfiles
1382 glob_reject(%w(*.y *.output), targetfiles())
1383 end
1384
1385 def rubyextentions(dir)
1386 ents = glob_select("*.#{@config.dllext}", targetfiles())
1387 if ents.empty?
1388 setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1389 end
1390 ents
1391 end
1392
1393 def targetfiles
1394 mapdir(existfiles() - hookfiles())
1395 end
1396
1397 def mapdir(ents)
1398 ents.map {|ent|
1399 if File.exist?(ent)
1400 then ent # objdir
1401 else "#{curr_srcdir()}/#{ent}" # srcdir
1402 end
1403 }
1404 end
1405
1406 # picked up many entries from cvs-1.11.1/src/ignore.c
1407 JUNK_FILES = %w(
1408 core RCSLOG tags TAGS .make.state
1409 .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1410 *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1411
1412 *.org *.in .*
1413 )
1414
1415 def existfiles
1416 glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1417 end
1418
1419 def hookfiles
1420 %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1421 %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1422 }.flatten
1423 end
1424
1425 def glob_select(pat, ents)
1426 re = globs2re([pat])
1427 ents.select {|ent| re =~ ent }
1428 end
1429
1430 def glob_reject(pats, ents)
1431 re = globs2re(pats)
1432 ents.reject {|ent| re =~ ent }
1433 end
1434
1435 GLOB2REGEX = {
1436 '.' => '\.',
1437 '$' => '\$',
1438 '#' => '\#',
1439 '*' => '.*'
1440 }
1441
1442 def globs2re(pats)
1443 /\A(?:#{
1444 pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1445 })\z/
1446 end
1447
1448 #
1449 # TASK test
1450 #
1451
1452 TESTDIR = 'test'
1453
1454 def exec_test
1455 unless File.directory?('test')
1456 $stderr.puts 'no test in this package' if verbose?
1457 return
1458 end
1459 $stderr.puts 'Running tests...' if verbose?
1460 begin
1461 require 'test/unit'
1462 rescue LoadError
1463 setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1464 end
1465 runner = Test::Unit::AutoRunner.new(true)
1466 runner.to_run << TESTDIR
1467 runner.run
1468 end
1469
1470 #
1471 # TASK clean
1472 #
1473
1474 def exec_clean
1475 exec_task_traverse 'clean'
1476 rm_f @config.savefile
1477 rm_f 'InstalledFiles'
1478 end
1479
1480 alias clean_dir_bin noop
1481 alias clean_dir_lib noop
1482 alias clean_dir_data noop
1483 alias clean_dir_conf noop
1484 alias clean_dir_man noop
1485
1486 def clean_dir_ext(rel)
1487 return unless extdir?(curr_srcdir())
1488 make 'clean' if File.file?('Makefile')
1489 end
1490
1491 #
1492 # TASK distclean
1493 #
1494
1495 def exec_distclean
1496 exec_task_traverse 'distclean'
1497 rm_f @config.savefile
1498 rm_f 'InstalledFiles'
1499 end
1500
1501 alias distclean_dir_bin noop
1502 alias distclean_dir_lib noop
1503
1504 def distclean_dir_ext(rel)
1505 return unless extdir?(curr_srcdir())
1506 make 'distclean' if File.file?('Makefile')
1507 end
1508
1509 alias distclean_dir_data noop
1510 alias distclean_dir_conf noop
1511 alias distclean_dir_man noop
1512
1513 #
1514 # Traversing
1515 #
1516
1517 def exec_task_traverse(task)
1518 run_hook "pre-#{task}"
1519 FILETYPES.each do |type|
1520 if type == 'ext' and config('without-ext') == 'yes'
1521 $stderr.puts 'skipping ext/* by user option' if verbose?
1522 next
1523 end
1524 traverse task, type, "#{task}_dir_#{type}"
1525 end
1526 run_hook "post-#{task}"
1527 end
1528
1529 def traverse(task, rel, mid)
1530 dive_into(rel) {
1531 run_hook "pre-#{task}"
1532 __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1533 directories_of(curr_srcdir()).each do |d|
1534 traverse task, "#{rel}/#{d}", mid
1535 end
1536 run_hook "post-#{task}"
1537 }
1538 end
1539
1540 def dive_into(rel)
1541 return unless File.dir?("#{@srcdir}/#{rel}")
1542
1543 dir = File.basename(rel)
1544 Dir.mkdir dir unless File.dir?(dir)
1545 prevdir = Dir.pwd
1546 Dir.chdir dir
1547 $stderr.puts '---> ' + rel if verbose?
1548 @currdir = rel
1549 yield
1550 Dir.chdir prevdir
1551 $stderr.puts '<--- ' + rel if verbose?
1552 @currdir = File.dirname(rel)
1553 end
1554
1555 def run_hook(id)
1556 path = [ "#{curr_srcdir()}/#{id}",
1557 "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1558 return unless path
1559 begin
1560 instance_eval File.read(path), path, 1
1561 rescue
1562 raise if $DEBUG
1563 setup_rb_error "hook #{path} failed:\n" + $!.message
1564 end
1565 end
1566
1567 end # class Installer
1568
1569
1570 class SetupError < StandardError; end
1571
1572 def setup_rb_error(msg)
1573 raise SetupError, msg
1574 end
1575
1576 if $0 == __FILE__
1577 begin
1578 ToplevelInstaller.invoke
1579 rescue SetupError
1580 raise if $DEBUG
1581 $stderr.puts $!.message
1582 $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1583 exit 1
1584 end
1585 end
0 require 'test/unit'
1 require 'feed2imap'
2 require 'mocha/test_unit'
3
4
5 class ItemToMailTest < Test::Unit::TestCase
6
7 def jpg
8 # a 1x1 white pixel
9 "\xFF\xD8\xFF\xE0\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000\u0001\u0000\u0001\u0000\u0000\xFF\xDB\u0000C\u0000\u0003\u0002\u0002\u0002\u0002\u0002\u0003\u0002\u0002\u0002\u0003\u0003\u0003\u0003\u0004\u0006\u0004\u0004\u0004\u0004\u0004\b\u0006\u0006\u0005\u0006\t\b\n\n\t\b\t\t\n\f\u000F\f\n\v\u000E\v\t\t\r\u0011\r\u000E\u000F\u0010\u0010\u0011\u0010\n\f\u0012\u0013\u0012\u0010\u0013\u000F\u0010\u0010\u0010\xFF\xC0\u0000\v\b\u0000\u0001\u0000\u0001\u0001\u0001\u0011\u0000\xFF\xC4\u0000\u0014\u0000\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\t\xFF\xC4\u0000\u0014\u0010\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\xFF\xDA\u0000\b\u0001\u0001\u0000\u0000?\u0000T\xDF\xFF\xD9"
10 end
11
12 def jpeg_base64
13 # base64 encoding of the image in #jpg above
14 "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQE\nBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/\nwAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAA\nAAAAAAAAAAAAAAD/2gAIAQEAAD8AVN//2Q==\n"
15 end
16
17 def config
18 F2IConfig.new(StringIO.new("{}"))
19 end
20
21 def new_item
22 feed = FeedParser::Feed.new
23 feed.instance_variable_set("@title", "Some blog")
24 feed.instance_variable_set("@link", "http://www.example.com/")
25 item = FeedParser::FeedItem.new
26 item.instance_variable_set("@feed", feed)
27 item.title = "Some post"
28 item
29 end
30
31 def test_img
32 id = "abcd1234"
33 item = new_item
34 item.content = '<img src="http://www.example.com/pixel.jpg"/>'
35 HTTPFetcher.any_instance.expects(:fetch).with("http://www.example.com/pixel.jpg", anything).returns(jpg)
36 mail = item_to_mail(config, item, id, true, "feed2imap", true, false)
37 assert_match %r{<img src="data:image/jpg;base64,#{jpeg_base64}"/>}, mail.to_s
38 end
39
40 end