1 # Util -- Perl utility functions for maemian
3 # Copyright (C) 1998 Christian Schwarz
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, you can find it on the World Wide
17 # Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
18 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
26 # Force export as soon as possible, since some of the modules we load also
27 # depend on us and the sequencing can cause things not to be exported
32 @EXPORT = qw(parse_dpkg_control
38 file_is_encoded_in_non_utf8
49 use Maemian::Command qw(spawn);
50 use Maemian::Output qw(string);
53 # general function to read dpkg control files
54 # this function can parse output of `dpkg-deb -f', .dsc,
55 # and .changes files (and probably all similar formats)
58 # $debconf_flag (true if the file is a debconf template file)
61 # (a hash contains one sections,
62 # keys in hash are lower case letters of control fields)
63 sub parse_dpkg_control {
64 my ($CONTROL, $debconf_flag) = @_;
75 # FIXME: comment lines are only allowed in debian/control and should
76 # be an error for other control files.
80 if ((!$debconf_flag && m/^\s*$/) or ($debconf_flag && m/^$/)) {
81 if ($open_section) { # end of current section
87 elsif (m/^-----BEGIN PGP SIGNATURE/) { # skip until end of signature
89 last if m/^-----END PGP SIGNATURE/o;
93 elsif (m/^-----BEGIN PGP/) { # skip until the next blank line
99 elsif (m/^(\S+):\s*$/o) {
103 $data[$cur_section]->{$tag} = '';
108 elsif (m/^(\S+):\s*(.*)$/o) {
111 # Policy: Horizontal whitespace (spaces and tabs) may occur
112 # immediately before or after the value and is ignored there.
113 my ($tag,$value) = (lc $1,$2);
115 $data[$cur_section]->{$tag} = $value;
120 elsif (m/^([ \t].*)$/o) {
121 $open_section or fail("syntax error in section $cur_section after the tag $last_tag: $_");
123 # Policy: Many fields' values may span several lines; in this case
124 # each continuation line must start with a space or a tab. Any
125 # trailing spaces or tabs at the end of individual lines of a
126 # field value are ignored.
129 $data[$cur_section]->{$last_tag} .= "\n" . $value;
136 sub read_dpkg_control {
137 my ($file, $debconf_flag) = @_;
139 if (not _ensure_file_is_sane($file)) {
143 open(my $CONTROL, '<', $file)
144 or fail("cannot open control file $file for reading: $!");
145 my @data = parse_dpkg_control($CONTROL, $debconf_flag);
147 or fail("pipe for control file $file exited with status: $?");
154 if (not _ensure_file_is_sane($file)) {
158 # `dpkg-deb -f $file' is very slow. Instead, we use ar and tar.
159 my $opts = { pipe_out => FileHandle->new };
161 ['ar', 'p', $file, 'control.tar.gz'],
162 '|', ['tar', '--wildcards', '-xzO', '-f', '-', '*control'])
163 or fail("cannot fork to unpack $file: $opts->{exception}\n");
164 my @data = parse_dpkg_control($opts->{pipe_out});
165 $opts->{harness}->finish();
172 if (not _ensure_file_is_sane($file)) {
176 my @data = read_dpkg_control($file);
180 sub _ensure_file_is_sane {
183 # if file exists and is not 0 bytes
184 if (-f $file and -s $file) {
190 sub slurp_entire_file {
193 or fail("cannot open file $file for reading: $!");
200 sub get_file_checksum {
201 my ($alg, $file) = @_;
202 open (FILE, '<', $file) or fail("Couldn't open $file");
205 $digest = Digest::MD5->new;
206 } elsif ($alg =~ /sha(\d+)/) {
208 $digest = Digest::SHA->new($1);
210 $digest->addfile(*FILE);
211 close FILE or fail("Couldn't close $file");
212 return $digest->hexdigest;
215 sub file_is_encoded_in_non_utf8 {
216 my ($file, $type, $pkg) = @_;
219 open (ICONV, '-|', "env LANG=C iconv -f utf8 -t utf8 $file 2>&1")
220 or fail("failure while checking encoding of $file for $type package $pkg");
223 if (m/iconv: illegal input sequence at position \d+$/) {
231 return $line if $non_utf8;
235 # Just like system, except cleanses the environment first to avoid any strange
236 # side effects due to the user's environment.
238 my @whitelist = qw(PATH INTLTOOL_EXTRACT);
239 my %newenv = map { exists $ENV{$_} ? ($_ => $ENV{$_}) : () } @whitelist;
241 if (not defined $pid) {
243 } elsif ($pid == 0) {
245 exec @_ or die("exec of $_[0] failed: $!\n");
252 # Translate permission strings like `-rwxrwxrwx' into an octal number.
258 $t =~ m/^.(.)(.)(.)(.)(.)(.)(.)(.)(.)/o;
260 $o += 00400 if $1 eq 'r'; # owner read
261 $o += 00200 if $2 eq 'w'; # owner write
262 $o += 00100 if $3 eq 'x'; # owner execute
263 $o += 04000 if $3 eq 'S'; # setuid
264 $o += 04100 if $3 eq 's'; # setuid + owner execute
265 $o += 00040 if $4 eq 'r'; # group read
266 $o += 00020 if $5 eq 'w'; # group write
267 $o += 00010 if $6 eq 'x'; # group execute
268 $o += 02000 if $6 eq 'S'; # setgid
269 $o += 02010 if $6 eq 's'; # setgid + group execute
270 $o += 00004 if $7 eq 'r'; # other read
271 $o += 00002 if $8 eq 'w'; # other write
272 $o += 00001 if $9 eq 'x'; # other execute
273 $o += 01000 if $9 eq 'T'; # stickybit
274 $o += 01001 if $9 eq 't'; # stickybit + other execute
280 return spawn(undef, ['rm', '-rf', '--', @_]);
284 return spawn(undef, ['cp', '-a', '--', @_]);
289 spawn({out => $out, fail => 'error'},
290 ['gzip', '-dc', $in]);
293 # create an empty file
294 # --okay, okay, this is not exactly what `touch' does :-)
296 open(T, '>', $_[0]) or return 0;
297 close(T) or return 0;
305 $str = string('internal error', @_);
307 $str = string('internal error', "$!");
309 $str = string('internal error');
311 $! = 2; # set return code outside eval()