]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - bin/rbotdb
[rbotdb] renamed import/export to restore/backup,
[user/henk/code/ruby/rbot.git] / bin / rbotdb
index 5f7491a73465ce822e71cbb2987290dd25863fca..b59498033011e9e2903612eb9fbbee70a416906b 100755 (executable)
@@ -2,11 +2,11 @@
 #-- vim:sw=2:et
 #++
 #
-# :title: RBot Registry Export, Import and Migration Script.
+# :title: RBot Registry Backup, Restore and Migration Script.
 #
 # You can use this script to,
-#   - export the rbot registry in a format that is platform/engine independent
-#   - import these backups in supported formats (dbm, daybreak)
+#   - backup the rbot registry in a format that is platform/engine independent
+#   - restore these backups in supported formats (dbm, daybreak)
 #   - migrate old rbot registries bdb (ruby 1.8) and tokyocabinet.
 #
 # For more information, just execute the script without any arguments!
 
 begin; require 'rubygems'; rescue Exception; end
 
-# old registry formats:
+# load registry formats:
 begin; require 'bdb'; rescue Exception; end
 begin; require 'tokyocabinet'; rescue Exception; end
-
-# new formats:
 begin; require 'dbm'; rescue Exception; end
 begin; require 'daybreak'; rescue Exception; end
+begin; require 'sqlite3'; rescue Exception; end
 
 puts 'RBot Registry Backup/Restore/Migrate'
 puts '[%s]' % ['Ruby: ' + RUBY_VERSION,
@@ -31,70 +30,72 @@ puts '[%s]' % ['Ruby: ' + RUBY_VERSION,
                'BDB: ' + (BDB::VERSION rescue '-'),
                'TokyoCabinet: ' + (TokyoCabinet::VERSION rescue '-'),
                'Daybreak: ' + (Daybreak::VERSION rescue '-'),
+               'SQLite: ' + (SQLite3::VERSION rescue '-'),
               ].join(' | ')
 
 require 'date'
 require 'optparse'
 
-TYPES = [:bdb, :tc, :dbm, :daybreak, :auto]
+TYPES = [:bdb, :tc, :dbm, :daybreak, :sqlite]
 options = {
   :profile => '~/.rbot',
-  :dbfile => './%s.rbot' % DateTime.now.strftime('export_%Y-%m-%d_%H%M%S'),
-  :type => :auto
+  :registry => nil,
+  :dbfile => './%s.rbot' % DateTime.now.strftime('backup_%Y-%m-%d_%H%M%S'),
+  :type => nil
 }
 opt_parser = OptionParser.new do |opt|
   opt.banner = 'Usage: rbotdb COMMAND [OPTIONS]'
   opt.separator ''
   opt.separator 'Commands:'
-  opt.separator '     export: store rbot registry platform-independently in a file.'
-  opt.separator '     import: restore rbot registry from such a file.'
+  opt.separator '     backup: store rbot registry platform-independently in a file.'
+  opt.separator '     restore: restore rbot registry from such a file.'
   opt.separator ''
   opt.separator 'Options:'
 
+  opt.on('-t', '--type TYPE', TYPES, 'format to backup/restore. Values: %s.' % [TYPES.join(', ')]) do |type|
+    options[:type] = type
+  end
+
   opt.on('-p', '--profile [PROFILE]', 'rbot profile directory. Defaults to: %s.' % options[:profile]) do |profile|
     options[:profile] = profile
   end
 
-  opt.on('-f', '--file [DBFILE]', 'cross-platform file to export to/import from. Defaults to: %s.' % options[:dbfile]) do |dbfile|
-    options[:dbfile] = dbfile
+  opt.on('-r', '--registry [REGISTRY]', 'registry-path to read/write, Optional, defaults to: <PROFILE>/registry_<TYPE>.') do |profile|
+    options[:registry] = profile
   end
 
-  opt.on('-t', '--type TYPE', TYPES, 'format to export/import. Values: %s. Defaults to %s.' % [TYPES.join(', '), options[:type]]) do |type|
-    options[:type] = type
+  opt.on('-f', '--file [DBFILE]', 'cross-platform file to backup to/restore from. Defaults to: %s.' % options[:dbfile]) do |dbfile|
+    options[:dbfile] = dbfile
   end
 
   opt.separator ''
 end
 
-class ExportRegistry
-  def initialize(profile, type)
+class BackupRegistry
+  def initialize(profile, type, registry)
     @profile = File.expand_path profile
     @type = type
-    puts 'Using type=%s profile=%s' % [@type, @profile]
+    @registry = registry
+    puts 'Using type=%s profile=%s registry=%s' % [@type, @profile, @registry.inspect]
   end
 
   # returns a hash with the complete registry data
-  def export
+  def backup
     listings = search
-    puts 'Found registry types: bdb=%d tc=%d dbm=%d daybreak=%d' % [
+    puts 'Found registry types: bdb=%d tc=%d dbm=%d daybreak=%d sqlite=%d' % [
       listings[:bdb].length, listings[:tc].length,
-      listings[:dbm].length, listings[:daybreak].length
+      listings[:dbm].length, listings[:daybreak].length, listings[:sqlite].length
     ]
-    if @type == :auto
-      @type = :bdb if listings[:bdb].length > 0
-      @type = :tc if listings[:tc].length > 0
-      @type = :dbm if listings[:dbm].length > 0
-      @type = :daybreak if listings[:daybreak].length > 0
-    end
-    if @type == :auto or listings[@type].empty?
+    if listings[@type].empty?
       puts 'No suitable registry found!'
-      return
+      exit
     end
     puts 'Using registry type: %s' % @type
     read(listings[@type])
   end
 
   def read(listing)
+    print "~Reading... (this might take a moment)\r"
     data = {}
     count = 0
     listing.each do |file|
@@ -108,6 +109,8 @@ class ExportRegistry
           read_dbm(file)
         when :daybreak
           read_daybreak(file)
+        when :sqlite
+          read_sqlite(file)
         end
         count += data[file.key].length
       rescue
@@ -161,16 +164,37 @@ class ExportRegistry
     data
   end
 
+  def read_sqlite(file)
+    data = {}
+    db = SQLite3::Database.new(file.abs)
+    res = db.execute('SELECT key, value FROM data')
+    res.each do |row|
+      key, value = row
+      data[key] = value
+    end
+    db.close
+    data
+  end
+
   # searches in profile directory for existing registry formats
   def search
     {
-      :tc => list(File.join(@profile, 'registry'), '*.tdb'),
-      :bdb => list(File.join(@profile, 'registry'), '*.db'),
-      :dbm => list(File.join(@profile, 'registry_dbm'), '*.*'),
-      :daybreak => list(File.join(@profile, 'registry_daybreak'), '*.db'),
+      :bdb => list(get_registry, '*.db'),
+      :tc => list(get_registry('_tc'), '*.tdb'),
+      :dbm => list(get_registry('_dbm'), '*.*'),
+      :daybreak => list(get_registry('_daybreak'), '*.db'),
+      :sqlite => list(get_registry('_sqlite'), '*.db'),
     }
   end
 
+  def get_registry(suffix='')
+    if @registry
+      File.expand_path(@registry)
+    else
+      File.join(@profile, 'registry'+suffix)
+    end
+  end
+
   class RegistryFile
     def initialize(folder, name)
       @folder = folder
@@ -196,27 +220,33 @@ class ExportRegistry
   end
 end
 
-class ImportRegistry
-  def initialize(profile, type)
+class RestoreRegistry
+  def initialize(profile, type, registry)
     @profile = File.expand_path profile
-    @type = (type == :auto) ? :dbm : type
+    @registry = registry ? File.expand_path(registry) : nil
+    @type = type
     puts 'Using type=%s profile=%s' % [@type, @profile]
   end
 
-  def import(data)
+  def restore(data)
     puts 'Using registry type: %s' % @type
     folder = create_folder
+    print "~Restoring... (this might take a moment)\r"
     data.each do |file, hash|
       file = File.join(folder, file)
       create_subdir(file)
       case @type
       when :dbm
         write_dbm(file, hash)
+      when :tc
+        write_tc(file, hash)
       when :daybreak
         write_daybreak(file, hash)
+      when :sqlite
+        write_sqlite(file, hash)
       end
     end
-    puts 'Import completed.'
+    puts  'Restore successful!                        '
   end
 
   def write_dbm(file, data)
@@ -227,6 +257,19 @@ class ImportRegistry
     db.close
   end
 
+  def write_tc(file, data)
+    db = TokyoCabinet::BDB.new
+    db.open(file + '.tdb',
+          TokyoCabinet::BDB::OREADER | 
+          TokyoCabinet::BDB::OCREAT | 
+          TokyoCabinet::BDB::OWRITER)
+    data.each_pair do |key, value|
+      db[key] = value
+    end
+    db.optimize
+    db.close
+  end
+
   def write_daybreak(file, data)
     db = Daybreak::DB.new(file + '.db')
     data.each_pair do |key, value|
@@ -235,21 +278,26 @@ class ImportRegistry
     db.close
   end
 
+  def write_sqlite(file, data)
+    db = SQLite3::Database.new(file + '.db')
+    db.execute('CREATE TABLE data (key string, value blob)') 
+    data.each_pair do |key, value|
+      db.execute('INSERT INTO data VALUES (?, ?)', 
+            key, value)
+    end
+    db.close
+  end
+
   def create_folder
-    folder = @profile
-    case @type
-    when :dbm
-      folder = File.join(folder, 'registry_dbm')
-    when :daybreak
-      folder = File.join(folder, 'registry_daybreak')
+    if @registry
+      folder = @registry
     else
-      puts 'ERROR: Unsupported import type: %s' % @type
-      exit
+      folder = File.join(@profile, 'registry_%s' % [@type.to_s])
     end
     Dir.mkdir(folder) unless File.directory?(folder)
     if File.directory?(folder) and Dir.glob(File.join(folder, '**')).select{|f|File.file? f}.length>0
-      puts 'ERROR: Unable to import!'
-      puts 'Import folder exists and is not empty: ' + folder
+      puts 'ERROR: Unable to restore!'
+      puts 'Restore folder exists and is not empty: ' + folder
       exit
     end
     folder
@@ -268,17 +316,22 @@ class ImportRegistry
 end
 
 opt_parser.parse!
+if ARGV.length > 0 and options[:type].nil?
+  puts opt_parser
+  puts 'Missing Argument: -t [type]'
+  exit
+end
 
 case ARGV[0]
-when 'export'
+when 'backup'
   if File.exists? options[:dbfile]
-    puts 'Export file already exists.'
+    puts 'Backup file already exists.'
     exit 
   end
 
-  reg = ExportRegistry.new(options[:profile], options[:type])
+  reg = BackupRegistry.new(options[:profile], options[:type], options[:registry])
 
-  data = reg.export
+  data = reg.backup
 
   if not data.empty?
     File.open(options[:dbfile], 'w') do |f|
@@ -287,17 +340,17 @@ when 'export'
     puts 'Written registry to ' + options[:dbfile]
   end
 
-when 'import'
+when 'restore'
   unless File.exists? options[:dbfile]
-    puts 'Import file does not exist.'
+    puts 'Backup file does not exist.'
     exit 
   end
 
-  reg = ImportRegistry.new(options[:profile], options[:type])
+  reg = RestoreRegistry.new(options[:profile], options[:type], options[:registry])
   data = Marshal.load File.read(options[:dbfile])
 
-  puts 'Read %d registry files from import file.' % data.length
-  reg.import data
+  puts 'Read %d registry files from backup file.' % data.length
+  reg.restore data
 
 else
   puts opt_parser