- Timestamp:
- 2021-02-26T19:39:51+13:00 (3 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone2/perllib/cpan/Image/ExifTool/DNG.pm
r24107 r34921 18 18 use Image::ExifTool::CanonRaw; 19 19 20 $VERSION = '1. 15';20 $VERSION = '1.23'; 21 21 22 22 sub ProcessOriginalRaw($$$); … … 49 49 50 50 %Image::ExifTool::DNG::AdobeData = ( #PH 51 GROUPS => { 0 => 'MakerNotes', 1 => 'Adobe ', 2 => 'Image' },51 GROUPS => { 0 => 'MakerNotes', 1 => 'AdobeDNG', 2 => 'Image' }, 52 52 PROCESS_PROC => \&ProcessAdobeData, 53 53 WRITE_PROC => \&WriteAdobeStuff, … … 144 144 sub ProcessOriginalRaw($$$) 145 145 { 146 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;146 my ($et, $dirInfo, $tagTablePtr) = @_; 147 147 my $dataPt = $$dirInfo{DataPt}; 148 148 my $start = $$dirInfo{DirStart}; … … 156 156 my $val = Get32u($dataPt, $pos); 157 157 $val or $pos += 4, next; # ignore zero values 158 my $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, $index);158 my $tagInfo = $et->GetTagInfo($tagTablePtr, $index); 159 159 $tagInfo or $err = "Missing DNG tag $index", last; 160 160 if ($index & 0x02) { … … 169 169 my $tag = $$tagInfo{Name}; 170 170 # only extract this information if requested (because it takes time) 171 if ($exifTool->{OPTIONS}->{Binary} or 172 $exifTool->{REQ_TAG_LOOKUP}->{lc($tag)}) 171 my $lcTag = lc $tag; 172 if (($$et{OPTIONS}{Binary} and not $$et{EXCL_TAG_LOOKUP}{$lcTag}) or 173 $$et{REQ_TAG_LOOKUP}{$lcTag}) 173 174 { 174 unless (eval 'require Compress::Zlib') {175 unless (eval { require Compress::Zlib }) { 175 176 $err = 'Install Compress::Zlib to extract compressed images'; 176 177 last; … … 208 209 } 209 210 } 210 $e xifTool->FoundTag($tagInfo, $val);211 } 212 $e xifTool->Warn($err || 'Bad OriginalRawFileData') if defined $err;211 $et->FoundTag($tagInfo, $val); 212 } 213 $et->Warn($err || 'Bad OriginalRawFileData') if defined $err; 213 214 return 1; 214 215 } … … 220 221 sub ProcessAdobeData($$$) 221 222 { 222 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;223 my ($et, $dirInfo, $tagTablePtr) = @_; 223 224 my $dataPt = $$dirInfo{DataPt}; 224 225 my $dataPos = $$dirInfo{DataPos}; … … 226 227 my $end = $$dirInfo{DirLen} + $pos; 227 228 my $outfile = $$dirInfo{OutFile}; 228 my $verbose = $e xifTool->Options('Verbose');229 my $htmlDump = $e xifTool->Options('HtmlDump');229 my $verbose = $et->Options('Verbose'); 230 my $htmlDump = $et->Options('HtmlDump'); 230 231 231 232 return 0 unless $$dataPt =~ /^Adobe\0/; 232 233 unless ($outfile) { 233 $e xifTool->VerboseDir($dirInfo);234 $et->VerboseDir($dirInfo); 234 235 # don't parse makernotes if FastScan > 1 235 my $fast = $e xifTool->Options('FastScan');236 my $fast = $et->Options('FastScan'); 236 237 return 1 if $fast and $fast > 1; 237 238 } 238 $htmlDump and $e xifTool->HDump($dataPos, 6, 'Adobe DNGPrivateData header');239 $htmlDump and $et->HDump($dataPos, 6, 'Adobe DNGPrivateData header'); 239 240 SetByteOrder('MM'); # always big endian 240 241 $pos += 6; … … 247 248 my $name = "Adobe$tag"; 248 249 $name =~ tr/ //d; 249 $e xifTool->HDump($dataPos + $pos - 8, 8, "$name header", "Data Size: $size bytes");250 $et->HDump($dataPos + $pos - 8, 8, "$name header", "Data Size: $size bytes"); 250 251 # dump non-EXIF format data 251 252 unless ($tag =~ /^(MakN|SR2 )$/) { 252 $e xifTool->HDump($dataPos + $pos, $size, "$name data");253 $et->HDump($dataPos + $pos, $size, "$name data"); 253 254 } 254 255 } 255 256 if ($verbose and not $outfile) { 256 $tagInfo or $e xifTool->VPrint(0, "$$exifTool{INDENT}Unsupported DNGAdobeData record: ($tag)\n");257 $e xifTool->VerboseInfo($tag,257 $tagInfo or $et->VPrint(0, "$$et{INDENT}Unsupported DNGAdobeData record: ($tag)\n"); 258 $et->VerboseInfo($tag, 258 259 ref $tagInfo eq 'HASH' ? $tagInfo : undef, 259 260 DataPt => $dataPt, … … 272 273 $value = substr($$dataPt, $pos, $size); 273 274 } else { 274 $e xifTool->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size));275 $et->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size)); 275 276 } 276 277 last; … … 295 296 if ($outfile) { 296 297 $dirInfo{Proc} = $processProc; # WriteAdobeStuff() calls this to do the actual writing 297 $value = $e xifTool->WriteDirectory(\%dirInfo, $subTable, \&WriteAdobeStuff);298 $value = $et->WriteDirectory(\%dirInfo, $subTable, \&WriteAdobeStuff); 298 299 # use old directory if an error occurred 299 300 defined $value or $value = substr($$dataPt, $pos, $size); 300 301 } else { 301 302 # override process proc for MakN 302 $e xifTool->ProcessDirectory(\%dirInfo, $subTable, $processProc);303 $et->ProcessDirectory(\%dirInfo, $subTable, $processProc); 303 304 } 304 305 last; … … 313 314 ++$pos if $size & 0x01; # (darn padding) 314 315 } 315 $pos == $end or $e xifTool->Warn("$pos $end Adobe private data is corrupt");316 $pos == $end or $et->Warn("$pos $end Adobe private data is corrupt"); 316 317 return 1; 317 318 } … … 326 327 sub ProcessAdobeCRW($$$) 327 328 { 328 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;329 my ($et, $dirInfo, $tagTablePtr) = @_; 329 330 my $dataPt = $$dirInfo{DataPt}; 330 331 my $start = $$dirInfo{DirStart}; 331 332 my $end = $start + $$dirInfo{DirLen}; 332 my $verbose = $e xifTool->Options('Verbose');333 my $buildMakerNotes = $e xifTool->Options('MakerNotes');333 my $verbose = $et->Options('Verbose'); 334 my $buildMakerNotes = $et->Options('MakerNotes'); 334 335 my $outfile = $$dirInfo{OutFile}; 335 336 my ($newTags, $oldChanged); … … 341 342 342 343 # initialize maker note data if building maker notes 343 $buildMakerNotes and Image::ExifTool::CanonRaw::InitMakerNotes($e xifTool);344 $buildMakerNotes and Image::ExifTool::CanonRaw::InitMakerNotes($et); 344 345 345 346 my $entries = Get16u($dataPt, $start + 2); 346 347 my $pos = $start + 4; 347 $e xifTool->VerboseDir($dirInfo, $entries) unless $outfile;348 $et->VerboseDir($dirInfo, $entries) unless $outfile; 348 349 if ($outfile) { 349 350 # get hash of new tags 350 $newTags = $e xifTool->GetNewTagInfoHash($tagTablePtr);351 $newTags = $et->GetNewTagInfoHash($tagTablePtr); 351 352 $$outfile = substr($$dataPt, $start, 4); 352 $oldChanged = $ exifTool->{CHANGED};353 $oldChanged = $$et{CHANGED}; 353 354 } 354 355 # loop through entries in Adobe CRW information … … 365 366 my $format = $Image::ExifTool::CanonRaw::crwTagFormat{$tagType}; 366 367 my $count; 367 my $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, $tagID, \$value);368 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tagID, \$value); 368 369 if ($tagInfo) { 369 370 $format = $$tagInfo{Format} if $$tagInfo{Format}; … … 405 406 #### eval Validate ($dirData, $subdirStart, $size) 406 407 if (defined $$subdir{Validate} and not eval $$subdir{Validate}) { 407 $e xifTool->Warn("Invalid $name data");408 $et->Warn("Invalid $name data"); 408 409 } else { 409 $subdir = $e xifTool->WriteDirectory(\%subdirInfo, $newTagTable);410 $subdir = $et->WriteDirectory(\%subdirInfo, $newTagTable); 410 411 if (defined $subdir and length $subdir) { 411 412 if ($subdirStart) { … … 418 419 } 419 420 } elsif ($$newTags{$tagID}) { 420 my $nvHash = $e xifTool->GetNewValueHash($tagInfo);421 if ( Image::ExifTool::IsOverwriting($nvHash, $val)) {422 my $newVal = Image::ExifTool::GetNewValues($nvHash);421 my $nvHash = $et->GetNewValueHash($tagInfo); 422 if ($et->IsOverwriting($nvHash, $val)) { 423 my $newVal = $et->GetNewValue($nvHash); 423 424 my $verboseVal; 424 425 $verboseVal = $newVal if $verbose > 1; … … 428 429 } 429 430 if (defined $newVal) { 430 $e xifTool->VerboseValue("- CanonRaw:$$tagInfo{Name}", $value);431 $e xifTool->VerboseValue("+ CanonRaw:$$tagInfo{Name}", $verboseVal);431 $et->VerboseValue("- CanonRaw:$$tagInfo{Name}", $value); 432 $et->VerboseValue("+ CanonRaw:$$tagInfo{Name}", $verboseVal); 432 433 $value = $newVal; 433 ++$ exifTool->{CHANGED};434 ++$$et{CHANGED}; 434 435 } 435 436 } … … 441 442 $$outfile .= Set16u($tag) . Set32u(length($value)) . $value; 442 443 } else { 443 $e xifTool->HandleTag($tagTablePtr, $tagID, $val,444 $et->HandleTag($tagTablePtr, $tagID, $val, 444 445 Index => $index, 445 446 DataPt => $dataPt, … … 451 452 if ($buildMakerNotes) { 452 453 # build maker notes information if requested 453 Image::ExifTool::CanonRaw::BuildMakerNotes($e xifTool, $tagID, $tagInfo,454 Image::ExifTool::CanonRaw::BuildMakerNotes($et, $tagID, $tagInfo, 454 455 \$value, $format, $count); 455 456 } … … 457 458 # (we lost the directory structure, but the second tag 0x0805 458 459 # should be in the ImageDescription directory) 459 $ exifTool->{DIR_NAME} = 'ImageDescription' if $tagID == 0x0805;460 $$et{DIR_NAME} = 'ImageDescription' if $tagID == 0x0805; 460 461 SetByteOrder('MM'); 461 462 $pos += $size; 462 463 } 463 464 if ($outfile and (not defined $$outfile or $index != $entries or 464 $ exifTool->{CHANGED} == $oldChanged))465 $$et{CHANGED} == $oldChanged)) 465 466 { 466 $ exifTool->{CHANGED} = $oldChanged; # nothing changed467 $$et{CHANGED} = $oldChanged; # nothing changed 467 468 undef $$outfile; # rewrite old directory 468 469 } 469 470 if ($index != $entries) { 470 $e xifTool->Warn('Truncated CRW notes');471 $et->Warn('Truncated CRW notes'); 471 472 } elsif ($pos < $end) { 472 $e xifTool->Warn($end-$pos . ' extra bytes at end of CRW notes');473 $et->Warn($end-$pos . ' extra bytes at end of CRW notes'); 473 474 } 474 475 # finish building maker notes if necessary 475 476 if ($buildMakerNotes) { 476 477 SetByteOrder($byteOrder); 477 Image::ExifTool::CanonRaw::SaveMakerNotes($e xifTool);478 Image::ExifTool::CanonRaw::SaveMakerNotes($et); 478 479 } 479 480 return 1; … … 487 488 sub ProcessAdobeMRW($$$) 488 489 { 489 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;490 my ($et, $dirInfo, $tagTablePtr) = @_; 490 491 my $dataPt = $$dirInfo{DataPt}; 491 492 my $dirLen = $$dirInfo{DirLen}; … … 499 500 my $raf = new File::RandomAccess(\$buff); 500 501 my %dirInfo = ( RAF => $raf, OutFile => $outfile ); 501 my $rtnVal = Image::ExifTool::MinoltaRaw::ProcessMRW($e xifTool, \%dirInfo);502 my $rtnVal = Image::ExifTool::MinoltaRaw::ProcessMRW($et, \%dirInfo); 502 503 if ($outfile and defined $$outfile and length $$outfile) { 503 504 # remove MRW header and add Adobe header … … 513 514 sub ProcessAdobeRAF($$$) 514 515 { 515 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;516 my ($et, $dirInfo, $tagTablePtr) = @_; 516 517 return 0 if $$dirInfo{OutFile}; # (can't write this yet) 517 518 my $dataPt = $$dirInfo{DataPt}; … … 524 525 $pos += 2; 525 526 } else { 526 $e xifTool->Warn('Invalid DNG RAF data');527 $et->Warn('Invalid DNG RAF data'); 527 528 return 0; 528 529 } 529 $e xifTool->VerboseDir($dirInfo);530 $et->VerboseDir($dirInfo); 530 531 # make fake RAF object for processing (same acronym, different meaning) 531 532 my $raf = new File::RandomAccess($dataPt); … … 545 546 DirStart => $pos - $len, 546 547 ); 547 $$e xifTool{SET_GROUP1} = "RAF$num";548 $e xifTool->ProcessDirectory(\%dirInfo, $tagTablePtr) or $warn = 1;549 delete $$e xifTool{SET_GROUP1};548 $$et{SET_GROUP1} = "RAF$num"; 549 $et->ProcessDirectory(\%dirInfo, $tagTablePtr) or $warn = 1; 550 delete $$et{SET_GROUP1}; 550 551 $num = ($num || 1) + 1; 551 552 } 552 $warn and $e xifTool->Warn('Possibly corrupt RAF information');553 $warn and $et->Warn('Possibly corrupt RAF information'); 553 554 return 1; 554 555 } … … 561 562 sub ProcessAdobeSR2($$$) 562 563 { 563 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;564 my ($et, $dirInfo, $tagTablePtr) = @_; 564 565 return 0 if $$dirInfo{OutFile}; # (can't write this yet) 565 566 my $dataPt = $$dirInfo{DataPt}; … … 572 573 return 0 unless SetByteOrder(substr($$dataPt, $start, 2)); 573 574 574 $e xifTool->VerboseDir($dirInfo);575 $et->VerboseDir($dirInfo); 575 576 my $dataPos = $$dirInfo{DataPos}; 576 577 my $dirStart = $start + 6; # pointer to maker note directory … … 589 590 Parent => $$dirInfo{DirName}, 590 591 ); 591 if ($e xifTool->Options('HtmlDump')) {592 $e xifTool->HDump($dataPos + $start, 6, 'Adobe SR2 data');592 if ($et->Options('HtmlDump')) { 593 $et->HDump($dataPos + $start, 6, 'Adobe SR2 data'); 593 594 } 594 595 # parse the SR2 directory 595 $e xifTool->ProcessDirectory(\%subdirInfo, $tagTablePtr);596 $et->ProcessDirectory(\%subdirInfo, $tagTablePtr); 596 597 return 1; 597 598 } … … 604 605 sub ProcessAdobeIFD($$$) 605 606 { 606 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;607 my ($et, $dirInfo, $tagTablePtr) = @_; 607 608 return 0 if $$dirInfo{OutFile}; # (can't write this yet) 608 609 my $dataPt = $$dirInfo{DataPt}; … … 616 617 # parse the mutilated IFD. This is similar to a TIFF IFD, except: 617 618 # - data follows directly after Count entry in IFD 618 # - byte order of IFD ent ires is always big-endian, but byte order of data changes619 # - byte order of IFD entries is always big-endian, but byte order of data changes 619 620 SetByteOrder('MM'); # IFD structure is always big-endian 620 621 my $entries = Get16u($dataPt, $pos + 2); 621 $e xifTool->VerboseDir($dirInfo, $entries);622 $et->VerboseDir($dirInfo, $entries); 622 623 $pos += 4; 623 624 … … 632 633 if ($format < 1 or $format > 13) { 633 634 # warn unless the IFD was just padded with zeros 634 $format and $e xifTool->Warn(635 $format and $et->Warn( 635 636 sprintf("Unknown format ($format) for $$dirInfo{DirName} tag 0x%x",$tagID)); 636 637 return 0; # must be corrupted … … 641 642 SetByteOrder($dataOrder); # data stored in native order 642 643 my $val = ReadValue($dataPt, $pos + 8, $formatStr, $count, $size); 643 $e xifTool->HandleTag($tagTablePtr, $tagID, $val,644 $et->HandleTag($tagTablePtr, $tagID, $val, 644 645 Index => $index, 645 646 DataPt => $dataPt, … … 651 652 } 652 653 if ($index < $entries) { 653 $e xifTool->Warn("Truncated $$dirInfo{DirName} directory");654 $et->Warn("Truncated $$dirInfo{DirName} directory"); 654 655 return 0; 655 656 } … … 662 663 # Returns: 1 on success, otherwise returns 0 and sets a Warning 663 664 # Notes: data has 6 byte header (2 for byte order and 4 for original offset) 665 # --> or 18 bytes for DNG converted from JPG by Adobe Camera Raw! 664 666 sub ProcessAdobeMakN($$$) 665 667 { 666 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;668 my ($et, $dirInfo, $tagTablePtr) = @_; 667 669 my $dataPt = $$dirInfo{DataPt}; 668 670 my $start = $$dirInfo{DirStart}; … … 675 677 return 0 unless SetByteOrder(substr($$dataPt, $start, 2)); 676 678 677 $e xifTool->VerboseDir($dirInfo) unless $outfile;679 $et->VerboseDir($dirInfo) unless $outfile; 678 680 my $dataPos = $$dirInfo{DataPos}; 679 my $dirStart = $start + 6; # pointer to maker note directory 680 my $dirLen = $len - 6; 681 my $hdrLen = 6; 682 683 # hack for extra 12 bytes in MakN header of JPEG converted to DNG by Adobe Camera Raw 684 # (4 bytes "00 00 00 01" followed by 8 unknown bytes) 685 $hdrLen += 12 if $len >= 18 and substr($$dataPt, $start+6, 4) eq "\0\0\0\x01"; 686 687 my $dirStart = $start + $hdrLen; # pointer to maker note directory 688 my $dirLen = $len - $hdrLen; 681 689 682 690 my $hdr = substr($$dataPt, $dirStart, $dirLen < 48 ? $dirLen : 48); 683 my $tagInfo = $e xifTool->GetTagInfo($tagTablePtr, 'MakN', \$hdr);691 my $tagInfo = $et->GetTagInfo($tagTablePtr, 'MakN', \$hdr); 684 692 return 0 unless $tagInfo and $$tagInfo{SubDirectory}; 685 693 my $subdir = $$tagInfo{SubDirectory}; … … 701 709 ); 702 710 # look for start of maker notes IFD 703 my $loc = Image::ExifTool::MakerNotes::LocateIFD($e xifTool,\%subdirInfo);711 my $loc = Image::ExifTool::MakerNotes::LocateIFD($et,\%subdirInfo); 704 712 unless (defined $loc) { 705 $e xifTool->Warn('Maker notes could not be parsed');713 $et->Warn('Maker notes could not be parsed'); 706 714 return 0; 707 715 } 708 if ($e xifTool->Options('HtmlDump')) {709 $e xifTool->HDump($dataPos + $start, 6, 'Adobe MakN data');710 $e xifTool->HDump($dataPos + $dirStart, $loc, "$$tagInfo{Name} header") if $loc;716 if ($et->Options('HtmlDump')) { 717 $et->HDump($dataPos + $start, $hdrLen, 'Adobe MakN data'); 718 $et->HDump($dataPos + $dirStart, $loc, "$$tagInfo{Name} header") if $loc; 711 719 } 712 720 … … 721 729 # rewrite the maker notes directory 722 730 my $fixup = $subdirInfo{Fixup} = new Image::ExifTool::Fixup; 723 my $oldChanged = $$e xifTool{CHANGED};724 my $buff = $e xifTool->WriteDirectory(\%subdirInfo, $subTable);731 my $oldChanged = $$et{CHANGED}; 732 my $buff = $et->WriteDirectory(\%subdirInfo, $subTable); 725 733 # nothing to do if error writing directory or nothing changed 726 unless (defined $buff and $ exifTool->{CHANGED} != $oldChanged) {727 $ exifTool->{CHANGED} = $oldChanged;734 unless (defined $buff and $$et{CHANGED} != $oldChanged) { 735 $$et{CHANGED} = $oldChanged; 728 736 return 1; 729 737 } … … 746 754 $fixup->{Shift} += $loc; # adjust for makernotes header 747 755 $fixup->ApplyFixup(\$buff); # fix up pointer offsets 748 # get copy of original Adobe header (6 ) and makernotes header ($loc)749 my $header = substr($$dataPt, $start, 6+ $loc);756 # get copy of original Adobe header (6 or 18) and makernotes header ($loc) 757 my $header = substr($$dataPt, $start, $hdrLen + $loc); 750 758 # add Adobe and makernotes headers to new directory 751 759 $$outfile = $header . $buff; 752 760 } else { 753 761 # parse the maker notes directory 754 $e xifTool->ProcessDirectory(\%subdirInfo, $subTable, $$subdir{ProcessProc});762 $et->ProcessDirectory(\%subdirInfo, $subTable, $$subdir{ProcessProc}); 755 763 # extract maker notes as a block if specified 756 if ($e xifTool->Options('MakerNotes') or757 $ exifTool->{REQ_TAG_LOOKUP}->{lc($$tagInfo{Name})})764 if ($et->Options('MakerNotes') or 765 $$et{REQ_TAG_LOOKUP}{lc($$tagInfo{Name})}) 758 766 { 759 767 my $val; … … 764 772 $subdirInfo{DirLen} = $dirLen; 765 773 # rebuild the maker notes to identify all offsets that require fixing up 766 $val = Image::ExifTool::Exif::RebuildMakerNotes($exifTool, $subTable, \%subdirInfo); 767 defined $val or $exifTool->Warn('Error rebuilding maker notes (may be corrupt)'); 774 $val = Image::ExifTool::Exif::RebuildMakerNotes($et, \%subdirInfo, $subTable); 775 if (not defined $val and $dirLen > 4) { 776 $et->Warn('Error rebuilding maker notes (may be corrupt)'); 777 } 768 778 } else { 769 779 # extract this directory as a block if specified … … 771 781 } 772 782 $val = substr($$dataPt, 20) unless defined $val; 773 $e xifTool->FoundTag($tagInfo, $val);783 $et->FoundTag($tagInfo, $val); 774 784 } 775 785 } … … 783 793 sub WriteAdobeStuff($$$) 784 794 { 785 my ($e xifTool, $dirInfo, $tagTablePtr) = @_;786 $e xifToolor return 1; # allow dummy access795 my ($et, $dirInfo, $tagTablePtr) = @_; 796 $et or return 1; # allow dummy access 787 797 my $proc = $$dirInfo{Proc} || \&ProcessAdobeData; 788 798 my $buff; 789 799 $$dirInfo{OutFile} = \$buff; 790 &$proc($e xifTool, $dirInfo, $tagTablePtr) or undef $buff;800 &$proc($et, $dirInfo, $tagTablePtr) or undef $buff; 791 801 return $buff; 792 802 } … … 811 821 =head1 AUTHOR 812 822 813 Copyright 2003-20 11, Phil Harvey (phil at owl.phy.queensu.ca)823 Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com) 814 824 815 825 This library is free software; you can redistribute it and/or modify it
Note:
See TracChangeset
for help on using the changeset viewer.