Ignore:
Timestamp:
2021-02-26T19:39:51+13:00 (3 years ago)
Author:
anupama
Message:

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone2/perllib/cpan/Image/ExifTool/EXE.pm

    r24107 r34921  
    55#
    66# Revisions:    2008/08/28 - P. Harvey Created
     7#               2011/07/12 - P. Harvey Added CHM (ok, not EXE, but it fits here)
    78#
    89# References:   1) http://www.openwatcom.org/ftp/devel/docs/pecoff.pdf
     
    2122use Image::ExifTool qw(:DataAccess :Utils);
    2223
    23 $VERSION = '1.04';
     24$VERSION = '1.17';
    2425
    2526sub ProcessPEResources($$);
     
    4950    23 => 'HTML',
    5051    24 => 'Manifest',
     52);
     53
     54my %languageCode = (
     55    Notes => q{
     56        See L<https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid>
     57        for the full list of Microsoft language codes.
     58    },
     59    '0000' => 'Neutral',
     60    '007F' => 'Invariant',
     61    '0400' => 'Process default',
     62    '0401' => 'Arabic',
     63    '0402' => 'Bulgarian',
     64    '0403' => 'Catalan',
     65    '0404' => 'Chinese (Traditional)',
     66    '0405' => 'Czech',
     67    '0406' => 'Danish',
     68    '0407' => 'German',
     69    '0408' => 'Greek',
     70    '0409' => 'English (U.S.)',
     71    '040A' => 'Spanish (Castilian)',
     72    '040B' => 'Finnish',
     73    '040C' => 'French',
     74    '040D' => 'Hebrew',
     75    '040E' => 'Hungarian',
     76    '040F' => 'Icelandic',
     77    '0410' => 'Italian',
     78    '0411' => 'Japanese',
     79    '0412' => 'Korean',
     80    '0413' => 'Dutch',
     81    '0414' => 'Norwegian (Bokml)',
     82    '0415' => 'Polish',
     83    '0416' => 'Portuguese (Brazilian)',
     84    '0417' => 'Rhaeto-Romanic',
     85    '0418' => 'Romanian',
     86    '0419' => 'Russian',
     87    '041A' => 'Croato-Serbian (Latin)',
     88    '041B' => 'Slovak',
     89    '041C' => 'Albanian',
     90    '041D' => 'Swedish',
     91    '041E' => 'Thai',
     92    '041F' => 'Turkish',
     93    '0420' => 'Urdu',
     94    # 0421-0493 ref 6
     95    '0421' => 'Indonesian',
     96    '0422' => 'Ukrainian',
     97    '0423' => 'Belarusian',
     98    '0424' => 'Slovenian',
     99    '0425' => 'Estonian',
     100    '0426' => 'Latvian',
     101    '0427' => 'Lithuanian',
     102    '0428' => 'Maori',
     103    '0429' => 'Farsi',
     104    '042a' => 'Vietnamese',
     105    '042b' => 'Armenian',
     106    '042c' => 'Azeri',
     107    '042d' => 'Basque',
     108    '042e' => 'Sorbian',
     109    '042f' => 'Macedonian',
     110    '0430' => 'Sutu',
     111    '0431' => 'Tsonga',
     112    '0432' => 'Tswana',
     113    '0433' => 'Venda',
     114    '0434' => 'Xhosa',
     115    '0435' => 'Zulu',
     116    '0436' => 'Afrikaans',
     117    '0437' => 'Georgian',
     118    '0438' => 'Faeroese',
     119    '0439' => 'Hindi',
     120    '043a' => 'Maltese',
     121    '043b' => 'Saami',
     122    '043c' => 'Gaelic',
     123    '043e' => 'Malay',
     124    '043f' => 'Kazak',
     125    '0440' => 'Kyrgyz',
     126    '0441' => 'Swahili',
     127    '0443' => 'Uzbek',
     128    '0444' => 'Tatar',
     129    '0445' => 'Bengali',
     130    '0446' => 'Punjabi',
     131    '0447' => 'Gujarati',
     132    '0448' => 'Oriya',
     133    '0449' => 'Tamil',
     134    '044a' => 'Telugu',
     135    '044b' => 'Kannada',
     136    '044c' => 'Malayalam',
     137    '044d' => 'Assamese',
     138    '044e' => 'Marathi',
     139    '044f' => 'Sanskrit',
     140    '0450' => 'Mongolian',
     141    '0456' => 'Galician',
     142    '0457' => 'Konkani',
     143    '0458' => 'Manipuri',
     144    '0459' => 'Sindhi',
     145    '045a' => 'Syriac',
     146    '0460' => 'Kashmiri',
     147    '0461' => 'Nepali',
     148    '0465' => 'Divehi',
     149    '047f' => 'Invariant',
     150    '048f' => 'Esperanto',
     151    '0490' => 'Walon',
     152    '0491' => 'Cornish',
     153    '0492' => 'Welsh',
     154    '0493' => 'Breton',
     155    '0800' => 'Neutral 2',
     156    '0804' => 'Chinese (Simplified)',
     157    '0807' => 'German (Swiss)',
     158    '0809' => 'English (British)',
     159    '080A' => 'Spanish (Mexican)',
     160    '080C' => 'French (Belgian)',
     161    '0810' => 'Italian (Swiss)',
     162    '0813' => 'Dutch (Belgian)',
     163    '0814' => 'Norwegian (Nynorsk)',
     164    '0816' => 'Portuguese',
     165    '081A' => 'Serbo-Croatian (Cyrillic)',
     166    '0C07' => 'German (Austrian)',
     167    '0C09' => 'English (Australian)',
     168    '0C0A' => 'Spanish (Modern)',
     169    '0C0C' => 'French (Canadian)',
     170    '1009' => 'English (Canadian)',
     171    '100C' => 'French (Swiss)',
    51172);
    52173
     
    102223        PrintConv => '$self->ConvertDateTime($val)',
    103224    },
     225    9 => {
     226        Name => 'ImageFileCharacteristics',
     227        # ref https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_file_header
     228        PrintConv => { BITMASK => {
     229            0 => 'No relocs',
     230            1 => 'Executable',
     231            2 => 'No line numbers',
     232            3 => 'No symbols',
     233            4 => 'Aggressive working-set trim',
     234            5 => 'Large address aware',
     235            7 => 'Bytes reversed lo',
     236            8 => '32-bit',
     237            9 => 'No debug',
     238            10 => 'Removable run from swap',
     239            11 => 'Net run from swap',
     240            12 => 'System file',
     241            13 => 'DLL',
     242            14 => 'Uniprocessor only',
     243            15 => 'Bytes reversed hi',
     244        }},
     245    },
    104246    10 => {
    105247        Name => 'PEType',
    106248        PrintHex => 1,
    107249        PrintConv => {
     250            0x107 => 'ROM Image',
    108251            0x10b => 'PE32',
    109252            0x20b => 'PE32+',
     
    154297            2 => 'Windows GUI',
    155298            3 => 'Windows command line',
    156             5 => 'OS/2 Command line', #5
     299            5 => 'OS/2 command line', #5
    157300            7 => 'POSIX command line',
    158301            9 => 'Windows CE GUI',
     
    259402    },
    260403    LanguageCode => {
    261         Notes => 'extracted from the StringFileInfo value',
     404        Notes => 'Windows code page; extracted from the StringFileInfo value',
    262405        # ref http://techsupt.winbatch.com/TS/T000001050F49.html
    263406        # (also see http://support.bigfix.com/fixlet/documents/WinInspectors-2006-08-10.pdf)
     
    265408        # (not a complete set)
    266409        PrintString => 1,
    267         PrintConv => {
    268             '0000' => 'Neutral',
    269             '007F' => 'Invariant',
    270             '0400' => 'Process default',
    271             '0401' => 'Arabic',
    272             '0402' => 'Bulgarian',
    273             '0403' => 'Catalan',
    274             '0404' => 'Chinese (Traditional)',
    275             '0405' => 'Czech',
    276             '0406' => 'Danish',
    277             '0407' => 'German',
    278             '0408' => 'Greek',
    279             '0409' => 'English (U.S.)',
    280             '040A' => 'Spanish (Castilian)',
    281             '040B' => 'Finnish',
    282             '040C' => 'French',
    283             '040D' => 'Hebrew',
    284             '040E' => 'Hungarian',
    285             '040F' => 'Icelandic',
    286             '0410' => 'Italian',
    287             '0411' => 'Japanese',
    288             '0412' => 'Korean',
    289             '0413' => 'Dutch',
    290             '0414' => 'Norwegian (Bokml)',
    291             '0415' => 'Polish',
    292             '0416' => 'Portuguese (Brazilian)',
    293             '0417' => 'Rhaeto-Romanic',
    294             '0418' => 'Romanian',
    295             '0419' => 'Russian',
    296             '041A' => 'Croato-Serbian (Latin)',
    297             '041B' => 'Slovak',
    298             '041C' => 'Albanian',
    299             '041D' => 'Swedish',
    300             '041E' => 'Thai',
    301             '041F' => 'Turkish',
    302             '0420' => 'Urdu',
    303             # 0421-0493 ref 6
    304             '0421' => 'Indonesian',
    305             '0422' => 'Ukrainian',
    306             '0423' => 'Belarusian',
    307             '0424' => 'Slovenian',
    308             '0425' => 'Estonian',
    309             '0426' => 'Latvian',
    310             '0427' => 'Lithuanian',
    311             '0428' => 'Maori',
    312             '0429' => 'Farsi',
    313             '042a' => 'Vietnamese',
    314             '042b' => 'Armenian',
    315             '042c' => 'Azeri',
    316             '042d' => 'Basque',
    317             '042e' => 'Sorbian',
    318             '042f' => 'Macedonian',
    319             '0430' => 'Sutu',
    320             '0431' => 'Tsonga',
    321             '0432' => 'Tswana',
    322             '0433' => 'Venda',
    323             '0434' => 'Xhosa',
    324             '0435' => 'Zulu',
    325             '0436' => 'Afrikaans',
    326             '0437' => 'Georgian',
    327             '0438' => 'Faeroese',
    328             '0439' => 'Hindi',
    329             '043a' => 'Maltese',
    330             '043b' => 'Saami',
    331             '043c' => 'Gaelic',
    332             '043e' => 'Malay',
    333             '043f' => 'Kazak',
    334             '0440' => 'Kyrgyz',
    335             '0441' => 'Swahili',
    336             '0443' => 'Uzbek',
    337             '0444' => 'Tatar',
    338             '0445' => 'Bengali',
    339             '0446' => 'Punjabi',
    340             '0447' => 'Gujarati',
    341             '0448' => 'Oriya',
    342             '0449' => 'Tamil',
    343             '044a' => 'Telugu',
    344             '044b' => 'Kannada',
    345             '044c' => 'Malayalam',
    346             '044d' => 'Assamese',
    347             '044e' => 'Marathi',
    348             '044f' => 'Sanskrit',
    349             '0450' => 'Mongolian',
    350             '0456' => 'Galician',
    351             '0457' => 'Konkani',
    352             '0458' => 'Manipuri',
    353             '0459' => 'Sindhi',
    354             '045a' => 'Syriac',
    355             '0460' => 'Kashmiri',
    356             '0461' => 'Nepali',
    357             '0465' => 'Divehi',
    358             '047f' => 'Invariant',
    359             '048f' => 'Esperanto',
    360             '0490' => 'Walon',
    361             '0491' => 'Cornish',
    362             '0492' => 'Welsh',
    363             '0493' => 'Breton',
    364             '0800' => 'Neutral 2',
    365             '0804' => 'Chinese (Simplified)',
    366             '0807' => 'German (Swiss)',
    367             '0809' => 'English (British)',
    368             '080A' => 'Spanish (Mexican)',
    369             '080C' => 'French (Belgian)',
    370             '0810' => 'Italian (Swiss)',
    371             '0813' => 'Dutch (Belgian)',
    372             '0814' => 'Norwegian (Nynorsk)',
    373             '0816' => 'Portuguese',
    374             '081A' => 'Serbo-Croatian (Cyrillic)',
    375             '0C07' => 'German (Austrian)',
    376             '0C09' => 'English (Australian)',
    377             '0C0A' => 'Spanish (Modern)',
    378             '0C0C' => 'French (Canadian)',
    379             '1009' => 'English (Canadian)',
    380             '100C' => 'French (Swiss)',
    381         },
     410        SeparateTable => 1,
     411        PrintConv => \%languageCode,
    382412    },
    383413    CharacterSet => {
     
    412442    LegalCopyright  => { },
    413443    LegalTrademarks => { },
    414     OriginalFilename=> { },
     444    OriginalFilename=> { Name => 'OriginalFileName' },
    415445    PrivateBuild    => { },
    416446    ProductName     => { },
     
    595625            8 => 'Dynamically bound bundle',
    596626            9 => 'Shared library stub for static linking',
     627            # (the following from Apple loader.h header file)
     628            10 => 'Debug information',
     629            11 => 'x86_64 kexts',
    597630        },
     631    },
     632    6 => {
     633        Name => 'ObjectFlags',
     634        PrintHex => 1,
     635        # ref Apple loader.h header file
     636        PrintConv => { BITMASK => {
     637             0 => 'No undefs',
     638             1 => 'Incrementa link',
     639             2 => 'Dyld link',
     640             3 => 'Bind at load',
     641             4 => 'Prebound',
     642             5 => 'Split segs',
     643             6 => 'Lazy init',
     644             7 => 'Two level',
     645             8 => 'Force flat',
     646             9 => 'No multi defs',
     647             10 => 'No fix prebinding',
     648             11 => 'Prebindable',
     649             12 => 'All mods bound',
     650             13 => 'Subsections via symbols',
     651             14 => 'Canonical',
     652             15 => 'Weak defines',
     653             16 => 'Binds to weak',
     654             17 => 'Allow stack execution',
     655             18 => 'Dead strippable dylib',
     656             19 => 'Root safe',
     657             20 => 'No reexported dylibs',
     658             21 => 'Random address',
     659        }},
    598660    },
    599661);
     
    699761);
    700762
     763# Information extracted from static library archives
     764# (ref http://opensource.apple.com//source/xnu/xnu-1456.1.26/EXTERNAL_HEADERS/ar.h)
     765%Image::ExifTool::EXE::AR = (
     766    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     767    GROUPS => { 2 => 'Other' },
     768    NOTES => q{
     769        Information extracted from static libraries.
     770    },
     771  #  0  string[16] ar_name
     772    16 => {
     773        Name => 'CreateDate',
     774        Groups => { 2 => 'Time' },
     775        Format => 'string[12]',
     776        ValueConv => 'ConvertUnixTime($val,1)',
     777        PrintConv => '$self->ConvertDateTime($val)',
     778    },
     779  # 28  string[6]  ar_uid
     780  # 34  string[6]  ar_gid
     781  # 40  string[8]  ar_mode
     782  # 48  string[10] ar_size
     783  # 58  string[2]  terminator "`\n"
     784);
     785
     786# Microsoft compiled help format (ref http://www.russotto.net/chm/chmformat.html)
     787%Image::ExifTool::EXE::CHM = (
     788    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     789    GROUPS => { 2 => 'Other' },
     790    NOTES => 'Tags extracted from Microsoft Compiled HTML files.',
     791    FORMAT => 'int32u',
     792    1 => { Name => 'CHMVersion' },
     793    # 2 - total header length
     794    # 3 - 1
     795    # 4 - low bits of date/time value plus 42 (ref http://www.nongnu.org/chmspec/latest/ITSF.html)
     796    5 => {
     797        Name => 'LanguageCode',
     798        SeparateTable => 1,
     799        ValueConv => 'sprintf("%.4X", $val)',
     800        PrintConv => \%languageCode,
     801    },
     802);
     803
     804#------------------------------------------------------------------------------
     805# Extract information from a CHM file
     806# Inputs: 0) ExifTool object reference, 1) dirInfo reference
     807# Returns: 1 on success, 0 if this wasn't a valid CHM file
     808sub ProcessCHM($$)
     809{
     810    my ($et, $dirInfo) = @_;
     811    my $raf = $$dirInfo{RAF};
     812    my $buff;
     813
     814    return 0 unless $raf->Read($buff, 56) == 56 and
     815        $buff =~ /^ITSF.{20}\x10\xfd\x01\x7c\xaa\x7b\xd0\x11\x9e\x0c\0\xa0\xc9\x22\xe6\xec/s;
     816    my $tagTablePtr = GetTagTable('Image::ExifTool::EXE::CHM');
     817    $et->SetFileType();
     818    SetByteOrder('II');
     819    $et->ProcessDirectory({ DataPt => \$buff }, $tagTablePtr);
     820    return 1;
     821}
     822
    701823#------------------------------------------------------------------------------
    702824# Read Unicode string (null terminated) from resource
    703 # Inputs: 0) data ref, 1) start offset, 2) optional ExifTool object ref
     825# Inputs: 0) data ref, 1) start offset, 2) data end, 3) optional ExifTool object ref
    704826# Returns: 0) Unicode string translated to UTF8, or current CharSet with ExifTool ref
    705827#          1) end pos (rounded up to nearest 4 bytes)
    706 sub ReadUnicodeStr($$;$)
     828sub ReadUnicodeStr($$$;$)
    707829{
    708     my ($dataPt, $pos, $exifTool) = @_;
    709     my $len = length $$dataPt;
     830    my ($dataPt, $pos, $end, $et) = @_;
     831    $end = length $$dataPt if $end > length $$dataPt;   # (be safe)
    710832    my $str = '';
    711     while ($pos + 2 <= $len) {
     833    while ($pos + 2 <= $end) {
    712834        my $ch = substr($$dataPt, $pos, 2);
    713835        $pos += 2;
     
    716838    }
    717839    $pos += 2 if $pos & 0x03;
    718     my $to = $exifTool ? $exifTool->Options('Charset') : 'UTF8';
     840    my $to = $et ? $et->Options('Charset') : 'UTF8';
    719841    return (Image::ExifTool::Decode(undef,$str,'UCS2','II',$to), $pos);
    720842}
     
    726848sub ProcessPEVersion($$)
    727849{
    728     my ($exifTool, $dirInfo) = @_;
     850    my ($et, $dirInfo) = @_;
    729851    my $dataPt = $$dirInfo{DataPt};
    730852    my $pos = $$dirInfo{DirStart};
     
    740862        $type = Get16u($dataPt, $pos + 4);
    741863        return 0 unless $len or $valLen;  # prevent possible infinite loop
    742         ($string, $strEnd) = ReadUnicodeStr($dataPt, $pos + 6);
     864        ($string, $strEnd) = ReadUnicodeStr($dataPt, $pos + 6, $pos + $len);
    743865        return 0 if $strEnd + $valLen > $end;
    744866        unless ($index or $string eq 'VS_VERSION_INFO') {
    745             $exifTool->Warn('Invalid Version Info block');
     867            $et->Warn('Invalid Version Info block');
    746868            return 0;
    747869        }
     
    751873            $$dirInfo{DirLen} = $valLen;
    752874            my $subTablePtr = GetTagTable('Image::ExifTool::EXE::PEVersion');
    753             $exifTool->ProcessDirectory($dirInfo, $subTablePtr);
     875            $et->ProcessDirectory($dirInfo, $subTablePtr);
    754876            $pos = $strEnd + $valLen;
    755877        } elsif ($string eq 'StringFileInfo' and $valLen == 0) {
     
    762884                $valLen = Get16u($dataPt, $pt + 2);
    763885                # $type = Get16u($dataPt, $pt + 4);
     886                my $entryEnd = $pt + $len;
    764887                # get tag ID (converted to UTF8)
    765                 ($string, $pt) = ReadUnicodeStr($dataPt, $pt + 6);
     888                ($string, $pt) = ReadUnicodeStr($dataPt, $pt + 6, $entryEnd);
    766889                unless ($index) {
    767890                    # separate the language code and character set
     
    774897                        $string = substr($string, 0, 4);
    775898                    }
    776                     $exifTool->HandleTag($tagTablePtr, 'LanguageCode', uc $string);
    777                     $exifTool->HandleTag($tagTablePtr, 'CharacterSet', uc $char) if $char;
     899                    $et->HandleTag($tagTablePtr, 'LanguageCode', uc $string);
     900                    $et->HandleTag($tagTablePtr, 'CharacterSet', uc $char) if $char;
    778901                    next;
    779902                }
     
    784907                    $name =~ tr/-_a-zA-Z0-9//dc; # remove illegal characters
    785908                    next unless length $name;
    786                     Image::ExifTool::AddTagToTable($tagTablePtr, $tag, { Name => $name });
     909                    AddTagToTable($tagTablePtr, $tag, { Name => $name });
    787910                }
    788911                # get tag value (converted to current Charset)
    789912                if ($valLen) {
    790                     ($string, $pt) = ReadUnicodeStr($dataPt, $pt, $exifTool);
     913                    ($string, $pt) = ReadUnicodeStr($dataPt, $pt, $entryEnd, $et);
    791914                } else {
    792915                    $string = '';
    793916                }
    794                 $exifTool->HandleTag($tagTablePtr, $tag, $string);
     917                $et->HandleTag($tagTablePtr, $tag, $string);
     918                # step to next entry (padded to an even word)
     919                $pt = ($entryEnd + 3) & 0xfffffffc;
    795920            }
    796921        } else {
     
    808933sub ProcessPEResources($$)
    809934{
    810     my ($exifTool, $dirInfo) = @_;
     935    my ($et, $dirInfo) = @_;
    811936    my $raf = $$dirInfo{RAF};
    812937    my $base = $$dirInfo{Base};
    813938    my $dirStart = $$dirInfo{DirStart} + $base;
    814939    my $level = $$dirInfo{Level} || 0;
    815     my $verbose = $exifTool->Options('Verbose');
     940    my $verbose = $et->Options('Verbose');
    816941    my ($buff, $buf2, $item);
    817942
     
    833958            # ignore everything but the Version resource unless verbose
    834959            if ($verbose) {
    835                 $exifTool->VPrint(0, "$resType resource:\n");
     960                $et->VPrint(0, "$resType resource:\n");
    836961            } else {
    837962                next unless $resType eq 'Version';
     
    843968            $$dirInfo{DirStart} = $entryPos & 0x7fffffff;
    844969            $$dirInfo{Level} = $level + 1;
    845             ProcessPEResources($exifTool, $dirInfo) or return 0;
     970            ProcessPEResources($et, $dirInfo) or return 0;
    846971            --$$dirInfo{Level};
    847972        } elsif ($$dirInfo{ResType} eq 'Version' and $level == 2 and
     
    863988            return 0 unless $filePos;
    864989            $raf->Seek($filePos, 0) and $raf->Read($buf2, $len) == $len or return 0;
    865             ProcessPEVersion($exifTool, {
     990            ProcessPEVersion($et, {
    866991                DataPt   => \$buf2,
    867992                DataLen  => $len,
    868993                DirStart => 0,
    869994                DirLen   => $len,
    870             }) or $exifTool->Warn('Possibly corrupt Version resource');
     995            }) or $et->Warn('Possibly corrupt Version resource');
    871996            $$dirInfo{GotVersion} = 1;  # set flag so we don't do this again
    872997        }
     
    8811006sub ProcessPEDict($$)
    8821007{
    883     my ($exifTool, $dirInfo) = @_;
     1008    my ($et, $dirInfo) = @_;
    8841009    my $raf = $$dirInfo{RAF};
    8851010    my $dataPt = $$dirInfo{DataPt};
    8861011    my $dirLen = length($$dataPt);
    887     my ($pos, @sections, %dirInfo);
     1012    my ($pos, @sections, %dirInfo, $rsrcFound);
    8881013
    8891014    # loop through all sections
     
    8951020        # remember the section offsets for the VirtualAddress lookup later
    8961021        push @sections, { Base => $offset, Size => $size, VirtualAddress => $va };
    897         # save details of the first resource section
     1022        # save details of the first resource section (or .text if .rsrc not found, ref forum11465)
     1023        next unless ($name eq ".rsrc\0\0\0" and not $rsrcFound and defined($rsrcFound = 1)) or
     1024                    ($name eq ".text\0\0\0" and not %dirInfo);
    8981025        %dirInfo = (
    8991026            RAF      => $raf,
     
    9021029            DirLen   => $size,
    9031030            Sections => \@sections,
    904         ) if $name eq ".rsrc\0\0\0" and not %dirInfo;
     1031        );
    9051032    }
    9061033    # process the first resource section
    907     ProcessPEResources($exifTool, \%dirInfo) or return 0 if %dirInfo;
     1034    ProcessPEResources($et, \%dirInfo) or return 0 if %dirInfo;
    9081035    return 1;
     1036}
     1037
     1038#------------------------------------------------------------------------------
     1039# Override file type if necessary for Mach object files and libraries
     1040# Inputs: 0) ExifTool ref, 1) ObjectFileType number, 2) flag for fat binary
     1041my %machOverride = (
     1042    1 => [ 'object file', 'O' ],
     1043    6 => [ 'dynamic link library', 'DYLIB' ],
     1044    8 => [ 'dynamic bound bundle', 'DYLIB' ],
     1045    9 => [ 'dynamic link library stub', 'DYLIB' ],
     1046);
     1047sub MachOverride($$;$)
     1048{
     1049    my ($et, $objType, $fat) = @_;
     1050    my $override = $machOverride{$objType};
     1051    if ($override) {
     1052        my $desc = 'Mach-O ' . ($fat ? 'fat ' : '') . $$override[0];
     1053        $et->OverrideFileType($desc, undef, $$override[1]);
     1054    }
     1055}
     1056
     1057#------------------------------------------------------------------------------
     1058# Extract tags from Mach header
     1059# Inputs: 0) ExifTool ref, 1) data ref, 2) flag to extract object type
     1060# Returns: true if Mach header was found
     1061# Mach type based on magic number
     1062# [bit depth, byte order starting with "Little" or "Big"]
     1063my %machType = (
     1064    "\xfe\xed\xfa\xce" => ['32 bit', 'Big endian'],
     1065    "\xce\xfa\xed\xfe" => ['32 bit', 'Little endian'],
     1066    "\xfe\xed\xfa\xcf" => ['64 bit', 'Big endian'],
     1067    "\xcf\xfa\xed\xfe" => ['64 bit', 'Little endian'],
     1068);
     1069sub ExtractMachTags($$;$)
     1070{
     1071    my ($et, $dataPt, $doObj) = @_;
     1072    # get information about mach header based on the magic number (first 4 bytes)
     1073    my $info = $machType{substr($$dataPt, 0, 4)};
     1074    if ($info) {
     1075        # Mach header structure:
     1076        #  0 int32u magic
     1077        #  4 int32u cputype
     1078        #  8 int32u cpusubtype
     1079        # 12 int32u filetype
     1080        # 16 int32u ncmds
     1081        # 20 int32u sizeofcmds
     1082        # 24 int32u flags
     1083        my $tagTablePtr = GetTagTable('Image::ExifTool::EXE::MachO');
     1084        SetByteOrder($$info[1]);
     1085        my $cpuType = Get32s($dataPt, 4);
     1086        my $subType = Get32s($dataPt, 8);
     1087        $et->HandleTag($tagTablePtr, 0, $$info[0]);
     1088        $et->HandleTag($tagTablePtr, 1, $$info[1]);
     1089        $et->HandleTag($tagTablePtr, 3, $cpuType);
     1090        $et->HandleTag($tagTablePtr, 4, "$cpuType $subType");
     1091        if ($doObj) {
     1092            my $objType = Get32u($dataPt, 12);
     1093            my $flags = Get32u($dataPt, 24);
     1094            $et->HandleTag($tagTablePtr, 5, $objType);
     1095            $et->HandleTag($tagTablePtr, 6, $flags);
     1096            # override file type if this is an object file or library
     1097            MachOverride($et, $objType);
     1098        } else { # otherwise this was a static library
     1099            $et->OverrideFileType('Mach-O static library', undef, 'A');
     1100        }
     1101        return 1;
     1102    }
     1103    return 0;
    9091104}
    9101105
     
    9151110sub ProcessEXE($$)
    9161111{
    917     my ($exifTool, $dirInfo) = @_;
     1112    my ($et, $dirInfo) = @_;
    9181113    my $raf = $$dirInfo{RAF};
    919     my ($buff, $buf2, $type, $tagTablePtr, %dirInfo);
     1114    my ($buff, $buf2, $type, $mime, $ext, $tagTablePtr, %dirInfo);
    9201115
    9211116    my $size = $raf->Read($buff, 0x40) or return 0;
     1117    my $fast3 = $$et{OPTIONS}{FastScan} && $$et{OPTIONS}{FastScan} == 3;
    9221118#
    9231119# DOS and Windows EXE
     
    9271123        # validate DOS header
    9281124        # (ref http://www.delphidabbler.com/articles?article=8&part=2)
    929         #   0  magic   : int16u    # Magic number ("MZ")
    930         #   2  cblp    : int16u    # Bytes on last page of file
    931         #   4  cp      : int16u    # Pages in file
    932         #   6  crlc    : int16u    # Relocations
    933         #   8  cparhdr : int16u    # Size of header in paragraphs
    934         #  10  minalloc: int16u    # Minimum extra paragraphs needed
    935         #  12  maxalloc: int16u    # Maximum extra paragraphs needed
    936         #  14  ss      : int16u    # Initial (relative) SS value
    937         #  16  sp      : int16u    # Initial SP value
    938         #  18  csum    : int16u    # Checksum
    939         #  20  ip      : int16u    # Initial IP value
    940         #  22  cs      : int16u    # Initial (relative) CS value
    941         #  24  lfarlc  : int16u    # Address of relocation table
    942         #  26  ovno    : int16u    # Overlay number
    943         #  28  res     : int16u[4] # Reserved words
    944         #  36  oemid   : int16u    # OEM identifier (for oeminfo)
    945         #  38  oeminfo : int16u    # OEM info; oemid specific
    946         #  40  res2    : int16u[10]# Reserved words
    947         #  60  lfanew  : int32u;   # File address of new exe header
     1125        #   0 int16u     magic    - Magic number ("MZ")
     1126        #   2 int16u     cblp     - Bytes on last page of file
     1127        #   4 int16u     cp       - Pages in file
     1128        #   6 int16u     crlc     - Relocations
     1129        #   8 int16u     cparhdr  - Size of header in paragraphs
     1130        #  10 int16u     minalloc - Minimum extra paragraphs needed
     1131        #  12 int16u     maxalloc - Maximum extra paragraphs needed
     1132        #  14 int16u     ss       - Initial (relative) SS value
     1133        #  16 int16u     sp       - Initial SP value
     1134        #  18 int16u     csum     - Checksum
     1135        #  20 int16u     ip       - Initial IP value
     1136        #  22 int16u     cs       - Initial (relative) CS value
     1137        #  24 int16u     lfarlc   - Address of relocation table
     1138        #  26 int16u     ovno     - Overlay number
     1139        #  28 int16u[4]  res      - Reserved words
     1140        #  36 int16u     oemid    - OEM identifier (for oeminfo)
     1141        #  38 int16u     oeminfo  - OEM info; oemid specific
     1142        #  40 int16u[10] res2     - Reserved words
     1143        #  60 int32u;    lfanew   - File address of new exe header
    9481144        SetByteOrder('II');
    9491145        my ($cblp, $cp, $lfarlc, $lfanew) = unpack('x2v2x18vx34V', $buff);
    9501146        my $fileSize = ($cp - ($cblp ? 1 : 0)) * 512 + $cblp;
    951         return 0 if $fileSize < 0x40 or $fileSize < $lfarlc;
    952         # read the Windows PE header
    953         if ($lfarlc == 0x40 and $fileSize > $lfanew + 2 and
    954             # read the Windows NE, PE or LE (virtual device driver) header
    955             $raf->Seek($lfanew, 0) and $raf->Read($buff, 0x40) and
    956             $buff =~ /^(NE|PE|LE)/)
    957         {
     1147        #(patch to accommodate observed 64-bit files)
     1148        #return 0 if $fileSize < 0x40 or $fileSize < $lfarlc;
     1149        #return 0 if $fileSize < 0x40; (changed to warning in ExifTool 12.08)
     1150        $et->Warn('Invalid file size in DOS header') if $fileSize < 0x40;
     1151        # read the Windows NE, PE or LE (virtual device driver) header
     1152        #if ($lfarlc == 0x40 and $fileSize > $lfanew + 2 and ...
     1153        if ($raf->Seek($lfanew, 0) and $raf->Read($buff, 0x40) and $buff =~ /^(NE|PE|LE)/) {
    9581154            if ($1 eq 'NE') {
    9591155                if ($size >= 0x40) { # NE header is 64 bytes (ref 2)
    9601156                    # check for DLL
    9611157                    my $appFlags = Get16u(\$buff, 0x0c);
    962                     $type = 'Win16 ' . ($appFlags & 0x80 ? 'DLL' : 'EXE');
     1158                    $ext = $appFlags & 0x80 ? 'DLL' : 'EXE';
     1159                    $type = "Win16 $ext";
    9631160                    # offset 0x02 is 2 bytes with linker version and revision numbers
    9641161                    # offset 0x36 is executable type (2 = Windows)
     
    9741171                #  22 int16u Characteristics
    9751172                if ($size >= 24) {  # PE header is 24 bytes (plus optional header)
    976                     my $flags = Get16u(\$buff, 22);
    977                     $exifTool->SetFileType('Win32 ' . ($flags & 0x2000 ? 'DLL' : 'EXE'));
     1173                    my $mach = Get16u(\$buff, 4);   # MachineType
     1174                    my $flags = Get16u(\$buff, 22); # ImageFileCharacteristics
     1175                    my $machine = $Image::ExifTool::EXE::Main{0}{PrintConv}{$mach} || '';
     1176                    my $winType = $machine =~ /64/ ? 'Win64' : 'Win32';
     1177                    $ext = $flags & 0x2000 ? 'DLL' : 'EXE';
     1178                    $et->SetFileType("$winType $ext", undef, $ext);
     1179                    return 1 if $fast3;
    9781180                    # read the rest of the optional header if necessary
    9791181                    my $optSize = Get16u(\$buff, 20);
     
    9841186                            $size += $more;
    9851187                            my $magic = Get16u(\$buff, 24);
    986                             # verify PE32/PE32+ magic number
    987                             unless ($magic == 0x10b or $magic == 0x20b) {
    988                                 $exifTool->Warn('Unknown PE magic number');
     1188                            # verify PE magic number
     1189                            unless ($magic == 0x107 or $magic == 0x10b or $magic == 0x20b) {
     1190                                $et->Warn('Unknown PE magic number');
    9891191                                return 1;
    9901192                            }
     1193                            # --> 64-bit if $magic is 0x20b ????
    9911194                        } else {
    992                             $exifTool->Warn('Error reading optional header');
     1195                            $et->Warn('Error reading optional header');
    9931196                        }
    9941197                    }
     
    10001203                        DataLen => $size,
    10011204                        DirStart => 4,
    1002                         DirLen => $size,
     1205                        DirLen => $size - 4,
    10031206                    );
    1004                     $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
     1207                    $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
    10051208                    # process data dictionary
    10061209                    my $num = Get16u(\$buff, 6);    # NumberOfSections
     
    10101213                            DataPt => \$buff,
    10111214                        );
    1012                         ProcessPEDict($exifTool, \%dirInfo) or $exifTool->Warn('Error processing PE data dictionary');
     1215                        ProcessPEDict($et, \%dirInfo) or $et->Warn('Error processing PE data dictionary');
    10131216                    }
    10141217                    return 1;
     
    10161219            } else {
    10171220                $type = 'Virtual Device Driver';
     1221                $ext = '386';
    10181222            }
    10191223        } else {
    10201224            $type = 'DOS EXE';
     1225            $ext = 'exe';
    10211226        }
    10221227#
     
    10291234        if ($1 eq "\xca\xfe\xba\xbe") {
    10301235            SetByteOrder('MM');
    1031             $exifTool->SetFileType('Mach-O fat binary executable');
     1236            $et->SetFileType('Mach-O fat binary executable', undef, '');
     1237            return 1 if $fast3;
    10321238            my $count = Get32u(\$buff, 4);  # get architecture count
    10331239            my $more = $count * 20 - ($size - 8);
    10341240            if ($more > 0) {
    10351241                unless ($raf->Read($buf2, $more) == $more) {
    1036                     $exifTool->Warn('Error reading fat-arch headers');
     1242                    $et->Warn('Error reading fat-arch headers');
    10371243                    return 1;
    10381244                }
     
    10401246                $size += $more;
    10411247            }
    1042             $exifTool->HandleTag($tagTablePtr, 2, $count);
     1248            $et->HandleTag($tagTablePtr, 2, $count);
    10431249            my $i;
    10441250            for ($i=0; $i<$count; ++$i) {
    10451251                my $cpuType = Get32s(\$buff, 8 + $i * 20);
    1046                 my $cpuSubtype = Get32u(\$buff, 12 + $i * 20);
    1047                 $exifTool->HandleTag($tagTablePtr, 3, $cpuType);
    1048                 $exifTool->HandleTag($tagTablePtr, 4, "$cpuType $cpuSubtype");
     1252                my $subType = Get32s(\$buff, 12 + $i * 20);
     1253                $et->HandleTag($tagTablePtr, 3, $cpuType);
     1254                $et->HandleTag($tagTablePtr, 4, "$cpuType $subType");
    10491255            }
    10501256            # load first Mach-O header to get the object file type
     
    10531259                if ($buf2 =~ /^(\xfe\xed\xfa(\xce|\xcf)|(\xce|\xcf)\xfa\xed\xfe)/) {
    10541260                    SetByteOrder($buf2 =~ /^\xfe\xed/ ? 'MM' : 'II');
    1055                     my $objType = Get32s(\$buf2, 12);
    1056                     $exifTool->HandleTag($tagTablePtr, 5, $objType);
     1261                    my $objType = Get32u(\$buf2, 12);
     1262                    $et->HandleTag($tagTablePtr, 5, $objType);
     1263                    # override file type if this is a library or object file
     1264                    MachOverride($et, $objType, 'fat');
    10571265                } elsif ($buf2 =~ /^!<arch>\x0a/) {
    10581266                    # .a libraries use this magic number
    1059                     $exifTool->HandleTag($tagTablePtr, 5, -1);
     1267                    $et->HandleTag($tagTablePtr, 5, -1);
     1268                    # override file type since this is a library
     1269                    $et->OverrideFileType('Mach-O fat static library', undef, 'A');
    10601270                } else {
    1061                     $exifTool->Warn('Unrecognized object file type');
     1271                    $et->Warn('Unrecognized object file type');
    10621272                }
    10631273            } else {
    1064                 $exifTool->Warn('Error reading file');
     1274                $et->Warn('Error reading file');
    10651275            }
    1066        } elsif ($size >= 16) {
    1067             $exifTool->SetFileType('Mach-O executable');
    1068             my $info = {
    1069                 "\xfe\xed\xfa\xce" => ['32 bit', 'Big endian'],
    1070                 "\xce\xfa\xed\xfe" => ['32 bit', 'Little endian'],
    1071                 "\xfe\xed\xfa\xcf" => ['64 bit', 'Big endian'],
    1072                 "\xcf\xfa\xed\xfe" => ['64 bit', 'Little endian'],
    1073             }->{substr($buff, 0, 4)};
    1074             my $byteOrder = ($buff =~ /^\xfe/) ? 'MM' : 'II';
    1075             SetByteOrder($byteOrder);
    1076             my $cpuType = Get32s(\$buff, 4);
    1077             my $cpuSubtype = Get32s(\$buff, 8);
    1078             my $objType = Get32s(\$buff, 12);
    1079             $exifTool->HandleTag($tagTablePtr, 0, $$info[0]);
    1080             $exifTool->HandleTag($tagTablePtr, 1, $$info[1]);
    1081             $exifTool->HandleTag($tagTablePtr, 3, $cpuType);
    1082             $exifTool->HandleTag($tagTablePtr, 4, "$cpuType $cpuSubtype");
    1083             $exifTool->HandleTag($tagTablePtr, 5, $objType);
     1276        } elsif ($size >= 16) {
     1277            $et->SetFileType('Mach-O executable', undef, '');
     1278            return 1 if $fast3;
     1279            ExtractMachTags($et, \$buff, 1);
    10841280        }
    10851281        return 1;
     
    10891285    } elsif ($buff =~ /^Joy!peff/ and $size > 12) {
    10901286        # ref http://developer.apple.com/documentation/mac/pdf/MacOS_RT_Architectures.pdf
    1091         $exifTool->SetFileType('Classic MacOS executable');
     1287        $et->SetFileType('Classic MacOS executable', undef, '');
     1288        return 1 if $fast3;
    10921289        SetByteOrder('MM');
    10931290        $tagTablePtr = GetTagTable('Image::ExifTool::EXE::PEF');
     
    10991296            DirLen => $size,
    11001297        );
    1101         $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
     1298        $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
    11021299        return 1;
    11031300#
     
    11051302#
    11061303    } elsif ($buff =~ /^\x7fELF/ and $size >= 16) {
    1107         $exifTool->SetFileType("ELF executable");
     1304        $et->SetFileType('ELF executable', undef, '');
     1305        return 1 if $fast3;
    11081306        SetByteOrder(Get8u(\$buff,5) == 1 ? 'II' : 'MM');
    11091307        $tagTablePtr = GetTagTable('Image::ExifTool::EXE::ELF');
    11101308        %dirInfo = (
    1111             DataPt => \$buff,
     1309            DataPt  => \$buff,
    11121310            DataPos => 0,
    11131311            DataLen => $size,
    1114             DirStart => 0,
    1115             DirLen => $size,
     1312            DirLen  => $size,
    11161313        );
    1117         $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
     1314        $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
     1315        # override file type if this is a library or object file
     1316        my $override = {
     1317            1 => [ 'ELF object file', 'O' ],
     1318            3 => [ 'ELF shared library', 'SO' ],
     1319        }->{$$et{VALUE}{ObjectFileType} || 0};
     1320        $et->OverrideFileType($$override[0], undef, $$override[1]) if $override;
     1321        return 1;
     1322#
     1323# .a libraries
     1324#
     1325    } elsif ($buff =~ /^!<arch>\x0a/) {
     1326        $et->SetFileType('Static library', undef, 'A');
     1327        return 1 if $fast3;
     1328        my $pos = 8;    # current file position
     1329        my $max = 10;   # maximum number of archive files to check
     1330        # read into list of ar structures (each 60 bytes long):
     1331        while ($max-- > 0) {
     1332            # seek to start of the ar structure and read it
     1333            $raf->Seek($pos, 0) and $raf->Read($buff, 60) == 60 or last;
     1334            substr($buff, 58, 2) eq "`\n" or $et->Warn('Invalid archive header'), last;
     1335            unless ($tagTablePtr) {
     1336                # extract some information from first file in archive
     1337                $tagTablePtr = GetTagTable('Image::ExifTool::EXE::AR');
     1338                %dirInfo = (
     1339                    DataPt  => \$buff,
     1340                    DataPos => $pos,
     1341                );
     1342                $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
     1343            }
     1344            my $name = substr($buff, 0, 16);
     1345            if ($name =~ m{^#1/(\d+) *$}) { # check for extended archive (BSD variant)
     1346                my $len = $1;
     1347                $len > 256 and $et->Warn('Invalid extended archive name length'), last;
     1348                # (we read the name here just to move the file pointer)
     1349                $raf->Read($name, $len) == $len or $et->Warn('Error reading archive name'), last;
     1350            }
     1351            my $arSize = substr($buff, 48, 10);
     1352            $arSize =~ s/^(\d+).*/$1/s or last;     # make sure archive size is a number
     1353            $raf->Read($buff, 28) == 28 or last;    # read (possible) Mach header
     1354            ExtractMachTags($et, \$buff) and last;  # try to extract tags
     1355            $pos += 60 + $arSize;   # step to next entry
     1356            ++$pos if $pos & 0x01;  # padded to an even byte
     1357        }
    11181358        return 1;
    11191359#
     
    11211361#
    11221362    } elsif ($buff =~ m{^#!\s*/\S*bin/(\w+)}) {
    1123         $type = "$1 script";
    1124 #
    1125 # .a libraries
    1126 #
    1127     } elsif ($buff =~ /^!<arch>\x0a/) {
    1128         $type = 'Static library',
     1363        my $prog = $1;
     1364        $prog = $1 if $prog eq 'env' and $buff =~ /\b(perl|python|ruby|php)\b/;
     1365        $type = "$prog script";
     1366        $mime = "text/x-$prog";
     1367        $ext = {
     1368            perl   => 'pl',
     1369            python => 'py',
     1370            ruby   => 'rb',
     1371            php    => 'php',
     1372        }->{$prog};
     1373        # use '.sh' for extension of all shell scripts
     1374        $ext = $prog =~ /sh$/ ? 'sh' : '' unless defined $ext;
    11291375    }
    11301376    return 0 unless $type;
    1131     $exifTool->SetFileType($type);
     1377    $et->SetFileType($type, $mime, $ext);
    11321378    return 1;
    11331379}
     
    11531399=head1 AUTHOR
    11541400
    1155 Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
     1401Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
    11561402
    11571403This library is free software; you can redistribute it and/or modify it
Note: See TracChangeset for help on using the changeset viewer.