File Coverage

File:Dpkg/Source/Package.pm
Coverage:23.5%

linestmtbrancondsubpodtimecode
1# Copyright © 2008-2011 Raphaël Hertzog <hertzog@debian.org>
2# Copyright © 2008-2019 Guillem Jover <guillem@debian.org>
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17package Dpkg::Source::Package;
18
19=encoding utf8
20
21 - 32
=head1 NAME

Dpkg::Source::Package - manipulate Debian source packages

=head1 DESCRIPTION

This module provides a class that can manipulate Debian source
packages. While it supports both the extraction and the creation
of source packages, the only API that is officially supported
is the one that supports the extraction of the source package.

=cut
33
34
3
3
3
9
3
36
use strict;
35
3
3
3
6
3
93
use warnings;
36
37our $VERSION = '2.02';
38our @EXPORT_OK = qw(
39    get_default_diff_ignore_regex
40    set_default_diff_ignore_regex
41    get_default_tar_ignore_pattern
42);
43
44
3
3
3
6
48
48
use Exporter qw(import);
45
3
3
3
3
3
9
use POSIX qw(:errno_h :sys_wait_h);
46
3
3
3
630
3
60
use Carp;
47
3
3
3
792
13362
108
use File::Temp;
48
3
3
3
429
3555
69
use File::Copy qw(cp);
49
3
3
3
6
3
60
use File::Basename;
50
3
3
3
6
0
24
use File::Spec;
51
52
3
3
3
6
3
57
use Dpkg::Gettext;
53
3
3
3
6
3
90
use Dpkg::ErrorHandling;
54
3
3
3
414
3
93
use Dpkg::Control;
55
3
3
3
414
3
69
use Dpkg::Checksums;
56
3
3
3
423
3
96
use Dpkg::Version;
57
3
3
3
387
0
123
use Dpkg::Compression;
58
3
3
3
432
3
84
use Dpkg::Path qw(check_files_are_the_same check_directory_traversal);
59
3
3
3
9
0
45
use Dpkg::Vendor qw(run_vendor_hook);
60
3
3
3
384
3
42
use Dpkg::Source::Format;
61
3
3
3
369
0
42
use Dpkg::OpenPGP;
62
3
3
3
6
0
171
use Dpkg::OpenPGP::ErrorCodes;
63
64my $diff_ignore_default_regex = '
65# Ignore general backup files
66(?:^|/).*~$|
67# Ignore emacs recovery files
68(?:^|/)\.#.*$|
69# Ignore vi swap files
70(?:^|/)\..*\.sw.$|
71# Ignore baz-style junk files or directories
72(?:^|/),,.*(?:$|/.*$)|
73# File-names that should be ignored (never directories)
74(?:^|/)(?:DEADJOE|\.arch-inventory|\.(?:bzr|cvs|hg|git|mtn-)ignore)$|
75# File or directory names that should be ignored
76(?:^|/)(?:CVS|RCS|\.deps|\{arch\}|\.arch-ids|\.svn|
77\.hg(?:tags|sigs)?|_darcs|\.git(?:attributes|modules|review)?|
78\.mailmap|\.shelf|_MTN|\.be|\.bzr(?:\.backup|tags)?)(?:$|/.*$)
79';
80# Take out comments and newlines
81$diff_ignore_default_regex =~ s/^#.*$//mg;
82$diff_ignore_default_regex =~ s/\n//sg;
83
84
3
3
3
6
3
3693
no warnings 'qw'; ## no critic (TestingAndDebugging::ProhibitNoWarnings)
85my @tar_ignore_default_pattern = qw(
86*.a
87*.la
88*.o
89*.so
90.*.sw?
91*/*~
92,,*
93.[#~]*
94.arch-ids
95.arch-inventory
96.be
97.bzr
98.bzr.backup
99.bzr.tags
100.bzrignore
101.cvsignore
102.deps
103.git
104.gitattributes
105.gitignore
106.gitmodules
107.gitreview
108.hg
109.hgignore
110.hgsigs
111.hgtags
112.mailmap
113.mtn-ignore
114.shelf
115.svn
116CVS
117DEADJOE
118RCS
119_MTN
120_darcs
121{arch}
122);
123## use critic
124
125 - 133
=head1 FUNCTIONS

=over 4

=item $string = get_default_diff_ignore_regex()

Returns the default diff ignore regex.

=cut
134
135sub get_default_diff_ignore_regex {
136
0
1
0
    return $diff_ignore_default_regex;
137}
138
139 - 143
=item set_default_diff_ignore_regex($string)

Set a regex as the new default diff ignore regex.

=cut
144
145sub set_default_diff_ignore_regex {
146
0
1
0
    my $regex = shift;
147
148
0
0
    $diff_ignore_default_regex = $regex;
149}
150
151 - 155
=item @array = get_default_tar_ignore_pattern()

Returns the default tar ignore pattern, as an array.

=cut
156
157sub get_default_tar_ignore_pattern {
158
0
1
0
    return @tar_ignore_default_pattern;
159}
160
161=back
162
163 - 206
=head1 METHODS

=over 4

=item $p = Dpkg::Source::Package->new(%opts, options => {})

Creates a new object corresponding to a source package. When the key
B<filename> is set to a F<.dsc> file, it will be used to initialize the
source package with its description. Otherwise if the B<format> key is
set to a valid value, the object will be initialized for that format
(since dpkg 1.19.3).

The B<options> key is a hash ref which supports the following options:

=over 8

=item skip_debianization

If set to 1, do not apply Debian changes on the extracted source package.

=item skip_patches

If set to 1, do not apply Debian-specific patches. This options is
specific for source packages using format "2.0" and "3.0 (quilt)".

=item require_valid_signature

If set to 1, the check_signature() method will be stricter and will error
out if the signature can't be verified.

=item require_strong_checksums

If set to 1, the check_checksums() method will be stricter and will error
out if there is no strong checksum.

=item copy_orig_tarballs

If set to 1, the extraction will copy the upstream tarballs next the
target directory. This is useful if you want to be able to rebuild the
source package after its extraction.

=back

=cut
207
208# Class methods
209sub new {
210
3
1
3
    my ($this, %args) = @_;
211
3
9
    my $class = ref($this) || $this;
212
3
6
    my $self = {
213        fields => Dpkg::Control->new(type => CTRL_PKG_SRC),
214        format => Dpkg::Source::Format->new(),
215        options => {},
216        checksums => Dpkg::Checksums->new(),
217        openpgp => Dpkg::OpenPGP->new(needs => { api => 'verify' }),
218    };
219
3
6
    bless $self, $class;
220
3
3
    if (exists $args{options}) {
221
0
0
        $self->{options} = $args{options};
222    }
223
3
3
    if (exists $args{filename}) {
224
0
0
        $self->initialize($args{filename});
225
0
0
        $self->init_options();
226    } elsif ($args{format}) {
227
0
0
        $self->{fields}{Format} = $args{format};
228
0
0
        $self->upgrade_object_type(0);
229
0
0
        $self->init_options();
230    }
231
232
3
6
    if ($self->{options}{require_valid_signature}) {
233
0
0
        $self->{report_verify} = \&error;
234    } else {
235
3
3
        $self->{report_verify} = \&warning;
236    }
237
238
3
3
    return $self;
239}
240
241sub init_options {
242
0
0
0
    my $self = shift;
243    # Use full ignore list by default
244    # note: this function is not called by V1 packages
245
0
0
    $self->{options}{diff_ignore_regex} ||= $diff_ignore_default_regex;
246
0
0
    $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/source/local-.*$';
247
0
0
    $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/files(?:\.new)?$';
248
0
0
    if (defined $self->{options}{tar_ignore}) {
249        $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ]
250
0
0
0
0
            unless @{$self->{options}{tar_ignore}};
251    } else {
252
0
0
        $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ];
253    }
254
0
0
0
0
    push @{$self->{options}{tar_ignore}},
255         'debian/source/local-options',
256         'debian/source/local-patch-header',
257         'debian/files',
258         'debian/files.new';
259
0
0
    $self->{options}{copy_orig_tarballs} //= 0;
260
261    # Skip debianization while specific to some formats has an impact
262    # on code common to all formats
263
0
0
    $self->{options}{skip_debianization} //= 0;
264
0
0
    $self->{options}{skip_patches} //= 0;
265
266    # Set default validation checks.
267
0
0
    $self->{options}{require_valid_signature} //= 0;
268
0
0
    $self->{options}{require_strong_checksums} //= 0;
269
270    # Set default compressor for new formats.
271
0
0
    $self->{options}{compression} //= 'xz';
272    $self->{options}{comp_level} //= compression_get_property($self->{options}{compression},
273
0
0
                                                              'default_level');
274    $self->{options}{comp_ext} //= compression_get_property($self->{options}{compression},
275
0
0
                                                            'file_ext');
276}
277
278sub initialize {
279
0
0
0
    my ($self, $filename) = @_;
280
0
0
    my ($fn, $dir) = fileparse($filename);
281
0
0
    error(g_('%s is not the name of a file'), $filename) unless $fn;
282
0
0
    $self->{basedir} = $dir || './';
283
0
0
    $self->{filename} = $fn;
284
285    # Read the fields
286
0
0
    my $fields = $self->{fields};
287
0
0
    $fields->load($filename);
288
0
0
    $self->{is_signed} = $fields->get_option('is_pgp_signed');
289
290
0
0
    foreach my $f (qw(Source Version Files)) {
291
0
0
        unless (defined($fields->{$f})) {
292
0
0
            error(g_('missing critical source control field %s'), $f);
293        }
294    }
295
296
0
0
    $self->{checksums}->add_from_control($fields, use_files_for_md5 => 1);
297
298
0
0
    $self->upgrade_object_type(0);
299}
300
301sub upgrade_object_type {
302
0
0
0
    my ($self, $update_format) = @_;
303
0
0
    $update_format //= 1;
304
305
0
0
    my $format = $self->{fields}{'Format'} // '1.0';
306
0
0
    my ($major, $minor, $variant) = $self->{format}->set($format);
307
308
0
0
    my $module = "Dpkg::Source::Package::V$major";
309
0
0
    $module .= '::' . ucfirst $variant if defined $variant;
310
0
0
    eval qq{
311        pop \@INC if \$INC[-1] eq '.';
312        require $module;
313        \$minor = \$${module}::CURRENT_MINOR_VERSION;
314    };
315
0
0
    if ($@) {
316
0
0
        error(g_("source package format '%s' is not supported: %s"),
317              $format, $@);
318    }
319
0
0
    if ($update_format) {
320
0
0
        $self->{format}->set_from_parts($major, $minor, $variant);
321
0
0
        $self->{fields}{'Format'} = $self->{format}->get();
322    }
323
324
0
0
    $module->prerequisites() if $module->can('prerequisites');
325
0
0
    bless $self, $module;
326}
327
328 - 332
=item $p->get_filename()

Returns the filename of the DSC file.

=cut
333
334sub get_filename {
335
0
1
0
    my $self = shift;
336
0
0
    return File::Spec->catfile($self->{basedir}, $self->{filename});
337}
338
339 - 344
=item $p->get_files()

Returns the list of files referenced by the source package. The filenames
usually do not have any path information.

=cut
345
346sub get_files {
347
0
1
0
    my $self = shift;
348
0
0
    return $self->{checksums}->get_files();
349}
350
351 - 361
=item $p->check_checksums()

Verify the checksums embedded in the DSC file. It requires the presence of
the other files constituting the source package. If any inconsistency is
discovered, it immediately errors out. It will make sure at least one strong
checksum is present.

If the object has been created with the "require_strong_checksums" option,
then any problem will result in a fatal error.

=cut
362
363sub check_checksums {
364
0
1
0
    my $self = shift;
365
0
0
    my $checksums = $self->{checksums};
366
0
0
    my $warn_on_weak = 0;
367
368    # add_from_file verify the checksums if they are already existing
369
0
0
    foreach my $file ($checksums->get_files()) {
370
0
0
        if (not $checksums->has_strong_checksums($file)) {
371
0
0
            if ($self->{options}{require_strong_checksums}) {
372
0
0
                error(g_('source package uses only weak checksums'));
373            } else {
374
0
0
                $warn_on_weak = 1;
375            }
376        }
377
0
0
        my $pathname = File::Spec->catfile($self->{basedir}, $file);
378
0
0
        $checksums->add_from_file($pathname, key => $file);
379    }
380
381
0
0
    warning(g_('source package uses only weak checksums')) if $warn_on_weak;
382}
383
384sub get_basename {
385
0
0
0
    my ($self, $with_revision) = @_;
386
0
0
    my $f = $self->{fields};
387
0
0
    unless (exists $f->{'Source'} and exists $f->{'Version'}) {
388
0
0
        error(g_('%s and %s fields are required to compute the source basename'),
389              'Source', 'Version');
390    }
391
0
0
    my $v = Dpkg::Version->new($f->{'Version'});
392
0
0
    my $vs = $v->as_string(omit_epoch => 1, omit_revision => !$with_revision);
393
0
0
    return $f->{'Source'} . '_' . $vs;
394}
395
396sub find_original_tarballs {
397
0
0
0
    my ($self, %opts) = @_;
398
0
0
    $opts{extension} //= compression_get_file_extension_regex();
399
0
0
    $opts{include_main} //= 1;
400
0
0
    $opts{include_supplementary} //= 1;
401
0
0
    my $basename = $self->get_basename();
402
0
0
    my @tar;
403
0
0
    foreach my $dir ('.', $self->{basedir}, $self->{options}{origtardir}) {
404
0
0
        next unless defined($dir) and -d $dir;
405
0
0
        opendir(my $dir_dh, $dir) or syserr(g_('cannot opendir %s'), $dir);
406
0
0
        push @tar, map { File::Spec->catfile($dir, $_) } grep {
407
0
0
                ($opts{include_main} and
408                 /^\Q$basename\E\.orig\.tar\.$opts{extension}$/) or
409                ($opts{include_supplementary} and
410
0
0
                 /^\Q$basename\E\.orig-[[:alnum:]-]+\.tar\.$opts{extension}$/)
411            } readdir($dir_dh);
412
0
0
        closedir($dir_dh);
413    }
414
0
0
    return @tar;
415}
416
417 - 421
=item $p->get_upstream_signing_key($dir)

Get the filename for the upstream key.

=cut
422
423sub get_upstream_signing_key {
424
0
1
0
    my ($self, $dir) = @_;
425
426
0
0
    return "$dir/debian/upstream/signing-key.asc";
427}
428
429 - 435
=item $p->armor_original_tarball_signature($bin, $asc)

Convert a signature from binary to ASCII armored form. If the signature file
does not exist, it is a no-op. If the signature file is already ASCII armored
then simply copy it, otherwise convert it from binary to ASCII armored form.

=cut
436
437sub armor_original_tarball_signature {
438
8
1
12
    my ($self, $bin, $asc) = @_;
439
440
8
46
    if (-e $bin) {
441
5
20
        return $self->{openpgp}->armor('SIGNATURE', $bin, $asc);
442    }
443
444
3
9
    return;
445}
446
447 - 454
=item $p->check_original_tarball_signature($dir, @asc)

Verify the original upstream tarball signatures @asc using the upstream
public keys. It requires the origin upstream tarballs, their signatures
and the upstream signing key, as found in an unpacked source tree $dir.
If any inconsistency is discovered, it immediately errors out.

=cut
455
456sub check_original_tarball_signature {
457
0
1
    my ($self, $dir, @asc) = @_;
458
459
0
    my $upstream_key = $self->get_upstream_signing_key($dir);
460
0
    if (not -e $upstream_key) {
461
0
        warning(g_('upstream tarball signatures but no upstream signing key'));
462
0
        return;
463    }
464
465
0
    foreach my $asc (@asc) {
466
0
        my $datafile = $asc =~ s/\.asc$//r;
467
468
0
        info(g_('verifying %s'), $asc);
469
0
        my $rc = $self->{openpgp}->verify($datafile, $asc, $upstream_key);
470
0
        if ($rc) {
471
0
            $self->{report_verify}->(g_('cannot verify upstream tarball signature for %s: %s'),
472                                     $datafile, openpgp_errorcode_to_string($rc));
473        }
474    }
475}
476
477 - 482
=item $bool = $p->is_signed()

Returns 1 if the DSC files contains an embedded OpenPGP signature.
Otherwise returns 0.

=cut
483
484sub is_signed {
485
0
1
    my $self = shift;
486
0
    return $self->{is_signed};
487}
488
489 - 497
=item $p->check_signature()

Implement the same OpenPGP signature check that dpkg-source does.
In case of problems, it prints a warning or errors out.

If the object has been created with the "require_valid_signature" option,
then any problem will result in a fatal error.

=cut
498
499sub check_signature {
500
0
1
    my $self = shift;
501
0
    my $dsc = $self->get_filename();
502
0
    my @certs;
503
504
0
    push @certs, $self->{openpgp}->get_trusted_keyrings();
505
506
0
    foreach my $vendor_keyring (run_vendor_hook('package-keyrings')) {
507
0
        if (-r $vendor_keyring) {
508
0
            push @certs, $vendor_keyring;
509        }
510    }
511
512
0
    my $rc = $self->{openpgp}->inline_verify($dsc, undef, @certs);
513
0
    if ($rc) {
514
0
        $self->{report_verify}->(g_('cannot verify inline signature for %s: %s'),
515                                 $dsc, openpgp_errorcode_to_string($rc));
516    }
517}
518
519sub describe_cmdline_options {
520
0
0
    return;
521}
522
523sub parse_cmdline_options {
524
0
0
    my ($self, @opts) = @_;
525
0
    foreach my $option (@opts) {
526
0
        if (not $self->parse_cmdline_option($option)) {
527
0
            warning(g_('%s is not a valid option for %s'), $option, ref $self);
528        }
529    }
530}
531
532sub parse_cmdline_option {
533
0
0
    return 0;
534}
535
536 - 542
=item $p->extract($targetdir)

Extracts the source package in the target directory $targetdir. Beware
that if $targetdir already exists, it will be erased (as long as the
no_overwrite_dir option is set).

=cut
543
544sub extract {
545
0
1
    my ($self, $newdirectory) = @_;
546
547
0
    my ($ok, $error) = version_check($self->{fields}{'Version'});
548
0
    if (not $ok) {
549
0
        if ($self->{options}{ignore_bad_version}) {
550
0
            warning($error);
551        } else {
552
0
            error($error);
553        }
554    }
555
556    # Copy orig tarballs
557
0
    if ($self->{options}{copy_orig_tarballs}) {
558
0
        my $basename = $self->get_basename();
559
0
        my ($dirname, $destdir) = fileparse($newdirectory);
560
0
        $destdir ||= './';
561
0
        my $ext = compression_get_file_extension_regex();
562
0
0
        foreach my $orig (grep { /^\Q$basename\E\.orig(-[[:alnum:]-]+)?\.tar\.$ext$/ }
563                          $self->get_files())
564        {
565
0
            my $src = File::Spec->catfile($self->{basedir}, $orig);
566
0
            my $dst = File::Spec->catfile($destdir, $orig);
567
0
            if (not check_files_are_the_same($src, $dst, 1)) {
568
0
                cp($src, $dst)
569                    or syserr(g_('cannot copy %s to %s'), $src, $dst);
570            }
571        }
572    }
573
574    # Try extract
575
0
    $self->do_extract($newdirectory);
576
577    # Check for directory traversals.
578
0
    if (not $self->{options}{skip_debianization} and not $self->{no_check}) {
579        # We need to add a trailing slash to handle the debian directory
580        # possibly being a symlink.
581
0
        check_directory_traversal($newdirectory, "$newdirectory/debian/");
582    }
583
584    # Store format if non-standard so that next build keeps the same format
585
0
    if ($self->{fields}{'Format'} and
586        $self->{fields}{'Format'} ne '1.0' and
587        not $self->{options}{skip_debianization})
588    {
589
0
        my $srcdir = File::Spec->catdir($newdirectory, 'debian', 'source');
590
0
        my $format_file = File::Spec->catfile($srcdir, 'format');
591
0
        unless (-e $format_file) {
592
0
            mkdir($srcdir) unless -e $srcdir;
593
0
            $self->{format}->save($format_file);
594        }
595    }
596
597    # Make sure debian/rules is executable
598
0
    my $rules = File::Spec->catfile($newdirectory, 'debian', 'rules');
599
0
    my @s = lstat($rules);
600
0
    if (not scalar(@s)) {
601
0
        unless ($! == ENOENT) {
602
0
            syserr(g_('cannot stat %s'), $rules);
603        }
604        warning(g_('%s does not exist'), $rules)
605
0
            unless $self->{options}{skip_debianization};
606    } elsif (-f _) {
607
0
        chmod($s[2] | 0111, $rules)
608            or syserr(g_('cannot make %s executable'), $rules);
609    } else {
610
0
        warning(g_('%s is not a plain file'), $rules);
611    }
612}
613
614sub do_extract {
615
0
0
    croak 'Dpkg::Source::Package does not know how to unpack a ' .
616          'source package; use one of the subclasses';
617}
618
619# Function used specifically during creation of a source package
620
621sub before_build {
622
0
0
    my ($self, $dir) = @_;
623}
624
625sub build {
626
0
0
    my $self = shift;
627
628
0
    $self->do_build(@_);
629}
630
631sub after_build {
632
0
0
    my ($self, $dir) = @_;
633}
634
635sub do_build {
636
0
0
    croak 'Dpkg::Source::Package does not know how to build a ' .
637          'source package; use one of the subclasses';
638}
639
640sub can_build {
641
0
0
    my ($self, $dir) = @_;
642
0
    return (0, 'can_build() has not been overridden');
643}
644
645sub add_file {
646
0
0
    my ($self, $filename) = @_;
647
0
    my ($fn, $dir) = fileparse($filename);
648
0
    if ($self->{checksums}->has_file($fn)) {
649
0
        croak "tried to add file '$fn' twice";
650    }
651
0
    $self->{checksums}->add_from_file($filename, key => $fn);
652    $self->{checksums}->export_to_control($self->{fields},
653
0
                                            use_files_for_md5 => 1);
654}
655
656sub commit {
657
0
0
    my $self = shift;
658
659
0
    $self->do_commit(@_);
660}
661
662sub do_commit {
663
0
0
    my ($self, $dir) = @_;
664    info(g_("'%s' is not supported by the source format '%s'"),
665
0
         'dpkg-source --commit', $self->{fields}{'Format'});
666}
667
668sub write_dsc {
669
0
0
    my ($self, %opts) = @_;
670
0
    my $fields = $self->{fields};
671
672
0
0
    foreach my $f (keys %{$opts{override}}) {
673
0
        $fields->{$f} = $opts{override}{$f};
674    }
675
676
0
    unless ($opts{nocheck}) {
677
0
        foreach my $f (qw(Source Version Architecture)) {
678
0
            unless (defined($fields->{$f})) {
679
0
                error(g_('missing information for critical output field %s'), $f);
680            }
681        }
682
0
        foreach my $f (qw(Maintainer Standards-Version)) {
683
0
            unless (defined($fields->{$f})) {
684
0
                warning(g_('missing information for output field %s'), $f);
685            }
686        }
687    }
688
689
0
0
    foreach my $f (keys %{$opts{remove}}) {
690
0
        delete $fields->{$f};
691    }
692
693
0
    my $filename = $opts{filename};
694
0
    $filename //= $self->get_basename(1) . '.dsc';
695
0
    open(my $dsc_fh, '>', $filename)
696        or syserr(g_('cannot write %s'), $filename);
697
0
    $fields->apply_substvars($opts{substvars});
698
0
    $fields->output($dsc_fh);
699
0
    close($dsc_fh);
700}
701
702=back
703
704 - 741
=head1 CHANGES

=head2 Version 2.02 (dpkg 1.21.10)

New method: armor_original_tarball_signature().

=head2 Version 2.01 (dpkg 1.20.1)

New method: get_upstream_signing_key().

=head2 Version 2.00 (dpkg 1.20.0)

New method: check_original_tarball_signature().

Remove variable: $diff_ignore_default_regexp.

Hide variable: @tar_ignore_default_pattern.

=head2 Version 1.03 (dpkg 1.19.3)

New option: format in new().

=head2 Version 1.02 (dpkg 1.18.7)

New option: require_strong_checksums in check_checksums().

=head2 Version 1.01 (dpkg 1.17.2)

New functions: get_default_diff_ignore_regex(), set_default_diff_ignore_regex(),
get_default_tar_ignore_pattern()

Deprecated variables: $diff_ignore_default_regexp, @tar_ignore_default_pattern

=head2 Version 1.00 (dpkg 1.16.1)

Mark the module as public.

=cut
742
7431;