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,
27 # Force export as soon as possible, since some of the modules we load also
28 # depend on us and the sequencing can cause things not to be exported
33 @EXPORT = qw(parse_dpkg_control
39 file_is_encoded_in_non_utf8
50 use Maemian::Command qw(spawn);
51 use Maemian::Output qw(string);
54 # general function to read dpkg control files
55 # this function can parse output of `dpkg-deb -f', .dsc,
56 # and .changes files (and probably all similar formats)
59 # $debconf_flag (true if the file is a debconf template file)
62 # (a hash contains one sections,
63 # keys in hash are lower case letters of control fields)
64 sub parse_dpkg_control {
65 my ($CONTROL, $debconf_flag) = @_;
76 # FIXME: comment lines are only allowed in debian/control and should
77 # be an error for other control files.
81 if ((!$debconf_flag && m/^\s*$/) or ($debconf_flag && m/^$/)) {
82 if ($open_section) { # end of current section
88 elsif (m/^-----BEGIN PGP SIGNATURE/) { # skip until end of signature
90 last if m/^-----END PGP SIGNATURE/o;
94 elsif (m/^-----BEGIN PGP/) { # skip until the next blank line
100 elsif (m/^(\S+):\s*$/o) {
104 $data[$cur_section]->{$tag} = '';
109 elsif (m/^(\S+):\s*(.*)$/o) {
112 # Policy: Horizontal whitespace (spaces and tabs) may occur
113 # immediately before or after the value and is ignored there.
114 my ($tag,$value) = (lc $1,$2);
116 $data[$cur_section]->{$tag} = $value;
121 elsif (m/^([ \t].*)$/o) {
122 $open_section or fail("syntax error in section $cur_section after the tag $last_tag: $_");
124 # Policy: Many fields' values may span several lines; in this case
125 # each continuation line must start with a space or a tab. Any
126 # trailing spaces or tabs at the end of individual lines of a
127 # field value are ignored.
130 $data[$cur_section]->{$last_tag} .= "\n" . $value;
137 sub read_dpkg_control {
138 my ($file, $debconf_flag) = @_;
140 if (not _ensure_file_is_sane($file)) {
144 open(my $CONTROL, '<', $file)
145 or fail("cannot open control file $file for reading: $!");
146 my @data = parse_dpkg_control($CONTROL, $debconf_flag);
148 or fail("pipe for control file $file exited with status: $?");
155 if (not _ensure_file_is_sane($file)) {
159 # `dpkg-deb -f $file' is very slow. Instead, we use ar and tar.
160 my $opts = { pipe_out => FileHandle->new };
162 ['ar', 'p', $file, 'control.tar.gz'],
163 '|', ['tar', '--wildcards', '-xzO', '-f', '-', '*control'])
164 or fail("cannot fork to unpack $file: $opts->{exception}\n");
165 my @data = parse_dpkg_control($opts->{pipe_out});
166 $opts->{harness}->finish();
173 if (not _ensure_file_is_sane($file)) {
177 my @data = read_dpkg_control($file);
181 sub _ensure_file_is_sane {
184 # if file exists and is not 0 bytes
185 if (-f $file and -s $file) {
191 sub slurp_entire_file {
194 or fail("cannot open file $file for reading: $!");
201 sub get_file_checksum {
202 my ($alg, $file) = @_;
203 open (FILE, '<', $file) or fail("Couldn't open $file");
206 $digest = Digest::MD5->new;
207 } elsif ($alg =~ /sha(\d+)/) {
209 $digest = Digest::SHA->new($1);
211 $digest->addfile(*FILE);
212 close FILE or fail("Couldn't close $file");
213 return $digest->hexdigest;
216 sub file_is_encoded_in_non_utf8 {
217 my ($file, $type, $pkg) = @_;
220 open (ICONV, '-|', "env LANG=C iconv -f utf8 -t utf8 $file 2>&1")
221 or fail("failure while checking encoding of $file for $type package $pkg");
224 if (m/iconv: illegal input sequence at position \d+$/) {
232 return $line if $non_utf8;
236 # Just like system, except cleanses the environment first to avoid any strange
237 # side effects due to the user's environment.
239 my @whitelist = qw(PATH INTLTOOL_EXTRACT);
240 my %newenv = map { exists $ENV{$_} ? ($_ => $ENV{$_}) : () } @whitelist;
242 if (not defined $pid) {
244 } elsif ($pid == 0) {
246 exec @_ or die("exec of $_[0] failed: $!\n");
253 # Translate permission strings like `-rwxrwxrwx' into an octal number.
259 $t =~ m/^.(.)(.)(.)(.)(.)(.)(.)(.)(.)/o;
261 $o += 00400 if $1 eq 'r'; # owner read
262 $o += 00200 if $2 eq 'w'; # owner write
263 $o += 00100 if $3 eq 'x'; # owner execute
264 $o += 04000 if $3 eq 'S'; # setuid
265 $o += 04100 if $3 eq 's'; # setuid + owner execute
266 $o += 00040 if $4 eq 'r'; # group read
267 $o += 00020 if $5 eq 'w'; # group write
268 $o += 00010 if $6 eq 'x'; # group execute
269 $o += 02000 if $6 eq 'S'; # setgid
270 $o += 02010 if $6 eq 's'; # setgid + group execute
271 $o += 00004 if $7 eq 'r'; # other read
272 $o += 00002 if $8 eq 'w'; # other write
273 $o += 00001 if $9 eq 'x'; # other execute
274 $o += 01000 if $9 eq 'T'; # stickybit
275 $o += 01001 if $9 eq 't'; # stickybit + other execute
281 return spawn(undef, ['rm', '-rf', '--', @_]);
285 return spawn(undef, ['cp', '-a', '--', @_]);
290 spawn({out => $out, fail => 'error'},
291 ['gzip', '-dc', $in]);
294 # create an empty file
295 # --okay, okay, this is not exactly what `touch' does :-)
297 open(T, '>', $_[0]) or return 0;
298 close(T) or return 0;
306 $str = string('internal error', @_);
308 $str = string('internal error', "$!");
310 $str = string('internal error');
312 $! = 2; # set return code outside eval()
319 # indent-tabs-mode: t
320 # cperl-indent-level: 4
322 # vim: syntax=perl sw=4 ts=8