summaryrefslogtreecommitdiff
path: root/perllib
diff options
context:
space:
mode:
authorModestas Vainius <modestas@vainius.eu>2010-03-16 00:32:16 +0200
committerModestas Vainius <modestas@vainius.eu>2010-03-16 00:32:16 +0200
commitedd5387961a5b01efba9b63ec3d9187462c246ef (patch)
treeb3437ec2c7fe2ef3c7a0198fffd3bb2fca66973b /perllib
parentd750b7bdd629cbec8560d8fbb0d133e5982c7e79 (diff)
downloadpkg-kde-tools-edd5387961a5b01efba9b63ec3d9187462c246ef.tar.gz
Reorganize source package directory structure.
- get rid of symbolshelper, debhelper, vcs subdirectires. Move perl modules to perllib subdirectory and the rest to the top of the source tree; - unify how datalibdir is found and exported in the Debian::PkgKde module ({find,setup}_datalibdir()). Make pkgkde-gensymbols and pkgkde-symbolshelper use these new subroutines; - change datalib path in pkgkde-deb2symbols; - rewrite Makefile for reorganized source tree.
Diffstat (limited to 'perllib')
-rw-r--r--perllib/Debian/Debhelper/Buildsystem/kde.pm63
-rw-r--r--perllib/Debian/Debhelper/Sequence/kde.pm44
-rw-r--r--perllib/Debian/Debhelper/Sequence/pkgkde_symbolshelper.pm8
-rw-r--r--perllib/Debian/Debhelper/Sequence/sodeps.pm3
-rw-r--r--perllib/Debian/PkgKde.pm101
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/CompileTest.pm85
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/Patching.pm272
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/String.pm101
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/Subst.pm77
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/Substs.pm57
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/Substs/TypeSubst.pm399
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/Substs/VirtTable.pm112
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/Symbol.pm384
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/SymbolFile.pm320
-rw-r--r--perllib/Debian/PkgKde/SymbolsHelper/SymbolFileCollection.pm835
15 files changed, 2861 insertions, 0 deletions
diff --git a/perllib/Debian/Debhelper/Buildsystem/kde.pm b/perllib/Debian/Debhelper/Buildsystem/kde.pm
new file mode 100644
index 0000000..c88961b
--- /dev/null
+++ b/perllib/Debian/Debhelper/Buildsystem/kde.pm
@@ -0,0 +1,63 @@
+# A debhelper build system class for building KDE 4 packages.
+# It is based on cmake class but passes KDE 4 flags by default.
+#
+# Copyright: © 2009 Modestas Vainius
+# License: GPL-2+
+
+package Debian::Debhelper::Buildsystem::kde;
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib qw(error);
+use base 'Debian::Debhelper::Buildsystem::cmake';
+
+sub DESCRIPTION {
+ "CMake with KDE 4 flags"
+}
+
+sub KDE4_FLAGS_FILE {
+ my $file = "kde4_flags";
+ if (! -r $file) {
+ $file = "/usr/share/pkg-kde-tools/lib/kde4_flags";
+ }
+ if (! -r $file) {
+ error "kde4_flags file could not be found";
+ }
+ return $file;
+}
+
+# Use shell for parsing contents of the kde4_flags file
+sub get_kde4_flags {
+ my $this=shift;
+ my $file = KDE4_FLAGS_FILE;
+ my ($escaped_flags, @escaped_flags);
+ my $flags;
+
+ # Read escaped flags from the file
+ open(KDE4_FLAGS, "<", $file) || error("unable to open KDE 4 flags file: $file");
+ @escaped_flags = <KDE4_FLAGS>;
+ chop @escaped_flags;
+ $escaped_flags = join(" ", @escaped_flags);
+ close KDE4_FLAGS;
+
+ # Unescape flags using shell
+ $flags = `$^X -w -Mstrict -e 'print join("\\x1e", \@ARGV);' -- $escaped_flags`;
+ return split("\x1e", $flags);
+}
+
+sub configure {
+ my $this=shift;
+ my @flags = $this->get_kde4_flags();
+
+ # Skip RPATH if kdelibs5-dev is older than 4:4.4.0
+ my $kdever = `dpkg-query -f='\${Version}\n' -W kdelibs5-dev 2>/dev/null`;
+ if ($kdever &&
+ system("dpkg", "--compare-versions", $kdever, "lt", "4:4.4.0") == 0)
+ {
+ push @flags, "-DCMAKE_SKIP_RPATH:BOOL=ON";
+ }
+
+ return $this->SUPER::configure(@flags, @_);
+}
+
+1;
diff --git a/perllib/Debian/Debhelper/Sequence/kde.pm b/perllib/Debian/Debhelper/Sequence/kde.pm
new file mode 100644
index 0000000..ca25d78
--- /dev/null
+++ b/perllib/Debian/Debhelper/Sequence/kde.pm
@@ -0,0 +1,44 @@
+{
+ package Debian::Debhelper::Sequence::kde;
+ use Debian::Debhelper::Dh_Version;
+ use Debian::Debhelper::Dh_Lib qw(error);
+
+ sub ensure_debhelper_version {
+ my @v = split(/\./, $Debian::Debhelper::Dh_Version::version);
+ if ($v[0] > $_[0]) {
+ return 1;
+ }
+ elsif ($v[0] == $_[0]) {
+ if ($v[1] > $_[1]) {
+ return 1;
+ }
+ elsif ($v[1] == $_[1]) {
+ return $1 >= $_[2] if ($v[2] =~ /^(\d+)/);
+ }
+ }
+ return 0;
+ }
+ unless (ensure_debhelper_version(7, 3, 16)) {
+ error "debhelper addon 'kde' requires debhelper 7.3.16 or later";
+ }
+
+ 1;
+}
+
+# Build with kde buildsystem by default
+add_command_options("dh_auto_configure", "--buildsystem=kde");
+add_command_options("dh_auto_build", "--buildsystem=kde");
+add_command_options("dh_auto_test", "--buildsystem=kde");
+add_command_options("dh_auto_install", "--buildsystem=kde");
+add_command_options("dh_auto_clean", "--buildsystem=kde");
+
+# Omit usr/lib/kde4 from dh_makeshlibs by default
+add_command_options("dh_makeshlibs", "-Xusr/lib/kde4/");
+
+# Exclude kde documentation from dh_compress by default
+add_command_options("dh_compress",
+ qw(-X.dcl -X.docbook -X-license -X.tag -X.sty -X.el));
+
+insert_after("dh_install", "dh_movelibkdeinit");
+
+1;
diff --git a/perllib/Debian/Debhelper/Sequence/pkgkde_symbolshelper.pm b/perllib/Debian/Debhelper/Sequence/pkgkde_symbolshelper.pm
new file mode 100644
index 0000000..3577bc9
--- /dev/null
+++ b/perllib/Debian/Debhelper/Sequence/pkgkde_symbolshelper.pm
@@ -0,0 +1,8 @@
+use constant PKGKDE_BINDIR => '/usr/share/pkg-kde-tools/bin';
+
+# Add /usr/share/pkg-kde-tools/bin to $PATH
+if (! grep { PKGKDE_BINDIR eq $_ } split(":", $ENV{PATH})) {
+ $ENV{PATH} = PKGKDE_BINDIR . ":" . $ENV{PATH};
+}
+
+1;
diff --git a/perllib/Debian/Debhelper/Sequence/sodeps.pm b/perllib/Debian/Debhelper/Sequence/sodeps.pm
new file mode 100644
index 0000000..9ae6a03
--- /dev/null
+++ b/perllib/Debian/Debhelper/Sequence/sodeps.pm
@@ -0,0 +1,3 @@
+insert_after("dh_shlibdeps", "dh_sodeps");
+
+1;
diff --git a/perllib/Debian/PkgKde.pm b/perllib/Debian/PkgKde.pm
new file mode 100644
index 0000000..b9e8b2f
--- /dev/null
+++ b/perllib/Debian/PkgKde.pm
@@ -0,0 +1,101 @@
+package Debian::PkgKde;
+
+use base qw(Exporter);
+our @EXPORT = qw(get_program_name
+ printmsg info warning errormsg error syserr usageerr);
+our @EXPORT_OK = qw(find_datalibdir setup_datalibdir DATALIBDIR);
+
+# Determine datalib for current script. It depends on the context the script
+# was executed from.
+use constant DATALIBDIR => '/usr/share/pkg-kde-tools/lib';
+
+sub find_datalibdir {
+ my @hintfiles = @_;
+ my @dirs;
+ if ($0 =~ m@^(.+)/[^/]+$@) {
+ push @dirs, "$1/datalib";
+ }
+ push @dirs, DATALIBDIR;
+
+ # Verify if the dir and hint files exist
+ my $founddir;
+ foreach my $dir (@dirs) {
+ my $ok;
+ if ($dir && -d $dir) {
+ $ok = 1;
+ foreach my $hint (@hintfiles) {
+ unless (-e "$dir/$hint") {
+ $ok = 0;
+ last;
+ }
+ }
+ }
+ if ($ok) {
+ $founddir = $dir;
+ last;
+ }
+ }
+
+ return $founddir;
+}
+
+# Add DATALIBDIR to @INC if the script is NOT being run from the source tree.
+sub setup_datalibdir {
+ my $dir = find_datalibdir(@_);
+ if ($dir) {
+ unshift @INC, DATALIBDIR if $dir eq DATALIBDIR;
+ } else {
+ error("unable to locate pkg-kde-tools library directory");
+ }
+ return $dir;
+}
+
+{
+ my $progname;
+ sub get_program_name {
+ unless (defined $progname) {
+ $progname = ($0 =~ m,/([^/]+)$,) ? $1 : $0;
+ }
+ return $progname;
+ }
+}
+
+sub format_message {
+ my $type = shift;
+ my $format = shift;
+
+ my $msg = sprintf($format, @_);
+ return ((defined $type) ?
+ get_program_name() . ": $type: " : "") . "$msg\n";
+}
+
+sub printmsg {
+ print STDERR format_message(undef, @_);
+}
+
+sub info {
+ print STDERR format_message("info", @_);
+}
+
+sub warning {
+ warn format_message("warning", @_);
+}
+
+sub syserr {
+ my $msg = shift;
+ die format_message("error", "$msg: $!", @_);
+}
+
+sub errormsg {
+ print STDERR format_message("error", @_);
+}
+
+sub error {
+ die format_message("error", @_);
+}
+
+sub usageerr {
+ die format_message("usage", @_);
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/CompileTest.pm b/perllib/Debian/PkgKde/SymbolsHelper/CompileTest.pm
new file mode 100644
index 0000000..b91dc4a
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/CompileTest.pm
@@ -0,0 +1,85 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::CompileTest;
+
+use strict;
+use warnings;
+use File::Temp qw(tempdir);
+use File::Spec;
+use Dpkg::ErrorHandling;
+
+sub new {
+ my ($cls, $compiler, $lib) = @_;
+
+ my $tmpdir = tempdir();
+ my $sourcefile = "testcomp";
+ my $out = "testcomp";
+ my $cmd;
+
+ error("Unable to create a temporary directory for test compilation") unless $tmpdir;
+
+ if ($compiler =~ /gcc/) {
+ $sourcefile .= ".c";
+ } elsif ($compiler =~ /g\+\+/) {
+ $sourcefile .= ".cpp";
+ } else {
+ error("Unrecognized compiler: $compiler");
+ }
+ $sourcefile = File::Spec::catfile($tmpdir, $sourcefile);
+
+ if ($lib) {
+ $cmd = "$compiler -shared -fPIC";
+ $out .= ".so";
+ } else {
+ $cmd = "$compiler";
+ }
+ $out = File::Spec::catfile($tmpdir, $out);
+
+ my $self = bless { tmpdir => $tmpdir,
+ sourcefile => $sourcefile, out => $out }, $cls;
+ $self->set_cmd($cmd);
+ return $self;
+}
+
+sub set_cmd {
+ my ($self, $cmd) = @_;
+ $self->{cmd} = "$cmd $self->{sourcefile} -o $self->{out}";
+}
+
+sub compile {
+ my ($self, $sourcecode) = @_;
+
+ open(SOURCE, ">", $self->{sourcefile})
+ or error("Unable to open temporary source file for writing: $self->{sourcefile}");
+ print SOURCE $sourcecode
+ or error("Unable to write to temporary source file $self->{sourcefile}");
+ close(SOURCE);
+
+ system($self->{cmd}) == 0 or error("Compilation failed: $self->{cmd}");
+ return $self->get_output_file();
+}
+
+sub get_output_file {
+ my $self = shift;
+ return (-f $self->{out}) ? $self->{out} : undef;
+}
+
+sub rm {
+ my $self = shift;
+ system("rm -rf $self->{tmpdir}") == 0 or error("Unable to delete temporary directory: $self->{tmpdir}");
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/Patching.pm b/perllib/Debian/PkgKde/SymbolsHelper/Patching.pm
new file mode 100644
index 0000000..c1ccd12
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/Patching.pm
@@ -0,0 +1,272 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::Patching;
+
+use strict;
+use warnings;
+use base 'Exporter';
+
+use Dpkg::ErrorHandling;
+
+our @EXPORT = qw(parse_patches_from_handle parse_patches_from_file);
+
+sub parse_patches_from_handle {
+ my ($fh) = @_;
+ my $reparse_line;
+ my @patches;
+ my $patch;
+
+ while ($reparse_line || ($_ = <$fh>)) {
+ $reparse_line = 0;
+ if (defined $patch) {
+ if ($patch->has_header()) {
+ if (m/^@@ /) {
+ unless ($patch->is_valid()) {
+ warning("patch '".$patch->get_name()."' hunk is invalid at line $.");
+ }
+ $patch->append_line($_);
+ } elsif (!$patch->is_valid() && m/^[+ -]/) {
+ # Patch continues
+ $patch->append_line($_);
+ } else {
+ # Patch ended
+ if ($patch->complete()) {
+ push @patches, $patch;
+ } else {
+ warning("patch '".$patch->get_name()."' is invalid");
+ }
+ $patch = undef;
+ $reparse_line = 1;
+ next;
+ }
+ } elsif (defined $patch->{source}) {
+ if (m/^[+]{3}\s+(\S+)/) {
+ # Found the patch header portion
+ $patch->set_target($1);
+ } else {
+ $patch = undef;
+ $reparse_line = 1;
+ }
+ }
+ } elsif (m/^[-]{3}\s+(\S+)(?:\s+\(([^_]+)_([^_]+)_([^_]+)\))?/) {
+ $patch = Debian::PkgKde::SymbolsHelper::Patch->new();
+ $patch->set_source($1);
+ $patch->set_info($2, $3, $4);
+ }
+ }
+ if (defined $patch) {
+ if ($patch->complete()) {
+ push @patches, $patch;
+ } else {
+ warning("patch '".$patch->get_name()."' is invalid");
+ }
+ }
+ return @patches;
+}
+
+sub parse_patches_from_file {
+ my ($filename) = @_;
+ open(my $fh, "<", $filename) or error("unable to open patch file '$filename'");
+ my @ret = parse_patches_from_handle($fh);
+ close $fh;
+ return @ret;
+}
+
+package Debian::PkgKde::SymbolsHelper::Patch;
+
+use strict;
+use warnings;
+
+use Dpkg::ErrorHandling;
+use Dpkg::IPC;
+
+sub new {
+ my $class = shift;
+ return bless {
+ file => undef,
+ source => undef,
+ target => undef,
+ package => undef,
+ version => undef,
+ arch => undef,
+ patch => undef,
+ hunk_minus => 0,
+ hunk_plus => 0,
+ }, $class;
+}
+
+sub set_source {
+ my ($self, $srcfile) = @_;
+ $self->{source} = $srcfile;
+}
+
+sub set_info {
+ my ($self, $package, $version, $arch) = @_;
+ $self->{package} = $package;
+ $self->{version} = $version;
+ $self->{arch} = $arch;
+}
+
+sub get_info {
+ my $self = shift;
+ return (
+ package => $self->{package},
+ version => $self->{version},
+ arch => $self->{arch},
+ );
+}
+
+sub has_info {
+ my $self = shift;
+ return defined $self->{package};
+}
+
+sub set_target {
+ my ($self, $target) = @_;
+ $self->{target} = $target;
+}
+
+sub has_header {
+ my $self = shift;
+ return defined $self->{source} && defined $self->{target};
+}
+
+sub get_name {
+ my $self = shift;
+ if ($self->{source}) {
+ if ($self->has_info()) {
+ return sprintf("%s_%s_%s (--- %s)", $self->{package},
+ $self->{version}, $self->{arch}, $self->{source});
+ } else {
+ return sprintf("--- %s +++ %s", $self->{source}, $self->{target});
+ }
+ } else {
+ return "<empty patch>";
+ }
+}
+
+sub is_valid {
+ my $self = shift;
+ return (defined $self->{hunk_minus} &&
+ $self->{hunk_minus} + $self->{hunk_plus} == 0);
+}
+
+sub open_patch_fh {
+ my ($self, $mode) = @_;
+ my $patch = $self->{patch};
+ if (!defined $patch) {
+ my $var;
+ $patch = $self->{patch} = \$var;
+ }
+ open(my $fh, $mode, $patch)
+ or systemerr("unable to open in-memory patch file");
+ return $fh;
+}
+
+sub append_line {
+ my ($self, $line) = @_;
+ my $fh = $self->{fh};
+ unless (defined $fh) {
+ $fh = $self->open_patch_fh(">");
+ $self->{fh} = $fh;
+ }
+ if (defined $self->{hunk_minus}) {
+ if ($line =~ /^@@\s*-\d+,(\d+)\s+[+]\d+,(\d+)\s*@@/) {
+ if ($self->{hunk_minus} + $self->{hunk_plus} == 0) {
+ $self->{hunk_minus} = $1;
+ $self->{hunk_plus} = $2;
+ } else {
+ # Bogus patch
+ $self->{hunk_minus} = undef;
+ $self->{hunk_plus} = undef;
+ }
+ } elsif ($line =~ /^-/) {
+ $self->{hunk_minus}--;
+ } elsif ($line =~ /^\+/) {
+ $self->{hunk_plus}--;
+ } elsif ($line =~ /^ /) {
+ $self->{hunk_minus}--;
+ $self->{hunk_plus}--;
+ } else {
+ warning("patch ignored. Invalid patch line: $line");
+ $self->{hunk_minus} = undef;
+ $self->{hunk_plus} = undef;
+ }
+ }
+ print $fh $line;
+}
+
+sub complete {
+ my $self = shift;
+ close $self->{fh};
+ delete $self->{fh};
+ return $self->is_valid();
+}
+
+sub output {
+ my ($self, $outfh, $filename) = @_;
+ $filename = $self->{target} unless $filename;
+
+ print $outfh "--- ", $filename, "\n";
+ print $outfh "+++ ", $filename, "\n";
+
+ my $infh = $self->open_patch_fh("<");
+ while (<$infh>) {
+ print $outfh $_;
+ }
+ close $infh;
+}
+
+sub apply {
+ my ($self, $filename) = @_;
+
+ my $outfile = File::Temp->new(TEMPLATE => "${filename}_patch.out.XXXXXX");
+ my $to_patch_process;
+ my $pid = spawn(exec => [ "patch", "--posix", "--force", "-r-", "-p0" ],
+ from_pipe => \$to_patch_process,
+ to_handle => $outfile,
+ error_to_handle => $outfile,
+ wait_child => 0
+ );
+ my $ret = $self->output($to_patch_process, $filename);
+ close $to_patch_process;
+ wait_child($pid, nocheck => 1);
+ $ret &&= !$?;
+ if ($ret) {
+ $self->{applied} = $filename;
+ } else {
+ open(my $outputfd, "<", $outfile->filename)
+ or syserr("unable to reopen temporary file");
+ my $output;
+ while (<$outputfd>) {
+ $output .= $_;
+ }
+ close $outputfd;
+ chop $output;
+ $self->{apply_output} = $output;
+ }
+ return $ret;
+}
+
+sub is_applied {
+ my $self = shift;
+ return $self->{applied};
+}
+
+sub get_apply_output {
+ my $self = shift;
+ return $self->{apply_output};
+}
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/String.pm b/perllib/Debian/PkgKde/SymbolsHelper/String.pm
new file mode 100644
index 0000000..477f34b
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/String.pm
@@ -0,0 +1,101 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::String;
+
+use strict;
+use warnings;
+
+use overload '""' => \&get_string;
+
+sub new {
+ my ($class, $str) = @_;
+ return bless { str => $str }, $class;
+}
+
+sub init_string2_by_re {
+ my ($self, $str2, $re, $values) = @_;
+ my @str2 = split(//, $self->get_string());
+ my $offset = 0;
+ while ($str2 =~ m/$re/g) {
+ my $key = $1;
+ my $i = pos($str2) - length($&) - $offset;
+ $str2[$i] = "$&";
+ my $count = $i + length($values->{$key});
+ for ($i++; $i < $count; $i++) {
+ $str2[$i] = undef;
+ }
+ $offset += length($&) - length($values->{$key});
+ }
+ $self->{str2} = \@str2;
+}
+
+sub substr {
+ my ($self, $offset, $length, $repl1, $repl2) = @_;
+ if (defined $repl2 || exists $self->{str2}) {
+ # If str2 has not been created yet, create it
+ if (!exists $self->{str2}) {
+ $self->{str2} = [ split(//, $self->{str}) ];
+ }
+ # Keep offset information intact with $repl1
+ my @repl2;
+ my $edit_str2 = 1;
+ if (defined $repl2) {
+ @repl2 = map { undef } split(//, $repl1);
+ $repl2[0] = $repl2;
+ } elsif ($length != length($repl1)) {
+ if (!defined $repl2) {
+ for (my $i = 0; $i < length($repl1); $i++) {
+ if ($i < $length) {
+ push @repl2, $self->{str2}[$offset+$i];
+ } else {
+ push @repl2, undef;
+ }
+ }
+ }
+ } else {
+ $edit_str2 = 0;
+ }
+ splice @{$self->{str2}}, $offset, $length, @repl2 if $edit_str2;
+ }
+ substr($self->{str}, $offset, $length) = $repl1;
+}
+
+sub get_string {
+ return shift()->{str};
+}
+
+sub has_string2 {
+ return exists shift()->{str2};
+}
+
+sub get_string2_char {
+ my ($self, $index) = @_;
+ return $self->{str2}->[$index];
+}
+
+sub get_string2 {
+ my $self = shift;
+ if (defined $self->{str2}) {
+ my $str = "";
+ foreach my $s (@{$self->{str2}}) {
+ $str .= $s if defined $s;
+ }
+ return $str;
+ }
+ return $self->get_string();
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/Subst.pm b/perllib/Debian/PkgKde/SymbolsHelper/Subst.pm
new file mode 100644
index 0000000..749c0c0
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/Subst.pm
@@ -0,0 +1,77 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::Subst;
+
+use strict;
+use warnings;
+
+sub new {
+ my ($class, %opts) = @_;
+ return bless { cache => {}, %opts }, $class;
+}
+
+sub get_name {
+ my $self = shift;
+ # Must be overriden
+}
+
+sub _expand {
+ my ($self, $arch, $val) = @_;
+ # Must be overriden
+}
+
+# $subst is here in order to support substs with values
+sub expand {
+ my ($self, $arch, $val) = @_;
+ my $cache = ($val) ? "${arch}__$val" : $arch;
+ unless (exists $self->{cache}{$cache}) {
+ $self->{cache}{$cache} = $self->_expand($arch, $val);
+ }
+ return $self->{cache}{$cache};
+}
+
+# Prepare $rawname before detect()/neutralize()
+# my ($self, $rawname, $arch) = @_;
+sub prep {
+}
+
+# Make the raw symbol name architecture neutral
+# my ($self, $rawname) = @_;
+sub neutralize {
+ return undef;
+}
+
+# Hinted neutralize where $hint is an already "detected"
+# SymbolsHelper::String
+# my ($self, $rawname, $hint) = @_;
+sub hinted_neutralize {
+ return undef;
+}
+
+# Detect if the substitution can be applied to a bunch of
+# arch specific raw names.
+# my ($self, $rawname, $arch, $arch_rawnames) = @_;
+sub detect {
+ return 0;
+}
+
+# Verifies if the subst is correct at $pos
+# my ($self, $pos, $arch_rawnames) = @_;
+sub verify_at {
+ return undef;
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/Substs.pm b/perllib/Debian/PkgKde/SymbolsHelper/Substs.pm
new file mode 100644
index 0000000..eed306f
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/Substs.pm
@@ -0,0 +1,57 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::Substs;
+
+use strict;
+use warnings;
+use Debian::PkgKde::SymbolsHelper::Substs::VirtTable;
+use Debian::PkgKde::SymbolsHelper::Substs::TypeSubst;
+use base 'Exporter';
+
+our @EXPORT = qw(%SUBSTS @SUBSTS @STANDALONE_SUBSTS @TYPE_SUBSTS @CPP_TYPE_SUBSTS);
+
+my $NS = 'Debian::PkgKde::SymbolsHelper::Substs';
+
+our @STANDALONE_SUBSTS = (
+ "${NS}::VirtTable"->new(),
+);
+
+our @TYPE_SUBSTS = (
+ "${NS}::TypeSubst::size_t"->new(),
+ "${NS}::TypeSubst::ssize_t"->new(),
+ "${NS}::TypeSubst::int64_t"->new(),
+ "${NS}::TypeSubst::uint64_t"->new(),
+ "${NS}::TypeSubst::qptrdiff"->new(),
+ "${NS}::TypeSubst::quintptr"->new(),
+ "${NS}::TypeSubst::qreal"->new(),
+);
+
+our @CPP_TYPE_SUBSTS;
+foreach my $subst (@TYPE_SUBSTS) {
+ push @CPP_TYPE_SUBSTS, "${NS}::TypeSubst::Cpp"->new($subst);
+}
+
+our @SUBSTS = (
+ @STANDALONE_SUBSTS,
+ @TYPE_SUBSTS,
+);
+
+our %SUBSTS;
+foreach my $subst (@SUBSTS, @CPP_TYPE_SUBSTS) {
+ $SUBSTS{$subst->get_name()} = $subst;
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/Substs/TypeSubst.pm b/perllib/Debian/PkgKde/SymbolsHelper/Substs/TypeSubst.pm
new file mode 100644
index 0000000..92af20a
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/Substs/TypeSubst.pm
@@ -0,0 +1,399 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Subst';
+
+use Debian::PkgKde::SymbolsHelper::Substs;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{'length'} = 1; # Basic typesubt must be one letter
+ return $self;
+}
+
+sub get_name {
+ my $self = shift;
+ return substr($self->{substvar}, 1, -1);
+}
+
+sub get_types_re {
+ my $self = shift;
+ unless (exists $self->{types_re}) {
+ my $s = '[' . join("", @{$self->{types}}) . ']';
+ $self->{types_re} = qr/$s/;
+ }
+ return $self->{types_re};
+}
+
+sub neutralize {
+ my ($self, $rawname) = @_;
+ my $ret = 0;
+ my $str = "$rawname";
+ my $l = $self->{'length'};
+ my $re = $self->get_types_re();
+
+ while ($str =~ /$re/g) {
+ $rawname->substr(pos($str)-$l, $l, $self->{types}->[0]);
+ $ret = 1;
+ }
+ return ($ret) ? $rawname : undef;
+}
+
+sub hinted_neutralize {
+ my ($self, $rawname, $hint) = @_;
+ my $hintstr = $hint->{str2};
+ my $ret = 1;
+ my $l = $self->{'length'};
+
+ for (my $i = 0; $i < @$hintstr; $i++) {
+ if (defined $hintstr->[$i] && $hintstr->[$i] eq $self->{substvar}) {
+ $rawname->substr($i, $l, $self->{types}->[0], $self->{substvar});
+ $ret = 1;
+ }
+ }
+ return ($ret) ? $rawname : undef;
+}
+
+sub detect {
+ my ($self, $rawname, $arch, $arch_rawnames) = @_;
+
+ my $l = $self->{'length'};
+ my $s1 = $rawname;
+ my $t1 = $self->expand($arch);
+ my ($s2, $t2);
+
+ # Find architecture with other type
+ foreach my $a2 (keys %$arch_rawnames) {
+ $t2 = $self->expand($a2);
+ if ($t2 ne $t1) {
+ $s2 = $arch_rawnames->{$a2};
+ last;
+ }
+ }
+
+ return 0 unless defined $s2;
+
+ # Verify subst and replace it with types[0] and substvar
+ my $ret = 0;
+ search_next: for (my $pos = 0; ($pos = index($s1, $t1, $pos)) != -1; $pos++) {
+ # Verify on the selected $a2
+ if ($t2 eq substr($s2, $pos, $l)) {
+ # Maybe subst is already there?
+ if ($rawname->has_string2() &&
+ (my $char = $rawname->get_string2_char($pos)))
+ {
+ if ($char eq $self->{substvar}) {
+ # Nothing to do
+ $ret = 1;
+ $pos += $l-1;
+ next search_next;
+ } elsif ($char =~ /^{(.*)}$/) {
+ # Another subst. Verify it
+ # NOTE: %SUBSTS won't work here due to recursive "use"
+ my $othersubst = $Debian::PkgKde::SymbolsHelper::Substs::SUBSTS{$1};
+ if (defined $othersubst && $othersubst->verify_at($pos, $arch_rawnames)) {
+ $ret = 1;
+ next search_next;
+ }
+ }
+ }
+ # Now verify detection on other arches
+ if ($self->verify_at($pos, $arch_rawnames)) {
+ $rawname->substr($pos, $l, $self->{types}->[0], $self->{substvar});
+ $ret = 1;
+ $pos += $l-1;
+ }
+ }
+ }
+ return $ret;
+}
+
+sub verify_at {
+ my ($self, $pos, $arch_rawnames) = @_;
+ my $l = $self->{'length'};
+ my $verified = 1;
+ foreach my $a (keys %$arch_rawnames) {
+ my $t = $self->expand($a);
+ if ($t ne substr($arch_rawnames->{$a}, $pos, $l)) {
+ $verified = 0;
+ last;
+ }
+ }
+ return $verified;
+}
+
+# Operates on %l% etc. same length types that cannot be present in demanged
+# symbols. Used by ::Cpp wrapper.
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::CppPrivate;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my ($class, $base) = @_;
+ my $self = $class->SUPER::new();
+ $self->{base} = $base;
+ $self->{'length'} = 3; # raw type + length('%%')
+ $self->{substvar} = '{' . $self->get_name() . '}';
+ $self->{types} = [ map { '%' . $_ . '%' } @{$base->{types}} ];
+ return $self;;
+}
+
+sub get_name {
+ my $self = shift;
+ return "c++:" . $self->{base}->get_name();
+}
+
+sub get_types_re {
+ my $self = shift;
+ unless (exists $self->{types_re}) {
+ my $s = '%[' . join("", @{$self->{base}{types}}) . ']%';
+ $self->{types_re} = qr/$s/;
+ }
+ return $self->{types_re};
+}
+
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return '%'.$self->{base}->_expand($arch).'%';
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::Cpp;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Subst';
+
+my %CPP_MAP = (
+ m => 'unsigned long',
+ j => 'unsigned int',
+ i => 'int',
+ l => 'long',
+ x => 'long long',
+ y => 'unsigned long long',
+ f => 'float',
+ d => 'double',
+);
+
+my %CPPRE_MAP = (
+ '%m%' => qr/\bunsigned long(?! long)\b/,
+ '%j%' => qr/\bunsigned int\b/,
+ '%i%' => qr/\b(?<!unsigned )int\b/,
+ '%l%' => qr/\b(?<!unsigned )long(?! long)\b/,
+ '%x%' => qr/\b(?<!unsigned )long long\b/,
+ '%y%' => qr/\bunsigned long long\b/,
+ '%f%' => qr/\bfloat\b/,
+ '%d%' => qr/\bdouble\b/,
+);
+
+sub new {
+ my ($class, $base) = @_;
+ my $self = $class->SUPER::new();
+ $self->{private} =
+ Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::CppPrivate->new($base);
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return $CPP_MAP{$self->{private}{base}->_expand($arch)};
+}
+
+sub get_name {
+ my $self = shift;
+ return $self->{private}->get_name();
+}
+
+# In order for detect()/neutralize() to work, all substs must be of the same
+# length. Therefore replace demangled names with %l% etc.
+sub prep {
+ my ($self, $rawname, $arch) = @_;
+
+ # We need to prepare $rawname only once for all Cpp substs
+ return if exists $rawname->{cpp_prepped};
+
+ my $str = "$rawname";
+ foreach my $key (keys %CPPRE_MAP) {
+ my $re = $CPPRE_MAP{$key};
+ while ($str =~ /$re/g) {
+ my $l = length($&);
+ $rawname->substr(pos($str)-$l, $l, $key, $&);
+ $str = "$rawname" if $l != length($key);
+ }
+ }
+ $rawname->{cpp_prepped} = 1;
+}
+
+sub detect {
+ my $self = shift;
+ return $self->{private}->detect(@_);
+}
+
+sub neutralize {
+ my $self = shift;
+ return $self->{private}->neutralize(@_);
+}
+
+sub hinted_neutralize {
+ my $self = shift;
+ return $self->{private}->hinted_neutralize(@_);
+}
+
+sub verify_at {
+ my $self = shift;
+ return $self->{private}->verify_at(@_);
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::size_t;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{substvar} = "{size_t}";
+ $self->{types} = [ qw(m j) ]; # unsigned long / unsigned int
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return ($arch =~ /amd64|ia64|alpha|s390/) ? "m" : "j";
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::ssize_t;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{substvar} = "{ssize_t}";
+ $self->{types} = [ qw(l i) ]; # long / int
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return ($arch =~ /amd64|ia64|alpha|s390/) ? 'l' : 'i';
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::int64_t;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{substvar} = "{int64_t}";
+ $self->{types} = [ qw(l x) ]; # long / long long
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return ($arch =~ /amd64|ia64|alpha/) ? 'l' : 'x';
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::uint64_t;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{substvar} = "{uint64_t}";
+ $self->{types} = [ qw(m y) ]; # unsigned long / unsigned long long
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return ($arch =~ /amd64|ia64|alpha/) ? 'm' : 'y';
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::qptrdiff;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{substvar} = "{qptrdiff}";
+ $self->{types} = [ qw(x i) ]; # long long / int
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return ($arch =~ /amd64|ia64|alpha/) ? 'x' : 'i';
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::quintptr;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{substvar} = "{quintptr}";
+ $self->{types} = [ qw(y j) ]; # unsigned long long / unsigned int
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return ($arch =~ /amd64|ia64|alpha/) ? 'y' : 'j';
+}
+
+package Debian::PkgKde::SymbolsHelper::Substs::TypeSubst::qreal;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Substs::TypeSubst';
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ $self->{substvar} = "{qreal}";
+ $self->{types} = [ qw(d f) ]; # double / float
+ return $self;
+}
+
+sub _expand {
+ my ($self, $arch) = @_;
+ return ($arch =~ /arm/) ? 'f' : 'd';
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/Substs/VirtTable.pm b/perllib/Debian/PkgKde/SymbolsHelper/Substs/VirtTable.pm
new file mode 100644
index 0000000..4cb0d61
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/Substs/VirtTable.pm
@@ -0,0 +1,112 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::Substs::VirtTable;
+
+use strict;
+use warnings;
+use base 'Debian::PkgKde::SymbolsHelper::Subst';
+
+use Dpkg::ErrorHandling;
+use Dpkg::Shlibs::Cppfilt;
+
+# Expand support (for backwards compatibility)
+# Neutralize support
+
+sub get_name {
+ "vt";
+}
+
+sub _expand {
+ my ($self, $arch, $value) = @_;
+
+ # Mult should be 1 on 32bit arches and 2 on 64bit arches
+ my $mult = ($arch =~ /amd64|ia64|alpha/) ? 2 : 1;
+ return $mult * $value;
+}
+
+sub subvt {
+ my ($self, $rawname, $number, $stroffset) = @_;
+ $rawname->substr($stroffset, length("$number"), "0",
+ ($self->{__detect__}) ? $number : undef);
+ return 1;
+}
+
+sub find_ztc_offset {
+ my ($self, $rawname) = @_;
+ $rawname = "$rawname";
+
+ # The idea behind the algorithm is that c++filt output does not
+ # change when offset is changed.
+ # e.g. _ZTCN6KParts15DockMainWindow3E56_NS_8PartBaseE
+
+ my @matches = ($rawname =~ m/(\d+)_/gc);
+ if (!@matches) {
+ error("Invalid construction table symbol: $rawname");
+ } elsif (@matches == 1) {
+ # Found it
+ return (pos($rawname) - length($1) - 1, $1);
+ } else {
+ # The idea behind the algorithm is that c++filt output does not
+ # change when an offset is changed.
+ $rawname =~ s/@[^@]+$//;
+ my $demangled = cppfilt_demangle_cpp($rawname, 'auto');
+ pos($rawname) = undef;
+ while ($rawname =~ m/(\d+)_/g) {
+ my $offset = $1;
+ my $pos = pos($rawname) - length($offset) - 1;
+ my $newsymbol = $rawname;
+ substr($newsymbol, $pos, length($offset)) = $offset + 1234;
+ my $newdemangled = cppfilt_demangle_cpp($newsymbol, 'auto');
+ return ($pos, $offset) if (defined $newdemangled && $newdemangled eq $demangled);
+ }
+ error("Unable to determine construction table offset position in symbol '$rawname'");
+ }
+}
+
+sub neutralize {
+ my ($self, $rawname) = @_;
+ my $ret = 1;
+
+ # construction vtable: e.g. _ZTCN6KParts15DockMainWindow3E56_NS_8PartBaseE
+ if ($rawname =~ /^_ZTC/) {
+ my ($pos, $num) = $self->find_ztc_offset($rawname);
+ $ret = $self->subvt($rawname, $num, $pos) if ($num > 0);
+ } elsif ($rawname =~ /^_ZThn(\d+)_/) {
+ # non-virtual base override: e.g. _ZThn8_N6KParts13ReadWritePartD0Ev
+ my $num = $1;
+ $ret = $self->subvt($rawname, $num, 5) if ($num > 0);
+ } elsif ($rawname =~ /^_ZTvn?(\d+)_(n?\d+)/) {
+ # virtual base override, with vcall offset, e.g. _ZTv0_n12_N6KParts6PluginD0Ev
+ my $voffset = $1;
+ my $num = $2;
+ my $numoffset = 4 + length("$voffset") + 1 + (($num =~ /^n/) ? 1 : 0);
+ $num =~ s/^n//;
+
+ $ret = $self->subvt($rawname, $voffset, 4) if ($voffset > 0);
+ $ret = $self->subvt($rawname, $num, $numoffset) || $ret if ($num > 0);
+ }
+ return ($ret) ? $rawname : undef;
+}
+
+sub detect {
+ my $self = shift;
+ $self->{__detect__} = 1;
+ my $ret = $self->neutralize(@_);
+ delete $self->{__detect__};
+ return $ret;
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/Symbol.pm b/perllib/Debian/PkgKde/SymbolsHelper/Symbol.pm
new file mode 100644
index 0000000..1588487
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/Symbol.pm
@@ -0,0 +1,384 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::Symbol;
+
+use strict;
+use warnings;
+use base 'Dpkg::Shlibs::Symbol';
+
+use Dpkg::Gettext;
+use Dpkg::Shlibs::Cppfilt;
+use Dpkg::Arch qw(get_valid_arches);
+use Dpkg::ErrorHandling;
+use Debian::PkgKde::SymbolsHelper::Substs;
+use Debian::PkgKde::SymbolsHelper::String;
+
+sub get_h_name {
+ my $self = shift;
+ if (!exists $self->{h_name}) {
+ $self->{h_name} = Debian::PkgKde::SymbolsHelper::String->new(
+ $self->get_symbolname()
+ );
+ if (exists $self->{substs}) {
+ # We need to recreate string2 from the templ
+ $self->{h_name}->init_string2_by_re($self->get_symboltempl(),
+ qr/\{([^}]+)\}/, $self->{substs});
+ if ($self->{h_name}->get_string2() ne $self->get_symboltempl()) {
+ internerr("unsupported substitutions/alterations in the symbol template '%s'. " .
+ "Parsed as '%s'. Cannot continue.",
+ $self->get_symboltempl(), $self->{h_name}->get_string2());
+ }
+ }
+ }
+ return $self->{h_name};
+}
+
+sub reset_h_name {
+ my ($self, $new_h_name) = @_;
+ if (defined $new_h_name) {
+ $self->{h_name} = $new_h_name;
+ } else {
+ delete $self->{h_name};
+ }
+}
+
+sub resync_name_with_h_name {
+ my $self = shift;
+ if (exists $self->{h_name}) {
+ my $h_name = $self->{h_name};
+ $self->set_symbolname($h_name->get_string(), $h_name->get_string2());
+ }
+}
+
+# Needed for h_name above
+sub get_substs {
+ my $self = shift;
+ return $self->{substs};
+}
+
+sub is_vtt {
+ return shift()->get_symbolname() =~ /^_ZT[VT]/;
+}
+
+sub initialize {
+ my ($self, %opts) = @_;
+
+ # Expand substvars
+ if ($self->has_tag('subst')) {
+ # Expand substitutions in the symbol name. See below.
+ if ($self->expand_substitutions(%opts) == 0) {
+ # Redundant subst tag. Warn.
+ warning(_g("%s: no valid substitutions, 'subst' tag is redundant"),
+ $self->get_symbolname());
+ }
+ }
+
+ # NOTE: backwards compatibility with pkgkde-symbolshelper (<< 0.6)
+ # symbol files.
+ if ($self->get_symbolname() =~ /\{.+\}/) {
+ $self->{symbol} =~ s/\{vt:/{vt=/g;
+ if (defined $self->{symbol_templ}) {
+ $self->{symbol_templ} =~ s/\{vt:/{vt=/g;
+ } else {
+ $self->{symbol_templ} = $self->{symbol};
+ }
+ my @substs = $self->expand_substitutions(%opts);
+
+ my $templ = $self->get_symboltempl();
+ my $vt = 0;
+ foreach my $subst (@substs) {
+ # Drop obsolete vt subst completely
+ if ($subst =~ /^vt=/) {
+ $templ =~ s/\Q{$subst}\E/$self->{substs}{$subst}/g;
+ $vt++;
+ }
+ }
+ $self->set_symbolname(undef, $templ) if $vt > 0;
+ if ($vt < scalar(@substs)) {
+ if ($vt) {
+ $self->add_tag('subst', 'compat-no-vt');
+ } else {
+ $self->add_tag('subst', 'compat');
+ }
+ }
+ }
+
+ return $self->SUPER::initialize(%opts);
+}
+
+sub expand_substitutions {
+ my ($self, %opts) = @_;
+ my $symbol = $self->get_symbolname();
+ my %substs;
+
+ # Collect substitutions in the symbol name
+ while ($symbol =~ /\{(([^}=]+)(?:=([^}]+))?)\}/g) {
+ my $subst = $1;
+ my $name = $2;
+ my $val = $3;
+ unless (exists $substs{$name}) {
+ my $substobj = $SUBSTS{$name};
+ if (defined $subst) {
+ $substs{$subst} = $substobj->expand($opts{arch}, $val);
+ if (!defined $substs{$subst}) {
+ error(_g("%s: unable to expand symbol substitution '%s'"), $symbol, $subst);
+ }
+ } # If not defined, silently ignore.
+ }
+ }
+
+ # Expand substitutions
+ for my $subst (keys %substs) {
+ $symbol =~ s/\Q{$subst}\E/$substs{$subst}/g;
+ }
+
+ $self->{symbol} = $symbol;
+ $self->{substs} = \%substs;
+ return keys %substs;
+}
+
+sub get_cppname {
+ my $self = shift;
+ unless (exists $self->{cppname}) {
+ $self->{cppname} = ($self->get_symbolname() =~ /^_Z/) ?
+ cppfilt_demangle_cpp($self->get_symbolname()) :
+ undef;
+ }
+ return $self->{cppname};
+}
+
+sub detect_cpp_templinst() {
+ my $self = shift;
+
+ my $cppname = $self->get_cppname();
+ if (defined $cppname) {
+ # Prepare for tokenizing: wipe out unnecessary spaces
+ $cppname =~ s/([,<>()])\s+/$1/g;
+ $cppname =~ s/\s+([,<>()])/$1/g;
+ $cppname =~ s/\s*((?:(?:un)?signed|volatile|restrict|const|long)[*&]*)\s*/$1/g;
+ if (my @tokens = split(/\s+/, $cppname)) {
+ my $func;
+ if ($tokens[0] =~ /[(]/) {
+ $func = $tokens[0];
+ } elsif ($#tokens >= 1 && $tokens[1] =~ /[(]/) {
+ # The first token was return type, try the second
+ $func = $tokens[1];
+ }
+ if (defined $func && $func =~ /<[^>]+>[^(]*[(]/) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+# Typically template instantiations are not useful public symbols
+sub mark_cpp_templinst_as_optional {
+ my ($self, @tag) = @_;
+ @tag = ("optional", "templinst") unless @tag;
+ if (!$self->is_optional() && $self->detect_cpp_templinst()) {
+ $self->add_tag(@tag);
+ }
+}
+
+# Converts symbol template to c++ alias converting substitutions as well.
+# Returns converted template string or undef in case of failure.
+sub convert_templ_to_cpp_alias {
+ my ($self, $templ) = @_;
+ $templ = $self->get_symboltempl() unless defined $templ;
+ my $result;
+
+ return undef unless $templ =~ /^_Z/;
+
+ if (! $self->has_tag('subst')) {
+ $result = cppfilt_demangle_cpp($templ);
+ } else {
+ my (%mangled, @possible_substs);
+
+ # Collect possible symbol variants by expanding on all valid arches
+ foreach my $arch (get_valid_arches()) {
+ $self->{symbol} = $templ;
+ @possible_substs = $self->expand_substitutions(arch => $arch);
+ push @{$mangled{$self->get_symbolname()}}, $arch;
+ }
+
+ # Prepare for checking of demangled symbols
+ my (@demangled, @arches);
+ foreach my $mangled (keys %mangled) {
+ my $d = cppfilt_demangle_cpp($mangled);
+ # Fail immediatelly if couldn't demangle a variant
+ return undef unless defined $d;
+
+ # Tokenize
+ push @demangled, [ split(/\b/, $d) ];
+ push @arches, $mangled{$mangled};
+ }
+
+ # Create a subst expansion result map for $main_arch
+ my $main_arch = $arches[0][0];
+ my %cppmap;
+ foreach my $subst (@possible_substs) {
+ my $name = "c++:$subst";
+ # Can't handle a subst which does not have a c++ replacement
+ if (exists $SUBSTS{$name}) {
+ my $cppsubst = $SUBSTS{$name};
+ push @{$cppmap{$cppsubst->expand($main_arch)}}, $cppsubst;
+ } else {
+ return undef;
+ }
+ }
+
+ # Now do detection
+ my @result;
+ my @expanded_size = map { 0 } @demangled;
+ while (@{$demangled[0]} > 0) {
+ # Check if the token is not the same in all symbols (i.e. no subst here)
+ my $token = shift @{$demangled[0]};
+ my $ok = 1;
+ for (my $i = 1; $i < @demangled; $i++) {
+ if ($token ne $demangled[$i][0]) {
+ $ok = 0;
+ last;
+ }
+ }
+ if ($ok) {
+ # Tokens match. Get next token and push to @result
+ for (my $i = 1; $i < @demangled; $i++) {
+ shift @{$demangled[$i]};
+ }
+ push @result, $token;
+ } else {
+ # Tokens do not match. We need to guess a subst
+ my $found_subst;
+
+ # Determine a set of candidate substs by expansion result
+ # for $main_arch
+ while (!exists $cppmap{$token}) {
+ my $next = shift @{$demangled[0]};
+ if (!defined $next) {
+ return undef;
+ }
+ # Add up next token to this one and check again
+ $token .= $next;
+ }
+
+ # If we are here, a set of candidate substs has been found
+ my $cand_substs = $cppmap{$token};
+
+ # Now we need to pick a right candidate from candidates
+ next_candidate: for my $cand_subst (@$cand_substs) {
+ # Expansion must be the same on index 0 arches
+ foreach my $arch (@{$arches[0]}) {
+ if ($cand_subst->expand($arch) ne $token) {
+ next next_candidate;
+ }
+ }
+
+ # On 1+ arches, $subst->expand() and our $demangled value must match
+ for (my $i = 1; $i < @arches; $i++) {
+ my $archset = $arches[$i];
+ my $expanded = $cand_subst->expand($archset->[0]);
+
+ # Check if expansion is the same on all arches in the current set
+ foreach my $arch (@$archset) {
+ if ($expanded ne $cand_subst->expand($arch)) {
+ next next_candidate;
+ }
+ }
+
+ # Now actually check if $expanded matches what was demangled
+ $expanded_size[$i] = scalar(my @s = split(/\b/, $expanded));
+ if (join("", @{$demangled[$i]}[0..($expanded_size[$i]-1)]) ne $expanded) {
+ next next_candidate;
+ }
+ }
+
+ # If we are here, candidate has been confirmed
+ $found_subst = $cand_subst;
+ last;
+ }
+
+ if (defined $found_subst) {
+ for (my $i = 1; $i < @demangled; $i++) {
+ splice @{$demangled[$i]}, 0, ($expanded_size[$i]);
+ }
+ push @result, '{' . $found_subst->get_name() . '}';
+ } else {
+ # Unable to find an appropriate subst
+ return undef;
+ }
+ }
+ }
+
+ foreach my $demangled (@demangled) {
+ # Fail if demangling was not complete
+ return undef if @$demangled > 0;
+ }
+
+ $result = join("", @result);
+ }
+ return $result;
+}
+
+sub upgrade_virtual_table_symbol {
+ my ($self, $arch) = @_;
+ if ($self->get_symboltempl() =~ /^_ZT[Chv]/) {
+ my $newtempl = $self->convert_templ_to_cpp_alias();
+ if (defined $newtempl) {
+ $self->set_symbolname($newtempl, $newtempl);
+ $self->add_tag("c++");
+ # Finally, reinitialize
+ $self->initialize(arch => $arch);
+ }
+ return $newtempl;
+ }
+ return undef;
+}
+
+sub set_min_version {
+ my ($self, $version, %opts) = @_;
+
+ $self->{minver} = $version
+ if ($opts{with_deprecated} || !$self->{deprecated});
+}
+
+sub normalize_min_version {
+ my ($self, %opts) = @_;
+
+ if ($opts{with_deprecated} || !$self->{deprecated}) {
+ my $minver = $self->{minver};
+ if ($minver =~ m/-.*[^~]$/) {
+ unless($minver =~ s/-[01](?:$|[^\d-][^-]*$)//) {
+ $minver =~ s/([^~])$/$1~/;
+ }
+ $self->{minver} = $minver;
+ }
+ }
+}
+
+sub handle_min_version {
+ my ($self, $version, %opts) = @_;
+
+ if (defined $version) {
+ if ($version) {
+ return $self->set_min_version($version, %opts);
+ } else {
+ return $self->normalize_min_version(%opts);
+ }
+ }
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/SymbolFile.pm b/perllib/Debian/PkgKde/SymbolsHelper/SymbolFile.pm
new file mode 100644
index 0000000..97037a9
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/SymbolFile.pm
@@ -0,0 +1,320 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::SymbolFile;
+
+use strict;
+use warnings;
+use base 'Dpkg::Shlibs::SymbolFile';
+
+use File::Temp qw();
+use File::Copy qw();
+use Storable qw();
+use IO::Handle;
+use Dpkg::ErrorHandling;
+use Dpkg::Version;
+use Debian::PkgKde::SymbolsHelper::Symbol;
+use Debian::PkgKde::SymbolsHelper::Substs;
+
+# Use Debian::PkgKde::SymbolsHelper::Symbol as base symbol
+sub parse {
+ my ($self, $fh, $file, $seen, $obj_ref, $base_symbol) = @_;
+ unless (defined $base_symbol) {
+ $base_symbol = 'Debian::PkgKde::SymbolsHelper::Symbol';
+ }
+ if (!defined $seen) {
+ # Read 'SymbolsHelper-Confirmed' header
+ open(my $fh, "<", $file)
+ or error("unable to open symbol file '$file' for reading");
+ my $line = <$fh>;
+ close $fh;
+
+ chop $line;
+ if ($line =~ /^#\s*SymbolsHelper-Confirmed:\s+(.+)$/) {
+ $self->set_confirmed(split(/\s+/, $1));
+ }
+ }
+ return $self->SUPER::parse($fh, $file, $seen, $obj_ref, $base_symbol);
+}
+
+sub set_confirmed {
+ my ($self, $version, @arches) = @_;
+ $self->{h_confirmed_version} = $version;
+ $self->{h_confirmed_arches} = (@arches) ? \@arches : undef;
+}
+
+sub get_confirmed_version {
+ my $self = shift;
+ return $self->{h_confirmed_version};
+}
+
+sub get_confirmed_arches {
+ my $self = shift;
+ return (defined $self->{h_confirmed_arches}) ?
+ @{$self->{h_confirmed_arches}} : ();
+}
+
+sub create_symbol {
+ my ($self, $spec, %opts) = @_;
+ $opts{base} = Debian::PkgKde::SymbolsHelper::Symbol->new()
+ unless exists $opts{base};
+ return $self->SUPER::create_symbol($spec, %opts);
+}
+
+sub fork_symbol {
+ my ($self, $sym, $arch) = @_;
+ $arch = $self->get_arch() unless $arch;
+ my $nsym = $sym->clone(symbol => $sym->get_symboltempl());
+ $nsym->initialize(arch => $arch);
+ return $nsym;
+}
+
+sub output {
+ my ($self, $fh, %opts) = @_;
+ $opts{with_confirmed} = 1 unless exists $opts{with_confirmed};
+ # Write SymbolsHelper-Confirmed header
+ if ($opts{with_confirmed}) {
+ my @carches = $self->get_confirmed_arches();
+ if (@carches) {
+ print $fh '# SymbolsHelper-Confirmed: ', $self->get_confirmed_version(),
+ " ", join(" ", sort @carches), "\n" if defined $fh;
+ }
+ }
+ return $self->SUPER::output($fh, %opts);
+}
+
+sub _resync_symbol_cache {
+ my ($self, $soname, $cache) = @_;
+ my %rename;
+
+ foreach my $symkey (keys %$cache) {
+ my $sym = $cache->{$symkey};
+ if ($sym->get_symbolname() ne $symkey) {
+ $rename{$sym->get_symbolname()} = $sym;
+ delete $cache->{$symkey};
+ }
+ }
+ foreach my $newname (keys %rename) {
+ my $e = $self->get_symbol_object($rename{$newname}, $soname);
+ if ($e && ! $rename{$newname}->equals($e)) {
+ warning("caution: newly generated symbol '%s' will replace not exactly equal '%s'. Please readd if unappropriate",
+ $rename{$newname}->get_symbolspec(1),
+ $e->get_symbolspec(1));
+ }
+ $self->add_symbol($rename{$newname}, $soname);
+ }
+}
+
+sub resync_soname_symbol_caches {
+ my ($self, $soname) = @_;
+ my $obj = $self->get_object($soname);
+
+ # We need this to avoid removal of symbols which names clash when renaming
+ $self->_resync_symbol_cache($obj, $obj->{syms});
+
+ # Resync aliases too
+ foreach my $alias (values %{$obj->{patterns}{aliases}}) {
+ $self->_resync_symbol_cache($obj, $alias);
+ }
+}
+
+sub resync_soname_with_h_name {
+ my ($self, $soname) = @_;
+ my $obj = $self->get_object($soname);
+
+ sub _resync_with_h_name {
+ my $cache = shift;
+ foreach my $symkey (keys %$cache) {
+ $cache->{$symkey}->resync_name_with_h_name();
+ }
+ }
+
+ # First resync h_name with symbol name and templ
+ _resync_with_h_name($obj->{syms});
+ foreach my $alias (values %{$obj->{patterns}{aliases}}) {
+ _resync_with_h_name($alias);
+ }
+ return $self->resync_soname_symbol_caches($soname);
+}
+
+# Detects (or just neutralizes) substitutes which can be guessed from the
+# symbol name alone. Currently unused.
+#sub detect_standalone_substs {
+# my ($self, $detect) = @_;
+#
+# foreach my $sym ($self->get_symbols()) {
+# my $str = $sym->get_h_name();
+# foreach my $subst (@STANDALONE_SUBSTS) {
+# if ($detect) {
+# $subst->detect($str, $self->{arch});
+# } else {
+# $subst->neutralize($str);
+# }
+# }
+# }
+# foreach my $soname (keys %{$self->{objects}}) {
+# # Rename soname object with data in h_name
+# $self->resync_soname_with_h_name($soname);
+# }
+#}
+
+# Upgrade virtual table symbols. Needed for templating.
+sub prepare_for_templating {
+ my $self = shift;
+ my %sonames;
+
+ foreach my $soname ($self->get_sonames()) {
+ foreach my $sym ($self->get_symbols($soname)) {
+ if ($sym->upgrade_virtual_table_symbol($self->get_arch())) {
+ $sonames{$soname} = 1;
+ }
+ }
+ }
+
+ foreach my $soname (keys %sonames) {
+ $self->resync_soname_symbol_caches($soname);
+ }
+}
+
+sub patch_template {
+ my ($self, @patches) = @_;
+ my @symfiles;
+ my %dumped;
+
+ foreach my $patch (@patches) {
+ my $package = $patch->{package} || '';
+ my $tmpfile;
+ if (!exists $dumped{$package}) {
+ $tmpfile = File::Temp->new(
+ TEMPLATE => "${package}_orig.symbolsXXXXXX",
+ UNLINK => 0,
+ );
+ $self->output($tmpfile,
+ package => $package,
+ template_mode => 1,
+ with_confirmed => 0,
+ );
+ $tmpfile->close();
+ $dumped{$package} = $tmpfile->filename;
+ }
+ $tmpfile = File::Temp->new(
+ TEMPLATE => "${package}_patched.symbolsXXXXXX",
+ UNLINK => 1,
+ );
+ $tmpfile->close();
+ unless (File::Copy::copy($dumped{$package}, $tmpfile->filename)) {
+ syserror("unable to copy file '%s' to '%s'",
+ $dumped{$package}, $tmpfile->filename);
+ }
+ if ($patch->apply($tmpfile->filename)) {
+ # Patching was successful. Parse new SymbolFile and return it
+ my $symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(
+ file => $tmpfile->filename,
+ arch => $patch->{arch},
+ );
+ if ($patch->has_info()) {
+ $symfile->set_confirmed($patch->{version}, $patch->{arch});
+ } else {
+ $symfile->set_confirmed(undef);
+ }
+ push @symfiles, $symfile;
+ last unless wantarray;
+ }
+ }
+ foreach my $file (values %dumped) {
+ unless ($FILE::Temp::KEEP_ALL) {
+ unlink $file;
+ }
+ }
+ return (wantarray) ? @symfiles : $symfiles[0];
+}
+
+sub _dclone_exclude {
+ my ($target, @exclude) = @_;
+ my %saved;
+ foreach my $e (@exclude) {
+ if (exists $target->{$e}) {
+ $saved{$e} = $target->{$e};
+ delete $target->{$e};
+ }
+ }
+ my $clone = Storable::dclone($target);
+ $target->{$_} = $saved{$_} foreach @exclude;
+ return $clone;
+}
+
+# Forks an empty symbol file (without symbols and patterns) from the current
+# one. Other properties are retained.
+sub fork_empty {
+ my $self = shift;
+
+ my $symfile = _dclone_exclude($self, qw(objects));
+ $symfile->clear();
+ foreach my $soname ($self->get_sonames()) {
+ $symfile->create_object($soname);
+ my $obj = $symfile->get_object($soname);
+ my $cloned = _dclone_exclude($self->get_object($soname),
+ qw(syms patterns minver_cache));
+ $obj->{$_} = $cloned->{$_} foreach keys %$cloned;
+ }
+ return $symfile;
+}
+
+sub fork {
+ my ($self, @optinstances) = @_;
+ unshift @optinstances, {} unless @optinstances;
+ @optinstances = ( $optinstances[0] ) unless wantarray;
+
+ my @symfiles;
+ foreach my $opts (@optinstances) {
+ my $symfile = $self->fork_empty();
+ $symfile->{$_} = $opts->{$_} foreach keys %$opts;
+ $symfile->{file} = '';
+ push @symfiles, $symfile;
+ }
+
+ # Fork symbols
+ foreach my $soname ($self->get_sonames()) {
+ foreach my $sym ($self->get_symbols($soname),
+ $self->get_patterns($soname))
+ {
+ foreach my $symfile (@symfiles) {
+ my $nsym = $self->fork_symbol($sym, $symfile->get_arch());
+ $nsym->{h_origin_symbol} = $sym;
+ $symfile->add_symbol($nsym, $soname);
+ }
+ }
+ }
+ return (wantarray) ? @symfiles : shift @symfiles;
+}
+
+
+sub get_highest_version {
+ my $self = shift;
+ my $maxver;
+
+ foreach my $sym ($self->get_symbols(),
+ $self->get_patterns()) {
+ if (!$sym->{deprecated} &&
+ (!defined $maxver || version_compare($sym->{minver}, $maxver) > 0))
+ {
+ $maxver = $sym->{minver};
+ }
+ }
+
+ return $maxver;
+}
+
+1;
diff --git a/perllib/Debian/PkgKde/SymbolsHelper/SymbolFileCollection.pm b/perllib/Debian/PkgKde/SymbolsHelper/SymbolFileCollection.pm
new file mode 100644
index 0000000..372942e
--- /dev/null
+++ b/perllib/Debian/PkgKde/SymbolsHelper/SymbolFileCollection.pm
@@ -0,0 +1,835 @@
+# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+package Debian::PkgKde::SymbolsHelper::SymbolFileCollection;
+
+use strict;
+use warnings;
+
+use Dpkg::Arch qw(debarch_is);
+use Dpkg::ErrorHandling;
+use Dpkg::Version;
+use Debian::PkgKde::SymbolsHelper::Substs;
+use Debian::PkgKde::SymbolsHelper::String;
+use Debian::PkgKde::SymbolsHelper::SymbolFile;
+
+sub new {
+ my ($class, $orig_symfile) = @_;
+ unless ($orig_symfile->get_confirmed_version()) {
+ error("original symbol file template must have 'Confirmed' header set");
+ }
+ return bless { orig_symfile => $orig_symfile,
+ new_arches => {},
+ new_non_latest => [],
+ confirmed_arches => [],
+ symfiles => {},
+ versions => {},
+ latest => undef }, $class;
+}
+
+sub get_symfiles {
+ my $self = shift;
+ return values %{$self->{symfiles}};
+}
+
+sub get_symfile {
+ my ($self, $arch) = @_;
+ if (defined $arch) {
+ return $self->{symfiles}{$arch};
+ } else {
+ return $self->{orig_symfile};
+ }
+}
+
+# NOTE: latest may also include $orig fork()s if no symbol files with higher
+# confirmed version have been added.
+sub get_latest_version {
+ my $self = shift;
+ return $self->{latest};
+}
+
+sub get_latest_arches {
+ my $self = shift;
+ return @{$self->{versions}{$self->{latest}}};
+}
+
+sub get_new_arches {
+ my $self = shift;
+ return keys %{$self->{new_arches}};
+}
+
+# This will NEVER include $orig fork()s
+sub get_new_non_latest_arches {
+ my $self = shift;
+ return @{$self->{new_non_latest}};
+}
+
+sub is_arch_latest {
+ my ($self, $arch) = @_;
+ return $self->get_symfile($arch)->get_confirmed_version() eq $self->{latest};
+}
+
+sub is_arch_new {
+ my ($self, $arch) = @_;
+ return exists $self->{new_arches}{$arch};
+}
+
+sub add_symfiles {
+ my ($self, @symfiles) = @_;
+ my $latest = $self->get_latest_version();
+ foreach my $symfile (@symfiles) {
+ my $arch = $symfile->get_arch();
+ my $ver = $symfile->get_confirmed_version();
+ unless ($ver) {
+ internerr("problem with %s symbol file: it must have 'Confirmed' header",
+ $arch);
+ }
+ if ($self->get_symfile($arch)) {
+ error("you cannot add symbol file for the same arch (%s) more than once",
+ $arch);
+ }
+ $self->{symfiles}{$arch} = $symfile;
+ push @{$self->{versions}{$ver}}, $arch;
+ if (!defined $latest ||
+ version_compare($ver, $latest) > 0)
+ {
+ $latest = $ver;
+ }
+ }
+ $self->{latest} = $latest;
+}
+
+sub add_confirmed_arches {
+ my ($self, @arches) = @_;
+ foreach my $arch (@arches) {
+ if ($self->get_symfile($arch)) {
+ error("new symbol file has already been added for arch (%s)", $arch);
+ }
+ }
+ push @{$self->{versions}{$self->get_symfile()->get_confirmed_version()}},
+ @arches;
+ push @{$self->{confirmed_arches}}, @arches;
+ $self->{latest} = $self->get_symfile()->get_confirmed_version()
+ unless defined $self->{latest};
+}
+
+sub get_confirmed_arches {
+ my ($self) = @_;
+ return @{$self->{confirmed_arches}};
+}
+
+sub add_new_symfiles {
+ my ($self, @symfiles) = @_;
+ $self->{new_arches} = { %{$self->{new_arches}},
+ map({ $_->{arch} => $_ } @symfiles) };
+ $self->add_symfiles(@symfiles);
+
+ # Recalc new_non_latest
+ my $ver = $self->get_latest_version();
+ my @new_non_latest;
+ foreach my $arch ($self->get_new_arches()) {
+ if (! $self->is_arch_latest($arch)) {
+ push @new_non_latest, $arch;
+ }
+ }
+ $self->{new_non_latest} = \@new_non_latest;
+}
+
+sub fork_orig_symfile {
+ my ($self, @arches) = @_;
+ my @symfiles = $self->get_symfile()->fork(
+ map +{ arch => $_ }, @arches
+ );
+ return map { $_->{arch} => $_ } @symfiles;
+}
+
+sub calc_group_name {
+ my ($self, $name, $arch, @substs) = @_;
+
+ my $str = Debian::PkgKde::SymbolsHelper::String->new($name);
+ foreach my $subst (@substs) {
+ $subst->prep($str, $arch);
+ $subst->neutralize($str, $arch);
+ }
+ return $str->get_string();
+}
+
+sub get_symbols_regrouped_by_name {
+ my ($self, $group) = @_;
+ my $byname = $group->regroup_by_name();
+ my @byname;
+ foreach my $grp (sort values %$byname) {
+ if (my $sym = $grp->calc_properties($self)) {
+ push @byname, $sym;
+ }
+ }
+ return sort { $a->get_symboltempl() cmp $b->get_symboltempl() } @byname;
+}
+
+sub select_group {
+ my ($self, $sym, $soname, $arch, $gsubsts, $gother) = @_;
+
+ # Substitution detection is only supported for regular symbols and c++
+ # aliases.
+ if (! $sym->is_pattern() || $sym->get_alias_type() eq "c++") {
+ my $substs = ($sym->has_tag("c++")) ? \@CPP_TYPE_SUBSTS : \@TYPE_SUBSTS;
+ my $groupname = $self->calc_group_name($sym->get_symbolname(), $arch, @$substs);
+
+ unless (exists $gsubsts->{$soname}{$groupname}) {
+ $gsubsts->{$soname}{$groupname} =
+ Debian::PkgKde::SymbolsHelper::SymbolFileCollection::Group->new($substs);
+ }
+ return $gsubsts->{$soname}{$groupname};
+ } else {
+ # Symbol of some other kind. Then just group by name
+ my $name = $sym->get_symbolname();
+ unless (exists $gother->{$soname}{$name}) {
+ $gother->{$soname}{$name} =
+ Debian::PkgKde::SymbolsHelper::SymbolFileCollection::Group->new();
+ }
+ return $gother->{$soname}{$name};
+ }
+}
+
+# Create a new template from the collection of symbol files
+sub create_template {
+ my ($self, %opts) = @_;
+
+ my $orig = $self->get_symfile();
+ my $orig_arch = $orig->get_arch();
+ my $template = $orig->fork_empty();
+
+ # Prepare original template and other arch specific symbol files (virtual
+ # table stuff etc.).
+ $orig->prepare_for_templating();
+ foreach my $symfile ($self->get_symfiles()) {
+ $symfile->prepare_for_templating();
+ }
+
+ # Group new symbols by fully arch-neutralized name or, if unsupported,
+ # simply by name.
+ my (%gsubsts, %gother);
+ my %osymfiles = $self->fork_orig_symfile($self->get_new_arches());
+
+ foreach my $arch ($self->get_new_arches()) {
+ my $nsymfile = $self->get_symfile($arch);
+ my $osymfile = $osymfiles{$arch};
+
+ my @new = $nsymfile->get_new_symbols($osymfile, with_optional => 1);
+ foreach my $n (@new) {
+ my $soname = $n->{soname};
+ my $nsym = $n->{symbol};
+ # Get a reference in the orig symfile if any
+ my $osym = $osymfile->get_symbol_object($nsym, $soname);
+
+ my $group = $self->select_group($nsym, $soname, $arch, \%gsubsts, \%gother);
+
+ # Add symbol to the group
+ $group->add_symbol($nsym, $arch);
+ $group->prep_substs($arch);
+
+ if (defined $osym) {
+ my $origin = $osym->{h_origin_symbol};
+ $group->add_symbol($origin);
+ # "Touch" the origin symbol
+ $origin->{h_touched} = 1;
+ }
+ }
+
+ my @lost = $nsymfile->get_lost_symbols($osymfile, with_optional => 1);
+ foreach my $l (@lost) {
+ my $soname = $l->{soname};
+ my $sym = $l->{symbol};
+ my $origin = $sym->{h_origin_symbol};
+ my $group = $self->select_group($sym, $soname, $arch, \%gsubsts, \%gother);
+
+ $group->add_lost_symbol($sym, $arch);
+ $group->add_symbol($origin);
+ # "Touch" the origin symbol
+ $origin->{h_touched} = 1;
+ }
+ }
+
+ # Fork confirmed symbols where it matters
+ if (my @carches = $self->get_confirmed_arches()) {
+ # Important for substs detection
+ foreach my $soname (values %gsubsts) {
+ foreach my $group (values %$soname) {
+ if ($group->get_arches() && (my $osym = $group->get_symbol())) {
+ foreach my $arch (@carches) {
+ if ($osym->arch_is_concerned($arch)) {
+ my $nsym = $orig->fork_symbol($osym, $arch);
+ $group->add_symbol($nsym, $arch);
+ $group->prep_substs($arch);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ # Readd all untouched symbols in $orig back to the $template
+ foreach my $soname ($orig->get_sonames()) {
+ foreach my $sym ($orig->get_symbols($soname),
+ $orig->get_patterns($soname))
+ {
+ if (!exists $sym->{h_touched}) {
+ $template->add_symbol($sym, $soname);
+ }
+ }
+ }
+
+ # Process substs groups (%gsubsts) first
+ foreach my $soname (keys %gsubsts) {
+ my $groups = $gsubsts{$soname};
+
+ foreach my $groupname (keys %$groups) {
+ my $group = $groups->{$groupname};
+
+# print "group: $groupname", "\n";
+
+ # Take care of ambiguous groups
+ if ($group->is_ambiguous()) {
+ if (my @byname = $self->get_symbols_regrouped_by_name($group)) {
+ $template->add_symbol($_, $soname) foreach @byname;
+ info("ambiguous symbols for subst detection (%s). Processed by name:\n" .
+ " %s", "$groupname/$soname",
+ join("\n ", map { $_->get_symbolspec(1) } @byname));
+ }
+ next;
+ }
+ # Calculate properties and detect substs.
+ if (my $sym = $group->calc_properties($self)) {
+ # Then detect substs (we need two or more arch specific symbols for that)
+ my $substs_ok = 0;
+ if (scalar($group->get_arches()) > 1 && ! $group->are_symbols_equal()) {
+ my $substs_arch = ($group->has_symbol($orig_arch)) ?
+ $orig_arch : ($group->get_arches())[0];
+ if ($group->detect_substs($substs_arch)) {
+ my $substs_sym = $group->get_symbol($substs_arch);
+ $sym->add_tag("subst");
+ $sym->reset_h_name($substs_sym->get_h_name());
+ # Properly handle the case when *some substs have been*
+ # detected but symbols in the group still differ. Since
+ # the symbols will be grouped, we need to add a subst
+ # tag to all of them and reset h_name of the orig
+ # symbol since it is not touched by substs detection.
+ unless ($substs_ok = $group->verify_substs()) {
+ foreach my $sym ($group->get_symbols()) {
+ $sym->add_tag("subst");
+ }
+ if ($orig_arch eq $substs_arch) {
+ $group->get_symbol()->add_tag("subst");
+ $group->get_symbol()->reset_h_name(
+ $substs_sym->get_h_name()
+ );
+ }
+ }
+ }
+ } else {
+ $substs_ok = 1;
+ }
+
+ if ($substs_ok) {
+ # Finally add to template
+ $template->add_symbol($sym, $soname);
+ } else {
+ # Substitutions do not verify. Regroup by name what remains
+ foreach my $sym ($group->get_symbols()) {
+ $sym->resync_name_with_h_name();
+ }
+ $group->get_symbol()->resync_name_with_h_name() if $group->get_symbol();
+ if (my @byname = $self->get_symbols_regrouped_by_name($group)) {
+ $template->add_symbol($_, $soname) foreach @byname;
+ info("possible incomplete subst detection (%s). Processed by name:\n" .
+ " %s", "$groupname/$soname",
+ join("\n ", map { $_->get_symbolspec(1) } @byname));
+ }
+ }
+ }
+ }
+ }
+
+ # Now process others groups (%gother). Just calculate properties (arch
+ # tags) and add to the template.
+ foreach my $soname (keys %gother) {
+ my $groups = $gother{$soname};
+ foreach my $groupname (keys %$groups) {
+ my $group = $groups->{$groupname};
+ if (my $sym = $group->calc_properties($self)) {
+ $template->add_symbol($sym, $soname);
+ }
+ }
+ }
+
+ # Finally, resync h_names
+ foreach my $soname ($template->get_sonames()) {
+ $template->resync_soname_with_h_name($soname);
+ }
+
+ return $template;
+}
+
+package Debian::PkgKde::SymbolsHelper::SymbolFileCollection::Group;
+
+sub new {
+ my ($class, $substs) = @_;
+ return bless {
+ arches => {},
+ lost => {},
+ orig => undef,
+ result => undef,
+ substs => $substs}, $class;
+}
+
+sub has_symbol {
+ my ($self, $arch) = @_;
+ return (defined $arch) ? exists $self->{arches}{$arch} : $self->{orig};
+}
+
+sub get_symbol {
+ my ($self, $arch) = @_;
+ return (defined $arch) ? $self->{arches}{$arch} : $self->{orig};
+}
+
+sub get_arches {
+ my $self = shift;
+ return keys %{$self->{arches}};
+}
+
+sub get_symbols {
+ my $self = shift;
+ return values %{$self->{arches}};
+}
+
+sub get_result {
+ my $self = shift;
+ return $self->{result};
+}
+
+# There might be a new version available (e.g. with corrected substs).
+sub is_lost {
+ my ($self, $arch) = @_;
+ return exists $self->{lost}{$arch} && ! $self->has_symbol($arch);
+}
+
+sub is_new {
+ my ($self, $arch) = @_;
+ if (my $osym = $self->get_symbol()) {
+ return ! $osym->is_legitimate($arch);
+ } else {
+ return 1;
+ }
+}
+
+sub init_result {
+ my ($self, $based_on_arch) = @_;
+ $self->{result} = $self->get_symbol($based_on_arch)->clone();
+ return $self->{result};
+}
+
+sub add_symbol {
+ my ($self, $sym, $arch, $lost) = @_;
+ my $status = ($lost) ? "lost" : "arches";
+
+ if (my $esym = ($lost) ? $self->{lost}{$arch} : $self->get_symbol($arch)) {
+ if ($esym != $sym) {
+ # Another symbol already exists in this group for $arch.
+ # Add to other syms
+ push @{$self->{ambiguous}{$status}{$arch || ''}}, $sym;
+ }
+ # Otherwise, don't do anything. This symbol has already been added.
+ return 0;
+ } else {
+ if (defined $arch) {
+ $self->{$status}{$arch} = $sym;
+ } else {
+ $self->{orig} = $sym;
+ }
+ return 1;
+ }
+}
+
+sub add_lost_symbol {
+ my ($self, $sym, $arch) = @_;
+ return $self->add_symbol($sym, $arch, 1);
+}
+
+sub dump {
+ my ($self, $fh) = @_;
+ $fh = \*STDERR unless defined $fh;
+ if (my $sym = $self->get_symbol()) {
+ print $fh "orig:", $sym->get_symbolspec(1), "\n";
+ }
+ foreach my $arch ($self->get_arches()) {
+ my $sym = $self->get_symbol($arch);
+ print $fh "arches{$arch}:", $sym->get_symbolspec(1), "\n";
+ }
+ foreach my $arch (keys %{$self->{lost}}) {
+ my $sym = $self->{lost}{$arch};
+ print $fh "lost{$arch}:", $sym->get_symbolspec(1), "\n";
+ }
+ if ($self->is_ambiguous()) {
+ foreach my $status (sort keys %{$self->{ambiguous}}) {
+ my $arches = $self->{ambiguous}{$status};
+ foreach my $arch (keys %$arches) {
+ foreach my $sym (@{$arches->{$arch}}) {
+ print $fh "ambiguous{$status}{$arch}:", $sym->get_symbolspec(1), "\n";
+ }
+ }
+ }
+ }
+}
+
+sub is_ambiguous {
+ my $self = shift;
+ return exists $self->{ambiguous};
+}
+
+# Regroup ambiguous symbols by symbol name
+sub regroup_by_name {
+ my $self = shift;
+ my %groups;
+
+ foreach my $arch (undef, $self->get_arches()) {
+ my $sym = $self->get_symbol($arch);
+ if (defined $sym) {
+ my $name = $sym->get_symbolname();
+ unless (exists $groups{$name}) {
+ $groups{$name} = ref($self)->new();
+ }
+ my $group = $groups{$name};
+ $group->add_symbol($sym, $arch, defined $arch &&
+ $self->is_lost($arch));
+ }
+ }
+ foreach my $arch (keys %{$self->{lost}}) {
+ my $sym = $self->{lost}{$arch};
+ my $name = $sym->get_symbolname();
+ unless (exists $groups{$name}) {
+ $groups{$name} = ref($self)->new();
+ }
+ my $group = $groups{$name};
+ $group->add_lost_symbol($sym, $arch);
+ }
+ if ($self->is_ambiguous()) {
+ foreach my $status (keys %{$self->{ambiguous}}) {
+ my $arches = $self->{ambiguous}{$status};
+ my $lost = ($status eq "lost");
+ foreach my $arch (keys %$arches) {
+ foreach my $sym (@{$arches->{$arch}}) {
+ $arch = undef if ! $arch;
+ my $name = $sym->get_symbolname();
+ unless (exists $groups{$name}) {
+ $groups{$name} = ref($self)->new();
+ }
+ my $group = $groups{$name};
+ $group->add_symbol($sym, $arch, $lost);
+ }
+ }
+ }
+ }
+
+ return \%groups;
+}
+
+sub are_symbols_equal {
+ my $self = shift;
+ my @arches = $self->get_arches();
+ my $name;
+
+ $name = ($self->get_symbol()) ?
+ $self->get_symbol() : $self->get_symbol(shift @arches);
+ $name = $name->get_symbolname();
+ foreach my $arch (@arches) {
+ if ($self->get_symbol($arch)->get_symbolname() ne $name) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+# Verify if all substs have been replaced (i.e. hint-neutralized)
+sub verify_substs {
+ my $self = shift;
+ my @arches = $self->get_arches();
+ my $str = $self->get_symbol(shift @arches)->get_h_name()->get_string();
+ foreach my $arch (@arches) {
+ if ($self->get_symbol($arch)->get_h_name()->get_string() ne $str) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+sub verify_result_arches {
+ my ($self, $add, $deprecate) = @_;
+ my $result = $self->get_result();
+ my $ok = 1;
+ foreach my $arch (keys %$add) {
+ unless ($result->arch_is_concerned($arch)) {
+ $ok = 0;
+ last;
+ }
+ }
+ if ($ok) {
+ foreach my $arch (keys %$deprecate) {
+ if ($result->arch_is_concerned($arch)) {
+ $ok = 0;
+ last;
+ }
+ }
+ }
+ return $ok;
+}
+
+# Gets symbol status on $arch:
+# -2 - if symbol got LOST;
+# -1 - if symbol is deprecated and has been such (status hasn't changed);
+# 0 - symbol is NOT present on $arch and original symbol is not available;
+# 1 - symbol is present and has been been such (status hasn't changed);
+# 2 - symbol is NEW.
+sub get_symbol_arch_status {
+ my ($self, $arch) = @_;
+ my $status;
+
+ # If $self->is_lost($arch) returns true, it means a symbol is really
+ # NOT (or no longer) present on that arch in comparision to original
+ # symbol file. If $self->has_symbol($arch) returns true, the symbol is
+ # KNOWN to be have BEEN present on that arch (and it still is if it is
+ # not deprecated). Otherwise, the symbol the symbols status has not changed
+ # so it is is either: 1) present on $arch if $osym is legitimate on $arch;
+ # 2) absent on $arch otherwise.
+ if ($self->is_lost($arch)) {
+ $status = -2;
+ } elsif ($self->has_symbol($arch)) {
+ if ($self->get_symbol($arch)->{deprecated}) {
+ $status = -1;
+ } else {
+ $status = ($self->is_new($arch)) ? 2 : 1;
+ }
+ } elsif ($self->has_symbol()) {
+ $status = ($self->get_symbol()->is_legitimate($arch)) ? 1 : -1;
+ } else {
+ $status = 0;
+ }
+ return $status;
+}
+
+sub is_arch_in_db {
+ my ($self, $arch, $db) = @_;
+ if ($arch =~ /any/) { # Might be a wildcard
+ foreach my $adb ((ref $db eq 'ARRAY') ? @$db : keys %$db) {
+ return 2 if debarch_is($adb, $arch);
+ }
+ } elsif (ref $db eq 'ARRAY') {
+ return 1 if grep { $arch eq $_ } @$db;
+ } else {
+ return exists $db->{$arch};
+ }
+ return 0;
+}
+
+# Calculate group properties and instantiates 'result'. At the moment, this
+# method will take care of arch tags and deprecated status. "Result" symbol is
+# returned if symbol is not useless in the group.
+sub calc_properties {
+ my ($self, $collection) = @_;
+
+ my @latest = $collection->get_latest_arches();
+ my @non_latest = $collection->get_new_non_latest_arches();
+ my $total_arches = scalar(@latest) + scalar(@non_latest);
+ my (%present, %absent);
+ my (@oarches, @narches, $arch_neg);
+ my $arch_added = 0;
+ my $osym = $self->get_symbol();
+ my $result;
+
+ if (defined $osym) {
+ # The symbol exists in the template. This might complicate things a lot.
+ if ($osym->has_tag("arch")) {
+ @oarches = split(/[\s,]+/, $osym->get_tag_value("arch"))
+ }
+ }
+
+ # Calculate status of @latest arches
+ foreach my $arch (@latest) {
+ my $status = $self->get_symbol_arch_status($arch);
+ if ($status > 0) {
+ $present{$arch} = $status;
+ } else {
+ $absent{$arch} = $status;
+ }
+ }
+
+ # Initialize $result
+ if (defined $osym) {
+ $result = $self->init_result(); # base result on original
+ } elsif (keys %present) {
+ $result = $self->init_result((keys %present)[0]);
+ } else {
+ return undef;
+ }
+
+ if (scalar(keys %absent) == scalar(@latest) &&
+ (grep { $absent{$_} == -2 } keys %absent))
+ {
+ if (!$osym->{deprecated} || $osym->is_optional()) {
+ $result->{deprecated} = $collection->get_latest_version();
+ }
+ } elsif (scalar(keys %present) == scalar(@latest) &&
+ (@oarches == 0 || @latest > 1))
+ {
+ # Do not remove arch tag if we based our findings only on a single
+ # arch.
+ $result->{deprecated} = 0;
+ if (@oarches > 0) {
+ $result->delete_tag("arch");
+ $arch_added += scalar(keys %present);
+ }
+ } else {
+ # We will need to add appropriate arch tag. But in addition,
+ # collect info from NEW non-latest arches (provided we had
+ # info about them from latest)
+ foreach my $arch (@non_latest) {
+ my $status = $self->get_symbol_arch_status($arch);
+ if ($status > 0) {
+ $present{$arch} = $status if keys(%present) > 0 &&
+ ! exists $absent{$arch};
+ } else {
+ $absent{$arch} = $status if keys(%absent) > 0 &&
+ ! exists $present{$arch};
+ }
+ }
+
+ if (keys %present || keys %absent) {
+ $result->{deprecated} = 0 if keys %present;
+ if (@oarches > 0) {
+ # We need to combine original and new data. Filter out hits
+ # (exact and wildcards) in @oarches first.
+ my $fail;
+ foreach my $arch (@oarches) {
+ my $not_arch = $1 if $arch =~ /^!+(.*)$/;
+ if (! defined $arch_neg) {
+ $arch_neg = ($not_arch) ? '!' : '';
+ } elsif ($arch_neg ne (($not_arch) ? '!' : '')) {
+ $fail = 1;
+ $osym->add_tag("helper-arch", "mixed-arch-tag-not-supported");
+ last;
+ }
+ if ($not_arch) {
+ if (! $self->is_arch_in_db($not_arch, \%present)) {
+ push @narches, $not_arch;
+ } else {
+ $arch_added++;
+ }
+ } else {
+ if (! $self->is_arch_in_db($arch, \%absent)) {
+ push @narches, $arch;
+ }
+ }
+ }
+
+ return $result if $fail;
+ }
+
+ if (@narches) {
+ # Now add new arches of the specified type
+ foreach my $arch (($arch_neg) ? (keys %absent) : (keys %present)) {
+ if (! $self->is_arch_in_db($arch, \@narches)) {
+ push @narches, $arch;
+ $arch_added++ if ! $arch_neg;
+ }
+ }
+
+ # Finally set arch tag
+ $result->add_tag("arch", join(" ", map { "${arch_neg}$_" } sort(@narches)));
+ } else { # Original symbol has no arch tags
+ if ($total_arches > 2 && keys(%present) == $total_arches - 1) {
+ # Use !missing_arch if only a single arch is missing
+ my $missarch;
+ if (keys(%absent) == 1) {
+ $missarch = (keys %absent)[0];
+ } else {
+ foreach my $arch (@latest, @non_latest) {
+ if (!exists $present{$arch}) {
+ $missarch = $arch;
+ last;
+ }
+ }
+ }
+ $result->add_tag("arch", "!$missarch");
+ } elsif ($total_arches > 2 && keys(%absent) == $total_arches - 1) {
+ # Use arch if only present on a single arch
+ my $okarch;
+ if (keys(%present) == 1) {
+ $okarch = (keys %present)[0];
+ } else {
+ foreach my $arch (@latest, @non_latest) {
+ if (!exists $absent{$arch}) {
+ $okarch = $arch;
+ last;
+ }
+ }
+ }
+ $result->add_tag("arch", $okarch);
+ } elsif (scalar(keys %present) <= scalar(keys %absent)) {
+ $result->add_tag("arch", join(" ", sort keys %present));
+ } else {
+ $result->add_tag("arch", join(" ", map { "!$_" } sort(keys %absent)));
+ }
+ }
+ }
+ }
+
+ # Bump symbol minver if new arches added
+ if (defined $result && keys(%present) && (!@oarches || $arch_added) &&
+ ! $result->is_optional())
+ {
+ $result->{minver} = $collection->get_latest_version();
+ }
+
+ return $result;
+}
+
+sub prep_substs {
+ my ($self, $arch) = @_;
+ my $sym = $self->get_symbol($arch);
+ my $h_name = $sym->get_h_name();
+ foreach my $subst (@{$self->{substs}}) {
+ $subst->prep($h_name, $arch);
+ }
+}
+
+sub detect_substs {
+ my ($self, $main_arch) = @_;
+
+ my %h_names = map { $_ => $self->get_symbol($_)->get_h_name() } $self->get_arches();
+ my $h_name = $h_names{$main_arch};
+
+ my $detected = 0;
+ foreach my $subst (@{$self->{substs}}) {
+ if ($subst->detect($h_name, $main_arch, \%h_names)) {
+ $detected++;
+ # Make other h_names arch independent with regard to this handler.
+ foreach my $arch (keys %h_names) {
+ $subst->hinted_neutralize($h_names{$arch}, $h_name);
+ }
+ }
+ }
+ return $detected;
+}
+
+1;