1 # Hey emacs! This is a -*- Perl -*- script!
2 # Util -- Perl utility functions for lintian
4 # Copyright (C) 1998 Christian Schwarz
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, you can find it on the World Wide
18 # Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
19 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
26 our @ISA = qw(Exporter);
27 our @EXPORT = qw(parse_dpkg_control
33 file_is_encoded_in_non_utf8
42 # general function to read dpkg control files
43 # this function can parse output of `dpkg-deb -f', .dsc,
44 # and .changes files (and probably all similar formats)
47 # $debconf_flag (true if the file is a debconf template file)
50 # (a hash contains one sections,
51 # keys in hash are lower case letters of control fields)
52 sub parse_dpkg_control {
53 my ($CONTROL, $debconf_flag) = @_;
63 # tabs at the beginning are illegal, but handle them anyways
65 next if /^#/; #comment line?
68 if ((!$debconf_flag && m/^\s*$/) or
69 ($debconf_flag && m/^$/)) {
70 if ($open_section) { # end of current section
76 elsif (m/^-----BEGIN PGP SIGNATURE/) { # skip until end of signature
78 last if m/^-----END PGP SIGNATURE/o;
82 elsif (m/^-----BEGIN PGP/) { # skip until the next blank line
88 elsif (m/^(\S+):\s*$/o) {
92 $data[$cur_section]->{$tag} = '';
97 elsif (m/^(\S+):\s*(.*)$/o) {
100 my ($tag,$value) = (lc $1,$2);
101 $data[$cur_section]->{$tag} = $value;
106 elsif (m/^ (.*)$/o) {
107 $open_section or fail("syntax error in section $cur_section after the tag $last_tag: $_");
109 $data[$cur_section]->{$last_tag} .= "\n".$1;
116 sub read_dpkg_control {
117 my ($file, $debconf_flag) = @_;
119 if (not _ensure_file_is_sane($file)) {
123 my $CONTROL = FileHandle->new;
124 open($CONTROL, '<', $file)
125 or fail("cannot open control file $file for reading: $!");
126 my @data = parse_dpkg_control($CONTROL, $debconf_flag);
128 or fail("pipe for control file $file exited with status: $?");
135 if (not _ensure_file_is_sane($file)) {
139 # `dpkg-deb -f $file' is very slow. Instead, we use ar and tar.
140 my $CONTROL = FileHandle->new;
141 pipeline_open($CONTROL,
142 (sub { exec 'ar', 'p', $file, 'control.tar.gz' }),
143 (sub { exec 'tar', '--wildcards', '-xzO', '-f', '-', '*control' }))
144 or fail("cannot fork to unpack $file: $!\n");
145 my @data = parse_dpkg_control($CONTROL);
146 close($CONTROL) or fail("broken input pipe for unpacking $file: $!");
153 if (not _ensure_file_is_sane($file)) {
157 my @data = read_dpkg_control($file);
161 sub _ensure_file_is_sane {
164 # if file exists and is not 0 bytes
165 if (-f $file and -s $file) {
171 sub slurp_entire_file {
174 or fail("cannot open file $file for reading: $!");
183 sub get_file_checksum {
184 my ($alg, $file) = @_;
185 open (FILE, '<', $file) or fail("Couldn't open $file");
188 $digest = Digest::MD5->new;
189 } elsif ($alg =~ /sha(\d+)/) {
191 $digest = Digest::SHA->new($1);
193 $digest->addfile(*FILE);
194 close FILE or fail("Couldn't close $file");
195 return $digest->hexdigest;
198 sub file_is_encoded_in_non_utf8 {
199 my ($file, $type, $pkg) = @_;
202 open (ICONV, '-|', "env LANG=C iconv -f utf8 -t utf8 $file 2>&1")
203 or fail("failure while checking encoding of $file for $type package $pkg");
206 if (m/iconv: illegal input sequence at position \d+$/) {
214 return $line if $non_utf8;
218 # Just like system, except cleanses the environment first to avoid any strange
219 # side effects due to the user's environment.
221 my @whitelist = qw(PATH INTLTOOL_EXTRACT);
222 my %newenv = map { exists $ENV{$_} ? ($_ => $ENV{$_}) : () } @whitelist;
224 if (not defined $pid) {
226 } elsif ($pid == 0) {
228 exec @_ or die("exec of $_[0] failed: $!\n");
235 # Translate permission strings like `-rwxrwxrwx' into an octal number.
241 $t =~ m/^.(.)(.)(.)(.)(.)(.)(.)(.)(.)/o;
243 $o += 00400 if $1 eq 'r'; # owner read
244 $o += 00200 if $2 eq 'w'; # owner write
245 $o += 00100 if $3 eq 'x'; # owner execute
246 $o += 04000 if $3 eq 'S'; # setuid
247 $o += 04100 if $3 eq 's'; # setuid + owner execute
248 $o += 00040 if $4 eq 'r'; # group read
249 $o += 00020 if $5 eq 'w'; # group write
250 $o += 00010 if $6 eq 'x'; # group execute
251 $o += 02000 if $6 eq 'S'; # setgid
252 $o += 02010 if $6 eq 's'; # setgid + group execute
253 $o += 00004 if $7 eq 'r'; # other read
254 $o += 00002 if $8 eq 'w'; # other write
255 $o += 00001 if $9 eq 'x'; # other execute
256 $o += 01000 if $9 eq 'T'; # stickybit
257 $o += 01001 if $9 eq 't'; # stickybit + other execute
262 # ------------------------
265 my $str = "internal error";
267 $str .= ": ".join( "\n", @_)."\n";
273 $! = 2; # set return code outside eval()