OLD | NEW |
(Empty) | |
| 1 # Copyright © 2008-2011 Raphaël Hertzog <hertzog@debian.org> |
| 2 # |
| 3 # This program is free software; you can redistribute it and/or modify |
| 4 # it under the terms of the GNU General Public License as published by |
| 5 # the Free Software Foundation; either version 2 of the License, or |
| 6 # (at your option) any later version. |
| 7 # |
| 8 # This program is distributed in the hope that it will be useful, |
| 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 # GNU General Public License for more details. |
| 12 # |
| 13 # You should have received a copy of the GNU General Public License |
| 14 # along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 15 |
| 16 package Dpkg::Source::Package; |
| 17 |
| 18 =encoding utf8 |
| 19 |
| 20 =head1 NAME |
| 21 |
| 22 Dpkg::Source::Package - manipulate Debian source packages |
| 23 |
| 24 =head1 DESCRIPTION |
| 25 |
| 26 This module provides an object that can manipulate Debian source |
| 27 packages. While it supports both the extraction and the creation |
| 28 of source packages, the only API that is officially supported |
| 29 is the one that supports the extraction of the source package. |
| 30 |
| 31 =head1 FUNCTIONS |
| 32 |
| 33 =cut |
| 34 |
| 35 use strict; |
| 36 use warnings; |
| 37 |
| 38 our $VERSION = '1.01'; |
| 39 |
| 40 use Dpkg::Gettext; |
| 41 use Dpkg::ErrorHandling; |
| 42 use Dpkg::Control; |
| 43 use Dpkg::Checksums; |
| 44 use Dpkg::Version; |
| 45 use Dpkg::Compression; |
| 46 use Dpkg::Exit qw(run_exit_handlers); |
| 47 use Dpkg::Path qw(check_files_are_the_same find_command); |
| 48 use Dpkg::IPC; |
| 49 use Dpkg::Vendor qw(run_vendor_hook); |
| 50 |
| 51 use Carp; |
| 52 use POSIX qw(:errno_h :sys_wait_h); |
| 53 use File::Basename; |
| 54 |
| 55 use Exporter qw(import); |
| 56 our @EXPORT_OK = qw(get_default_diff_ignore_regex |
| 57 set_default_diff_ignore_regex |
| 58 get_default_tar_ignore_pattern); |
| 59 |
| 60 my $diff_ignore_default_regex = ' |
| 61 # Ignore general backup files |
| 62 (?:^|/).*~$| |
| 63 # Ignore emacs recovery files |
| 64 (?:^|/)\.#.*$| |
| 65 # Ignore vi swap files |
| 66 (?:^|/)\..*\.sw.$| |
| 67 # Ignore baz-style junk files or directories |
| 68 (?:^|/),,.*(?:$|/.*$)| |
| 69 # File-names that should be ignored (never directories) |
| 70 (?:^|/)(?:DEADJOE|\.arch-inventory|\.(?:bzr|cvs|hg|git)ignore)$| |
| 71 # File or directory names that should be ignored |
| 72 (?:^|/)(?:CVS|RCS|\.deps|\{arch\}|\.arch-ids|\.svn| |
| 73 \.hg(?:tags|sigs)?|_darcs|\.git(?:attributes|modules)?| |
| 74 \.shelf|_MTN|\.be|\.bzr(?:\.backup|tags)?)(?:$|/.*$) |
| 75 '; |
| 76 # Take out comments and newlines |
| 77 $diff_ignore_default_regex =~ s/^#.*$//mg; |
| 78 $diff_ignore_default_regex =~ s/\n//sg; |
| 79 |
| 80 # Public variables |
| 81 # XXX: Backwards compatibility, stop exporting on VERSION 2.00. |
| 82 ## no critic (Variables::ProhibitPackageVars) |
| 83 our $diff_ignore_default_regexp; |
| 84 *diff_ignore_default_regexp = \$diff_ignore_default_regex; |
| 85 |
| 86 no warnings 'qw'; ## no critic (TestingAndDebugging::ProhibitNoWarnings) |
| 87 our @tar_ignore_default_pattern = qw( |
| 88 *.a |
| 89 *.la |
| 90 *.o |
| 91 *.so |
| 92 .*.sw? |
| 93 */*~ |
| 94 ,,* |
| 95 .[#~]* |
| 96 .arch-ids |
| 97 .arch-inventory |
| 98 .be |
| 99 .bzr |
| 100 .bzr.backup |
| 101 .bzr.tags |
| 102 .bzrignore |
| 103 .cvsignore |
| 104 .deps |
| 105 .git |
| 106 .gitattributes |
| 107 .gitignore |
| 108 .gitmodules |
| 109 .hg |
| 110 .hgignore |
| 111 .hgsigs |
| 112 .hgtags |
| 113 .shelf |
| 114 .svn |
| 115 CVS |
| 116 DEADJOE |
| 117 RCS |
| 118 _MTN |
| 119 _darcs |
| 120 {arch} |
| 121 ); |
| 122 ## use critic |
| 123 |
| 124 =over 4 |
| 125 |
| 126 =item my $string = get_default_diff_ignore_regex() |
| 127 |
| 128 Returns the default diff ignore regex. |
| 129 |
| 130 =cut |
| 131 |
| 132 sub get_default_diff_ignore_regex { |
| 133 return $diff_ignore_default_regex; |
| 134 } |
| 135 |
| 136 =item set_default_diff_ignore_regex($string) |
| 137 |
| 138 Set a regex as the new default diff ignore regex. |
| 139 |
| 140 =cut |
| 141 |
| 142 sub set_default_diff_ignore_regex { |
| 143 my ($regex) = @_; |
| 144 |
| 145 $diff_ignore_default_regex = $regex; |
| 146 } |
| 147 |
| 148 =item my @array = get_default_tar_ignore_pattern() |
| 149 |
| 150 Returns the default tar ignore pattern, as an array. |
| 151 |
| 152 =cut |
| 153 |
| 154 sub get_default_tar_ignore_pattern { |
| 155 return @tar_ignore_default_pattern; |
| 156 } |
| 157 |
| 158 =item $p = Dpkg::Source::Package->new(filename => $dscfile, options => {}) |
| 159 |
| 160 Creates a new object corresponding to the source package described |
| 161 by the file $dscfile. |
| 162 |
| 163 The options hash supports the following options: |
| 164 |
| 165 =over 8 |
| 166 |
| 167 =item skip_debianization |
| 168 |
| 169 If set to 1, do not apply Debian changes on the extracted source package. |
| 170 |
| 171 =item skip_patches |
| 172 |
| 173 If set to 1, do not apply Debian-specific patches. This options is |
| 174 specific for source packages using format "2.0" and "3.0 (quilt)". |
| 175 |
| 176 =item require_valid_signature |
| 177 |
| 178 If set to 1, the check_signature() method will be stricter and will error |
| 179 out if the signature can't be verified. |
| 180 |
| 181 =item copy_orig_tarballs |
| 182 |
| 183 If set to 1, the extraction will copy the upstream tarballs next the |
| 184 target directory. This is useful if you want to be able to rebuild the |
| 185 source package after its extraction. |
| 186 |
| 187 =back |
| 188 |
| 189 =cut |
| 190 |
| 191 # Object methods |
| 192 sub new { |
| 193 my ($this, %args) = @_; |
| 194 my $class = ref($this) || $this; |
| 195 my $self = { |
| 196 fields => Dpkg::Control->new(type => CTRL_PKG_SRC), |
| 197 options => {}, |
| 198 checksums => Dpkg::Checksums->new(), |
| 199 }; |
| 200 bless $self, $class; |
| 201 if (exists $args{options}) { |
| 202 $self->{options} = $args{options}; |
| 203 } |
| 204 if (exists $args{filename}) { |
| 205 $self->initialize($args{filename}); |
| 206 $self->init_options(); |
| 207 } |
| 208 return $self; |
| 209 } |
| 210 |
| 211 sub init_options { |
| 212 my ($self) = @_; |
| 213 # Use full ignore list by default |
| 214 # note: this function is not called by V1 packages |
| 215 $self->{options}{diff_ignore_regex} ||= $diff_ignore_default_regex; |
| 216 $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/source/local-.*$'; |
| 217 if (defined $self->{options}{tar_ignore}) { |
| 218 $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ] |
| 219 unless @{$self->{options}{tar_ignore}}; |
| 220 } else { |
| 221 $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ]; |
| 222 } |
| 223 push @{$self->{options}{tar_ignore}}, 'debian/source/local-options', |
| 224 'debian/source/local-patch-header'; |
| 225 # Skip debianization while specific to some formats has an impact |
| 226 # on code common to all formats |
| 227 $self->{options}{skip_debianization} ||= 0; |
| 228 } |
| 229 |
| 230 sub initialize { |
| 231 my ($self, $filename) = @_; |
| 232 my ($fn, $dir) = fileparse($filename); |
| 233 error(_g('%s is not the name of a file'), $filename) unless $fn; |
| 234 $self->{basedir} = $dir || './'; |
| 235 $self->{filename} = $fn; |
| 236 |
| 237 # Read the fields |
| 238 my $fields = Dpkg::Control->new(type => CTRL_PKG_SRC); |
| 239 $fields->load($filename); |
| 240 $self->{fields} = $fields; |
| 241 $self->{is_signed} = $fields->get_option('is_pgp_signed'); |
| 242 |
| 243 foreach my $f (qw(Source Version Files)) { |
| 244 unless (defined($fields->{$f})) { |
| 245 error(_g('missing critical source control field %s'), $f); |
| 246 } |
| 247 } |
| 248 |
| 249 $self->{checksums}->add_from_control($fields, use_files_for_md5 => 1); |
| 250 |
| 251 $self->upgrade_object_type(0); |
| 252 } |
| 253 |
| 254 sub upgrade_object_type { |
| 255 my ($self, $update_format) = @_; |
| 256 $update_format //= 1; |
| 257 $self->{fields}{'Format'} = '1.0' |
| 258 unless exists $self->{fields}{'Format'}; |
| 259 my $format = $self->{fields}{'Format'}; |
| 260 |
| 261 if ($format =~ /^([\d\.]+)(?:\s+\((.*)\))?$/) { |
| 262 my ($version, $variant, $major, $minor) = ($1, $2, $1, undef); |
| 263 |
| 264 if (defined $variant and $variant ne lc $variant) { |
| 265 error(_g("source package format '%s' is not supported: %s"), |
| 266 $format, _g('format variant must be in lowercase')); |
| 267 } |
| 268 |
| 269 $major =~ s/\.[\d\.]+$//; |
| 270 my $module = "Dpkg::Source::Package::V$major"; |
| 271 $module .= '::' . ucfirst $variant if defined $variant; |
| 272 eval "require $module; \$minor = \$${module}::CURRENT_MINOR_VERSION;"; |
| 273 $minor //= 0; |
| 274 if ($update_format) { |
| 275 $self->{fields}{'Format'} = "$major.$minor"; |
| 276 $self->{fields}{'Format'} .= " ($variant)" if defined $variant; |
| 277 } |
| 278 if ($@) { |
| 279 error(_g("source package format '%s' is not supported: %s"), |
| 280 $format, $@); |
| 281 } |
| 282 bless $self, $module; |
| 283 } else { |
| 284 error(_g("invalid Format field `%s'"), $format); |
| 285 } |
| 286 } |
| 287 |
| 288 =item $p->get_filename() |
| 289 |
| 290 Returns the filename of the DSC file. |
| 291 |
| 292 =cut |
| 293 |
| 294 sub get_filename { |
| 295 my ($self) = @_; |
| 296 return $self->{basedir} . $self->{filename}; |
| 297 } |
| 298 |
| 299 =item $p->get_files() |
| 300 |
| 301 Returns the list of files referenced by the source package. The filenames |
| 302 usually do not have any path information. |
| 303 |
| 304 =cut |
| 305 |
| 306 sub get_files { |
| 307 my ($self) = @_; |
| 308 return $self->{checksums}->get_files(); |
| 309 } |
| 310 |
| 311 =item $p->check_checksums() |
| 312 |
| 313 Verify the checksums embedded in the DSC file. It requires the presence of |
| 314 the other files constituting the source package. If any inconsistency is |
| 315 discovered, it immediately errors out. |
| 316 |
| 317 =cut |
| 318 |
| 319 sub check_checksums { |
| 320 my ($self) = @_; |
| 321 my $checksums = $self->{checksums}; |
| 322 # add_from_file verify the checksums if they are already existing |
| 323 foreach my $file ($checksums->get_files()) { |
| 324 $checksums->add_from_file($self->{basedir} . $file, key => $file); |
| 325 } |
| 326 } |
| 327 |
| 328 sub get_basename { |
| 329 my ($self, $with_revision) = @_; |
| 330 my $f = $self->{fields}; |
| 331 unless (exists $f->{'Source'} and exists $f->{'Version'}) { |
| 332 error(_g('source and version are required to compute the source basename
')); |
| 333 } |
| 334 my $v = Dpkg::Version->new($f->{'Version'}); |
| 335 my $vs = $v->as_string(omit_epoch => 1, omit_revision => !$with_revision); |
| 336 return $f->{'Source'} . '_' . $vs; |
| 337 } |
| 338 |
| 339 sub find_original_tarballs { |
| 340 my ($self, %opts) = @_; |
| 341 $opts{extension} = compression_get_file_extension_regex() |
| 342 unless exists $opts{extension}; |
| 343 $opts{include_main} = 1 unless exists $opts{include_main}; |
| 344 $opts{include_supplementary} = 1 unless exists $opts{include_supplementary}; |
| 345 my $basename = $self->get_basename(); |
| 346 my @tar; |
| 347 foreach my $dir ('.', $self->{basedir}, $self->{options}{origtardir}) { |
| 348 next unless defined($dir) and -d $dir; |
| 349 opendir(my $dir_dh, $dir) or syserr(_g('cannot opendir %s'), $dir); |
| 350 push @tar, map { "$dir/$_" } grep { |
| 351 ($opts{include_main} and |
| 352 /^\Q$basename\E\.orig\.tar\.$opts{extension}$/) or |
| 353 ($opts{include_supplementary} and |
| 354 /^\Q$basename\E\.orig-[[:alnum:]-]+\.tar\.$opts{extension}$/) |
| 355 } readdir($dir_dh); |
| 356 closedir($dir_dh); |
| 357 } |
| 358 return @tar; |
| 359 } |
| 360 |
| 361 =item $bool = $p->is_signed() |
| 362 |
| 363 Returns 1 if the DSC files contains an embedded OpenPGP signature. |
| 364 Otherwise returns 0. |
| 365 |
| 366 =cut |
| 367 |
| 368 sub is_signed { |
| 369 my $self = shift; |
| 370 return $self->{is_signed}; |
| 371 } |
| 372 |
| 373 =item $p->check_signature() |
| 374 |
| 375 Implement the same OpenPGP signature check that dpkg-source does. |
| 376 In case of problems, it prints a warning or errors out. |
| 377 |
| 378 If the object has been created with the "require_valid_signature" option, |
| 379 then any problem will result in a fatal error. |
| 380 |
| 381 =cut |
| 382 |
| 383 sub check_signature { |
| 384 my ($self) = @_; |
| 385 my $dsc = $self->get_filename(); |
| 386 my @exec; |
| 387 |
| 388 if (find_command('gpgv2')) { |
| 389 push @exec, 'gpgv2'; |
| 390 } elsif (find_command('gpgv')) { |
| 391 push @exec, 'gpgv'; |
| 392 } elsif (find_command('gpg2')) { |
| 393 push @exec, 'gpg2', '--no-default-keyring', '-q', '--verify'; |
| 394 } elsif (find_command('gpg')) { |
| 395 push @exec, 'gpg', '--no-default-keyring', '-q', '--verify'; |
| 396 } |
| 397 if (scalar(@exec)) { |
| 398 if (defined $ENV{HOME} and -r "$ENV{HOME}/.gnupg/trustedkeys.gpg") { |
| 399 push @exec, '--keyring', "$ENV{HOME}/.gnupg/trustedkeys.gpg"; |
| 400 } |
| 401 foreach my $vendor_keyring (run_vendor_hook('keyrings')) { |
| 402 if (-r $vendor_keyring) { |
| 403 push @exec, '--keyring', $vendor_keyring; |
| 404 } |
| 405 } |
| 406 push @exec, $dsc; |
| 407 |
| 408 my ($stdout, $stderr); |
| 409 spawn(exec => \@exec, wait_child => 1, nocheck => 1, |
| 410 to_string => \$stdout, error_to_string => \$stderr, |
| 411 timeout => 10); |
| 412 if (WIFEXITED($?)) { |
| 413 my $gpg_status = WEXITSTATUS($?); |
| 414 print { *STDERR } "$stdout$stderr" if $gpg_status; |
| 415 if ($gpg_status == 1 or ($gpg_status && |
| 416 $self->{options}{require_valid_signature})) |
| 417 { |
| 418 error(_g('failed to verify signature on %s'), $dsc); |
| 419 } elsif ($gpg_status) { |
| 420 warning(_g('failed to verify signature on %s'), $dsc); |
| 421 } |
| 422 } else { |
| 423 subprocerr("@exec"); |
| 424 } |
| 425 } else { |
| 426 if ($self->{options}{require_valid_signature}) { |
| 427 error(_g("could not verify signature on %s since gpg isn't installed
"), $dsc); |
| 428 } else { |
| 429 warning(_g("could not verify signature on %s since gpg isn't install
ed"), $dsc); |
| 430 } |
| 431 } |
| 432 } |
| 433 |
| 434 sub parse_cmdline_options { |
| 435 my ($self, @opts) = @_; |
| 436 foreach (@opts) { |
| 437 if (not $self->parse_cmdline_option($_)) { |
| 438 warning(_g('%s is not a valid option for %s'), $_, ref($self)); |
| 439 } |
| 440 } |
| 441 } |
| 442 |
| 443 sub parse_cmdline_option { |
| 444 return 0; |
| 445 } |
| 446 |
| 447 =item $p->extract($targetdir) |
| 448 |
| 449 Extracts the source package in the target directory $targetdir. Beware |
| 450 that if $targetdir already exists, it will be erased. |
| 451 |
| 452 =cut |
| 453 |
| 454 sub extract { |
| 455 my $self = shift; |
| 456 my $newdirectory = $_[0]; |
| 457 |
| 458 my ($ok, $error) = version_check($self->{fields}{'Version'}); |
| 459 error($error) unless $ok; |
| 460 |
| 461 # Copy orig tarballs |
| 462 if ($self->{options}{copy_orig_tarballs}) { |
| 463 my $basename = $self->get_basename(); |
| 464 my ($dirname, $destdir) = fileparse($newdirectory); |
| 465 $destdir ||= './'; |
| 466 my $ext = compression_get_file_extension_regex(); |
| 467 foreach my $orig (grep { /^\Q$basename\E\.orig(-[[:alnum:]-]+)?\.tar\.$e
xt$/ } |
| 468 $self->get_files()) |
| 469 { |
| 470 my $src = File::Spec->catfile($self->{basedir}, $orig); |
| 471 my $dst = File::Spec->catfile($destdir, $orig); |
| 472 if (not check_files_are_the_same($src, $dst, 1)) { |
| 473 system('cp', '--', $src, $dst); |
| 474 subprocerr("cp $src to $dst") if $?; |
| 475 } |
| 476 } |
| 477 } |
| 478 |
| 479 # Try extract |
| 480 eval { $self->do_extract(@_) }; |
| 481 if ($@) { |
| 482 run_exit_handlers(); |
| 483 die $@; |
| 484 } |
| 485 |
| 486 # Store format if non-standard so that next build keeps the same format |
| 487 if ($self->{fields}{'Format'} ne '1.0' and |
| 488 not $self->{options}{skip_debianization}) |
| 489 { |
| 490 my $srcdir = File::Spec->catdir($newdirectory, 'debian', 'source'); |
| 491 my $format_file = File::Spec->catfile($srcdir, 'format'); |
| 492 unless (-e $format_file) { |
| 493 mkdir($srcdir) unless -e $srcdir; |
| 494 open(my $format_fh, '>', $format_file) |
| 495 or syserr(_g('cannot write %s'), $format_file); |
| 496 print { $format_fh } $self->{fields}{'Format'} . "\n"; |
| 497 close($format_fh); |
| 498 } |
| 499 } |
| 500 |
| 501 # Make sure debian/rules is executable |
| 502 my $rules = File::Spec->catfile($newdirectory, 'debian', 'rules'); |
| 503 my @s = lstat($rules); |
| 504 if (not scalar(@s)) { |
| 505 unless ($! == ENOENT) { |
| 506 syserr(_g('cannot stat %s'), $rules); |
| 507 } |
| 508 warning(_g('%s does not exist'), $rules) |
| 509 unless $self->{options}{skip_debianization}; |
| 510 } elsif (-f _) { |
| 511 chmod($s[2] | 0111, $rules) |
| 512 or syserr(_g('cannot make %s executable'), $rules); |
| 513 } else { |
| 514 warning(_g('%s is not a plain file'), $rules); |
| 515 } |
| 516 } |
| 517 |
| 518 sub do_extract { |
| 519 croak 'Dpkg::Source::Package does not know how to unpack a ' . |
| 520 'source package; use one of the subclasses'; |
| 521 } |
| 522 |
| 523 # Function used specifically during creation of a source package |
| 524 |
| 525 sub before_build { |
| 526 my ($self, $dir) = @_; |
| 527 } |
| 528 |
| 529 sub build { |
| 530 my $self = shift; |
| 531 eval { $self->do_build(@_) }; |
| 532 if ($@) { |
| 533 run_exit_handlers(); |
| 534 die $@; |
| 535 } |
| 536 } |
| 537 |
| 538 sub after_build { |
| 539 my ($self, $dir) = @_; |
| 540 } |
| 541 |
| 542 sub do_build { |
| 543 croak 'Dpkg::Source::Package does not know how to build a ' . |
| 544 'source package; use one of the subclasses'; |
| 545 } |
| 546 |
| 547 sub can_build { |
| 548 my ($self, $dir) = @_; |
| 549 return (0, 'can_build() has not been overriden'); |
| 550 } |
| 551 |
| 552 sub add_file { |
| 553 my ($self, $filename) = @_; |
| 554 my ($fn, $dir) = fileparse($filename); |
| 555 if ($self->{checksums}->has_file($fn)) { |
| 556 croak "tried to add file '$fn' twice"; |
| 557 } |
| 558 $self->{checksums}->add_from_file($filename, key => $fn); |
| 559 $self->{checksums}->export_to_control($self->{fields}, |
| 560 use_files_for_md5 => 1); |
| 561 } |
| 562 |
| 563 sub commit { |
| 564 my $self = shift; |
| 565 eval { $self->do_commit(@_) }; |
| 566 if ($@) { |
| 567 run_exit_handlers(); |
| 568 die $@; |
| 569 } |
| 570 } |
| 571 |
| 572 sub do_commit { |
| 573 my ($self, $dir) = @_; |
| 574 info(_g("'%s' is not supported by the source format '%s'"), |
| 575 'dpkg-source --commit', $self->{fields}{'Format'}); |
| 576 } |
| 577 |
| 578 sub write_dsc { |
| 579 my ($self, %opts) = @_; |
| 580 my $fields = $self->{fields}; |
| 581 |
| 582 foreach my $f (keys %{$opts{override}}) { |
| 583 $fields->{$f} = $opts{override}{$f}; |
| 584 } |
| 585 |
| 586 unless($opts{nocheck}) { |
| 587 foreach my $f (qw(Source Version)) { |
| 588 unless (defined($fields->{$f})) { |
| 589 error(_g('missing information for critical output field %s'), $f
); |
| 590 } |
| 591 } |
| 592 foreach my $f (qw(Maintainer Architecture Standards-Version)) { |
| 593 unless (defined($fields->{$f})) { |
| 594 warning(_g('missing information for output field %s'), $f); |
| 595 } |
| 596 } |
| 597 } |
| 598 |
| 599 foreach my $f (keys %{$opts{remove}}) { |
| 600 delete $fields->{$f}; |
| 601 } |
| 602 |
| 603 my $filename = $opts{filename}; |
| 604 unless (defined $filename) { |
| 605 $filename = $self->get_basename(1) . '.dsc'; |
| 606 } |
| 607 open(my $dsc_fh, '>', $filename) |
| 608 or syserr(_g('cannot write %s'), $filename); |
| 609 $fields->apply_substvars($opts{substvars}); |
| 610 $fields->output($dsc_fh); |
| 611 close($dsc_fh); |
| 612 } |
| 613 |
| 614 =back |
| 615 |
| 616 =head1 CHANGES |
| 617 |
| 618 =head2 Version 1.01 |
| 619 |
| 620 New functions: get_default_diff_ignore_regex(), set_default_diff_ignore_regex(), |
| 621 get_default_tar_ignore_pattern() |
| 622 |
| 623 Deprecated variables: $diff_ignore_default_regexp, @tar_ignore_default_pattern |
| 624 |
| 625 =head1 AUTHOR |
| 626 |
| 627 Raphaël Hertzog, E<lt>hertzog@debian.orgE<gt> |
| 628 |
| 629 =cut |
| 630 |
| 631 1; |
OLD | NEW |