From 32a3713d07f8df7429a55a29150e3008d825c816 Mon Sep 17 00:00:00 2001 From: Akira TAGOH Date: Mon, 20 Dec 2010 20:04:43 +0900 Subject: some utilities written in Ruby to parse comps, package handling through yum etc. --- fontpackages/compat.rb | 79 ++++++++++++++++++ fontpackages/comps.rb | 195 +++++++++++++++++++++++++++++++++++++++++++ fontpackages/fontpackages.rb | 138 ++++++++++++++++++++++++++++++ fontpackages/yum.rb | 169 +++++++++++++++++++++++++++++++++++++ 4 files changed, 581 insertions(+) create mode 100644 fontpackages/compat.rb create mode 100755 fontpackages/comps.rb create mode 100755 fontpackages/fontpackages.rb create mode 100644 fontpackages/yum.rb diff --git a/fontpackages/compat.rb b/fontpackages/compat.rb new file mode 100644 index 0000000..421310c --- /dev/null +++ b/fontpackages/compat.rb @@ -0,0 +1,79 @@ +# compat.rb +# Copyright (C) 2010 Red Hat, Inc. + +# Authors: +# Akira TAGOH + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +begin + Shellwords.escape("foo") +rescue NoMethodError + module Shellwords + # + # Escapes a string so that it can be safely used in a Bourne shell + # command line. + # + # Note that a resulted string should be used unquoted and is not + # intended for use in double quotes nor in single quotes. + # + # open("| grep #{Shellwords.escape(pattern)} file") { |pipe| + # # ... + # } + # + # +String#shellescape+ is a shorthand for this function. + # + # open("| grep #{pattern.shellescape} file") { |pipe| + # # ... + # } + # + def shellescape(str) + # An empty argument will be skipped, so return empty quotes. + return "''" if str.empty? + + str = str.dup + + # Process as a single byte sequence because not all shell + # implementations are multibyte aware. + str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1") + + # A LF cannot be escaped with a backslash because a backslash + LF + # combo is regarded as line continuation and simply ignored. + str.gsub!(/\n/, "'\n'") + + return str + end + + module_function :shellescape + + class << self + alias escape shellescape + end + end # module Shellwords + + class String + # + # call-seq: + # str.shellescape => string + # + # Escapes +str+ so that it can be safely used in a Bourne shell + # command line. See +Shellwords::shellescape+ for details. + # + def shellescape + Shellwords.escape(self) + end + end +end diff --git a/fontpackages/comps.rb b/fontpackages/comps.rb new file mode 100755 index 0000000..9f654fc --- /dev/null +++ b/fontpackages/comps.rb @@ -0,0 +1,195 @@ +#! /usr/bin/env ruby +# -*- encoding: utf-8 mode: ruby -*- +# comps.rb +# Copyright (C) 2009-2010 Red Hat, Inc. + +# Authors: +# Akira TAGOH + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +require 'rubygems' +gem 'hpricot' +require 'hpricot' + +module Comps + +=begin rdoc + +== Comps::Package + +=end + + class Package + + def initialize(name, type, requires) + @name = name + @type = type + @requires = requires + end # def initialize + + attr_reader :name + + def is_default? + @type == "mandatory" || @type == "default" + end # def is_default? + + def is_mandatory? + @type == "mandatory" + end # def is_mandatory? + + def <=>(b) + @name <=> b.name + end # def + + end # class Package + +=begin rdoc + +== Comps::Group + +=end + + class Group + + def initialize(name, lang, is_enabled, is_visible) + @name = name + @is_lang_support = !lang.nil? && !lang.empty? + @lang = lang + @is_enabled = is_enabled + @is_visible = is_visible + @packages = [] + end # def initialize + + attr_reader :name, :lang + + def is_language_support? + @is_lang_support + end # def is_language_support? + + def is_enabled? + @is_enabled + end # def is_enabled? + + def is_visible? + @is_visible + end # def is_visible? + + def push(*x) + @packages.push(*x) + end # def push + + alias :<< :push + + def packages(mode = :all) + case mode + when :all + @packages + when :default + @packages.map do |pkg| + pkg.is_default? ? pkg : nil + end.compact + else + STDERR.printf("W: unknown query mode: %s\n", mode) + end + end # def packages + + def has_package?(package) + @packages.map do |pkg| + if package.kind_of?(Comps::Package) then + pkg.name == package.name + else + pkg.name == package + end + end.include?(true) + end # def has_package? + + def package(package) + @package.each do |pkg| + if package.kind_of?(Comps::Package) then + return pkg if pkg.name == package.name + else + return pkg if pkg.name == package + end + end + nil + end # def package + + end # class Group + +=begin rdoc + +== Comps::Root + +=end + + class Root + + def initialize(file) + File.open(file) do |f| + x = f.read + @doc = Hpricot(x) + end + end # def initialize + + def inspect + sprintf("#<%s:0x%x>", self.class, self.object_id) + end # def inspect + + def group(name) + _groups.map do |g| + g.name == name ? g : nil + end.compact[0] + end # def group + + def groups(mode = :all) + case mode + when :all + _groups + when :langonly + _groups.map do |g| + g.is_language_support? ? g : nil + end.compact + else + STDERR.printf("W: unknown query mode: %s\n", mode) + end + end # def groups + + private + + def _groups + retval = [] + @doc.search("group") do |element| + id = element.search("id") + if id.empty? then + STDERR.printf("W: invalid entry: %s", element.pretty_print) + else + lang = element.search("langonly") + default = element.search("default") + visible = element.search("uservisible") + retval << Comps::Group.new(id.inner_html, lang.inner_html, default.inner_html == "true", visible.inner_html == "true") + list = element.search("packagereq") + list.each do |pkg| + retval[-1] << Comps::Package.new(pkg.inner_html, pkg[:type], pkg[:conditional]) + end + end + end + retval + end # def _groups + + end # class Root + +end # module Comps diff --git a/fontpackages/fontpackages.rb b/fontpackages/fontpackages.rb new file mode 100755 index 0000000..95b2cb7 --- /dev/null +++ b/fontpackages/fontpackages.rb @@ -0,0 +1,138 @@ +#! /usr/bin/env ruby +# -*- encoding: utf-8 mode: ruby -*- +# fontpackages.rb +# Copyright (C) 2009-2010 Red Hat, Inc. + +# Authors: +# Akira TAGOH + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +begin + require 'fontpackages/comps' +rescue LoadError + require File.join(File.dirname(__FILE__), '..', 'fontpackages', 'comps') +end + +module FontPackages + +=begin rdoc + +== FontPackages::FontPackages + +=end + + class FontPackages + + def initialize(comps) + @comps = Comps::Root.new(comps) + end # def initialize + + def fontpackages(mode = :all) + grps = @comps.groups(:langonly) + grps << @comps.group("fonts") + grps << @comps.group("legacy-fonts") + grps.map do |grp| + case mode + when :all + grp.packages + when :default + grp.packages(:default) + else + STDERR.printf("W: unknown query mode: %s\n", mode) + [] + end.map do |pkg| + pkg.name =~ /-fonts\Z/ ? pkg : nil + end.compact + end.flatten + end # def fontpackages + + def supported_languages(package) + retval = [] + + @comps.groups.each do |grp| + if grp.has_package?(package) then + if grp.name !~ /fonts/ && (grp.lang.nil? || grp.lang.empty?) then + # assuming it may be "en" + retval << "en" + else + retval << grp.lang if !grp.lang.nil? && !grp.lang.empty? + end + end + end + + retval + end # def supported_languages + + def is_lgc_font?(package) + lang = supported_languages(package) + ll = [] + # Latin-1 + ll[1] = ['af', 'sq', 'br', 'ca', 'da', 'en', 'en_GB', 'gl', 'de', 'is', 'ga', 'it', 'ku', 'la', 'lb', 'nb', 'oc', 'pt_BR', 'pt', 'es', 'sw', 'sv', 'wa', 'eu'] # XXX: Faroese, Leonese, Rhaeto-Romanic, Scottish Gaelic + # Latin-2 + ll[2] = ['bs', 'hr', 'cs', 'de', 'hu', 'pl', 'ro', 'sr', 'sk', 'sl', 'hsb'] # XXX: Lower Sorbian + # Latin-3 + ll[3] = ['tr', 'mt', 'eo'] + # Latin-4 + ll[4] = ['et', 'lv', 'lt'] # XXX: Greenlandic, Sami + # Latin/Cyrillic + ll[5] = ['bg', 'be', 'ru', 'sr', 'mk'] + # Latin/Arabic + #ll[6] = ['ar'] + # Latin/Greek + ll[7] = ['el', 'ka'] + # Latin/Hebrew + #ll[8] = ['he'] + # Latin-5 + ll[9] = ['tr'] + # Latin-6 + ll[10] = ['is', 'nb', 'da', 'sv'] # XXX: Faroese + # Latin/Thai + #ll[11] = ['th'] + # Latin/Devanagari + # ll12: for Devanagari + # Latin-7 + #ll[13] = [] # XXX: Western Baltic, Eastern Baltic + # Latin-8 + ll[14] = ['gd', 'cy', 'br'] + # Latin-9 + ll[15] = ['af', 'sq', 'br', 'ca', 'da', 'nl', 'en', 'en_GB', 'et', 'fi', 'fr', 'gl', 'de', 'is', 'ga', 'it', 'ku', 'la', 'lb', 'ms', 'nb', 'oc', 'pt_BR', 'pt', 'es', 'sw', 'tl', 'wa'] # XXX: Faroese, Rhaeto-Romanic, Scottish Gaelic, Scots + # Latin-10 + ll[16] = ['sq', 'hr', 'hu', 'pl', 'ro', 'sl', 'fr', 'de', 'it', 'ga'] + + retval = false + ll.flatten.compact.sort.uniq.each do |l| + retval ||= lang.include?(l) + break if retval + end + retval + end # def is_lgc_font? + + def is_cijk_font?(package) + lang = supported_languages(package) + indic = ['as', 'bn', 'hne', 'gu', 'hi', 'kn', 'ks', 'kok', 'mai', 'ml', 'mr', 'ne', 'or', 'pa', 'sa', 'sd', 'si', 'ta', 'te'] + cjk = ['zh', 'ja', 'ko'] + retval = false + (cjk + indic).each do |ll| + retval ||= lang.include?(ll) + break if retval + end + retval + end # def is_cijk_font? + + end # class FontPackages + +end # module FontPackages diff --git a/fontpackages/yum.rb b/fontpackages/yum.rb new file mode 100644 index 0000000..4047b2d --- /dev/null +++ b/fontpackages/yum.rb @@ -0,0 +1,169 @@ +# yum.rb +# Copyright (C) 2010 Red Hat, Inc. + +# Authors: +# Akira TAGOH + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +require 'rubygems' +gem 'ruby-stemp' +require 'stemp' + +require 'fileutils' +require 'optparse' +require 'shellwords' +require 'tmpdir' +begin + require 'fontpackages/compat' +rescue LoadError + require File.join(File.dirname(__FILE__), 'comps') +end + + +class OptionParser + + module Arguable + + alias :orig_options :options + + def options + @yum_config ||= [] + orig_options do |opt| + opt.on('-C', '--cache', '[YUM] run from cache only') {|v| @yum_config << '-C'} + opt.on('--enablerepo=REPO', '[YUM] enable one or more repositories (wildcards allowed)') {|v| @yum_config << build_yumopt(:enablerepo, v)} + opt.on('--disablerepo=REPO', '[YUM] disable one or more repositories (wildcards allowed)') {|v| @yum_config << build_yumopt(:disablerepo, v)} + + yield opt + end + end # def options + + def yum_options + @yum_config + end # def yum_options + + private + + def build_yumopt(key, val) + sprintf("--%s=%s", key, val.shellescape) + end + + end # module Arguable + +end # class OptionParser + +module FontPackages + + class YumRepos + + def initialize(yumopts) + @yumopts = yumopts + @query_format = "" + @ignore_error = false + end # def initialize + + attr_accessor :query_format, :ignore_error + + def query(name, &block) + repoquery([yum_options, "-q", @query_format.empty? ? "" : sprintf("--qf=%s", @query_format), name], &block) + end # def query + + def packagelist(name, &block) + repoquery([yum_options, "-l", name], &block) + end # def packagelist + + def download(name) + tmpdir = nil + nvra = nil + begin + old_qf = query_format + self.query_format = "%{name}-%{version}-%{release}.%{arch}" + query(name) do |ret| + nvra = ret + break + end + ensure + self.query_format = old_qf + end + if nvra.nil? then + e = RuntimeError.new(sprintf("No such packages: %s", name)) + if ignore_error then + STDERR.printf("E: %s\n", e.message) + return + else + raise e + end + end + if block_given? then + tmpdir = STemp.mkdtemp(File.join(Dir.tmpdir, sprintf("%sXXXXXXXX", name))) + end + cwd = Dir.pwd + begin + Dir.chdir(tmpdir) unless tmpdir.nil? + cmd = sprintf("yumdownloader %s %s > /dev/null 2>&1", yum_options, nvra) + STDERR.printf("D: %s\n", cmd) if $DEBUG + system(cmd) + rpm = sprintf("%s.rpm", nvra) + unless File.exist?(rpm) then + e = RuntimeError.new(sprintf("Unable to download rpm: %s", nvra)) + if ignore_error then + STDERR.printf("E: %s\n", e.message) + else + raise e + end + end + yield self, rpm + ensure + FileUtils.rm_rf(tmpdir) unless tmpdir.nil? + Dir.chdir(cwd) + end + end # def download + + def extract(name) + download(name) do |x, rpm| + cmd = sprintf("rpm2cpio %s | cpio -id > /dev/null 2>&1", rpm) + STDERR.printf("D: %s\n", cmd) if $DEBUG + system(cmd) + yield x, rpm + end + end # def extract + + private + + def yum_options + if @yumopts.kind_of?(Array) then + @yumopts.join(' ') + elsif !@yumopts.nil? then + @yumopts + else + "" + end + end # def yum_options + + def repoquery(opts) + cmd = sprintf("repoquery %s 2> /dev/null", opts.join(' ')) + STDERR.printf("D: %s\n", cmd) if $DEBUG + IO.popen(cmd) do |f| + until f.eof? do + s = f.gets + yield s.chomp unless s.nil? + end + end + end # def repoquery + + end # class YumRepos + +end # module FontPackages -- cgit