| # Copyright © 2007-2010 Raphaël Hertzog <hertzog@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 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, see <http://www.gnu.org/licenses/>. |
| |
| package Dpkg::Substvars; |
| |
| use strict; |
| use warnings; |
| |
| our $VERSION = "1.00"; |
| |
| use Dpkg qw($version); |
| use Dpkg::Arch qw(get_host_arch); |
| use Dpkg::ErrorHandling; |
| use Dpkg::Gettext; |
| |
| use POSIX qw(:errno_h); |
| |
| use base qw(Dpkg::Interface::Storable); |
| |
| my $maxsubsts = 50; |
| |
| =encoding utf8 |
| |
| =head1 NAME |
| |
| Dpkg::Substvars - handle variable substitution in strings |
| |
| =head1 DESCRIPTION |
| |
| It provides some an object which is able to substitute variables in |
| strings. |
| |
| =head1 METHODS |
| |
| =over 8 |
| |
| =item my $s = Dpkg::Substvars->new($file) |
| |
| Create a new object that can do substitutions. By default it contains |
| generic substitutions like ${Newline}, ${Space}, ${Tab}, ${dpkg:Version} |
| and ${dpkg:Upstream-Version}. |
| |
| Additional substitutions will be read from the $file passed as parameter. |
| |
| It keeps track of which substitutions were actually used (only counting |
| substvars(), not get()), and warns about unused substvars when asked to. The |
| substitutions that are always present are not included in these warnings. |
| |
| =cut |
| |
| sub new { |
| my ($this, $arg) = @_; |
| my $class = ref($this) || $this; |
| my $self = { |
| vars => { |
| "Newline" => "\n", |
| "Space" => " ", |
| "Tab" => "\t", |
| "dpkg:Version" => $version, |
| "dpkg:Upstream-Version" => $version, |
| }, |
| used => {}, |
| msg_prefix => "", |
| }; |
| $self->{'vars'}{'dpkg:Upstream-Version'} =~ s/-[^-]+$//; |
| bless $self, $class; |
| $self->no_warn($_) foreach keys %{$self->{'vars'}}; |
| if ($arg) { |
| $self->load($arg) if -e $arg; |
| } |
| return $self; |
| } |
| |
| =item $s->set($key, $value) |
| |
| Add/replace a substitution. |
| |
| =cut |
| |
| sub set { |
| my ($self, $key, $value) = @_; |
| $self->{'vars'}{$key} = $value; |
| } |
| |
| =item $s->get($key) |
| |
| Get the value of a given substitution. |
| |
| =cut |
| |
| sub get { |
| my ($self, $key) = @_; |
| return $self->{'vars'}{$key}; |
| } |
| |
| =item $s->delete($key) |
| |
| Remove a given substitution. |
| |
| =cut |
| |
| sub delete { |
| my ($self, $key) = @_; |
| delete $self->{'used'}{$key}; |
| return delete $self->{'vars'}{$key}; |
| } |
| |
| =item $s->no_warn($key) |
| |
| Prevents warnings about a unused substitution, for example if it is provided by |
| default. |
| |
| =cut |
| |
| sub no_warn { |
| my ($self, $key) = @_; |
| $self->{'used'}{$key}++; |
| } |
| |
| =item $s->load($file) |
| |
| Add new substitutions read from $file. |
| |
| =item $s->parse($fh, $desc) |
| |
| Add new substitutions read from the filehandle. $desc is used to identify |
| the filehandle in error messages. |
| |
| =cut |
| |
| sub parse { |
| my ($self, $fh, $varlistfile) = @_; |
| binmode($fh); |
| while (<$fh>) { |
| next if m/^\s*\#/ || !m/\S/; |
| s/\s*\n$//; |
| m/^(\w[-:0-9A-Za-z]*)\=(.*)$/ || |
| error(_g("bad line in substvars file %s at line %d"), |
| $varlistfile, $.); |
| $self->{'vars'}{$1} = $2; |
| } |
| } |
| |
| =item $s->set_version_substvars($version) |
| |
| Defines ${binary:Version}, ${source:Version} and |
| ${source:Upstream-Version} based on the given version string. |
| |
| These will never be warned about when unused. |
| |
| =cut |
| |
| sub set_version_substvars { |
| my ($self, $version) = @_; |
| |
| $self->{'vars'}{'binary:Version'} = $version; |
| $self->{'vars'}{'source:Version'} = $version; |
| $self->{'vars'}{'source:Version'} =~ s/\+b[0-9]+$//; |
| $self->{'vars'}{'source:Upstream-Version'} = $version; |
| $self->{'vars'}{'source:Upstream-Version'} =~ s/-[^-]*$//; |
| |
| # XXX: Source-Version is now deprecated, remove in the future. |
| $self->{'vars'}{'Source-Version'} = $version; |
| |
| $self->no_warn($_) foreach qw/binary:Version source:Version source:Upstream-Version Source-Version/; |
| } |
| |
| =item $s->set_arch_substvars() |
| |
| Defines architecture variables: ${Arch}. |
| |
| This will never be warned about when unused. |
| |
| =cut |
| |
| sub set_arch_substvars { |
| my ($self) = @_; |
| |
| $self->{'vars'}{'Arch'} = get_host_arch(); |
| $self->no_warn('Arch'); |
| } |
| |
| =item $newstring = $s->substvars($string) |
| |
| Substitutes variables in $string and return the result in $newstring. |
| |
| =cut |
| |
| sub substvars { |
| my ($self, $v, %opts) = @_; |
| my $lhs; |
| my $vn; |
| my $rhs = ''; |
| my $count = 0; |
| $opts{msg_prefix} = $self->{msg_prefix} unless exists $opts{msg_prefix}; |
| $opts{no_warn} = 0 unless exists $opts{no_warn}; |
| |
| while ($v =~ m/^(.*?)\$\{([-:0-9a-z]+)\}(.*)$/si) { |
| # If we have consumed more from the leftover data, then |
| # reset the recursive counter. |
| $count = 0 if (length($3) < length($rhs)); |
| |
| $count < $maxsubsts || |
| error($opts{msg_prefix} . |
| _g("too many substitutions - recursive ? - in \`%s'"), $v); |
| $lhs = $1; $vn = $2; $rhs = $3; |
| if (defined($self->{'vars'}{$vn})) { |
| $v = $lhs . $self->{'vars'}{$vn} . $rhs; |
| $self->no_warn($vn); |
| $count++; |
| } else { |
| warning($opts{msg_prefix} . _g("unknown substitution variable \${%s}"), |
| $vn) unless $opts{no_warn}; |
| $v = $lhs . $rhs; |
| } |
| } |
| return $v; |
| } |
| |
| =item $s->warn_about_unused() |
| |
| Issues warning about any variables that were set, but not used |
| |
| =cut |
| |
| sub warn_about_unused { |
| my ($self, %opts) = @_; |
| $opts{msg_prefix} = $self->{msg_prefix} unless exists $opts{msg_prefix}; |
| |
| foreach my $vn (keys %{$self->{'vars'}}) { |
| next if $self->{'used'}{$vn}; |
| # Empty substitutions variables are ignored on the basis |
| # that they are not required in the current situation |
| # (example: debhelper's misc:Depends in many cases) |
| next if $self->{'vars'}{$vn} eq ""; |
| warning($opts{msg_prefix} . _g("unused substitution variable \${%s}"), $vn); |
| } |
| } |
| |
| =item $s->set_msg_prefix($prefix) |
| |
| Define a prefix displayed before all warnings/error messages output |
| by the module. |
| |
| =cut |
| |
| sub set_msg_prefix { |
| my ($self, $prefix) = @_; |
| $self->{msg_prefix} = $prefix; |
| } |
| |
| =item $s->save($file) |
| |
| Store all substitutions variables except the automatic ones in the |
| indicated file. |
| |
| =item "$s" |
| |
| Return a string representation of all substitutions variables except the |
| automatic ones. |
| |
| =item $str = $s->output($fh) |
| |
| Print all substitutions variables except the automatic ones in the |
| filehandle and return the content written. |
| |
| =cut |
| |
| sub output { |
| my ($self, $fh) = @_; |
| my $str = ""; |
| # Store all non-automatic substitutions only |
| foreach my $vn (sort keys %{$self->{'vars'}}) { |
| next if /^(?:(?:dpkg|source|binary):(?:Source-)?Version|Space|Tab|Newline|Arch|Source-Version|F:.+)$/; |
| my $line = "$vn=" . $self->{vars}{$vn} . "\n"; |
| print $fh $line if defined $fh; |
| $str .= $line; |
| } |
| return $str; |
| } |
| |
| =back |
| |
| =head1 AUTHOR |
| |
| Raphaël Hertzog <hertzog@debian.org>. |
| |
| =cut |
| |
| 1; |