1#!/usr/bin/perl
2###########################################################################
3# ABI Compliance Checker (ABICC) 1.99.26
4# A tool for checking backward compatibility of a C/C++ library API
5#
6# Copyright (C) 2009-2011 Institute for System Programming, RAS
7# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies)
8# Copyright (C) 2011-2012 ROSA Laboratory
9# Copyright (C) 2012-2016 Andrey Ponomarenko's ABI Laboratory
10#
11# Written by Andrey Ponomarenko
12#
13# PLATFORMS
14# =========
15#  Linux, FreeBSD, Mac OS X, Haiku, MS Windows, Symbian
16#
17# REQUIREMENTS
18# ============
19#  Linux
20#    - G++ (3.0-4.7, 4.8.3, 4.9 or newer)
21#    - GNU Binutils (readelf, c++filt, objdump)
22#    - Perl 5
23#    - Ctags
24#    - ABI Dumper >= 0.99.15
25#
26#  Mac OS X
27#    - Xcode (g++, c++filt, otool, nm)
28#    - Ctags
29#
30#  MS Windows
31#    - MinGW (3.0-4.7, 4.8.3, 4.9 or newer)
32#    - MS Visual C++ (dumpbin, undname, cl)
33#    - Active Perl 5 (5.8 or newer)
34#    - Sigcheck v2.52 or newer
35#    - GnuWin Zip and UnZip
36#    - Ctags (Exuberant or Universal)
37#    - Add tool locations to the PATH environment variable
38#    - Run vcvars64.bat (C:\Microsoft Visual Studio 9.0\VC\bin\)
39#
40# This program is free software: you can redistribute it and/or modify
41# it under the terms of the GNU General Public License or the GNU Lesser
42# General Public License as published by the Free Software Foundation.
43#
44# This program is distributed in the hope that it will be useful,
45# but WITHOUT ANY WARRANTY; without even the implied warranty of
46# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
47# GNU General Public License for more details.
48#
49# You should have received a copy of the GNU General Public License
50# and the GNU Lesser General Public License along with this program.
51# If not, see <http://www.gnu.org/licenses/>.
52###########################################################################
53use Getopt::Long;
54Getopt::Long::Configure ("posix_default", "no_ignore_case");
55use File::Path qw(mkpath rmtree);
56use File::Temp qw(tempdir);
57use File::Copy qw(copy move);
58use Cwd qw(abs_path cwd realpath);
59use Storable qw(dclone);
60use Data::Dumper;
61use Config;
62
63my $TOOL_VERSION = "1.99.26";
64my $ABI_DUMP_VERSION = "3.3";
65my $XML_REPORT_VERSION = "1.2";
66my $XML_ABI_DUMP_VERSION = "1.2";
67my $OSgroup = get_OSgroup();
68my $ORIG_DIR = cwd();
69my $TMP_DIR = tempdir(CLEANUP=>1);
70my $LOCALE = "C.UTF-8";
71
72# Internal modules
73my $MODULES_DIR = get_Modules();
74push(@INC, get_dirname($MODULES_DIR));
75# Rules DB
76my %RULES_PATH = (
77    "Binary" => $MODULES_DIR."/RulesBin.xml",
78    "Source" => $MODULES_DIR."/RulesSrc.xml");
79
80my ($Help, $ShowVersion, %Descriptor, $TargetLibraryName,
81$TestTool, $DumpAPI, $SymbolsListPath, $CheckHeadersOnly_Opt, $UseDumps,
82$AppPath, $StrictCompat, $DumpVersion, $ParamNamesPath,
83%RelativeDirectory, $TargetTitle, $TestDump, $LoggingPath,
84%TargetVersion, $InfoMsg, $CrossGcc, %OutputLogPath,
85$OutputReportPath, $OutputDumpPath, $ShowRetVal, $SystemRoot_Opt, $DumpSystem,
86$CmpSystems, $TargetLibsPath, $Debug, $CrossPrefix, $UseStaticLibs, $NoStdInc,
87$TargetComponent_Opt, $TargetSysInfo, $TargetHeader, $ExtendedCheck, $Quiet,
88$SkipHeadersPath, $CxxCompat, $LogMode, $StdOut, $ListAffected, $ReportFormat,
89$UserLang, $TargetHeadersPath, $BinaryOnly, $SourceOnly, $BinaryReportPath,
90$SourceReportPath, $UseXML, $SortDump, $DumpFormat,
91$ExtraInfo, $ExtraDump, $Force, $Tolerance, $Tolerant, $SkipSymbolsListPath,
92$CheckInfo, $Quick, $AffectLimit, $AllAffected, $CxxIncompat,
93$SkipInternalSymbols, $SkipInternalTypes, $TargetArch, $GccOptions,
94$TypesListPath, $SkipTypesListPath, $CheckPrivateABI, $CountSymbols, $OldStyle,
95$DisableQuickEmptyReport, $SkipTypedefUncover, $MinGWCompat, $SkipUnidentified,
96$DisableConstantsCheck, $SkipAddedConstants, $SkipRemovedConstants, $TestABIDumper);
97
98my $CmdName = get_filename($0);
99my %OS_LibExt = (
100    "dynamic" => {
101        "linux"=>"so",
102        "macos"=>"dylib",
103        "windows"=>"dll",
104        "symbian"=>"dso",
105        "default"=>"so"
106    },
107    "static" => {
108        "linux"=>"a",
109        "windows"=>"lib",
110        "symbian"=>"lib",
111        "default"=>"a"
112    }
113);
114
115my %OS_Archive = (
116    "windows"=>"zip",
117    "default"=>"tar.gz"
118);
119
120my %ERROR_CODE = (
121    # Compatible verdict
122    "Compatible"=>0,
123    "Success"=>0,
124    # Incompatible verdict
125    "Incompatible"=>1,
126    # Undifferentiated error code
127    "Error"=>2,
128    # System command is not found
129    "Not_Found"=>3,
130    # Cannot access input files
131    "Access_Error"=>4,
132    # Cannot compile header files
133    "Cannot_Compile"=>5,
134    # Header compiled with errors
135    "Compile_Error"=>6,
136    # Invalid input ABI dump
137    "Invalid_Dump"=>7,
138    # Incompatible version of ABI dump
139    "Dump_Version"=>8,
140    # Cannot find a module
141    "Module_Error"=>9,
142    # Empty intersection between
143    # headers and shared objects
144    "Empty_Intersection"=>10,
145    # Empty set of symbols in headers
146    "Empty_Set"=>11
147);
148
149my %HomePage = (
150    "Dev"=>"https://github.com/lvc/abi-compliance-checker",
151    "Wiki"=>"https://lvc.github.io/abi-compliance-checker/"
152);
153
154my $ShortUsage = "ABI Compliance Checker (ABICC) $TOOL_VERSION
155A tool for checking backward compatibility of a C/C++ library API
156Copyright (C) 2016 Andrey Ponomarenko's ABI Laboratory
157License: GNU LGPL or GNU GPL
158
159Usage: $CmdName [options]
160Example: $CmdName -lib NAME -old OLD.xml -new NEW.xml
161
162OLD.xml and NEW.xml are XML-descriptors:
163
164    <version>
165        1.0
166    </version>
167
168    <headers>
169        /path/to/headers/
170    </headers>
171
172    <libs>
173        /path/to/libraries/
174    </libs>
175
176More info: $CmdName --help\n";
177
178if($#ARGV==-1)
179{
180    printMsg("INFO", $ShortUsage);
181    exit(0);
182}
183
184GetOptions("h|help!" => \$Help,
185  "i|info!" => \$InfoMsg,
186  "v|version!" => \$ShowVersion,
187  "dumpversion!" => \$DumpVersion,
188# general options
189  "l|lib|library=s" => \$TargetLibraryName,
190  "d1|old|o=s" => \$Descriptor{1}{"Path"},
191  "d2|new|n=s" => \$Descriptor{2}{"Path"},
192  "dump|dump-abi|dump_abi=s" => \$DumpAPI,
193# extra options
194  "app|application=s" => \$AppPath,
195  "static|static-libs!" => \$UseStaticLibs,
196  "gcc-path|cross-gcc=s" => \$CrossGcc,
197  "gcc-prefix|cross-prefix=s" => \$CrossPrefix,
198  "gcc-options=s" => \$GccOptions,
199  "sysroot=s" => \$SystemRoot_Opt,
200  "v1|vnum1|version1|vnum=s" => \$TargetVersion{1},
201  "v2|vnum2|version2=s" => \$TargetVersion{2},
202  "s|strict!" => \$StrictCompat,
203  "symbols-list=s" => \$SymbolsListPath,
204  "types-list=s" => \$TypesListPath,
205  "skip-symbols=s" => \$SkipSymbolsListPath,
206  "skip-types=s" => \$SkipTypesListPath,
207  "disable-constants-check!" => \$DisableConstantsCheck,
208  "skip-added-constants!" => \$SkipAddedConstants,
209  "skip-removed-constants!" => \$SkipRemovedConstants,
210  "headers-list=s" => \$TargetHeadersPath,
211  "skip-headers=s" => \$SkipHeadersPath,
212  "header=s" => \$TargetHeader,
213  "headers-only|headers_only!" => \$CheckHeadersOnly_Opt,
214  "show-retval!" => \$ShowRetVal,
215  "use-dumps!" => \$UseDumps,
216  "nostdinc!" => \$NoStdInc,
217  "dump-system=s" => \$DumpSystem,
218  "sysinfo=s" => \$TargetSysInfo,
219  "cmp-systems!" => \$CmpSystems,
220  "libs-list=s" => \$TargetLibsPath,
221  "ext|extended!" => \$ExtendedCheck,
222  "q|quiet!" => \$Quiet,
223  "stdout!" => \$StdOut,
224  "report-format=s" => \$ReportFormat,
225  "dump-format=s" => \$DumpFormat,
226  "xml!" => \$UseXML,
227  "lang=s" => \$UserLang,
228  "arch=s" => \$TargetArch,
229  "binary|bin|abi!" => \$BinaryOnly,
230  "source|src|api!" => \$SourceOnly,
231  "limit-affected|affected-limit=s" => \$AffectLimit,
232  "count-symbols=s" => \$CountSymbols,
233  "old-style!" => \$OldStyle,
234# other options
235  "test!" => \$TestTool,
236  "test-dump!" => \$TestDump,
237  "test-abi-dumper!" => \$TestABIDumper,
238  "debug!" => \$Debug,
239  "cpp-compatible!" => \$CxxCompat,
240  "cxx-incompatible|cpp-incompatible!" => \$CxxIncompat,
241  "mingw-compatible!" => \$MinGWCompat,
242  "p|params=s" => \$ParamNamesPath,
243  "relpath1|relpath=s" => \$RelativeDirectory{1},
244  "relpath2=s" => \$RelativeDirectory{2},
245  "dump-path=s" => \$OutputDumpPath,
246  "sort!" => \$SortDump,
247  "report-path=s" => \$OutputReportPath,
248  "bin-report-path=s" => \$BinaryReportPath,
249  "src-report-path=s" => \$SourceReportPath,
250  "log-path=s" => \$LoggingPath,
251  "log1-path=s" => \$OutputLogPath{1},
252  "log2-path=s" => \$OutputLogPath{2},
253  "logging-mode=s" => \$LogMode,
254  "list-affected!" => \$ListAffected,
255  "title|l-full|lib-full=s" => \$TargetTitle,
256  "component=s" => \$TargetComponent_Opt,
257  "extra-info=s" => \$ExtraInfo,
258  "extra-dump!" => \$ExtraDump,
259  "force!" => \$Force,
260  "tolerance=s" => \$Tolerance,
261  "tolerant!" => \$Tolerant,
262  "skip-unidentified!" => \$SkipUnidentified,
263  "check!" => \$CheckInfo,
264  "quick!" => \$Quick,
265  "disable-quick-empty-report!" => \$DisableQuickEmptyReport,
266  "all-affected!" => \$AllAffected,
267  "skip-internal-symbols|skip-internal=s" => \$SkipInternalSymbols,
268  "skip-internal-types=s" => \$SkipInternalTypes,
269  "skip-typedef-uncover!" => \$SkipTypedefUncover,
270  "check-private-abi!" => \$CheckPrivateABI
271) or ERR_MESSAGE();
272
273sub ERR_MESSAGE()
274{
275    printMsg("INFO", "\n".$ShortUsage);
276    exit($ERROR_CODE{"Error"});
277}
278
279my $LIB_TYPE = $UseStaticLibs?"static":"dynamic";
280my $SLIB_TYPE = $LIB_TYPE;
281if($OSgroup!~/macos|windows/ and $SLIB_TYPE eq "dynamic")
282{ # show as "shared" library
283    $SLIB_TYPE = "shared";
284}
285my $LIB_EXT = getLIB_EXT($OSgroup);
286my $AR_EXT = getAR_EXT($OSgroup);
287my $BYTE_SIZE = 8;
288my $COMMON_LOG_PATH = "logs/run.log";
289
290my $HelpMessage="
291NAME:
292  ABI Compliance Checker ($CmdName)
293  Check backward compatibility of a C/C++ library API
294
295DESCRIPTION:
296  ABI Compliance Checker (ABICC) is a tool for checking backward binary and
297  source-level compatibility of a $SLIB_TYPE C/C++ library. The tool checks
298  header files and $SLIB_TYPE libraries (*.$LIB_EXT) of old and new versions and
299  analyzes changes in API and ABI (ABI=API+compiler ABI) that may break binary
300  and/or source-level compatibility: changes in calling stack, v-table changes,
301  removed symbols, renamed fields, etc. Binary incompatibility may result in
302  crashing or incorrect behavior of applications built with an old version of
303  a library if they run on a new one. Source incompatibility may result in
304  recompilation errors with a new library version.
305
306  The tool is intended for developers of software libraries and maintainers
307  of operating systems who are interested in ensuring backward compatibility,
308  i.e. allow old applications to run or to be recompiled with newer library
309  versions.
310
311  Also the tool can be used by ISVs for checking applications portability to
312  new library versions. Found issues can be taken into account when adapting
313  the application to a new library version.
314
315  This tool is free software: you can redistribute it and/or modify it
316  under the terms of the GNU LGPL or GNU GPL.
317
318USAGE:
319  $CmdName [options]
320
321EXAMPLE:
322  $CmdName -lib NAME -old OLD.xml -new NEW.xml
323
324  OLD.xml and NEW.xml are XML-descriptors:
325
326    <version>
327        1.0
328    </version>
329
330    <headers>
331        /path1/to/header(s)/
332        /path2/to/header(s)/
333         ...
334    </headers>
335
336    <libs>
337        /path1/to/library(ies)/
338        /path2/to/library(ies)/
339         ...
340    </libs>
341
342INFORMATION OPTIONS:
343  -h|-help
344      Print this help.
345
346  -i|-info
347      Print complete info.
348
349  -v|-version
350      Print version information.
351
352  -dumpversion
353      Print the tool version ($TOOL_VERSION) and don't do anything else.
354
355GENERAL OPTIONS:
356  -l|-lib|-library NAME
357      Library name (without version).
358
359  -d1|-old|-o PATH
360      Descriptor of 1st (old) library version.
361      It may be one of the following:
362
363         1. XML-descriptor (VERSION.xml file):
364
365              <version>
366                  1.0
367              </version>
368
369              <headers>
370                  /path1/to/header(s)/
371                  /path2/to/header(s)/
372                   ...
373              </headers>
374
375              <libs>
376                  /path1/to/library(ies)/
377                  /path2/to/library(ies)/
378                   ...
379              </libs>
380
381                 ...
382
383         2. ABI dump generated by -dump option
384         3. Directory with headers and/or $SLIB_TYPE libraries
385         4. Single header file
386
387      If you are using an 2-4 descriptor types then you should
388      specify version numbers with -v1 and -v2 options too.
389
390      For more information, please see:
391        http://ispras.linuxbase.org/index.php/Library_Descriptor
392
393  -d2|-new|-n PATH
394      Descriptor of 2nd (new) library version.
395
396  -dump|-dump-abi PATH
397      Create library ABI dump for the input XML descriptor. You can
398      transfer it anywhere and pass instead of the descriptor. Also
399      it can be used for debugging the tool.
400
401      Supported versions of ABI dump: 2.0<=V<=$ABI_DUMP_VERSION\n";
402
403sub HELP_MESSAGE() {
404    printMsg("INFO", $HelpMessage."
405MORE INFO:
406     $CmdName --info\n");
407}
408
409sub INFO_MESSAGE()
410{
411    printMsg("INFO", "$HelpMessage
412EXTRA OPTIONS:
413  -app|-application PATH
414      This option allows to specify the application that should be checked
415      for portability to the new library version.
416
417  -static
418      Check static libraries instead of the shared ones. The <libs> section
419      of the XML-descriptor should point to static libraries location.
420
421  -gcc-path PATH
422      Path to the cross GCC compiler to use instead of the usual (host) GCC.
423
424  -gcc-prefix PREFIX
425      GCC toolchain prefix.
426
427  -gcc-options OPTS
428      Additional compiler options.
429
430  -sysroot DIR
431      Specify the alternative root directory. The tool will search for include
432      paths in the DIR/usr/include and DIR/usr/lib directories.
433
434  -v1|-version1 NUM
435      Specify 1st library version outside the descriptor. This option is needed
436      if you have preferred an alternative descriptor type (see -d1 option).
437
438      In general case you should specify it in the XML-descriptor:
439          <version>
440              VERSION
441          </version>
442
443  -v2|-version2 NUM
444      Specify 2nd library version outside the descriptor.
445
446  -vnum NUM
447      Specify the library version in the generated ABI dump. The <version> section
448      of the input XML descriptor will be overwritten in this case.
449
450  -s|-strict
451      Treat all compatibility warnings as problems. Add a number of \"Low\"
452      severity problems to the return value of the tool.
453
454  -headers-only
455      Check header files without $SLIB_TYPE libraries. It is easy to run, but may
456      provide a low quality compatibility report with false positives and
457      without detecting of added/removed symbols.
458
459      Alternatively you can write \"none\" word to the <libs> section
460      in the XML-descriptor:
461          <libs>
462              none
463          </libs>
464
465  -show-retval
466      Show the symbol's return type in the report.
467
468  -symbols-list PATH
469      This option allows to specify a file with a list of symbols (mangled
470      names in C++) that should be checked. Other symbols will not be checked.
471
472  -types-list PATH
473      This option allows to specify a file with a list of types that should
474      be checked. Other types will not be checked.
475
476  -skip-symbols PATH
477      The list of symbols that should not be checked.
478
479  -skip-types PATH
480      The list of types that should not be checked.
481
482  -disable-constants-check
483      Do not check for changes in constants.
484
485  -skip-added-constants
486      Do not detect added constants.
487
488  -skip-removed-constants
489      Do not detect removed constants.
490
491  -headers-list PATH
492      The file with a list of headers, that should be checked/dumped.
493
494  -skip-headers PATH
495      The file with the list of header files, that should not be checked.
496
497  -header NAME
498      Check/Dump ABI of this header only.
499
500  -use-dumps
501      Make dumps for two versions of a library and compare dumps. This should
502      increase the performance of the tool and decrease the system memory usage.
503
504  -nostdinc
505      Do not search in GCC standard system directories for header files.
506
507  -dump-system NAME -sysroot DIR
508      Find all the shared libraries and header files in DIR directory,
509      create XML descriptors and make ABI dumps for each library. The result
510      set of ABI dumps can be compared (--cmp-systems) with the other one
511      created for other version of operating system in order to check them for
512      compatibility. Do not forget to specify -cross-gcc option if your target
513      system requires some specific version of GCC compiler (different from
514      the host GCC). The system ABI dump will be generated to:
515          sys_dumps/NAME/ARCH
516
517  -dump-system DESCRIPTOR.xml
518      The same as the previous option but takes an XML descriptor of the target
519      system as input, where you should describe it:
520
521          /* Primary sections */
522
523          <name>
524              /* Name of the system */
525          </name>
526
527          <headers>
528              /* The list of paths to header files and/or
529                 directories with header files, one per line */
530          </headers>
531
532          <libs>
533              /* The list of paths to shared libraries and/or
534                 directories with shared libraries, one per line */
535          </libs>
536
537          /* Optional sections */
538
539          <search_headers>
540              /* List of directories to be searched
541                 for header files to automatically
542                 generate include paths, one per line */
543          </search_headers>
544
545          <search_libs>
546              /* List of directories to be searched
547                 for shared libraries to resolve
548                 dependencies, one per line */
549          </search_libs>
550
551          <tools>
552              /* List of directories with tools used
553                 for analysis (GCC toolchain), one per line */
554          </tools>
555
556          <cross_prefix>
557              /* GCC toolchain prefix.
558                 Examples:
559                     arm-linux-gnueabi
560                     arm-none-symbianelf */
561          </cross_prefix>
562
563          <gcc_options>
564              /* Additional GCC options, one per line */
565          </gcc_options>
566
567  -sysinfo DIR
568      This option should be used with -dump-system option to dump
569      ABI of operating systems and configure the dumping process.
570
571  -cmp-systems -d1 sys_dumps/NAME1/ARCH -d2 sys_dumps/NAME2/ARCH
572      Compare two ABI dumps of a system. Create compatibility reports for
573      each system library and the common HTML report including the summary
574      of test results for all checked libraries.
575
576      Summary report will be generated to:
577          sys_compat_reports/NAME1_to_NAME2/ARCH
578
579  -libs-list PATH
580      The file with a list of libraries, that should be dumped by
581      the -dump-system option or should be checked by the -cmp-systems option.
582
583  -ext|-extended
584      If your library A is supposed to be used by other library B and you
585      want to control the ABI of B, then you should enable this option. The
586      tool will check for changes in all data types, even if they are not
587      used by any function in the library A. Such data types are not part
588      of the A library ABI, but may be a part of the ABI of the B library.
589
590      The short scheme is:
591        app C (broken) -> lib B (broken ABI) -> lib A (stable ABI)
592
593  -q|-quiet
594      Print all messages to the file instead of stdout and stderr.
595      Default path (can be changed by -log-path option):
596          $COMMON_LOG_PATH
597
598  -stdout
599      Print analysis results (compatibility reports and ABI dumps) to stdout
600      instead of creating a file. This would allow piping data to other programs.
601
602  -report-format FMT
603      Change format of compatibility report.
604      Formats:
605        htm - HTML format (default)
606        xml - XML format
607
608  -dump-format FMT
609      Change format of ABI dump.
610      Formats:
611        perl - Data::Dumper format (default)
612        xml - XML format
613
614  -xml
615      Alias for: --report-format=xml or --dump-format=xml
616
617  -lang LANG
618      Set library language (C or C++). You can use this option if the tool
619      cannot auto-detect a language. This option may be useful for checking
620      C-library headers (--lang=C) in --headers-only or --extended modes.
621
622  -arch ARCH
623      Set library architecture (x86, x86_64, ia64, arm, ppc32, ppc64, s390,
624      ect.). The option is useful if the tool cannot detect correct architecture
625      of the input objects.
626
627  -binary|-bin|-abi
628      Show \"Binary\" compatibility problems only.
629      Generate report to:
630        compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
631
632  -source|-src|-api
633      Show \"Source\" compatibility problems only.
634      Generate report to:
635        compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
636
637  -limit-affected LIMIT
638      The maximum number of affected symbols listed under the description
639      of the changed type in the report.
640
641  -count-symbols PATH
642      Count total public symbols in the ABI dump.
643
644  -old-style
645      Generate old-style report.
646
647OTHER OPTIONS:
648  -test
649      Run internal tests. Create two binary incompatible versions of a sample
650      library and run the tool to check them for compatibility. This option
651      allows to check if the tool works correctly in the current environment.
652
653  -test-dump
654      Test ability to create, read and compare ABI dumps.
655
656  -test-abi-dumper
657      Compare ABI dumps created by the ABI Dumper tool.
658
659  -debug
660      Debugging mode. Print debug info on the screen. Save intermediate
661      analysis stages in the debug directory:
662          debug/LIB_NAME/VERSION/
663
664      Also consider using -dump option for debugging the tool.
665
666  -cpp-compatible
667      Do nothing.
668
669  -cxx-incompatible
670      Set this option if input C header files use C++ keywords. The tool
671      will try to replace such keywords at preprocessor stage and replace
672      them back in the final TU dump.
673
674  -mingw-compatible
675      If input header files are compatible with the MinGW GCC compiler,
676      then you can tell the tool about this and speedup the analysis.
677
678  -p|-params PATH
679      Path to file with the function parameter names. It can be used
680      for improving report view if the library header files have no
681      parameter names. File format:
682
683            func1;param1;param2;param3 ...
684            func2;param1;param2;param3 ...
685             ...
686
687  -relpath PATH
688      Replace {RELPATH} macros to PATH in the XML-descriptor used
689      for dumping the library ABI (see -dump option).
690
691  -relpath1 PATH
692      Replace {RELPATH} macros to PATH in the 1st XML-descriptor (-d1).
693
694  -relpath2 PATH
695      Replace {RELPATH} macros to PATH in the 2nd XML-descriptor (-d2).
696
697  -dump-path PATH
698      Specify a *.abi.$AR_EXT or *.abi file path where to generate an ABI dump.
699      Default:
700          abi_dumps/LIB_NAME/LIB_NAME_VERSION.abi.$AR_EXT
701
702  -sort
703      Enable sorting of data in ABI dumps.
704
705  -report-path PATH
706      Path to compatibility report.
707      Default:
708          compat_reports/LIB_NAME/V1_to_V2/compat_report.html
709
710  -bin-report-path PATH
711      Path to \"Binary\" compatibility report.
712      Default:
713          compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
714
715  -src-report-path PATH
716      Path to \"Source\" compatibility report.
717      Default:
718          compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
719
720  -log-path PATH
721      Log path for all messages.
722      Default:
723          logs/LIB_NAME/VERSION/log.txt
724
725  -log1-path PATH
726      Log path for 1st version of a library.
727      Default:
728          logs/LIB_NAME/V1/log.txt
729
730  -log2-path PATH
731      Log path for 2nd version of a library.
732      Default:
733          logs/LIB_NAME/V2/log.txt
734
735  -logging-mode MODE
736      Change logging mode.
737      Modes:
738        w - overwrite old logs (default)
739        a - append old logs
740        n - do not write any logs
741
742  -list-affected
743      Generate file with the list of incompatible
744      symbols beside the HTML compatibility report.
745      Use 'c++filt \@file' command from GNU binutils
746      to unmangle C++ symbols in the generated file.
747      Default names:
748          abi_affected.txt
749          src_affected.txt
750
751  -component NAME
752      The component name in the title and summary of the HTML report.
753      Default:
754          library
755
756  -title NAME
757      Change library name in the report title to NAME. By default
758      will be displayed a name specified by -l option.
759
760  -extra-info DIR
761      Dump extra info to DIR.
762
763  -extra-dump
764      Create extended ABI dump containing all symbols
765      from the translation unit.
766
767  -force
768      Try to enable this option if the tool checked not all
769      types and symbols in header files.
770
771  -tolerance LEVEL
772      Apply a set of heuristics to successfully compile input
773      header files. You can enable several tolerance levels by
774      joining them into one string (e.g. 13, 124, etc.).
775      Levels:
776          1 - skip non-Linux headers (e.g. win32_*.h, etc.)
777          2 - skip internal headers (e.g. *_p.h, impl/*.h, etc.)
778          3 - skip headers that include non-Linux headers
779          4 - skip headers included by others
780
781  -tolerant
782      Enable highest tolerance level [1234].
783
784  -skip-unidentified
785      Skip header files in 'headers' and 'include_preamble' sections
786      of the XML descriptor that cannot be found. This is useful if
787      you are trying to use the same descriptor for different targets.
788
789  -check
790      Check completeness of the ABI dump.
791
792  -quick
793      Quick analysis. Disable check of some template instances.
794
795  -disable-quick-empty-report
796      Do not generate quick empty report if input ABI dumps are equal.
797
798  -skip-internal-symbols PATTERN
799      Do not check symbols matched by the pattern.
800
801  -skip-internal-types PATTERN
802      Do not check types matched by the pattern.
803
804  -skip-typedef-uncover
805      Do not report a problem if type is covered or
806      uncovered by typedef (useful for broken debug info).
807
808  -check-private-abi
809      Check data types from the private part of the ABI when
810      comparing ABI dumps created by the ABI Dumper tool with
811      use of the -public-headers option.
812
813      Requires ABI Dumper >= 0.99.14
814
815REPORT:
816    Compatibility report will be generated to:
817        compat_reports/LIB_NAME/V1_to_V2/compat_report.html
818
819    Log will be generated to:
820        logs/LIB_NAME/V1/log.txt
821        logs/LIB_NAME/V2/log.txt
822
823EXIT CODES:
824    0 - Compatible. The tool has run without any errors.
825    non-zero - Incompatible or the tool has run with errors.
826
827MORE INFORMATION:
828    ".$HomePage{"Wiki"}."
829    ".$HomePage{"Dev"}."\n\n");
830}
831
832my %Operator_Indication = (
833    "not" => "~",
834    "assign" => "=",
835    "andassign" => "&=",
836    "orassign" => "|=",
837    "xorassign" => "^=",
838    "or" => "|",
839    "xor" => "^",
840    "addr" => "&",
841    "and" => "&",
842    "lnot" => "!",
843    "eq" => "==",
844    "ne" => "!=",
845    "lt" => "<",
846    "lshift" => "<<",
847    "lshiftassign" => "<<=",
848    "rshiftassign" => ">>=",
849    "call" => "()",
850    "mod" => "%",
851    "modassign" => "%=",
852    "subs" => "[]",
853    "land" => "&&",
854    "lor" => "||",
855    "rshift" => ">>",
856    "ref" => "->",
857    "le" => "<=",
858    "deref" => "*",
859    "mult" => "*",
860    "preinc" => "++",
861    "delete" => " delete",
862    "vecnew" => " new[]",
863    "vecdelete" => " delete[]",
864    "predec" => "--",
865    "postinc" => "++",
866    "postdec" => "--",
867    "plusassign" => "+=",
868    "plus" => "+",
869    "minus" => "-",
870    "minusassign" => "-=",
871    "gt" => ">",
872    "ge" => ">=",
873    "new" => " new",
874    "multassign" => "*=",
875    "divassign" => "/=",
876    "div" => "/",
877    "neg" => "-",
878    "pos" => "+",
879    "memref" => "->*",
880    "compound" => "," );
881
882my %UnknownOperator;
883
884my %NodeType= (
885  "array_type" => "Array",
886  "binfo" => "Other",
887  "boolean_type" => "Intrinsic",
888  "complex_type" => "Intrinsic",
889  "const_decl" => "Other",
890  "enumeral_type" => "Enum",
891  "field_decl" => "Other",
892  "function_decl" => "Other",
893  "function_type" => "FunctionType",
894  "identifier_node" => "Other",
895  "integer_cst" => "Other",
896  "integer_type" => "Intrinsic",
897  "vector_type" => "Vector",
898  "method_type" => "MethodType",
899  "namespace_decl" => "Other",
900  "parm_decl" => "Other",
901  "pointer_type" => "Pointer",
902  "real_cst" => "Other",
903  "real_type" => "Intrinsic",
904  "record_type" => "Struct",
905  "reference_type" => "Ref",
906  "string_cst" => "Other",
907  "template_decl" => "Other",
908  "template_type_parm" => "TemplateParam",
909  "typename_type" => "TypeName",
910  "sizeof_expr" => "SizeOf",
911  "tree_list" => "Other",
912  "tree_vec" => "Other",
913  "type_decl" => "Other",
914  "union_type" => "Union",
915  "var_decl" => "Other",
916  "void_type" => "Intrinsic",
917  "nop_expr" => "Other", #
918  "addr_expr" => "Other", #
919  "offset_type" => "Other" );
920
921my %CppKeywords_C = map {$_=>1} (
922    # C++ 2003 keywords
923    "public",
924    "protected",
925    "private",
926    "default",
927    "template",
928    "new",
929    #"asm",
930    "dynamic_cast",
931    "auto",
932    "try",
933    "namespace",
934    "typename",
935    "using",
936    "reinterpret_cast",
937    "friend",
938    "class",
939    "virtual",
940    "const_cast",
941    "mutable",
942    "static_cast",
943    "export",
944    # C++0x keywords
945    "noexcept",
946    "nullptr",
947    "constexpr",
948    "static_assert",
949    "explicit",
950    # cannot be used as a macro name
951    # as it is an operator in C++
952    "and",
953    #"and_eq",
954    "not",
955    #"not_eq",
956    "or"
957    #"or_eq",
958    #"bitand",
959    #"bitor",
960    #"xor",
961    #"xor_eq",
962    #"compl"
963);
964
965my %CppKeywords_F = map {$_=>1} (
966    "delete",
967    "catch",
968    "alignof",
969    "thread_local",
970    "decltype",
971    "typeid"
972);
973
974my %CppKeywords_O = map {$_=>1} (
975    "bool",
976    "register",
977    "inline",
978    "operator"
979);
980
981my %CppKeywords_A = map {$_=>1} (
982    "this",
983    "throw",
984    "template"
985);
986
987foreach (keys(%CppKeywords_C),
988keys(%CppKeywords_F),
989keys(%CppKeywords_O)) {
990    $CppKeywords_A{$_}=1;
991}
992
993# Header file extensions as described by gcc
994my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+";
995
996my %IntrinsicMangling = (
997    "void" => "v",
998    "bool" => "b",
999    "wchar_t" => "w",
1000    "char" => "c",
1001    "signed char" => "a",
1002    "unsigned char" => "h",
1003    "short" => "s",
1004    "unsigned short" => "t",
1005    "int" => "i",
1006    "unsigned int" => "j",
1007    "long" => "l",
1008    "unsigned long" => "m",
1009    "long long" => "x",
1010    "__int64" => "x",
1011    "unsigned long long" => "y",
1012    "__int128" => "n",
1013    "unsigned __int128" => "o",
1014    "float" => "f",
1015    "double" => "d",
1016    "long double" => "e",
1017    "__float80" => "e",
1018    "__float128" => "g",
1019    "..." => "z"
1020);
1021
1022my %IntrinsicNames = map {$_=>1} keys(%IntrinsicMangling);
1023
1024my %StdcxxMangling = (
1025    "3std"=>"St",
1026    "3std9allocator"=>"Sa",
1027    "3std12basic_string"=>"Sb",
1028    "3std12basic_stringIcE"=>"Ss",
1029    "3std13basic_istreamIcE"=>"Si",
1030    "3std13basic_ostreamIcE"=>"So",
1031    "3std14basic_iostreamIcE"=>"Sd"
1032);
1033
1034my $DEFAULT_STD_PARMS = "std::(allocator|less|char_traits|regex_traits|istreambuf_iterator|ostreambuf_iterator)";
1035my %DEFAULT_STD_ARGS = map {$_=>1} ("_Alloc", "_Compare", "_Traits", "_Rx_traits", "_InIter", "_OutIter");
1036
1037my $ADD_TMPL_INSTANCES = 1;
1038my $GCC_MISSED_MNGL = 0;
1039
1040my %ConstantSuffix = (
1041    "unsigned int"=>"u",
1042    "long"=>"l",
1043    "unsigned long"=>"ul",
1044    "long long"=>"ll",
1045    "unsigned long long"=>"ull"
1046);
1047
1048my %ConstantSuffixR =
1049reverse(%ConstantSuffix);
1050
1051my %OperatorMangling = (
1052    "~" => "co",
1053    "=" => "aS",
1054    "|" => "or",
1055    "^" => "eo",
1056    "&" => "an",#ad (addr)
1057    "==" => "eq",
1058    "!" => "nt",
1059    "!=" => "ne",
1060    "<" => "lt",
1061    "<=" => "le",
1062    "<<" => "ls",
1063    "<<=" => "lS",
1064    ">" => "gt",
1065    ">=" => "ge",
1066    ">>" => "rs",
1067    ">>=" => "rS",
1068    "()" => "cl",
1069    "%" => "rm",
1070    "[]" => "ix",
1071    "&&" => "aa",
1072    "||" => "oo",
1073    "*" => "ml",#de (deref)
1074    "++" => "pp",#
1075    "--" => "mm",#
1076    "new" => "nw",
1077    "delete" => "dl",
1078    "new[]" => "na",
1079    "delete[]" => "da",
1080    "+=" => "pL",
1081    "+" => "pl",#ps (pos)
1082    "-" => "mi",#ng (neg)
1083    "-=" => "mI",
1084    "*=" => "mL",
1085    "/=" => "dV",
1086    "&=" => "aN",
1087    "|=" => "oR",
1088    "%=" => "rM",
1089    "^=" => "eO",
1090    "/" => "dv",
1091    "->*" => "pm",
1092    "->" => "pt",#rf (ref)
1093    "," => "cm",
1094    "?" => "qu",
1095    "." => "dt",
1096    "sizeof"=> "sz"#st
1097);
1098
1099my %Intrinsic_Keywords = map {$_=>1} (
1100    "true",
1101    "false",
1102    "_Bool",
1103    "_Complex",
1104    "const",
1105    "int",
1106    "long",
1107    "void",
1108    "short",
1109    "float",
1110    "volatile",
1111    "restrict",
1112    "unsigned",
1113    "signed",
1114    "char",
1115    "double",
1116    "class",
1117    "struct",
1118    "union",
1119    "enum"
1120);
1121
1122my %GlibcHeader = map {$_=>1} (
1123    "aliases.h",
1124    "argp.h",
1125    "argz.h",
1126    "assert.h",
1127    "cpio.h",
1128    "ctype.h",
1129    "dirent.h",
1130    "envz.h",
1131    "errno.h",
1132    "error.h",
1133    "execinfo.h",
1134    "fcntl.h",
1135    "fstab.h",
1136    "ftw.h",
1137    "glob.h",
1138    "grp.h",
1139    "iconv.h",
1140    "ifaddrs.h",
1141    "inttypes.h",
1142    "langinfo.h",
1143    "limits.h",
1144    "link.h",
1145    "locale.h",
1146    "malloc.h",
1147    "math.h",
1148    "mntent.h",
1149    "monetary.h",
1150    "nl_types.h",
1151    "obstack.h",
1152    "printf.h",
1153    "pwd.h",
1154    "regex.h",
1155    "sched.h",
1156    "search.h",
1157    "setjmp.h",
1158    "shadow.h",
1159    "signal.h",
1160    "spawn.h",
1161    "stdarg.h",
1162    "stdint.h",
1163    "stdio.h",
1164    "stdlib.h",
1165    "string.h",
1166    "strings.h",
1167    "tar.h",
1168    "termios.h",
1169    "time.h",
1170    "ulimit.h",
1171    "unistd.h",
1172    "utime.h",
1173    "wchar.h",
1174    "wctype.h",
1175    "wordexp.h" );
1176
1177my %GlibcDir = map {$_=>1} (
1178    "arpa",
1179    "bits",
1180    "gnu",
1181    "netinet",
1182    "net",
1183    "nfs",
1184    "rpc",
1185    "sys",
1186    "linux" );
1187
1188my %WinHeaders = map {$_=>1} (
1189    "dos.h",
1190    "process.h",
1191    "winsock.h",
1192    "config-win.h",
1193    "mem.h",
1194    "windows.h",
1195    "winsock2.h",
1196    "crtdbg.h",
1197    "ws2tcpip.h"
1198);
1199
1200my %ObsoleteHeaders = map {$_=>1} (
1201    "iostream.h",
1202    "fstream.h"
1203);
1204
1205my %AlienHeaders = map {$_=>1} (
1206 # Solaris
1207    "thread.h",
1208    "sys/atomic.h",
1209 # HPUX
1210    "sys/stream.h",
1211 # Symbian
1212    "AknDoc.h",
1213 # Atari ST
1214    "ext.h",
1215    "tos.h",
1216 # MS-DOS
1217    "alloc.h",
1218 # Sparc
1219    "sys/atomic.h"
1220);
1221
1222my %ConfHeaders = map {$_=>1} (
1223    "atomic",
1224    "conf.h",
1225    "config.h",
1226    "configure.h",
1227    "build.h",
1228    "setup.h"
1229);
1230
1231my %LocalIncludes = map {$_=>1} (
1232    "/usr/local/include",
1233    "/usr/local" );
1234
1235my %OS_AddPath=(
1236# These paths are needed if the tool cannot detect them automatically
1237    "macos"=>{
1238        "include"=>[
1239            "/Library",
1240            "/Developer/usr/include"
1241        ],
1242        "lib"=>[
1243            "/Library",
1244            "/Developer/usr/lib"
1245        ],
1246        "bin"=>[
1247            "/Developer/usr/bin"
1248        ]
1249    },
1250    "beos"=>{
1251    # Haiku has GCC 2.95.3 by default
1252    # try to find GCC>=3.0 in /boot/develop/abi
1253        "include"=>[
1254            "/boot/common",
1255            "/boot/develop"
1256        ],
1257        "lib"=>[
1258            "/boot/common/lib",
1259            "/boot/system/lib",
1260            "/boot/apps"
1261        ],
1262        "bin"=>[
1263            "/boot/common/bin",
1264            "/boot/system/bin",
1265            "/boot/develop/abi"
1266        ]
1267    }
1268);
1269
1270my %Slash_Type=(
1271    "default"=>"/",
1272    "windows"=>"\\"
1273);
1274
1275my $SLASH = $Slash_Type{$OSgroup}?$Slash_Type{$OSgroup}:$Slash_Type{"default"};
1276
1277# Global Variables
1278my %COMMON_LANGUAGE=(
1279  1 => "C",
1280  2 => "C" );
1281
1282my $MAX_COMMAND_LINE_ARGUMENTS = 4096;
1283my $MAX_CPPFILT_FILE_SIZE = 50000;
1284my $CPPFILT_SUPPORT_FILE;
1285
1286my (%WORD_SIZE, %CPU_ARCH, %GCC_VERSION, %CLANG_VERSION);
1287
1288my $STDCXX_TESTING = 0;
1289my $GLIBC_TESTING = 0;
1290my $CPP_HEADERS = 0;
1291
1292my $CheckHeadersOnly = $CheckHeadersOnly_Opt;
1293my $CheckUndefined = 0;
1294
1295my $TargetComponent = undef;
1296if($TargetComponent_Opt) {
1297    $TargetComponent = lc($TargetComponent_Opt);
1298}
1299else
1300{ # default: library
1301  # other components: header, system, ...
1302    $TargetComponent = "library";
1303}
1304
1305my $TOP_REF = "<a class='top_ref' href='#Top'>to the top</a>";
1306
1307my $SystemRoot;
1308
1309my $MAIN_CPP_DIR;
1310my %RESULT;
1311my %LOG_PATH;
1312my %DEBUG_PATH;
1313my %Cache;
1314my %LibInfo;
1315my $COMPILE_ERRORS = 0;
1316my %CompilerOptions;
1317my %CheckedDyLib;
1318my $TargetLibraryShortName = parse_libname($TargetLibraryName, "shortest", $OSgroup);
1319
1320# Constants (#defines)
1321my %Constants;
1322my %SkipConstants;
1323my %EnumConstants;
1324
1325# Extra Info
1326my %SymbolHeader;
1327my %KnownLibs;
1328
1329# Templates
1330my %TemplateInstance;
1331my %BasicTemplate;
1332my %TemplateArg;
1333my %TemplateDecl;
1334my %TemplateMap;
1335
1336# Types
1337my %TypeInfo;
1338my %SkipTypes = (
1339  "1"=>{},
1340  "2"=>{} );
1341my %CheckedTypes;
1342my %TName_Tid;
1343my %EnumMembName_Id;
1344my %NestedNameSpaces = (
1345  "1"=>{},
1346  "2"=>{} );
1347my %VirtualTable;
1348my %VirtualTable_Model;
1349my %ClassVTable;
1350my %ClassVTable_Content;
1351my %VTableClass;
1352my %AllocableClass;
1353my %ClassMethods;
1354my %ClassNames;
1355my %Class_SubClasses;
1356my %OverriddenMethods;
1357my %TypedefToAnon;
1358my $MAX_ID = 0;
1359
1360my %CheckedTypeInfo;
1361
1362# Typedefs
1363my %Typedef_BaseName;
1364my %Typedef_Tr;
1365my %Typedef_Eq;
1366my %StdCxxTypedef;
1367my %MissedTypedef;
1368my %MissedBase;
1369my %MissedBase_R;
1370my %TypeTypedef;
1371
1372# Symbols
1373my %SymbolInfo;
1374my %tr_name;
1375my %mangled_name_gcc;
1376my %mangled_name;
1377my %SkipSymbols = (
1378  "1"=>{},
1379  "2"=>{} );
1380my %SkipNameSpaces = (
1381  "1"=>{},
1382  "2"=>{} );
1383my %AddNameSpaces = (
1384  "1"=>{},
1385  "2"=>{} );
1386my %SymbolsList;
1387my %TypesList;
1388my %SymbolsList_App;
1389my %CheckedSymbols;
1390my %Symbol_Library = (
1391  "1"=>{},
1392  "2"=>{} );
1393my %Library_Symbol = (
1394  "1"=>{},
1395  "2"=>{} );
1396my %DepSymbol_Library = (
1397  "1"=>{},
1398  "2"=>{} );
1399my %DepLibrary_Symbol = (
1400  "1"=>{},
1401  "2"=>{} );
1402my %MangledNames;
1403my %Func_ShortName;
1404my %AddIntParams;
1405my %GlobalDataObject;
1406my %WeakSymbols;
1407my %Library_Needed= (
1408  "1"=>{},
1409  "2"=>{} );
1410my $DisabledMSVCUnmangling = undef;
1411
1412# Extra Info
1413my %UndefinedSymbols;
1414my %PreprocessedHeaders;
1415
1416# Headers
1417my %Include_Preamble = (
1418    "1"=>[],
1419    "2"=>[] );
1420my %Registered_Headers;
1421my %Registered_Sources;
1422my %HeaderName_Paths;
1423my %Header_Dependency;
1424my %Include_Neighbors;
1425my %Include_Paths = (
1426    "1"=>[],
1427    "2"=>[] );
1428my %INC_PATH_AUTODETECT = (
1429  "1"=>1,
1430  "2"=>1 );
1431my %Add_Include_Paths = (
1432    "1"=>[],
1433    "2"=>[] );
1434my %Skip_Include_Paths;
1435my %RegisteredDirs;
1436my %Header_ErrorRedirect;
1437my %Header_Includes;
1438my %Header_Includes_R;
1439my %Header_ShouldNotBeUsed;
1440my %RecursiveIncludes;
1441my %Header_Include_Prefix;
1442my %SkipHeaders;
1443my %SkipHeadersList=(
1444  "1"=>{},
1445  "2"=>{} );
1446my %SkipLibs;
1447my %Include_Order;
1448my %TUnit_NameSpaces;
1449my %TUnit_Classes;
1450my %TUnit_Funcs;
1451my %TUnit_Vars;
1452
1453my %CppMode = (
1454  "1"=>0,
1455  "2"=>0 );
1456my %AutoPreambleMode = (
1457  "1"=>0,
1458  "2"=>0 );
1459my %MinGWMode = (
1460  "1"=>0,
1461  "2"=>0 );
1462my %Cpp0xMode = (
1463  "1"=>0,
1464  "2"=>0 );
1465
1466# Shared Objects
1467my %RegisteredObjects;
1468my %RegisteredObjects_Short;
1469my %RegisteredSONAMEs;
1470my %RegisteredObject_Dirs;
1471
1472my %CheckedArch;
1473
1474# System Objects
1475my %SystemObjects;
1476my @DefaultLibPaths;
1477my %DyLib_DefaultPath;
1478
1479# System Headers
1480my %SystemHeaders;
1481my @DefaultCppPaths;
1482my @DefaultGccPaths;
1483my @DefaultIncPaths;
1484my %DefaultCppHeader;
1485my %DefaultGccHeader;
1486my @UsersIncPath;
1487
1488# Merging
1489my %CompleteSignature;
1490my $Version;
1491my %AddedInt;
1492my %RemovedInt;
1493my %AddedInt_Virt;
1494my %RemovedInt_Virt;
1495my %VirtualReplacement;
1496my %ChangedTypedef;
1497my %CompatRules;
1498my %IncompleteRules;
1499my %UnknownRules;
1500my %VTableChanged_M;
1501my %ExtendedSymbols;
1502my %ReturnedClass;
1503my %ParamClass;
1504my %SourceAlternative;
1505my %SourceAlternative_B;
1506my %SourceReplacement;
1507my $CurrentSymbol; # for debugging
1508
1509#Report
1510my %TypeChanges;
1511
1512#Speedup
1513my %TypeProblemsIndex;
1514
1515# Calling Conventions
1516my %UseConv_Real = (
1517  1=>{ "R"=>0, "P"=>0 },
1518  2=>{ "R"=>0, "P"=>0 }
1519);
1520
1521# ABI Dump
1522my %UsedDump;
1523
1524# Filters
1525my %TargetLibs;
1526my %TargetHeaders;
1527
1528# Format of objects
1529my $OStarget = $OSgroup;
1530my %TargetTools;
1531
1532# Recursion locks
1533my @RecurLib;
1534my @RecurTypes;
1535my @RecurTypes_Diff;
1536my @RecurInclude;
1537my @RecurConstant;
1538
1539# System
1540my %SystemPaths = (
1541    "include"=>[],
1542    "lib"=>[],
1543    "bin"=>[]
1544);
1545my @DefaultBinPaths;
1546my $GCC_PATH;
1547
1548# Symbols versioning
1549my %SymVer = (
1550  "1"=>{},
1551  "2"=>{} );
1552
1553# Problem descriptions
1554my %CompatProblems;
1555my %CompatProblems_Constants;
1556my %TotalAffected;
1557
1558# Reports
1559my $ContentID = 1;
1560my $ContentSpanStart = "<span class=\"section\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1561my $ContentSpanStart_Affected = "<span class=\"sect_aff\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1562my $ContentSpanStart_Info = "<span class=\"sect_info\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1563my $ContentSpanEnd = "</span>\n";
1564my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
1565my $ContentDivEnd = "</div>\n";
1566my $Content_Counter = 0;
1567
1568# Modes
1569my $JoinReport = 1;
1570my $DoubleReport = 0;
1571
1572my %Severity_Val=(
1573    "High"=>3,
1574    "Medium"=>2,
1575    "Low"=>1,
1576    "Safe"=>-1
1577);
1578
1579sub get_Modules()
1580{
1581    my $TOOL_DIR = get_dirname($0);
1582    if(not $TOOL_DIR)
1583    { # patch for MS Windows
1584        $TOOL_DIR = ".";
1585    }
1586    my @SEARCH_DIRS = (
1587        # tool's directory
1588        abs_path($TOOL_DIR),
1589        # relative path to modules
1590        abs_path($TOOL_DIR)."/../share/abi-compliance-checker",
1591        # install path
1592        'MODULES_INSTALL_PATH'
1593    );
1594    foreach my $DIR (@SEARCH_DIRS)
1595    {
1596        if(not is_abs($DIR))
1597        { # relative path
1598            $DIR = abs_path($TOOL_DIR)."/".$DIR;
1599        }
1600        if(-d $DIR."/modules") {
1601            return $DIR."/modules";
1602        }
1603    }
1604    exitStatus("Module_Error", "can't find modules");
1605}
1606
1607my %LoadedModules = ();
1608
1609sub loadModule($)
1610{
1611    my $Name = $_[0];
1612    if(defined $LoadedModules{$Name}) {
1613        return;
1614    }
1615    my $Path = $MODULES_DIR."/Internals/$Name.pm";
1616    if(not -f $Path) {
1617        exitStatus("Module_Error", "can't access \'$Path\'");
1618    }
1619    require $Path;
1620    $LoadedModules{$Name} = 1;
1621}
1622
1623sub readModule($$)
1624{
1625    my ($Module, $Name) = @_;
1626    my $Path = $MODULES_DIR."/Internals/$Module/".$Name;
1627    if(not -f $Path) {
1628        exitStatus("Module_Error", "can't access \'$Path\'");
1629    }
1630    return readFile($Path);
1631}
1632
1633sub showPos($)
1634{
1635    my $Number = $_[0];
1636    if(not $Number) {
1637        $Number = 1;
1638    }
1639    else {
1640        $Number = int($Number)+1;
1641    }
1642    if($Number>3) {
1643        return $Number."th";
1644    }
1645    elsif($Number==1) {
1646        return "1st";
1647    }
1648    elsif($Number==2) {
1649        return "2nd";
1650    }
1651    elsif($Number==3) {
1652        return "3rd";
1653    }
1654    else {
1655        return $Number;
1656    }
1657}
1658
1659sub search_Tools($)
1660{
1661    my $Name = $_[0];
1662    return "" if(not $Name);
1663    if(my @Paths = keys(%TargetTools))
1664    {
1665        foreach my $Path (@Paths)
1666        {
1667            if(-f join_P($Path, $Name)) {
1668                return join_P($Path, $Name);
1669            }
1670            if($CrossPrefix)
1671            { # user-defined prefix (arm-none-symbianelf, ...)
1672                my $Candidate = join_P($Path, $CrossPrefix."-".$Name);
1673                if(-f $Candidate) {
1674                    return $Candidate;
1675                }
1676            }
1677        }
1678    }
1679    else {
1680        return "";
1681    }
1682}
1683
1684sub synch_Cmd($)
1685{
1686    my $Name = $_[0];
1687    if(not $GCC_PATH)
1688    { # GCC was not found yet
1689        return "";
1690    }
1691    my $Candidate = $GCC_PATH;
1692    if($Candidate=~s/\bgcc(|\.\w+)\Z/$Name$1/) {
1693        return $Candidate;
1694    }
1695    return "";
1696}
1697
1698sub get_CmdPath($)
1699{
1700    my $Name = $_[0];
1701    return "" if(not $Name);
1702    if(defined $Cache{"get_CmdPath"}{$Name}) {
1703        return $Cache{"get_CmdPath"}{$Name};
1704    }
1705    my %BinUtils = map {$_=>1} (
1706        "c++filt",
1707        "objdump",
1708        "readelf"
1709    );
1710    if($BinUtils{$Name} and $GCC_PATH)
1711    {
1712        if(my $Dir = get_dirname($GCC_PATH)) {
1713            $TargetTools{$Dir}=1;
1714        }
1715    }
1716    my $Path = search_Tools($Name);
1717    if(not $Path and $OSgroup eq "windows") {
1718        $Path = search_Tools($Name.".exe");
1719    }
1720    if(not $Path and $BinUtils{$Name})
1721    {
1722        if($CrossPrefix)
1723        { # user-defined prefix
1724            $Path = search_Cmd($CrossPrefix."-".$Name);
1725        }
1726    }
1727    if(not $Path and $BinUtils{$Name})
1728    {
1729        if(my $Candidate = synch_Cmd($Name))
1730        { # synch with GCC
1731            if($Candidate=~/[\/\\]/)
1732            { # command path
1733                if(-f $Candidate) {
1734                    $Path = $Candidate;
1735                }
1736            }
1737            elsif($Candidate = search_Cmd($Candidate))
1738            { # command name
1739                $Path = $Candidate;
1740            }
1741        }
1742    }
1743    if(not $Path) {
1744        $Path = search_Cmd($Name);
1745    }
1746    if(not $Path and $OSgroup eq "windows")
1747    { # search for *.exe file
1748        $Path=search_Cmd($Name.".exe");
1749    }
1750    if($Path=~/\s/) {
1751        $Path = "\"".$Path."\"";
1752    }
1753    return ($Cache{"get_CmdPath"}{$Name}=$Path);
1754}
1755
1756sub search_Cmd($)
1757{
1758    my $Name = $_[0];
1759    return "" if(not $Name);
1760    if(defined $Cache{"search_Cmd"}{$Name}) {
1761        return $Cache{"search_Cmd"}{$Name};
1762    }
1763    if(my $DefaultPath = get_CmdPath_Default($Name)) {
1764        return ($Cache{"search_Cmd"}{$Name} = $DefaultPath);
1765    }
1766    foreach my $Path (@{$SystemPaths{"bin"}})
1767    {
1768        my $CmdPath = join_P($Path,$Name);
1769        if(-f $CmdPath)
1770        {
1771            if($Name=~/gcc/) {
1772                next if(not check_gcc($CmdPath, "3"));
1773            }
1774            return ($Cache{"search_Cmd"}{$Name} = $CmdPath);
1775        }
1776    }
1777    return ($Cache{"search_Cmd"}{$Name} = "");
1778}
1779
1780sub get_CmdPath_Default($)
1781{ # search in PATH
1782    return "" if(not $_[0]);
1783    if(defined $Cache{"get_CmdPath_Default"}{$_[0]}) {
1784        return $Cache{"get_CmdPath_Default"}{$_[0]};
1785    }
1786    return ($Cache{"get_CmdPath_Default"}{$_[0]} = get_CmdPath_Default_I($_[0]));
1787}
1788
1789sub get_CmdPath_Default_I($)
1790{ # search in PATH
1791    my $Name = $_[0];
1792    if($Name=~/find/)
1793    { # special case: search for "find" utility
1794        if(`find \"$TMP_DIR\" -maxdepth 0 2>\"$TMP_DIR/null\"`) {
1795            return "find";
1796        }
1797    }
1798    elsif($Name=~/gcc/) {
1799        return check_gcc($Name, "3");
1800    }
1801    if(checkCmd($Name)) {
1802        return $Name;
1803    }
1804    if($OSgroup eq "windows")
1805    {
1806        if(`$Name /? 2>\"$TMP_DIR/null\"`) {
1807            return $Name;
1808        }
1809    }
1810    foreach my $Path (@DefaultBinPaths)
1811    {
1812        if(-f $Path."/".$Name) {
1813            return join_P($Path, $Name);
1814        }
1815    }
1816    return "";
1817}
1818
1819sub classifyPath($)
1820{
1821    my $Path = $_[0];
1822    if($Path=~/[\*\+\(\[\|]/)
1823    { # pattern
1824        return ($Path, "Pattern");
1825    }
1826    elsif($Path=~/[\/\\]/)
1827    { # directory or relative path
1828        return (path_format($Path, $OSgroup), "Path");
1829    }
1830    else {
1831        return ($Path, "Name");
1832    }
1833}
1834
1835sub readDescriptor($$)
1836{
1837    my ($LibVersion, $Content) = @_;
1838    return if(not $LibVersion);
1839    my $DName = $DumpAPI?"descriptor":"descriptor \"d$LibVersion\"";
1840    if(not $Content) {
1841        exitStatus("Error", "$DName is empty");
1842    }
1843    if($Content!~/\</) {
1844        exitStatus("Error", "incorrect descriptor (see -d1 option)");
1845    }
1846    $Content=~s/\/\*(.|\n)+?\*\///g;
1847    $Content=~s/<\!--(.|\n)+?-->//g;
1848
1849    $Descriptor{$LibVersion}{"Version"} = parseTag(\$Content, "version");
1850    if($TargetVersion{$LibVersion}) {
1851        $Descriptor{$LibVersion}{"Version"} = $TargetVersion{$LibVersion};
1852    }
1853    if(not $Descriptor{$LibVersion}{"Version"}) {
1854        exitStatus("Error", "version in the $DName is not specified (<version> section)");
1855    }
1856    if($Content=~/{RELPATH}/)
1857    {
1858        if(my $RelDir = $RelativeDirectory{$LibVersion}) {
1859            $Content =~ s/{RELPATH}/$RelDir/g;
1860        }
1861        else
1862        {
1863            my $NeedRelpath = $DumpAPI?"-relpath":"-relpath$LibVersion";
1864            exitStatus("Error", "you have not specified $NeedRelpath option, but the $DName contains {RELPATH} macro");
1865        }
1866    }
1867
1868    my $DHeaders = parseTag(\$Content, "headers");
1869    if(not $DHeaders) {
1870        exitStatus("Error", "header files in the $DName are not specified (<headers> section)");
1871    }
1872    elsif(lc($DHeaders) ne "none")
1873    { # append the descriptor headers list
1874        if($Descriptor{$LibVersion}{"Headers"})
1875        { # multiple descriptors
1876            $Descriptor{$LibVersion}{"Headers"} .= "\n".$DHeaders;
1877        }
1878        else {
1879            $Descriptor{$LibVersion}{"Headers"} = $DHeaders;
1880        }
1881        foreach my $Path (split(/\s*\n\s*/, $DHeaders))
1882        {
1883            if(not -e $Path) {
1884                exitStatus("Access_Error", "can't access \'$Path\'");
1885            }
1886        }
1887    }
1888
1889    if(not $CheckHeadersOnly_Opt)
1890    {
1891        my $DObjects = parseTag(\$Content, "libs");
1892        if(not $DObjects) {
1893            exitStatus("Error", "$SLIB_TYPE libraries in the $DName are not specified (<libs> section)");
1894        }
1895        elsif(lc($DObjects) ne "none")
1896        { # append the descriptor libraries list
1897            if($Descriptor{$LibVersion}{"Libs"})
1898            { # multiple descriptors
1899                $Descriptor{$LibVersion}{"Libs"} .= "\n".$DObjects;
1900            }
1901            else {
1902                $Descriptor{$LibVersion}{"Libs"} .= $DObjects;
1903            }
1904            foreach my $Path (split(/\s*\n\s*/, $DObjects))
1905            {
1906                if(not -e $Path) {
1907                    exitStatus("Access_Error", "can't access \'$Path\'");
1908                }
1909            }
1910        }
1911    }
1912    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers")))
1913    {
1914        if(not -d $Path) {
1915            exitStatus("Access_Error", "can't access directory \'$Path\'");
1916        }
1917        $Path = get_abs_path($Path);
1918        $Path = path_format($Path, $OSgroup);
1919        push_U($SystemPaths{"include"}, $Path);
1920    }
1921    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs")))
1922    {
1923        if(not -d $Path) {
1924            exitStatus("Access_Error", "can't access directory \'$Path\'");
1925        }
1926        $Path = get_abs_path($Path);
1927        $Path = path_format($Path, $OSgroup);
1928        push_U($SystemPaths{"lib"}, $Path);
1929    }
1930    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools")))
1931    {
1932        if(not -d $Path) {
1933            exitStatus("Access_Error", "can't access directory \'$Path\'");
1934        }
1935        $Path = get_abs_path($Path);
1936        $Path = path_format($Path, $OSgroup);
1937        push_U($SystemPaths{"bin"}, $Path);
1938        $TargetTools{$Path}=1;
1939    }
1940    if(my $Prefix = parseTag(\$Content, "cross_prefix")) {
1941        $CrossPrefix = $Prefix;
1942    }
1943    $Descriptor{$LibVersion}{"IncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"IncludePaths"}); # perl 5.8 doesn't support //=
1944    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "include_paths")))
1945    {
1946        if(not -d $Path) {
1947            exitStatus("Access_Error", "can't access directory \'$Path\'");
1948        }
1949        $Path = get_abs_path($Path);
1950        $Path = path_format($Path, $OSgroup);
1951        push(@{$Descriptor{$LibVersion}{"IncludePaths"}}, $Path);
1952    }
1953    $Descriptor{$LibVersion}{"AddIncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"AddIncludePaths"});
1954    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "add_include_paths")))
1955    {
1956        if(not -d $Path) {
1957            exitStatus("Access_Error", "can't access directory \'$Path\'");
1958        }
1959        $Path = get_abs_path($Path);
1960        $Path = path_format($Path, $OSgroup);
1961        push(@{$Descriptor{$LibVersion}{"AddIncludePaths"}}, $Path);
1962    }
1963    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_include_paths")))
1964    { # skip some auto-generated include paths
1965        if(not is_abs($Path))
1966        {
1967            if(my $P = abs_path($Path)) {
1968                $Path = $P;
1969            }
1970        }
1971        $Skip_Include_Paths{$LibVersion}{path_format($Path)} = 1;
1972    }
1973    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_including")))
1974    { # skip direct including of some headers
1975        my ($CPath, $Type) = classifyPath($Path);
1976        $SkipHeaders{$LibVersion}{$Type}{$CPath} = 2;
1977    }
1978    $Descriptor{$LibVersion}{"GccOptions"} = parseTag(\$Content, "gcc_options");
1979    foreach my $Option (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"GccOptions"}))
1980    {
1981        if($Option!~/\A\-(Wl|l|L)/)
1982        { # skip linker options
1983            $CompilerOptions{$LibVersion} .= " ".$Option;
1984        }
1985    }
1986    $Descriptor{$LibVersion}{"SkipHeaders"} = parseTag(\$Content, "skip_headers");
1987    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipHeaders"}))
1988    {
1989        $SkipHeadersList{$LibVersion}{$Path} = 1;
1990
1991        my ($CPath, $Type) = classifyPath($Path);
1992        $SkipHeaders{$LibVersion}{$Type}{$CPath} = 1;
1993    }
1994    $Descriptor{$LibVersion}{"SkipLibs"} = parseTag(\$Content, "skip_libs");
1995    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipLibs"}))
1996    {
1997        my ($CPath, $Type) = classifyPath($Path);
1998        $SkipLibs{$LibVersion}{$Type}{$CPath} = 1;
1999    }
2000    if(my $DDefines = parseTag(\$Content, "defines"))
2001    {
2002        if($Descriptor{$LibVersion}{"Defines"})
2003        { # multiple descriptors
2004            $Descriptor{$LibVersion}{"Defines"} .= "\n".$DDefines;
2005        }
2006        else {
2007            $Descriptor{$LibVersion}{"Defines"} = $DDefines;
2008        }
2009    }
2010    foreach my $Order (split(/\s*\n\s*/, parseTag(\$Content, "include_order")))
2011    {
2012        if($Order=~/\A(.+):(.+)\Z/) {
2013            $Include_Order{$LibVersion}{$1} = $2;
2014        }
2015    }
2016    foreach my $Type_Name (split(/\s*\n\s*/, parseTag(\$Content, "opaque_types")),
2017    split(/\s*\n\s*/, parseTag(\$Content, "skip_types")))
2018    { # opaque_types renamed to skip_types (1.23.4)
2019        $SkipTypes{$LibVersion}{$Type_Name} = 1;
2020    }
2021    foreach my $Symbol (split(/\s*\n\s*/, parseTag(\$Content, "skip_interfaces")),
2022    split(/\s*\n\s*/, parseTag(\$Content, "skip_symbols")))
2023    { # skip_interfaces renamed to skip_symbols (1.22.1)
2024        $SkipSymbols{$LibVersion}{$Symbol} = 1;
2025    }
2026    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "skip_namespaces"))) {
2027        $SkipNameSpaces{$LibVersion}{$NameSpace} = 1;
2028    }
2029    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "add_namespaces"))) {
2030        $AddNameSpaces{$LibVersion}{$NameSpace} = 1;
2031    }
2032    foreach my $Constant (split(/\s*\n\s*/, parseTag(\$Content, "skip_constants"))) {
2033        $SkipConstants{$LibVersion}{$Constant} = 1;
2034    }
2035    if(my $DIncPreamble = parseTag(\$Content, "include_preamble"))
2036    {
2037        if($Descriptor{$LibVersion}{"IncludePreamble"})
2038        { # multiple descriptors
2039            $Descriptor{$LibVersion}{"IncludePreamble"} .= "\n".$DIncPreamble;
2040        }
2041        else {
2042            $Descriptor{$LibVersion}{"IncludePreamble"} = $DIncPreamble;
2043        }
2044    }
2045}
2046
2047sub parseTag(@)
2048{
2049    my $CodeRef = shift(@_);
2050    my $Tag = shift(@_);
2051    if(not $Tag or not $CodeRef) {
2052        return undef;
2053    }
2054    my $Sp = 0;
2055    if(@_) {
2056        $Sp = shift(@_);
2057    }
2058    my $Start = index(${$CodeRef}, "<$Tag>");
2059    if($Start!=-1)
2060    {
2061        my $End = index(${$CodeRef}, "</$Tag>");
2062        if($End!=-1)
2063        {
2064            my $TS = length($Tag)+3;
2065            my $Content = substr(${$CodeRef}, $Start, $End-$Start+$TS, "");
2066            substr($Content, 0, $TS-1, ""); # cut start tag
2067            substr($Content, -$TS, $TS, ""); # cut end tag
2068            if(not $Sp)
2069            {
2070                $Content=~s/\A\s+//g;
2071                $Content=~s/\s+\Z//g;
2072            }
2073            if(substr($Content, 0, 1) ne "<") {
2074                $Content = xmlSpecChars_R($Content);
2075            }
2076            return $Content;
2077        }
2078    }
2079    return undef;
2080}
2081
2082sub getInfo($)
2083{
2084    my $DumpPath = $_[0];
2085    return if(not $DumpPath or not -f $DumpPath);
2086
2087    readTUDump($DumpPath);
2088
2089    # processing info
2090    setTemplateParams_All();
2091
2092    if($ExtraDump) {
2093        setAnonTypedef_All();
2094    }
2095
2096    getTypeInfo_All();
2097    simplifyNames();
2098    simplifyConstants();
2099    getVarInfo_All();
2100    getSymbolInfo_All();
2101
2102    # clean memory
2103    %LibInfo = ();
2104    %TemplateInstance = ();
2105    %BasicTemplate = ();
2106    %MangledNames = ();
2107    %TemplateDecl = ();
2108    %StdCxxTypedef = ();
2109    %MissedTypedef = ();
2110    %Typedef_Tr = ();
2111    %Typedef_Eq = ();
2112    %TypedefToAnon = ();
2113
2114    # clean cache
2115    delete($Cache{"getTypeAttr"});
2116    delete($Cache{"getTypeDeclId"});
2117
2118    if($ExtraDump)
2119    {
2120        remove_Unused($Version, "Extra");
2121    }
2122    else
2123    { # remove unused types
2124        if($BinaryOnly and not $ExtendedCheck)
2125        { # --binary
2126            remove_Unused($Version, "All");
2127        }
2128        else {
2129            remove_Unused($Version, "Extended");
2130        }
2131    }
2132
2133    if($CheckInfo)
2134    {
2135        foreach my $Tid (keys(%{$TypeInfo{$Version}})) {
2136            check_Completeness($TypeInfo{$Version}{$Tid}, $Version);
2137        }
2138
2139        foreach my $Sid (keys(%{$SymbolInfo{$Version}})) {
2140            check_Completeness($SymbolInfo{$Version}{$Sid}, $Version);
2141        }
2142    }
2143
2144    if($Debug) {
2145        # debugMangling($Version);
2146    }
2147}
2148
2149sub readTUDump($)
2150{
2151    my $DumpPath = $_[0];
2152
2153    open(TU_DUMP, $DumpPath);
2154    local $/ = undef;
2155    my $Content = <TU_DUMP>;
2156    close(TU_DUMP);
2157
2158    unlink($DumpPath);
2159
2160    $Content=~s/\n[ ]+/ /g;
2161    my @Lines = split(/\n/, $Content);
2162
2163    # clean memory
2164    undef $Content;
2165
2166    $MAX_ID = $#Lines+1; # number of lines == number of nodes
2167
2168    foreach (0 .. $#Lines)
2169    {
2170        if($Lines[$_]=~/\A\@(\d+)[ ]+([a-z_]+)[ ]+(.+)\Z/i)
2171        { # get a number and attributes of a node
2172            next if(not $NodeType{$2});
2173            $LibInfo{$Version}{"info_type"}{$1}=$2;
2174            $LibInfo{$Version}{"info"}{$1}=$3." ";
2175        }
2176
2177        # clean memory
2178        delete($Lines[$_]);
2179    }
2180
2181    # clean memory
2182    undef @Lines;
2183}
2184
2185sub simplifyConstants()
2186{
2187    foreach my $Constant (keys(%{$Constants{$Version}}))
2188    {
2189        if(defined $Constants{$Version}{$Constant}{"Header"})
2190        {
2191            my $Value = $Constants{$Version}{$Constant}{"Value"};
2192            if(defined $EnumConstants{$Version}{$Value}) {
2193                $Constants{$Version}{$Constant}{"Value"} = $EnumConstants{$Version}{$Value}{"Value"};
2194            }
2195        }
2196    }
2197}
2198
2199sub simplifyNames()
2200{
2201    foreach my $Base (keys(%{$Typedef_Tr{$Version}}))
2202    {
2203        if($Typedef_Eq{$Version}{$Base}) {
2204            next;
2205        }
2206        my @Translations = sort keys(%{$Typedef_Tr{$Version}{$Base}});
2207        if($#Translations==0)
2208        {
2209            if(length($Translations[0])<=length($Base)) {
2210                $Typedef_Eq{$Version}{$Base} = $Translations[0];
2211            }
2212        }
2213        else
2214        { # select most appropriate
2215            foreach my $Tr (@Translations)
2216            {
2217                if($Base=~/\A\Q$Tr\E/)
2218                {
2219                    $Typedef_Eq{$Version}{$Base} = $Tr;
2220                    last;
2221                }
2222            }
2223        }
2224    }
2225    foreach my $TypeId (keys(%{$TypeInfo{$Version}}))
2226    {
2227        my $TypeName = $TypeInfo{$Version}{$TypeId}{"Name"};
2228        if(not $TypeName) {
2229            next;
2230        }
2231        next if(index($TypeName,"<")==-1);# template instances only
2232        if($TypeName=~/>(::\w+)+\Z/)
2233        { # skip unused types
2234            next;
2235        }
2236        foreach my $Base (sort {length($b)<=>length($a)}
2237        sort {$b cmp $a} keys(%{$Typedef_Eq{$Version}}))
2238        {
2239            next if(not $Base);
2240            next if(index($TypeName,$Base)==-1);
2241            next if(length($TypeName) - length($Base) <= 3);
2242            if(my $Typedef = $Typedef_Eq{$Version}{$Base})
2243            {
2244                $TypeName=~s/(\<|\,)\Q$Base\E(\W|\Z)/$1$Typedef$2/g;
2245                $TypeName=~s/(\<|\,)\Q$Base\E(\w|\Z)/$1$Typedef $2/g;
2246                if(defined $TypeInfo{$Version}{$TypeId}{"TParam"})
2247                {
2248                    foreach my $TPos (keys(%{$TypeInfo{$Version}{$TypeId}{"TParam"}}))
2249                    {
2250                        if(my $TPName = $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"})
2251                        {
2252                            $TPName=~s/\A\Q$Base\E(\W|\Z)/$Typedef$1/g;
2253                            $TPName=~s/\A\Q$Base\E(\w|\Z)/$Typedef $1/g;
2254                            $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"} = formatName($TPName, "T");
2255                        }
2256                    }
2257                }
2258            }
2259        }
2260        $TypeName = formatName($TypeName, "T");
2261        $TypeInfo{$Version}{$TypeId}{"Name"} = $TypeName;
2262        $TName_Tid{$Version}{$TypeName} = $TypeId;
2263    }
2264}
2265
2266sub setAnonTypedef_All()
2267{
2268    foreach my $InfoId (keys(%{$LibInfo{$Version}{"info"}}))
2269    {
2270        if($LibInfo{$Version}{"info_type"}{$InfoId} eq "type_decl")
2271        {
2272            if(isAnon(getNameByInfo($InfoId))) {
2273                $TypedefToAnon{getTypeId($InfoId)} = 1;
2274            }
2275        }
2276    }
2277}
2278
2279sub setTemplateParams_All()
2280{
2281    foreach (keys(%{$LibInfo{$Version}{"info"}}))
2282    {
2283        if($LibInfo{$Version}{"info_type"}{$_} eq "template_decl") {
2284            setTemplateParams($_);
2285        }
2286    }
2287}
2288
2289sub setTemplateParams($)
2290{
2291    my $Tid = getTypeId($_[0]);
2292    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
2293    {
2294        if($Info=~/(inst|spcs)[ ]*:[ ]*@(\d+) /)
2295        {
2296            my $TmplInst_Id = $2;
2297            setTemplateInstParams($_[0], $TmplInst_Id);
2298            while($TmplInst_Id = getNextElem($TmplInst_Id)) {
2299                setTemplateInstParams($_[0], $TmplInst_Id);
2300            }
2301        }
2302
2303        $BasicTemplate{$Version}{$Tid} = $_[0];
2304
2305        if(my $Prms = getTreeAttr_Prms($_[0]))
2306        {
2307            if(my $Valu = getTreeAttr_Valu($Prms))
2308            {
2309                my $Vector = getTreeVec($Valu);
2310                foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Vector}))
2311                {
2312                    if(my $Val = getTreeAttr_Valu($Vector->{$Pos}))
2313                    {
2314                        if(my $Name = getNameByInfo($Val))
2315                        {
2316                            $TemplateArg{$Version}{$_[0]}{$Pos} = $Name;
2317                            if($LibInfo{$Version}{"info_type"}{$Val} eq "parm_decl") {
2318                                $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = $Val;
2319                            }
2320                            else {
2321                                $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = getTreeAttr_Type($Val);
2322                            }
2323                        }
2324                    }
2325                }
2326            }
2327        }
2328    }
2329    if(my $TypeId = getTreeAttr_Type($_[0]))
2330    {
2331        if(my $IType = $LibInfo{$Version}{"info_type"}{$TypeId})
2332        {
2333            if($IType eq "record_type") {
2334                $TemplateDecl{$Version}{$TypeId} = 1;
2335            }
2336        }
2337    }
2338}
2339
2340sub setTemplateInstParams($$)
2341{
2342    my ($Tmpl, $Inst) = @_;
2343
2344    if(my $Info = $LibInfo{$Version}{"info"}{$Inst})
2345    {
2346        my ($Params_InfoId, $ElemId) = ();
2347        if($Info=~/purp[ ]*:[ ]*@(\d+) /) {
2348            $Params_InfoId = $1;
2349        }
2350        if($Info=~/valu[ ]*:[ ]*@(\d+) /) {
2351            $ElemId = $1;
2352        }
2353        if($Params_InfoId and $ElemId)
2354        {
2355            my $Params_Info = $LibInfo{$Version}{"info"}{$Params_InfoId};
2356            while($Params_Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
2357            {
2358                my ($PPos, $PTypeId) = ($1, $2);
2359                if(my $PType = $LibInfo{$Version}{"info_type"}{$PTypeId})
2360                {
2361                    if($PType eq "template_type_parm") {
2362                        $TemplateDecl{$Version}{$ElemId} = 1;
2363                    }
2364                }
2365                if($LibInfo{$Version}{"info_type"}{$ElemId} eq "function_decl")
2366                { # functions
2367                    $TemplateInstance{$Version}{"Func"}{$ElemId}{$PPos} = $PTypeId;
2368                    $BasicTemplate{$Version}{$ElemId} = $Tmpl;
2369                }
2370                else
2371                { # types
2372                    $TemplateInstance{$Version}{"Type"}{$ElemId}{$PPos} = $PTypeId;
2373                    $BasicTemplate{$Version}{$ElemId} = $Tmpl;
2374                }
2375            }
2376        }
2377    }
2378}
2379
2380sub getTypeDeclId($)
2381{
2382    if($_[0])
2383    {
2384        if(defined $Cache{"getTypeDeclId"}{$Version}{$_[0]}) {
2385            return $Cache{"getTypeDeclId"}{$Version}{$_[0]};
2386        }
2387        if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
2388        {
2389            if($Info=~/name[ ]*:[ ]*@(\d+)/) {
2390                return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = $1);
2391            }
2392        }
2393    }
2394    return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = 0);
2395}
2396
2397sub getTypeInfo_All()
2398{
2399    if(not check_gcc($GCC_PATH, "4.5"))
2400    { # support for GCC < 4.5
2401      # missed typedefs: QStyle::State is typedef to QFlags<QStyle::StateFlag>
2402      # but QStyleOption.state is of type QFlags<QStyle::StateFlag> in the TU dump
2403      # FIXME: check GCC versions
2404        addMissedTypes_Pre();
2405    }
2406
2407    foreach (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2408    { # forward order only
2409        my $IType = $LibInfo{$Version}{"info_type"}{$_};
2410        if($IType=~/_type\Z/ and $IType ne "function_type"
2411        and $IType ne "method_type") {
2412            getTypeInfo("$_");
2413        }
2414    }
2415
2416    # add "..." type
2417    $TypeInfo{$Version}{"-1"} = {
2418        "Name" => "...",
2419        "Type" => "Intrinsic",
2420        "Tid" => "-1"
2421    };
2422    $TName_Tid{$Version}{"..."} = "-1";
2423
2424    if(not check_gcc($GCC_PATH, "4.5"))
2425    { # support for GCC < 4.5
2426        addMissedTypes_Post();
2427    }
2428
2429    if($ADD_TMPL_INSTANCES)
2430    {
2431        # templates
2432        foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}}))
2433        {
2434            if(defined $TemplateMap{$Version}{$Tid}
2435            and not defined $TypeInfo{$Version}{$Tid}{"Template"})
2436            {
2437                if(defined $TypeInfo{$Version}{$Tid}{"Memb"})
2438                {
2439                    foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Memb"}}))
2440                    {
2441                        if(my $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"})
2442                        {
2443                            if(my %MAttr = getTypeAttr($MembTypeId))
2444                            {
2445                                $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"algn"} = $MAttr{"Algn"};
2446                                $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"} = instType($TemplateMap{$Version}{$Tid}, $MembTypeId, $Version);
2447                            }
2448                        }
2449                    }
2450                }
2451                if(defined $TypeInfo{$Version}{$Tid}{"Base"})
2452                {
2453                    foreach my $Bid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Base"}}))
2454                    {
2455                        my $NBid = instType($TemplateMap{$Version}{$Tid}, $Bid, $Version);
2456
2457                        if($NBid ne $Bid
2458                        and $NBid ne $Tid)
2459                        {
2460                            %{$TypeInfo{$Version}{$Tid}{"Base"}{$NBid}} = %{$TypeInfo{$Version}{$Tid}{"Base"}{$Bid}};
2461                            delete($TypeInfo{$Version}{$Tid}{"Base"}{$Bid});
2462                        }
2463                    }
2464                }
2465            }
2466        }
2467    }
2468}
2469
2470sub createType($$)
2471{
2472    my ($Attr, $LibVersion) = @_;
2473    my $NewId = ++$MAX_ID;
2474
2475    $Attr->{"Tid"} = $NewId;
2476    $TypeInfo{$Version}{$NewId} = $Attr;
2477    $TName_Tid{$Version}{formatName($Attr->{"Name"}, "T")} = $NewId;
2478
2479    return "$NewId";
2480}
2481
2482sub instType($$$)
2483{ # create template instances
2484    my ($Map, $Tid, $LibVersion) = @_;
2485
2486    if(not $TypeInfo{$LibVersion}{$Tid}) {
2487        return undef;
2488    }
2489    my $Attr = dclone($TypeInfo{$LibVersion}{$Tid});
2490
2491    foreach my $Key (sort keys(%{$Map}))
2492    {
2493        if(my $Val = $Map->{$Key})
2494        {
2495            $Attr->{"Name"}=~s/\b$Key\b/$Val/g;
2496
2497            if(defined $Attr->{"NameSpace"}) {
2498                $Attr->{"NameSpace"}=~s/\b$Key\b/$Val/g;
2499            }
2500            foreach (keys(%{$Attr->{"TParam"}})) {
2501                $Attr->{"TParam"}{$_}{"name"}=~s/\b$Key\b/$Val/g;
2502            }
2503        }
2504        else
2505        { # remove absent
2506          # _Traits, etc.
2507            $Attr->{"Name"}=~s/,\s*\b$Key(,|>)/$1/g;
2508            if(defined $Attr->{"NameSpace"}) {
2509                $Attr->{"NameSpace"}=~s/,\s*\b$Key(,|>)/$1/g;
2510            }
2511            foreach (keys(%{$Attr->{"TParam"}}))
2512            {
2513                if($Attr->{"TParam"}{$_}{"name"} eq $Key) {
2514                    delete($Attr->{"TParam"}{$_});
2515                }
2516                else {
2517                    $Attr->{"TParam"}{$_}{"name"}=~s/,\s*\b$Key(,|>)/$1/g;
2518                }
2519            }
2520        }
2521    }
2522
2523    my $Tmpl = 0;
2524
2525    if(defined $Attr->{"TParam"})
2526    {
2527        foreach (sort {int($a)<=>int($b)} keys(%{$Attr->{"TParam"}}))
2528        {
2529            my $PName = $Attr->{"TParam"}{$_}{"name"};
2530
2531            if(my $PTid = $TName_Tid{$LibVersion}{$PName})
2532            {
2533                my %Base = get_BaseType($PTid, $LibVersion);
2534
2535                if($Base{"Type"} eq "TemplateParam"
2536                or defined $Base{"Template"})
2537                {
2538                    $Tmpl = 1;
2539                    last
2540                }
2541            }
2542        }
2543    }
2544
2545    if(my $Id = getTypeIdByName($Attr->{"Name"}, $LibVersion)) {
2546        return "$Id";
2547    }
2548    else
2549    {
2550        if(not $Tmpl) {
2551            delete($Attr->{"Template"});
2552        }
2553
2554        my $New = createType($Attr, $LibVersion);
2555
2556        my %EMap = ();
2557        if(defined $TemplateMap{$LibVersion}{$Tid}) {
2558            %EMap = %{$TemplateMap{$LibVersion}{$Tid}};
2559        }
2560        foreach (keys(%{$Map})) {
2561            $EMap{$_} = $Map->{$_};
2562        }
2563
2564        if(defined $TypeInfo{$LibVersion}{$New}{"BaseType"}) {
2565            $TypeInfo{$LibVersion}{$New}{"BaseType"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"BaseType"}, $LibVersion);
2566        }
2567        if(defined $TypeInfo{$LibVersion}{$New}{"Base"})
2568        {
2569            foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$New}{"Base"}}))
2570            {
2571                my $NBid = instType(\%EMap, $Bid, $LibVersion);
2572
2573                if($NBid ne $Bid
2574                and $NBid ne $New)
2575                {
2576                    %{$TypeInfo{$LibVersion}{$New}{"Base"}{$NBid}} = %{$TypeInfo{$LibVersion}{$New}{"Base"}{$Bid}};
2577                    delete($TypeInfo{$LibVersion}{$New}{"Base"}{$Bid});
2578                }
2579            }
2580        }
2581
2582        if(defined $TypeInfo{$LibVersion}{$New}{"Memb"})
2583        {
2584            foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Memb"}}))
2585            {
2586                if(defined $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}) {
2587                    $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}, $LibVersion);
2588                }
2589            }
2590        }
2591
2592        if(defined $TypeInfo{$LibVersion}{$New}{"Param"})
2593        {
2594            foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Param"}})) {
2595                $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"}, $LibVersion);
2596            }
2597        }
2598
2599        if(defined $TypeInfo{$LibVersion}{$New}{"Return"}) {
2600            $TypeInfo{$LibVersion}{$New}{"Return"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Return"}, $LibVersion);
2601        }
2602
2603        return $New;
2604    }
2605}
2606
2607sub addMissedTypes_Pre()
2608{
2609    my %MissedTypes = ();
2610    foreach my $MissedTDid (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2611    { # detecting missed typedefs
2612        if($LibInfo{$Version}{"info_type"}{$MissedTDid} eq "type_decl")
2613        {
2614            my $TypeId = getTreeAttr_Type($MissedTDid);
2615            next if(not $TypeId);
2616            my $TypeType = getTypeType($TypeId);
2617            if($TypeType eq "Unknown")
2618            { # template_type_parm
2619                next;
2620            }
2621            my $TypeDeclId = getTypeDeclId($TypeId);
2622            next if($TypeDeclId eq $MissedTDid);#or not $TypeDeclId
2623            my $TypedefName = getNameByInfo($MissedTDid);
2624            next if(not $TypedefName);
2625            next if($TypedefName eq "__float80");
2626            next if(isAnon($TypedefName));
2627            if(not $TypeDeclId
2628            or getNameByInfo($TypeDeclId) ne $TypedefName) {
2629                $MissedTypes{$Version}{$TypeId}{$MissedTDid} = 1;
2630            }
2631        }
2632    }
2633    my %AddTypes = ();
2634    foreach my $Tid (keys(%{$MissedTypes{$Version}}))
2635    { # add missed typedefs
2636        my @Missed = keys(%{$MissedTypes{$Version}{$Tid}});
2637        if(not @Missed or $#Missed>=1) {
2638            next;
2639        }
2640        my $MissedTDid = $Missed[0];
2641        my ($TypedefName, $TypedefNS) = getTrivialName($MissedTDid, $Tid);
2642        if(not $TypedefName) {
2643            next;
2644        }
2645        my $NewId = ++$MAX_ID;
2646        my %MissedInfo = ( # typedef info
2647            "Name" => $TypedefName,
2648            "NameSpace" => $TypedefNS,
2649            "BaseType" => $Tid,
2650            "Type" => "Typedef",
2651            "Tid" => "$NewId" );
2652        my ($H, $L) = getLocation($MissedTDid);
2653        $MissedInfo{"Header"} = $H;
2654        $MissedInfo{"Line"} = $L;
2655        if($TypedefName=~/\*|\&|\s/)
2656        { # other types
2657            next;
2658        }
2659        if($TypedefName=~/>(::\w+)+\Z/)
2660        { # QFlags<Qt::DropAction>::enum_type
2661            next;
2662        }
2663        if(getTypeType($Tid)=~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/)
2664        { # double-check for the name of typedef
2665            my ($TName, $TNS) = getTrivialName(getTypeDeclId($Tid), $Tid); # base type info
2666            next if(not $TName);
2667            if(length($TypedefName)>=length($TName))
2668            { # too long typedef
2669                next;
2670            }
2671            if($TName=~/\A\Q$TypedefName\E</) {
2672                next;
2673            }
2674            if($TypedefName=~/\A\Q$TName\E/)
2675            { # QDateTimeEdit::Section and QDateTimeEdit::Sections::enum_type
2676                next;
2677            }
2678            if(get_depth($TypedefName)==0 and get_depth($TName)!=0)
2679            { # std::_Vector_base and std::vector::_Base
2680                next;
2681            }
2682        }
2683
2684        $AddTypes{$MissedInfo{"Tid"}} = \%MissedInfo;
2685
2686        # register typedef
2687        $MissedTypedef{$Version}{$Tid}{"Tid"} = $MissedInfo{"Tid"};
2688        $MissedTypedef{$Version}{$Tid}{"TDid"} = $MissedTDid;
2689        $TName_Tid{$Version}{$TypedefName} = $MissedInfo{"Tid"};
2690    }
2691
2692    # add missed & remove other
2693    $TypeInfo{$Version} = \%AddTypes;
2694    delete($Cache{"getTypeAttr"}{$Version});
2695}
2696
2697sub addMissedTypes_Post()
2698{
2699    foreach my $BaseId (keys(%{$MissedTypedef{$Version}}))
2700    {
2701        if(my $Tid = $MissedTypedef{$Version}{$BaseId}{"Tid"})
2702        {
2703            $TypeInfo{$Version}{$Tid}{"Size"} = $TypeInfo{$Version}{$BaseId}{"Size"};
2704            if(my $TName = $TypeInfo{$Version}{$Tid}{"Name"}) {
2705                $Typedef_BaseName{$Version}{$TName} = $TypeInfo{$Version}{$BaseId}{"Name"};
2706            }
2707        }
2708    }
2709}
2710
2711sub getTypeInfo($)
2712{
2713    my $TypeId = $_[0];
2714    %{$TypeInfo{$Version}{$TypeId}} = getTypeAttr($TypeId);
2715    my $TName = $TypeInfo{$Version}{$TypeId}{"Name"};
2716    if(not $TName) {
2717        delete($TypeInfo{$Version}{$TypeId});
2718    }
2719}
2720
2721sub getArraySize($$)
2722{
2723    my ($TypeId, $BaseName) = @_;
2724    if(my $Size = getSize($TypeId))
2725    {
2726        my $Elems = $Size/$BYTE_SIZE;
2727        while($BaseName=~s/\s*\[(\d+)\]//) {
2728            $Elems/=$1;
2729        }
2730        if(my $BasicId = $TName_Tid{$Version}{$BaseName})
2731        {
2732            if(my $BasicSize = $TypeInfo{$Version}{$BasicId}{"Size"}) {
2733                $Elems/=$BasicSize;
2734            }
2735        }
2736        return $Elems;
2737    }
2738    return 0;
2739}
2740
2741sub getTParams($$)
2742{
2743    my ($TypeId, $Kind) = @_;
2744    my @TmplParams = ();
2745    my @Positions = sort {int($a)<=>int($b)} keys(%{$TemplateInstance{$Version}{$Kind}{$TypeId}});
2746    foreach my $Pos (@Positions)
2747    {
2748        my $Param_TypeId = $TemplateInstance{$Version}{$Kind}{$TypeId}{$Pos};
2749        my $NodeType = $LibInfo{$Version}{"info_type"}{$Param_TypeId};
2750        if(not $NodeType)
2751        { # typename_type
2752            return ();
2753        }
2754        if($NodeType eq "tree_vec")
2755        {
2756            if($Pos!=$#Positions)
2757            { # select last vector of parameters ( ns<P1>::type<P2> )
2758                next;
2759            }
2760        }
2761        my @Params = get_TemplateParam($Pos, $Param_TypeId);
2762        foreach my $P (@Params)
2763        {
2764            if($P eq "") {
2765                return ();
2766            }
2767            elsif($P ne "\@skip\@") {
2768                @TmplParams = (@TmplParams, $P);
2769            }
2770        }
2771    }
2772    return @TmplParams;
2773}
2774
2775sub getTypeAttr($)
2776{
2777    my $TypeId = $_[0];
2778    my %TypeAttr = ();
2779    if(defined $TypeInfo{$Version}{$TypeId}
2780    and $TypeInfo{$Version}{$TypeId}{"Name"})
2781    { # already created
2782        return %{$TypeInfo{$Version}{$TypeId}};
2783    }
2784    elsif($Cache{"getTypeAttr"}{$Version}{$TypeId})
2785    { # incomplete type
2786        return ();
2787    }
2788    $Cache{"getTypeAttr"}{$Version}{$TypeId} = 1;
2789
2790    my $TypeDeclId = getTypeDeclId($TypeId);
2791    $TypeAttr{"Tid"} = $TypeId;
2792
2793    if(not $MissedBase{$Version}{$TypeId} and isTypedef($TypeId))
2794    {
2795        if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
2796        {
2797            if($Info=~/qual[ ]*:/)
2798            {
2799                my $NewId = ++$MAX_ID;
2800
2801                $MissedBase{$Version}{$TypeId} = "$NewId";
2802                $MissedBase_R{$Version}{$NewId} = $TypeId;
2803                $LibInfo{$Version}{"info"}{$NewId} = $LibInfo{$Version}{"info"}{$TypeId};
2804                $LibInfo{$Version}{"info_type"}{$NewId} = $LibInfo{$Version}{"info_type"}{$TypeId};
2805            }
2806        }
2807        $TypeAttr{"Type"} = "Typedef";
2808    }
2809    else {
2810        $TypeAttr{"Type"} = getTypeType($TypeId);
2811    }
2812
2813    if(my $ScopeId = getTreeAttr_Scpe($TypeDeclId))
2814    {
2815        if($LibInfo{$Version}{"info_type"}{$ScopeId} eq "function_decl")
2816        { # local code
2817            return ();
2818        }
2819    }
2820
2821    if($TypeAttr{"Type"} eq "Unknown") {
2822        return ();
2823    }
2824    elsif($TypeAttr{"Type"}=~/(Func|Method|Field)Ptr/)
2825    {
2826        %TypeAttr = getMemPtrAttr(pointTo($TypeId), $TypeId, $TypeAttr{"Type"});
2827        if(my $TName = $TypeAttr{"Name"})
2828        {
2829            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2830            $TName_Tid{$Version}{$TName} = $TypeId;
2831            return %TypeAttr;
2832        }
2833        else {
2834            return ();
2835        }
2836    }
2837    elsif($TypeAttr{"Type"} eq "Array")
2838    {
2839        my ($BTid, $BTSpec) = selectBaseType($TypeId);
2840        if(not $BTid) {
2841            return ();
2842        }
2843        if(my $Algn = getAlgn($TypeId)) {
2844            $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
2845        }
2846        $TypeAttr{"BaseType"} = $BTid;
2847        if(my %BTAttr = getTypeAttr($BTid))
2848        {
2849            if(not $BTAttr{"Name"}) {
2850                return ();
2851            }
2852            if(my $NElems = getArraySize($TypeId, $BTAttr{"Name"}))
2853            {
2854                if(my $Size = getSize($TypeId)) {
2855                    $TypeAttr{"Size"} = $Size/$BYTE_SIZE;
2856                }
2857                if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2858                    $TypeAttr{"Name"} = $1."[$NElems]".$2;
2859                }
2860                else {
2861                    $TypeAttr{"Name"} = $BTAttr{"Name"}."[$NElems]";
2862                }
2863            }
2864            else
2865            {
2866                $TypeAttr{"Size"} = $WORD_SIZE{$Version}; # pointer
2867                if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2868                    $TypeAttr{"Name"} = $1."[]".$2;
2869                }
2870                else {
2871                    $TypeAttr{"Name"} = $BTAttr{"Name"}."[]";
2872                }
2873            }
2874            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
2875            if($BTAttr{"Header"})  {
2876                $TypeAttr{"Header"} = $BTAttr{"Header"};
2877            }
2878            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2879            $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2880            return %TypeAttr;
2881        }
2882        return ();
2883    }
2884    elsif($TypeAttr{"Type"}=~/\A(Intrinsic|Union|Struct|Enum|Class|Vector)\Z/)
2885    {
2886        %TypeAttr = getTrivialTypeAttr($TypeId);
2887        if($TypeAttr{"Name"})
2888        {
2889            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2890
2891            if(not defined $IntrinsicNames{$TypeAttr{"Name"}}
2892            or getTypeDeclId($TypeAttr{"Tid"}))
2893            { # NOTE: register only one int: with built-in decl
2894                if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2895                    $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2896                }
2897            }
2898            return %TypeAttr;
2899        }
2900        else {
2901            return ();
2902        }
2903    }
2904    elsif($TypeAttr{"Type"}=~/TemplateParam|TypeName/)
2905    {
2906        %TypeAttr = getTrivialTypeAttr($TypeId);
2907        if($TypeAttr{"Name"})
2908        {
2909            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2910            if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2911                $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2912            }
2913            return %TypeAttr;
2914        }
2915        else {
2916            return ();
2917        }
2918    }
2919    elsif($TypeAttr{"Type"} eq "SizeOf")
2920    {
2921        $TypeAttr{"BaseType"} = getTreeAttr_Type($TypeId);
2922        my %BTAttr = getTypeAttr($TypeAttr{"BaseType"});
2923        $TypeAttr{"Name"} = "sizeof(".$BTAttr{"Name"}.")";
2924        if($TypeAttr{"Name"})
2925        {
2926            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2927            return %TypeAttr;
2928        }
2929        else {
2930            return ();
2931        }
2932    }
2933    else
2934    { # derived types
2935        my ($BTid, $BTSpec) = selectBaseType($TypeId);
2936        if(not $BTid) {
2937            return ();
2938        }
2939        $TypeAttr{"BaseType"} = $BTid;
2940        if(defined $MissedTypedef{$Version}{$BTid})
2941        {
2942            if(my $MissedTDid = $MissedTypedef{$Version}{$BTid}{"TDid"})
2943            {
2944                if($MissedTDid ne $TypeDeclId) {
2945                    $TypeAttr{"BaseType"} = $MissedTypedef{$Version}{$BTid}{"Tid"};
2946                }
2947            }
2948        }
2949        my %BTAttr = getTypeAttr($TypeAttr{"BaseType"});
2950        if(not $BTAttr{"Name"})
2951        { # templates
2952            return ();
2953        }
2954        if($BTAttr{"Type"} eq "Typedef")
2955        { # relinking typedefs
2956            my %BaseBase = get_Type($BTAttr{"BaseType"}, $Version);
2957            if($BTAttr{"Name"} eq $BaseBase{"Name"}) {
2958                $TypeAttr{"BaseType"} = $BaseBase{"Tid"};
2959            }
2960        }
2961        if($BTSpec)
2962        {
2963            if($TypeAttr{"Type"} eq "Pointer"
2964            and $BTAttr{"Name"}=~/\([\*]+\)/)
2965            {
2966                $TypeAttr{"Name"} = $BTAttr{"Name"};
2967                $TypeAttr{"Name"}=~s/\(([*]+)\)/($1*)/g;
2968            }
2969            else {
2970                $TypeAttr{"Name"} = $BTAttr{"Name"}." ".$BTSpec;
2971            }
2972        }
2973        else {
2974            $TypeAttr{"Name"} = $BTAttr{"Name"};
2975        }
2976        if($TypeAttr{"Type"} eq "Typedef")
2977        {
2978            $TypeAttr{"Name"} = getNameByInfo($TypeDeclId);
2979
2980            if(index($TypeAttr{"Name"}, "tmp_add_type")==0) {
2981                return ();
2982            }
2983
2984            if(isAnon($TypeAttr{"Name"}))
2985            { # anon typedef to anon type: ._N
2986                return ();
2987            }
2988
2989            if($LibInfo{$Version}{"info"}{$TypeDeclId}=~/ artificial /i)
2990            { # artificial typedef of "struct X" to "X"
2991                $TypeAttr{"Artificial"} = 1;
2992            }
2993
2994            if(my $NS = getNameSpace($TypeDeclId))
2995            {
2996                my $TypeName = $TypeAttr{"Name"};
2997                if($NS=~/\A(struct |union |class |)((.+)::|)\Q$TypeName\E\Z/)
2998                { # "some_type" is the typedef to "struct some_type" in C++
2999                    if($3) {
3000                        $TypeAttr{"Name"} = $3."::".$TypeName;
3001                    }
3002                }
3003                else
3004                {
3005                    $TypeAttr{"NameSpace"} = $NS;
3006                    $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
3007
3008                    if($TypeAttr{"NameSpace"}=~/\Astd(::|\Z)/
3009                    and $TypeAttr{"Name"}!~/>(::\w+)+\Z/)
3010                    {
3011                        if($BTAttr{"NameSpace"}
3012                        and $BTAttr{"NameSpace"}=~/\Astd(::|\Z)/ and $BTAttr{"Name"}=~/</)
3013                        { # types like "std::fpos<__mbstate_t>" are
3014                          # not covered by typedefs in the TU dump
3015                          # so trying to add such typedefs manually
3016                            $StdCxxTypedef{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
3017                            if(length($TypeAttr{"Name"})<=length($BTAttr{"Name"}))
3018                            {
3019                                if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/))
3020                                { # skip "other" in "std" and "type" in "boost"
3021                                    $Typedef_Eq{$Version}{$BTAttr{"Name"}} = $TypeAttr{"Name"};
3022                                }
3023                            }
3024                        }
3025                    }
3026                }
3027            }
3028            if($TypeAttr{"Name"} ne $BTAttr{"Name"} and not $TypeAttr{"Artificial"}
3029            and $TypeAttr{"Name"}!~/>(::\w+)+\Z/ and $BTAttr{"Name"}!~/>(::\w+)+\Z/)
3030            {
3031                if(not defined $Typedef_BaseName{$Version}{$TypeAttr{"Name"}})
3032                { # typedef int*const TYPEDEF; // first
3033                  # int foo(TYPEDEF p); // const is optimized out
3034                    $Typedef_BaseName{$Version}{$TypeAttr{"Name"}} = $BTAttr{"Name"};
3035                    if($BTAttr{"Name"}=~/</)
3036                    {
3037                        if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) {
3038                            $Typedef_Tr{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
3039                        }
3040                    }
3041                }
3042            }
3043            ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeDeclId);
3044        }
3045        if(not $TypeAttr{"Size"})
3046        {
3047            if($TypeAttr{"Type"} eq "Pointer") {
3048                $TypeAttr{"Size"} = $WORD_SIZE{$Version};
3049            }
3050            elsif($BTAttr{"Size"}) {
3051                $TypeAttr{"Size"} = $BTAttr{"Size"};
3052            }
3053        }
3054        if(my $Algn = getAlgn($TypeId)) {
3055            $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
3056        }
3057        $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
3058        if(not $TypeAttr{"Header"} and $BTAttr{"Header"})  {
3059            $TypeAttr{"Header"} = $BTAttr{"Header"};
3060        }
3061        %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
3062        if($TypeAttr{"Name"} ne $BTAttr{"Name"})
3063        { # typedef to "class Class"
3064          # should not be registered in TName_Tid
3065            if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
3066                $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
3067            }
3068        }
3069        return %TypeAttr;
3070    }
3071}
3072
3073sub getTreeVec($)
3074{
3075    my %Vector = ();
3076    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3077    {
3078        while($Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
3079        { # string length is N-1 because of the null terminator
3080            $Vector{$1} = $2;
3081        }
3082    }
3083    return \%Vector;
3084}
3085
3086sub get_TemplateParam($$)
3087{
3088    my ($Pos, $Type_Id) = @_;
3089    return () if(not $Type_Id);
3090    my $NodeType = $LibInfo{$Version}{"info_type"}{$Type_Id};
3091    return () if(not $NodeType);
3092    if($NodeType eq "integer_cst")
3093    { # int (1), unsigned (2u), char ('c' as 99), ...
3094        my $CstTid = getTreeAttr_Type($Type_Id);
3095        my %CstType = getTypeAttr($CstTid); # without recursion
3096        my $Num = getNodeIntCst($Type_Id);
3097        if(my $CstSuffix = $ConstantSuffix{$CstType{"Name"}}) {
3098            return ($Num.$CstSuffix);
3099        }
3100        else {
3101            return ("(".$CstType{"Name"}.")".$Num);
3102        }
3103    }
3104    elsif($NodeType eq "string_cst") {
3105        return (getNodeStrCst($Type_Id));
3106    }
3107    elsif($NodeType eq "tree_vec")
3108    {
3109        my $Vector = getTreeVec($Type_Id);
3110        my @Params = ();
3111        foreach my $P1 (sort {int($a)<=>int($b)} keys(%{$Vector}))
3112        {
3113            foreach my $P2 (get_TemplateParam($Pos, $Vector->{$P1})) {
3114                push(@Params, $P2);
3115            }
3116        }
3117        return @Params;
3118    }
3119    elsif($NodeType eq "parm_decl")
3120    {
3121        (getNameByInfo($Type_Id));
3122    }
3123    else
3124    {
3125        my %ParamAttr = getTypeAttr($Type_Id);
3126        my $PName = $ParamAttr{"Name"};
3127        if(not $PName) {
3128            return ();
3129        }
3130        if($PName=~/\>/)
3131        {
3132            if(my $Cover = cover_stdcxx_typedef($PName)) {
3133                $PName = $Cover;
3134            }
3135        }
3136        if($Pos>=1 and
3137        $PName=~/\A$DEFAULT_STD_PARMS\</)
3138        { # template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
3139          # template<typename _Key, typename _Compare = std::less<_Key>
3140          # template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
3141          # template<typename _Ch_type, typename _Rx_traits = regex_traits<_Ch_type> >
3142          # template<typename _CharT, typename _InIter = istreambuf_iterator<_CharT> >
3143          # template<typename _CharT, typename _OutIter = ostreambuf_iterator<_CharT> >
3144            return ("\@skip\@");
3145        }
3146        return ($PName);
3147    }
3148}
3149
3150sub cover_stdcxx_typedef($)
3151{
3152    my $TypeName = $_[0];
3153    if(my @Covers = sort {length($a)<=>length($b)}
3154    sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
3155    { # take the shortest typedef
3156      # FIXME: there may be more than
3157      # one typedefs to the same type
3158        return $Covers[0];
3159    }
3160    my $Covered = $TypeName;
3161    while($TypeName=~s/(>)[ ]*(const|volatile|restrict| |\*|\&)\Z/$1/g){};
3162    if(my @Covers = sort {length($a)<=>length($b)} sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
3163    {
3164        if(my $Cover = $Covers[0])
3165        {
3166            $Covered=~s/\b\Q$TypeName\E(\W|\Z)/$Cover$1/g;
3167            $Covered=~s/\b\Q$TypeName\E(\w|\Z)/$Cover $1/g;
3168        }
3169    }
3170    return formatName($Covered, "T");
3171}
3172
3173sub getNodeIntCst($)
3174{
3175    my $CstId = $_[0];
3176    my $CstTypeId = getTreeAttr_Type($CstId);
3177    if($EnumMembName_Id{$Version}{$CstId}) {
3178        return $EnumMembName_Id{$Version}{$CstId};
3179    }
3180    elsif((my $Value = getTreeValue($CstId)) ne "")
3181    {
3182        if($Value eq "0")
3183        {
3184            if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") {
3185                return "false";
3186            }
3187            else {
3188                return "0";
3189            }
3190        }
3191        elsif($Value eq "1")
3192        {
3193            if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") {
3194                return "true";
3195            }
3196            else {
3197                return "1";
3198            }
3199        }
3200        else {
3201            return $Value;
3202        }
3203    }
3204    return "";
3205}
3206
3207sub getNodeStrCst($)
3208{
3209    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3210    {
3211        if($Info=~/strg[ ]*: (.+) lngt:[ ]*(\d+)/)
3212        {
3213            if($LibInfo{$Version}{"info_type"}{$_[0]} eq "string_cst")
3214            { # string length is N-1 because of the null terminator
3215                return substr($1, 0, $2-1);
3216            }
3217            else
3218            { # identifier_node
3219                return substr($1, 0, $2);
3220            }
3221        }
3222    }
3223    return "";
3224}
3225
3226sub getMemPtrAttr($$$)
3227{ # function, method and field pointers
3228    my ($PtrId, $TypeId, $Type) = @_;
3229    my $MemInfo = $LibInfo{$Version}{"info"}{$PtrId};
3230    if($Type eq "FieldPtr") {
3231        $MemInfo = $LibInfo{$Version}{"info"}{$TypeId};
3232    }
3233    my $MemInfo_Type = $LibInfo{$Version}{"info_type"}{$PtrId};
3234    my $MemPtrName = "";
3235    my %TypeAttr = ("Size"=>$WORD_SIZE{$Version}, "Type"=>$Type, "Tid"=>$TypeId);
3236    if($Type eq "MethodPtr")
3237    { # size of "method pointer" may be greater than WORD size
3238        if(my $Size = getSize($TypeId))
3239        {
3240            $Size/=$BYTE_SIZE;
3241            $TypeAttr{"Size"} = "$Size";
3242        }
3243    }
3244    if(my $Algn = getAlgn($TypeId)) {
3245        $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
3246    }
3247    # Return
3248    if($Type eq "FieldPtr")
3249    {
3250        my %ReturnAttr = getTypeAttr($PtrId);
3251        if($ReturnAttr{"Name"}) {
3252            $MemPtrName .= $ReturnAttr{"Name"};
3253        }
3254        $TypeAttr{"Return"} = $PtrId;
3255    }
3256    else
3257    {
3258        if($MemInfo=~/retn[ ]*:[ ]*\@(\d+) /)
3259        {
3260            my $ReturnTypeId = $1;
3261            my %ReturnAttr = getTypeAttr($ReturnTypeId);
3262            if(not $ReturnAttr{"Name"})
3263            { # templates
3264                return ();
3265            }
3266            $MemPtrName .= $ReturnAttr{"Name"};
3267            $TypeAttr{"Return"} = $ReturnTypeId;
3268        }
3269    }
3270    # Class
3271    if($MemInfo=~/(clas|cls)[ ]*:[ ]*@(\d+) /)
3272    {
3273        $TypeAttr{"Class"} = $2;
3274        my %Class = getTypeAttr($TypeAttr{"Class"});
3275        if($Class{"Name"}) {
3276            $MemPtrName .= " (".$Class{"Name"}."\:\:*)";
3277        }
3278        else {
3279            $MemPtrName .= " (*)";
3280        }
3281    }
3282    else {
3283        $MemPtrName .= " (*)";
3284    }
3285    # Parameters
3286    if($Type eq "FuncPtr"
3287    or $Type eq "MethodPtr")
3288    {
3289        my @ParamTypeName = ();
3290        if($MemInfo=~/prms[ ]*:[ ]*@(\d+) /)
3291        {
3292            my $PTypeInfoId = $1;
3293            my ($Pos, $PPos) = (0, 0);
3294            while($PTypeInfoId)
3295            {
3296                my $PTypeInfo = $LibInfo{$Version}{"info"}{$PTypeInfoId};
3297                if($PTypeInfo=~/valu[ ]*:[ ]*@(\d+) /)
3298                {
3299                    my $PTypeId = $1;
3300                    my %ParamAttr = getTypeAttr($PTypeId);
3301                    if(not $ParamAttr{"Name"})
3302                    { # templates (template_type_parm), etc.
3303                        return ();
3304                    }
3305                    if($ParamAttr{"Name"} eq "void") {
3306                        last;
3307                    }
3308                    if($Pos!=0 or $Type ne "MethodPtr")
3309                    {
3310                        $TypeAttr{"Param"}{$PPos++}{"type"} = $PTypeId;
3311                        push(@ParamTypeName, $ParamAttr{"Name"});
3312                    }
3313                    if($PTypeInfoId = getNextElem($PTypeInfoId)) {
3314                        $Pos+=1;
3315                    }
3316                    else {
3317                        last;
3318                    }
3319                }
3320                else {
3321                    last;
3322                }
3323            }
3324        }
3325        $MemPtrName .= " (".join(", ", @ParamTypeName).")";
3326    }
3327    $TypeAttr{"Name"} = formatName($MemPtrName, "T");
3328    return %TypeAttr;
3329}
3330
3331sub getTreeTypeName($)
3332{
3333    my $TypeId = $_[0];
3334    if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
3335    {
3336        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "integer_type")
3337        {
3338            if(my $Name = getNameByInfo($TypeId))
3339            { # bit_size_type
3340                return $Name;
3341            }
3342            elsif($Info=~/unsigned/) {
3343                return "unsigned int";
3344            }
3345            else {
3346                return "int";
3347            }
3348        }
3349        elsif($Info=~/name[ ]*:[ ]*@(\d+) /) {
3350            return getNameByInfo($1);
3351        }
3352    }
3353    return "";
3354}
3355
3356sub isFuncPtr($)
3357{
3358    my $Ptd = pointTo($_[0]);
3359    return 0 if(not $Ptd);
3360    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3361    {
3362        if($Info=~/unql[ ]*:/ and $Info!~/qual[ ]*:/) {
3363            return 0;
3364        }
3365    }
3366    if(my $InfoT1 = $LibInfo{$Version}{"info_type"}{$_[0]}
3367    and my $InfoT2 = $LibInfo{$Version}{"info_type"}{$Ptd})
3368    {
3369        if($InfoT1 eq "pointer_type"
3370        and $InfoT2 eq "function_type") {
3371            return 1;
3372        }
3373    }
3374    return 0;
3375}
3376
3377sub isMethodPtr($)
3378{
3379    my $Ptd = pointTo($_[0]);
3380    return 0 if(not $Ptd);
3381    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3382    {
3383        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "record_type"
3384        and $LibInfo{$Version}{"info_type"}{$Ptd} eq "method_type"
3385        and $Info=~/ ptrmem /) {
3386            return 1;
3387        }
3388    }
3389    return 0;
3390}
3391
3392sub isFieldPtr($)
3393{
3394    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3395    {
3396        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "offset_type"
3397        and $Info=~/ ptrmem /) {
3398            return 1;
3399        }
3400    }
3401    return 0;
3402}
3403
3404sub pointTo($)
3405{
3406    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3407    {
3408        if($Info=~/ptd[ ]*:[ ]*@(\d+)/) {
3409            return $1;
3410        }
3411    }
3412    return "";
3413}
3414
3415sub getTypeTypeByTypeId($)
3416{
3417    my $TypeId = $_[0];
3418    if(my $TType = $LibInfo{$Version}{"info_type"}{$TypeId})
3419    {
3420        my $NType = $NodeType{$TType};
3421        if($NType eq "Intrinsic") {
3422            return $NType;
3423        }
3424        elsif(isFuncPtr($TypeId)) {
3425            return "FuncPtr";
3426        }
3427        elsif(isMethodPtr($TypeId)) {
3428            return "MethodPtr";
3429        }
3430        elsif(isFieldPtr($TypeId)) {
3431            return "FieldPtr";
3432        }
3433        elsif($NType ne "Other") {
3434            return $NType;
3435        }
3436    }
3437    return "Unknown";
3438}
3439
3440my %UnQual = (
3441    "r"=>"restrict",
3442    "v"=>"volatile",
3443    "c"=>"const",
3444    "cv"=>"const volatile"
3445);
3446
3447sub getQual($)
3448{
3449    my $TypeId = $_[0];
3450    if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
3451    {
3452        my ($Qual, $To) = ();
3453        if($Info=~/qual[ ]*:[ ]*(r|c|v|cv) /) {
3454            $Qual = $UnQual{$1};
3455        }
3456        if($Info=~/unql[ ]*:[ ]*\@(\d+)/) {
3457            $To = $1;
3458        }
3459        if($Qual and $To) {
3460            return ($Qual, $To);
3461        }
3462    }
3463    return ();
3464}
3465
3466sub getQualType($)
3467{
3468    if($_[0] eq "const volatile") {
3469        return "ConstVolatile";
3470    }
3471    return ucfirst($_[0]);
3472}
3473
3474sub getTypeType($)
3475{
3476    my $TypeId = $_[0];
3477    my $TypeDeclId = getTypeDeclId($TypeId);
3478    if(defined $MissedTypedef{$Version}{$TypeId})
3479    { # support for old GCC versions
3480        if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq $TypeDeclId) {
3481            return "Typedef";
3482        }
3483    }
3484    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
3485    my ($Qual, $To) = getQual($TypeId);
3486    if(($Qual or $To) and $TypeDeclId
3487    and (getTypeId($TypeDeclId) ne $TypeId))
3488    { # qualified types (special)
3489        return getQualType($Qual);
3490    }
3491    elsif(not $MissedBase_R{$Version}{$TypeId}
3492    and isTypedef($TypeId)) {
3493        return "Typedef";
3494    }
3495    elsif($Qual)
3496    { # qualified types
3497        return getQualType($Qual);
3498    }
3499
3500    if($Info=~/unql[ ]*:[ ]*\@(\d+)/)
3501    { # typedef struct { ... } name
3502        $TypeTypedef{$Version}{$TypeId} = $1;
3503    }
3504
3505    my $TypeType = getTypeTypeByTypeId($TypeId);
3506    if($TypeType eq "Struct")
3507    {
3508        if($TypeDeclId
3509        and $LibInfo{$Version}{"info_type"}{$TypeDeclId} eq "template_decl") {
3510            return "Template";
3511        }
3512    }
3513    return $TypeType;
3514}
3515
3516sub isTypedef($)
3517{
3518    if($_[0])
3519    {
3520        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "vector_type")
3521        { # typedef float La_x86_64_xmm __attribute__ ((__vector_size__ (16)));
3522            return 0;
3523        }
3524        if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3525        {
3526            if(my $TDid = getTypeDeclId($_[0]))
3527            {
3528                if(getTypeId($TDid) eq $_[0]
3529                and getNameByInfo($TDid))
3530                {
3531                    if($Info=~/unql[ ]*:[ ]*\@(\d+) /) {
3532                        return $1;
3533                    }
3534                }
3535            }
3536        }
3537    }
3538    return 0;
3539}
3540
3541sub selectBaseType($)
3542{
3543    my $TypeId = $_[0];
3544    if(defined $MissedTypedef{$Version}{$TypeId})
3545    { # add missed typedefs
3546        if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq getTypeDeclId($TypeId)) {
3547            return ($TypeId, "");
3548        }
3549    }
3550    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
3551    my $InfoType = $LibInfo{$Version}{"info_type"}{$TypeId};
3552
3553    my $MB_R = $MissedBase_R{$Version}{$TypeId};
3554    my $MB = $MissedBase{$Version}{$TypeId};
3555
3556    my ($Qual, $To) = getQual($TypeId);
3557    if(($Qual or $To) and $Info=~/name[ ]*:[ ]*\@(\d+) /
3558    and (getTypeId($1) ne $TypeId)
3559    and (not $MB_R or getTypeId($1) ne $MB_R))
3560    { # qualified types (special)
3561        return (getTypeId($1), $Qual);
3562    }
3563    elsif($MB)
3564    { # add base
3565        return ($MB, "");
3566    }
3567    elsif(not $MB_R and my $Bid = isTypedef($TypeId))
3568    { # typedefs
3569        return ($Bid, "");
3570    }
3571    elsif($Qual or $To)
3572    { # qualified types
3573        return ($To, $Qual);
3574    }
3575    elsif($InfoType eq "reference_type")
3576    {
3577        if($Info=~/refd[ ]*:[ ]*@(\d+) /) {
3578            return ($1, "&");
3579        }
3580    }
3581    elsif($InfoType eq "array_type")
3582    {
3583        if($Info=~/elts[ ]*:[ ]*@(\d+) /) {
3584            return ($1, "");
3585        }
3586    }
3587    elsif($InfoType eq "pointer_type")
3588    {
3589        if($Info=~/ptd[ ]*:[ ]*@(\d+) /) {
3590            return ($1, "*");
3591        }
3592    }
3593
3594    return (0, "");
3595}
3596
3597sub getSymbolInfo_All()
3598{
3599    foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
3600    { # reverse order
3601        if($LibInfo{$Version}{"info_type"}{$_} eq "function_decl") {
3602            getSymbolInfo($_);
3603        }
3604    }
3605
3606    if($ADD_TMPL_INSTANCES)
3607    {
3608        # templates
3609        foreach my $Sid (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$Version}}))
3610        {
3611            my %Map = ();
3612
3613            if(my $ClassId = $SymbolInfo{$Version}{$Sid}{"Class"})
3614            {
3615                if(defined $TemplateMap{$Version}{$ClassId})
3616                {
3617                    foreach (keys(%{$TemplateMap{$Version}{$ClassId}})) {
3618                        $Map{$_} = $TemplateMap{$Version}{$ClassId}{$_};
3619                    }
3620                }
3621            }
3622
3623            if(defined $TemplateMap{$Version}{$Sid})
3624            {
3625                foreach (keys(%{$TemplateMap{$Version}{$Sid}})) {
3626                    $Map{$_} = $TemplateMap{$Version}{$Sid}{$_};
3627                }
3628            }
3629
3630            if(defined $SymbolInfo{$Version}{$Sid}{"Param"})
3631            {
3632                foreach (keys(%{$SymbolInfo{$Version}{$Sid}{"Param"}}))
3633                {
3634                    my $PTid = $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"};
3635                    $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"} = instType(\%Map, $PTid, $Version);
3636                }
3637            }
3638            if(my $Return = $SymbolInfo{$Version}{$Sid}{"Return"}) {
3639                $SymbolInfo{$Version}{$Sid}{"Return"} = instType(\%Map, $Return, $Version);
3640            }
3641        }
3642    }
3643}
3644
3645sub getVarInfo_All()
3646{
3647    foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
3648    { # reverse order
3649        if($LibInfo{$Version}{"info_type"}{$_} eq "var_decl") {
3650            getVarInfo($_);
3651        }
3652    }
3653}
3654
3655sub isBuiltIn($) {
3656    return ($_[0] and $_[0]=~/\<built\-in\>|\<internal\>|\A\./);
3657}
3658
3659sub getVarInfo($)
3660{
3661    my $InfoId = $_[0];
3662    if(my $NSid = getTreeAttr_Scpe($InfoId))
3663    {
3664        my $NSInfoType = $LibInfo{$Version}{"info_type"}{$NSid};
3665        if($NSInfoType and $NSInfoType eq "function_decl") {
3666            return;
3667        }
3668    }
3669    ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId);
3670    if(not $SymbolInfo{$Version}{$InfoId}{"Header"}
3671    or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"})) {
3672        delete($SymbolInfo{$Version}{$InfoId});
3673        return;
3674    }
3675    my $ShortName = getTreeStr(getTreeAttr_Name($InfoId));
3676    if(not $ShortName) {
3677        delete($SymbolInfo{$Version}{$InfoId});
3678        return;
3679    }
3680    if($ShortName=~/\Atmp_add_class_\d+\Z/) {
3681        delete($SymbolInfo{$Version}{$InfoId});
3682        return;
3683    }
3684    $SymbolInfo{$Version}{$InfoId}{"ShortName"} = $ShortName;
3685    if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId)))
3686    {
3687        if($OSgroup eq "windows")
3688        { # cut the offset
3689            $MnglName=~s/\@\d+\Z//g;
3690        }
3691        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName;
3692    }
3693    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}
3694    and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0)
3695    { # validate mangled name
3696        delete($SymbolInfo{$Version}{$InfoId});
3697        return;
3698    }
3699    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}
3700    and index($ShortName, "_Z")==0)
3701    { # _ZTS, etc.
3702        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3703    }
3704    if(isPrivateData($SymbolInfo{$Version}{$InfoId}{"MnglName"}))
3705    { # non-public global data
3706        delete($SymbolInfo{$Version}{$InfoId});
3707        return;
3708    }
3709    $SymbolInfo{$Version}{$InfoId}{"Data"} = 1;
3710    if(my $Rid = getTypeId($InfoId))
3711    {
3712        if(not defined $TypeInfo{$Version}{$Rid}
3713        or not $TypeInfo{$Version}{$Rid}{"Name"})
3714        {
3715            delete($SymbolInfo{$Version}{$InfoId});
3716            return;
3717        }
3718        $SymbolInfo{$Version}{$InfoId}{"Return"} = $Rid;
3719        my $Val = getDataVal($InfoId, $Rid);
3720        if(defined $Val) {
3721            $SymbolInfo{$Version}{$InfoId}{"Value"} = $Val;
3722        }
3723    }
3724    set_Class_And_Namespace($InfoId);
3725    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
3726    {
3727        if(not defined $TypeInfo{$Version}{$ClassId}
3728        or not $TypeInfo{$Version}{$ClassId}{"Name"})
3729        {
3730            delete($SymbolInfo{$Version}{$InfoId});
3731            return;
3732        }
3733    }
3734    if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i)
3735    { # extern "C"
3736        $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C";
3737        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3738    }
3739    if($UserLang and $UserLang eq "C")
3740    { # --lang=C option
3741        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3742    }
3743    if(not $CheckHeadersOnly)
3744    {
3745        if(not $SymbolInfo{$Version}{$InfoId}{"Class"})
3746        {
3747            if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}
3748            or not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps"))
3749            {
3750                if(link_symbol($ShortName, $Version, "-Deps"))
3751                { # "const" global data is mangled as _ZL... in the TU dump
3752                  # but not mangled when compiling a C shared library
3753                    $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3754                }
3755            }
3756        }
3757    }
3758    if($COMMON_LANGUAGE{$Version} eq "C++")
3759    {
3760        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3761        { # for some symbols (_ZTI) the short name is the mangled name
3762            if(index($ShortName, "_Z")==0) {
3763                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3764            }
3765        }
3766        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3767        { # try to mangle symbol (link with libraries)
3768            $SymbolInfo{$Version}{$InfoId}{"MnglName"} = linkSymbol($InfoId);
3769        }
3770        if($OStarget eq "windows")
3771        {
3772            if(my $Mangled = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")})
3773            { # link MS C++ symbols from library with GCC symbols from headers
3774                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled;
3775            }
3776        }
3777    }
3778    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) {
3779        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3780    }
3781    if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3782    {
3783        if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version))
3784        { # non-target symbols
3785            delete($SymbolInfo{$Version}{$InfoId});
3786            return;
3787        }
3788    }
3789    if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"})
3790    {
3791        if(defined $MissedTypedef{$Version}{$Rid})
3792        {
3793            if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) {
3794                $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid;
3795            }
3796        }
3797    }
3798    setFuncAccess($InfoId);
3799    if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_ZTV")==0) {
3800        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
3801    }
3802    if($ShortName=~/\A(_Z|\?)/) {
3803        delete($SymbolInfo{$Version}{$InfoId}{"ShortName"});
3804    }
3805
3806    if($ExtraDump) {
3807        $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId);
3808    }
3809}
3810
3811sub isConstType($$)
3812{
3813    my ($TypeId, $LibVersion) = @_;
3814    my %Base = get_Type($TypeId, $LibVersion);
3815    while(defined $Base{"Type"} and $Base{"Type"} eq "Typedef") {
3816        %Base = get_OneStep_BaseType($Base{"Tid"}, $TypeInfo{$LibVersion});
3817    }
3818    return ($Base{"Type"} eq "Const");
3819}
3820
3821sub getTrivialName($$)
3822{
3823    my ($TypeInfoId, $TypeId) = @_;
3824    my %TypeAttr = ();
3825    $TypeAttr{"Name"} = getNameByInfo($TypeInfoId);
3826    if(not $TypeAttr{"Name"}) {
3827        $TypeAttr{"Name"} = getTreeTypeName($TypeId);
3828    }
3829    ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId);
3830    $TypeAttr{"Type"} = getTypeType($TypeId);
3831    $TypeAttr{"Name"}=~s/<(.+)\Z//g; # GCC 3.4.4 add template params to the name
3832    if(isAnon($TypeAttr{"Name"}))
3833    {
3834        my $NameSpaceId = $TypeId;
3835        while(my $NSId = getTreeAttr_Scpe(getTypeDeclId($NameSpaceId)))
3836        { # searching for a first not anon scope
3837            if($NSId eq $NameSpaceId) {
3838                last;
3839            }
3840            else
3841            {
3842                $TypeAttr{"NameSpace"} = getNameSpace(getTypeDeclId($TypeId));
3843                if(not $TypeAttr{"NameSpace"}
3844                or not isAnon($TypeAttr{"NameSpace"})) {
3845                    last;
3846                }
3847            }
3848            $NameSpaceId = $NSId;
3849        }
3850    }
3851    else
3852    {
3853        if(my $NameSpaceId = getTreeAttr_Scpe($TypeInfoId))
3854        {
3855            if($NameSpaceId ne $TypeId) {
3856                $TypeAttr{"NameSpace"} = getNameSpace($TypeInfoId);
3857            }
3858        }
3859    }
3860    if($TypeAttr{"NameSpace"} and not isAnon($TypeAttr{"Name"})) {
3861        $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
3862    }
3863    $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
3864    if(isAnon($TypeAttr{"Name"}))
3865    { # anon-struct-header.h-line
3866        $TypeAttr{"Name"} = "anon-".lc($TypeAttr{"Type"})."-";
3867        $TypeAttr{"Name"} .= $TypeAttr{"Header"}."-".$TypeAttr{"Line"};
3868        if($TypeAttr{"NameSpace"}) {
3869            $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
3870        }
3871    }
3872    if(defined $TemplateInstance{$Version}{"Type"}{$TypeId}
3873    and getTypeDeclId($TypeId) eq $TypeInfoId)
3874    {
3875        if(my @TParams = getTParams($TypeId, "Type")) {
3876            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."< ".join(", ", @TParams)." >", "T");
3877        }
3878        else {
3879            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."<...>", "T");
3880        }
3881    }
3882    return ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"});
3883}
3884
3885sub getTrivialTypeAttr($)
3886{
3887    my $TypeId = $_[0];
3888    my $TypeInfoId = getTypeDeclId($_[0]);
3889
3890    my %TypeAttr = ();
3891
3892    if($TemplateDecl{$Version}{$TypeId})
3893    { # template_decl
3894        $TypeAttr{"Template"} = 1;
3895    }
3896
3897    setTypeAccess($TypeId, \%TypeAttr);
3898    ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId);
3899    if(isBuiltIn($TypeAttr{"Header"}))
3900    {
3901        delete($TypeAttr{"Header"});
3902        delete($TypeAttr{"Line"});
3903    }
3904
3905    $TypeAttr{"Type"} = getTypeType($TypeId);
3906    ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}) = getTrivialName($TypeInfoId, $TypeId);
3907    if(not $TypeAttr{"Name"}) {
3908        return ();
3909    }
3910    if(not $TypeAttr{"NameSpace"}) {
3911        delete($TypeAttr{"NameSpace"});
3912    }
3913
3914    if($TypeAttr{"Type"} eq "Intrinsic")
3915    {
3916        if(defined $TypeAttr{"Header"})
3917        {
3918            if($TypeAttr{"Header"}=~/\Adump[1-2]\.[ih]\Z/)
3919            { # support for SUSE 11.2
3920              # integer_type has srcp dump{1-2}.i
3921                delete($TypeAttr{"Header"});
3922            }
3923        }
3924    }
3925
3926    my $Tmpl = undef;
3927
3928    if(defined $TemplateInstance{$Version}{"Type"}{$TypeId})
3929    {
3930        $Tmpl = $BasicTemplate{$Version}{$TypeId};
3931
3932        if(my @TParams = getTParams($TypeId, "Type"))
3933        {
3934            foreach my $Pos (0 .. $#TParams)
3935            {
3936                my $Val = $TParams[$Pos];
3937                $TypeAttr{"TParam"}{$Pos}{"name"} = $Val;
3938
3939                if(not defined $TypeAttr{"Template"})
3940                {
3941                    my %Base = get_BaseType($TemplateInstance{$Version}{"Type"}{$TypeId}{$Pos}, $Version);
3942
3943                    if($Base{"Type"} eq "TemplateParam"
3944                    or defined $Base{"Template"}) {
3945                        $TypeAttr{"Template"} = 1;
3946                    }
3947                }
3948
3949                if($Tmpl)
3950                {
3951                    if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos})
3952                    {
3953                        $TemplateMap{$Version}{$TypeId}{$Arg} = $Val;
3954
3955                        if($Val eq $Arg) {
3956                            $TypeAttr{"Template"} = 1;
3957                        }
3958                    }
3959                }
3960            }
3961
3962            if($Tmpl)
3963            {
3964                foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}}))
3965                {
3966                    if($Pos>$#TParams)
3967                    {
3968                        my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos};
3969                        $TemplateMap{$Version}{$TypeId}{$Arg} = "";
3970                    }
3971                }
3972            }
3973        }
3974
3975        if($ADD_TMPL_INSTANCES)
3976        {
3977            if($Tmpl)
3978            {
3979                if(my $MainInst = getTreeAttr_Type($Tmpl))
3980                {
3981                    if(not getTreeAttr_Flds($TypeId))
3982                    {
3983                        if(my $Flds = getTreeAttr_Flds($MainInst)) {
3984                            $LibInfo{$Version}{"info"}{$TypeId} .= " flds: \@$Flds ";
3985                        }
3986                    }
3987                    if(not getTreeAttr_Binf($TypeId))
3988                    {
3989                        if(my $Binf = getTreeAttr_Binf($MainInst)) {
3990                            $LibInfo{$Version}{"info"}{$TypeId} .= " binf: \@$Binf ";
3991                        }
3992                    }
3993                }
3994            }
3995        }
3996    }
3997
3998    my $StaticFields = setTypeMemb($TypeId, \%TypeAttr);
3999
4000    if(my $Size = getSize($TypeId))
4001    {
4002        $Size = $Size/$BYTE_SIZE;
4003        $TypeAttr{"Size"} = "$Size";
4004    }
4005    else
4006    {
4007        if($ExtraDump)
4008        {
4009            if(not defined $TypeAttr{"Memb"}
4010            and not $Tmpl)
4011            { # declaration only
4012                $TypeAttr{"Forward"} = 1;
4013            }
4014        }
4015    }
4016
4017    if($TypeAttr{"Type"} eq "Struct"
4018    and ($StaticFields or detect_lang($TypeId)))
4019    {
4020        $TypeAttr{"Type"} = "Class";
4021        $TypeAttr{"Copied"} = 1; # default, will be changed in getSymbolInfo()
4022    }
4023    if($TypeAttr{"Type"} eq "Struct"
4024    or $TypeAttr{"Type"} eq "Class")
4025    {
4026        my $Skip = setBaseClasses($TypeId, \%TypeAttr);
4027        if($Skip) {
4028            return ();
4029        }
4030    }
4031    if(my $Algn = getAlgn($TypeId)) {
4032        $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
4033    }
4034    setSpec($TypeId, \%TypeAttr);
4035
4036    if($TypeAttr{"Type"}=~/\A(Struct|Union|Enum)\Z/)
4037    {
4038        if(not $TypedefToAnon{$TypeId}
4039        and not defined $TemplateInstance{$Version}{"Type"}{$TypeId})
4040        {
4041            if(not isAnon($TypeAttr{"Name"})) {
4042                $TypeAttr{"Name"} = lc($TypeAttr{"Type"})." ".$TypeAttr{"Name"};
4043            }
4044        }
4045    }
4046
4047    $TypeAttr{"Tid"} = $TypeId;
4048    if(my $VTable = $ClassVTable_Content{$Version}{$TypeAttr{"Name"}})
4049    {
4050        my @Entries = split(/\n/, $VTable);
4051        foreach (1 .. $#Entries)
4052        {
4053            my $Entry = $Entries[$_];
4054            if($Entry=~/\A(\d+)\s+(.+)\Z/) {
4055                $TypeAttr{"VTable"}{$1} = simplifyVTable($2);
4056            }
4057        }
4058    }
4059
4060    if($TypeAttr{"Type"} eq "Enum")
4061    {
4062        if(not $TypeAttr{"NameSpace"})
4063        {
4064            foreach my $Pos (keys(%{$TypeAttr{"Memb"}}))
4065            {
4066                my $MName = $TypeAttr{"Memb"}{$Pos}{"name"};
4067                my $MVal = $TypeAttr{"Memb"}{$Pos}{"value"};
4068                $EnumConstants{$Version}{$MName} = {
4069                    "Value"=>$MVal,
4070                    "Header"=>$TypeAttr{"Header"}
4071                };
4072                if(isAnon($TypeAttr{"Name"}))
4073                {
4074                    if($ExtraDump
4075                    or is_target_header($TypeAttr{"Header"}, $Version))
4076                    {
4077                        %{$Constants{$Version}{$MName}} = (
4078                            "Value" => $MVal,
4079                            "Header" => $TypeAttr{"Header"}
4080                        );
4081                    }
4082                }
4083            }
4084        }
4085    }
4086    if($ExtraDump)
4087    {
4088        if(defined $TypedefToAnon{$TypeId}) {
4089            $TypeAttr{"AnonTypedef"} = 1;
4090        }
4091    }
4092
4093    return %TypeAttr;
4094}
4095
4096sub simplifyVTable($)
4097{
4098    my $Content = $_[0];
4099    if($Content=~s/ \[with (.+)]//)
4100    { # std::basic_streambuf<_CharT, _Traits>::imbue [with _CharT = char, _Traits = std::char_traits<char>]
4101        if(my @Elems = separate_Params($1, 0, 0))
4102        {
4103            foreach my $Elem (@Elems)
4104            {
4105                if($Elem=~/\A(.+?)\s*=\s*(.+?)\Z/)
4106                {
4107                    my ($Arg, $Val) = ($1, $2);
4108
4109                    if(defined $DEFAULT_STD_ARGS{$Arg}) {
4110                        $Content=~s/,\s*$Arg\b//g;
4111                    }
4112                    else {
4113                        $Content=~s/\b$Arg\b/$Val/g;
4114                    }
4115                }
4116            }
4117        }
4118    }
4119
4120    return $Content;
4121}
4122
4123sub detect_lang($)
4124{
4125    my $TypeId = $_[0];
4126    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4127    if(check_gcc($GCC_PATH, "4"))
4128    { # GCC 4 fncs-node points to only non-artificial methods
4129        return ($Info=~/(fncs)[ ]*:[ ]*@(\d+) /);
4130    }
4131    else
4132    { # GCC 3
4133        my $Fncs = getTreeAttr_Fncs($TypeId);
4134        while($Fncs)
4135        {
4136            if($LibInfo{$Version}{"info"}{$Fncs}!~/artificial/) {
4137                return 1;
4138            }
4139            $Fncs = getTreeAttr_Chan($Fncs);
4140        }
4141    }
4142    return 0;
4143}
4144
4145sub setSpec($$)
4146{
4147    my ($TypeId, $TypeAttr) = @_;
4148    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4149    if($Info=~/\s+spec\s+/) {
4150        $TypeAttr->{"Spec"} = 1;
4151    }
4152}
4153
4154sub setBaseClasses($$)
4155{
4156    my ($TypeId, $TypeAttr) = @_;
4157    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4158    if(my $Binf = getTreeAttr_Binf($TypeId))
4159    {
4160        my $Info = $LibInfo{$Version}{"info"}{$Binf};
4161        my $Pos = 0;
4162        while($Info=~s/(pub|public|prot|protected|priv|private|)[ ]+binf[ ]*:[ ]*@(\d+) //)
4163        {
4164            my ($Access, $BInfoId) = ($1, $2);
4165            my $ClassId = getBinfClassId($BInfoId);
4166
4167            if($ClassId eq $TypeId)
4168            { # class A<N>:public A<N-1>
4169                next;
4170            }
4171
4172            my $CType = $LibInfo{$Version}{"info_type"}{$ClassId};
4173            if(not $CType or $CType eq "template_type_parm"
4174            or $CType eq "typename_type")
4175            { # skip
4176                # return 1;
4177            }
4178            my $BaseInfo = $LibInfo{$Version}{"info"}{$BInfoId};
4179            if($Access=~/prot/) {
4180                $TypeAttr->{"Base"}{$ClassId}{"access"} = "protected";
4181            }
4182            elsif($Access=~/priv/) {
4183                $TypeAttr->{"Base"}{$ClassId}{"access"} = "private";
4184            }
4185            $TypeAttr->{"Base"}{$ClassId}{"pos"} = "$Pos";
4186            if($BaseInfo=~/virt/)
4187            { # virtual base
4188                $TypeAttr->{"Base"}{$ClassId}{"virtual"} = 1;
4189            }
4190            $Class_SubClasses{$Version}{$ClassId}{$TypeId}=1;
4191            $Pos += 1;
4192        }
4193    }
4194    return 0;
4195}
4196
4197sub getBinfClassId($)
4198{
4199    my $Info = $LibInfo{$Version}{"info"}{$_[0]};
4200    if($Info=~/type[ ]*:[ ]*@(\d+) /) {
4201        return $1;
4202    }
4203
4204    return "";
4205}
4206
4207sub unmangledFormat($$)
4208{
4209    my ($Name, $LibVersion) = @_;
4210    $Name = uncover_typedefs($Name, $LibVersion);
4211    while($Name=~s/([^\w>*])(const|volatile)(,|>|\Z)/$1$3/g){};
4212    $Name=~s/\(\w+\)(\d)/$1/;
4213    return $Name;
4214}
4215
4216sub modelUnmangled($$)
4217{
4218    my ($InfoId, $Compiler) = @_;
4219    if($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId}) {
4220        return $Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId};
4221    }
4222    my $PureSignature = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
4223    if($SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
4224        $PureSignature = "~".$PureSignature;
4225    }
4226    if(not $SymbolInfo{$Version}{$InfoId}{"Data"})
4227    {
4228        my (@Params, @ParamTypes) = ();
4229        if(defined $SymbolInfo{$Version}{$InfoId}{"Param"}
4230        and not $SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
4231            @Params = keys(%{$SymbolInfo{$Version}{$InfoId}{"Param"}});
4232        }
4233        foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
4234        { # checking parameters
4235            my $PId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"type"};
4236            my $PName = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"name"};
4237            my %PType = get_PureType($PId, $TypeInfo{$Version});
4238            my $PTName = unmangledFormat($PType{"Name"}, $Version);
4239
4240            if($PName eq "this"
4241            and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method")
4242            {
4243                next;
4244            }
4245
4246            $PTName=~s/\b(restrict|register)\b//g;
4247            if($Compiler eq "MSVC") {
4248                $PTName=~s/\blong long\b/__int64/;
4249            }
4250            @ParamTypes = (@ParamTypes, $PTName);
4251        }
4252        if(@ParamTypes) {
4253            $PureSignature .= "(".join(", ", @ParamTypes).")";
4254        }
4255        else
4256        {
4257            if($Compiler eq "MSVC")
4258            {
4259                $PureSignature .= "(void)";
4260            }
4261            else
4262            { # GCC
4263                $PureSignature .= "()";
4264            }
4265        }
4266        $PureSignature = delete_keywords($PureSignature);
4267    }
4268    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
4269    {
4270        my $ClassName = unmangledFormat($TypeInfo{$Version}{$ClassId}{"Name"}, $Version);
4271        $PureSignature = $ClassName."::".$PureSignature;
4272    }
4273    elsif(my $NS = $SymbolInfo{$Version}{$InfoId}{"NameSpace"}) {
4274        $PureSignature = $NS."::".$PureSignature;
4275    }
4276    if($SymbolInfo{$Version}{$InfoId}{"Const"}) {
4277        $PureSignature .= " const";
4278    }
4279    if($SymbolInfo{$Version}{$InfoId}{"Volatile"}) {
4280        $PureSignature .= " volatile";
4281    }
4282    my $ShowReturn = 0;
4283    if($Compiler eq "MSVC"
4284    and $SymbolInfo{$Version}{$InfoId}{"Data"})
4285    {
4286        $ShowReturn=1;
4287    }
4288    elsif(defined $TemplateInstance{$Version}{"Func"}{$InfoId}
4289    and keys(%{$TemplateInstance{$Version}{"Func"}{$InfoId}}))
4290    {
4291        $ShowReturn=1;
4292    }
4293    if($ShowReturn)
4294    { # mangled names for template function specializations include return value
4295        if(my $ReturnId = $SymbolInfo{$Version}{$InfoId}{"Return"})
4296        {
4297            my %RType = get_PureType($ReturnId, $TypeInfo{$Version});
4298            my $ReturnName = unmangledFormat($RType{"Name"}, $Version);
4299            $PureSignature = $ReturnName." ".$PureSignature;
4300        }
4301    }
4302    return ($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId} = formatName($PureSignature, "S"));
4303}
4304
4305sub mangle_symbol($$$)
4306{ # mangling for simple methods
4307  # see gcc-4.6.0/gcc/cp/mangle.c
4308    my ($InfoId, $LibVersion, $Compiler) = @_;
4309    if($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler}) {
4310        return $Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler};
4311    }
4312    my $Mangled = "";
4313    if($Compiler eq "GCC") {
4314        $Mangled = mangle_symbol_GCC($InfoId, $LibVersion);
4315    }
4316    elsif($Compiler eq "MSVC") {
4317        $Mangled = mangle_symbol_MSVC($InfoId, $LibVersion);
4318    }
4319    return ($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler} = $Mangled);
4320}
4321
4322sub mangle_symbol_MSVC($$)
4323{ # TODO
4324    my ($InfoId, $LibVersion) = @_;
4325    return "";
4326}
4327
4328sub mangle_symbol_GCC($$)
4329{ # see gcc-4.6.0/gcc/cp/mangle.c
4330    my ($InfoId, $LibVersion) = @_;
4331    my ($Mangled, $ClassId, $NameSpace) = ("_Z", 0, "");
4332    my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"};
4333    my %Repl = ();# SN_ replacements
4334    if($ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
4335    {
4336        my $MangledClass = mangle_param($ClassId, $LibVersion, \%Repl);
4337        if($MangledClass!~/\AN/) {
4338            $MangledClass = "N".$MangledClass;
4339        }
4340        else {
4341            $MangledClass=~s/E\Z//;
4342        }
4343        if($SymbolInfo{$LibVersion}{$InfoId}{"Volatile"}) {
4344            $MangledClass=~s/\AN/NV/;
4345        }
4346        if($SymbolInfo{$LibVersion}{$InfoId}{"Const"}) {
4347            $MangledClass=~s/\AN/NK/;
4348        }
4349        $Mangled .= $MangledClass;
4350    }
4351    elsif($NameSpace = $SymbolInfo{$LibVersion}{$InfoId}{"NameSpace"})
4352    { # mangled by name due to the absence of structured info
4353        my $MangledNS = mangle_ns($NameSpace, $LibVersion, \%Repl);
4354        if($MangledNS!~/\AN/) {
4355            $MangledNS = "N".$MangledNS;
4356        }
4357        else {
4358            $MangledNS=~s/E\Z//;
4359        }
4360        $Mangled .= $MangledNS;
4361    }
4362    my ($ShortName, $TmplParams) = template_Base($SymbolInfo{$LibVersion}{$InfoId}{"ShortName"});
4363    my @TParams = ();
4364    if(my @TPos = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}}))
4365    { # parsing mode
4366        foreach (@TPos) {
4367            push(@TParams, $SymbolInfo{$LibVersion}{$InfoId}{"TParam"}{$_}{"name"});
4368        }
4369    }
4370    elsif($TmplParams)
4371    { # remangling mode
4372      # support for old ABI dumps
4373        @TParams = separate_Params($TmplParams, 0, 0);
4374    }
4375    if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"}) {
4376        $Mangled .= "C1";
4377    }
4378    elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
4379        $Mangled .= "D0";
4380    }
4381    elsif($ShortName)
4382    {
4383        if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
4384        {
4385            if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
4386            and isConstType($Return, $LibVersion))
4387            { # "const" global data is mangled as _ZL...
4388                $Mangled .= "L";
4389            }
4390        }
4391        if($ShortName=~/\Aoperator(\W.*)\Z/)
4392        {
4393            my $Op = $1;
4394            $Op=~s/\A[ ]+//g;
4395            if(my $OpMngl = $OperatorMangling{$Op}) {
4396                $Mangled .= $OpMngl;
4397            }
4398            else { # conversion operator
4399                $Mangled .= "cv".mangle_param(getTypeIdByName($Op, $LibVersion), $LibVersion, \%Repl);
4400            }
4401        }
4402        else {
4403            $Mangled .= length($ShortName).$ShortName;
4404        }
4405        if(@TParams)
4406        { # templates
4407            $Mangled .= "I";
4408            foreach my $TParam (@TParams) {
4409                $Mangled .= mangle_template_param($TParam, $LibVersion, \%Repl);
4410            }
4411            $Mangled .= "E";
4412        }
4413        if(not $ClassId and @TParams) {
4414            add_substitution($ShortName, \%Repl, 0);
4415        }
4416    }
4417    if($ClassId or $NameSpace) {
4418        $Mangled .= "E";
4419    }
4420    if(@TParams)
4421    {
4422        if($Return) {
4423            $Mangled .= mangle_param($Return, $LibVersion, \%Repl);
4424        }
4425    }
4426    if(not $SymbolInfo{$LibVersion}{$InfoId}{"Data"})
4427    {
4428        my @Params = ();
4429        if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
4430        and not $SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
4431            @Params = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}});
4432        }
4433        foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
4434        { # checking parameters
4435            my $ParamType_Id = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$ParamPos}{"type"};
4436            $Mangled .= mangle_param($ParamType_Id, $LibVersion, \%Repl);
4437        }
4438        if(not @Params) {
4439            $Mangled .= "v";
4440        }
4441    }
4442    $Mangled = correct_incharge($InfoId, $LibVersion, $Mangled);
4443    $Mangled = write_stdcxx_substitution($Mangled);
4444    if($Mangled eq "_Z") {
4445        return "";
4446    }
4447    return $Mangled;
4448}
4449
4450sub correct_incharge($$$)
4451{
4452    my ($InfoId, $LibVersion, $Mangled) = @_;
4453    if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"})
4454    {
4455        if($MangledNames{$LibVersion}{$Mangled}) {
4456            $Mangled=~s/C1([EI])/C2$1/;
4457        }
4458    }
4459    elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"})
4460    {
4461        if($MangledNames{$LibVersion}{$Mangled}) {
4462            $Mangled=~s/D0([EI])/D1$1/;
4463        }
4464        if($MangledNames{$LibVersion}{$Mangled}) {
4465            $Mangled=~s/D1([EI])/D2$1/;
4466        }
4467    }
4468    return $Mangled;
4469}
4470
4471sub template_Base($)
4472{ # NOTE: std::_Vector_base<mysqlpp::mysql_type_info>::_Vector_impl
4473  # NOTE: operators: >>, <<
4474    my $Name = $_[0];
4475    if($Name!~/>\Z/ or $Name!~/</) {
4476        return $Name;
4477    }
4478    my $TParams = $Name;
4479    while(my $CPos = find_center($TParams, "<"))
4480    { # search for the last <T>
4481        $TParams = substr($TParams, $CPos);
4482    }
4483    if($TParams=~s/\A<(.+)>\Z/$1/) {
4484        $Name=~s/<\Q$TParams\E>\Z//;
4485    }
4486    else
4487    { # error
4488        $TParams = "";
4489    }
4490    return ($Name, $TParams);
4491}
4492
4493sub get_sub_ns($)
4494{
4495    my $Name = $_[0];
4496    my @NS = ();
4497    while(my $CPos = find_center($Name, ":"))
4498    {
4499        push(@NS, substr($Name, 0, $CPos));
4500        $Name = substr($Name, $CPos);
4501        $Name=~s/\A:://;
4502    }
4503    return (join("::", @NS), $Name);
4504}
4505
4506sub mangle_ns($$$)
4507{
4508    my ($Name, $LibVersion, $Repl) = @_;
4509    if(my $Tid = $TName_Tid{$LibVersion}{$Name})
4510    {
4511        my $Mangled = mangle_param($Tid, $LibVersion, $Repl);
4512        $Mangled=~s/\AN(.+)E\Z/$1/;
4513        return $Mangled;
4514
4515    }
4516    else
4517    {
4518        my ($MangledNS, $SubNS) = ("", "");
4519        ($SubNS, $Name) = get_sub_ns($Name);
4520        if($SubNS) {
4521            $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
4522        }
4523        $MangledNS .= length($Name).$Name;
4524        add_substitution($MangledNS, $Repl, 0);
4525        return $MangledNS;
4526    }
4527}
4528
4529sub mangle_param($$$)
4530{
4531    my ($PTid, $LibVersion, $Repl) = @_;
4532    my ($MPrefix, $Mangled) = ("", "");
4533    my %ReplCopy = %{$Repl};
4534    my %BaseType = get_BaseType($PTid, $LibVersion);
4535    my $BaseType_Name = $BaseType{"Name"};
4536    $BaseType_Name=~s/\A(struct|union|enum) //g;
4537    if(not $BaseType_Name) {
4538        return "";
4539    }
4540    my ($ShortName, $TmplParams) = template_Base($BaseType_Name);
4541    my $Suffix = get_BaseTypeQual($PTid, $LibVersion);
4542    while($Suffix=~s/\s*(const|volatile|restrict)\Z//g){};
4543    while($Suffix=~/(&|\*|const)\Z/)
4544    {
4545        if($Suffix=~s/[ ]*&\Z//) {
4546            $MPrefix .= "R";
4547        }
4548        if($Suffix=~s/[ ]*\*\Z//) {
4549            $MPrefix .= "P";
4550        }
4551        if($Suffix=~s/[ ]*const\Z//)
4552        {
4553            if($MPrefix=~/R|P/
4554            or $Suffix=~/&|\*/) {
4555                $MPrefix .= "K";
4556            }
4557        }
4558        if($Suffix=~s/[ ]*volatile\Z//) {
4559            $MPrefix .= "V";
4560        }
4561        #if($Suffix=~s/[ ]*restrict\Z//) {
4562            #$MPrefix .= "r";
4563        #}
4564    }
4565    if(my $Token = $IntrinsicMangling{$BaseType_Name}) {
4566        $Mangled .= $Token;
4567    }
4568    elsif($BaseType{"Type"}=~/(Class|Struct|Union|Enum)/)
4569    {
4570        my @TParams = ();
4571        if(my @TPos = keys(%{$BaseType{"TParam"}}))
4572        { # parsing mode
4573            foreach (@TPos) {
4574                push(@TParams, $BaseType{"TParam"}{$_}{"name"});
4575            }
4576        }
4577        elsif($TmplParams)
4578        { # remangling mode
4579          # support for old ABI dumps
4580            @TParams = separate_Params($TmplParams, 0, 0);
4581        }
4582        my $MangledNS = "";
4583        my ($SubNS, $SName) = get_sub_ns($ShortName);
4584        if($SubNS) {
4585            $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
4586        }
4587        $MangledNS .= length($SName).$SName;
4588        if(@TParams) {
4589            add_substitution($MangledNS, $Repl, 0);
4590        }
4591        $Mangled .= "N".$MangledNS;
4592        if(@TParams)
4593        { # templates
4594            $Mangled .= "I";
4595            foreach my $TParam (@TParams) {
4596                $Mangled .= mangle_template_param($TParam, $LibVersion, $Repl);
4597            }
4598            $Mangled .= "E";
4599        }
4600        $Mangled .= "E";
4601    }
4602    elsif($BaseType{"Type"}=~/(FuncPtr|MethodPtr)/)
4603    {
4604        if($BaseType{"Type"} eq "MethodPtr") {
4605            $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl)."F";
4606        }
4607        else {
4608            $Mangled .= "PF";
4609        }
4610        $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
4611        my @Params = keys(%{$BaseType{"Param"}});
4612        foreach my $Num (sort {int($a)<=>int($b)} @Params) {
4613            $Mangled .= mangle_param($BaseType{"Param"}{$Num}{"type"}, $LibVersion, $Repl);
4614        }
4615        if(not @Params) {
4616            $Mangled .= "v";
4617        }
4618        $Mangled .= "E";
4619    }
4620    elsif($BaseType{"Type"} eq "FieldPtr")
4621    {
4622        $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl);
4623        $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
4624    }
4625    $Mangled = $MPrefix.$Mangled;# add prefix (RPK)
4626    if(my $Optimized = write_substitution($Mangled, \%ReplCopy))
4627    {
4628        if($Mangled eq $Optimized)
4629        {
4630            if($ShortName!~/::/)
4631            { # remove "N ... E"
4632                if($MPrefix) {
4633                    $Mangled=~s/\A($MPrefix)N(.+)E\Z/$1$2/g;
4634                }
4635                else {
4636                    $Mangled=~s/\AN(.+)E\Z/$1/g;
4637                }
4638            }
4639        }
4640        else {
4641            $Mangled = $Optimized;
4642        }
4643    }
4644    add_substitution($Mangled, $Repl, 1);
4645    return $Mangled;
4646}
4647
4648sub mangle_template_param($$$)
4649{ # types + literals
4650    my ($TParam, $LibVersion, $Repl) = @_;
4651    if(my $TPTid = $TName_Tid{$LibVersion}{$TParam}) {
4652        return mangle_param($TPTid, $LibVersion, $Repl);
4653    }
4654    elsif($TParam=~/\A(\d+)(\w+)\Z/)
4655    { # class_name<1u>::method(...)
4656        return "L".$IntrinsicMangling{$ConstantSuffixR{$2}}.$1."E";
4657    }
4658    elsif($TParam=~/\A\(([\w ]+)\)(\d+)\Z/)
4659    { # class_name<(signed char)1>::method(...)
4660        return "L".$IntrinsicMangling{$1}.$2."E";
4661    }
4662    elsif($TParam eq "true")
4663    { # class_name<true>::method(...)
4664        return "Lb1E";
4665    }
4666    elsif($TParam eq "false")
4667    { # class_name<true>::method(...)
4668        return "Lb0E";
4669    }
4670    else { # internal error
4671        return length($TParam).$TParam;
4672    }
4673}
4674
4675sub add_substitution($$$)
4676{
4677    my ($Value, $Repl, $Rec) = @_;
4678    if($Rec)
4679    { # subtypes
4680        my @Subs = ($Value);
4681        while($Value=~s/\A(R|P|K)//) {
4682            push(@Subs, $Value);
4683        }
4684        foreach (reverse(@Subs)) {
4685            add_substitution($_, $Repl, 0);
4686        }
4687        return;
4688    }
4689    return if($Value=~/\AS(\d*)_\Z/);
4690    $Value=~s/\AN(.+)E\Z/$1/g;
4691    return if(defined $Repl->{$Value});
4692    return if(length($Value)<=1);
4693    return if($StdcxxMangling{$Value});
4694    # check for duplicates
4695    my $Base = $Value;
4696    foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
4697    {
4698        my $Num = $Repl->{$Type};
4699        my $Replace = macro_mangle($Num);
4700        $Base=~s/\Q$Replace\E/$Type/;
4701    }
4702    if(my $OldNum = $Repl->{$Base})
4703    {
4704        $Repl->{$Value} = $OldNum;
4705        return;
4706    }
4707    my @Repls = sort {$b<=>$a} values(%{$Repl});
4708    if(@Repls) {
4709        $Repl->{$Value} = $Repls[0]+1;
4710    }
4711    else {
4712        $Repl->{$Value} = -1;
4713    }
4714    # register duplicates
4715    # upward
4716    $Base = $Value;
4717    foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
4718    {
4719        next if($Base eq $Type);
4720        my $Num = $Repl->{$Type};
4721        my $Replace = macro_mangle($Num);
4722        $Base=~s/\Q$Type\E/$Replace/;
4723        $Repl->{$Base} = $Repl->{$Value};
4724    }
4725}
4726
4727sub macro_mangle($)
4728{
4729    my $Num = $_[0];
4730    if($Num==-1) {
4731        return "S_";
4732    }
4733    else
4734    {
4735        my $Code = "";
4736        if($Num<10)
4737        { # S0_, S1_, S2_, ...
4738            $Code = $Num;
4739        }
4740        elsif($Num>=10 and $Num<=35)
4741        { # SA_, SB_, SC_, ...
4742            $Code = chr(55+$Num);
4743        }
4744        else
4745        { # S10_, S11_, S12_
4746            $Code = $Num-26; # 26 is length of english alphabet
4747        }
4748        return "S".$Code."_";
4749    }
4750}
4751
4752sub write_stdcxx_substitution($)
4753{
4754    my $Mangled = $_[0];
4755    if($StdcxxMangling{$Mangled}) {
4756        return $StdcxxMangling{$Mangled};
4757    }
4758    else
4759    {
4760        my @Repls = keys(%StdcxxMangling);
4761        @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
4762        foreach my $MangledType (@Repls)
4763        {
4764            my $Replace = $StdcxxMangling{$MangledType};
4765            #if($Mangled!~/$Replace/) {
4766                $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
4767                $Mangled=~s/\Q$MangledType\E/$Replace/g;
4768            #}
4769        }
4770    }
4771    return $Mangled;
4772}
4773
4774sub write_substitution($$)
4775{
4776    my ($Mangled, $Repl) = @_;
4777    if(defined $Repl->{$Mangled}
4778    and my $MnglNum = $Repl->{$Mangled}) {
4779        $Mangled = macro_mangle($MnglNum);
4780    }
4781    else
4782    {
4783        my @Repls = keys(%{$Repl});
4784        #@Repls = sort {$Repl->{$a}<=>$Repl->{$b}} @Repls;
4785        # FIXME: how to apply replacements? by num or by pos
4786        @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
4787        foreach my $MangledType (@Repls)
4788        {
4789            my $Replace = macro_mangle($Repl->{$MangledType});
4790            if($Mangled!~/$Replace/) {
4791                $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
4792                $Mangled=~s/\Q$MangledType\E/$Replace/g;
4793            }
4794        }
4795    }
4796    return $Mangled;
4797}
4798
4799sub delete_keywords($)
4800{
4801    my $TypeName = $_[0];
4802    $TypeName=~s/\b(enum|struct|union|class) //g;
4803    return $TypeName;
4804}
4805
4806sub uncover_typedefs($$)
4807{
4808    my ($TypeName, $LibVersion) = @_;
4809    return "" if(not $TypeName);
4810    if(defined $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName}) {
4811        return $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName};
4812    }
4813    my ($TypeName_New, $TypeName_Pre) = (formatName($TypeName, "T"), "");
4814    while($TypeName_New ne $TypeName_Pre)
4815    {
4816        $TypeName_Pre = $TypeName_New;
4817        my $TypeName_Copy = $TypeName_New;
4818        my %Words = ();
4819        while($TypeName_Copy=~s/\b([a-z_]([\w:]*\w|))\b//io)
4820        {
4821            if(not $Intrinsic_Keywords{$1}) {
4822                $Words{$1} = 1;
4823            }
4824        }
4825        foreach my $Word (keys(%Words))
4826        {
4827            my $BaseType_Name = $Typedef_BaseName{$LibVersion}{$Word};
4828            next if(not $BaseType_Name);
4829            next if($TypeName_New=~/\b(struct|union|enum)\s\Q$Word\E\b/);
4830            if($BaseType_Name=~/\([\*]+\)/)
4831            { # FuncPtr
4832                if($TypeName_New=~/\Q$Word\E(.*)\Z/)
4833                {
4834                    my $Type_Suffix = $1;
4835                    $TypeName_New = $BaseType_Name;
4836                    if($TypeName_New=~s/\(([\*]+)\)/($1 $Type_Suffix)/) {
4837                        $TypeName_New = formatName($TypeName_New, "T");
4838                    }
4839                }
4840            }
4841            else
4842            {
4843                if($TypeName_New=~s/\b\Q$Word\E\b/$BaseType_Name/g) {
4844                    $TypeName_New = formatName($TypeName_New, "T");
4845                }
4846            }
4847        }
4848    }
4849    return ($Cache{"uncover_typedefs"}{$LibVersion}{$TypeName} = $TypeName_New);
4850}
4851
4852sub isInternal($)
4853{
4854    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
4855    {
4856        if($Info=~/mngl[ ]*:[ ]*@(\d+) /)
4857        {
4858            if($LibInfo{$Version}{"info"}{$1}=~/\*[ ]*INTERNAL[ ]*\*/)
4859            { # _ZN7mysqlpp8DateTimeC1ERKS0_ *INTERNAL*
4860                return 1;
4861            }
4862        }
4863    }
4864    return 0;
4865}
4866
4867sub getDataVal($$)
4868{
4869    my ($InfoId, $TypeId) = @_;
4870    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4871    {
4872        if($Info=~/init[ ]*:[ ]*@(\d+) /)
4873        {
4874            if(defined $LibInfo{$Version}{"info_type"}{$1}
4875            and $LibInfo{$Version}{"info_type"}{$1} eq "nop_expr")
4876            {
4877                if(my $Nop = getTreeAttr_Op($1))
4878                {
4879                    if(defined $LibInfo{$Version}{"info_type"}{$Nop}
4880                    and $LibInfo{$Version}{"info_type"}{$Nop} eq "addr_expr")
4881                    {
4882                        if(my $Addr = getTreeAttr_Op($1)) {
4883                            return getInitVal($Addr, $TypeId);
4884                        }
4885                    }
4886                }
4887            }
4888            else {
4889                return getInitVal($1, $TypeId);
4890            }
4891        }
4892    }
4893    return undef;
4894}
4895
4896sub getInitVal($$)
4897{
4898    my ($InfoId, $TypeId) = @_;
4899    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4900    {
4901        if(my $InfoType = $LibInfo{$Version}{"info_type"}{$InfoId})
4902        {
4903            if($InfoType eq "integer_cst")
4904            {
4905                my $Val = getNodeIntCst($InfoId);
4906                if($TypeId and $TypeInfo{$Version}{$TypeId}{"Name"}=~/\Achar(| const)\Z/)
4907                { # characters
4908                    $Val = chr($Val);
4909                }
4910                return $Val;
4911            }
4912            elsif($InfoType eq "string_cst") {
4913                return getNodeStrCst($InfoId);
4914            }
4915            elsif($InfoType eq "var_decl")
4916            {
4917                if(my $Name = getNodeStrCst(getTreeAttr_Mngl($InfoId))) {
4918                    return $Name;
4919                }
4920            }
4921        }
4922    }
4923    return undef;
4924}
4925
4926sub set_Class_And_Namespace($)
4927{
4928    my $InfoId = $_[0];
4929    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4930    {
4931        if($Info=~/scpe[ ]*:[ ]*@(\d+) /)
4932        {
4933            my $NSInfoId = $1;
4934            if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId})
4935            {
4936                if($InfoType eq "namespace_decl") {
4937                    $SymbolInfo{$Version}{$InfoId}{"NameSpace"} = getNameSpace($InfoId);
4938                }
4939                elsif($InfoType eq "record_type") {
4940                    $SymbolInfo{$Version}{$InfoId}{"Class"} = $NSInfoId;
4941                }
4942            }
4943        }
4944    }
4945    if($SymbolInfo{$Version}{$InfoId}{"Class"}
4946    or $SymbolInfo{$Version}{$InfoId}{"NameSpace"})
4947    {
4948        if($COMMON_LANGUAGE{$Version} ne "C++")
4949        { # skip
4950            return 1;
4951        }
4952    }
4953
4954    return 0;
4955}
4956
4957sub debugMangling($)
4958{
4959    my $LibVersion = $_[0];
4960    my %Mangled = ();
4961    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
4962    {
4963        if(my $Mngl = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
4964        {
4965            if($Mngl=~/\A(_Z|\?)/) {
4966                $Mangled{$Mngl}=$InfoId;
4967            }
4968        }
4969    }
4970    translateSymbols(keys(%Mangled), $LibVersion);
4971    foreach my $Mngl (keys(%Mangled))
4972    {
4973        my $U1 = modelUnmangled($Mangled{$Mngl}, "GCC");
4974        my $U2 = $tr_name{$Mngl};
4975        if($U1 ne $U2) {
4976            printMsg("INFO", "INCORRECT MANGLING:\n  $Mngl\n  $U1\n  $U2\n");
4977        }
4978    }
4979}
4980
4981sub linkSymbol($)
4982{ # link symbols from shared libraries
4983  # with the symbols from header files
4984    my $InfoId = $_[0];
4985    # try to mangle symbol
4986    if((not check_gcc($GCC_PATH, "4") and $SymbolInfo{$Version}{$InfoId}{"Class"})
4987    or (check_gcc($GCC_PATH, "4") and not $SymbolInfo{$Version}{$InfoId}{"Class"})
4988    or $GCC_MISSED_MNGL)
4989    { # GCC 3.x doesn't mangle class methods names in the TU dump (only functions and global data)
4990      # GCC 4.x doesn't mangle C++ functions in the TU dump (only class methods) except extern "C" functions
4991      # GCC 4.8.[012] and 6.[12].0 don't mangle anything
4992        if(not $CheckHeadersOnly)
4993        {
4994            if(my $Mangled = $mangled_name_gcc{modelUnmangled($InfoId, "GCC")}) {
4995                return correct_incharge($InfoId, $Version, $Mangled);
4996            }
4997        }
4998        if($CheckHeadersOnly
4999        or not $BinaryOnly
5000        or $GCC_MISSED_MNGL)
5001        { # 1. --headers-only mode
5002          # 2. not mangled src-only symbols
5003            if(my $Mangled = mangle_symbol($InfoId, $Version, "GCC")) {
5004                return $Mangled;
5005            }
5006        }
5007    }
5008    return "";
5009}
5010
5011sub setLanguage($$)
5012{
5013    my ($LibVersion, $Lang) = @_;
5014    if(not $UserLang) {
5015        $COMMON_LANGUAGE{$LibVersion} = $Lang;
5016    }
5017}
5018
5019sub getSymbolInfo($)
5020{
5021    my $InfoId = $_[0];
5022    if(isInternal($InfoId)) {
5023        return;
5024    }
5025    ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId);
5026    if(not $SymbolInfo{$Version}{$InfoId}{"Header"}
5027    or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"}))
5028    {
5029        delete($SymbolInfo{$Version}{$InfoId});
5030        return;
5031    }
5032    setFuncAccess($InfoId);
5033    setFuncKind($InfoId);
5034    if($SymbolInfo{$Version}{$InfoId}{"PseudoTemplate"})
5035    {
5036        delete($SymbolInfo{$Version}{$InfoId});
5037        return;
5038    }
5039
5040    $SymbolInfo{$Version}{$InfoId}{"Type"} = getFuncType($InfoId);
5041    if(my $Return = getFuncReturn($InfoId))
5042    {
5043        if(not defined $TypeInfo{$Version}{$Return}
5044        or not $TypeInfo{$Version}{$Return}{"Name"})
5045        {
5046            delete($SymbolInfo{$Version}{$InfoId});
5047            return;
5048        }
5049        $SymbolInfo{$Version}{$InfoId}{"Return"} = $Return;
5050    }
5051    if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"})
5052    {
5053        if(defined $MissedTypedef{$Version}{$Rid})
5054        {
5055            if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) {
5056                $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid;
5057            }
5058        }
5059    }
5060    if(not $SymbolInfo{$Version}{$InfoId}{"Return"}) {
5061        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
5062    }
5063    my $Orig = getFuncOrig($InfoId);
5064    $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getFuncShortName($Orig);
5065    if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "\._")!=-1)
5066    {
5067        delete($SymbolInfo{$Version}{$InfoId});
5068        return;
5069    }
5070
5071    if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "tmp_add_func")==0)
5072    {
5073        delete($SymbolInfo{$Version}{$InfoId});
5074        return;
5075    }
5076
5077    if(defined $TemplateInstance{$Version}{"Func"}{$Orig})
5078    {
5079        my $Tmpl = $BasicTemplate{$Version}{$InfoId};
5080
5081        my @TParams = getTParams($Orig, "Func");
5082        if(not @TParams)
5083        {
5084            delete($SymbolInfo{$Version}{$InfoId});
5085            return;
5086        }
5087        foreach my $Pos (0 .. $#TParams)
5088        {
5089            my $Val = $TParams[$Pos];
5090            $SymbolInfo{$Version}{$InfoId}{"TParam"}{$Pos}{"name"} = $Val;
5091
5092            if($Tmpl)
5093            {
5094                if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos})
5095                {
5096                    $TemplateMap{$Version}{$InfoId}{$Arg} = $Val;
5097                }
5098            }
5099        }
5100
5101        if($Tmpl)
5102        {
5103            foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}}))
5104            {
5105                if($Pos>$#TParams)
5106                {
5107                    my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos};
5108                    $TemplateMap{$Version}{$InfoId}{$Arg} = "";
5109                }
5110            }
5111        }
5112
5113        if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\Aoperator\W+\Z/)
5114        { # operator<< <T>, operator>> <T>
5115            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= " ";
5116        }
5117        if(@TParams) {
5118            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<".join(", ", @TParams).">";
5119        }
5120        else {
5121            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<...>";
5122        }
5123        $SymbolInfo{$Version}{$InfoId}{"ShortName"} = formatName($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "S");
5124    }
5125    else
5126    { # support for GCC 3.4
5127        $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//;
5128    }
5129    if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId)))
5130    {
5131        if($OSgroup eq "windows")
5132        { # cut the offset
5133            $MnglName=~s/\@\d+\Z//g;
5134        }
5135        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName;
5136
5137        # NOTE: mangling of some symbols may change depending on GCC version
5138        # GCC 4.6: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2IT_EERKS_IT_E
5139        # GCC 4.7: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2ERKS1_
5140    }
5141
5142    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}
5143    and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0)
5144    {
5145        delete($SymbolInfo{$Version}{$InfoId});
5146        return;
5147    }
5148    if(not $SymbolInfo{$Version}{$InfoId}{"Destructor"})
5149    { # destructors have an empty parameter list
5150        my $Skip = setFuncParams($InfoId);
5151        if($Skip)
5152        {
5153            delete($SymbolInfo{$Version}{$InfoId});
5154            return;
5155        }
5156    }
5157    if($LibInfo{$Version}{"info"}{$InfoId}=~/ artificial /i) {
5158        $SymbolInfo{$Version}{$InfoId}{"Artificial"} = 1;
5159    }
5160
5161    if(set_Class_And_Namespace($InfoId))
5162    {
5163        delete($SymbolInfo{$Version}{$InfoId});
5164        return;
5165    }
5166
5167    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
5168    {
5169        if(not defined $TypeInfo{$Version}{$ClassId}
5170        or not $TypeInfo{$Version}{$ClassId}{"Name"})
5171        {
5172            delete($SymbolInfo{$Version}{$InfoId});
5173            return;
5174        }
5175    }
5176    if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i)
5177    { # extern "C"
5178        $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C";
5179        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5180    }
5181    if($UserLang and $UserLang eq "C")
5182    { # --lang=C option
5183        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5184    }
5185    if($COMMON_LANGUAGE{$Version} eq "C++")
5186    { # correct mangled & short names
5187      # C++ or --headers-only mode
5188        if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\A__(comp|base|deleting)_(c|d)tor\Z/)
5189        { # support for old GCC versions: reconstruct real names for constructors and destructors
5190            $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getNameByInfo(getTypeDeclId($SymbolInfo{$Version}{$InfoId}{"Class"}));
5191            $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//;
5192        }
5193        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5194        { # try to mangle symbol (link with libraries)
5195            if(my $Mangled = linkSymbol($InfoId)) {
5196                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled;
5197            }
5198        }
5199        if($OStarget eq "windows")
5200        { # link MS C++ symbols from library with GCC symbols from headers
5201            if(my $Mangled1 = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")})
5202            { # exported symbols
5203                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled1;
5204            }
5205            elsif(my $Mangled2 = mangle_symbol($InfoId, $Version, "MSVC"))
5206            { # pure virtual symbols
5207                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled2;
5208            }
5209        }
5210    }
5211    else
5212    { # not mangled in C
5213        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5214    }
5215    if(not $CheckHeadersOnly
5216    and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function"
5217    and not $SymbolInfo{$Version}{$InfoId}{"Class"})
5218    {
5219        my $Incorrect = 0;
5220
5221        if($SymbolInfo{$Version}{$InfoId}{"MnglName"})
5222        {
5223            if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")==0
5224            and not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps"))
5225            { # mangled in the TU dump, but not mangled in the library
5226                $Incorrect = 1;
5227            }
5228        }
5229        else
5230        {
5231            if($SymbolInfo{$Version}{$InfoId}{"Lang"} ne "C")
5232            { # all C++ functions are not mangled in the TU dump
5233                $Incorrect = 1;
5234            }
5235        }
5236        if($Incorrect)
5237        {
5238            if(link_symbol($SymbolInfo{$Version}{$InfoId}{"ShortName"}, $Version, "-Deps")) {
5239                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5240            }
5241        }
5242    }
5243    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5244    { # can't detect symbol name
5245        delete($SymbolInfo{$Version}{$InfoId});
5246        return;
5247    }
5248    if(not $SymbolInfo{$Version}{$InfoId}{"Constructor"}
5249    and my $Spec = getVirtSpec($Orig))
5250    { # identify virtual and pure virtual functions
5251      # NOTE: constructors cannot be virtual
5252      # NOTE: in GCC 4.7 D1 destructors have no virtual spec
5253      # in the TU dump, so taking it from the original symbol
5254        if(not ($SymbolInfo{$Version}{$InfoId}{"Destructor"}
5255        and $SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/D2E/))
5256        { # NOTE: D2 destructors are not present in a v-table
5257            $SymbolInfo{$Version}{$InfoId}{$Spec} = 1;
5258        }
5259    }
5260    if(isInline($InfoId)) {
5261        $SymbolInfo{$Version}{$InfoId}{"InLine"} = 1;
5262    }
5263    if(hasThrow($InfoId)) {
5264        $SymbolInfo{$Version}{$InfoId}{"Throw"} = 1;
5265    }
5266    if($SymbolInfo{$Version}{$InfoId}{"Constructor"}
5267    and my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
5268    {
5269        if(not $SymbolInfo{$Version}{$InfoId}{"InLine"}
5270        and not $SymbolInfo{$Version}{$InfoId}{"Artificial"})
5271        { # inline or auto-generated constructor
5272            delete($TypeInfo{$Version}{$ClassId}{"Copied"});
5273        }
5274    }
5275    if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5276    {
5277        if(not $ExtraDump)
5278        {
5279            if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version))
5280            { # non-target symbols
5281                delete($SymbolInfo{$Version}{$InfoId});
5282                return;
5283            }
5284        }
5285    }
5286    if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method"
5287    or $SymbolInfo{$Version}{$InfoId}{"Constructor"}
5288    or $SymbolInfo{$Version}{$InfoId}{"Destructor"}
5289    or $SymbolInfo{$Version}{$InfoId}{"Class"})
5290    {
5291        if($SymbolInfo{$Version}{$InfoId}{"MnglName"}!~/\A(_Z|\?)/)
5292        {
5293            delete($SymbolInfo{$Version}{$InfoId});
5294            return;
5295        }
5296    }
5297    if($SymbolInfo{$Version}{$InfoId}{"MnglName"})
5298    {
5299        if($MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}})
5300        { # one instance for one mangled name only
5301            delete($SymbolInfo{$Version}{$InfoId});
5302            return;
5303        }
5304        else {
5305            $MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}} = 1;
5306        }
5307    }
5308    if($SymbolInfo{$Version}{$InfoId}{"Constructor"}
5309    or $SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
5310        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
5311    }
5312    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/
5313    and $SymbolInfo{$Version}{$InfoId}{"Class"})
5314    {
5315        if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function")
5316        { # static methods
5317            $SymbolInfo{$Version}{$InfoId}{"Static"} = 1;
5318        }
5319    }
5320    if(getFuncLink($InfoId) eq "Static") {
5321        $SymbolInfo{$Version}{$InfoId}{"Static"} = 1;
5322    }
5323    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/)
5324    {
5325        if(my $Unmangled = $tr_name{$SymbolInfo{$Version}{$InfoId}{"MnglName"}})
5326        {
5327            if($Unmangled=~/\.\_\d/)
5328            {
5329                delete($SymbolInfo{$Version}{$InfoId});
5330                return;
5331            }
5332        }
5333    }
5334
5335    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(V|)K/) {
5336        $SymbolInfo{$Version}{$InfoId}{"Const"} = 1;
5337    }
5338    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(K|)V/) {
5339        $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1;
5340    }
5341
5342    if($WeakSymbols{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}}) {
5343        $SymbolInfo{$Version}{$InfoId}{"Weak"} = 1;
5344    }
5345
5346    if($ExtraDump) {
5347        $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId);
5348    }
5349}
5350
5351sub guessHeader($)
5352{
5353    my $InfoId = $_[0];
5354    my $ShortName = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5355    my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"};
5356    my $ClassName = $ClassId?get_ShortClass($ClassId, $Version):"";
5357    my $Header = $SymbolInfo{$Version}{$InfoId}{"Header"};
5358    if(my $HPath = $SymbolHeader{$Version}{$ClassName}{$ShortName})
5359    {
5360        if(get_filename($HPath) eq $Header)
5361        {
5362            my $HDir = get_filename(get_dirname($HPath));
5363            if($HDir ne "include"
5364            and $HDir=~/\A[a-z]+\Z/i) {
5365                return join_P($HDir, $Header);
5366            }
5367        }
5368    }
5369    return $Header;
5370}
5371
5372sub isInline($)
5373{ # "body: undefined" in the tree
5374  # -fkeep-inline-functions GCC option should be specified
5375    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5376    {
5377        if($Info=~/ undefined /i) {
5378            return 0;
5379        }
5380    }
5381    return 1;
5382}
5383
5384sub hasThrow($)
5385{
5386    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5387    {
5388        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5389            return getTreeAttr_Unql($1, "unql");
5390        }
5391    }
5392    return 1;
5393}
5394
5395sub getTypeId($)
5396{
5397    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5398    {
5399        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5400            return $1;
5401        }
5402    }
5403    return "";
5404}
5405
5406sub setTypeMemb($$)
5407{
5408    my ($TypeId, $TypeAttr) = @_;
5409    my $TypeType = $TypeAttr->{"Type"};
5410    my ($Pos, $UnnamedPos) = (0, 0);
5411    my $StaticFields = 0;
5412    if($TypeType eq "Enum")
5413    {
5414        my $MInfoId = getTreeAttr_Csts($TypeId);
5415        while($MInfoId)
5416        {
5417            $TypeAttr->{"Memb"}{$Pos}{"value"} = getEnumMembVal($MInfoId);
5418            my $MembName = getTreeStr(getTreeAttr_Purp($MInfoId));
5419            $TypeAttr->{"Memb"}{$Pos}{"name"} = $MembName;
5420            $EnumMembName_Id{$Version}{getTreeAttr_Valu($MInfoId)} = ($TypeAttr->{"NameSpace"})?$TypeAttr->{"NameSpace"}."::".$MembName:$MembName;
5421            $MInfoId = getNextElem($MInfoId);
5422            $Pos += 1;
5423        }
5424    }
5425    elsif($TypeType=~/\A(Struct|Class|Union)\Z/)
5426    {
5427        my $MInfoId = getTreeAttr_Flds($TypeId);
5428        while($MInfoId)
5429        {
5430            my $IType = $LibInfo{$Version}{"info_type"}{$MInfoId};
5431            my $MInfo = $LibInfo{$Version}{"info"}{$MInfoId};
5432            if(not $IType or $IType ne "field_decl")
5433            { # search for fields, skip other stuff in the declaration
5434
5435                if($IType eq "var_decl")
5436                { # static field
5437                    $StaticFields = 1;
5438                }
5439
5440                $MInfoId = getNextElem($MInfoId);
5441                next;
5442            }
5443            my $StructMembName = getTreeStr(getTreeAttr_Name($MInfoId));
5444            if(index($StructMembName, "_vptr.")==0)
5445            { # virtual tables
5446                $StructMembName = "_vptr";
5447            }
5448            if(not $StructMembName)
5449            { # unnamed fields
5450                if(index($TypeAttr->{"Name"}, "_type_info_pseudo")==-1)
5451                {
5452                    my $UnnamedTid = getTreeAttr_Type($MInfoId);
5453                    my $UnnamedTName = getNameByInfo(getTypeDeclId($UnnamedTid));
5454                    if(isAnon($UnnamedTName))
5455                    { # rename unnamed fields to unnamed0, unnamed1, ...
5456                        $StructMembName = "unnamed".($UnnamedPos++);
5457                    }
5458                }
5459            }
5460            if(not $StructMembName)
5461            { # unnamed fields and base classes
5462                $MInfoId = getNextElem($MInfoId);
5463                next;
5464            }
5465            my $MembTypeId = getTreeAttr_Type($MInfoId);
5466            if(defined $MissedTypedef{$Version}{$MembTypeId})
5467            {
5468                if(my $AddedTid = $MissedTypedef{$Version}{$MembTypeId}{"Tid"}) {
5469                    $MembTypeId = $AddedTid;
5470                }
5471            }
5472
5473            $TypeAttr->{"Memb"}{$Pos}{"type"} = $MembTypeId;
5474            $TypeAttr->{"Memb"}{$Pos}{"name"} = $StructMembName;
5475            if((my $Access = getTreeAccess($MInfoId)) ne "public")
5476            { # marked only protected and private, public by default
5477                $TypeAttr->{"Memb"}{$Pos}{"access"} = $Access;
5478            }
5479            if($MInfo=~/spec:\s*mutable /)
5480            { # mutable fields
5481                $TypeAttr->{"Memb"}{$Pos}{"mutable"} = 1;
5482            }
5483            if(my $Algn = getAlgn($MInfoId)) {
5484                $TypeAttr->{"Memb"}{$Pos}{"algn"} = $Algn;
5485            }
5486            if(my $BFSize = getBitField($MInfoId))
5487            { # in bits
5488                $TypeAttr->{"Memb"}{$Pos}{"bitfield"} = $BFSize;
5489            }
5490            else
5491            { # in bytes
5492                if($TypeAttr->{"Memb"}{$Pos}{"algn"}==1)
5493                { # template
5494                    delete($TypeAttr->{"Memb"}{$Pos}{"algn"});
5495                }
5496                else {
5497                    $TypeAttr->{"Memb"}{$Pos}{"algn"} /= $BYTE_SIZE;
5498                }
5499            }
5500
5501            $MInfoId = getNextElem($MInfoId);
5502            $Pos += 1;
5503        }
5504    }
5505
5506    return $StaticFields;
5507}
5508
5509sub setFuncParams($)
5510{
5511    my $InfoId = $_[0];
5512    my $ParamInfoId = getTreeAttr_Args($InfoId);
5513
5514    my $FType = getFuncType($InfoId);
5515
5516    if($FType eq "Method")
5517    { # check type of "this" pointer
5518        my $ObjectTypeId = getTreeAttr_Type($ParamInfoId);
5519        if(my $ObjectName = $TypeInfo{$Version}{$ObjectTypeId}{"Name"})
5520        {
5521            if($ObjectName=~/\bconst(| volatile)\*const\b/) {
5522                $SymbolInfo{$Version}{$InfoId}{"Const"} = 1;
5523            }
5524            if($ObjectName=~/\bvolatile\b/) {
5525                $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1;
5526            }
5527        }
5528        else
5529        { # skip
5530            return 1;
5531        }
5532        # skip "this"-parameter
5533        # $ParamInfoId = getNextElem($ParamInfoId);
5534    }
5535    my ($Pos, $PPos, $Vtt_Pos) = (0, 0, -1);
5536    while($ParamInfoId)
5537    { # formal args
5538        my $ParamTypeId = getTreeAttr_Type($ParamInfoId);
5539        my $ParamName = getTreeStr(getTreeAttr_Name($ParamInfoId));
5540        if(not $ParamName)
5541        { # unnamed
5542            $ParamName = "p".($PPos+1);
5543        }
5544        if(defined $MissedTypedef{$Version}{$ParamTypeId})
5545        {
5546            if(my $AddedTid = $MissedTypedef{$Version}{$ParamTypeId}{"Tid"}) {
5547                $ParamTypeId = $AddedTid;
5548            }
5549        }
5550        my $PType = $TypeInfo{$Version}{$ParamTypeId}{"Type"};
5551        if(not $PType or $PType eq "Unknown") {
5552            return 1;
5553        }
5554        my $PTName = $TypeInfo{$Version}{$ParamTypeId}{"Name"};
5555        if(not $PTName) {
5556            return 1;
5557        }
5558        if($PTName eq "void") {
5559            last;
5560        }
5561        if($ParamName eq "__vtt_parm"
5562        and $TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void const**")
5563        {
5564            $Vtt_Pos = $Pos;
5565            $ParamInfoId = getNextElem($ParamInfoId);
5566            next;
5567        }
5568        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5569
5570        if(my %Base = get_BaseType($ParamTypeId, $Version))
5571        {
5572            if(defined $Base{"Template"}) {
5573                return 1;
5574            }
5575        }
5576
5577        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = $ParamName;
5578        if(my $Algn = getAlgn($ParamInfoId)) {
5579            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"algn"} = $Algn/$BYTE_SIZE;
5580        }
5581        if($LibInfo{$Version}{"info"}{$ParamInfoId}=~/spec:\s*register /)
5582        { # foo(register type arg)
5583            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"reg"} = 1;
5584        }
5585        $ParamInfoId = getNextElem($ParamInfoId);
5586        $Pos += 1;
5587        if($ParamName ne "this" or $FType ne "Method") {
5588            $PPos += 1;
5589        }
5590    }
5591    if(setFuncArgs($InfoId, $Vtt_Pos)) {
5592        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = "-1";
5593    }
5594    return 0;
5595}
5596
5597sub setFuncArgs($$)
5598{
5599    my ($InfoId, $Vtt_Pos) = @_;
5600    my $FuncTypeId = getFuncTypeId($InfoId);
5601    my $ParamListElemId = getTreeAttr_Prms($FuncTypeId);
5602    my $FType = getFuncType($InfoId);
5603
5604    if($FType eq "Method")
5605    {
5606        # skip "this"-parameter
5607        # $ParamListElemId = getNextElem($ParamListElemId);
5608    }
5609    if(not $ParamListElemId)
5610    { # foo(...)
5611        return 1;
5612    }
5613    my $HaveVoid = 0;
5614    my ($Pos, $PPos) = (0, 0);
5615    while($ParamListElemId)
5616    { # actual params: may differ from formal args
5617      # formal int*const
5618      # actual: int*
5619        if($Vtt_Pos!=-1 and $Pos==$Vtt_Pos)
5620        {
5621            $Vtt_Pos=-1;
5622            $ParamListElemId = getNextElem($ParamListElemId);
5623            next;
5624        }
5625        my $ParamTypeId = getTreeAttr_Valu($ParamListElemId);
5626        if($TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void")
5627        {
5628            $HaveVoid = 1;
5629            last;
5630        }
5631        else
5632        {
5633            if(not defined $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"})
5634            {
5635                $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5636                if(not $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"})
5637                { # unnamed
5638                    $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = "p".($PPos+1);
5639                }
5640            }
5641            elsif(my $OldId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"})
5642            {
5643                if($Pos>0 or getFuncType($InfoId) ne "Method")
5644                { # params
5645                    if($OldId ne $ParamTypeId)
5646                    {
5647                        my %Old_Pure = get_PureType($OldId, $TypeInfo{$Version});
5648                        my %New_Pure = get_PureType($ParamTypeId, $TypeInfo{$Version});
5649
5650                        if($Old_Pure{"Name"} ne $New_Pure{"Name"}) {
5651                            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5652                        }
5653                    }
5654                }
5655            }
5656        }
5657        if(my $PurpId = getTreeAttr_Purp($ParamListElemId))
5658        { # default arguments
5659            if(my $PurpType = $LibInfo{$Version}{"info_type"}{$PurpId})
5660            {
5661                if($PurpType eq "nop_expr")
5662                { # func ( const char* arg = (const char*)(void*)0 )
5663                    $PurpId = getTreeAttr_Op($PurpId);
5664                }
5665                my $Val = getInitVal($PurpId, $ParamTypeId);
5666                if(defined $Val) {
5667                    $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"default"} = $Val;
5668                }
5669            }
5670        }
5671        $ParamListElemId = getNextElem($ParamListElemId);
5672        if($Pos!=0 or $FType ne "Method") {
5673            $PPos += 1;
5674        }
5675        $Pos += 1;
5676    }
5677    return ($Pos>=1 and not $HaveVoid);
5678}
5679
5680sub getTreeAttr_Chan($)
5681{
5682    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5683    {
5684        if($Info=~/chan[ ]*:[ ]*@(\d+) /) {
5685            return $1;
5686        }
5687    }
5688    return "";
5689}
5690
5691sub getTreeAttr_Chain($)
5692{
5693    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5694    {
5695        if($Info=~/chain[ ]*:[ ]*@(\d+) /) {
5696            return $1;
5697        }
5698    }
5699    return "";
5700}
5701
5702sub getTreeAttr_Unql($)
5703{
5704    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5705    {
5706        if($Info=~/unql[ ]*:[ ]*@(\d+) /) {
5707            return $1;
5708        }
5709    }
5710    return "";
5711}
5712
5713sub getTreeAttr_Scpe($)
5714{
5715    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5716    {
5717        if($Info=~/scpe[ ]*:[ ]*@(\d+) /) {
5718            return $1;
5719        }
5720    }
5721    return "";
5722}
5723
5724sub getTreeAttr_Type($)
5725{
5726    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5727    {
5728        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5729            return $1;
5730        }
5731    }
5732    return "";
5733}
5734
5735sub getTreeAttr_Name($)
5736{
5737    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5738    {
5739        if($Info=~/name[ ]*:[ ]*@(\d+) /) {
5740            return $1;
5741        }
5742    }
5743    return "";
5744}
5745
5746sub getTreeAttr_Mngl($)
5747{
5748    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5749    {
5750        if($Info=~/mngl[ ]*:[ ]*@(\d+) /) {
5751            return $1;
5752        }
5753    }
5754    return "";
5755}
5756
5757sub getTreeAttr_Prms($)
5758{
5759    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5760    {
5761        if($Info=~/prms[ ]*:[ ]*@(\d+) /) {
5762            return $1;
5763        }
5764    }
5765    return "";
5766}
5767
5768sub getTreeAttr_Fncs($)
5769{
5770    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5771    {
5772        if($Info=~/fncs[ ]*:[ ]*@(\d+) /) {
5773            return $1;
5774        }
5775    }
5776    return "";
5777}
5778
5779sub getTreeAttr_Csts($)
5780{
5781    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5782    {
5783        if($Info=~/csts[ ]*:[ ]*@(\d+) /) {
5784            return $1;
5785        }
5786    }
5787    return "";
5788}
5789
5790sub getTreeAttr_Purp($)
5791{
5792    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5793    {
5794        if($Info=~/purp[ ]*:[ ]*@(\d+) /) {
5795            return $1;
5796        }
5797    }
5798    return "";
5799}
5800
5801sub getTreeAttr_Op($)
5802{
5803    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5804    {
5805        if($Info=~/op 0[ ]*:[ ]*@(\d+) /) {
5806            return $1;
5807        }
5808    }
5809    return "";
5810}
5811
5812sub getTreeAttr_Valu($)
5813{
5814    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5815    {
5816        if($Info=~/valu[ ]*:[ ]*@(\d+) /) {
5817            return $1;
5818        }
5819    }
5820    return "";
5821}
5822
5823sub getTreeAttr_Flds($)
5824{
5825    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5826    {
5827        if($Info=~/flds[ ]*:[ ]*@(\d+) /) {
5828            return $1;
5829        }
5830    }
5831    return "";
5832}
5833
5834sub getTreeAttr_Binf($)
5835{
5836    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5837    {
5838        if($Info=~/binf[ ]*:[ ]*@(\d+) /) {
5839            return $1;
5840        }
5841    }
5842    return "";
5843}
5844
5845sub getTreeAttr_Args($)
5846{
5847    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5848    {
5849        if($Info=~/args[ ]*:[ ]*@(\d+) /) {
5850            return $1;
5851        }
5852    }
5853    return "";
5854}
5855
5856sub getTreeValue($)
5857{
5858    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5859    {
5860        if($Info=~/(low|int)[ ]*:[ ]*([^ ]+) /) {
5861            return $2;
5862        }
5863    }
5864    return "";
5865}
5866
5867sub getTreeAccess($)
5868{
5869    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5870    {
5871        if($Info=~/accs[ ]*:[ ]*([a-zA-Z]+) /)
5872        {
5873            my $Access = $1;
5874            if($Access eq "prot") {
5875                return "protected";
5876            }
5877            elsif($Access eq "priv") {
5878                return "private";
5879            }
5880        }
5881        elsif($Info=~/ protected /)
5882        { # support for old GCC versions
5883            return "protected";
5884        }
5885        elsif($Info=~/ private /)
5886        { # support for old GCC versions
5887            return "private";
5888        }
5889    }
5890    return "public";
5891}
5892
5893sub setFuncAccess($)
5894{
5895    my $Access = getTreeAccess($_[0]);
5896    if($Access eq "protected") {
5897        $SymbolInfo{$Version}{$_[0]}{"Protected"} = 1;
5898    }
5899    elsif($Access eq "private") {
5900        $SymbolInfo{$Version}{$_[0]}{"Private"} = 1;
5901    }
5902}
5903
5904sub setTypeAccess($$)
5905{
5906    my ($TypeId, $TypeAttr) = @_;
5907    my $Access = getTreeAccess($TypeId);
5908    if($Access eq "protected") {
5909        $TypeAttr->{"Protected"} = 1;
5910    }
5911    elsif($Access eq "private") {
5912        $TypeAttr->{"Private"} = 1;
5913    }
5914}
5915
5916sub setFuncKind($)
5917{
5918    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5919    {
5920        if($Info=~/pseudo tmpl/) {
5921            $SymbolInfo{$Version}{$_[0]}{"PseudoTemplate"} = 1;
5922        }
5923        elsif($Info=~/ constructor /) {
5924            $SymbolInfo{$Version}{$_[0]}{"Constructor"} = 1;
5925        }
5926        elsif($Info=~/ destructor /) {
5927            $SymbolInfo{$Version}{$_[0]}{"Destructor"} = 1;
5928        }
5929    }
5930}
5931
5932sub getVirtSpec($)
5933{
5934    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5935    {
5936        if($Info=~/spec[ ]*:[ ]*pure /) {
5937            return "PureVirt";
5938        }
5939        elsif($Info=~/spec[ ]*:[ ]*virt /) {
5940            return "Virt";
5941        }
5942        elsif($Info=~/ pure\s+virtual /)
5943        { # support for old GCC versions
5944            return "PureVirt";
5945        }
5946        elsif($Info=~/ virtual /)
5947        { # support for old GCC versions
5948            return "Virt";
5949        }
5950    }
5951    return "";
5952}
5953
5954sub getFuncLink($)
5955{
5956    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5957    {
5958        if($Info=~/link[ ]*:[ ]*static /) {
5959            return "Static";
5960        }
5961        elsif($Info=~/link[ ]*:[ ]*([a-zA-Z]+) /) {
5962            return $1;
5963        }
5964    }
5965    return "";
5966}
5967
5968sub select_Symbol_NS($$)
5969{
5970    my ($Symbol, $LibVersion) = @_;
5971    return "" if(not $Symbol or not $LibVersion);
5972    my $NS = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"};
5973    if(not $NS)
5974    {
5975        if(my $Class = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) {
5976            $NS = $TypeInfo{$LibVersion}{$Class}{"NameSpace"};
5977        }
5978    }
5979    if($NS)
5980    {
5981        if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5982            return $NS;
5983        }
5984        else
5985        {
5986            while($NS=~s/::[^:]+\Z//)
5987            {
5988                if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5989                    return $NS;
5990                }
5991            }
5992        }
5993    }
5994
5995    return "";
5996}
5997
5998sub select_Type_NS($$)
5999{
6000    my ($TypeName, $LibVersion) = @_;
6001    return "" if(not $TypeName or not $LibVersion);
6002    if(my $NS = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$TypeName}}{"NameSpace"})
6003    {
6004        if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
6005            return $NS;
6006        }
6007        else
6008        {
6009            while($NS=~s/::[^:]+\Z//)
6010            {
6011                if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
6012                    return $NS;
6013                }
6014            }
6015        }
6016    }
6017    return "";
6018}
6019
6020sub getNameSpace($)
6021{
6022    my $InfoId = $_[0];
6023    if(my $NSInfoId = getTreeAttr_Scpe($InfoId))
6024    {
6025        if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId})
6026        {
6027            if($InfoType eq "namespace_decl")
6028            {
6029                if($LibInfo{$Version}{"info"}{$NSInfoId}=~/name[ ]*:[ ]*@(\d+) /)
6030                {
6031                    my $NameSpace = getTreeStr($1);
6032                    if($NameSpace eq "::")
6033                    { # global namespace
6034                        return "";
6035                    }
6036                    if(my $BaseNameSpace = getNameSpace($NSInfoId)) {
6037                        $NameSpace = $BaseNameSpace."::".$NameSpace;
6038                    }
6039                    $NestedNameSpaces{$Version}{$NameSpace} = 1;
6040                    return $NameSpace;
6041                }
6042                else {
6043                    return "";
6044                }
6045            }
6046            elsif($InfoType ne "function_decl")
6047            { # inside data type
6048                my ($Name, $NameNS) = getTrivialName(getTypeDeclId($NSInfoId), $NSInfoId);
6049                return $Name;
6050            }
6051        }
6052    }
6053    return "";
6054}
6055
6056sub getEnumMembVal($)
6057{
6058    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6059    {
6060        if($Info=~/valu[ ]*:[ ]*\@(\d+)/)
6061        {
6062            if(my $VInfo = $LibInfo{$Version}{"info"}{$1})
6063            {
6064                if($VInfo=~/cnst[ ]*:[ ]*\@(\d+)/)
6065                { # in newer versions of GCC the value is in the "const_decl->cnst" node
6066                    return getTreeValue($1);
6067                }
6068                else
6069                { # some old versions of GCC (3.3) have the value in the "integer_cst" node
6070                    return getTreeValue($1);
6071                }
6072            }
6073        }
6074    }
6075    return "";
6076}
6077
6078sub getSize($)
6079{
6080    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6081    {
6082        if($Info=~/size[ ]*:[ ]*\@(\d+)/) {
6083            return getTreeValue($1);
6084        }
6085    }
6086    return 0;
6087}
6088
6089sub getAlgn($)
6090{
6091    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6092    {
6093        if($Info=~/algn[ ]*:[ ]*(\d+) /) {
6094            return $1;
6095        }
6096    }
6097    return "";
6098}
6099
6100sub getBitField($)
6101{
6102    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6103    {
6104        if($Info=~/ bitfield /) {
6105            return getSize($_[0]);
6106        }
6107    }
6108    return 0;
6109}
6110
6111sub getNextElem($)
6112{
6113    if(my $Chan = getTreeAttr_Chan($_[0])) {
6114        return $Chan;
6115    }
6116    elsif(my $Chain = getTreeAttr_Chain($_[0])) {
6117        return $Chain;
6118    }
6119    return "";
6120}
6121
6122sub registerHeader($$)
6123{ # input: absolute path of header, relative path or name
6124    my ($Header, $LibVersion) = @_;
6125    if(not $Header) {
6126        return "";
6127    }
6128    if(is_abs($Header) and not -f $Header)
6129    { # incorrect absolute path
6130        exitStatus("Access_Error", "can't access \'$Header\'");
6131    }
6132    if(skipHeader($Header, $LibVersion))
6133    { # skip
6134        return "";
6135    }
6136    if(my $Header_Path = identifyHeader($Header, $LibVersion))
6137    {
6138        detect_header_includes($Header_Path, $LibVersion);
6139
6140        if(defined $Tolerance and $Tolerance=~/3/)
6141        { # 3 - skip headers that include non-Linux headers
6142            if($OSgroup ne "windows")
6143            {
6144                foreach my $Inc (keys(%{$Header_Includes{$LibVersion}{$Header_Path}}))
6145                {
6146                    if(specificHeader($Inc, "windows")) {
6147                        return "";
6148                    }
6149                }
6150            }
6151        }
6152
6153        if(my $RHeader_Path = $Header_ErrorRedirect{$LibVersion}{$Header_Path})
6154        { # redirect
6155            if($Registered_Headers{$LibVersion}{$RHeader_Path}{"Identity"}
6156            or skipHeader($RHeader_Path, $LibVersion))
6157            { # skip
6158                return "";
6159            }
6160            $Header_Path = $RHeader_Path;
6161        }
6162        elsif($Header_ShouldNotBeUsed{$LibVersion}{$Header_Path})
6163        { # skip
6164            return "";
6165        }
6166
6167        if(my $HName = get_filename($Header_Path))
6168        { # register
6169            $Registered_Headers{$LibVersion}{$Header_Path}{"Identity"} = $HName;
6170            $HeaderName_Paths{$LibVersion}{$HName}{$Header_Path} = 1;
6171        }
6172
6173        if(($Header=~/\.(\w+)\Z/ and $1 ne "h")
6174        or $Header!~/\.(\w+)\Z/)
6175        { # hpp, hh, etc.
6176            setLanguage($LibVersion, "C++");
6177            $CPP_HEADERS = 1;
6178        }
6179
6180        if($CheckHeadersOnly
6181        and $Header=~/(\A|\/)c\+\+(\/|\Z)/)
6182        { # /usr/include/c++/4.6.1/...
6183            $STDCXX_TESTING = 1;
6184        }
6185
6186        return $Header_Path;
6187    }
6188    return "";
6189}
6190
6191sub registerDir($$$)
6192{
6193    my ($Dir, $WithDeps, $LibVersion) = @_;
6194    $Dir=~s/[\/\\]+\Z//g;
6195    return if(not $LibVersion or not $Dir or not -d $Dir);
6196    $Dir = get_abs_path($Dir);
6197
6198    my $Mode = "All";
6199    if($WithDeps)
6200    {
6201        if($RegisteredDirs{$LibVersion}{$Dir}{1}) {
6202            return;
6203        }
6204        elsif($RegisteredDirs{$LibVersion}{$Dir}{0}) {
6205            $Mode = "DepsOnly";
6206        }
6207    }
6208    else
6209    {
6210        if($RegisteredDirs{$LibVersion}{$Dir}{1}
6211        or $RegisteredDirs{$LibVersion}{$Dir}{0}) {
6212            return;
6213        }
6214    }
6215    $Header_Dependency{$LibVersion}{$Dir} = 1;
6216    $RegisteredDirs{$LibVersion}{$Dir}{$WithDeps} = 1;
6217    if($Mode eq "DepsOnly")
6218    {
6219        foreach my $Path (cmd_find($Dir,"d")) {
6220            $Header_Dependency{$LibVersion}{$Path} = 1;
6221        }
6222        return;
6223    }
6224    foreach my $Path (sort {length($b)<=>length($a)} cmd_find($Dir,"f"))
6225    {
6226        if($WithDeps)
6227        {
6228            my $SubDir = $Path;
6229            while(($SubDir = get_dirname($SubDir)) ne $Dir)
6230            { # register all sub directories
6231                $Header_Dependency{$LibVersion}{$SubDir} = 1;
6232            }
6233        }
6234        next if(is_not_header($Path));
6235        next if(ignore_path($Path));
6236        # Neighbors
6237        foreach my $Part (get_prefixes($Path)) {
6238            $Include_Neighbors{$LibVersion}{$Part} = $Path;
6239        }
6240    }
6241    if(get_filename($Dir) eq "include")
6242    { # search for "lib/include/" directory
6243        my $LibDir = $Dir;
6244        if($LibDir=~s/([\/\\])include\Z/$1lib/g and -d $LibDir) {
6245            registerDir($LibDir, $WithDeps, $LibVersion);
6246        }
6247    }
6248}
6249
6250sub parse_redirect($$$)
6251{
6252    my ($Content, $Path, $LibVersion) = @_;
6253    my @Errors = ();
6254    while($Content=~s/#\s*error\s+([^\n]+?)\s*(\n|\Z)//) {
6255        push(@Errors, $1);
6256    }
6257    my $Redirect = "";
6258    foreach (@Errors)
6259    {
6260        s/\s{2,}/ /g;
6261        if(/(only|must\ include
6262        |update\ to\ include
6263        |replaced\ with
6264        |replaced\ by|renamed\ to
6265        |\ is\ in|\ use)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))/ix)
6266        {
6267            $Redirect = $2;
6268            last;
6269        }
6270        elsif(/(include|use|is\ in)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))\ instead/i)
6271        {
6272            $Redirect = $2;
6273            last;
6274        }
6275        elsif(/this\ header\ should\ not\ be\ used
6276         |programs\ should\ not\ directly\ include
6277         |you\ should\ not\ (include|be\ (including|using)\ this\ (file|header))
6278         |is\ not\ supported\ API\ for\ general\ use
6279         |do\ not\ use
6280         |should\ not\ be\ (used|using)
6281         |cannot\ be\ included\ directly/ix and not /\ from\ /i) {
6282            $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1;
6283        }
6284    }
6285    if($Redirect)
6286    {
6287        $Redirect=~s/\A<//g;
6288        $Redirect=~s/>\Z//g;
6289    }
6290    return $Redirect;
6291}
6292
6293sub parse_includes($$)
6294{
6295    my ($Content, $Path) = @_;
6296    my %Includes = ();
6297    while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]*([<"].+?[">])[ \t]*//m)
6298    { # C/C++: include, Objective C/C++: import directive
6299        my $Header = $2;
6300        my $Method = substr($Header, 0, 1, "");
6301        substr($Header, length($Header)-1, 1, "");
6302        $Header = path_format($Header, $OSgroup);
6303        if($Method eq "\"" or is_abs($Header))
6304        {
6305            if(-e join_P(get_dirname($Path), $Header))
6306            { # relative path exists
6307                $Includes{$Header} = -1;
6308            }
6309            else
6310            { # include "..." that doesn't exist is equal to include <...>
6311                $Includes{$Header} = 2;
6312            }
6313        }
6314        else {
6315            $Includes{$Header} = 1;
6316        }
6317    }
6318    if($ExtraInfo)
6319    {
6320        while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]+(\w+)[ \t]*//m)
6321        { # FT_FREETYPE_H
6322            $Includes{$2} = 0;
6323        }
6324    }
6325    return \%Includes;
6326}
6327
6328sub ignore_path($)
6329{
6330    my $Path = $_[0];
6331    if($Path=~/\~\Z/)
6332    {# skipping system backup files
6333        return 1;
6334    }
6335    if($Path=~/(\A|[\/\\]+)(\.(svn|git|bzr|hg)|CVS)([\/\\]+|\Z)/)
6336    {# skipping hidden .svn, .git, .bzr, .hg and CVS directories
6337        return 1;
6338    }
6339    return 0;
6340}
6341
6342sub sortByWord($$)
6343{
6344    my ($ArrRef, $W) = @_;
6345    return if(length($W)<2);
6346    @{$ArrRef} = sort {get_filename($b)=~/\Q$W\E/i<=>get_filename($a)=~/\Q$W\E/i} @{$ArrRef};
6347}
6348
6349sub sortHeaders($$)
6350{
6351    my ($H1, $H2) = @_;
6352
6353    $H1=~s/\.[a-z]+\Z//ig;
6354    $H2=~s/\.[a-z]+\Z//ig;
6355
6356    my $Hname1 = get_filename($H1);
6357    my $Hname2 = get_filename($H2);
6358    my $HDir1 = get_dirname($H1);
6359    my $HDir2 = get_dirname($H2);
6360    my $Dirname1 = get_filename($HDir1);
6361    my $Dirname2 = get_filename($HDir2);
6362
6363    $HDir1=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
6364    $HDir2=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
6365
6366    if($_[0] eq $_[1]
6367    or $H1 eq $H2) {
6368        return 0;
6369    }
6370    elsif($H1=~/\A\Q$H2\E/) {
6371        return 1;
6372    }
6373    elsif($H2=~/\A\Q$H1\E/) {
6374        return -1;
6375    }
6376    elsif($HDir1=~/\Q$Hname1\E/i
6377    and $HDir2!~/\Q$Hname2\E/i)
6378    { # include/glib-2.0/glib.h
6379        return -1;
6380    }
6381    elsif($HDir2=~/\Q$Hname2\E/i
6382    and $HDir1!~/\Q$Hname1\E/i)
6383    { # include/glib-2.0/glib.h
6384        return 1;
6385    }
6386    elsif($Hname1=~/\Q$Dirname1\E/i
6387    and $Hname2!~/\Q$Dirname2\E/i)
6388    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
6389        return -1;
6390    }
6391    elsif($Hname2=~/\Q$Dirname2\E/i
6392    and $Hname1!~/\Q$Dirname1\E/i)
6393    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
6394        return 1;
6395    }
6396    elsif($Hname1=~/(config|lib|util)/i
6397    and $Hname2!~/(config|lib|util)/i)
6398    { # include/alsa/asoundlib.h
6399        return -1;
6400    }
6401    elsif($Hname2=~/(config|lib|util)/i
6402    and $Hname1!~/(config|lib|util)/i)
6403    { # include/alsa/asoundlib.h
6404        return 1;
6405    }
6406    else
6407    {
6408        my $R1 = checkRelevance($H1);
6409        my $R2 = checkRelevance($H2);
6410        if($R1 and not $R2)
6411        { # libebook/e-book.h
6412            return -1;
6413        }
6414        elsif($R2 and not $R1)
6415        { # libebook/e-book.h
6416            return 1;
6417        }
6418        else
6419        {
6420            return (lc($H1) cmp lc($H2));
6421        }
6422    }
6423}
6424
6425sub searchForHeaders($)
6426{
6427    my $LibVersion = $_[0];
6428
6429    # gcc standard include paths
6430    registerGccHeaders();
6431
6432    if($COMMON_LANGUAGE{$LibVersion} eq "C++" and not $STDCXX_TESTING)
6433    { # c++ standard include paths
6434        registerCppHeaders();
6435    }
6436
6437    # processing header paths
6438    foreach my $Path (@{$Descriptor{$LibVersion}{"IncludePaths"}},
6439    @{$Descriptor{$LibVersion}{"AddIncludePaths"}})
6440    {
6441        my $IPath = $Path;
6442        if($SystemRoot)
6443        {
6444            if(is_abs($Path)) {
6445                $Path = $SystemRoot.$Path;
6446            }
6447        }
6448        if(not -e $Path) {
6449            exitStatus("Access_Error", "can't access \'$Path\'");
6450        }
6451        elsif(-f $Path) {
6452            exitStatus("Access_Error", "\'$Path\' - not a directory");
6453        }
6454        elsif(-d $Path)
6455        {
6456            $Path = get_abs_path($Path);
6457            registerDir($Path, 0, $LibVersion);
6458            if(grep {$IPath eq $_} @{$Descriptor{$LibVersion}{"AddIncludePaths"}}) {
6459                push(@{$Add_Include_Paths{$LibVersion}}, $Path);
6460            }
6461            else {
6462                push(@{$Include_Paths{$LibVersion}}, $Path);
6463            }
6464        }
6465    }
6466    if(@{$Include_Paths{$LibVersion}}) {
6467        $INC_PATH_AUTODETECT{$LibVersion} = 0;
6468    }
6469
6470    # registering directories
6471    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
6472    {
6473        next if(not -e $Path);
6474        $Path = get_abs_path($Path);
6475        $Path = path_format($Path, $OSgroup);
6476        if(-d $Path) {
6477            registerDir($Path, 1, $LibVersion);
6478        }
6479        elsif(-f $Path)
6480        {
6481            my $Dir = get_dirname($Path);
6482            if(not grep { $Dir eq $_ } (@{$SystemPaths{"include"}})
6483            and not $LocalIncludes{$Dir})
6484            {
6485                registerDir($Dir, 1, $LibVersion);
6486                # if(my $OutDir = get_dirname($Dir))
6487                # { # registering the outer directory
6488                #     if(not grep { $OutDir eq $_ } (@{$SystemPaths{"include"}})
6489                #     and not $LocalIncludes{$OutDir}) {
6490                #         registerDir($OutDir, 0, $LibVersion);
6491                #     }
6492                # }
6493            }
6494        }
6495    }
6496
6497    # clean memory
6498    %RegisteredDirs = ();
6499
6500    # registering headers
6501    my $Position = 0;
6502    foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
6503    {
6504        if(is_abs($Dest) and not -e $Dest) {
6505            exitStatus("Access_Error", "can't access \'$Dest\'");
6506        }
6507        $Dest = path_format($Dest, $OSgroup);
6508        if(is_header($Dest, 1, $LibVersion))
6509        {
6510            if(my $HPath = registerHeader($Dest, $LibVersion)) {
6511                $Registered_Headers{$LibVersion}{$HPath}{"Pos"} = $Position++;
6512            }
6513        }
6514        elsif(-d $Dest)
6515        {
6516            my @Registered = ();
6517            foreach my $Path (cmd_find($Dest,"f"))
6518            {
6519                next if(ignore_path($Path));
6520                next if(not is_header($Path, 0, $LibVersion));
6521                if(my $HPath = registerHeader($Path, $LibVersion)) {
6522                    push(@Registered, $HPath);
6523                }
6524            }
6525            @Registered = sort {sortHeaders($a, $b)} @Registered;
6526            sortByWord(\@Registered, $TargetLibraryShortName);
6527            foreach my $Path (@Registered) {
6528                $Registered_Headers{$LibVersion}{$Path}{"Pos"} = $Position++;
6529            }
6530        }
6531        elsif(not defined $SkipUnidentified) {
6532            exitStatus("Access_Error", "can't identify \'$Dest\' as a header file");
6533        }
6534    }
6535
6536    if(defined $Tolerance and $Tolerance=~/4/)
6537    { # 4 - skip headers included by others
6538        foreach my $Path (keys(%{$Registered_Headers{$LibVersion}}))
6539        {
6540            if(defined $Header_Includes_R{$LibVersion}{$Path}) {
6541                delete($Registered_Headers{$LibVersion}{$Path});
6542            }
6543        }
6544    }
6545
6546    if(my $HList = $Descriptor{$LibVersion}{"IncludePreamble"})
6547    { # preparing preamble headers
6548        foreach my $Header (split(/\s*\n\s*/, $HList))
6549        {
6550            if(is_abs($Header) and not -f $Header) {
6551                exitStatus("Access_Error", "can't access file \'$Header\'");
6552            }
6553            $Header = path_format($Header, $OSgroup);
6554            if(my $Header_Path = is_header($Header, 1, $LibVersion))
6555            {
6556                next if(skipHeader($Header_Path, $LibVersion));
6557                push_U($Include_Preamble{$LibVersion}, $Header_Path);
6558            }
6559            elsif($SkipUnidentified) {
6560                exitStatus("Access_Error", "can't identify \'$Header\' as a header file");
6561            }
6562        }
6563    }
6564    foreach my $Header_Name (keys(%{$HeaderName_Paths{$LibVersion}}))
6565    { # set relative paths (for duplicates)
6566        if(keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})>=2)
6567        { # search for duplicates
6568            my $FirstPath = (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))[0];
6569            my $Prefix = get_dirname($FirstPath);
6570            while($Prefix=~/\A(.+)[\/\\]+[^\/\\]+\Z/)
6571            { # detect a shortest distinguishing prefix
6572                my $NewPrefix = $1;
6573                my %Identity = ();
6574                foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
6575                {
6576                    if($Path=~/\A\Q$Prefix\E[\/\\]+(.*)\Z/) {
6577                        $Identity{$Path} = $1;
6578                    }
6579                }
6580                if(keys(%Identity)==keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
6581                { # all names are different with current prefix
6582                    foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})) {
6583                        $Registered_Headers{$LibVersion}{$Path}{"Identity"} = $Identity{$Path};
6584                    }
6585                    last;
6586                }
6587                $Prefix = $NewPrefix; # increase prefix
6588            }
6589        }
6590    }
6591
6592    # clean memory
6593    %HeaderName_Paths = ();
6594
6595    foreach my $HeaderName (keys(%{$Include_Order{$LibVersion}}))
6596    { # ordering headers according to descriptor
6597        my $PairName = $Include_Order{$LibVersion}{$HeaderName};
6598        my ($Pos, $PairPos) = (-1, -1);
6599        my ($Path, $PairPath) = ();
6600        my @Paths = keys(%{$Registered_Headers{$LibVersion}});
6601        @Paths = sort {int($Registered_Headers{$LibVersion}{$a}{"Pos"})<=>int($Registered_Headers{$LibVersion}{$b}{"Pos"})} @Paths;
6602        foreach my $Header_Path (@Paths)
6603        {
6604            if(get_filename($Header_Path) eq $PairName)
6605            {
6606                $PairPos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
6607                $PairPath = $Header_Path;
6608            }
6609            if(get_filename($Header_Path) eq $HeaderName)
6610            {
6611                $Pos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
6612                $Path = $Header_Path;
6613            }
6614        }
6615        if($PairPos!=-1 and $Pos!=-1
6616        and int($PairPos)<int($Pos))
6617        {
6618            my %Tmp = %{$Registered_Headers{$LibVersion}{$Path}};
6619            %{$Registered_Headers{$LibVersion}{$Path}} = %{$Registered_Headers{$LibVersion}{$PairPath}};
6620            %{$Registered_Headers{$LibVersion}{$PairPath}} = %Tmp;
6621        }
6622    }
6623    if(not keys(%{$Registered_Headers{$LibVersion}})) {
6624        exitStatus("Error", "header files are not found in the ".$Descriptor{$LibVersion}{"Version"});
6625    }
6626}
6627
6628sub detect_real_includes($$)
6629{
6630    my ($AbsPath, $LibVersion) = @_;
6631    return () if(not $LibVersion or not $AbsPath or not -e $AbsPath);
6632    if($Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}
6633    or keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
6634        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6635    }
6636    $Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}=1;
6637
6638    my $Path = callPreprocessor($AbsPath, "", $LibVersion);
6639    return () if(not $Path);
6640    open(PREPROC, $Path);
6641    while(<PREPROC>)
6642    {
6643        if(/#\s+\d+\s+"([^"]+)"[\s\d]*\n/)
6644        {
6645            my $Include = path_format($1, $OSgroup);
6646            if($Include=~/\<(built\-in|internal|command(\-|\s)line)\>|\A\./) {
6647                next;
6648            }
6649            if($Include eq $AbsPath) {
6650                next;
6651            }
6652            $RecursiveIncludes{$LibVersion}{$AbsPath}{$Include} = 1;
6653        }
6654    }
6655    close(PREPROC);
6656    return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6657}
6658
6659sub detect_header_includes($$)
6660{
6661    my ($Path, $LibVersion) = @_;
6662    return if(not $LibVersion or not $Path);
6663    if(defined $Cache{"detect_header_includes"}{$LibVersion}{$Path}) {
6664        return;
6665    }
6666    $Cache{"detect_header_includes"}{$LibVersion}{$Path}=1;
6667
6668    if(not -e $Path) {
6669        return;
6670    }
6671
6672    my $Content = readFile($Path);
6673    if(my $Redirect = parse_redirect($Content, $Path, $LibVersion))
6674    { # detect error directive in headers
6675        if(my $RedirectPath = identifyHeader($Redirect, $LibVersion))
6676        {
6677            if($RedirectPath=~/\/usr\/include\// and $Path!~/\/usr\/include\//) {
6678                $RedirectPath = identifyHeader(get_filename($Redirect), $LibVersion);
6679            }
6680            if($RedirectPath ne $Path) {
6681                $Header_ErrorRedirect{$LibVersion}{$Path} = $RedirectPath;
6682            }
6683        }
6684        else
6685        { # can't find
6686            $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1;
6687        }
6688    }
6689    if(my $Inc = parse_includes($Content, $Path))
6690    {
6691        foreach my $Include (keys(%{$Inc}))
6692        { # detect includes
6693            $Header_Includes{$LibVersion}{$Path}{$Include} = $Inc->{$Include};
6694
6695            if(defined $Tolerance and $Tolerance=~/4/)
6696            {
6697                if(my $HPath = identifyHeader($Include, $LibVersion))
6698                {
6699                    $Header_Includes_R{$LibVersion}{$HPath}{$Path} = 1;
6700                }
6701            }
6702        }
6703    }
6704}
6705
6706sub fromLibc($)
6707{ # system GLIBC header
6708    my $Path = $_[0];
6709    my ($Dir, $Name) = separate_path($Path);
6710    if($OStarget eq "symbian")
6711    {
6712        if(get_filename($Dir) eq "libc" and $GlibcHeader{$Name})
6713        { # epoc32/include/libc/{stdio, ...}.h
6714            return 1;
6715        }
6716    }
6717    else
6718    {
6719        if($Dir eq "/usr/include" and $GlibcHeader{$Name})
6720        { # /usr/include/{stdio, ...}.h
6721            return 1;
6722        }
6723    }
6724    return 0;
6725}
6726
6727sub isLibcDir($)
6728{ # system GLIBC directory
6729    my $Dir = $_[0];
6730    my ($OutDir, $Name) = separate_path($Dir);
6731    if($OStarget eq "symbian")
6732    {
6733        if(get_filename($OutDir) eq "libc"
6734        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
6735        { # epoc32/include/libc/{sys,bits,asm,asm-*}/*.h
6736            return 1;
6737        }
6738    }
6739    else
6740    { # linux
6741        if($OutDir eq "/usr/include"
6742        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
6743        { # /usr/include/{sys,bits,asm,asm-*}/*.h
6744            return 1;
6745        }
6746    }
6747    return 0;
6748}
6749
6750sub detect_recursive_includes($$)
6751{
6752    my ($AbsPath, $LibVersion) = @_;
6753    return () if(not $AbsPath);
6754    if(isCyclical(\@RecurInclude, $AbsPath)) {
6755        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6756    }
6757    my ($AbsDir, $Name) = separate_path($AbsPath);
6758    if(isLibcDir($AbsDir))
6759    { # system GLIBC internals
6760        return () if(not $ExtraInfo);
6761    }
6762    if(keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
6763        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6764    }
6765    return () if($OSgroup ne "windows" and $Name=~/windows|win32|win64/i);
6766
6767    if($MAIN_CPP_DIR and $AbsPath=~/\A\Q$MAIN_CPP_DIR\E/ and not $STDCXX_TESTING)
6768    { # skip /usr/include/c++/*/ headers
6769        return () if(not $ExtraInfo);
6770    }
6771
6772    push(@RecurInclude, $AbsPath);
6773    if(grep { $AbsDir eq $_ } @DefaultGccPaths
6774    or (grep { $AbsDir eq $_ } @DefaultIncPaths and fromLibc($AbsPath)))
6775    { # check "real" (non-"model") include paths
6776        my @Paths = detect_real_includes($AbsPath, $LibVersion);
6777        pop(@RecurInclude);
6778        return @Paths;
6779    }
6780    if(not keys(%{$Header_Includes{$LibVersion}{$AbsPath}})) {
6781        detect_header_includes($AbsPath, $LibVersion);
6782    }
6783    foreach my $Include (keys(%{$Header_Includes{$LibVersion}{$AbsPath}}))
6784    {
6785        my $IncType = $Header_Includes{$LibVersion}{$AbsPath}{$Include};
6786        my $HPath = "";
6787        if($IncType<0)
6788        { # for #include "..."
6789            my $Candidate = join_P($AbsDir, $Include);
6790            if(-f $Candidate) {
6791                $HPath = realpath_F($Candidate);
6792            }
6793        }
6794        elsif($IncType>0
6795        and $Include=~/[\/\\]/) # and not find_in_defaults($Include)
6796        { # search for the nearest header
6797          # QtCore/qabstractanimation.h includes <QtCore/qobject.h>
6798            my $Candidate = join_P(get_dirname($AbsDir), $Include);
6799            if(-f $Candidate) {
6800                $HPath = $Candidate;
6801            }
6802        }
6803        if(not $HPath) {
6804            $HPath = identifyHeader($Include, $LibVersion);
6805        }
6806        next if(not $HPath);
6807        if($HPath eq $AbsPath) {
6808            next;
6809        }
6810
6811        if($Debug)
6812        { # boundary headers
6813#             if($HPath=~/vtk/ and $AbsPath!~/vtk/)
6814#             {
6815#                 print STDERR "$AbsPath -> $HPath\n";
6816#             }
6817        }
6818
6819        $RecursiveIncludes{$LibVersion}{$AbsPath}{$HPath} = $IncType;
6820        if($IncType>0)
6821        { # only include <...>, skip include "..." prefixes
6822            $Header_Include_Prefix{$LibVersion}{$AbsPath}{$HPath}{get_dirname($Include)} = 1;
6823        }
6824        foreach my $IncPath (detect_recursive_includes($HPath, $LibVersion))
6825        {
6826            if($IncPath eq $AbsPath) {
6827                next;
6828            }
6829            my $RIncType = $RecursiveIncludes{$LibVersion}{$HPath}{$IncPath};
6830            if($RIncType==-1)
6831            { # include "..."
6832                $RIncType = $IncType;
6833            }
6834            elsif($RIncType==2)
6835            {
6836                if($IncType!=-1) {
6837                    $RIncType = $IncType;
6838                }
6839            }
6840            $RecursiveIncludes{$LibVersion}{$AbsPath}{$IncPath} = $RIncType;
6841            foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$HPath}{$IncPath}})) {
6842                $Header_Include_Prefix{$LibVersion}{$AbsPath}{$IncPath}{$Prefix} = 1;
6843            }
6844        }
6845        foreach my $Dep (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}}))
6846        {
6847            if($GlibcHeader{get_filename($Dep)} and keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}})>=2
6848            and defined $Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""})
6849            { # distinguish math.h from glibc and math.h from the tested library
6850                delete($Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""});
6851                last;
6852            }
6853        }
6854    }
6855    pop(@RecurInclude);
6856    return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6857}
6858
6859sub find_in_framework($$$)
6860{
6861    my ($Header, $Framework, $LibVersion) = @_;
6862    return "" if(not $Header or not $Framework or not $LibVersion);
6863    if(defined $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header}) {
6864        return $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header};
6865    }
6866    foreach my $Dependency (sort {get_depth($a)<=>get_depth($b)} keys(%{$Header_Dependency{$LibVersion}}))
6867    {
6868        if(get_filename($Dependency) eq $Framework
6869        and -f get_dirname($Dependency)."/".$Header) {
6870            return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = get_dirname($Dependency));
6871        }
6872    }
6873    return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = "");
6874}
6875
6876sub find_in_defaults($)
6877{
6878    my $Header = $_[0];
6879    return "" if(not $Header);
6880    if(defined $Cache{"find_in_defaults"}{$Header}) {
6881        return $Cache{"find_in_defaults"}{$Header};
6882    }
6883    foreach my $Dir (@DefaultIncPaths,
6884                     @DefaultGccPaths,
6885                     @DefaultCppPaths,
6886                     @UsersIncPath)
6887    {
6888        next if(not $Dir);
6889        if(-f $Dir."/".$Header) {
6890            return ($Cache{"find_in_defaults"}{$Header}=$Dir);
6891        }
6892    }
6893    return ($Cache{"find_in_defaults"}{$Header}="");
6894}
6895
6896sub cmp_paths($$)
6897{
6898    my ($Path1, $Path2) = @_;
6899    my @Parts1 = split(/[\/\\]/, $Path1);
6900    my @Parts2 = split(/[\/\\]/, $Path2);
6901    foreach my $Num (0 .. $#Parts1)
6902    {
6903        my $Part1 = $Parts1[$Num];
6904        my $Part2 = $Parts2[$Num];
6905        if($GlibcDir{$Part1}
6906        and not $GlibcDir{$Part2}) {
6907            return 1;
6908        }
6909        elsif($GlibcDir{$Part2}
6910        and not $GlibcDir{$Part1}) {
6911            return -1;
6912        }
6913        elsif($Part1=~/glib/
6914        and $Part2!~/glib/) {
6915            return 1;
6916        }
6917        elsif($Part1!~/glib/
6918        and $Part2=~/glib/) {
6919            return -1;
6920        }
6921        elsif(my $CmpRes = ($Part1 cmp $Part2)) {
6922            return $CmpRes;
6923        }
6924    }
6925    return 0;
6926}
6927
6928sub checkRelevance($)
6929{
6930    my $Path = $_[0];
6931    return 0 if(not $Path);
6932
6933    if($SystemRoot) {
6934        $Path = cut_path_prefix($Path, $SystemRoot);
6935    }
6936
6937    my $Name = lc(get_filename($Path));
6938    my $Dir = lc(get_dirname($Path));
6939
6940    $Name=~s/\.\w+\Z//g; # remove extension (.h)
6941
6942    foreach my $Token (split(/[_\d\W]+/, $Name))
6943    {
6944        my $Len = length($Token);
6945        next if($Len<=1);
6946        if($Dir=~/(\A|lib|[_\d\W])\Q$Token\E([_\d\W]|lib|\Z)/)
6947        { # include/evolution-data-server-1.4/libebook/e-book.h
6948            return 1;
6949        }
6950        if($Len>=4 and index($Dir, $Token)!=-1)
6951        { # include/gupnp-1.0/libgupnp/gupnp-context.h
6952            return 1;
6953        }
6954    }
6955    return 0;
6956}
6957
6958sub checkFamily(@)
6959{
6960    my @Paths = @_;
6961    return 1 if($#Paths<=0);
6962    my %Prefix = ();
6963    foreach my $Path (@Paths)
6964    {
6965        if($SystemRoot) {
6966            $Path = cut_path_prefix($Path, $SystemRoot);
6967        }
6968        if(my $Dir = get_dirname($Path))
6969        {
6970            $Dir=~s/(\/[^\/]+?)[\d\.\-\_]+\Z/$1/g; # remove version suffix
6971            $Prefix{$Dir} += 1;
6972            $Prefix{get_dirname($Dir)} += 1;
6973        }
6974    }
6975    foreach (sort keys(%Prefix))
6976    {
6977        if(get_depth($_)>=3
6978        and $Prefix{$_}==$#Paths+1) {
6979            return 1;
6980        }
6981    }
6982    return 0;
6983}
6984
6985sub isAcceptable($$$)
6986{
6987    my ($Header, $Candidate, $LibVersion) = @_;
6988    my $HName = get_filename($Header);
6989    if(get_dirname($Header))
6990    { # with prefix
6991        return 1;
6992    }
6993    if($HName=~/config|setup/i and $Candidate=~/[\/\\]lib\d*[\/\\]/)
6994    { # allow to search for glibconfig.h in /usr/lib/glib-2.0/include/
6995        return 1;
6996    }
6997    if(checkRelevance($Candidate))
6998    { # allow to search for atk.h in /usr/include/atk-1.0/atk/
6999        return 1;
7000    }
7001    if(checkFamily(getSystemHeaders($HName, $LibVersion)))
7002    { # /usr/include/qt4/QtNetwork/qsslconfiguration.h
7003      # /usr/include/qt4/Qt/qsslconfiguration.h
7004        return 1;
7005    }
7006    if($OStarget eq "symbian")
7007    {
7008        if($Candidate=~/[\/\\]stdapis[\/\\]/) {
7009            return 1;
7010        }
7011    }
7012    return 0;
7013}
7014
7015sub isRelevant($$$)
7016{ # disallow to search for "abstract" headers in too deep directories
7017    my ($Header, $Candidate, $LibVersion) = @_;
7018    my $HName = get_filename($Header);
7019    if($OStarget eq "symbian")
7020    {
7021        if($Candidate=~/[\/\\](tools|stlportv5)[\/\\]/) {
7022            return 0;
7023        }
7024    }
7025    if($OStarget ne "bsd")
7026    {
7027        if($Candidate=~/[\/\\]include[\/\\]bsd[\/\\]/)
7028        { # openssh: skip /usr/lib/bcc/include/bsd/signal.h
7029            return 0;
7030        }
7031    }
7032    if($OStarget ne "windows")
7033    {
7034        if($Candidate=~/[\/\\](wine|msvcrt|windows)[\/\\]/)
7035        { # skip /usr/include/wine/msvcrt
7036            return 0;
7037        }
7038    }
7039    if(not get_dirname($Header)
7040    and $Candidate=~/[\/\\]wx[\/\\]/)
7041    { # do NOT search in system /wx/ directory
7042      # for headers without a prefix: sstream.h
7043        return 0;
7044    }
7045    if($Candidate=~/c\+\+[\/\\]\d+/ and $MAIN_CPP_DIR
7046    and $Candidate!~/\A\Q$MAIN_CPP_DIR\E/)
7047    { # skip ../c++/3.3.3/ if using ../c++/4.5/
7048        return 0;
7049    }
7050    if($Candidate=~/[\/\\]asm-/
7051    and (my $Arch = getArch($LibVersion)) ne "unknown")
7052    { # arch-specific header files
7053        if($Candidate!~/[\/\\]asm-\Q$Arch\E/)
7054        {# skip ../asm-arm/ if using x86 architecture
7055            return 0;
7056        }
7057    }
7058    my @Candidates = getSystemHeaders($HName, $LibVersion);
7059    if($#Candidates==1)
7060    { # unique header
7061        return 1;
7062    }
7063    my @SCandidates = getSystemHeaders($Header, $LibVersion);
7064    if($#SCandidates==1)
7065    { # unique name
7066        return 1;
7067    }
7068    my $SystemDepth = $SystemRoot?get_depth($SystemRoot):0;
7069    if(get_depth($Candidate)-$SystemDepth>=5)
7070    { # abstract headers in too deep directories
7071      # sstream.h or typeinfo.h in /usr/include/wx-2.9/wx/
7072        if(not isAcceptable($Header, $Candidate, $LibVersion)) {
7073            return 0;
7074        }
7075    }
7076    if($Header eq "parser.h"
7077    and $Candidate!~/\/libxml2\//)
7078    { # select parser.h from xml2 library
7079        return 0;
7080    }
7081    if(not get_dirname($Header)
7082    and keys(%{$SystemHeaders{$HName}})>=3)
7083    { # many headers with the same name
7084      # like thread.h included without a prefix
7085        if(not checkFamily(@Candidates)) {
7086            return 0;
7087        }
7088    }
7089    return 1;
7090}
7091
7092sub selectSystemHeader($$)
7093{ # cache function
7094    if(defined $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}) {
7095        return $Cache{"selectSystemHeader"}{$_[1]}{$_[0]};
7096    }
7097    return ($Cache{"selectSystemHeader"}{$_[1]}{$_[0]} = selectSystemHeader_I(@_));
7098}
7099
7100sub selectSystemHeader_I($$)
7101{
7102    my ($Header, $LibVersion) = @_;
7103    if(-f $Header) {
7104        return $Header;
7105    }
7106    if(is_abs($Header) and not -f $Header)
7107    { # incorrect absolute path
7108        return "";
7109    }
7110    if(defined $ConfHeaders{lc($Header)})
7111    { # too abstract configuration headers
7112        return "";
7113    }
7114    my $HName = get_filename($Header);
7115    if($OSgroup ne "windows")
7116    {
7117        if(defined $WinHeaders{lc($HName)}
7118        or $HName=~/windows|win32|win64/i)
7119        { # windows headers
7120            return "";
7121        }
7122    }
7123    if($OSgroup ne "macos")
7124    {
7125        if($HName eq "fp.h")
7126        { # pngconf.h includes fp.h in Mac OS
7127            return "";
7128        }
7129    }
7130
7131    if(defined $ObsoleteHeaders{$HName})
7132    { # obsolete headers
7133        return "";
7134    }
7135    if($OSgroup eq "linux" or $OSgroup eq "bsd")
7136    {
7137        if(defined $AlienHeaders{$HName}
7138        or defined $AlienHeaders{$Header})
7139        { # alien headers from other systems
7140            return "";
7141        }
7142    }
7143
7144    foreach my $Path (@{$SystemPaths{"include"}})
7145    { # search in default paths
7146        if(-f $Path."/".$Header) {
7147            return join_P($Path,$Header);
7148        }
7149    }
7150    if(not defined $Cache{"checkSystemFiles"})
7151    { # register all headers in system include dirs
7152        checkSystemFiles();
7153    }
7154    foreach my $Candidate (sort {get_depth($a)<=>get_depth($b)}
7155    sort {cmp_paths($b, $a)} getSystemHeaders($Header, $LibVersion))
7156    {
7157        if(isRelevant($Header, $Candidate, $LibVersion)) {
7158            return $Candidate;
7159        }
7160    }
7161    # error
7162    return "";
7163}
7164
7165sub getSystemHeaders($$)
7166{
7167    my ($Header, $LibVersion) = @_;
7168    my @Candidates = ();
7169    foreach my $Candidate (sort keys(%{$SystemHeaders{$Header}}))
7170    {
7171        if(skipHeader($Candidate, $LibVersion)) {
7172            next;
7173        }
7174        push(@Candidates, $Candidate);
7175    }
7176    return @Candidates;
7177}
7178
7179sub cut_path_prefix($$)
7180{
7181    my ($Path, $Prefix) = @_;
7182    return $Path if(not $Prefix);
7183    $Prefix=~s/[\/\\]+\Z//;
7184    $Path=~s/\A\Q$Prefix\E([\/\\]+|\Z)//;
7185    return $Path;
7186}
7187
7188sub is_default_include_dir($)
7189{
7190    my $Dir = $_[0];
7191    $Dir=~s/[\/\\]+\Z//;
7192    return grep { $Dir eq $_ } (@DefaultGccPaths, @DefaultCppPaths, @DefaultIncPaths);
7193}
7194
7195sub identifyHeader($$)
7196{ # cache function
7197    my ($Header, $LibVersion) = @_;
7198    if(not $Header) {
7199        return "";
7200    }
7201    $Header=~s/\A(\.\.[\\\/])+//g;
7202    if(defined $Cache{"identifyHeader"}{$LibVersion}{$Header}) {
7203        return $Cache{"identifyHeader"}{$LibVersion}{$Header};
7204    }
7205    return ($Cache{"identifyHeader"}{$LibVersion}{$Header} = identifyHeader_I($Header, $LibVersion));
7206}
7207
7208sub identifyHeader_I($$)
7209{ # search for header by absolute path, relative path or name
7210    my ($Header, $LibVersion) = @_;
7211    if(-f $Header)
7212    { # it's relative or absolute path
7213        return get_abs_path($Header);
7214    }
7215    elsif($GlibcHeader{$Header} and not $GLIBC_TESTING
7216    and my $HeaderDir = find_in_defaults($Header))
7217    { # search for libc headers in the /usr/include
7218      # for non-libc target library before searching
7219      # in the library paths
7220        return join_P($HeaderDir,$Header);
7221    }
7222    elsif(my $Path = $Include_Neighbors{$LibVersion}{$Header})
7223    { # search in the target library paths
7224        return $Path;
7225    }
7226    elsif(defined $DefaultGccHeader{$Header})
7227    { # search in the internal GCC include paths
7228        return $DefaultGccHeader{$Header};
7229    }
7230    elsif(my $DefaultDir = find_in_defaults($Header))
7231    { # search in the default GCC include paths
7232        return join_P($DefaultDir,$Header);
7233    }
7234    elsif(defined $DefaultCppHeader{$Header})
7235    { # search in the default G++ include paths
7236        return $DefaultCppHeader{$Header};
7237    }
7238    elsif(my $AnyPath = selectSystemHeader($Header, $LibVersion))
7239    { # search everywhere in the system
7240        return $AnyPath;
7241    }
7242    elsif($OSgroup eq "macos")
7243    { # search in frameworks: "OpenGL/gl.h" is "OpenGL.framework/Headers/gl.h"
7244        if(my $Dir = get_dirname($Header))
7245        {
7246            my $RelPath = "Headers\/".get_filename($Header);
7247            if(my $HeaderDir = find_in_framework($RelPath, $Dir.".framework", $LibVersion)) {
7248                return join_P($HeaderDir, $RelPath);
7249            }
7250        }
7251    }
7252    # cannot find anything
7253    return "";
7254}
7255
7256sub getLocation($)
7257{
7258    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7259    {
7260        if($Info=~/srcp[ ]*:[ ]*([\w\-\<\>\.\+\/\\]+):(\d+) /) {
7261            return (path_format($1, $OSgroup), $2);
7262        }
7263    }
7264    return ();
7265}
7266
7267sub getNameByInfo($)
7268{
7269    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7270    {
7271        if($Info=~/name[ ]*:[ ]*@(\d+) /)
7272        {
7273            if(my $NInfo = $LibInfo{$Version}{"info"}{$1})
7274            {
7275                if($NInfo=~/strg[ ]*:[ ]*(.*?)[ ]+lngt/)
7276                { # short unsigned int (may include spaces)
7277                    my $Str = $1;
7278                    if($CppMode{$Version}
7279                    and $Str=~/\Ac99_(.+)\Z/)
7280                    {
7281                        if($CppKeywords_A{$1}) {
7282                            $Str=$1;
7283                        }
7284                    }
7285                    return $Str;
7286                }
7287            }
7288        }
7289    }
7290    return "";
7291}
7292
7293sub getTreeStr($)
7294{
7295    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7296    {
7297        if($Info=~/strg[ ]*:[ ]*([^ ]*)/)
7298        {
7299            my $Str = $1;
7300            if($CppMode{$Version}
7301            and $Str=~/\Ac99_(.+)\Z/)
7302            {
7303                if($CppKeywords_A{$1}) {
7304                    $Str=$1;
7305                }
7306            }
7307            return $Str;
7308        }
7309    }
7310    return "";
7311}
7312
7313sub getFuncShortName($)
7314{
7315    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7316    {
7317        if(index($Info, " operator ")!=-1)
7318        {
7319            if(index($Info, " conversion ")!=-1)
7320            {
7321                if(my $Rid = $SymbolInfo{$Version}{$_[0]}{"Return"})
7322                {
7323                    if(my $RName = $TypeInfo{$Version}{$Rid}{"Name"}) {
7324                        return "operator ".$RName;
7325                    }
7326                }
7327            }
7328            else
7329            {
7330                if($Info=~/ operator[ ]+([a-zA-Z]+) /)
7331                {
7332                    if(my $Ind = $Operator_Indication{$1}) {
7333                        return "operator".$Ind;
7334                    }
7335                    elsif(not $UnknownOperator{$1})
7336                    {
7337                        printMsg("WARNING", "unknown operator $1");
7338                        $UnknownOperator{$1} = 1;
7339                    }
7340                }
7341            }
7342        }
7343        else
7344        {
7345            if($Info=~/name[ ]*:[ ]*@(\d+) /) {
7346                return getTreeStr($1);
7347            }
7348        }
7349    }
7350    return "";
7351}
7352
7353sub getFuncReturn($)
7354{
7355    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7356    {
7357        if($Info=~/type[ ]*:[ ]*@(\d+) /)
7358        {
7359            if($LibInfo{$Version}{"info"}{$1}=~/retn[ ]*:[ ]*@(\d+) /) {
7360                return $1;
7361            }
7362        }
7363    }
7364    return "";
7365}
7366
7367sub getFuncOrig($)
7368{
7369    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7370    {
7371        if($Info=~/orig[ ]*:[ ]*@(\d+) /) {
7372            return $1;
7373        }
7374    }
7375    return $_[0];
7376}
7377
7378sub unmangleArray(@)
7379{
7380    if($_[0]=~/\A\?/)
7381    { # MSVC mangling
7382        if(defined $DisabledMSVCUnmangling) {
7383            return @_;
7384        }
7385        my $UndNameCmd = get_CmdPath("undname");
7386        if(not $UndNameCmd)
7387        {
7388            if($OSgroup eq "windows") {
7389                exitStatus("Not_Found", "can't find \"undname\"");
7390            }
7391            elsif(not defined $DisabledMSVCUnmangling)
7392            {
7393                printMsg("WARNING", "can't find \"undname\", disable MSVC unmangling");
7394                $DisabledMSVCUnmangling = 1;
7395                return @_;
7396            }
7397        }
7398        writeFile("$TMP_DIR/unmangle", join("\n", @_));
7399        return split(/\n/, `$UndNameCmd 0x8386 \"$TMP_DIR/unmangle\"`);
7400    }
7401    else
7402    { # GCC mangling
7403        my $CppFiltCmd = get_CmdPath("c++filt");
7404        if(not $CppFiltCmd) {
7405            exitStatus("Not_Found", "can't find c++filt in PATH");
7406        }
7407        if(not defined $CPPFILT_SUPPORT_FILE)
7408        {
7409            my $Info = `$CppFiltCmd -h 2>&1`;
7410            $CPPFILT_SUPPORT_FILE = $Info=~/\@<file>/;
7411        }
7412        my $NoStrip = ($OSgroup=~/macos|windows/)?"-n":"";
7413        if($CPPFILT_SUPPORT_FILE)
7414        { # new versions of c++filt can take a file
7415            if($#_>$MAX_CPPFILT_FILE_SIZE)
7416            { # c++filt <= 2.22 may crash on large files (larger than 8mb)
7417              # this is fixed in the oncoming version of Binutils
7418                my @Half = splice(@_, 0, ($#_+1)/2);
7419                return (unmangleArray(@Half), unmangleArray(@_))
7420            }
7421            else
7422            {
7423                writeFile("$TMP_DIR/unmangle", join("\n", @_));
7424                my $Res = `$CppFiltCmd $NoStrip \@\"$TMP_DIR/unmangle\"`;
7425                if($?==139)
7426                { # segmentation fault
7427                    printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_CPPFILT_FILE_SIZE constant");
7428                }
7429                return split(/\n/, $Res);
7430            }
7431        }
7432        else
7433        { # old-style unmangling
7434            if($#_>$MAX_COMMAND_LINE_ARGUMENTS)
7435            {
7436                my @Half = splice(@_, 0, ($#_+1)/2);
7437                return (unmangleArray(@Half), unmangleArray(@_))
7438            }
7439            else
7440            {
7441                my $Strings = join(" ", @_);
7442                my $Res = `$CppFiltCmd $NoStrip $Strings`;
7443                if($?==139)
7444                { # segmentation fault
7445                    printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_COMMAND_LINE_ARGUMENTS constant");
7446                }
7447                return split(/\n/, $Res);
7448            }
7449        }
7450    }
7451}
7452
7453sub get_ChargeLevel($$)
7454{
7455    my ($Symbol, $LibVersion) = @_;
7456    return "" if($Symbol!~/\A(_Z|\?)/);
7457    if(defined $CompleteSignature{$LibVersion}{$Symbol}
7458    and $CompleteSignature{$LibVersion}{$Symbol}{"Header"})
7459    {
7460        if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"})
7461        {
7462            if($Symbol=~/C1[EI]/) {
7463                return "[in-charge]";
7464            }
7465            elsif($Symbol=~/C2[EI]/) {
7466                return "[not-in-charge]";
7467            }
7468        }
7469        elsif($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"})
7470        {
7471            if($Symbol=~/D1[EI]/) {
7472                return "[in-charge]";
7473            }
7474            elsif($Symbol=~/D2[EI]/) {
7475                return "[not-in-charge]";
7476            }
7477            elsif($Symbol=~/D0[EI]/) {
7478                return "[in-charge-deleting]";
7479            }
7480        }
7481    }
7482    else
7483    {
7484        if($Symbol=~/C1[EI]/) {
7485            return "[in-charge]";
7486        }
7487        elsif($Symbol=~/C2[EI]/) {
7488            return "[not-in-charge]";
7489        }
7490        elsif($Symbol=~/D1[EI]/) {
7491            return "[in-charge]";
7492        }
7493        elsif($Symbol=~/D2[EI]/) {
7494            return "[not-in-charge]";
7495        }
7496        elsif($Symbol=~/D0[EI]/) {
7497            return "[in-charge-deleting]";
7498        }
7499    }
7500    return "";
7501}
7502
7503sub get_Signature_M($$)
7504{
7505    my ($Symbol, $LibVersion) = @_;
7506    my $Signature_M = $tr_name{$Symbol};
7507    if(my $RTid = $CompleteSignature{$LibVersion}{$Symbol}{"Return"})
7508    { # add return type name
7509        $Signature_M = $TypeInfo{$LibVersion}{$RTid}{"Name"}." ".$Signature_M;
7510    }
7511    return $Signature_M;
7512}
7513
7514sub get_Signature($$)
7515{
7516    my ($Symbol, $LibVersion) = @_;
7517    if($Cache{"get_Signature"}{$LibVersion}{$Symbol}) {
7518        return $Cache{"get_Signature"}{$LibVersion}{$Symbol};
7519    }
7520    my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
7521    my ($Signature, @Param_Types_FromUnmangledName) = ();
7522
7523    my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"};
7524
7525    if($Symbol=~/\A(_Z|\?)/)
7526    {
7527        if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
7528        {
7529            my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
7530            $ClassName=~s/\bstruct //g;
7531
7532            if(index($Symbol, "_ZTV")==0) {
7533                return "vtable for $ClassName [data]";
7534            }
7535
7536            $Signature .= $ClassName."::";
7537            if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}) {
7538                $Signature .= "~";
7539            }
7540            $Signature .= $ShortName;
7541        }
7542        elsif(my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"}) {
7543            $Signature .= $NameSpace."::".$ShortName;
7544        }
7545        else {
7546            $Signature .= $ShortName;
7547        }
7548        my ($Short, $Params) = split_Signature($tr_name{$MnglName});
7549        @Param_Types_FromUnmangledName = separate_Params($Params, 0, 1);
7550    }
7551    else
7552    {
7553        $Signature .= $MnglName;
7554    }
7555    my @ParamArray = ();
7556    foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
7557    {
7558        if($Pos eq "") {
7559            next;
7560        }
7561
7562        my $ParamTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"};
7563        if(not $ParamTypeId) {
7564            next;
7565        }
7566
7567        my $ParamTypeName = $TypeInfo{$LibVersion}{$ParamTypeId}{"Name"};
7568        if(not $ParamTypeName) {
7569            $ParamTypeName = $Param_Types_FromUnmangledName[$Pos];
7570        }
7571        foreach my $Typedef (keys(%ChangedTypedef))
7572        {
7573            if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) {
7574                $ParamTypeName=~s/\b\Q$Typedef\E\b/$Base/g;
7575            }
7576        }
7577        if(my $ParamName = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"name"})
7578        {
7579            if($ParamName eq "this"
7580            and $Symbol=~/\A(_Z|\?)/)
7581            { # do NOT show first hidded "this"-parameter
7582                next;
7583            }
7584            push(@ParamArray, create_member_decl($ParamTypeName, $ParamName));
7585        }
7586        else {
7587            push(@ParamArray, $ParamTypeName);
7588        }
7589    }
7590    if($CompleteSignature{$LibVersion}{$Symbol}{"Data"}
7591    or $GlobalDataObject{$LibVersion}{$Symbol}) {
7592        $Signature .= " [data]";
7593    }
7594    else
7595    {
7596        if(my $ChargeLevel = get_ChargeLevel($Symbol, $LibVersion))
7597        { # add [in-charge]
7598            $Signature .= " ".$ChargeLevel;
7599        }
7600        $Signature .= " (".join(", ", @ParamArray).")";
7601        if($CompleteSignature{$LibVersion}{$Symbol}{"Const"}
7602        or $Symbol=~/\A_ZN(V|)K/) {
7603            $Signature .= " const";
7604        }
7605        if($CompleteSignature{$LibVersion}{$Symbol}{"Volatile"}
7606        or $Symbol=~/\A_ZN(K|)V/) {
7607            $Signature .= " volatile";
7608        }
7609        if($CompleteSignature{$LibVersion}{$Symbol}{"Static"}
7610        and $Symbol=~/\A(_Z|\?)/)
7611        { # for static methods
7612            $Signature .= " [static]";
7613        }
7614    }
7615    if(defined $ShowRetVal
7616    and my $ReturnTId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"}) {
7617        $Signature .= ":".$TypeInfo{$LibVersion}{$ReturnTId}{"Name"};
7618    }
7619    if($SymbolVersion) {
7620        $Signature .= $VersionSpec.$SymbolVersion;
7621    }
7622    return ($Cache{"get_Signature"}{$LibVersion}{$Symbol} = $Signature);
7623}
7624
7625sub create_member_decl($$)
7626{
7627    my ($TName, $Member) = @_;
7628    if($TName=~/\([\*]+\)/)
7629    {
7630        $TName=~s/\(([\*]+)\)/\($1$Member\)/;
7631        return $TName;
7632    }
7633    else
7634    {
7635        my @ArraySizes = ();
7636        while($TName=~s/(\[[^\[\]]*\])\Z//) {
7637            push(@ArraySizes, $1);
7638        }
7639        return $TName." ".$Member.join("", @ArraySizes);
7640    }
7641}
7642
7643sub getFuncType($)
7644{
7645    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7646    {
7647        if($Info=~/type[ ]*:[ ]*@(\d+) /)
7648        {
7649            if(my $Type = $LibInfo{$Version}{"info_type"}{$1})
7650            {
7651                if($Type eq "method_type") {
7652                    return "Method";
7653                }
7654                elsif($Type eq "function_type") {
7655                    return "Function";
7656                }
7657                else {
7658                    return "Other";
7659                }
7660            }
7661        }
7662    }
7663    return "";
7664}
7665
7666sub getFuncTypeId($)
7667{
7668    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7669    {
7670        if($Info=~/type[ ]*:[ ]*@(\d+)( |\Z)/) {
7671            return $1;
7672        }
7673    }
7674    return 0;
7675}
7676
7677sub isAnon($)
7678{ # "._N" or "$_N" in older GCC versions
7679    return ($_[0] and $_[0]=~/(\.|\$)\_\d+|anon\-/);
7680}
7681
7682sub formatName($$)
7683{ # type name correction
7684    if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) {
7685        return $Cache{"formatName"}{$_[1]}{$_[0]};
7686    }
7687
7688    my $N = $_[0];
7689
7690    if($_[1] ne "S")
7691    {
7692        $N=~s/\A[ ]+//g;
7693        $N=~s/[ ]+\Z//g;
7694        $N=~s/[ ]{2,}/ /g;
7695    }
7696
7697    $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string<char> const
7698
7699    $N=~s/\b(const|volatile) ([\w\:]+)([\*&,>]|\Z)/$2 $1$3/g; # "const void" to "void const"
7700
7701    $N=~s/\bvolatile const\b/const volatile/g;
7702
7703    $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g;
7704    $N=~s/\b(short|long) int\b/$1/g;
7705
7706    $N=~s/([\)\]])(const|volatile)\b/$1 $2/g;
7707
7708    while($N=~s/>>/> >/g) {};
7709
7710    if($_[1] eq "S")
7711    {
7712        if(index($N, "operator")!=-1) {
7713            $N=~s/\b(operator[ ]*)> >/$1>>/;
7714        }
7715    }
7716
7717    $N=~s/,([^ ])/, $1/g;
7718
7719    return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N);
7720}
7721
7722sub get_HeaderDeps($$)
7723{
7724    my ($AbsPath, $LibVersion) = @_;
7725    return () if(not $AbsPath or not $LibVersion);
7726    if(defined $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}) {
7727        return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
7728    }
7729    my %IncDir = ();
7730    detect_recursive_includes($AbsPath, $LibVersion);
7731    foreach my $HeaderPath (keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}))
7732    {
7733        next if(not $HeaderPath);
7734        next if($MAIN_CPP_DIR and $HeaderPath=~/\A\Q$MAIN_CPP_DIR\E([\/\\]|\Z)/);
7735        my $Dir = get_dirname($HeaderPath);
7736        foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$HeaderPath}}))
7737        {
7738            my $Dep = $Dir;
7739            if($Prefix)
7740            {
7741                if($OSgroup eq "windows")
7742                { # case insensitive seach on windows
7743                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//ig) {
7744                        next;
7745                    }
7746                }
7747                elsif($OSgroup eq "macos")
7748                { # seach in frameworks
7749                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
7750                    {
7751                        if($HeaderPath=~/(.+\.framework)\/Headers\/([^\/]+)/)
7752                        {# frameworks
7753                            my ($HFramework, $HName) = ($1, $2);
7754                            $Dep = $HFramework;
7755                        }
7756                        else
7757                        {# mismatch
7758                            next;
7759                        }
7760                    }
7761                }
7762                elsif(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
7763                { # Linux, FreeBSD
7764                    next;
7765                }
7766            }
7767            if(not $Dep)
7768            { # nothing to include
7769                next;
7770            }
7771            if(is_default_include_dir($Dep))
7772            { # included by the compiler
7773                next;
7774            }
7775            if(get_depth($Dep)==1)
7776            { # too short
7777                next;
7778            }
7779            if(isLibcDir($Dep))
7780            { # do NOT include /usr/include/{sys,bits}
7781                next;
7782            }
7783            $IncDir{$Dep} = 1;
7784        }
7785    }
7786    $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath} = sortIncPaths([keys(%IncDir)], $LibVersion);
7787    return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
7788}
7789
7790sub sortIncPaths($$)
7791{
7792    my ($ArrRef, $LibVersion) = @_;
7793    if(not $ArrRef or $#{$ArrRef}<0) {
7794        return $ArrRef;
7795    }
7796    @{$ArrRef} = sort {$b cmp $a} @{$ArrRef};
7797    @{$ArrRef} = sort {get_depth($a)<=>get_depth($b)} @{$ArrRef};
7798    @{$ArrRef} = sort {sortDeps($b, $a, $LibVersion)} @{$ArrRef};
7799    return $ArrRef;
7800}
7801
7802sub sortDeps($$$)
7803{
7804    if($Header_Dependency{$_[2]}{$_[0]}
7805    and not $Header_Dependency{$_[2]}{$_[1]}) {
7806        return 1;
7807    }
7808    elsif(not $Header_Dependency{$_[2]}{$_[0]}
7809    and $Header_Dependency{$_[2]}{$_[1]}) {
7810        return -1;
7811    }
7812    return 0;
7813}
7814
7815sub join_P($$)
7816{
7817    my $S = "/";
7818    if($OSgroup eq "windows") {
7819        $S = "\\";
7820    }
7821    return join($S, @_);
7822}
7823
7824sub get_namespace_additions($)
7825{
7826    my $NameSpaces = $_[0];
7827    my ($Additions, $AddNameSpaceId) = ("", 1);
7828    foreach my $NS (sort {$a=~/_/ <=> $b=~/_/} sort {lc($a) cmp lc($b)} keys(%{$NameSpaces}))
7829    {
7830        next if($SkipNameSpaces{$Version}{$NS});
7831        next if(not $NS or $NameSpaces->{$NS}==-1);
7832        next if($NS=~/(\A|::)iterator(::|\Z)/i);
7833        next if($NS=~/\A__/i);
7834        next if(($NS=~/\Astd::/ or $NS=~/\A(std|tr1|rel_ops|fcntl)\Z/) and not $STDCXX_TESTING);
7835        $NestedNameSpaces{$Version}{$NS} = 1; # for future use in reports
7836        my ($TypeDecl_Prefix, $TypeDecl_Suffix) = ();
7837        my @NS_Parts = split(/::/, $NS);
7838        next if($#NS_Parts==-1);
7839        next if($NS_Parts[0]=~/\A(random|or)\Z/);
7840        foreach my $NS_Part (@NS_Parts)
7841        {
7842            $TypeDecl_Prefix .= "namespace $NS_Part\{";
7843            $TypeDecl_Suffix .= "}";
7844        }
7845        my $TypeDecl = $TypeDecl_Prefix."typedef int tmp_add_type_".$AddNameSpaceId.";".$TypeDecl_Suffix;
7846        my $FuncDecl = "$NS\:\:tmp_add_type_$AddNameSpaceId tmp_add_func_$AddNameSpaceId(){return 0;};";
7847        $Additions.="  $TypeDecl\n  $FuncDecl\n";
7848        $AddNameSpaceId+=1;
7849    }
7850    return $Additions;
7851}
7852
7853sub path_format($$)
7854{
7855    my ($Path, $Fmt) = @_;
7856    $Path=~s/[\/\\]+\.?\Z//g;
7857    if($Fmt eq "windows")
7858    {
7859        $Path=~s/\//\\/g;
7860        $Path=lc($Path);
7861    }
7862    else
7863    { # forward slash to pass into MinGW GCC
7864        $Path=~s/\\/\//g;
7865    }
7866    return $Path;
7867}
7868
7869sub inc_opt($$)
7870{
7871    my ($Path, $Style) = @_;
7872    if($Style eq "GCC")
7873    { # GCC options
7874        if($OSgroup eq "windows")
7875        { # to MinGW GCC
7876            return "-I\"".path_format($Path, "unix")."\"";
7877        }
7878        elsif($OSgroup eq "macos"
7879        and $Path=~/\.framework\Z/)
7880        { # to Apple's GCC
7881            return "-F".esc(get_dirname($Path));
7882        }
7883        else {
7884            return "-I".esc($Path);
7885        }
7886    }
7887    elsif($Style eq "CL") {
7888        return "/I \"".$Path."\"";
7889    }
7890    return "";
7891}
7892
7893sub platformSpecs($)
7894{
7895    my $LibVersion = $_[0];
7896    my $Arch = getArch($LibVersion);
7897    if($OStarget eq "symbian")
7898    { # options for GCCE compiler
7899        my %Symbian_Opts = map {$_=>1} (
7900            "-D__GCCE__",
7901            "-DUNICODE",
7902            "-fexceptions",
7903            "-D__SYMBIAN32__",
7904            "-D__MARM_INTERWORK__",
7905            "-D_UNICODE",
7906            "-D__S60_50__",
7907            "-D__S60_3X__",
7908            "-D__SERIES60_3X__",
7909            "-D__EPOC32__",
7910            "-D__MARM__",
7911            "-D__EABI__",
7912            "-D__MARM_ARMV5__",
7913            "-D__SUPPORT_CPP_EXCEPTIONS__",
7914            "-march=armv5t",
7915            "-mapcs",
7916            "-mthumb-interwork",
7917            "-DEKA2",
7918            "-DSYMBIAN_ENABLE_SPLIT_HEADERS"
7919        );
7920        return join(" ", keys(%Symbian_Opts));
7921    }
7922    elsif($OSgroup eq "windows"
7923    and get_dumpmachine($GCC_PATH)=~/mingw/i)
7924    { # add options to MinGW compiler
7925      # to simulate the MSVC compiler
7926        my %MinGW_Opts = map {$_=>1} (
7927            "-D__unaligned=\" \"",
7928            "-D__nullptr=\"nullptr\"",
7929            "-D_WIN32",
7930            "-D_STDCALL_SUPPORTED",
7931            "-D__int64=\"long long\"",
7932            "-D__int32=int",
7933            "-D__int16=short",
7934            "-D__int8=char",
7935            "-D__possibly_notnullterminated=\" \"",
7936            "-D__nullterminated=\" \"",
7937            "-D__nullnullterminated=\" \"",
7938            "-D__assume=\" \"",
7939            "-D__w64=\" \"",
7940            "-D__ptr32=\" \"",
7941            "-D__ptr64=\" \"",
7942            "-D__forceinline=inline",
7943            "-D__inline=inline",
7944            "-D__uuidof(x)=IID()",
7945            "-D__try=",
7946            "-D__except(x)=",
7947            "-D__declspec(x)=__attribute__((x))",
7948            "-D__pragma(x)=",
7949            "-D_inline=inline",
7950            "-D__forceinline=__inline",
7951            "-D__stdcall=__attribute__((__stdcall__))",
7952            "-D__cdecl=__attribute__((__cdecl__))",
7953            "-D__fastcall=__attribute__((__fastcall__))",
7954            "-D__thiscall=__attribute__((__thiscall__))",
7955            "-D_stdcall=__attribute__((__stdcall__))",
7956            "-D_cdecl=__attribute__((__cdecl__))",
7957            "-D_fastcall=__attribute__((__fastcall__))",
7958            "-D_thiscall=__attribute__((__thiscall__))",
7959            "-DSHSTDAPI_(x)=x",
7960            "-D_MSC_EXTENSIONS",
7961            "-DSECURITY_WIN32",
7962            "-D_MSC_VER=1500",
7963            "-D_USE_DECLSPECS_FOR_SAL",
7964            "-D__noop=\" \"",
7965            "-DDECLSPEC_DEPRECATED=\" \"",
7966            "-D__builtin_alignof(x)=__alignof__(x)",
7967            "-DSORTPP_PASS");
7968        if($Arch eq "x86")
7969        {
7970            $MinGW_Opts{"-D_X86_=300"}=1;
7971            $MinGW_Opts{"-D_M_IX86=300"}=1;
7972        }
7973        elsif($Arch eq "x86_64")
7974        {
7975            $MinGW_Opts{"-D_AMD64_=300"}=1;
7976            $MinGW_Opts{"-D_M_AMD64=300"}=1;
7977            $MinGW_Opts{"-D_M_X64=300"}=1;
7978        }
7979        elsif($Arch eq "ia64")
7980        {
7981            $MinGW_Opts{"-D_IA64_=300"}=1;
7982            $MinGW_Opts{"-D_M_IA64=300"}=1;
7983        }
7984        return join(" ", sort keys(%MinGW_Opts));
7985    }
7986    return "";
7987}
7988
7989my %C_Structure = map {$_=>1} (
7990# FIXME: Can't separate union and struct data types before dumping,
7991# so it sometimes cause compilation errors for unknown reason
7992# when trying to declare TYPE* tmp_add_class_N
7993# This is a list of such structures + list of other C structures
7994    "sigval",
7995    "sigevent",
7996    "sigaction",
7997    "sigvec",
7998    "sigstack",
7999    "timeval",
8000    "timezone",
8001    "rusage",
8002    "rlimit",
8003    "wait",
8004    "flock",
8005    "stat",
8006    "_stat",
8007    "stat32",
8008    "_stat32",
8009    "stat64",
8010    "_stat64",
8011    "_stati64",
8012    "if_nameindex",
8013    "usb_device",
8014    "sigaltstack",
8015    "sysinfo",
8016    "timeLocale",
8017    "tcp_debug",
8018    "rpc_createerr",
8019 # Other
8020    "timespec",
8021    "random_data",
8022    "drand48_data",
8023    "_IO_marker",
8024    "_IO_FILE",
8025    "lconv",
8026    "sched_param",
8027    "tm",
8028    "itimerspec",
8029    "_pthread_cleanup_buffer",
8030    "fd_set",
8031    "siginfo",
8032    "mallinfo",
8033    "timex",
8034    "sigcontext",
8035    "ucontext",
8036 # Mac
8037    "_timex",
8038    "_class_t",
8039    "_category_t",
8040    "_class_ro_t",
8041    "_protocol_t",
8042    "_message_ref_t",
8043    "_super_message_ref_t",
8044    "_ivar_t",
8045    "_ivar_list_t"
8046);
8047
8048sub getCompileCmd($$$)
8049{
8050    my ($Path, $Opt, $Inc) = @_;
8051    my $GccCall = $GCC_PATH;
8052    if($Opt) {
8053        $GccCall .= " ".$Opt;
8054    }
8055    $GccCall .= " -x ";
8056    if($OSgroup eq "macos") {
8057        $GccCall .= "objective-";
8058    }
8059
8060    if($GCC_MISSED_MNGL)
8061    { # workaround for GCC 4.8 (C only)
8062        $GccCall .= "c++";
8063    }
8064    elsif(check_gcc($GCC_PATH, "4"))
8065    { # compile as "C++" header
8066      # to obtain complete dump using GCC 4.0
8067        $GccCall .= "c++-header";
8068    }
8069    else
8070    { # compile as "C++" source
8071      # GCC 3.3 cannot compile headers
8072        $GccCall .= "c++";
8073    }
8074    if(my $Opts = platformSpecs($Version))
8075    { # platform-specific options
8076        $GccCall .= " ".$Opts;
8077    }
8078    # allow extra qualifications
8079    # and other nonconformant code
8080    $GccCall .= " -fpermissive";
8081    $GccCall .= " -w";
8082    if($NoStdInc)
8083    {
8084        $GccCall .= " -nostdinc";
8085        $GccCall .= " -nostdinc++";
8086    }
8087    if(my $Opts_GCC = getGCC_Opts($Version))
8088    { # user-defined options
8089        $GccCall .= " ".$Opts_GCC;
8090    }
8091    $GccCall .= " \"$Path\"";
8092    if($Inc)
8093    { # include paths
8094        $GccCall .= " ".$Inc;
8095    }
8096    return $GccCall;
8097}
8098
8099sub detectPreamble($$)
8100{
8101    my ($Content, $LibVersion) = @_;
8102    my %HeaderElems = (
8103        # Types
8104        "stdio.h" => ["FILE", "va_list"],
8105        "stddef.h" => ["NULL", "ptrdiff_t"],
8106        "stdint.h" => ["uint8_t", "uint16_t", "uint32_t", "uint64_t",
8107                       "int8_t", "int16_t", "int32_t", "int64_t"],
8108        "time.h" => ["time_t"],
8109        "sys/types.h" => ["ssize_t", "u_int32_t", "u_short", "u_char",
8110                          "u_int", "off_t", "u_quad_t", "u_long", "mode_t"],
8111        "unistd.h" => ["gid_t", "uid_t", "socklen_t"],
8112        "stdbool.h" => ["_Bool"],
8113        "rpc/xdr.h" => ["bool_t"],
8114        "in_systm.h" => ["n_long", "n_short"],
8115        # Fields
8116        "arpa/inet.h" => ["fw_src", "ip_src"],
8117        # Functions
8118        "stdlib.h" => ["free", "malloc", "size_t"],
8119        "string.h" => ["memmove", "strcmp"]
8120    );
8121    my %AutoPreamble = ();
8122    foreach (keys(%HeaderElems))
8123    {
8124        foreach my $Elem (@{$HeaderElems{$_}}) {
8125            $AutoPreamble{$Elem} = $_;
8126        }
8127    }
8128    my %Types = ();
8129    while($Content=~s/error\:\s*(field\s*|)\W+(.+?)\W+//)
8130    { # error: 'FILE' has not been declared
8131        $Types{$2} = 1;
8132    }
8133    if(keys(%Types))
8134    {
8135        my %AddHeaders = ();
8136        foreach my $Type (keys(%Types))
8137        {
8138            if(my $Header = $AutoPreamble{$Type})
8139            {
8140                if(my $Path = identifyHeader($Header, $LibVersion))
8141                {
8142                    if(skipHeader($Path, $LibVersion)) {
8143                        next;
8144                    }
8145                    $Path = path_format($Path, $OSgroup);
8146                    $AddHeaders{$Path}{"Type"} = $Type;
8147                    $AddHeaders{$Path}{"Header"} = $Header;
8148                }
8149            }
8150        }
8151        if(keys(%AddHeaders)) {
8152            return \%AddHeaders;
8153        }
8154    }
8155    return undef;
8156}
8157
8158sub checkCTags($)
8159{
8160    my $Path = $_[0];
8161    if(not $Path) {
8162        return;
8163    }
8164    my $CTags = undef;
8165
8166    if($OSgroup eq "bsd")
8167    { # use ectags on BSD
8168        $CTags = get_CmdPath("ectags");
8169        if(not $CTags) {
8170            printMsg("WARNING", "can't find \'ectags\' program");
8171        }
8172    }
8173    if(not $CTags) {
8174        $CTags = get_CmdPath("ctags");
8175    }
8176    if(not $CTags)
8177    {
8178        printMsg("WARNING", "can't find \'ctags\' program");
8179        return;
8180    }
8181
8182    if($OSgroup ne "linux")
8183    { # macos, freebsd, etc.
8184        my $Info = `$CTags --version 2>\"$TMP_DIR/null\"`;
8185        if($Info!~/universal|exuberant/i)
8186        {
8187            printMsg("WARNING", "incompatible version of \'ctags\' program");
8188            return;
8189        }
8190    }
8191
8192    my $Out = $TMP_DIR."/ctags.txt";
8193    system("$CTags --c-kinds=pxn -f \"$Out\" \"$Path\" 2>\"$TMP_DIR/null\"");
8194    if($Debug) {
8195        copy($Out, $DEBUG_PATH{$Version}."/ctags.txt");
8196    }
8197    open(CTAGS, "<", $Out);
8198    while(my $Line = <CTAGS>)
8199    {
8200        chomp($Line);
8201        my ($Name, $Header, $Def, $Type, $Scpe) = split(/\t/, $Line);
8202        if(defined $Intrinsic_Keywords{$Name})
8203        { # noise
8204            next;
8205        }
8206        if($Type eq "n")
8207        {
8208            if(index($Scpe, "class:")==0) {
8209                next;
8210            }
8211            if(index($Scpe, "struct:")==0) {
8212                next;
8213            }
8214            if(index($Scpe, "namespace:")==0)
8215            {
8216                if($Scpe=~s/\Anamespace://) {
8217                    $Name = $Scpe."::".$Name;
8218                }
8219            }
8220            $TUnit_NameSpaces{$Version}{$Name} = 1;
8221        }
8222        elsif($Type eq "p")
8223        {
8224            if(not $Scpe or index($Scpe, "namespace:")==0) {
8225                $TUnit_Funcs{$Version}{$Name} = 1;
8226            }
8227        }
8228        elsif($Type eq "x")
8229        {
8230            if(not $Scpe or index($Scpe, "namespace:")==0) {
8231                $TUnit_Vars{$Version}{$Name} = 1;
8232            }
8233        }
8234    }
8235    close(CTAGS);
8236}
8237
8238sub preChange($$)
8239{
8240    my ($HeaderPath, $IncStr) = @_;
8241
8242    my $PreprocessCmd = getCompileCmd($HeaderPath, "-E", $IncStr);
8243    my $Content = undef;
8244
8245    if(not defined $MinGWCompat and $OStarget eq "windows"
8246    and get_dumpmachine($GCC_PATH)=~/mingw/i
8247    and $MinGWMode{$Version}!=-1)
8248    { # modify headers to compile by MinGW
8249        if(not $Content)
8250        { # preprocessing
8251            $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`;
8252        }
8253        if($Content=~s/__asm\s*(\{[^{}]*?\}|[^{};]*)//g)
8254        { # __asm { ... }
8255            $MinGWMode{$Version}=1;
8256        }
8257        if($Content=~s/\s+(\/ \/.*?)\n/\n/g)
8258        { # comments after preprocessing
8259            $MinGWMode{$Version}=1;
8260        }
8261        if($Content=~s/(\W)(0x[a-f]+|\d+)(i|ui)(8|16|32|64)(\W)/$1$2$5/g)
8262        { # 0xffui8
8263            $MinGWMode{$Version}=1;
8264        }
8265
8266        if($MinGWMode{$Version}) {
8267            printMsg("INFO", "Using MinGW compatibility mode");
8268        }
8269    }
8270
8271    if(defined $CxxIncompat and ($COMMON_LANGUAGE{$Version} eq "C" or $CheckHeadersOnly)
8272    and $CppMode{$Version}!=-1 and not $CPP_HEADERS)
8273    { # rename C++ keywords in C code
8274        printMsg("INFO", "Checking the code for C++ keywords");
8275        if(not $Content)
8276        { # preprocessing
8277            $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`;
8278        }
8279
8280        my $RegExp_C = join("|", keys(%CppKeywords_C));
8281        my $RegExp_F = join("|", keys(%CppKeywords_F));
8282        my $RegExp_O = join("|", keys(%CppKeywords_O));
8283
8284        my $Detected = undef;
8285        my $Sentence_O = undef;
8286        my $Sentence_N = undef;
8287        my $Regex = undef;
8288
8289        $Regex = qr/(\A|\n[^\#\/\n][^\n]*?|\n)(\*\s*|\s+|\@|\,|\()($RegExp_C|$RegExp_F)(\s*([\,\)\;\.\[]|\-\>|\:\s*\d))/;
8290        while($Content=~/$Regex/)
8291        { # MATCH:
8292          # int foo(int new, int class, int (*new)(int));
8293          # int foo(char template[], char*);
8294          # unsigned private: 8;
8295          # DO NOT MATCH:
8296          # #pragma GCC visibility push(default)
8297            $Sentence_O = "$1$2$3$4";
8298            $Sentence_N = "$1$2c99_$3$4";
8299
8300            if($Sentence_O=~/\s+decltype\(/)
8301            { # C++
8302              # decltype(nullptr)
8303                last;
8304            }
8305            else
8306            {
8307                $Content=~s/$Regex/$Sentence_N/g;
8308                $CppMode{$Version} = 1;
8309                if(not defined $Detected) {
8310                    $Detected = $Sentence_O;
8311                }
8312            }
8313        }
8314        if($Content=~s/([^\w\s]|\w\s+)(?<!operator )(delete)(\s*\()/$1c99_$2$3/g)
8315        { # MATCH:
8316          # int delete(...);
8317          # int explicit(...);
8318          # DO NOT MATCH:
8319          # void operator delete(...)
8320            $CppMode{$Version} = 1;
8321            $Detected = "$1$2$3" if(not defined $Detected);
8322        }
8323        if($Content=~s/(\s+)($RegExp_O)(\s*(\;|\:))/$1c99_$2$3/g)
8324        { # MATCH:
8325          # int bool;
8326          # DO NOT MATCH:
8327          # bool X;
8328          # return *this;
8329          # throw;
8330            $CppMode{$Version} = 1;
8331            $Detected = "$1$2$3" if(not defined $Detected);
8332        }
8333        if($Content=~s/(\s+)(operator)(\s*(\(\s*\)\s*[^\(\s]|\(\s*[^\)\s]))/$1c99_$2$3/g)
8334        { # MATCH:
8335          # int operator(...);
8336          # DO NOT MATCH:
8337          # int operator()(...);
8338            $CppMode{$Version} = 1;
8339            $Detected = "$1$2$3" if(not defined $Detected);
8340        }
8341        if($Content=~s/([^\w\(\,\s]\s*|\s+)(operator)(\s*(\,\s*[^\(\s]|\)))/$1c99_$2$3/g)
8342        { # MATCH:
8343          # int foo(int operator);
8344          # int foo(int operator, int other);
8345          # DO NOT MATCH:
8346          # int operator,(...);
8347            $CppMode{$Version} = 1;
8348            $Detected = "$1$2$3" if(not defined $Detected);
8349        }
8350        if($Content=~s/(\*\s*|\w\s+)(bool)(\s*(\,|\)))/$1c99_$2$3/g)
8351        { # MATCH:
8352          # int foo(gboolean *bool);
8353          # DO NOT MATCH:
8354          # void setTabEnabled(int index, bool);
8355            $CppMode{$Version} = 1;
8356            $Detected = "$1$2$3" if(not defined $Detected);
8357        }
8358        if($Content=~s/(\w)(\s*[^\w\(\,\s]\s*|\s+)(this|throw)(\s*[\,\)])/$1$2c99_$3$4/g)
8359        { # MATCH:
8360          # int foo(int* this);
8361          # int bar(int this);
8362          # int baz(int throw);
8363          # DO NOT MATCH:
8364          # foo(X, this);
8365            $CppMode{$Version} = 1;
8366            $Detected = "$1$2$3$4" if(not defined $Detected);
8367        }
8368        if($Content=~s/(struct |extern )(template) /$1c99_$2 /g)
8369        { # MATCH:
8370          # struct template {...};
8371          # extern template foo(...);
8372            $CppMode{$Version} = 1;
8373            $Detected = "$1$2" if(not defined $Detected);
8374        }
8375
8376        if($CppMode{$Version} == 1)
8377        {
8378            if($Debug)
8379            {
8380                $Detected=~s/\A\s+//g;
8381                printMsg("INFO", "Detected code: \"$Detected\"");
8382            }
8383        }
8384
8385        # remove typedef enum NAME NAME;
8386        my @FwdTypedefs = $Content=~/typedef\s+enum\s+(\w+)\s+(\w+);/g;
8387        my $N = 0;
8388        while($N<=$#FwdTypedefs-1)
8389        {
8390            my $S = $FwdTypedefs[$N];
8391            if($S eq $FwdTypedefs[$N+1])
8392            {
8393                $Content=~s/typedef\s+enum\s+\Q$S\E\s+\Q$S\E;//g;
8394                $CppMode{$Version} = 1;
8395
8396                if($Debug) {
8397                    printMsg("INFO", "Detected code: \"typedef enum $S $S;\"");
8398                }
8399            }
8400            $N+=2;
8401        }
8402
8403        if($CppMode{$Version}==1) {
8404            printMsg("INFO", "Using C++ compatibility mode");
8405        }
8406        else {
8407            printMsg("INFO", "C++ keywords in the C code are not found");
8408        }
8409    }
8410
8411    if($CppMode{$Version}==1
8412    or $MinGWMode{$Version}==1)
8413    {
8414        my $IPath = $TMP_DIR."/dump$Version.i";
8415        writeFile($IPath, $Content);
8416        return $IPath;
8417    }
8418
8419    return undef;
8420}
8421
8422sub getDump()
8423{
8424    if(not $GCC_PATH) {
8425        exitStatus("Error", "internal error - GCC path is not set");
8426    }
8427
8428    my @Headers = keys(%{$Registered_Headers{$Version}});
8429    @Headers = sort {int($Registered_Headers{$Version}{$a}{"Pos"})<=>int($Registered_Headers{$Version}{$b}{"Pos"})} @Headers;
8430
8431    my $IncludeString = getIncString(getIncPaths(@{$Include_Preamble{$Version}}, @Headers), "GCC");
8432
8433    my $TmpHeaderPath = $TMP_DIR."/dump".$Version.".h";
8434    my $HeaderPath = $TmpHeaderPath;
8435
8436    # write tmp-header
8437    open(TMP_HEADER, ">", $TmpHeaderPath) || die ("can't open file \'$TmpHeaderPath\': $!\n");
8438    if(my $AddDefines = $Descriptor{$Version}{"Defines"})
8439    {
8440        $AddDefines=~s/\n\s+/\n  /g;
8441        print TMP_HEADER "\n  // add defines\n  ".$AddDefines."\n";
8442    }
8443    print TMP_HEADER "\n  // add includes\n";
8444    foreach my $HPath (@{$Include_Preamble{$Version}}) {
8445        print TMP_HEADER "  #include \"".path_format($HPath, "unix")."\"\n";
8446    }
8447    foreach my $HPath (@Headers)
8448    {
8449        if(not grep {$HPath eq $_} (@{$Include_Preamble{$Version}})) {
8450            print TMP_HEADER "  #include \"".path_format($HPath, "unix")."\"\n";
8451        }
8452    }
8453    close(TMP_HEADER);
8454
8455    if($ExtraInfo)
8456    { # extra information for other tools
8457        if($IncludeString) {
8458            writeFile($ExtraInfo."/include-string", $IncludeString);
8459        }
8460        writeFile($ExtraInfo."/recursive-includes", Dumper($RecursiveIncludes{$Version}));
8461        writeFile($ExtraInfo."/direct-includes", Dumper($Header_Includes{$Version}));
8462
8463        if(my @Redirects = keys(%{$Header_ErrorRedirect{$Version}}))
8464        {
8465            my $REDIR = "";
8466            foreach my $P1 (sort @Redirects) {
8467                $REDIR .= $P1.";".$Header_ErrorRedirect{$Version}{$P1}."\n";
8468            }
8469            writeFile($ExtraInfo."/include-redirect", $REDIR);
8470        }
8471    }
8472
8473    if(not keys(%{$TargetHeaders{$Version}}))
8474    { # Target headers
8475        addTargetHeaders($Version);
8476    }
8477
8478    # clean memory
8479    %RecursiveIncludes = ();
8480    %Header_Include_Prefix = ();
8481    %Header_Includes = ();
8482
8483    # clean cache
8484    delete($Cache{"identifyHeader"});
8485    delete($Cache{"detect_header_includes"});
8486    delete($Cache{"selectSystemHeader"});
8487
8488    # preprocessing stage
8489    my $Pre = callPreprocessor($TmpHeaderPath, $IncludeString, $Version);
8490    checkPreprocessedUnit($Pre);
8491
8492    if($ExtraInfo)
8493    { # extra information for other tools
8494        writeFile($ExtraInfo."/header-paths", join("\n", sort keys(%{$PreprocessedHeaders{$Version}})));
8495    }
8496
8497    # clean memory
8498    delete($Include_Neighbors{$Version});
8499    delete($PreprocessedHeaders{$Version});
8500
8501    if($COMMON_LANGUAGE{$Version} eq "C++") {
8502        checkCTags($Pre);
8503    }
8504
8505    if(my $PrePath = preChange($TmpHeaderPath, $IncludeString))
8506    { # try to correct the preprocessor output
8507        $HeaderPath = $PrePath;
8508    }
8509
8510    if($COMMON_LANGUAGE{$Version} eq "C++")
8511    { # add classes and namespaces to the dump
8512        my $CHdump = "-fdump-class-hierarchy -c";
8513        if($CppMode{$Version}==1
8514        or $MinGWMode{$Version}==1) {
8515            $CHdump .= " -fpreprocessed";
8516        }
8517        my $ClassHierarchyCmd = getCompileCmd($HeaderPath, $CHdump, $IncludeString);
8518        chdir($TMP_DIR);
8519        system($ClassHierarchyCmd." >null 2>&1");
8520        chdir($ORIG_DIR);
8521        if(my $ClassDump = (cmd_find($TMP_DIR,"f","*.class",1))[0])
8522        {
8523            my $Content = readFile($ClassDump);
8524            foreach my $ClassInfo (split(/\n\n/, $Content))
8525            {
8526                if($ClassInfo=~/\AClass\s+(.+)\s*/i)
8527                {
8528                    my $CName = $1;
8529                    next if($CName=~/\A(__|_objc_|_opaque_)/);
8530                    $TUnit_NameSpaces{$Version}{$CName} = -1;
8531                    if($CName=~/\A[\w:]+\Z/)
8532                    { # classes
8533                        $TUnit_Classes{$Version}{$CName} = 1;
8534                    }
8535                    if($CName=~/(\w[\w:]*)::/)
8536                    { # namespaces
8537                        my $NS = $1;
8538                        if(not defined $TUnit_NameSpaces{$Version}{$NS}) {
8539                            $TUnit_NameSpaces{$Version}{$NS} = 1;
8540                        }
8541                    }
8542                }
8543                elsif($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i)
8544                { # read v-tables (advanced approach)
8545                    my ($CName, $VTable) = ($1, $2);
8546                    $ClassVTable_Content{$Version}{$CName} = $VTable;
8547                }
8548            }
8549            foreach my $NS (keys(%{$AddNameSpaces{$Version}}))
8550            { # add user-defined namespaces
8551                $TUnit_NameSpaces{$Version}{$NS} = 1;
8552            }
8553            if($Debug)
8554            { # debug mode
8555                mkpath($DEBUG_PATH{$Version});
8556                copy($ClassDump, $DEBUG_PATH{$Version}."/class-hierarchy-dump.txt");
8557            }
8558            unlink($ClassDump);
8559        }
8560
8561        # add namespaces and classes
8562        if(my $NS_Add = get_namespace_additions($TUnit_NameSpaces{$Version}))
8563        { # GCC on all supported platforms does not include namespaces to the dump by default
8564            appendFile($HeaderPath, "\n  // add namespaces\n".$NS_Add);
8565        }
8566        # some GCC versions don't include class methods to the TU dump by default
8567        my ($AddClass, $ClassNum) = ("", 0);
8568        my $GCC_44 = check_gcc($GCC_PATH, "4.4"); # support for old GCC versions
8569        foreach my $CName (sort keys(%{$TUnit_Classes{$Version}}))
8570        {
8571            next if($C_Structure{$CName});
8572            next if(not $STDCXX_TESTING and $CName=~/\Astd::/);
8573            next if($SkipTypes{$Version}{$CName});
8574            if(not $Force and $GCC_44
8575            and $OSgroup eq "linux")
8576            { # optimization for linux with GCC >= 4.4
8577              # disable this code by -force option
8578                if(index($CName, "::")!=-1)
8579                { # should be added by name space
8580                    next;
8581                }
8582            }
8583            else
8584            {
8585                if($CName=~/\A(.+)::[^:]+\Z/
8586                and $TUnit_Classes{$Version}{$1})
8587                { # classes inside other classes
8588                    next;
8589                }
8590            }
8591            if(defined $TUnit_Funcs{$Version}{$CName})
8592            { # the same name for a function and type
8593                next;
8594            }
8595            if(defined $TUnit_Vars{$Version}{$CName})
8596            { # the same name for a variable and type
8597                next;
8598            }
8599            $AddClass .= "  $CName* tmp_add_class_".($ClassNum++).";\n";
8600        }
8601        if($AddClass) {
8602            appendFile($HeaderPath, "\n  // add classes\n".$AddClass);
8603        }
8604    }
8605    writeLog($Version, "Temporary header file \'$TmpHeaderPath\' with the following content will be compiled to create GCC translation unit dump:\n".readFile($TmpHeaderPath)."\n");
8606    # create TU dump
8607    my $TUdump = "-fdump-translation-unit -fkeep-inline-functions -c";
8608    if($UserLang eq "C") {
8609        $TUdump .= " -U__cplusplus -D_Bool=\"bool\"";
8610    }
8611    if($CppMode{$Version}==1
8612    or $MinGWMode{$Version}==1) {
8613        $TUdump .= " -fpreprocessed";
8614    }
8615    my $SyntaxTreeCmd = getCompileCmd($HeaderPath, $TUdump, $IncludeString);
8616    writeLog($Version, "The GCC parameters:\n  $SyntaxTreeCmd\n\n");
8617    chdir($TMP_DIR);
8618    system($SyntaxTreeCmd." >\"$TMP_DIR/tu_errors\" 2>&1");
8619    my $Errors = "";
8620    if($?)
8621    { # failed to compile, but the TU dump still can be created
8622        if($Errors = readFile($TMP_DIR."/tu_errors"))
8623        { # try to recompile
8624          # FIXME: handle errors and try to recompile
8625            if($AutoPreambleMode{$Version}!=-1
8626            and my $AddHeaders = detectPreamble($Errors, $Version))
8627            { # add auto preamble headers and try again
8628                $AutoPreambleMode{$Version}=-1;
8629                my @Headers = sort {$b cmp $a} keys(%{$AddHeaders}); # sys/types.h should be the first
8630                foreach my $Num (0 .. $#Headers)
8631                {
8632                    my $Path = $Headers[$Num];
8633                    if(not grep {$Path eq $_} (@{$Include_Preamble{$Version}}))
8634                    {
8635                        push_U($Include_Preamble{$Version}, $Path);
8636                        printMsg("INFO", "Add \'".$AddHeaders->{$Path}{"Header"}."\' preamble header for \'".$AddHeaders->{$Path}{"Type"}."\'");
8637                    }
8638                }
8639                resetLogging($Version);
8640                $TMP_DIR = tempdir(CLEANUP=>1);
8641                return getDump();
8642            }
8643            elsif($Cpp0xMode{$Version}!=-1
8644            and ($Errors=~/\Q-std=c++0x\E/
8645            or $Errors=~/is not a class or namespace/))
8646            { # c++0x: enum class
8647                if(check_gcc($GCC_PATH, "4.6"))
8648                {
8649                    $Cpp0xMode{$Version}=-1;
8650                    printMsg("INFO", "Enabling c++0x mode");
8651                    resetLogging($Version);
8652                    $TMP_DIR = tempdir(CLEANUP=>1);
8653                    $CompilerOptions{$Version} .= " -std=c++0x";
8654                    return getDump();
8655                }
8656                else {
8657                    printMsg("WARNING", "Probably c++0x element detected");
8658                }
8659
8660            }
8661            #elsif($MinGWMode{$Version}==1)
8662            #{ # disable MinGW mode and try again
8663            #    $MinGWMode{$Version}=-1;
8664            #    resetLogging($Version);
8665            #    $TMP_DIR = tempdir(CLEANUP=>1);
8666            #    return getDump();
8667            #}
8668            writeLog($Version, $Errors);
8669        }
8670        else {
8671            writeLog($Version, "$!: $?\n");
8672        }
8673        printMsg("ERROR", "some errors occurred when compiling headers");
8674        printErrorLog($Version);
8675        $COMPILE_ERRORS = $ERROR_CODE{"Compile_Error"};
8676        writeLog($Version, "\n"); # new line
8677    }
8678    chdir($ORIG_DIR);
8679    unlink($TmpHeaderPath);
8680    unlink($HeaderPath);
8681
8682    if(my @TUs = cmd_find($TMP_DIR,"f","*.tu",1)) {
8683        return $TUs[0];
8684    }
8685    else
8686    {
8687        my $Msg = "can't compile header(s)";
8688        if($Errors=~/error trying to exec \W+cc1plus\W+/) {
8689            $Msg .= "\nDid you install G++?";
8690        }
8691        exitStatus("Cannot_Compile", $Msg);
8692    }
8693}
8694
8695sub cmd_file($)
8696{
8697    my $Path = $_[0];
8698    return "" if(not $Path or not -e $Path);
8699    if(my $CmdPath = get_CmdPath("file")) {
8700        return `$CmdPath -b \"$Path\"`;
8701    }
8702    return "";
8703}
8704
8705sub getIncString($$)
8706{
8707    my ($ArrRef, $Style) = @_;
8708    return "" if(not $ArrRef or $#{$ArrRef}<0);
8709    my $String = "";
8710    foreach (@{$ArrRef}) {
8711        $String .= " ".inc_opt($_, $Style);
8712    }
8713    return $String;
8714}
8715
8716sub getIncPaths(@)
8717{
8718    my @HeaderPaths = @_;
8719    my @IncPaths = @{$Add_Include_Paths{$Version}};
8720    if($INC_PATH_AUTODETECT{$Version})
8721    { # auto-detecting dependencies
8722        my %Includes = ();
8723        foreach my $HPath (@HeaderPaths)
8724        {
8725            foreach my $Dir (get_HeaderDeps($HPath, $Version))
8726            {
8727                if($Skip_Include_Paths{$Version}{$Dir}) {
8728                    next;
8729                }
8730                if($SystemRoot)
8731                {
8732                    if($Skip_Include_Paths{$Version}{$SystemRoot.$Dir}) {
8733                        next;
8734                    }
8735                }
8736                $Includes{$Dir} = 1;
8737            }
8738        }
8739        foreach my $Dir (@{sortIncPaths([keys(%Includes)], $Version)}) {
8740            push_U(\@IncPaths, $Dir);
8741        }
8742    }
8743    else
8744    { # user-defined paths
8745        @IncPaths = @{$Include_Paths{$Version}};
8746    }
8747    return \@IncPaths;
8748}
8749
8750sub push_U($@)
8751{ # push unique
8752    if(my $Array = shift @_)
8753    {
8754        if(@_)
8755        {
8756            my %Exist = map {$_=>1} @{$Array};
8757            foreach my $Elem (@_)
8758            {
8759                if(not defined $Exist{$Elem})
8760                {
8761                    push(@{$Array}, $Elem);
8762                    $Exist{$Elem} = 1;
8763                }
8764            }
8765        }
8766    }
8767}
8768
8769sub callPreprocessor($$$)
8770{
8771    my ($Path, $Inc, $LibVersion) = @_;
8772    return "" if(not $Path or not -f $Path);
8773    my $IncludeString=$Inc;
8774    if(not $Inc) {
8775        $IncludeString = getIncString(getIncPaths($Path), "GCC");
8776    }
8777    my $Cmd = getCompileCmd($Path, "-dD -E", $IncludeString);
8778    my $Out = $TMP_DIR."/preprocessed.h";
8779    system($Cmd." >\"$Out\" 2>\"$TMP_DIR/null\"");
8780    return $Out;
8781}
8782
8783sub cmd_find($;$$$$)
8784{ # native "find" is much faster than File::Find (~6x)
8785  # also the File::Find doesn't support --maxdepth N option
8786  # so using the cross-platform wrapper for the native one
8787    my ($Path, $Type, $Name, $MaxDepth, $UseRegex) = @_;
8788    return () if(not $Path or not -e $Path);
8789    if($OSgroup eq "windows")
8790    {
8791        $Path = get_abs_path($Path);
8792        $Path = path_format($Path, $OSgroup);
8793        my $Cmd = "dir \"$Path\" /B /O";
8794        if($MaxDepth!=1) {
8795            $Cmd .= " /S";
8796        }
8797        if($Type eq "d") {
8798            $Cmd .= " /AD";
8799        }
8800        elsif($Type eq "f") {
8801            $Cmd .= " /A-D";
8802        }
8803        my @Files = split(/\n/, `$Cmd 2>\"$TMP_DIR/null\"`);
8804        if($Name)
8805        {
8806            if(not $UseRegex)
8807            { # FIXME: how to search file names in MS shell?
8808              # wildcard to regexp
8809                $Name=~s/\*/.*/g;
8810                $Name='\A'.$Name.'\Z';
8811            }
8812            @Files = grep { /$Name/i } @Files;
8813        }
8814        my @AbsPaths = ();
8815        foreach my $File (@Files)
8816        {
8817            if(not is_abs($File)) {
8818                $File = join_P($Path, $File);
8819            }
8820            if($Type eq "f" and not -f $File)
8821            { # skip dirs
8822                next;
8823            }
8824            push(@AbsPaths, path_format($File, $OSgroup));
8825        }
8826        if($Type eq "d") {
8827            push(@AbsPaths, $Path);
8828        }
8829        return @AbsPaths;
8830    }
8831    else
8832    {
8833        my $FindCmd = get_CmdPath("find");
8834        if(not $FindCmd) {
8835            exitStatus("Not_Found", "can't find a \"find\" command");
8836        }
8837        $Path = get_abs_path($Path);
8838        if(-d $Path and -l $Path
8839        and $Path!~/\/\Z/)
8840        { # for directories that are symlinks
8841            $Path.="/";
8842        }
8843        my $Cmd = $FindCmd." \"$Path\"";
8844        if($MaxDepth) {
8845            $Cmd .= " -maxdepth $MaxDepth";
8846        }
8847        if($Type) {
8848            $Cmd .= " -type $Type";
8849        }
8850        if($Name and not $UseRegex)
8851        { # wildcards
8852            $Cmd .= " -name \"$Name\"";
8853        }
8854        my $Res = `$Cmd 2>\"$TMP_DIR/null\"`;
8855        if($? and $!) {
8856            printMsg("ERROR", "problem with \'find\' utility ($?): $!");
8857        }
8858        my @Files = split(/\n/, $Res);
8859        if($Name and $UseRegex)
8860        { # regex
8861            @Files = grep { /$Name/ } @Files;
8862        }
8863        return @Files;
8864    }
8865}
8866
8867sub unpackDump($)
8868{
8869    my $Path = $_[0];
8870    return "" if(not $Path or not -e $Path);
8871
8872    $Path = get_abs_path($Path);
8873    $Path = path_format($Path, $OSgroup);
8874    my ($Dir, $FileName) = separate_path($Path);
8875    my $UnpackDir = $TMP_DIR."/unpack";
8876    rmtree($UnpackDir);
8877    mkpath($UnpackDir);
8878
8879    if($FileName=~s/\Q.zip\E\Z//g)
8880    { # *.zip
8881        my $UnzipCmd = get_CmdPath("unzip");
8882        if(not $UnzipCmd) {
8883            exitStatus("Not_Found", "can't find \"unzip\" command");
8884        }
8885        chdir($UnpackDir);
8886        system("$UnzipCmd \"$Path\" >\"$TMP_DIR/null\"");
8887        if($?) {
8888            exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8889        }
8890        chdir($ORIG_DIR);
8891        my @Contents = cmd_find($UnpackDir, "f");
8892        if(not @Contents) {
8893            exitStatus("Error", "can't extract \'$Path\'");
8894        }
8895        return $Contents[0];
8896    }
8897    elsif($FileName=~s/\Q.tar.gz\E(\.\w+|)\Z//g)
8898    { # *.tar.gz
8899      # *.tar.gz.amd64 (dh & cdbs)
8900        if($OSgroup eq "windows")
8901        { # -xvzf option is not implemented in tar.exe (2003)
8902          # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead
8903            my $TarCmd = get_CmdPath("tar");
8904            if(not $TarCmd) {
8905                exitStatus("Not_Found", "can't find \"tar\" command");
8906            }
8907            my $GzipCmd = get_CmdPath("gzip");
8908            if(not $GzipCmd) {
8909                exitStatus("Not_Found", "can't find \"gzip\" command");
8910            }
8911            chdir($UnpackDir);
8912            system("$GzipCmd -k -d -f \"$Path\""); # keep input files (-k)
8913            if($?) {
8914                exitStatus("Error", "can't extract \'$Path\'");
8915            }
8916            system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >\"$TMP_DIR/null\"");
8917            if($?) {
8918                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8919            }
8920            chdir($ORIG_DIR);
8921            unlink($Dir."/".$FileName.".tar");
8922            my @Contents = cmd_find($UnpackDir, "f");
8923            if(not @Contents) {
8924                exitStatus("Error", "can't extract \'$Path\'");
8925            }
8926            return $Contents[0];
8927        }
8928        else
8929        { # Unix, Mac
8930            my $TarCmd = get_CmdPath("tar");
8931            if(not $TarCmd) {
8932                exitStatus("Not_Found", "can't find \"tar\" command");
8933            }
8934            chdir($UnpackDir);
8935            system("$TarCmd -xvzf \"$Path\" >\"$TMP_DIR/null\"");
8936            if($?) {
8937                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8938            }
8939            chdir($ORIG_DIR);
8940            my @Contents = cmd_find($UnpackDir, "f");
8941            if(not @Contents) {
8942                exitStatus("Error", "can't extract \'$Path\'");
8943            }
8944            return $Contents[0];
8945        }
8946    }
8947}
8948
8949sub createArchive($$)
8950{
8951    my ($Path, $To) = @_;
8952    if(not $To) {
8953        $To = ".";
8954    }
8955    if(not $Path or not -e $Path
8956    or not -d $To) {
8957        return "";
8958    }
8959    my ($From, $Name) = separate_path($Path);
8960    if($OSgroup eq "windows")
8961    { # *.zip
8962        my $ZipCmd = get_CmdPath("zip");
8963        if(not $ZipCmd) {
8964            exitStatus("Not_Found", "can't find \"zip\"");
8965        }
8966        my $Pkg = $To."/".$Name.".zip";
8967        unlink($Pkg);
8968        chdir($To);
8969        system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"$TMP_DIR/null\"");
8970        if($?)
8971        { # cannot allocate memory (or other problems with "zip")
8972            unlink($Path);
8973            exitStatus("Error", "can't pack the ABI dump: ".$!);
8974        }
8975        chdir($ORIG_DIR);
8976        unlink($Path);
8977        return $Pkg;
8978    }
8979    else
8980    { # *.tar.gz
8981        my $TarCmd = get_CmdPath("tar");
8982        if(not $TarCmd) {
8983            exitStatus("Not_Found", "can't find \"tar\"");
8984        }
8985        my $GzipCmd = get_CmdPath("gzip");
8986        if(not $GzipCmd) {
8987            exitStatus("Not_Found", "can't find \"gzip\"");
8988        }
8989        my $Pkg = abs_path($To)."/".$Name.".tar.gz";
8990        unlink($Pkg);
8991        chdir($From);
8992        system($TarCmd, "-czf", $Pkg, $Name);
8993        if($?)
8994        { # cannot allocate memory (or other problems with "tar")
8995            unlink($Path);
8996            exitStatus("Error", "can't pack the ABI dump: ".$!);
8997        }
8998        chdir($ORIG_DIR);
8999        unlink($Path);
9000        return $To."/".$Name.".tar.gz";
9001    }
9002}
9003
9004sub is_header_file($)
9005{
9006    if($_[0]=~/\.($HEADER_EXT)\Z/i) {
9007        return $_[0];
9008    }
9009    return 0;
9010}
9011
9012sub is_not_header($)
9013{
9014    if($_[0]=~/\.\w+\Z/
9015    and $_[0]!~/\.($HEADER_EXT)\Z/i) {
9016        return 1;
9017    }
9018    return 0;
9019}
9020
9021sub is_header($$$)
9022{
9023    my ($Header, $UserDefined, $LibVersion) = @_;
9024    return 0 if(-d $Header);
9025    if(-f $Header) {
9026        $Header = get_abs_path($Header);
9027    }
9028    else
9029    {
9030        if(is_abs($Header))
9031        { # incorrect absolute path
9032            return 0;
9033        }
9034        if(my $HPath = identifyHeader($Header, $LibVersion)) {
9035            $Header = $HPath;
9036        }
9037        else
9038        { # can't find header
9039            return 0;
9040        }
9041    }
9042    if($Header=~/\.\w+\Z/)
9043    { # have an extension
9044        return is_header_file($Header);
9045    }
9046    else
9047    {
9048        if($UserDefined==2)
9049        { # specified on the command line
9050            if(cmd_file($Header)!~/HTML|XML/i) {
9051                return $Header;
9052            }
9053        }
9054        elsif($UserDefined)
9055        { # specified in the XML-descriptor
9056          # header file without an extension
9057            return $Header;
9058        }
9059        else
9060        {
9061            if(index($Header, "/include/")!=-1
9062            or cmd_file($Header)=~/C[\+]*\s+program/i)
9063            { # !~/HTML|XML|shared|dynamic/i
9064                return $Header;
9065            }
9066        }
9067    }
9068    return 0;
9069}
9070
9071sub addTargetHeaders($)
9072{
9073    my $LibVersion = $_[0];
9074    foreach my $RegHeader (keys(%{$Registered_Headers{$LibVersion}}))
9075    {
9076        my $RegDir = get_dirname($RegHeader);
9077        $TargetHeaders{$LibVersion}{get_filename($RegHeader)} = 1;
9078
9079        if(not $INC_PATH_AUTODETECT{$LibVersion}) {
9080            detect_recursive_includes($RegHeader, $LibVersion);
9081        }
9082
9083        foreach my $RecInc (keys(%{$RecursiveIncludes{$LibVersion}{$RegHeader}}))
9084        {
9085            my $Dir = get_dirname($RecInc);
9086
9087            if(familiarDirs($RegDir, $Dir)
9088            or $RecursiveIncludes{$LibVersion}{$RegHeader}{$RecInc}!=1)
9089            { # in the same directory or included by #include "..."
9090                $TargetHeaders{$LibVersion}{get_filename($RecInc)} = 1;
9091            }
9092        }
9093    }
9094}
9095
9096sub familiarDirs($$)
9097{
9098    my ($D1, $D2) = @_;
9099    if($D1 eq $D2) {
9100        return 1;
9101    }
9102
9103    my $U1 = index($D1, "/usr/");
9104    my $U2 = index($D2, "/usr/");
9105
9106    if($U1==0 and $U2!=0) {
9107        return 0;
9108    }
9109
9110    if($U2==0 and $U1!=0) {
9111        return 0;
9112    }
9113
9114    if(index($D2, $D1."/")==0) {
9115        return 1;
9116    }
9117
9118    # /usr/include/DIR
9119    # /home/user/DIR
9120
9121    my $DL = get_depth($D1);
9122
9123    my @Dirs1 = ($D1);
9124    while($DL - get_depth($D1)<=2
9125    and get_depth($D1)>=4
9126    and $D1=~s/[\/\\]+[^\/\\]*?\Z//) {
9127        push(@Dirs1, $D1);
9128    }
9129
9130    my @Dirs2 = ($D2);
9131    while(get_depth($D2)>=4
9132    and $D2=~s/[\/\\]+[^\/\\]*?\Z//) {
9133        push(@Dirs2, $D2);
9134    }
9135
9136    foreach my $P1 (@Dirs1)
9137    {
9138        foreach my $P2 (@Dirs2)
9139        {
9140
9141            if($P1 eq $P2) {
9142                return 1;
9143            }
9144        }
9145    }
9146    return 0;
9147}
9148
9149sub readHeaders($)
9150{
9151    $Version = $_[0];
9152    printMsg("INFO", "checking header(s) ".$Descriptor{$Version}{"Version"}." ...");
9153    my $DumpPath = getDump();
9154    if($Debug)
9155    { # debug mode
9156        mkpath($DEBUG_PATH{$Version});
9157        copy($DumpPath, $DEBUG_PATH{$Version}."/translation-unit-dump.txt");
9158    }
9159    getInfo($DumpPath);
9160}
9161
9162sub prepareTypes($)
9163{
9164    my $LibVersion = $_[0];
9165    if(not checkDump($LibVersion, "2.0"))
9166    { # support for old ABI dumps
9167      # type names have been corrected in ACC 1.22 (dump 2.0 format)
9168        foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}}))
9169        {
9170            my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
9171            if($TName=~/\A(\w+)::(\w+)/) {
9172                my ($P1, $P2) = ($1, $2);
9173                if($P1 eq $P2) {
9174                    $TName=~s/\A$P1:\:$P1(\W)/$P1$1/;
9175                }
9176                else {
9177                    $TName=~s/\A(\w+:\:)$P2:\:$P2(\W)/$1$P2$2/;
9178                }
9179            }
9180            $TypeInfo{$LibVersion}{$TypeId}{"Name"} = $TName;
9181        }
9182    }
9183    if(not checkDump($LibVersion, "2.5"))
9184    { # support for old ABI dumps
9185      # V < 2.5: array size == "number of elements"
9186      # V >= 2.5: array size in bytes
9187        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9188        {
9189            my %Type = get_PureType($TypeId, $TypeInfo{$LibVersion});
9190            if($Type{"Type"} eq "Array")
9191            {
9192                if(my $Size = $Type{"Size"})
9193                { # array[N]
9194                    my %Base = get_OneStep_BaseType($Type{"Tid"}, $TypeInfo{$LibVersion});
9195                    $Size *= $Base{"Size"};
9196                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = "$Size";
9197                }
9198                else
9199                { # array[] is a pointer
9200                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $WORD_SIZE{$LibVersion};
9201                }
9202            }
9203        }
9204    }
9205    my $V2 = ($LibVersion==1)?2:1;
9206    if(not checkDump($LibVersion, "2.7"))
9207    { # support for old ABI dumps
9208      # size of "method ptr" corrected in 2.7
9209        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9210        {
9211            my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion});
9212            if($PureType{"Type"} eq "MethodPtr")
9213            {
9214                my %Type = get_Type($TypeId, $LibVersion);
9215                my $TypeId_2 = getTypeIdByName($PureType{"Name"}, $V2);
9216                my %Type2 = get_Type($TypeId_2, $V2);
9217                if($Type{"Size"} ne $Type2{"Size"}) {
9218                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $Type2{"Size"};
9219                }
9220            }
9221        }
9222    }
9223}
9224
9225sub prepareSymbols($)
9226{
9227    my $LibVersion = $_[0];
9228
9229    if(not keys(%{$SymbolInfo{$LibVersion}}))
9230    { # check if input is valid
9231        if(not $ExtendedCheck)
9232        {
9233            if($CheckHeadersOnly) {
9234                exitStatus("Empty_Set", "the set of public symbols is empty (".$Descriptor{$LibVersion}{"Version"}.")");
9235            }
9236            else {
9237                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection (".$Descriptor{$LibVersion}{"Version"}.")");
9238            }
9239        }
9240    }
9241
9242    my $Remangle = 0;
9243    if(not checkDump(1, "2.10")
9244    or not checkDump(2, "2.10"))
9245    { # different formats
9246        $Remangle = 1;
9247    }
9248    if($CheckHeadersOnly)
9249    { # different languages
9250        if($UserLang)
9251        { # --lang=LANG for both versions
9252            if(($UsedDump{1}{"V"} and $UserLang ne $UsedDump{1}{"L"})
9253            or ($UsedDump{2}{"V"} and $UserLang ne $UsedDump{2}{"L"}))
9254            {
9255                if($UserLang eq "C++")
9256                { # remangle symbols
9257                    $Remangle = 1;
9258                }
9259                elsif($UserLang eq "C")
9260                { # remove mangling
9261                    $Remangle = -1;
9262                }
9263            }
9264        }
9265    }
9266
9267    foreach my $InfoId (sort {int($b)<=>int($a)} keys(%{$SymbolInfo{$LibVersion}}))
9268    { # reverse order: D0, D1, D2, D0 (artificial, GCC < 4.5), C1, C2
9269        if(not checkDump($LibVersion, "2.13"))
9270        { # support for old ABI dumps
9271            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
9272            {
9273                foreach my $P (keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}}))
9274                {
9275                    my $TypeId = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"type"};
9276                    my $DVal = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"};
9277                    my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
9278                    if(defined $DVal and $DVal ne "")
9279                    {
9280                        if($TName eq "char") {
9281                            $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = chr($DVal);
9282                        }
9283                        elsif($TName eq "bool") {
9284                            $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = $DVal?"true":"false";
9285                        }
9286                    }
9287                }
9288            }
9289        }
9290        if($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"})
9291        {
9292            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
9293            and keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})
9294            and $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this")
9295            { # support for old GCC < 4.5: skip artificial ~dtor(int __in_chrg)
9296              # + support for old ABI dumps
9297                next;
9298            }
9299        }
9300        my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
9301        my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
9302        my $ClassID = $SymbolInfo{$LibVersion}{$InfoId}{"Class"};
9303        my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"};
9304
9305        my $SRemangle = 0;
9306        if(not checkDump(1, "2.12")
9307        or not checkDump(2, "2.12"))
9308        { # support for old ABI dumps
9309            if($ShortName eq "operator>>")
9310            {
9311                if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
9312                { # corrected mangling of operator>>
9313                    $SRemangle = 1;
9314                }
9315            }
9316            if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
9317            {
9318                if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
9319                and isConstType($Return, $LibVersion) and $MnglName!~/L\d+$ShortName/)
9320                { # corrected mangling of const global data
9321                  # some global data is not mangled in the TU dump: qt_sine_table (Qt 4.8)
9322                  # and incorrectly mangled by old ACC versions
9323                    $SRemangle = 1;
9324                }
9325            }
9326        }
9327        if(not $CheckHeadersOnly)
9328        { # support for old ABI dumps
9329            if(not checkDump(1, "2.17")
9330            or not checkDump(2, "2.17"))
9331            {
9332                if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
9333                {
9334                    if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
9335                    {
9336                        if(link_symbol($ShortName, $LibVersion, "-Deps"))
9337                        {
9338                            $MnglName = $ShortName;
9339                            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName;
9340                        }
9341                    }
9342                }
9343            }
9344        }
9345        if($Remangle==1 or $SRemangle==1)
9346        { # support for old ABI dumps: some symbols are not mangled in old dumps
9347          # mangle both sets of symbols (old and new)
9348          # NOTE: remangling all symbols by the same mangler
9349            if($MnglName=~/\A_ZN(V|)K/)
9350            { # mangling may be incorrect on old ABI dumps
9351              # because of absent "Const" attribute
9352                $SymbolInfo{$LibVersion}{$InfoId}{"Const"} = 1;
9353            }
9354            if($MnglName=~/\A_ZN(K|)V/)
9355            { # mangling may be incorrect on old ABI dumps
9356              # because of absent "Volatile" attribute
9357                $SymbolInfo{$LibVersion}{$InfoId}{"Volatile"} = 1;
9358            }
9359            if(($ClassID and $MnglName!~/\A(_Z|\?)/)
9360            or (not $ClassID and $CheckHeadersOnly)
9361            or (not $ClassID and not link_symbol($MnglName, $LibVersion, "-Deps")))
9362            { # support for old ABI dumps, GCC >= 4.0
9363              # remangling all manually mangled symbols
9364                if($MnglName = mangle_symbol($InfoId, $LibVersion, "GCC"))
9365                {
9366                    $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName;
9367                    $MangledNames{$LibVersion}{$MnglName} = 1;
9368                }
9369            }
9370        }
9371        elsif($Remangle==-1)
9372        { # remove mangling
9373            $MnglName = "";
9374            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = "";
9375        }
9376        if(not $MnglName) {
9377            next;
9378        }
9379
9380        # NOTE: duplicated entries in the ABI Dump
9381        if(defined $CompleteSignature{$LibVersion}{$MnglName})
9382        {
9383            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
9384            {
9385                if($SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} eq "p1")
9386                {
9387                    next;
9388                }
9389            }
9390        }
9391
9392        if(not $CompleteSignature{$LibVersion}{$MnglName}{"MnglName"})
9393        { # NOTE: global data may enter here twice
9394            %{$CompleteSignature{$LibVersion}{$MnglName}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9395
9396        }
9397        if(not checkDump($LibVersion, "2.6"))
9398        { # support for old dumps
9399          # add "Volatile" attribute
9400            if($MnglName=~/_Z(K|)V/) {
9401                $CompleteSignature{$LibVersion}{$MnglName}{"Volatile"}=1;
9402            }
9403        }
9404        # symbol and its symlink have same signatures
9405        if($SymVer{$LibVersion}{$MnglName}) {
9406            %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$MnglName}}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9407        }
9408
9409        if(my $Alias = $CompleteSignature{$LibVersion}{$MnglName}{"Alias"})
9410        {
9411            %{$CompleteSignature{$LibVersion}{$Alias}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9412
9413            if($SymVer{$LibVersion}{$Alias}) {
9414                %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$Alias}}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9415            }
9416        }
9417
9418        # clean memory
9419        delete($SymbolInfo{$LibVersion}{$InfoId});
9420    }
9421    if($COMMON_LANGUAGE{$LibVersion} eq "C++" or $OSgroup eq "windows") {
9422        translateSymbols(keys(%{$CompleteSignature{$LibVersion}}), $LibVersion);
9423    }
9424    if($ExtendedCheck)
9425    { # --ext option
9426        addExtension($LibVersion);
9427    }
9428
9429    # clean memory
9430    delete($SymbolInfo{$LibVersion});
9431
9432    foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}}))
9433    { # detect allocable classes with public exported constructors
9434      # or classes with auto-generated or inline-only constructors
9435      # and other temp info
9436        if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
9437        {
9438            my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
9439            if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"}
9440            and not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"})
9441            { # Class() { ... } will not be exported
9442                if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"})
9443                {
9444                    if($CheckHeadersOnly or link_symbol($Symbol, $LibVersion, "-Deps")) {
9445                        $AllocableClass{$LibVersion}{$ClassName} = 1;
9446                    }
9447                }
9448            }
9449            if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"})
9450            { # all imported class methods
9451                if(symbolFilter($Symbol, $LibVersion, "Affected", "Binary"))
9452                {
9453                    if($CheckHeadersOnly)
9454                    {
9455                        if(not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"}
9456                        or $CompleteSignature{$LibVersion}{$Symbol}{"Virt"})
9457                        { # all symbols except non-virtual inline
9458                            $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9459                        }
9460                    }
9461                    else {
9462                        $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9463                    }
9464                }
9465                if(symbolFilter($Symbol, $LibVersion, "Affected", "Source")) {
9466                    $ClassMethods{"Source"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9467                }
9468            }
9469            $ClassNames{$LibVersion}{$ClassName} = 1;
9470        }
9471        if(my $RetId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"})
9472        {
9473            my %Base = get_BaseType($RetId, $LibVersion);
9474            if(defined $Base{"Type"}
9475            and $Base{"Type"}=~/Struct|Class/)
9476            {
9477                my $Name = $TypeInfo{$LibVersion}{$Base{"Tid"}}{"Name"};
9478                if($Name=~/<([^<>\s]+)>/)
9479                {
9480                    if(my $Tid = getTypeIdByName($1, $LibVersion)) {
9481                        $ReturnedClass{$LibVersion}{$Tid} = 1;
9482                    }
9483                }
9484                else {
9485                    $ReturnedClass{$LibVersion}{$Base{"Tid"}} = 1;
9486                }
9487            }
9488        }
9489        foreach my $Num (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
9490        {
9491            my $PId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Num}{"type"};
9492            if(get_PLevel($PId, $LibVersion)>=1)
9493            {
9494                if(my %Base = get_BaseType($PId, $LibVersion))
9495                {
9496                    if($Base{"Type"}=~/Struct|Class/)
9497                    {
9498                        $ParamClass{$LibVersion}{$Base{"Tid"}}{$Symbol} = 1;
9499                        foreach my $SubId (get_sub_classes($Base{"Tid"}, $LibVersion, 1))
9500                        { # mark all derived classes
9501                            $ParamClass{$LibVersion}{$SubId}{$Symbol} = 1;
9502                        }
9503                    }
9504                }
9505            }
9506        }
9507
9508        # mapping {short name => symbols}
9509        $Func_ShortName{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"ShortName"}}{$Symbol} = 1;
9510    }
9511    foreach my $MnglName (keys(%VTableClass))
9512    { # reconstruct attributes of v-tables
9513        if(index($MnglName, "_ZTV")==0)
9514        {
9515            if(my $ClassName = $VTableClass{$MnglName})
9516            {
9517                if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName})
9518                {
9519                    $CompleteSignature{$LibVersion}{$MnglName}{"Header"} = $TypeInfo{$LibVersion}{$ClassId}{"Header"};
9520                    $CompleteSignature{$LibVersion}{$MnglName}{"Class"} = $ClassId;
9521                }
9522            }
9523        }
9524    }
9525
9526    # types
9527    foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}}))
9528    {
9529        if(my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"})
9530        {
9531            if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"}) {
9532                $ClassNames{$LibVersion}{$TName} = 1;
9533            }
9534            if(defined $TypeInfo{$LibVersion}{$TypeId}{"Base"})
9535            {
9536                $ClassNames{$LibVersion}{$TName} = 1;
9537                foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"Base"}}))
9538                {
9539                    if(my $BName = $TypeInfo{$LibVersion}{$Bid}{"Name"}) {
9540                        $ClassNames{$LibVersion}{$BName} = 1;
9541                    }
9542                }
9543            }
9544        }
9545    }
9546}
9547
9548sub getFirst($$)
9549{
9550    my ($Tid, $LibVersion) = @_;
9551    if(not $Tid) {
9552        return $Tid;
9553    }
9554
9555    if(my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"})
9556    {
9557        if($TName_Tid{$LibVersion}{$Name}) {
9558            return $TName_Tid{$LibVersion}{$Name};
9559        }
9560    }
9561
9562    return $Tid;
9563}
9564
9565sub register_SymbolUsage($$$)
9566{
9567    my ($InfoId, $UsedType, $LibVersion) = @_;
9568
9569    my %FuncInfo = %{$SymbolInfo{$LibVersion}{$InfoId}};
9570    if(my $RTid = getFirst($FuncInfo{"Return"}, $LibVersion))
9571    {
9572        register_TypeUsage($RTid, $UsedType, $LibVersion);
9573        $SymbolInfo{$LibVersion}{$InfoId}{"Return"} = $RTid;
9574    }
9575    if(my $FCid = getFirst($FuncInfo{"Class"}, $LibVersion))
9576    {
9577        register_TypeUsage($FCid, $UsedType, $LibVersion);
9578        $SymbolInfo{$LibVersion}{$InfoId}{"Class"} = $FCid;
9579
9580        if(my $ThisId = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."*const", $LibVersion))
9581        { # register "this" pointer
9582            register_TypeUsage($ThisId, $UsedType, $LibVersion);
9583        }
9584        if(my $ThisId_C = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."const*const", $LibVersion))
9585        { # register "this" pointer (const method)
9586            register_TypeUsage($ThisId_C, $UsedType, $LibVersion);
9587        }
9588    }
9589    foreach my $PPos (keys(%{$FuncInfo{"Param"}}))
9590    {
9591        if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"}, $LibVersion))
9592        {
9593            register_TypeUsage($PTid, $UsedType, $LibVersion);
9594            $FuncInfo{"Param"}{$PPos}{"type"} = $PTid;
9595        }
9596    }
9597    foreach my $TPos (keys(%{$FuncInfo{"TParam"}}))
9598    {
9599        my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"};
9600        if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) {
9601            register_TypeUsage($TTid, $UsedType, $LibVersion);
9602        }
9603    }
9604}
9605
9606sub register_TypeUsage($$$)
9607{
9608    my ($TypeId, $UsedType, $LibVersion) = @_;
9609    if(not $TypeId) {
9610        return;
9611    }
9612    if($UsedType->{$TypeId})
9613    { # already registered
9614        return;
9615    }
9616
9617    my %TInfo = get_Type($TypeId, $LibVersion);
9618    if($TInfo{"Type"})
9619    {
9620        if(my $NS = $TInfo{"NameSpace"})
9621        {
9622            if(my $NSTid = $TName_Tid{$LibVersion}{$NS}) {
9623                register_TypeUsage($NSTid, $UsedType, $LibVersion);
9624            }
9625        }
9626
9627        if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/)
9628        {
9629            $UsedType->{$TypeId} = 1;
9630            if($TInfo{"Type"}=~/\A(Struct|Class)\Z/)
9631            {
9632                foreach my $BaseId (keys(%{$TInfo{"Base"}})) {
9633                    register_TypeUsage($BaseId, $UsedType, $LibVersion);
9634                }
9635                foreach my $TPos (keys(%{$TInfo{"TParam"}}))
9636                {
9637                    my $TPName = $TInfo{"TParam"}{$TPos}{"name"};
9638                    if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) {
9639                        register_TypeUsage($TTid, $UsedType, $LibVersion);
9640                    }
9641                }
9642            }
9643            foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}}))
9644            {
9645                if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"}, $LibVersion))
9646                {
9647                    register_TypeUsage($MTid, $UsedType, $LibVersion);
9648                    $TInfo{"Memb"}{$Memb_Pos}{"type"} = $MTid;
9649                }
9650            }
9651            if($TInfo{"Type"} eq "FuncPtr"
9652            or $TInfo{"Type"} eq "MethodPtr"
9653            or $TInfo{"Type"} eq "Func")
9654            {
9655                if(my $RTid = $TInfo{"Return"}) {
9656                    register_TypeUsage($RTid, $UsedType, $LibVersion);
9657                }
9658                foreach my $PPos (keys(%{$TInfo{"Param"}}))
9659                {
9660                    if(my $PTid = $TInfo{"Param"}{$PPos}{"type"}) {
9661                        register_TypeUsage($PTid, $UsedType, $LibVersion);
9662                    }
9663                }
9664            }
9665            if($TInfo{"Type"} eq "FieldPtr")
9666            {
9667                if(my $RTid = $TInfo{"Return"}) {
9668                    register_TypeUsage($RTid, $UsedType, $LibVersion);
9669                }
9670                if(my $CTid = $TInfo{"Class"}) {
9671                    register_TypeUsage($CTid, $UsedType, $LibVersion);
9672                }
9673            }
9674            if($TInfo{"Type"} eq "MethodPtr")
9675            {
9676                if(my $CTid = $TInfo{"Class"}) {
9677                    register_TypeUsage($CTid, $UsedType, $LibVersion);
9678                }
9679            }
9680        }
9681        elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|Restrict|Array|Typedef)\Z/)
9682        {
9683            $UsedType->{$TypeId} = 1;
9684            if(my $BTid = getFirst($TInfo{"BaseType"}, $LibVersion))
9685            {
9686                register_TypeUsage($BTid, $UsedType, $LibVersion);
9687                $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $BTid;
9688            }
9689        }
9690        else
9691        { # Intrinsic, TemplateParam, TypeName, SizeOf, etc.
9692            $UsedType->{$TypeId} = 1;
9693        }
9694    }
9695}
9696
9697sub selectSymbol($$$$)
9698{ # select symbol to check or to dump
9699    my ($Symbol, $SInfo, $Level, $LibVersion) = @_;
9700
9701    if($Level eq "Dump")
9702    {
9703        if($SInfo->{"Virt"} or $SInfo->{"PureVirt"})
9704        { # TODO: check if this symbol is from
9705          # base classes of other target symbols
9706            return 1;
9707        }
9708    }
9709
9710    if(not $STDCXX_TESTING and $Symbol=~/\A(_ZS|_ZNS|_ZNKS)/)
9711    { # stdc++ interfaces
9712        return 0;
9713    }
9714
9715    my $Target = 0;
9716    if(my $Header = $SInfo->{"Header"}) {
9717        $Target = (is_target_header($Header, 1) or is_target_header($Header, 2));
9718    }
9719    if($ExtendedCheck)
9720    {
9721        if(index($Symbol, "external_func_")==0) {
9722            $Target = 1;
9723        }
9724    }
9725    if($CheckHeadersOnly or $Level eq "Source")
9726    {
9727        if($Target)
9728        {
9729            if($Level eq "Dump")
9730            { # dumped
9731                if($BinaryOnly)
9732                {
9733                    if(not $SInfo->{"InLine"} or $SInfo->{"Data"}) {
9734                        return 1;
9735                    }
9736                }
9737                else {
9738                    return 1;
9739                }
9740            }
9741            elsif($Level eq "Source")
9742            { # checked
9743                return 1;
9744            }
9745            elsif($Level eq "Binary")
9746            { # checked
9747                if(not $SInfo->{"InLine"} or $SInfo->{"Data"}
9748                or $SInfo->{"Virt"} or $SInfo->{"PureVirt"}) {
9749                    return 1;
9750                }
9751            }
9752        }
9753    }
9754    else
9755    { # library is available
9756        if(link_symbol($Symbol, $LibVersion, "-Deps"))
9757        { # exported symbols
9758            return 1;
9759        }
9760        if($Level eq "Dump")
9761        { # dumped
9762            if($BinaryOnly)
9763            {
9764                if($SInfo->{"Data"})
9765                {
9766                    if($Target) {
9767                        return 1;
9768                    }
9769                }
9770            }
9771            else
9772            { # SrcBin
9773                if($Target) {
9774                    return 1;
9775                }
9776            }
9777        }
9778        elsif($Level eq "Source")
9779        { # checked
9780            if($SInfo->{"PureVirt"} or $SInfo->{"Data"} or $SInfo->{"InLine"}
9781            or isInLineInst($SInfo, $LibVersion))
9782            { # skip LOCAL symbols
9783                if($Target) {
9784                    return 1;
9785                }
9786            }
9787        }
9788        elsif($Level eq "Binary")
9789        { # checked
9790            if($SInfo->{"PureVirt"} or $SInfo->{"Data"})
9791            {
9792                if($Target) {
9793                    return 1;
9794                }
9795            }
9796        }
9797    }
9798    return 0;
9799}
9800
9801sub cleanDump($)
9802{ # clean data
9803    my $LibVersion = $_[0];
9804    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
9805    {
9806        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}}))
9807        {
9808            delete($SymbolInfo{$LibVersion}{$InfoId});
9809            next;
9810        }
9811        my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
9812        if(not $MnglName)
9813        {
9814            delete($SymbolInfo{$LibVersion}{$InfoId});
9815            next;
9816        }
9817        my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
9818        if(not $ShortName)
9819        {
9820            delete($SymbolInfo{$LibVersion}{$InfoId});
9821            next;
9822        }
9823        if($MnglName eq $ShortName)
9824        { # remove duplicate data
9825            delete($SymbolInfo{$LibVersion}{$InfoId}{"MnglName"});
9826        }
9827        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})) {
9828            delete($SymbolInfo{$LibVersion}{$InfoId}{"Param"});
9829        }
9830        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}})) {
9831            delete($SymbolInfo{$LibVersion}{$InfoId}{"TParam"});
9832        }
9833        delete($SymbolInfo{$LibVersion}{$InfoId}{"Type"});
9834    }
9835    foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
9836    {
9837        if(not keys(%{$TypeInfo{$LibVersion}{$Tid}}))
9838        {
9839            delete($TypeInfo{$LibVersion}{$Tid});
9840            next;
9841        }
9842        delete($TypeInfo{$LibVersion}{$Tid}{"Tid"});
9843        foreach my $Attr ("Header", "Line", "Size", "NameSpace")
9844        {
9845            if(not $TypeInfo{$LibVersion}{$Tid}{$Attr}) {
9846                delete($TypeInfo{$LibVersion}{$Tid}{$Attr});
9847            }
9848        }
9849        if(not keys(%{$TypeInfo{$LibVersion}{$Tid}{"TParam"}})) {
9850            delete($TypeInfo{$LibVersion}{$Tid}{"TParam"});
9851        }
9852    }
9853}
9854
9855sub pickType($$)
9856{
9857    my ($Tid, $LibVersion) = @_;
9858
9859    if(my $Dupl = $TypeTypedef{$LibVersion}{$Tid})
9860    {
9861        if(defined $TypeInfo{$LibVersion}{$Dupl})
9862        {
9863            if($TypeInfo{$LibVersion}{$Dupl}{"Name"} eq $TypeInfo{$LibVersion}{$Tid}{"Name"})
9864            { # duplicate
9865                return 0;
9866            }
9867        }
9868    }
9869
9870    my $THeader = $TypeInfo{$LibVersion}{$Tid}{"Header"};
9871
9872    if(isBuiltIn($THeader)) {
9873        return 0;
9874    }
9875
9876    if($TypeInfo{$LibVersion}{$Tid}{"Type"}!~/Class|Struct|Union|Enum|Typedef/) {
9877        return 0;
9878    }
9879
9880    if(isAnon($TypeInfo{$LibVersion}{$Tid}{"Name"})) {
9881        return 0;
9882    }
9883
9884    if(selfTypedef($Tid, $LibVersion)) {
9885        return 0;
9886    }
9887
9888    if(not isTargetType($Tid, $LibVersion)) {
9889        return 0;
9890    }
9891
9892    return 0;
9893}
9894
9895sub isTargetType($$)
9896{
9897    my ($Tid, $LibVersion) = @_;
9898
9899    if($TypeInfo{$LibVersion}{$Tid}{"Type"}!~/Class|Struct|Union|Enum|Typedef/)
9900    { # derived
9901        return 1;
9902    }
9903
9904    if(my $THeader = $TypeInfo{$LibVersion}{$Tid}{"Header"})
9905    { # NOTE: header is defined to source if undefined (DWARF dumps)
9906        if(not is_target_header($THeader, $LibVersion))
9907        { # from target headers
9908            return 0;
9909        }
9910    }
9911    else
9912    { # NOTE: if type is defined in source
9913        if($UsedDump{$LibVersion}{"Public"})
9914        {
9915            if(isPrivateABI($Tid, $LibVersion)) {
9916                return 0;
9917            }
9918            else {
9919                return 1;
9920            }
9921        }
9922        else {
9923            return 0;
9924        }
9925    }
9926
9927    if($SkipInternalTypes)
9928    {
9929        if($TypeInfo{$LibVersion}{$Tid}{"Name"}=~/($SkipInternalTypes)/)
9930        {
9931            return 0;
9932        }
9933    }
9934
9935    return 1;
9936}
9937
9938sub remove_Unused($$)
9939{ # remove unused data types from the ABI dump
9940    my ($LibVersion, $Kind) = @_;
9941
9942    my %UsedType = ();
9943
9944    foreach my $InfoId (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}}))
9945    {
9946        register_SymbolUsage($InfoId, \%UsedType, $LibVersion);
9947    }
9948    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9949    {
9950        if($UsedType{$Tid})
9951        { # All & Extended
9952            next;
9953        }
9954
9955        if($Kind eq "Extended")
9956        {
9957            if(pickType($Tid, $LibVersion))
9958            {
9959                my %Tree = ();
9960                register_TypeUsage($Tid, \%Tree, $LibVersion);
9961
9962                my $Tmpl = 0;
9963                foreach (sort {int($a)<=>int($b)} keys(%Tree))
9964                {
9965                    if(defined $TypeInfo{$LibVersion}{$_}{"Template"}
9966                    or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam")
9967                    {
9968                        $Tmpl = 1;
9969                        last;
9970                    }
9971                }
9972                if(not $Tmpl)
9973                {
9974                    foreach (keys(%Tree)) {
9975                        $UsedType{$_} = 1;
9976                    }
9977                }
9978            }
9979        }
9980    }
9981
9982    my %Delete = ();
9983
9984    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9985    { # remove unused types
9986        if($UsedType{$Tid})
9987        { # All & Extended
9988            next;
9989        }
9990
9991        if($Kind eq "Extra")
9992        {
9993            my %Tree = ();
9994            register_TypeUsage($Tid, \%Tree, $LibVersion);
9995
9996            foreach (sort {int($a)<=>int($b)} keys(%Tree))
9997            {
9998                if(defined $TypeInfo{$LibVersion}{$_}{"Template"}
9999                or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam")
10000                {
10001                    $Delete{$Tid} = 1;
10002                    last;
10003                }
10004            }
10005        }
10006        else
10007        {
10008            # remove type
10009            delete($TypeInfo{$LibVersion}{$Tid});
10010        }
10011    }
10012
10013    if($Kind eq "Extra")
10014    { # remove duplicates
10015        foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
10016        {
10017            if($UsedType{$Tid})
10018            { # All & Extended
10019                next;
10020            }
10021
10022            my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"};
10023
10024            if($TName_Tid{$LibVersion}{$Name} ne $Tid) {
10025                delete($TypeInfo{$LibVersion}{$Tid});
10026            }
10027        }
10028    }
10029
10030    foreach my $Tid (keys(%Delete))
10031    {
10032        delete($TypeInfo{$LibVersion}{$Tid});
10033    }
10034}
10035
10036sub check_Completeness($$)
10037{
10038    my ($Info, $LibVersion) = @_;
10039
10040    # data types
10041    if(defined $Info->{"Memb"})
10042    {
10043        foreach my $Pos (keys(%{$Info->{"Memb"}}))
10044        {
10045            if(defined $Info->{"Memb"}{$Pos}{"type"}) {
10046                check_TypeInfo($Info->{"Memb"}{$Pos}{"type"}, $LibVersion);
10047            }
10048        }
10049    }
10050    if(defined $Info->{"Base"})
10051    {
10052        foreach my $Bid (keys(%{$Info->{"Base"}})) {
10053            check_TypeInfo($Bid, $LibVersion);
10054        }
10055    }
10056    if(defined $Info->{"BaseType"}) {
10057        check_TypeInfo($Info->{"BaseType"}, $LibVersion);
10058    }
10059    if(defined $Info->{"TParam"})
10060    {
10061        foreach my $Pos (keys(%{$Info->{"TParam"}}))
10062        {
10063            my $TName = $Info->{"TParam"}{$Pos}{"name"};
10064            if($TName=~/\A\(.+\)(true|false|\d.*)\Z/) {
10065                next;
10066            }
10067            if($TName eq "_BoolType") {
10068                next;
10069            }
10070            if($TName=~/\Asizeof\(/) {
10071                next;
10072            }
10073            if(my $Tid = $TName_Tid{$LibVersion}{$TName}) {
10074                check_TypeInfo($Tid, $LibVersion);
10075            }
10076            else
10077            {
10078                if(defined $Debug) {
10079                    printMsg("WARNING", "missed type $TName");
10080                }
10081            }
10082        }
10083    }
10084
10085    # symbols
10086    if(defined $Info->{"Param"})
10087    {
10088        foreach my $Pos (keys(%{$Info->{"Param"}}))
10089        {
10090            if(defined $Info->{"Param"}{$Pos}{"type"}) {
10091                check_TypeInfo($Info->{"Param"}{$Pos}{"type"}, $LibVersion);
10092            }
10093        }
10094    }
10095    if(defined $Info->{"Return"}) {
10096        check_TypeInfo($Info->{"Return"}, $LibVersion);
10097    }
10098    if(defined $Info->{"Class"}) {
10099        check_TypeInfo($Info->{"Class"}, $LibVersion);
10100    }
10101}
10102
10103sub check_TypeInfo($$)
10104{
10105    my ($Tid, $LibVersion) = @_;
10106
10107    if(defined $CheckedTypeInfo{$LibVersion}{$Tid}) {
10108        return;
10109    }
10110    $CheckedTypeInfo{$LibVersion}{$Tid} = 1;
10111
10112    if(defined $TypeInfo{$LibVersion}{$Tid})
10113    {
10114        if(not $TypeInfo{$LibVersion}{$Tid}{"Name"}) {
10115            printMsg("ERROR", "missed type name ($Tid)");
10116        }
10117        check_Completeness($TypeInfo{$LibVersion}{$Tid}, $LibVersion);
10118    }
10119    else {
10120        printMsg("ERROR", "missed type id $Tid");
10121    }
10122}
10123
10124sub selfTypedef($$)
10125{
10126    my ($TypeId, $LibVersion) = @_;
10127    my %Type = get_Type($TypeId, $LibVersion);
10128    if($Type{"Type"} eq "Typedef")
10129    {
10130        my %Base = get_OneStep_BaseType($TypeId, $TypeInfo{$LibVersion});
10131        if($Base{"Type"}=~/Class|Struct/)
10132        {
10133            if($Type{"Name"} eq $Base{"Name"}) {
10134                return 1;
10135            }
10136            elsif($Type{"Name"}=~/::(\w+)\Z/)
10137            {
10138                if($Type{"Name"} eq $Base{"Name"}."::".$1)
10139                { # QPointer<QWidget>::QPointer
10140                    return 1;
10141                }
10142            }
10143        }
10144    }
10145    return 0;
10146}
10147
10148sub addExtension($)
10149{
10150    my $LibVersion = $_[0];
10151    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
10152    {
10153        if(pickType($Tid, $LibVersion))
10154        {
10155            my $TName = $TypeInfo{$LibVersion}{$Tid}{"Name"};
10156            $TName=~s/\A(struct|union|class|enum) //;
10157            my $Symbol = "external_func_".$TName;
10158
10159            %{$CompleteSignature{$LibVersion}{$Symbol}} = (
10160                "Header" => "extended.h",
10161                "ShortName" => $Symbol,
10162                "MnglName" => $Symbol,
10163                "Param" => { 0 => { "type"=>$Tid, "name"=>"p1" } }
10164            );
10165
10166            $ExtendedSymbols{$Symbol} = 1;
10167            $CheckedSymbols{"Binary"}{$Symbol} = 1;
10168            $CheckedSymbols{"Source"}{$Symbol} = 1;
10169        }
10170    }
10171    $ExtendedSymbols{"external_func_0"} = 1;
10172    $CheckedSymbols{"Binary"}{"external_func_0"} = 1;
10173    $CheckedSymbols{"Source"}{"external_func_0"} = 1;
10174}
10175
10176sub findMethod($$$)
10177{
10178    my ($VirtFunc, $ClassId, $LibVersion) = @_;
10179    foreach my $BaseClass_Id (keys(%{$TypeInfo{$LibVersion}{$ClassId}{"Base"}}))
10180    {
10181        if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LibVersion)) {
10182            return $VirtMethodInClass;
10183        }
10184        elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LibVersion)) {
10185            return $VirtMethodInBaseClasses;
10186        }
10187    }
10188    return "";
10189}
10190
10191sub findMethod_Class($$$)
10192{
10193    my ($VirtFunc, $ClassId, $LibVersion) = @_;
10194    my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
10195    return "" if(not defined $VirtualTable{$LibVersion}{$ClassName});
10196    my $TargetSuffix = get_symbol_suffix($VirtFunc, 1);
10197    my $TargetShortName = $CompleteSignature{$LibVersion}{$VirtFunc}{"ShortName"};
10198    foreach my $Candidate (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10199    { # search for interface with the same parameters suffix (overridden)
10200        if($TargetSuffix eq get_symbol_suffix($Candidate, 1))
10201        {
10202            if($CompleteSignature{$LibVersion}{$VirtFunc}{"Destructor"})
10203            {
10204                if($CompleteSignature{$LibVersion}{$Candidate}{"Destructor"})
10205                {
10206                    if(($VirtFunc=~/D0E/ and $Candidate=~/D0E/)
10207                    or ($VirtFunc=~/D1E/ and $Candidate=~/D1E/)
10208                    or ($VirtFunc=~/D2E/ and $Candidate=~/D2E/)) {
10209                        return $Candidate;
10210                    }
10211                }
10212            }
10213            else
10214            {
10215                if($TargetShortName eq $CompleteSignature{$LibVersion}{$Candidate}{"ShortName"}) {
10216                    return $Candidate;
10217                }
10218            }
10219        }
10220    }
10221    return "";
10222}
10223
10224sub registerVTable($)
10225{
10226    my $LibVersion = $_[0];
10227    foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}}))
10228    {
10229        if($CompleteSignature{$LibVersion}{$Symbol}{"Virt"}
10230        or $CompleteSignature{$LibVersion}{$Symbol}{"PureVirt"})
10231        {
10232            my $ClassName = $TypeInfo{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"Class"}}{"Name"};
10233            next if(not $STDCXX_TESTING and $ClassName=~/\A(std::|__cxxabi)/);
10234            if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}
10235            and $Symbol=~/D2E/)
10236            { # pure virtual D2-destructors are marked as "virt" in the dump
10237              # virtual D2-destructors are NOT marked as "virt" in the dump
10238              # both destructors are not presented in the v-table
10239                next;
10240            }
10241            my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
10242            $VirtualTable{$LibVersion}{$ClassName}{$MnglName} = 1;
10243        }
10244    }
10245}
10246
10247sub registerOverriding($)
10248{
10249    my $LibVersion = $_[0];
10250    my @Classes = keys(%{$VirtualTable{$LibVersion}});
10251    @Classes = sort {int($TName_Tid{$LibVersion}{$a})<=>int($TName_Tid{$LibVersion}{$b})} @Classes;
10252    foreach my $ClassName (@Classes)
10253    {
10254        foreach my $VirtFunc (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10255        {
10256            if($CompleteSignature{$LibVersion}{$VirtFunc}{"PureVirt"})
10257            { # pure virtuals
10258                next;
10259            }
10260            my $ClassId = $TName_Tid{$LibVersion}{$ClassName};
10261            if(my $Overridden = findMethod($VirtFunc, $ClassId, $LibVersion))
10262            {
10263                if($CompleteSignature{$LibVersion}{$Overridden}{"Virt"}
10264                or $CompleteSignature{$LibVersion}{$Overridden}{"PureVirt"})
10265                { # both overridden virtual methods
10266                  # and implemented pure virtual methods
10267                    $CompleteSignature{$LibVersion}{$VirtFunc}{"Override"} = $Overridden;
10268                    $OverriddenMethods{$LibVersion}{$Overridden}{$VirtFunc} = 1;
10269                    delete($VirtualTable{$LibVersion}{$ClassName}{$VirtFunc}); # remove from v-table model
10270                }
10271            }
10272        }
10273        if(not keys(%{$VirtualTable{$LibVersion}{$ClassName}})) {
10274            delete($VirtualTable{$LibVersion}{$ClassName});
10275        }
10276    }
10277}
10278
10279sub setVirtFuncPositions($)
10280{
10281    my $LibVersion = $_[0];
10282    foreach my $ClassName (keys(%{$VirtualTable{$LibVersion}}))
10283    {
10284        my ($Num, $Rel) = (1, 0);
10285
10286        if(my @Funcs = sort keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10287        {
10288            if($UsedDump{$LibVersion}{"DWARF"}) {
10289                @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @Funcs;
10290            }
10291            else {
10292                @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @Funcs;
10293            }
10294            foreach my $VirtFunc (@Funcs)
10295            {
10296                if($UsedDump{$LibVersion}{"DWARF"}) {
10297                    $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $CompleteSignature{$LibVersion}{$VirtFunc}{"VirtPos"};
10298                }
10299                else {
10300                    $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $Num++;
10301                }
10302
10303                # set relative positions
10304                if(defined $VirtualTable{1}{$ClassName} and defined $VirtualTable{1}{$ClassName}{$VirtFunc}
10305                and defined $VirtualTable{2}{$ClassName} and defined $VirtualTable{2}{$ClassName}{$VirtFunc})
10306                { # relative position excluding added and removed virtual functions
10307                    if(not $CompleteSignature{1}{$VirtFunc}{"Override"}
10308                    and not $CompleteSignature{2}{$VirtFunc}{"Override"}) {
10309                        $CompleteSignature{$LibVersion}{$VirtFunc}{"RelPos"} = $Rel++;
10310                    }
10311                }
10312            }
10313        }
10314    }
10315    foreach my $ClassName (keys(%{$ClassNames{$LibVersion}}))
10316    {
10317        my $AbsNum = 1;
10318        foreach my $VirtFunc (getVTable_Model($TName_Tid{$LibVersion}{$ClassName}, $LibVersion)) {
10319            $VirtualTable_Model{$LibVersion}{$ClassName}{$VirtFunc} = $AbsNum++;
10320        }
10321    }
10322}
10323
10324sub get_sub_classes($$$)
10325{
10326    my ($ClassId, $LibVersion, $Recursive) = @_;
10327    return () if(not defined $Class_SubClasses{$LibVersion}{$ClassId});
10328    my @Subs = ();
10329    foreach my $SubId (keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
10330    {
10331        if($Recursive)
10332        {
10333            foreach my $SubSubId (get_sub_classes($SubId, $LibVersion, $Recursive)) {
10334                push(@Subs, $SubSubId);
10335            }
10336        }
10337        push(@Subs, $SubId);
10338    }
10339    return @Subs;
10340}
10341
10342sub get_base_classes($$$)
10343{
10344    my ($ClassId, $LibVersion, $Recursive) = @_;
10345    my %ClassType = get_Type($ClassId, $LibVersion);
10346    return () if(not defined $ClassType{"Base"});
10347    my @Bases = ();
10348    foreach my $BaseId (sort {int($ClassType{"Base"}{$a}{"pos"})<=>int($ClassType{"Base"}{$b}{"pos"})}
10349    keys(%{$ClassType{"Base"}}))
10350    {
10351        if($Recursive)
10352        {
10353            foreach my $SubBaseId (get_base_classes($BaseId, $LibVersion, $Recursive)) {
10354                push(@Bases, $SubBaseId);
10355            }
10356        }
10357        push(@Bases, $BaseId);
10358    }
10359    return @Bases;
10360}
10361
10362sub getVTable_Model($$)
10363{ # return an ordered list of v-table elements
10364    my ($ClassId, $LibVersion) = @_;
10365    my @Bases = get_base_classes($ClassId, $LibVersion, 1);
10366    my @Elements = ();
10367    foreach my $BaseId (@Bases, $ClassId)
10368    {
10369        if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"})
10370        {
10371            if(defined $VirtualTable{$LibVersion}{$BName})
10372            {
10373                my @VFuncs = keys(%{$VirtualTable{$LibVersion}{$BName}});
10374                if($UsedDump{$LibVersion}{"DWARF"}) {
10375                    @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @VFuncs;
10376                }
10377                else {
10378                    @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @VFuncs;
10379                }
10380                foreach my $VFunc (@VFuncs) {
10381                    push(@Elements, $VFunc);
10382                }
10383            }
10384        }
10385    }
10386    return @Elements;
10387}
10388
10389sub getVShift($$)
10390{
10391    my ($ClassId, $LibVersion) = @_;
10392    my @Bases = get_base_classes($ClassId, $LibVersion, 1);
10393    my $VShift = 0;
10394    foreach my $BaseId (@Bases)
10395    {
10396        if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"})
10397        {
10398            if(defined $VirtualTable{$LibVersion}{$BName}) {
10399                $VShift+=keys(%{$VirtualTable{$LibVersion}{$BName}});
10400            }
10401        }
10402    }
10403    return $VShift;
10404}
10405
10406sub getShift($$)
10407{
10408    my ($ClassId, $LibVersion) = @_;
10409    my @Bases = get_base_classes($ClassId, $LibVersion, 0);
10410    my $Shift = 0;
10411    foreach my $BaseId (@Bases)
10412    {
10413        if(my $Size = $TypeInfo{$LibVersion}{$BaseId}{"Size"})
10414        {
10415            if($Size!=1)
10416            { # not empty base class
10417                $Shift+=$Size;
10418            }
10419        }
10420    }
10421    return $Shift;
10422}
10423
10424sub getVTable_Size($$)
10425{ # number of v-table elements
10426    my ($ClassName, $LibVersion) = @_;
10427    my $Size = 0;
10428    # three approaches
10429    if(not $Size)
10430    { # real size
10431        if(my %VTable = getVTable_Real($ClassName, $LibVersion)) {
10432            $Size = keys(%VTable);
10433        }
10434    }
10435    if(not $Size)
10436    { # shared library symbol size
10437        if($Size = getSymbolSize($ClassVTable{$ClassName}, $LibVersion)) {
10438            $Size /= $WORD_SIZE{$LibVersion};
10439        }
10440    }
10441    if(not $Size)
10442    { # model size
10443        if(defined $VirtualTable_Model{$LibVersion}{$ClassName}) {
10444            $Size = keys(%{$VirtualTable_Model{$LibVersion}{$ClassName}}) + 2;
10445        }
10446    }
10447    return $Size;
10448}
10449
10450sub isCopyingClass($$)
10451{
10452    my ($TypeId, $LibVersion) = @_;
10453    return $TypeInfo{$LibVersion}{$TypeId}{"Copied"};
10454}
10455
10456sub isLeafClass($$)
10457{
10458    my ($ClassId, $LibVersion) = @_;
10459    return (not keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}));
10460}
10461
10462sub havePubFields($)
10463{ # check structured type for public fields
10464    return isAccessible($_[0], {}, 0, -1);
10465}
10466
10467sub isAccessible($$$$)
10468{ # check interval in structured type for public fields
10469    my ($TypePtr, $Skip, $Start, $End) = @_;
10470    return 0 if(not $TypePtr);
10471    if($End==-1) {
10472        $End = keys(%{$TypePtr->{"Memb"}})-1;
10473    }
10474    foreach my $MemPos (sort {int($a)<=>int($b)} keys(%{$TypePtr->{"Memb"}}))
10475    {
10476        if($Skip and $Skip->{$MemPos})
10477        { # skip removed/added fields
10478            next;
10479        }
10480        if(int($MemPos)>=$Start and int($MemPos)<=$End)
10481        {
10482            if(isPublic($TypePtr, $MemPos)) {
10483                return ($MemPos+1);
10484            }
10485        }
10486    }
10487    return 0;
10488}
10489
10490sub isReserved($)
10491{ # reserved fields == private
10492    my $MName = $_[0];
10493    if($MName=~/reserved|padding|f_spare/i) {
10494        return 1;
10495    }
10496    if($MName=~/\A[_]*(spare|pad|unused|dummy)[_\d]*\Z/i) {
10497        return 1;
10498    }
10499    if($MName=~/(pad\d+)/i) {
10500        return 1;
10501    }
10502    return 0;
10503}
10504
10505sub isPublic($$)
10506{
10507    my ($TypePtr, $FieldPos) = @_;
10508
10509    return 0 if(not $TypePtr);
10510    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos});
10511    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}{"name"});
10512
10513    my $Access = $TypePtr->{"Memb"}{$FieldPos}{"access"};
10514    if($Access eq "private")
10515    { # by access in C++ language
10516        return 0;
10517    }
10518
10519    # by name in C language
10520    # TODO: add other methods to detect private members
10521    my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"};
10522    if($MName=~/priv|abidata|parent_object|impl/i)
10523    { # C-styled private data
10524        return 0;
10525    }
10526    if(lc($MName) eq "abi")
10527    { # ABI information/reserved field
10528        return 0;
10529    }
10530    if(isReserved($MName))
10531    { # reserved fields
10532        return 0;
10533    }
10534
10535    return 1;
10536}
10537
10538sub getVTable_Real($$)
10539{
10540    my ($ClassName, $LibVersion) = @_;
10541    if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName})
10542    {
10543        my %Type = get_Type($ClassId, $LibVersion);
10544        if(defined $Type{"VTable"}) {
10545            return %{$Type{"VTable"}};
10546        }
10547    }
10548    return ();
10549}
10550
10551sub cmpVTables($)
10552{
10553    my $ClassName = $_[0];
10554    my $Res = cmpVTables_Real($ClassName, 1);
10555    if($Res==-1) {
10556        $Res = cmpVTables_Model($ClassName);
10557    }
10558    return $Res;
10559}
10560
10561sub cmpVTables_Model($)
10562{
10563    my $ClassName = $_[0];
10564    foreach my $Symbol (keys(%{$VirtualTable_Model{1}{$ClassName}}))
10565    {
10566        if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol}) {
10567            return 1;
10568        }
10569    }
10570    return 0;
10571}
10572
10573sub cmpVTables_Real($$)
10574{
10575    my ($ClassName, $Strong) = @_;
10576    if(defined $Cache{"cmpVTables_Real"}{$Strong}{$ClassName}) {
10577        return $Cache{"cmpVTables_Real"}{$Strong}{$ClassName};
10578    }
10579    my %VTable_Old = getVTable_Real($ClassName, 1);
10580    my %VTable_New = getVTable_Real($ClassName, 2);
10581    if(not %VTable_Old or not %VTable_New)
10582    { # old ABI dumps
10583        return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = -1);
10584    }
10585    my %Indexes = map {$_=>1} (keys(%VTable_Old), keys(%VTable_New));
10586    foreach my $Offset (sort {int($a)<=>int($b)} keys(%Indexes))
10587    {
10588        if(not defined $VTable_Old{$Offset})
10589        { # v-table v.1 < v-table v.2
10590            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = $Strong);
10591        }
10592        my $Entry1 = $VTable_Old{$Offset};
10593        if(not defined $VTable_New{$Offset})
10594        { # v-table v.1 > v-table v.2
10595            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = ($Strong or $Entry1!~/__cxa_pure_virtual/));
10596        }
10597        my $Entry2 = $VTable_New{$Offset};
10598
10599        $Entry1 = simpleVEntry($Entry1);
10600        $Entry2 = simpleVEntry($Entry2);
10601
10602        if($Entry1=~/ 0x/ or $Entry2=~/ 0x/)
10603        { # NOTE: problem with vtable-dumper
10604            next;
10605        }
10606
10607        if($Entry1 ne $Entry2)
10608        { # register as changed
10609            if($Entry1=~/::([^:]+)\Z/)
10610            {
10611                my $M1 = $1;
10612                if($Entry2=~/::([^:]+)\Z/)
10613                {
10614                    my $M2 = $1;
10615                    if($M1 eq $M2)
10616                    { # overridden
10617                        next;
10618                    }
10619                }
10620            }
10621            if(differentDumps("G"))
10622            {
10623                if($Entry1=~/\A\-(0x|\d+)/ and $Entry2=~/\A\-(0x|\d+)/)
10624                {
10625                    # GCC 4.6.1: -0x00000000000000010
10626                    # GCC 4.7.0: -16
10627                    next;
10628                }
10629            }
10630            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 1);
10631        }
10632    }
10633    return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 0);
10634}
10635
10636sub mergeVTables($)
10637{ # merging v-tables without diagnostics
10638    my $Level = $_[0];
10639    foreach my $ClassName (keys(%{$VirtualTable{1}}))
10640    {
10641        my $ClassId = $TName_Tid{1}{$ClassName};
10642        if(isPrivateABI($ClassId, 1)) {
10643            next;
10644        }
10645
10646        if($VTableChanged_M{$ClassName})
10647        { # already registered
10648            next;
10649        }
10650        if(cmpVTables_Real($ClassName, 0)==1)
10651        {
10652            my @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
10653            foreach my $Symbol (@Affected)
10654            {
10655                %{$CompatProblems{$Level}{$Symbol}{"Virtual_Table_Changed_Unknown"}{$ClassName}}=(
10656                    "Type_Name"=>$ClassName,
10657                    "Target"=>$ClassName);
10658            }
10659        }
10660    }
10661}
10662
10663sub mergeBases($)
10664{
10665    my $Level = $_[0];
10666    foreach my $ClassName (keys(%{$ClassNames{1}}))
10667    { # detect added and removed virtual functions
10668        my $ClassId = $TName_Tid{1}{$ClassName};
10669        next if(not $ClassId);
10670
10671        if(isPrivateABI($ClassId, 1)) {
10672            next;
10673        }
10674
10675        if(defined $VirtualTable{2}{$ClassName})
10676        {
10677            foreach my $Symbol (keys(%{$VirtualTable{2}{$ClassName}}))
10678            {
10679                if($TName_Tid{1}{$ClassName}
10680                and not defined $VirtualTable{1}{$ClassName}{$Symbol})
10681                { # added to v-table
10682                    if(defined $CompleteSignature{1}{$Symbol}
10683                    and $CompleteSignature{1}{$Symbol}{"Virt"})
10684                    { # override some method in v.1
10685                        next;
10686                    }
10687                    $AddedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
10688                }
10689            }
10690        }
10691        if(defined $VirtualTable{1}{$ClassName})
10692        {
10693            foreach my $Symbol (keys(%{$VirtualTable{1}{$ClassName}}))
10694            {
10695                if($TName_Tid{2}{$ClassName}
10696                and not defined $VirtualTable{2}{$ClassName}{$Symbol})
10697                { # removed from v-table
10698                    if(defined $CompleteSignature{2}{$Symbol}
10699                    and $CompleteSignature{2}{$Symbol}{"Virt"})
10700                    { # override some method in v.2
10701                        next;
10702                    }
10703                    $RemovedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
10704                }
10705            }
10706        }
10707        if($Level eq "Binary")
10708        { # Binary-level
10709            my %Class_Type = get_Type($ClassId, 1);
10710            foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$ClassName}}))
10711            { # check replacements, including pure virtual methods
10712                my $AddedPos = $VirtualTable{2}{$ClassName}{$AddedVFunc};
10713                foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$ClassName}}))
10714                {
10715                    my $RemovedPos = $VirtualTable{1}{$ClassName}{$RemovedVFunc};
10716                    if($AddedPos==$RemovedPos)
10717                    {
10718                        $VirtualReplacement{$AddedVFunc} = $RemovedVFunc;
10719                        $VirtualReplacement{$RemovedVFunc} = $AddedVFunc;
10720                        last; # other methods will be reported as "added" or "removed"
10721                    }
10722                }
10723                if(my $RemovedVFunc = $VirtualReplacement{$AddedVFunc})
10724                {
10725                    if(lc($AddedVFunc) eq lc($RemovedVFunc))
10726                    { # skip: DomUi => DomUI parameter (Qt 4.2.3 to 4.3.0)
10727                        next;
10728                    }
10729                    my $ProblemType = "Virtual_Replacement";
10730                    my @Affected = ($RemovedVFunc);
10731                    if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
10732                    { # pure methods
10733                        if(not isUsedClass($ClassId, 1, $Level))
10734                        { # not a parameter of some exported method
10735                            next;
10736                        }
10737                        $ProblemType = "Pure_Virtual_Replacement";
10738
10739                        # affected all methods (both virtual and non-virtual ones)
10740                        @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
10741                        push(@Affected, keys(%{$OverriddenMethods{1}{$RemovedVFunc}}));
10742                    }
10743                    $VTableChanged_M{$ClassName}=1;
10744                    foreach my $AffectedInt (@Affected)
10745                    {
10746                        if($CompleteSignature{1}{$AffectedInt}{"PureVirt"})
10747                        { # affected exported methods only
10748                            next;
10749                        }
10750                        if(not symbolFilter($AffectedInt, 1, "Affected", $Level)) {
10751                            next;
10752                        }
10753                        %{$CompatProblems{$Level}{$AffectedInt}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
10754                            "Type_Name"=>$Class_Type{"Name"},
10755                            "Target"=>get_Signature($AddedVFunc, 2),
10756                            "Old_Value"=>get_Signature($RemovedVFunc, 1));
10757                    }
10758                }
10759            }
10760        }
10761    }
10762    if(not checkDump(1, "2.0")
10763    or not checkDump(2, "2.0"))
10764    { # support for old ABI dumps
10765      # "Base" attribute introduced in ACC 1.22 (ABI dump 2.0 format)
10766        return;
10767    }
10768    foreach my $ClassName (sort keys(%{$ClassNames{1}}))
10769    {
10770        my $ClassId_Old = $TName_Tid{1}{$ClassName};
10771        next if(not $ClassId_Old);
10772
10773        if(isPrivateABI($ClassId_Old, 1)) {
10774            next;
10775        }
10776
10777        if(not isCreatable($ClassId_Old, 1))
10778        { # skip classes without public constructors (including auto-generated)
10779          # example: class has only a private exported or private inline constructor
10780            next;
10781        }
10782        if($ClassName=~/>/)
10783        { # skip affected template instances
10784            next;
10785        }
10786        my %Class_Old = get_Type($ClassId_Old, 1);
10787        my $ClassId_New = $TName_Tid{2}{$ClassName};
10788        if(not $ClassId_New) {
10789            next;
10790        }
10791        my %Class_New = get_Type($ClassId_New, 2);
10792        if($Class_New{"Type"}!~/Class|Struct/)
10793        { # became typedef
10794            if($Level eq "Binary") {
10795                next;
10796            }
10797            if($Level eq "Source")
10798            {
10799                %Class_New = get_PureType($ClassId_New, $TypeInfo{2});
10800                if($Class_New{"Type"}!~/Class|Struct/) {
10801                    next;
10802                }
10803                $ClassId_New = $Class_New{"Tid"};
10804            }
10805        }
10806
10807        if(not $Class_New{"Size"} or not $Class_Old{"Size"})
10808        { # incomplete info in the ABI dump
10809            next;
10810        }
10811
10812
10813        my @Bases_Old = sort {$Class_Old{"Base"}{$a}{"pos"}<=>$Class_Old{"Base"}{$b}{"pos"}} keys(%{$Class_Old{"Base"}});
10814        my @Bases_New = sort {$Class_New{"Base"}{$a}{"pos"}<=>$Class_New{"Base"}{$b}{"pos"}} keys(%{$Class_New{"Base"}});
10815
10816        my %Tr_Old = map {$TypeInfo{1}{$_}{"Name"} => uncover_typedefs($TypeInfo{1}{$_}{"Name"}, 1)} @Bases_Old;
10817        my %Tr_New = map {$TypeInfo{2}{$_}{"Name"} => uncover_typedefs($TypeInfo{2}{$_}{"Name"}, 2)} @Bases_New;
10818
10819        my ($BNum1, $BNum2) = (1, 1);
10820        my %BasePos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @Bases_Old;
10821        my %BasePos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @Bases_New;
10822        my %ShortBase_Old = map {get_ShortClass($_, 1) => 1} @Bases_Old;
10823        my %ShortBase_New = map {get_ShortClass($_, 2) => 1} @Bases_New;
10824        my $Shift_Old = getShift($ClassId_Old, 1);
10825        my $Shift_New = getShift($ClassId_New, 2);
10826        my %BaseId_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @Bases_New;
10827        my ($Added, $Removed) = (0, 0);
10828        my @StableBases_Old = ();
10829        foreach my $BaseId (@Bases_Old)
10830        {
10831            my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
10832            if($BasePos_New{$Tr_Old{$BaseName}}) {
10833                push(@StableBases_Old, $BaseId);
10834            }
10835            elsif(not $ShortBase_New{$Tr_Old{$BaseName}}
10836            and not $ShortBase_New{get_ShortClass($BaseId, 1)})
10837            { # removed base
10838              # excluding namespace::SomeClass to SomeClass renaming
10839                my $ProblemKind = "Removed_Base_Class";
10840                if($Level eq "Binary")
10841                { # Binary-level
10842                    if($Shift_Old ne $Shift_New)
10843                    { # affected fields
10844                        if(havePubFields(\%Class_Old)) {
10845                            $ProblemKind .= "_And_Shift";
10846                        }
10847                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
10848                            $ProblemKind .= "_And_Size";
10849                        }
10850                    }
10851                    if(keys(%{$VirtualTable_Model{1}{$BaseName}})
10852                    and cmpVTables($ClassName)==1)
10853                    { # affected v-table
10854                        $ProblemKind .= "_And_VTable";
10855                        $VTableChanged_M{$ClassName}=1;
10856                    }
10857                }
10858                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
10859                foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
10860                {
10861                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
10862                    {
10863                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
10864                        if($ProblemKind=~/VTable/) {
10865                            $VTableChanged_M{$SubName}=1;
10866                        }
10867                    }
10868                }
10869                foreach my $Interface (@Affected)
10870                {
10871                    if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10872                        next;
10873                    }
10874                    %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
10875                        "Type_Name"=>$ClassName,
10876                        "Target"=>$BaseName,
10877                        "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
10878                        "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
10879                        "Shift"=>abs($Shift_New-$Shift_Old)  );
10880                }
10881                $Removed+=1;
10882            }
10883        }
10884        my @StableBases_New = ();
10885        foreach my $BaseId (@Bases_New)
10886        {
10887            my $BaseName = $TypeInfo{2}{$BaseId}{"Name"};
10888            if($BasePos_Old{$Tr_New{$BaseName}}) {
10889                push(@StableBases_New, $BaseId);
10890            }
10891            elsif(not $ShortBase_Old{$Tr_New{$BaseName}}
10892            and not $ShortBase_Old{get_ShortClass($BaseId, 2)})
10893            { # added base
10894              # excluding namespace::SomeClass to SomeClass renaming
10895                my $ProblemKind = "Added_Base_Class";
10896                if($Level eq "Binary")
10897                { # Binary-level
10898                    if($Shift_Old ne $Shift_New)
10899                    { # affected fields
10900                        if(havePubFields(\%Class_Old)) {
10901                            $ProblemKind .= "_And_Shift";
10902                        }
10903                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
10904                            $ProblemKind .= "_And_Size";
10905                        }
10906                    }
10907                    if(keys(%{$VirtualTable_Model{2}{$BaseName}})
10908                    and cmpVTables($ClassName)==1)
10909                    { # affected v-table
10910                        $ProblemKind .= "_And_VTable";
10911                        $VTableChanged_M{$ClassName}=1;
10912                    }
10913                }
10914                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
10915                foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
10916                {
10917                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
10918                    {
10919                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
10920                        if($ProblemKind=~/VTable/) {
10921                            $VTableChanged_M{$SubName}=1;
10922                        }
10923                    }
10924                }
10925                foreach my $Interface (@Affected)
10926                {
10927                    if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10928                        next;
10929                    }
10930                    %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
10931                        "Type_Name"=>$ClassName,
10932                        "Target"=>$BaseName,
10933                        "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
10934                        "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
10935                        "Shift"=>abs($Shift_New-$Shift_Old)  );
10936                }
10937                $Added+=1;
10938            }
10939        }
10940        if($Level eq "Binary")
10941        { # Binary-level
10942            ($BNum1, $BNum2) = (1, 1);
10943            my %BaseRelPos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @StableBases_Old;
10944            my %BaseRelPos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @StableBases_New;
10945            foreach my $BaseId (@Bases_Old)
10946            {
10947                my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
10948                if(my $NewPos = $BaseRelPos_New{$Tr_Old{$BaseName}})
10949                {
10950                    my $BaseNewId = $BaseId_New{$Tr_Old{$BaseName}};
10951                    my $OldPos = $BaseRelPos_Old{$Tr_Old{$BaseName}};
10952                    if($NewPos!=$OldPos)
10953                    { # changed position of the base class
10954                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10955                        {
10956                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10957                                next;
10958                            }
10959                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Position"}{"this"}}=(
10960                                "Type_Name"=>$ClassName,
10961                                "Target"=>$BaseName,
10962                                "Old_Value"=>$OldPos-1,
10963                                "New_Value"=>$NewPos-1  );
10964                        }
10965                    }
10966                    if($Class_Old{"Base"}{$BaseId}{"virtual"}
10967                    and not $Class_New{"Base"}{$BaseNewId}{"virtual"})
10968                    { # became non-virtual base
10969                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10970                        {
10971                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10972                                next;
10973                            }
10974                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=(
10975                                "Type_Name"=>$ClassName,
10976                                "Target"=>$BaseName  );
10977                        }
10978                    }
10979                    elsif(not $Class_Old{"Base"}{$BaseId}{"virtual"}
10980                    and $Class_New{"Base"}{$BaseNewId}{"virtual"})
10981                    { # became virtual base
10982                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10983                        {
10984                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10985                                next;
10986                            }
10987                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Virtually_Inherited"}{"this->".$BaseName}}=(
10988                                "Type_Name"=>$ClassName,
10989                                "Target"=>$BaseName  );
10990                        }
10991                    }
10992                }
10993            }
10994            # detect size changes in base classes
10995            if($Shift_Old!=$Shift_New)
10996            { # size of allocable class
10997                foreach my $BaseId (@StableBases_Old)
10998                { # search for changed base
10999                    my %BaseType = get_Type($BaseId, 1);
11000                    my $Size_Old = $TypeInfo{1}{$BaseId}{"Size"};
11001                    my $Size_New = $TypeInfo{2}{$BaseId_New{$Tr_Old{$BaseType{"Name"}}}}{"Size"};
11002                    if($Size_Old ne $Size_New
11003                    and $Size_Old and $Size_New)
11004                    {
11005                        my $ProblemType = undef;
11006                        if(isCopyingClass($BaseId, 1)) {
11007                            $ProblemType = "Size_Of_Copying_Class";
11008                        }
11009                        elsif($AllocableClass{1}{$BaseType{"Name"}})
11010                        {
11011                            if($Size_New>$Size_Old)
11012                            { # increased size
11013                                $ProblemType = "Size_Of_Allocable_Class_Increased";
11014                            }
11015                            else
11016                            { # decreased size
11017                                $ProblemType = "Size_Of_Allocable_Class_Decreased";
11018                                if(not havePubFields(\%Class_Old))
11019                                { # affected class has no public members
11020                                    next;
11021                                }
11022                            }
11023                        }
11024                        next if(not $ProblemType);
11025                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
11026                        { # base class size changes affecting current class
11027                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
11028                                next;
11029                            }
11030                            %{$CompatProblems{$Level}{$Interface}{$ProblemType}{"this->".$BaseType{"Name"}}}=(
11031                                "Type_Name"=>$BaseType{"Name"},
11032                                "Target"=>$BaseType{"Name"},
11033                                "Old_Size"=>$Size_Old*$BYTE_SIZE,
11034                                "New_Size"=>$Size_New*$BYTE_SIZE  );
11035                        }
11036                    }
11037                }
11038            }
11039            if(defined $VirtualTable_Model{1}{$ClassName}
11040            and cmpVTables_Real($ClassName, 1)==1
11041            and my @VFunctions = keys(%{$VirtualTable_Model{1}{$ClassName}}))
11042            { # compare virtual tables size in base classes
11043                my $VShift_Old = getVShift($ClassId_Old, 1);
11044                my $VShift_New = getVShift($ClassId_New, 2);
11045                if($VShift_Old ne $VShift_New)
11046                { # changes in the base class or changes in the list of base classes
11047                    my @AllBases_Old = get_base_classes($ClassId_Old, 1, 1);
11048                    my @AllBases_New = get_base_classes($ClassId_New, 2, 1);
11049                    ($BNum1, $BNum2) = (1, 1);
11050                    my %StableBase = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @AllBases_New;
11051                    foreach my $BaseId (@AllBases_Old)
11052                    {
11053                        my %BaseType = get_Type($BaseId, 1);
11054                        if(not $StableBase{$Tr_Old{$BaseType{"Name"}}})
11055                        { # lost base
11056                            next;
11057                        }
11058                        my $VSize_Old = getVTable_Size($BaseType{"Name"}, 1);
11059                        my $VSize_New = getVTable_Size($BaseType{"Name"}, 2);
11060                        if($VSize_Old!=$VSize_New)
11061                        {
11062                            foreach my $Symbol (@VFunctions)
11063                            { # TODO: affected non-virtual methods?
11064                                if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol})
11065                                { # Removed_Virtual_Method, will be registered in mergeVirtualTables()
11066                                    next;
11067                                }
11068                                if($VirtualTable_Model{2}{$ClassName}{$Symbol}-$VirtualTable_Model{1}{$ClassName}{$Symbol}==0)
11069                                { # skip interfaces that have not changed the absolute virtual position
11070                                    next;
11071                                }
11072                                if(not symbolFilter($Symbol, 1, "Affected", $Level)) {
11073                                    next;
11074                                }
11075                                $VTableChanged_M{$BaseType{"Name"}} = 1;
11076                                $VTableChanged_M{$ClassName} = 1;
11077                                foreach my $VirtFunc (keys(%{$AddedInt_Virt{$Level}{$BaseType{"Name"}}}))
11078                                { # the reason of the layout change: added virtual functions
11079                                    next if($VirtualReplacement{$VirtFunc});
11080                                    my $ProblemType = "Added_Virtual_Method";
11081                                    if($CompleteSignature{2}{$VirtFunc}{"PureVirt"}) {
11082                                        $ProblemType = "Added_Pure_Virtual_Method";
11083                                    }
11084                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 2)}}=(
11085                                        "Type_Name"=>$BaseType{"Name"},
11086                                        "Target"=>get_Signature($VirtFunc, 2)  );
11087                                }
11088                                foreach my $VirtFunc (keys(%{$RemovedInt_Virt{$Level}{$BaseType{"Name"}}}))
11089                                { # the reason of the layout change: removed virtual functions
11090                                    next if($VirtualReplacement{$VirtFunc});
11091                                    my $ProblemType = "Removed_Virtual_Method";
11092                                    if($CompleteSignature{1}{$VirtFunc}{"PureVirt"}) {
11093                                        $ProblemType = "Removed_Pure_Virtual_Method";
11094                                    }
11095                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 1)}}=(
11096                                        "Type_Name"=>$BaseType{"Name"},
11097                                        "Target"=>get_Signature($VirtFunc, 1)  );
11098                                }
11099                            }
11100                        }
11101                    }
11102                }
11103            }
11104        }
11105    }
11106}
11107
11108sub isCreatable($$)
11109{
11110    my ($ClassId, $LibVersion) = @_;
11111    if($AllocableClass{$LibVersion}{$TypeInfo{$LibVersion}{$ClassId}{"Name"}}
11112    or isCopyingClass($ClassId, $LibVersion)) {
11113        return 1;
11114    }
11115    if(keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
11116    { # Fix for incomplete data: if this class has
11117      # a base class then it should also has a constructor
11118        return 1;
11119    }
11120    if($ReturnedClass{$LibVersion}{$ClassId})
11121    { # returned by some method of this class
11122      # or any other class
11123        return 1;
11124    }
11125    return 0;
11126}
11127
11128sub isUsedClass($$$)
11129{
11130    my ($ClassId, $LibVersion, $Level) = @_;
11131    if(keys(%{$ParamClass{$LibVersion}{$ClassId}}))
11132    { # parameter of some exported method
11133        return 1;
11134    }
11135    my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
11136    if(keys(%{$ClassMethods{$Level}{$LibVersion}{$CName}}))
11137    { # method from target class
11138        return 1;
11139    }
11140    return 0;
11141}
11142
11143sub mergeVirtualTables($$)
11144{ # check for changes in the virtual table
11145    my ($Interface, $Level) = @_;
11146    # affected methods:
11147    #  - virtual
11148    #  - pure-virtual
11149    #  - non-virtual
11150    if($CompleteSignature{1}{$Interface}{"Data"})
11151    { # global data is not affected
11152        return;
11153    }
11154    my $Class_Id = $CompleteSignature{1}{$Interface}{"Class"};
11155    if(not $Class_Id) {
11156        return;
11157    }
11158    my $CName = $TypeInfo{1}{$Class_Id}{"Name"};
11159    if(cmpVTables_Real($CName, 1)==0)
11160    { # no changes
11161        return;
11162    }
11163    $CheckedTypes{$Level}{$CName} = 1;
11164    if($Level eq "Binary")
11165    { # Binary-level
11166        if($CompleteSignature{1}{$Interface}{"PureVirt"}
11167        and not isUsedClass($Class_Id, 1, $Level))
11168        { # pure virtuals should not be affected
11169          # if there are no exported methods using this class
11170            return;
11171        }
11172    }
11173    foreach my $Func (keys(%{$VirtualTable{1}{$CName}}))
11174    {
11175        if(defined $VirtualTable{2}{$CName}{$Func}
11176        and defined $CompleteSignature{2}{$Func})
11177        {
11178            if(not $CompleteSignature{1}{$Func}{"PureVirt"}
11179            and $CompleteSignature{2}{$Func}{"PureVirt"})
11180            { # became pure virtual
11181                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Pure"}{$tr_name{$Func}}}=(
11182                    "Type_Name"=>$CName,
11183                    "Target"=>get_Signature_M($Func, 1)  );
11184                $VTableChanged_M{$CName} = 1;
11185            }
11186            elsif($CompleteSignature{1}{$Func}{"PureVirt"}
11187            and not $CompleteSignature{2}{$Func}{"PureVirt"})
11188            { # became non-pure virtual
11189                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Non_Pure"}{$tr_name{$Func}}}=(
11190                    "Type_Name"=>$CName,
11191                    "Target"=>get_Signature_M($Func, 1)  );
11192                $VTableChanged_M{$CName} = 1;
11193            }
11194        }
11195    }
11196    if($Level eq "Binary")
11197    { # Binary-level
11198        # check virtual table structure
11199        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
11200        {
11201            next if($Interface eq $AddedVFunc);
11202            next if($VirtualReplacement{$AddedVFunc});
11203            my $VPos_Added = $VirtualTable{2}{$CName}{$AddedVFunc};
11204            if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"})
11205            { # pure virtual methods affect all others (virtual and non-virtual)
11206                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11207                    "Type_Name"=>$CName,
11208                    "Target"=>get_Signature($AddedVFunc, 2)  );
11209                $VTableChanged_M{$CName} = 1;
11210            }
11211            elsif(not defined $VirtualTable{1}{$CName}
11212            or $VPos_Added>keys(%{$VirtualTable{1}{$CName}}))
11213            { # added virtual function at the end of v-table
11214                if(not keys(%{$VirtualTable_Model{1}{$CName}}))
11215                { # became polymorphous class, added v-table pointer
11216                    %{$CompatProblems{$Level}{$Interface}{"Added_First_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11217                        "Type_Name"=>$CName,
11218                        "Target"=>get_Signature($AddedVFunc, 2)  );
11219                    $VTableChanged_M{$CName} = 1;
11220                }
11221                else
11222                {
11223                    my $VSize_Old = getVTable_Size($CName, 1);
11224                    my $VSize_New = getVTable_Size($CName, 2);
11225                    next if($VSize_Old==$VSize_New); # exception: register as removed and added virtual method
11226                    if(isCopyingClass($Class_Id, 1))
11227                    { # class has no constructors and v-table will be copied by applications, this may affect all methods
11228                        my $ProblemType = "Added_Virtual_Method";
11229                        if(isLeafClass($Class_Id, 1)) {
11230                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Copying_Class";
11231                        }
11232                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
11233                            "Type_Name"=>$CName,
11234                            "Target"=>get_Signature($AddedVFunc, 2)  );
11235                        $VTableChanged_M{$CName} = 1;
11236                    }
11237                    else
11238                    {
11239                        my $ProblemType = "Added_Virtual_Method";
11240                        if(isLeafClass($Class_Id, 1)) {
11241                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class";
11242                        }
11243                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
11244                            "Type_Name"=>$CName,
11245                            "Target"=>get_Signature($AddedVFunc, 2)  );
11246                        $VTableChanged_M{$CName} = 1;
11247                    }
11248                }
11249            }
11250            elsif($CompleteSignature{1}{$Interface}{"Virt"}
11251            or $CompleteSignature{1}{$Interface}{"PureVirt"})
11252            {
11253                if(defined $VirtualTable{1}{$CName}
11254                and defined $VirtualTable{2}{$CName})
11255                {
11256                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
11257                    my $VPos_New = $VirtualTable{2}{$CName}{$Interface};
11258
11259                    if($VPos_Added<=$VPos_Old and $VPos_Old!=$VPos_New)
11260                    {
11261                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
11262                        foreach my $ASymbol (@Affected)
11263                        {
11264                            if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"})
11265                            {
11266                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
11267                                    next;
11268                                }
11269                            }
11270                            $CheckedSymbols{$Level}{$ASymbol} = 1;
11271                            %{$CompatProblems{$Level}{$ASymbol}{"Added_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11272                                "Type_Name"=>$CName,
11273                                "Target"=>get_Signature($AddedVFunc, 2)  );
11274                            $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
11275                        }
11276                    }
11277                }
11278            }
11279            else {
11280                # safe
11281            }
11282        }
11283        foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}}))
11284        {
11285            next if($VirtualReplacement{$RemovedVFunc});
11286            if($RemovedVFunc eq $Interface
11287            and $CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
11288            { # This case is for removed virtual methods
11289              # implemented in both versions of a library
11290                next;
11291            }
11292            if(not keys(%{$VirtualTable_Model{2}{$CName}}))
11293            { # became non-polymorphous class, removed v-table pointer
11294                %{$CompatProblems{$Level}{$Interface}{"Removed_Last_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=(
11295                    "Type_Name"=>$CName,
11296                    "Target"=>get_Signature($RemovedVFunc, 1)  );
11297                $VTableChanged_M{$CName} = 1;
11298            }
11299            elsif($CompleteSignature{1}{$Interface}{"Virt"}
11300            or $CompleteSignature{1}{$Interface}{"PureVirt"})
11301            {
11302                if(defined $VirtualTable{1}{$CName} and defined $VirtualTable{2}{$CName})
11303                {
11304                    if(not defined $VirtualTable{1}{$CName}{$Interface}) {
11305                        next;
11306                    }
11307                    my $VPos_New = -1;
11308                    if(defined $VirtualTable{2}{$CName}{$Interface})
11309                    {
11310                        $VPos_New = $VirtualTable{2}{$CName}{$Interface};
11311                    }
11312                    else
11313                    {
11314                        if($Interface ne $RemovedVFunc) {
11315                            next;
11316                        }
11317                    }
11318                    my $VPos_Removed = $VirtualTable{1}{$CName}{$RemovedVFunc};
11319                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
11320                    if($VPos_Removed<=$VPos_Old and $VPos_Old!=$VPos_New)
11321                    {
11322                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
11323                        foreach my $ASymbol (@Affected)
11324                        {
11325                            if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"})
11326                            {
11327                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
11328                                    next;
11329                                }
11330                            }
11331                            my $ProblemType = "Removed_Virtual_Method";
11332                            if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) {
11333                                $ProblemType = "Removed_Pure_Virtual_Method";
11334                            }
11335                            $CheckedSymbols{$Level}{$ASymbol} = 1;
11336                            %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$RemovedVFunc}}}=(
11337                                "Type_Name"=>$CName,
11338                                "Target"=>get_Signature($RemovedVFunc, 1)  );
11339                            $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
11340                        }
11341                    }
11342                }
11343            }
11344        }
11345    }
11346    else
11347    { # Source-level
11348        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
11349        {
11350            next if($Interface eq $AddedVFunc);
11351            if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"})
11352            {
11353                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11354                    "Type_Name"=>$CName,
11355                    "Target"=>get_Signature($AddedVFunc, 2)  );
11356            }
11357        }
11358        foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}}))
11359        {
11360            if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
11361            {
11362                %{$CompatProblems{$Level}{$Interface}{"Removed_Pure_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=(
11363                    "Type_Name"=>$CName,
11364                    "Target"=>get_Signature($RemovedVFunc, 1)  );
11365            }
11366        }
11367    }
11368}
11369
11370sub find_MemberPair_Pos_byName($$)
11371{
11372    my ($Member_Name, $Pair_Type) = @_;
11373    $Member_Name=~s/\A[_]+|[_]+\Z//g;
11374    foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
11375    {
11376        if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos})
11377        {
11378            my $Name = $Pair_Type->{"Memb"}{$MemberPair_Pos}{"name"};
11379            $Name=~s/\A[_]+|[_]+\Z//g;
11380            if($Name eq $Member_Name) {
11381                return $MemberPair_Pos;
11382            }
11383        }
11384    }
11385    return "lost";
11386}
11387
11388sub find_MemberPair_Pos_byVal($$)
11389{
11390    my ($Member_Value, $Pair_Type) = @_;
11391    foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
11392    {
11393        if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos}
11394        and $Pair_Type->{"Memb"}{$MemberPair_Pos}{"value"} eq $Member_Value) {
11395            return $MemberPair_Pos;
11396        }
11397    }
11398    return "lost";
11399}
11400
11401sub isRecurType($$$)
11402{
11403    foreach (@{$_[2]})
11404    {
11405        if( $_->{"T1"} eq $_[0]
11406        and $_->{"T2"} eq $_[1] )
11407        {
11408            return 1;
11409        }
11410    }
11411    return 0;
11412}
11413
11414sub pushType($$$)
11415{
11416    my %IDs = (
11417        "T1" => $_[0],
11418        "T2" => $_[1]
11419    );
11420    push(@{$_[2]}, \%IDs);
11421}
11422
11423sub isRenamed($$$$$)
11424{
11425    my ($MemPos, $Type1, $LVersion1, $Type2, $LVersion2) = @_;
11426    my $Member_Name = $Type1->{"Memb"}{$MemPos}{"name"};
11427    my $MemberType_Id = $Type1->{"Memb"}{$MemPos}{"type"};
11428    my %MemberType_Pure = get_PureType($MemberType_Id, $TypeInfo{$LVersion1});
11429    if(not defined $Type2->{"Memb"}{$MemPos}) {
11430        return "";
11431    }
11432    my $PairType_Id = $Type2->{"Memb"}{$MemPos}{"type"};
11433    my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{$LVersion2});
11434
11435    my $Pair_Name = $Type2->{"Memb"}{$MemPos}{"name"};
11436    my $MemberPair_Pos_Rev = ($Member_Name eq $Pair_Name)?$MemPos:find_MemberPair_Pos_byName($Pair_Name, $Type1);
11437    if($MemberPair_Pos_Rev eq "lost")
11438    {
11439        if($MemberType_Pure{"Name"} eq $PairType_Pure{"Name"})
11440        { # base type match
11441            return $Pair_Name;
11442        }
11443        if($TypeInfo{$LVersion1}{$MemberType_Id}{"Name"} eq $TypeInfo{$LVersion2}{$PairType_Id}{"Name"})
11444        { # exact type match
11445            return $Pair_Name;
11446        }
11447        if($MemberType_Pure{"Size"} eq $PairType_Pure{"Size"})
11448        { # size match
11449            return $Pair_Name;
11450        }
11451        if(isReserved($Pair_Name))
11452        { # reserved fields
11453            return $Pair_Name;
11454        }
11455    }
11456    return "";
11457}
11458
11459sub isLastElem($$)
11460{
11461    my ($Pos, $TypeRef) = @_;
11462    my $Name = $TypeRef->{"Memb"}{$Pos}{"name"};
11463    if($Name=~/last|count|max|total|num/i)
11464    { # GST_LEVEL_COUNT, GST_RTSP_ELAST
11465        return 1;
11466    }
11467    elsif($Name=~/END|NLIMITS\Z/)
11468    { # __RLIMIT_NLIMITS
11469        return 1;
11470    }
11471    elsif($Name=~/\AN[A-Z](.+)[a-z]+s\Z/
11472    and $Pos+1==keys(%{$TypeRef->{"Memb"}}))
11473    { # NImageFormats, NColorRoles
11474        return 1;
11475    }
11476    return 0;
11477}
11478
11479sub nonComparable($$)
11480{
11481    my ($T1, $T2) = @_;
11482
11483    my $N1 = $T1->{"Name"};
11484    my $N2 = $T2->{"Name"};
11485
11486    $N1=~s/\A(struct|union|enum) //;
11487    $N2=~s/\A(struct|union|enum) //;
11488
11489    if($N1 ne $N2
11490    and not isAnon($N1)
11491    and not isAnon($N2))
11492    { # different names
11493        if($T1->{"Type"} ne "Pointer"
11494        or $T2->{"Type"} ne "Pointer")
11495        { # compare base types
11496            return 1;
11497        }
11498        if($N1!~/\Avoid\s*\*/
11499        and $N2=~/\Avoid\s*\*/)
11500        {
11501            return 1;
11502        }
11503    }
11504    elsif($T1->{"Type"} ne $T2->{"Type"})
11505    { # different types
11506        if($T1->{"Type"} eq "Class"
11507        and $T2->{"Type"} eq "Struct")
11508        { # "class" to "struct"
11509            return 0;
11510        }
11511        elsif($T2->{"Type"} eq "Class"
11512        and $T1->{"Type"} eq "Struct")
11513        { # "struct" to "class"
11514            return 0;
11515        }
11516        else
11517        { # "class" to "enum"
11518          # "union" to "class"
11519          #  ...
11520            return 1;
11521        }
11522    }
11523    return 0;
11524}
11525
11526sub isOpaque($)
11527{
11528    my $T = $_[0];
11529    if(not defined $T->{"Memb"})
11530    {
11531        return 1;
11532    }
11533    return 0;
11534}
11535
11536sub removeVPtr($)
11537{ # support for old ABI dumps
11538    my $TPtr = $_[0];
11539    my @Pos = sort {int($a)<=>int($b)} keys(%{$TPtr->{"Memb"}});
11540    if($#Pos>=1)
11541    {
11542        foreach my $Pos (0 .. $#Pos-1)
11543        {
11544            %{$TPtr->{"Memb"}{$Pos}} = %{$TPtr->{"Memb"}{$Pos+1}};
11545        }
11546        delete($TPtr->{"Memb"}{$#Pos});
11547    }
11548}
11549
11550sub isPrivateABI($$)
11551{
11552    my ($TypeId, $LibVersion) = @_;
11553
11554    if($CheckPrivateABI) {
11555        return 0;
11556    }
11557
11558    if(defined $TypeInfo{$LibVersion}{$TypeId}{"PrivateABI"}) {
11559        return 1;
11560    }
11561
11562    return 0;
11563}
11564
11565sub mergeTypes($$$)
11566{
11567    my ($Type1_Id, $Type2_Id, $Level) = @_;
11568    return {} if(not $Type1_Id or not $Type2_Id);
11569
11570    if(defined $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id})
11571    { # already merged
11572        return $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id};
11573    }
11574
11575    my %Type1 = get_Type($Type1_Id, 1);
11576    my %Type2 = get_Type($Type2_Id, 2);
11577    if(not $Type1{"Name"} or not $Type2{"Name"}) {
11578        return {};
11579    }
11580
11581    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
11582    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
11583
11584    if(defined $UsedDump{1}{"DWARF"})
11585    {
11586        if($Type1_Pure{"Name"} eq "__unknown__"
11587        or $Type2_Pure{"Name"} eq "__unknown__")
11588        { # Error ABI dump
11589            return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {});
11590        }
11591    }
11592
11593    if(isPrivateABI($Type1_Id, 1)) {
11594        return {};
11595    }
11596
11597    $CheckedTypes{$Level}{$Type1{"Name"}} = 1;
11598    $CheckedTypes{$Level}{$Type1_Pure{"Name"}} = 1;
11599
11600    my %SubProblems = ();
11601
11602    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
11603    {
11604        if($Type1_Pure{"Type"}=~/Struct|Union/
11605        and $Type2_Pure{"Type"}=~/Struct|Union/)
11606        {
11607            if(isOpaque(\%Type2_Pure) and not isOpaque(\%Type1_Pure))
11608            {
11609                if(not defined $UsedDump{1}{"DWARF"}
11610                and not defined $UsedDump{2}{"DWARF"})
11611                {
11612                    %{$SubProblems{"Type_Became_Opaque"}{$Type1_Pure{"Name"}}}=(
11613                        "Target"=>$Type1_Pure{"Name"},
11614                        "Type_Name"=>$Type1_Pure{"Name"}  );
11615                }
11616
11617                return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
11618            }
11619        }
11620    }
11621
11622    if(not $Type1_Pure{"Size"}
11623    or not $Type2_Pure{"Size"})
11624    { # including a case when "class Class { ... };" changed to "class Class;"
11625        if(not defined $Type1_Pure{"Memb"} or not defined $Type2_Pure{"Memb"}
11626        or index($Type1_Pure{"Name"}, "<")==-1 or index($Type2_Pure{"Name"}, "<")==-1)
11627        { # NOTE: template instances have no size
11628            return {};
11629        }
11630    }
11631    if(isRecurType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes))
11632    { # skip recursive declarations
11633        return {};
11634    }
11635    return {} if(not $Type1_Pure{"Name"} or not $Type2_Pure{"Name"});
11636    return {} if($SkipTypes{1}{$Type1_Pure{"Name"}});
11637    return {} if($SkipTypes{1}{$Type1{"Name"}});
11638
11639    if(not isTargetType($Type1_Pure{"Tid"}, 1)) {
11640        return {};
11641    }
11642
11643    if($Type1_Pure{"Type"}=~/Class|Struct/ and $Type2_Pure{"Type"}=~/Class|Struct/)
11644    { # support for old ABI dumps
11645      # _vptr field added in 3.0
11646        if(not checkDump(1, "3.0") and checkDump(2, "3.0"))
11647        {
11648            if(defined $Type2_Pure{"Memb"}
11649            and $Type2_Pure{"Memb"}{0}{"name"} eq "_vptr")
11650            {
11651                if(keys(%{$Type2_Pure{"Memb"}})==1) {
11652                    delete($Type2_Pure{"Memb"}{0});
11653                }
11654                else {
11655                    removeVPtr(\%Type2_Pure);
11656                }
11657            }
11658        }
11659        if(checkDump(1, "3.0") and not checkDump(2, "3.0"))
11660        {
11661            if(defined $Type1_Pure{"Memb"}
11662            and $Type1_Pure{"Memb"}{0}{"name"} eq "_vptr")
11663            {
11664                if(keys(%{$Type1_Pure{"Memb"}})==1) {
11665                    delete($Type1_Pure{"Memb"}{0});
11666                }
11667                else {
11668                    removeVPtr(\%Type1_Pure);
11669                }
11670            }
11671        }
11672    }
11673
11674    my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
11675    my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
11676
11677    if(%Typedef_1 and %Typedef_2
11678    and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef"
11679    and $Typedef_1{"Name"} eq $Typedef_2{"Name"})
11680    {
11681        my %Base_1 = get_OneStep_BaseType($Typedef_1{"Tid"}, $TypeInfo{1});
11682        my %Base_2 = get_OneStep_BaseType($Typedef_2{"Tid"}, $TypeInfo{2});
11683        if($Base_1{"Name"} ne $Base_2{"Name"})
11684        {
11685            if(differentDumps("G")
11686            or differentDumps("V")
11687            or $SkipTypedefUncover)
11688            { # different GCC versions or different dumps
11689                $Base_1{"Name"} = uncover_typedefs($Base_1{"Name"}, 1);
11690                $Base_2{"Name"} = uncover_typedefs($Base_2{"Name"}, 2);
11691                # std::__va_list and __va_list
11692                $Base_1{"Name"}=~s/\A(\w+::)+//;
11693                $Base_2{"Name"}=~s/\A(\w+::)+//;
11694                $Base_1{"Name"} = formatName($Base_1{"Name"}, "T");
11695                $Base_2{"Name"} = formatName($Base_2{"Name"}, "T");
11696            }
11697        }
11698        if($Base_1{"Name"}!~/anon\-/ and $Base_2{"Name"}!~/anon\-/
11699        and $Base_1{"Name"} ne $Base_2{"Name"})
11700        {
11701            if($Level eq "Binary"
11702            and $Type1{"Size"} and $Type2{"Size"}
11703            and $Type1{"Size"} ne $Type2{"Size"})
11704            {
11705                %{$SubProblems{"DataType_Size"}{$Typedef_1{"Name"}}}=(
11706                    "Target"=>$Typedef_1{"Name"},
11707                    "Type_Name"=>$Typedef_1{"Name"},
11708                    "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11709                    "New_Size"=>$Type2{"Size"}*$BYTE_SIZE  );
11710            }
11711            my %Base1_Pure = get_PureType($Base_1{"Tid"}, $TypeInfo{1});
11712            my %Base2_Pure = get_PureType($Base_2{"Tid"}, $TypeInfo{2});
11713
11714            if(defined $UsedDump{1}{"DWARF"})
11715            {
11716                if($Base1_Pure{"Name"}=~/\b__unknown__\b/
11717                or $Base2_Pure{"Name"}=~/\b__unknown__\b/)
11718                { # Error ABI dump
11719                    return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = {});
11720                }
11721            }
11722
11723            if(tNameLock($Base_1{"Tid"}, $Base_2{"Tid"}))
11724            {
11725                if(diffTypes($Base1_Pure{"Tid"}, $Base2_Pure{"Tid"}, $Level))
11726                {
11727                    %{$SubProblems{"Typedef_BaseType_Format"}{$Typedef_1{"Name"}}}=(
11728                        "Target"=>$Typedef_1{"Name"},
11729                        "Type_Name"=>$Typedef_1{"Name"},
11730                        "Old_Value"=>$Base_1{"Name"},
11731                        "New_Value"=>$Base_2{"Name"}  );
11732                }
11733                else
11734                {
11735                    %{$SubProblems{"Typedef_BaseType"}{$Typedef_1{"Name"}}}=(
11736                        "Target"=>$Typedef_1{"Name"},
11737                        "Type_Name"=>$Typedef_1{"Name"},
11738                        "Old_Value"=>$Base_1{"Name"},
11739                        "New_Value"=>$Base_2{"Name"}  );
11740                }
11741            }
11742        }
11743    }
11744    if(nonComparable(\%Type1_Pure, \%Type2_Pure))
11745    { # different types (reported in detectTypeChange(...))
11746        my $TT1 = $Type1_Pure{"Type"};
11747        my $TT2 = $Type2_Pure{"Type"};
11748
11749        if($TT1 ne $TT2
11750        and $TT1!~/Intrinsic|Pointer|Ref|Typedef/)
11751        { # different type of the type
11752            my $Short1 = $Type1_Pure{"Name"};
11753            my $Short2 = $Type2_Pure{"Name"};
11754
11755            $Short1=~s/\A\Q$TT1\E //ig;
11756            $Short2=~s/\A\Q$TT2\E //ig;
11757
11758            if($Short1 eq $Short2)
11759            {
11760                %{$SubProblems{"DataType_Type"}{$Type1_Pure{"Name"}}}=(
11761                    "Target"=>$Type1_Pure{"Name"},
11762                    "Type_Name"=>$Type1_Pure{"Name"},
11763                    "Old_Value"=>lc($Type1_Pure{"Type"}),
11764                    "New_Value"=>lc($Type2_Pure{"Type"})  );
11765            }
11766        }
11767        return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
11768    }
11769
11770    pushType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes);
11771
11772    if(($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}
11773    or (isAnon($Type1_Pure{"Name"}) and isAnon($Type2_Pure{"Name"})))
11774    and $Type1_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11775    { # checking size
11776        if($Level eq "Binary"
11777        and $Type1_Pure{"Size"} and $Type2_Pure{"Size"}
11778        and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11779        {
11780            my $ProblemKind = "DataType_Size";
11781            if($Type1_Pure{"Type"} eq "Class"
11782            and keys(%{$ClassMethods{$Level}{1}{$Type1_Pure{"Name"}}}))
11783            {
11784                if(isCopyingClass($Type1_Pure{"Tid"}, 1)) {
11785                    $ProblemKind = "Size_Of_Copying_Class";
11786                }
11787                elsif($AllocableClass{1}{$Type1_Pure{"Name"}})
11788                {
11789                    if(int($Type2_Pure{"Size"})>int($Type1_Pure{"Size"})) {
11790                        $ProblemKind = "Size_Of_Allocable_Class_Increased";
11791                    }
11792                    else
11793                    {
11794                        # descreased size of allocable class
11795                        # it has no special effects
11796                    }
11797                }
11798            }
11799            %{$SubProblems{$ProblemKind}{$Type1_Pure{"Name"}}}=(
11800                "Target"=>$Type1_Pure{"Name"},
11801                "Type_Name"=>$Type1_Pure{"Name"},
11802                "Old_Size"=>$Type1_Pure{"Size"}*$BYTE_SIZE,
11803                "New_Size"=>$Type2_Pure{"Size"}*$BYTE_SIZE);
11804        }
11805    }
11806    if(defined $Type1_Pure{"BaseType"}
11807    and defined $Type2_Pure{"BaseType"})
11808    { # checking base types
11809        my $Sub_SubProblems = mergeTypes($Type1_Pure{"BaseType"}, $Type2_Pure{"BaseType"}, $Level);
11810        foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems}))
11811        {
11812            foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) {
11813                $SubProblems{$Sub_SubProblemType}{$Sub_SubLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
11814            }
11815        }
11816    }
11817    my (%AddedField, %RemovedField, %RenamedField, %RenamedField_Rev, %RelatedField, %RelatedField_Rev) = ();
11818    my %NameToPosA = map {$Type1_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type1_Pure{"Memb"}});
11819    my %NameToPosB = map {$Type2_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type2_Pure{"Memb"}});
11820    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11821    { # detect removed and renamed fields
11822        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11823        next if(not $Member_Name);
11824        my $MemberPair_Pos = (defined $Type2_Pure{"Memb"}{$Member_Pos} and $Type2_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:find_MemberPair_Pos_byName($Member_Name, \%Type2_Pure);
11825        if($MemberPair_Pos eq "lost")
11826        {
11827            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11828            {
11829                if(isUnnamed($Member_Name))
11830                { # support for old-version dumps
11831                  # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
11832                    if(not checkDump(2, "2.1")) {
11833                        next;
11834                    }
11835                }
11836                if(my $RenamedTo = isRenamed($Member_Pos, \%Type1_Pure, 1, \%Type2_Pure, 2))
11837                { # renamed
11838                    $RenamedField{$Member_Pos} = $RenamedTo;
11839                    $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
11840                }
11841                else
11842                { # removed
11843                    $RemovedField{$Member_Pos} = 1;
11844                }
11845            }
11846            elsif($Type1_Pure{"Type"} eq "Enum")
11847            {
11848                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
11849                next if($Member_Value1 eq "");
11850                $MemberPair_Pos = find_MemberPair_Pos_byVal($Member_Value1, \%Type2_Pure);
11851                if($MemberPair_Pos ne "lost")
11852                { # renamed
11853                    my $RenamedTo = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"name"};
11854                    my $MemberPair_Pos_Rev = find_MemberPair_Pos_byName($RenamedTo, \%Type1_Pure);
11855                    if($MemberPair_Pos_Rev eq "lost")
11856                    {
11857                        $RenamedField{$Member_Pos} = $RenamedTo;
11858                        $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
11859                    }
11860                    else {
11861                        $RemovedField{$Member_Pos} = 1;
11862                    }
11863                }
11864                else
11865                { # removed
11866                    $RemovedField{$Member_Pos} = 1;
11867                }
11868            }
11869        }
11870        else
11871        { # related
11872            $RelatedField{$Member_Pos} = $MemberPair_Pos;
11873            $RelatedField_Rev{$MemberPair_Pos} = $Member_Pos;
11874        }
11875    }
11876    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
11877    { # detect added fields
11878        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
11879        next if(not $Member_Name);
11880        my $MemberPair_Pos = (defined $Type1_Pure{"Memb"}{$Member_Pos} and $Type1_Pure{"Memb"}{$Member_Pos}{"name"} eq $Member_Name)?$Member_Pos:find_MemberPair_Pos_byName($Member_Name, \%Type1_Pure);
11881        if($MemberPair_Pos eq "lost")
11882        {
11883            if(isUnnamed($Member_Name))
11884            { # support for old-version dumps
11885            # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
11886                if(not checkDump(1, "2.1")) {
11887                    next;
11888                }
11889            }
11890            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union|Enum)\Z/)
11891            {
11892                if(not $RenamedField_Rev{$Member_Pos})
11893                { # added
11894                    $AddedField{$Member_Pos}=1;
11895                }
11896            }
11897        }
11898    }
11899    if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
11900    { # detect moved fields
11901        my (%RelPos, %RelPosName, %AbsPos) = ();
11902        my $Pos = 0;
11903        foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11904        { # relative positions in 1st version
11905            my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11906            next if(not $Member_Name);
11907            if(not $RemovedField{$Member_Pos})
11908            { # old type without removed fields
11909                $RelPos{1}{$Member_Name} = $Pos;
11910                $RelPosName{1}{$Pos} = $Member_Name;
11911                $AbsPos{1}{$Pos++} = $Member_Pos;
11912            }
11913        }
11914        $Pos = 0;
11915        foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
11916        { # relative positions in 2nd version
11917            my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
11918            next if(not $Member_Name);
11919            if(not $AddedField{$Member_Pos})
11920            { # new type without added fields
11921                $RelPos{2}{$Member_Name} = $Pos;
11922                $RelPosName{2}{$Pos} = $Member_Name;
11923                $AbsPos{2}{$Pos++} = $Member_Pos;
11924            }
11925        }
11926        foreach my $Member_Name (keys(%{$RelPos{1}}))
11927        {
11928            my $RPos1 = $RelPos{1}{$Member_Name};
11929            my $AbsPos1 = $NameToPosA{$Member_Name};
11930            my $Member_Name2 = $Member_Name;
11931            if(my $RenamedTo = $RenamedField{$AbsPos1})
11932            { # renamed
11933                $Member_Name2 = $RenamedTo;
11934            }
11935            my $RPos2 = $RelPos{2}{$Member_Name2};
11936            if($RPos2 ne "" and $RPos1 ne $RPos2)
11937            { # different relative positions
11938                my $AbsPos2 = $NameToPosB{$Member_Name2};
11939                if($AbsPos1 ne $AbsPos2)
11940                { # different absolute positions
11941                    my $ProblemType = "Moved_Field";
11942                    if(not isPublic(\%Type1_Pure, $AbsPos1))
11943                    { # may change layout and size of type
11944                        if($Level eq "Source") {
11945                            next;
11946                        }
11947                        $ProblemType = "Moved_Private_Field";
11948                    }
11949                    if($Level eq "Binary"
11950                    and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11951                    { # affected size
11952                        my $MemSize1 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$AbsPos1}{"type"}}{"Size"};
11953                        my $MovedAbsPos = $AbsPos{1}{$RPos2};
11954                        my $MemSize2 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$MovedAbsPos}{"type"}}{"Size"};
11955                        if($MemSize1 ne $MemSize2) {
11956                            $ProblemType .= "_And_Size";
11957                        }
11958                    }
11959                    if($ProblemType eq "Moved_Private_Field") {
11960                        next;
11961                    }
11962                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
11963                        "Target"=>$Member_Name,
11964                        "Type_Name"=>$Type1_Pure{"Name"},
11965                        "Old_Value"=>$RPos1,
11966                        "New_Value"=>$RPos2 );
11967                }
11968            }
11969        }
11970    }
11971    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11972    { # check older fields, public and private
11973        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11974        next if(not $Member_Name);
11975        next if($Member_Name eq "_vptr");
11976        if(my $RenamedTo = $RenamedField{$Member_Pos})
11977        { # renamed
11978            if(defined $Constants{2}{$Member_Name})
11979            {
11980                if($Constants{2}{$Member_Name}{"Value"} eq $RenamedTo)
11981                { # define OLD NEW
11982                    next; # Safe
11983                }
11984            }
11985
11986            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11987            {
11988                if(isPublic(\%Type1_Pure, $Member_Pos))
11989                {
11990                    %{$SubProblems{"Renamed_Field"}{$Member_Name}}=(
11991                        "Target"=>$Member_Name,
11992                        "Type_Name"=>$Type1_Pure{"Name"},
11993                        "Old_Value"=>$Member_Name,
11994                        "New_Value"=>$RenamedTo  );
11995                }
11996                elsif(isReserved($Member_Name))
11997                {
11998                    %{$SubProblems{"Used_Reserved_Field"}{$Member_Name}}=(
11999                        "Target"=>$Member_Name,
12000                        "Type_Name"=>$Type1_Pure{"Name"},
12001                        "Old_Value"=>$Member_Name,
12002                        "New_Value"=>$RenamedTo  );
12003                }
12004            }
12005            elsif($Type1_Pure{"Type"} eq "Enum")
12006            {
12007                %{$SubProblems{"Enum_Member_Name"}{$Type1_Pure{"Memb"}{$Member_Pos}{"value"}}}=(
12008                    "Target"=>$Type1_Pure{"Memb"}{$Member_Pos}{"value"},
12009                    "Type_Name"=>$Type1_Pure{"Name"},
12010                    "Old_Value"=>$Member_Name,
12011                    "New_Value"=>$RenamedTo  );
12012            }
12013        }
12014        elsif($RemovedField{$Member_Pos})
12015        { # removed
12016            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
12017            {
12018                my $ProblemType = "Removed_Field";
12019                if(not isPublic(\%Type1_Pure, $Member_Pos)
12020                or isUnnamed($Member_Name))
12021                {
12022                    if($Level eq "Source") {
12023                        next;
12024                    }
12025                    $ProblemType = "Removed_Private_Field";
12026                }
12027                if($Level eq "Binary"
12028                and not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
12029                {
12030                    if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
12031                    { # affected fields
12032                        if(getOffset($MNum-1, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2}))
12033                        { # changed offset
12034                            $ProblemType .= "_And_Layout";
12035                        }
12036                    }
12037                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
12038                    { # affected size
12039                        $ProblemType .= "_And_Size";
12040                    }
12041                }
12042                if($ProblemType eq "Removed_Private_Field") {
12043                    next;
12044                }
12045                %{$SubProblems{$ProblemType}{$Member_Name}}=(
12046                    "Target"=>$Member_Name,
12047                    "Type_Name"=>$Type1_Pure{"Name"}  );
12048            }
12049            elsif($Type2_Pure{"Type"} eq "Union")
12050            {
12051                if($Level eq "Binary"
12052                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
12053                {
12054                    %{$SubProblems{"Removed_Union_Field_And_Size"}{$Member_Name}}=(
12055                        "Target"=>$Member_Name,
12056                        "Type_Name"=>$Type1_Pure{"Name"}  );
12057                }
12058                else
12059                {
12060                    %{$SubProblems{"Removed_Union_Field"}{$Member_Name}}=(
12061                        "Target"=>$Member_Name,
12062                        "Type_Name"=>$Type1_Pure{"Name"}  );
12063                }
12064            }
12065            elsif($Type1_Pure{"Type"} eq "Enum")
12066            {
12067                %{$SubProblems{"Enum_Member_Removed"}{$Member_Name}}=(
12068                    "Target"=>$Member_Name,
12069                    "Type_Name"=>$Type1_Pure{"Name"},
12070                    "Old_Value"=>$Member_Name  );
12071            }
12072        }
12073        else
12074        { # changed
12075            my $MemberPair_Pos = $RelatedField{$Member_Pos};
12076            if($Type1_Pure{"Type"} eq "Enum")
12077            {
12078                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
12079                next if($Member_Value1 eq "");
12080                my $Member_Value2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"value"};
12081                next if($Member_Value2 eq "");
12082                if($Member_Value1 ne $Member_Value2)
12083                {
12084                    my $ProblemType = "Enum_Member_Value";
12085                    if(isLastElem($Member_Pos, \%Type1_Pure)) {
12086                        $ProblemType = "Enum_Last_Member_Value";
12087                    }
12088                    if($SkipConstants{1}{$Member_Name}) {
12089                        $ProblemType = "Enum_Private_Member_Value";
12090                    }
12091                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
12092                        "Target"=>$Member_Name,
12093                        "Type_Name"=>$Type1_Pure{"Name"},
12094                        "Old_Value"=>$Member_Value1,
12095                        "New_Value"=>$Member_Value2  );
12096                }
12097            }
12098            elsif($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
12099            {
12100                my $Access1 = $Type1_Pure{"Memb"}{$Member_Pos}{"access"};
12101                my $Access2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"access"};
12102
12103                if($Access1 ne "private"
12104                and $Access2 eq "private")
12105                {
12106                    %{$SubProblems{"Field_Became_Private"}{$Member_Name}}=(
12107                        "Target"=>$Member_Name,
12108                        "Type_Name"=>$Type1_Pure{"Name"});
12109                }
12110                elsif($Access1 ne "protected"
12111                and $Access1 ne "private"
12112                and $Access2 eq "protected")
12113                {
12114                    %{$SubProblems{"Field_Became_Protected"}{$Member_Name}}=(
12115                        "Target"=>$Member_Name,
12116                        "Type_Name"=>$Type1_Pure{"Name"});
12117                }
12118
12119                my $MemberType1_Id = $Type1_Pure{"Memb"}{$Member_Pos}{"type"};
12120                my $MemberType2_Id = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"type"};
12121                my $SizeV1 = $TypeInfo{1}{$MemberType1_Id}{"Size"}*$BYTE_SIZE;
12122                if(my $BSize1 = $Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}) {
12123                    $SizeV1 = $BSize1;
12124                }
12125                my $SizeV2 = $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE;
12126                if(my $BSize2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) {
12127                    $SizeV2 = $BSize2;
12128                }
12129                my $MemberType1_Name = $TypeInfo{1}{$MemberType1_Id}{"Name"};
12130                my $MemberType2_Name = $TypeInfo{2}{$MemberType2_Id}{"Name"};
12131                if($Level eq "Binary"
12132                and $SizeV1 and $SizeV2
12133                and $SizeV1 ne $SizeV2)
12134                {
12135                    if($MemberType1_Name eq $MemberType2_Name or (isAnon($MemberType1_Name) and isAnon($MemberType2_Name))
12136                    or ($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"} and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}))
12137                    { # field size change (including anon-structures and unions)
12138                      # - same types
12139                      # - unnamed types
12140                      # - bitfields
12141                        my $ProblemType = "Field_Size";
12142                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12143                        or isUnnamed($Member_Name))
12144                        { # should not be accessed by applications, goes to "Low Severity"
12145                          # example: "abidata" members in GStreamer types
12146                            $ProblemType = "Private_".$ProblemType;
12147                        }
12148                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
12149                        { # check an effect
12150                            if($Type2_Pure{"Type"} ne "Union"
12151                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
12152                            { # public fields after the current
12153                                if(getOffset($MNum-1, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2}))
12154                                { # changed offset
12155                                    $ProblemType .= "_And_Layout";
12156                                }
12157                            }
12158                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12159                                $ProblemType .= "_And_Type_Size";
12160                            }
12161                        }
12162                        if($ProblemType eq "Private_Field_Size")
12163                        { # private field size with no effect
12164                        }
12165                        if($ProblemType eq "Field_Size")
12166                        {
12167                            if($Type1_Pure{"Type"}=~/Union|Struct/ and $SizeV1<$SizeV2)
12168                            { # Low severity
12169                                $ProblemType = "Struct_Field_Size_Increased";
12170                            }
12171                        }
12172                        if($ProblemType)
12173                        { # register a problem
12174                            %{$SubProblems{$ProblemType}{$Member_Name}}=(
12175                                "Target"=>$Member_Name,
12176                                "Type_Name"=>$Type1_Pure{"Name"},
12177                                "Old_Size"=>$SizeV1,
12178                                "New_Size"=>$SizeV2);
12179                        }
12180                    }
12181                }
12182                if($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}
12183                or $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"})
12184                { # do NOT check bitfield type changes
12185                    next;
12186                }
12187                if(checkDump(1, "2.13") and checkDump(2, "2.13"))
12188                {
12189                    if(not $Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
12190                    and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
12191                    {
12192                        %{$SubProblems{"Field_Became_Mutable"}{$Member_Name}}=(
12193                            "Target"=>$Member_Name,
12194                            "Type_Name"=>$Type1_Pure{"Name"});
12195                    }
12196                    elsif($Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
12197                    and not $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
12198                    {
12199                        %{$SubProblems{"Field_Became_Non_Mutable"}{$Member_Name}}=(
12200                            "Target"=>$Member_Name,
12201                            "Type_Name"=>$Type1_Pure{"Name"});
12202                    }
12203                }
12204                my %Sub_SubChanges = detectTypeChange($MemberType1_Id, $MemberType2_Id, "Field", $Level);
12205                foreach my $ProblemType (keys(%Sub_SubChanges))
12206                {
12207                    my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
12208                    my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
12209
12210                    # quals
12211                    if($ProblemType eq "Field_Type"
12212                    or $ProblemType eq "Field_Type_And_Size"
12213                    or $ProblemType eq "Field_Type_Format")
12214                    {
12215                        if(checkDump(1, "2.6") and checkDump(2, "2.6"))
12216                        {
12217                            if(addedQual($Old_Value, $New_Value, "volatile")) {
12218                                %{$Sub_SubChanges{"Field_Became_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
12219                            }
12220                            elsif(removedQual($Old_Value, $New_Value, "volatile")) {
12221                                %{$Sub_SubChanges{"Field_Became_Non_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
12222                            }
12223                        }
12224                        if(my $RA = addedQual($Old_Value, $New_Value, "const"))
12225                        {
12226                            if($RA==2) {
12227                                %{$Sub_SubChanges{"Field_Added_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12228                            }
12229                            else {
12230                                %{$Sub_SubChanges{"Field_Became_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12231                            }
12232                        }
12233                        elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
12234                        {
12235                            if($RR==2) {
12236                                %{$Sub_SubChanges{"Field_Removed_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12237                            }
12238                            else {
12239                                %{$Sub_SubChanges{"Field_Became_Non_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12240                            }
12241                        }
12242                    }
12243                }
12244
12245                if($Level eq "Source")
12246                {
12247                    foreach my $ProblemType (keys(%Sub_SubChanges))
12248                    {
12249                        my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
12250                        my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
12251
12252                        if($ProblemType eq "Field_Type")
12253                        {
12254                            if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
12255                                delete($Sub_SubChanges{$ProblemType});
12256                            }
12257                        }
12258                    }
12259                }
12260
12261                foreach my $ProblemType (keys(%Sub_SubChanges))
12262                {
12263                    my $ProblemType_Init = $ProblemType;
12264                    if($ProblemType eq "Field_Type_And_Size")
12265                    { # Binary
12266                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12267                        or isUnnamed($Member_Name)) {
12268                            $ProblemType = "Private_".$ProblemType;
12269                        }
12270                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
12271                        { # check an effect
12272                            if($Type2_Pure{"Type"} ne "Union"
12273                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
12274                            { # public fields after the current
12275                                if(getOffset($MNum-1, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1})!=getOffset($RelatedField{$MNum-1}, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2}))
12276                                { # changed offset
12277                                    $ProblemType .= "_And_Layout";
12278                                }
12279                            }
12280                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12281                                $ProblemType .= "_And_Type_Size";
12282                            }
12283                        }
12284                    }
12285                    else
12286                    {
12287                        # TODO: Private_Field_Type rule?
12288
12289                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12290                        or isUnnamed($Member_Name)) {
12291                            next;
12292                        }
12293                    }
12294                    if($ProblemType eq "Private_Field_Type_And_Size")
12295                    { # private field change with no effect
12296                    }
12297                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
12298                        "Target"=>$Member_Name,
12299                        "Type_Name"=>$Type1_Pure{"Name"});
12300
12301                    foreach my $Attr (keys(%{$Sub_SubChanges{$ProblemType_Init}}))
12302                    { # other properties
12303                        $SubProblems{$ProblemType}{$Member_Name}{$Attr} = $Sub_SubChanges{$ProblemType_Init}{$Attr};
12304                    }
12305                }
12306                if(not isPublic(\%Type1_Pure, $Member_Pos))
12307                { # do NOT check internal type changes
12308                    next;
12309                }
12310                if($MemberType1_Id and $MemberType2_Id)
12311                { # checking member type changes
12312                    my $Sub_SubProblems = mergeTypes($MemberType1_Id, $MemberType2_Id, $Level);
12313
12314                    my %DupProblems = ();
12315
12316                    foreach my $Sub_SubProblemType (sort keys(%{$Sub_SubProblems}))
12317                    {
12318                        foreach my $Sub_SubLocation (sort {length($a)<=>length($b)} sort keys(%{$Sub_SubProblems->{$Sub_SubProblemType}}))
12319                        {
12320                            if(not defined $AllAffected)
12321                            {
12322                                if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) {
12323                                    next;
12324                                }
12325                            }
12326
12327                            my $NewLocation = ($Sub_SubLocation)?$Member_Name."->".$Sub_SubLocation:$Member_Name;
12328                            $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
12329
12330                            if(not defined $AllAffected)
12331                            {
12332                                $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1;
12333                            }
12334                        }
12335                    }
12336
12337                    %DupProblems = ();
12338                }
12339            }
12340        }
12341    }
12342    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
12343    { # checking added members, public and private
12344        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
12345        next if(not $Member_Name);
12346        next if($Member_Name eq "_vptr");
12347        if($AddedField{$Member_Pos})
12348        { # added
12349            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
12350            {
12351                my $ProblemType = "Added_Field";
12352                if(not isPublic(\%Type2_Pure, $Member_Pos)
12353                or isUnnamed($Member_Name))
12354                {
12355                    if($Level eq "Source") {
12356                        next;
12357                    }
12358                    $ProblemType = "Added_Private_Field";
12359                }
12360                if($Level eq "Binary"
12361                and not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, $TypeInfo{2}, getArch(2), $WORD_SIZE{2}))
12362                {
12363                    if(my $MNum = isAccessible(\%Type2_Pure, \%AddedField, $Member_Pos, -1))
12364                    { # public fields after the current
12365                        if(getOffset($MNum-1, \%Type2_Pure, $TypeInfo{2}, getArch(2), $WORD_SIZE{2})!=getOffset($RelatedField_Rev{$MNum-1}, \%Type1_Pure, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
12366                        { # changed offset
12367                            $ProblemType .= "_And_Layout";
12368                        }
12369                    }
12370                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12371                        $ProblemType .= "_And_Size";
12372                    }
12373                }
12374                if($ProblemType eq "Added_Private_Field")
12375                { # skip added private fields
12376                    next;
12377                }
12378                %{$SubProblems{$ProblemType}{$Member_Name}}=(
12379                    "Target"=>$Member_Name,
12380                    "Type_Name"=>$Type1_Pure{"Name"});
12381            }
12382            elsif($Type2_Pure{"Type"} eq "Union")
12383            {
12384                if($Level eq "Binary"
12385                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
12386                {
12387                    %{$SubProblems{"Added_Union_Field_And_Size"}{$Member_Name}}=(
12388                        "Target"=>$Member_Name,
12389                        "Type_Name"=>$Type1_Pure{"Name"});
12390                }
12391                else
12392                {
12393                    %{$SubProblems{"Added_Union_Field"}{$Member_Name}}=(
12394                        "Target"=>$Member_Name,
12395                        "Type_Name"=>$Type1_Pure{"Name"});
12396                }
12397            }
12398            elsif($Type2_Pure{"Type"} eq "Enum")
12399            {
12400                my $Member_Value = $Type2_Pure{"Memb"}{$Member_Pos}{"value"};
12401                next if($Member_Value eq "");
12402                %{$SubProblems{"Added_Enum_Member"}{$Member_Name}}=(
12403                    "Target"=>$Member_Name,
12404                    "Type_Name"=>$Type2_Pure{"Name"},
12405                    "New_Value"=>$Member_Value);
12406            }
12407        }
12408    }
12409
12410    if($Type1_Pure{"Type"} eq "FuncPtr")
12411    {
12412        foreach my $PPos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Param"}}))
12413        {
12414            if(not defined $Type2_Pure{"Param"}{$PPos}) {
12415                next;
12416            }
12417
12418            my $PT1 = $Type1_Pure{"Param"}{$PPos}{"type"};
12419            my $PT2 = $Type2_Pure{"Param"}{$PPos}{"type"};
12420
12421            my $PName = "p".$PPos;
12422
12423            my $FP_SubProblems = mergeTypes($PT1, $PT2, $Level);
12424            my %DupProblems = ();
12425
12426            foreach my $FP_SubProblemType (keys(%{$FP_SubProblems}))
12427            {
12428                foreach my $FP_SubLocation (keys(%{$FP_SubProblems->{$FP_SubProblemType}}))
12429                {
12430                    if(not defined $AllAffected)
12431                    {
12432                        if(defined $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}}) {
12433                            next;
12434                        }
12435                    }
12436
12437                    my $NewLocation = ($FP_SubLocation)?$PName."->".$FP_SubLocation:$PName;
12438                    $SubProblems{$FP_SubProblemType}{$NewLocation} = $FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation};
12439
12440                    if(not defined $AllAffected)
12441                    {
12442                        $DupProblems{$FP_SubProblems->{$FP_SubProblemType}{$FP_SubLocation}} = 1;
12443                    }
12444                }
12445            }
12446
12447            %DupProblems = ();
12448        }
12449    }
12450
12451    pop(@RecurTypes);
12452    return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
12453}
12454
12455sub isUnnamed($) {
12456    return $_[0]=~/\Aunnamed\d+\Z/;
12457}
12458
12459sub get_ShortClass($$)
12460{
12461    my ($TypeId, $LibVersion) = @_;
12462    my $TypeName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
12463    if($TypeInfo{$LibVersion}{$TypeId}{"Type"}!~/Intrinsic|Class|Struct|Union|Enum/) {
12464        $TypeName = uncover_typedefs($TypeName, $LibVersion);
12465    }
12466    if(my $NameSpace = $TypeInfo{$LibVersion}{$TypeId}{"NameSpace"}) {
12467        $TypeName=~s/\A(struct |)\Q$NameSpace\E\:\://g;
12468    }
12469    return $TypeName;
12470}
12471
12472sub goToFirst($$$)
12473{
12474    my ($TypeId, $LibVersion, $Type_Type) = @_;
12475    return () if(not $TypeId);
12476    if(defined $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}) {
12477        return %{$Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}};
12478    }
12479    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12480    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12481    return () if(not $Type{"Type"});
12482    if($Type{"Type"} ne $Type_Type)
12483    {
12484        return () if(not $Type{"BaseType"});
12485        %Type = goToFirst($Type{"BaseType"}, $LibVersion, $Type_Type);
12486    }
12487    $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type} = \%Type;
12488    return %Type;
12489}
12490
12491my %TypeSpecAttributes = (
12492    "Const" => 1,
12493    "Volatile" => 1,
12494    "ConstVolatile" => 1,
12495    "Restrict" => 1,
12496    "Typedef" => 1
12497);
12498
12499sub get_PureType($$)
12500{
12501    my ($TypeId, $Info) = @_;
12502    if(not $TypeId or not $Info
12503    or not $Info->{$TypeId}) {
12504        return ();
12505    }
12506    if(defined $Cache{"get_PureType"}{$TypeId}{$Info}) {
12507        return %{$Cache{"get_PureType"}{$TypeId}{$Info}};
12508    }
12509    my %Type = %{$Info->{$TypeId}};
12510    return %Type if(not $Type{"BaseType"});
12511    if($TypeSpecAttributes{$Type{"Type"}}) {
12512        %Type = get_PureType($Type{"BaseType"}, $Info);
12513    }
12514    $Cache{"get_PureType"}{$TypeId}{$Info} = \%Type;
12515    return %Type;
12516}
12517
12518sub get_PLevel($$)
12519{
12520    my ($TypeId, $LibVersion) = @_;
12521    return 0 if(not $TypeId);
12522    if(defined $Cache{"get_PLevel"}{$TypeId}{$LibVersion}) {
12523        return $Cache{"get_PLevel"}{$TypeId}{$LibVersion};
12524    }
12525    return 0 if(not $TypeInfo{$LibVersion}{$TypeId});
12526    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12527    return 1 if($Type{"Type"}=~/FuncPtr|FieldPtr/);
12528    my $PLevel = 0;
12529    if($Type{"Type"} =~/Pointer|Ref|FuncPtr|FieldPtr/) {
12530        $PLevel += 1;
12531    }
12532    return $PLevel if(not $Type{"BaseType"});
12533    $PLevel += get_PLevel($Type{"BaseType"}, $LibVersion);
12534    $Cache{"get_PLevel"}{$TypeId}{$LibVersion} = $PLevel;
12535    return $PLevel;
12536}
12537
12538sub get_BaseType($$)
12539{
12540    my ($TypeId, $LibVersion) = @_;
12541    return () if(not $TypeId);
12542    if(defined $Cache{"get_BaseType"}{$TypeId}{$LibVersion}) {
12543        return %{$Cache{"get_BaseType"}{$TypeId}{$LibVersion}};
12544    }
12545    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12546    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12547    return %Type if(not $Type{"BaseType"});
12548    %Type = get_BaseType($Type{"BaseType"}, $LibVersion);
12549    $Cache{"get_BaseType"}{$TypeId}{$LibVersion} = \%Type;
12550    return %Type;
12551}
12552
12553sub get_BaseTypeQual($$)
12554{
12555    my ($TypeId, $LibVersion) = @_;
12556    return "" if(not $TypeId);
12557    return "" if(not $TypeInfo{$LibVersion}{$TypeId});
12558    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12559    return "" if(not $Type{"BaseType"});
12560    my $Qual = "";
12561    if($Type{"Type"} eq "Pointer") {
12562        $Qual .= "*";
12563    }
12564    elsif($Type{"Type"} eq "Ref") {
12565        $Qual .= "&";
12566    }
12567    elsif($Type{"Type"} eq "ConstVolatile") {
12568        $Qual .= "const volatile";
12569    }
12570    elsif($Type{"Type"} eq "Const"
12571    or $Type{"Type"} eq "Volatile"
12572    or $Type{"Type"} eq "Restrict") {
12573        $Qual .= lc($Type{"Type"});
12574    }
12575    my $BQual = get_BaseTypeQual($Type{"BaseType"}, $LibVersion);
12576    return $BQual.$Qual;
12577}
12578
12579sub get_OneStep_BaseType($$)
12580{
12581    my ($TypeId, $Info) = @_;
12582    if(not $TypeId or not $Info
12583    or not $Info->{$TypeId}) {
12584        return ();
12585    }
12586    my %Type = %{$Info->{$TypeId}};
12587    return %Type if(not $Type{"BaseType"});
12588    if(my $BTid = $Type{"BaseType"})
12589    {
12590        if($Info->{$BTid}) {
12591            return %{$Info->{$BTid}};
12592        }
12593        else { # something is going wrong
12594            return ();
12595        }
12596    }
12597    else {
12598        return %Type;
12599    }
12600}
12601
12602sub get_Type($$)
12603{
12604    my ($TypeId, $LibVersion) = @_;
12605    return () if(not $TypeId);
12606    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12607    return %{$TypeInfo{$LibVersion}{$TypeId}};
12608}
12609
12610sub isPrivateData($)
12611{ # non-public global data
12612    my $Symbol = $_[0];
12613    return ($Symbol=~/\A(_ZGV|_ZTI|_ZTS|_ZTT|_ZTV|_ZTC|_ZThn|_ZTv0_n)/);
12614}
12615
12616sub isInLineInst($$) {
12617    return (isTemplateInstance(@_) and not isTemplateSpec(@_));
12618}
12619
12620sub isTemplateInstance($$)
12621{
12622    my ($SInfo, $LibVersion) = @_;
12623
12624    if(my $ClassId = $SInfo->{"Class"})
12625    {
12626        if(my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"})
12627        {
12628            if(index($ClassName,"<")!=-1) {
12629                return 1;
12630            }
12631        }
12632    }
12633    if(my $ShortName = $SInfo->{"ShortName"})
12634    {
12635        if(index($ShortName,"<")!=-1
12636        and index($ShortName,">")!=-1) {
12637            return 1;
12638        }
12639    }
12640
12641    return 0;
12642}
12643
12644sub isTemplateSpec($$)
12645{
12646    my ($SInfo, $LibVersion) = @_;
12647    if(my $ClassId = $SInfo->{"Class"})
12648    {
12649        if($TypeInfo{$LibVersion}{$ClassId}{"Spec"})
12650        { # class specialization
12651            return 1;
12652        }
12653        elsif($SInfo->{"Spec"})
12654        { # method specialization
12655            return 1;
12656        }
12657    }
12658    return 0;
12659}
12660
12661sub symbolFilter($$$$)
12662{ # some special cases when the symbol cannot be imported
12663    my ($Symbol, $LibVersion, $Type, $Level) = @_;
12664
12665    if(isPrivateData($Symbol))
12666    { # non-public global data
12667        return 0;
12668    }
12669
12670    if(defined $SkipInternalSymbols)
12671    {
12672        return 0 if($Symbol=~/($SkipInternalSymbols)/);
12673    }
12674
12675    if($Symbol=~/\A_Z/)
12676    {
12677        if($Symbol=~/[CD][3-4]E/) {
12678            return 0;
12679        }
12680    }
12681
12682    if($CheckHeadersOnly and not checkDump($LibVersion, "2.7"))
12683    { # support for old ABI dumps in --headers-only mode
12684        foreach my $Pos (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
12685        {
12686            if(my $Pid = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"})
12687            {
12688                my $PType = $TypeInfo{$LibVersion}{$Pid}{"Type"};
12689                if(not $PType or $PType eq "Unknown") {
12690                    return 0;
12691                }
12692            }
12693        }
12694    }
12695    if($Type=~/Affected/)
12696    {
12697        my $Header = $CompleteSignature{$LibVersion}{$Symbol}{"Header"};
12698
12699        if($SkipSymbols{$LibVersion}{$Symbol})
12700        { # user defined symbols to ignore
12701            return 0;
12702        }
12703
12704        if($SymbolsListPath and not $SymbolsList{$Symbol})
12705        { # user defined symbols
12706            if(not $TargetHeadersPath or not $Header
12707            or not is_target_header($Header, 1))
12708            { # -symbols-list | -headers-list
12709                return 0;
12710            }
12711        }
12712
12713        if($AppPath and not $SymbolsList_App{$Symbol})
12714        { # user defined symbols (in application)
12715            return 0;
12716        }
12717
12718        my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"};
12719
12720        if($ClassId)
12721        {
12722            if(not isTargetType($ClassId, $LibVersion)) {
12723                return 0;
12724            }
12725        }
12726
12727        my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"};
12728        if(not $NameSpace and $ClassId)
12729        { # class methods have no "NameSpace" attribute
12730            $NameSpace = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"};
12731        }
12732        if($NameSpace)
12733        { # user defined namespaces to ignore
12734            if($SkipNameSpaces{$LibVersion}{$NameSpace}) {
12735                return 0;
12736            }
12737            foreach my $NS (keys(%{$SkipNameSpaces{$LibVersion}}))
12738            { # nested namespaces
12739                if($NameSpace=~/\A\Q$NS\E(\:\:|\Z)/) {
12740                    return 0;
12741                }
12742            }
12743        }
12744        if($Header)
12745        {
12746            if(my $Skip = skipHeader($Header, $LibVersion))
12747            { # --skip-headers or <skip_headers> (not <skip_including>)
12748                if($Skip==1) {
12749                    return 0;
12750                }
12751            }
12752        }
12753        if($TypesListPath and $ClassId)
12754        { # user defined types
12755            my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
12756
12757            if(not $TypesList{$CName})
12758            {
12759                if(my $NS = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"})
12760                {
12761                    $CName=~s/\A\Q$NS\E\:\://g;
12762                }
12763
12764                if(not $TypesList{$CName})
12765                {
12766                    my $Found = 0;
12767
12768                    while($CName=~s/\:\:.+?\Z//)
12769                    {
12770                        if($TypesList{$CName})
12771                        {
12772                            $Found = 1;
12773                            last;
12774                        }
12775                    }
12776
12777                    if(not $Found) {
12778                        return 0;
12779                    }
12780                }
12781            }
12782        }
12783
12784        if(not selectSymbol($Symbol, $CompleteSignature{$LibVersion}{$Symbol}, $Level, $LibVersion))
12785        { # non-target symbols
12786            return 0;
12787        }
12788        if($Level eq "Binary")
12789        {
12790            if($CompleteSignature{$LibVersion}{$Symbol}{"InLine"}
12791            or isInLineInst($CompleteSignature{$LibVersion}{$Symbol}, $LibVersion))
12792            {
12793                if($ClassId and $CompleteSignature{$LibVersion}{$Symbol}{"Virt"})
12794                { # inline virtual methods
12795                    if($Type=~/InlineVirt/) {
12796                        return 1;
12797                    }
12798                    my $Allocable = (not isCopyingClass($ClassId, $LibVersion));
12799                    if(not $Allocable)
12800                    { # check bases
12801                        foreach my $DCId (get_sub_classes($ClassId, $LibVersion, 1))
12802                        {
12803                            if(not isCopyingClass($DCId, $LibVersion))
12804                            { # exists a derived class without default c-tor
12805                                $Allocable=1;
12806                                last;
12807                            }
12808                        }
12809                    }
12810                    if(not $Allocable) {
12811                        return 0;
12812                    }
12813                }
12814                else
12815                { # inline non-virtual methods
12816                    return 0;
12817                }
12818            }
12819        }
12820    }
12821    return 1;
12822}
12823
12824sub detectAdded($)
12825{
12826    my $Level = $_[0];
12827    foreach my $Symbol (keys(%{$Symbol_Library{2}}))
12828    {
12829        if(link_symbol($Symbol, 1, "+Deps"))
12830        { # linker can find a new symbol
12831          # in the old-version library
12832          # So, it's not a new symbol
12833            next;
12834        }
12835        if(my $VSym = $SymVer{2}{$Symbol}
12836        and index($Symbol,"\@")==-1) {
12837            next;
12838        }
12839        $AddedInt{$Level}{$Symbol} = 1;
12840    }
12841}
12842
12843sub detectRemoved($)
12844{
12845    my $Level = $_[0];
12846    foreach my $Symbol (keys(%{$Symbol_Library{1}}))
12847    {
12848        if(link_symbol($Symbol, 2, "+Deps"))
12849        { # linker can find an old symbol
12850          # in the new-version library
12851            next;
12852        }
12853        if(my $VSym = $SymVer{1}{$Symbol}
12854        and index($Symbol,"\@")==-1) {
12855            next;
12856        }
12857        $RemovedInt{$Level}{$Symbol} = 1;
12858    }
12859}
12860
12861sub mergeLibs($)
12862{
12863    my $Level = $_[0];
12864    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
12865    { # checking added symbols
12866        next if($CompleteSignature{2}{$Symbol}{"Private"});
12867        next if(not $CompleteSignature{2}{$Symbol}{"Header"});
12868        next if(not symbolFilter($Symbol, 2, "Affected + InlineVirt", $Level));
12869        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=();
12870    }
12871    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
12872    { # checking removed symbols
12873        next if($CompleteSignature{1}{$Symbol}{"Private"});
12874        next if(not $CompleteSignature{1}{$Symbol}{"Header"});
12875        if(index($Symbol, "_ZTV")==0)
12876        { # skip v-tables for templates, that should not be imported by applications
12877            next if($tr_name{$Symbol}=~/</);
12878            if(my $CName = $VTableClass{$Symbol})
12879            {
12880                if(not keys(%{$ClassMethods{$Level}{1}{$CName}}))
12881                { # vtables for "private" classes
12882                  # use case: vtable for QDragManager (Qt 4.5.3 to 4.6.0) became HIDDEN symbol
12883                    next;
12884                }
12885            }
12886
12887            if($SkipSymbols{1}{$Symbol})
12888            { # user defined symbols to ignore
12889                next;
12890            }
12891        }
12892        else {
12893            next if(not symbolFilter($Symbol, 1, "Affected + InlineVirt", $Level));
12894        }
12895        if($CompleteSignature{1}{$Symbol}{"PureVirt"})
12896        { # symbols for pure virtual methods cannot be called by clients
12897            next;
12898        }
12899        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=();
12900    }
12901}
12902
12903sub checkDump($$)
12904{
12905    my ($LibVersion, $V) = @_;
12906    if(defined $Cache{"checkDump"}{$LibVersion}{$V}) {
12907        return $Cache{"checkDump"}{$LibVersion}{$V};
12908    }
12909    return ($Cache{"checkDump"}{$LibVersion}{$V} = (not $UsedDump{$LibVersion}{"V"} or cmpVersions($UsedDump{$LibVersion}{"V"}, $V)>=0));
12910}
12911
12912sub detectAdded_H($)
12913{
12914    my $Level = $_[0];
12915    foreach my $Symbol (sort keys(%{$CompleteSignature{2}}))
12916    {
12917        if($Level eq "Source")
12918        { # remove symbol version
12919            my ($SN, $SS, $SV) = separate_symbol($Symbol);
12920            $Symbol=$SN;
12921
12922            if($CompleteSignature{2}{$Symbol}{"Artificial"})
12923            { # skip artificial constructors
12924                next;
12925            }
12926        }
12927        if(not $CompleteSignature{2}{$Symbol}{"Header"}
12928        or not $CompleteSignature{2}{$Symbol}{"MnglName"}) {
12929            next;
12930        }
12931        if($ExtendedSymbols{$Symbol}) {
12932            next;
12933        }
12934        if(not defined $CompleteSignature{1}{$Symbol}
12935        or not $CompleteSignature{1}{$Symbol}{"MnglName"})
12936        {
12937            if($UsedDump{2}{"SrcBin"})
12938            {
12939                if($UsedDump{1}{"BinOnly"} or not checkDump(1, "2.11"))
12940                { # support for old and different (!) ABI dumps
12941                    if(not $CompleteSignature{2}{$Symbol}{"Virt"}
12942                    and not $CompleteSignature{2}{$Symbol}{"PureVirt"})
12943                    {
12944                        if($CheckHeadersOnly)
12945                        {
12946                            if(my $Lang = $CompleteSignature{2}{$Symbol}{"Lang"})
12947                            {
12948                                if($Lang eq "C")
12949                                { # support for old ABI dumps: missed extern "C" functions
12950                                    next;
12951                                }
12952                            }
12953                        }
12954                        else
12955                        {
12956                            if(not link_symbol($Symbol, 2, "-Deps"))
12957                            { # skip added inline symbols and const global data
12958                                next;
12959                            }
12960                        }
12961                    }
12962                }
12963            }
12964            $AddedInt{$Level}{$Symbol} = 1;
12965        }
12966    }
12967}
12968
12969sub detectRemoved_H($)
12970{
12971    my $Level = $_[0];
12972    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
12973    {
12974        my $ISymbol = $Symbol;
12975
12976        if($Level eq "Source")
12977        { # remove symbol version
12978            my ($SN, $SS, $SV) = separate_symbol($Symbol);
12979            $Symbol = $SN;
12980        }
12981
12982        if(not $CompleteSignature{1}{$Symbol}{"Header"}
12983        or not $CompleteSignature{1}{$Symbol}{"MnglName"}) {
12984            next;
12985        }
12986        if($ExtendedSymbols{$Symbol}) {
12987            next;
12988        }
12989        if(not defined $CompleteSignature{2}{$Symbol}
12990        or not $CompleteSignature{2}{$Symbol}{"MnglName"})
12991        {
12992            if(defined $UsedDump{1}{"DWARF"}
12993            and defined $UsedDump{2}{"DWARF"}
12994            and $Level eq "Source")
12995            { # not present in debug-info,
12996              # but present in dynsym
12997                if(link_symbol($Symbol, 2, "-Deps")) {
12998                    next;
12999                }
13000
13001                if($ISymbol ne $Symbol)
13002                {
13003                    if(link_symbol($ISymbol, 2, "-Deps")) {
13004                        next;
13005                    }
13006                }
13007
13008                if(my $SVer = $SymVer{2}{$Symbol})
13009                {
13010                    if(link_symbol($SVer, 2, "-Deps")) {
13011                        next;
13012                    }
13013                }
13014
13015                if(my $Alias = $CompleteSignature{1}{$ISymbol}{"Alias"})
13016                {
13017                    if(link_symbol($Alias, 2, "-Deps")) {
13018                        next;
13019                    }
13020
13021                    if(my $SAVer = $SymVer{2}{$Alias})
13022                    {
13023                        if(link_symbol($SAVer, 2, "-Deps")) {
13024                            next;
13025                        }
13026                    }
13027                }
13028            }
13029            if($UsedDump{1}{"SrcBin"})
13030            {
13031                if($UsedDump{2}{"BinOnly"} or not checkDump(2, "2.11"))
13032                { # support for old and different (!) ABI dumps
13033                    if(not $CompleteSignature{1}{$Symbol}{"Virt"}
13034                    and not $CompleteSignature{1}{$Symbol}{"PureVirt"})
13035                    {
13036                        if($CheckHeadersOnly)
13037                        { # skip all removed symbols
13038                            if(my $Lang = $CompleteSignature{1}{$Symbol}{"Lang"})
13039                            {
13040                                if($Lang eq "C")
13041                                { # support for old ABI dumps: missed extern "C" functions
13042                                    next;
13043                                }
13044                            }
13045                        }
13046                        else
13047                        {
13048                            if(not link_symbol($Symbol, 1, "-Deps"))
13049                            { # skip removed inline symbols
13050                                next;
13051                            }
13052                        }
13053                    }
13054                }
13055            }
13056            if(not checkDump(1, "2.15"))
13057            {
13058                if($Symbol=~/_IT_E\Z/)
13059                { # _ZN28QExplicitlySharedDataPointerI22QSslCertificatePrivateEC1IT_EERKS_IT_E
13060                    next;
13061                }
13062            }
13063            if(not $CompleteSignature{1}{$Symbol}{"Class"})
13064            {
13065                if(my $Short = $CompleteSignature{1}{$Symbol}{"ShortName"})
13066                {
13067                    if(defined $Constants{2}{$Short})
13068                    {
13069                        my $Val = $Constants{2}{$Short}{"Value"};
13070                        if(defined $Func_ShortName{2}{$Val})
13071                        { # old name defined to new
13072                            next;
13073                        }
13074                    }
13075                }
13076
13077            }
13078            $RemovedInt{$Level}{$Symbol} = 1;
13079            if($Level eq "Source")
13080            { # search for a source-compatible equivalent
13081                setAlternative($Symbol, $Level);
13082            }
13083        }
13084    }
13085}
13086
13087sub mergeHeaders($)
13088{
13089    my $Level = $_[0];
13090    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
13091    { # checking added symbols
13092        next if($CompleteSignature{2}{$Symbol}{"PureVirt"});
13093        next if($CompleteSignature{2}{$Symbol}{"Private"});
13094        next if(not symbolFilter($Symbol, 2, "Affected", $Level));
13095        if($Level eq "Binary")
13096        {
13097            if($CompleteSignature{2}{$Symbol}{"InLine"})
13098            {
13099                if(not $CompleteSignature{2}{$Symbol}{"Virt"})
13100                { # skip inline non-virtual functions
13101                    next;
13102                }
13103            }
13104        }
13105        else
13106        { # Source
13107            if($SourceAlternative_B{$Symbol}) {
13108                next;
13109            }
13110        }
13111        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=();
13112    }
13113    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
13114    { # checking removed symbols
13115        next if($CompleteSignature{1}{$Symbol}{"PureVirt"});
13116        next if($CompleteSignature{1}{$Symbol}{"Private"});
13117        next if(not symbolFilter($Symbol, 1, "Affected", $Level));
13118        if($Level eq "Binary")
13119        {
13120            if($CompleteSignature{1}{$Symbol}{"InLine"})
13121            {
13122                if(not $CompleteSignature{1}{$Symbol}{"Virt"})
13123                { # skip inline non-virtual functions
13124                    next;
13125                }
13126            }
13127        }
13128        else
13129        { # Source
13130            if(my $Alt = $SourceAlternative{$Symbol})
13131            {
13132                if(defined $CompleteSignature{1}{$Alt}
13133                and $CompleteSignature{1}{$Symbol}{"Const"})
13134                {
13135                    my $Cid = $CompleteSignature{1}{$Symbol}{"Class"};
13136                    %{$CompatProblems{$Level}{$Symbol}{"Removed_Const_Overload"}{"this"}}=(
13137                        "Type_Name"=>$TypeInfo{1}{$Cid}{"Name"},
13138                        "Target"=>get_Signature($Alt, 1));
13139                }
13140                else
13141                { # do NOT show removed symbol
13142                    next;
13143                }
13144            }
13145        }
13146        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=();
13147    }
13148}
13149
13150sub addParamNames($)
13151{
13152    my $LibraryVersion = $_[0];
13153    return if(not keys(%AddIntParams));
13154    my $SecondVersion = $LibraryVersion==1?2:1;
13155    foreach my $Interface (sort keys(%{$CompleteSignature{$LibraryVersion}}))
13156    {
13157        next if(not keys(%{$AddIntParams{$Interface}}));
13158        foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibraryVersion}{$Interface}{"Param"}}))
13159        { # add absent parameter names
13160            my $ParamName = $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"};
13161            if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddIntParams{$Interface}{$ParamPos})
13162            { # names from the external file
13163                if(defined $CompleteSignature{$SecondVersion}{$Interface}
13164                and defined $CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos})
13165                {
13166                    if($CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) {
13167                        $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
13168                    }
13169                }
13170                else {
13171                    $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
13172                }
13173            }
13174        }
13175    }
13176}
13177
13178sub detectChangedTypedefs()
13179{ # detect changed typedefs to show
13180  # correct function signatures
13181    foreach my $Typedef (keys(%{$Typedef_BaseName{1}}))
13182    {
13183        next if(not $Typedef);
13184        my $BName1 = $Typedef_BaseName{1}{$Typedef};
13185        if(not $BName1 or isAnon($BName1)) {
13186            next;
13187        }
13188        my $BName2 = $Typedef_BaseName{2}{$Typedef};
13189        if(not $BName2 or isAnon($BName2)) {
13190            next;
13191        }
13192        if($BName1 ne $BName2) {
13193            $ChangedTypedef{$Typedef} = 1;
13194        }
13195    }
13196}
13197
13198sub get_symbol_suffix($$)
13199{
13200    my ($Symbol, $Full) = @_;
13201    my ($SN, $SO, $SV) = separate_symbol($Symbol);
13202    $Symbol=$SN; # remove version
13203    my $Signature = $tr_name{$Symbol};
13204    my $Suffix = substr($Signature, find_center($Signature, "("));
13205    if(not $Full) {
13206        $Suffix=~s/(\))\s*(const volatile|volatile const|const|volatile)\Z/$1/g;
13207    }
13208    return $Suffix;
13209}
13210
13211sub get_symbol_prefix($$)
13212{
13213    my ($Symbol, $LibVersion) = @_;
13214    my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"};
13215    if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
13216    { # methods
13217        $ShortName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}."::".$ShortName;
13218    }
13219    return $ShortName;
13220}
13221
13222sub setAlternative($)
13223{
13224    my $Symbol = $_[0];
13225    my $PSymbol = $Symbol;
13226    if(not defined $CompleteSignature{2}{$PSymbol}
13227    or (not $CompleteSignature{2}{$PSymbol}{"MnglName"}
13228    and not $CompleteSignature{2}{$PSymbol}{"ShortName"}))
13229    { # search for a pair
13230        if(my $ShortName = $CompleteSignature{1}{$PSymbol}{"ShortName"})
13231        {
13232            if($CompleteSignature{1}{$PSymbol}{"Data"})
13233            {
13234                if($PSymbol=~s/L(\d+$ShortName(E)\Z)/$1/
13235                or $PSymbol=~s/(\d+$ShortName(E)\Z)/L$1/)
13236                {
13237                    if(defined $CompleteSignature{2}{$PSymbol}
13238                    and $CompleteSignature{2}{$PSymbol}{"MnglName"})
13239                    {
13240                        $SourceAlternative{$Symbol} = $PSymbol;
13241                        $SourceAlternative_B{$PSymbol} = $Symbol;
13242                        if(not defined $CompleteSignature{1}{$PSymbol}
13243                        or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) {
13244                            $SourceReplacement{$Symbol} = $PSymbol;
13245                        }
13246                    }
13247                }
13248            }
13249            else
13250            {
13251                foreach my $Sp ("KV", "VK", "K", "V")
13252                {
13253                    if($PSymbol=~s/\A_ZN$Sp/_ZN/
13254                    or $PSymbol=~s/\A_ZN/_ZN$Sp/)
13255                    {
13256                        if(defined $CompleteSignature{2}{$PSymbol}
13257                        and $CompleteSignature{2}{$PSymbol}{"MnglName"})
13258                        {
13259                            $SourceAlternative{$Symbol} = $PSymbol;
13260                            $SourceAlternative_B{$PSymbol} = $Symbol;
13261                            if(not defined $CompleteSignature{1}{$PSymbol}
13262                            or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) {
13263                                $SourceReplacement{$Symbol} = $PSymbol;
13264                            }
13265                        }
13266                    }
13267                    $PSymbol = $Symbol;
13268                }
13269            }
13270        }
13271    }
13272    return "";
13273}
13274
13275sub getSymKind($$)
13276{
13277    my ($Symbol, $LibVersion) = @_;
13278    if($CompleteSignature{$LibVersion}{$Symbol}{"Data"})
13279    {
13280        return "Global_Data";
13281    }
13282    elsif($CompleteSignature{$LibVersion}{$Symbol}{"Class"})
13283    {
13284        return "Method";
13285    }
13286    return "Function";
13287}
13288
13289sub mergeSymbols($)
13290{
13291    my $Level = $_[0];
13292    my %SubProblems = ();
13293
13294    mergeBases($Level);
13295
13296    my %AddedOverloads = ();
13297    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
13298    { # check all added exported symbols
13299        if(not $CompleteSignature{2}{$Symbol}{"Header"}) {
13300            next;
13301        }
13302        if(defined $CompleteSignature{1}{$Symbol}
13303        and $CompleteSignature{1}{$Symbol}{"Header"})
13304        { # double-check added symbol
13305            next;
13306        }
13307        if(not symbolFilter($Symbol, 2, "Affected", $Level)) {
13308            next;
13309        }
13310        if($Symbol=~/\A(_Z|\?)/)
13311        { # C++
13312            $AddedOverloads{get_symbol_prefix($Symbol, 2)}{get_symbol_suffix($Symbol, 1)} = $Symbol;
13313        }
13314        if(my $OverriddenMethod = $CompleteSignature{2}{$Symbol}{"Override"})
13315        { # register virtual overridings
13316            my $Cid = $CompleteSignature{2}{$Symbol}{"Class"};
13317            my $AffectedClass_Name = $TypeInfo{2}{$Cid}{"Name"};
13318            if(defined $CompleteSignature{1}{$OverriddenMethod} and $CompleteSignature{1}{$OverriddenMethod}{"Virt"}
13319            and not $CompleteSignature{1}{$OverriddenMethod}{"Private"})
13320            {
13321                if($TName_Tid{1}{$AffectedClass_Name})
13322                { # class should exist in previous version
13323                    if(not isCopyingClass($TName_Tid{1}{$AffectedClass_Name}, 1))
13324                    { # old v-table is NOT copied by old applications
13325                        %{$CompatProblems{$Level}{$OverriddenMethod}{"Overridden_Virtual_Method"}{$tr_name{$Symbol}}}=(
13326                            "Type_Name"=>$AffectedClass_Name,
13327                            "Target"=>get_Signature($Symbol, 2),
13328                            "Old_Value"=>get_Signature($OverriddenMethod, 2),
13329                            "New_Value"=>get_Signature($Symbol, 2));
13330                    }
13331                }
13332            }
13333        }
13334    }
13335    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
13336    { # check all removed exported symbols
13337        if(not $CompleteSignature{1}{$Symbol}{"Header"}) {
13338            next;
13339        }
13340        if(defined $CompleteSignature{2}{$Symbol}
13341        and $CompleteSignature{2}{$Symbol}{"Header"})
13342        { # double-check removed symbol
13343            next;
13344        }
13345        if($CompleteSignature{1}{$Symbol}{"Private"})
13346        { # skip private methods
13347            next;
13348        }
13349        if(not symbolFilter($Symbol, 1, "Affected", $Level)) {
13350            next;
13351        }
13352        $CheckedSymbols{$Level}{$Symbol} = 1;
13353        if(my $OverriddenMethod = $CompleteSignature{1}{$Symbol}{"Override"})
13354        { # register virtual overridings
13355            my $Cid = $CompleteSignature{1}{$Symbol}{"Class"};
13356            my $AffectedClass_Name = $TypeInfo{1}{$Cid}{"Name"};
13357            if(defined $CompleteSignature{2}{$OverriddenMethod}
13358            and $CompleteSignature{2}{$OverriddenMethod}{"Virt"})
13359            {
13360                if($TName_Tid{2}{$AffectedClass_Name})
13361                { # class should exist in newer version
13362                    if(not isCopyingClass($CompleteSignature{1}{$Symbol}{"Class"}, 1))
13363                    { # old v-table is NOT copied by old applications
13364                        %{$CompatProblems{$Level}{$Symbol}{"Overridden_Virtual_Method_B"}{$tr_name{$OverriddenMethod}}}=(
13365                            "Type_Name"=>$AffectedClass_Name,
13366                            "Target"=>get_Signature($OverriddenMethod, 1),
13367                            "Old_Value"=>get_Signature($Symbol, 1),
13368                            "New_Value"=>get_Signature($OverriddenMethod, 1));
13369                    }
13370                }
13371            }
13372        }
13373        if($Level eq "Binary"
13374        and $OStarget eq "windows")
13375        { # register the reason of symbol name change
13376            if(my $NewSym = $mangled_name{2}{$tr_name{$Symbol}})
13377            {
13378                if($AddedInt{$Level}{$NewSym})
13379                {
13380                    if($CompleteSignature{1}{$Symbol}{"Static"} ne $CompleteSignature{2}{$NewSym}{"Static"})
13381                    {
13382                        if($CompleteSignature{2}{$NewSym}{"Static"})
13383                        {
13384                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Static"}{$tr_name{$Symbol}}}=(
13385                                "Target"=>$tr_name{$Symbol},
13386                                "Old_Value"=>$Symbol,
13387                                "New_Value"=>$NewSym  );
13388                        }
13389                        else
13390                        {
13391                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Static"}{$tr_name{$Symbol}}}=(
13392                                "Target"=>$tr_name{$Symbol},
13393                                "Old_Value"=>$Symbol,
13394                                "New_Value"=>$NewSym  );
13395                        }
13396                    }
13397                    if($CompleteSignature{1}{$Symbol}{"Virt"} ne $CompleteSignature{2}{$NewSym}{"Virt"})
13398                    {
13399                        if($CompleteSignature{2}{$NewSym}{"Virt"})
13400                        {
13401                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Virtual"}{$tr_name{$Symbol}}}=(
13402                                "Target"=>$tr_name{$Symbol},
13403                                "Old_Value"=>$Symbol,
13404                                "New_Value"=>$NewSym  );
13405                        }
13406                        else
13407                        {
13408                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Virtual"}{$tr_name{$Symbol}}}=(
13409                                "Target"=>$tr_name{$Symbol},
13410                                "Old_Value"=>$Symbol,
13411                                "New_Value"=>$NewSym  );
13412                        }
13413                    }
13414                    my $RTId1 = $CompleteSignature{1}{$Symbol}{"Return"};
13415                    my $RTId2 = $CompleteSignature{2}{$NewSym}{"Return"};
13416                    my $RTName1 = $TypeInfo{1}{$RTId1}{"Name"};
13417                    my $RTName2 = $TypeInfo{2}{$RTId2}{"Name"};
13418                    if($RTName1 ne $RTName2)
13419                    {
13420                        my $ProblemType = "Symbol_Changed_Return";
13421                        if($CompleteSignature{1}{$Symbol}{"Data"}) {
13422                            $ProblemType = "Global_Data_Symbol_Changed_Type";
13423                        }
13424                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{$tr_name{$Symbol}}}=(
13425                            "Target"=>$tr_name{$Symbol},
13426                            "Old_Type"=>$RTName1,
13427                            "New_Type"=>$RTName2,
13428                            "Old_Value"=>$Symbol,
13429                            "New_Value"=>$NewSym  );
13430                    }
13431                }
13432            }
13433        }
13434        if($Symbol=~/\A(_Z|\?)/)
13435        { # C++
13436            my $Prefix = get_symbol_prefix($Symbol, 1);
13437            if(my @Overloads = sort keys(%{$AddedOverloads{$Prefix}})
13438            and not $AddedOverloads{$Prefix}{get_symbol_suffix($Symbol, 1)})
13439            { # changed signature: params, "const"-qualifier
13440                my $NewSym = $AddedOverloads{$Prefix}{$Overloads[0]};
13441                if($CompleteSignature{1}{$Symbol}{"Constructor"})
13442                {
13443                    if($Symbol=~/(C[1-2][EI])/)
13444                    {
13445                        my $CtorType = $1;
13446                        $NewSym=~s/(C[1-2][EI])/$CtorType/g;
13447                    }
13448                }
13449                elsif($CompleteSignature{1}{$Symbol}{"Destructor"})
13450                {
13451                    if($Symbol=~/(D[0-2][EI])/)
13452                    {
13453                        my $DtorType = $1;
13454                        $NewSym=~s/(D[0-2][EI])/$DtorType/g;
13455                    }
13456                }
13457                my $NS1 = $CompleteSignature{1}{$Symbol}{"NameSpace"};
13458                my $NS2 = $CompleteSignature{2}{$NewSym}{"NameSpace"};
13459                if((not $NS1 and not $NS2) or ($NS1 and $NS2 and $NS1 eq $NS2))
13460                { # from the same class and namespace
13461                    if($CompleteSignature{1}{$Symbol}{"Const"}
13462                    and not $CompleteSignature{2}{$NewSym}{"Const"})
13463                    { # "const" to non-"const"
13464                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Const"}{$tr_name{$Symbol}}}=(
13465                            "Type_Name"=>$TypeInfo{1}{$CompleteSignature{1}{$Symbol}{"Class"}}{"Name"},
13466                            "Target"=>$tr_name{$Symbol},
13467                            "New_Signature"=>get_Signature($NewSym, 2),
13468                            "Old_Value"=>$Symbol,
13469                            "New_Value"=>$NewSym  );
13470                    }
13471                    elsif(not $CompleteSignature{1}{$Symbol}{"Const"}
13472                    and $CompleteSignature{2}{$NewSym}{"Const"})
13473                    { # non-"const" to "const"
13474                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Const"}{$tr_name{$Symbol}}}=(
13475                            "Target"=>$tr_name{$Symbol},
13476                            "New_Signature"=>get_Signature($NewSym, 2),
13477                            "Old_Value"=>$Symbol,
13478                            "New_Value"=>$NewSym  );
13479                    }
13480                    if($CompleteSignature{1}{$Symbol}{"Volatile"}
13481                    and not $CompleteSignature{2}{$NewSym}{"Volatile"})
13482                    { # "volatile" to non-"volatile"
13483
13484                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Volatile"}{$tr_name{$Symbol}}}=(
13485                            "Target"=>$tr_name{$Symbol},
13486                            "New_Signature"=>get_Signature($NewSym, 2),
13487                            "Old_Value"=>$Symbol,
13488                            "New_Value"=>$NewSym  );
13489                    }
13490                    elsif(not $CompleteSignature{1}{$Symbol}{"Volatile"}
13491                    and $CompleteSignature{2}{$NewSym}{"Volatile"})
13492                    { # non-"volatile" to "volatile"
13493                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Volatile"}{$tr_name{$Symbol}}}=(
13494                            "Target"=>$tr_name{$Symbol},
13495                            "New_Signature"=>get_Signature($NewSym, 2),
13496                            "Old_Value"=>$Symbol,
13497                            "New_Value"=>$NewSym  );
13498                    }
13499                    if(get_symbol_suffix($Symbol, 0) ne get_symbol_suffix($NewSym, 0))
13500                    { # params list
13501                        %{$CompatProblems{$Level}{$Symbol}{"Symbol_Changed_Parameters"}{$tr_name{$Symbol}}}=(
13502                            "Target"=>$tr_name{$Symbol},
13503                            "New_Signature"=>get_Signature($NewSym, 2),
13504                            "Old_Value"=>$Symbol,
13505                            "New_Value"=>$NewSym  );
13506                    }
13507                }
13508            }
13509        }
13510    }
13511    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
13512    { # checking symbols
13513        $CurrentSymbol = $Symbol;
13514
13515        my ($SN, $SS, $SV) = separate_symbol($Symbol);
13516        if($Level eq "Source")
13517        { # remove symbol version
13518            $Symbol=$SN;
13519        }
13520        else
13521        { # Binary
13522            if(not $SV)
13523            { # symbol without version
13524                if(my $VSym = $SymVer{1}{$Symbol})
13525                { # the symbol is linked with versioned symbol
13526                    if($CompleteSignature{2}{$VSym}{"MnglName"})
13527                    { # show report for symbol@ver only
13528                        next;
13529                    }
13530                    elsif(not link_symbol($VSym, 2, "-Deps"))
13531                    { # changed version: sym@v1 to sym@v2
13532                      # do NOT show report for symbol
13533                        next;
13534                    }
13535                }
13536            }
13537        }
13538        my $PSymbol = $Symbol;
13539        if($Level eq "Source"
13540        and my $S = $SourceReplacement{$Symbol})
13541        { # take a source-compatible replacement function
13542            $PSymbol = $S;
13543        }
13544        if($CompleteSignature{1}{$Symbol}{"Private"})
13545        { # private symbols
13546            next;
13547        }
13548        if(not defined $CompleteSignature{1}{$Symbol}
13549        or not defined $CompleteSignature{2}{$PSymbol})
13550        { # no info
13551            next;
13552        }
13553        if(not $CompleteSignature{1}{$Symbol}{"MnglName"}
13554        or not $CompleteSignature{2}{$PSymbol}{"MnglName"})
13555        { # no mangled name
13556            next;
13557        }
13558        if(not $CompleteSignature{1}{$Symbol}{"Header"}
13559        or not $CompleteSignature{2}{$PSymbol}{"Header"})
13560        { # without a header
13561            next;
13562        }
13563
13564        if(not $CompleteSignature{1}{$Symbol}{"PureVirt"}
13565        and $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13566        { # became pure
13567            next;
13568        }
13569        if($CompleteSignature{1}{$Symbol}{"PureVirt"}
13570        and not $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13571        { # became non-pure
13572            next;
13573        }
13574
13575        if(not symbolFilter($Symbol, 1, "Affected + InlineVirt", $Level))
13576        { # exported, target, inline virtual and pure virtual
13577            next;
13578        }
13579        if(not symbolFilter($PSymbol, 2, "Affected + InlineVirt", $Level))
13580        { # exported, target, inline virtual and pure virtual
13581            next;
13582        }
13583
13584        if(checkDump(1, "2.13") and checkDump(2, "2.13"))
13585        {
13586            if($CompleteSignature{1}{$Symbol}{"Data"}
13587            and $CompleteSignature{2}{$PSymbol}{"Data"})
13588            {
13589                my $Value1 = $CompleteSignature{1}{$Symbol}{"Value"};
13590                my $Value2 = $CompleteSignature{2}{$PSymbol}{"Value"};
13591                if(defined $Value1)
13592                {
13593                    $Value1 = showVal($Value1, $CompleteSignature{1}{$Symbol}{"Return"}, 1);
13594                    if(defined $Value2)
13595                    {
13596                        $Value2 = showVal($Value2, $CompleteSignature{2}{$PSymbol}{"Return"}, 2);
13597                        if($Value1 ne $Value2)
13598                        {
13599                            %{$CompatProblems{$Level}{$Symbol}{"Global_Data_Value_Changed"}{""}}=(
13600                                "Old_Value"=>$Value1,
13601                                "New_Value"=>$Value2,
13602                                "Target"=>get_Signature($Symbol, 1)  );
13603                        }
13604                    }
13605                }
13606            }
13607        }
13608
13609        if($CompleteSignature{2}{$PSymbol}{"Private"})
13610        {
13611            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Private"}{""}}=(
13612                "Target"=>get_Signature_M($PSymbol, 2)  );
13613        }
13614        elsif(not $CompleteSignature{1}{$Symbol}{"Protected"}
13615        and $CompleteSignature{2}{$PSymbol}{"Protected"})
13616        {
13617            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Protected"}{""}}=(
13618                "Target"=>get_Signature_M($PSymbol, 2)  );
13619        }
13620        elsif($CompleteSignature{1}{$Symbol}{"Protected"}
13621        and not $CompleteSignature{2}{$PSymbol}{"Protected"})
13622        {
13623            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Public"}{""}}=(
13624                "Target"=>get_Signature_M($PSymbol, 2)  );
13625        }
13626
13627        # checking virtual table
13628        mergeVirtualTables($Symbol, $Level);
13629
13630        if($COMPILE_ERRORS)
13631        { # if some errors occurred at the compiling stage
13632          # then some false positives can be skipped here
13633            if(not $CompleteSignature{1}{$Symbol}{"Data"} and $CompleteSignature{2}{$PSymbol}{"Data"}
13634            and not $GlobalDataObject{2}{$Symbol})
13635            { # missed information about parameters in newer version
13636                next;
13637            }
13638            if($CompleteSignature{1}{$Symbol}{"Data"} and not $GlobalDataObject{1}{$Symbol}
13639            and not $CompleteSignature{2}{$PSymbol}{"Data"})
13640            { # missed information about parameters in older version
13641                next;
13642            }
13643        }
13644        my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
13645        # checking attributes
13646        if($CompleteSignature{2}{$PSymbol}{"Static"}
13647        and not $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
13648        {
13649            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Static"}{""}}=(
13650                "Target"=>get_Signature($Symbol, 1)
13651            );
13652        }
13653        elsif(not $CompleteSignature{2}{$PSymbol}{"Static"}
13654        and $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
13655        {
13656            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Static"}{""}}=(
13657                "Target"=>get_Signature($Symbol, 1)
13658            );
13659        }
13660        if(($CompleteSignature{1}{$Symbol}{"Virt"} and $CompleteSignature{2}{$PSymbol}{"Virt"})
13661        or ($CompleteSignature{1}{$Symbol}{"PureVirt"} and $CompleteSignature{2}{$PSymbol}{"PureVirt"}))
13662        { # relative position of virtual and pure virtual methods
13663            if($Level eq "Binary")
13664            {
13665                if(defined $CompleteSignature{1}{$Symbol}{"RelPos"} and defined $CompleteSignature{2}{$PSymbol}{"RelPos"}
13666                and $CompleteSignature{1}{$Symbol}{"RelPos"}!=$CompleteSignature{2}{$PSymbol}{"RelPos"})
13667                { # top-level virtual methods only
13668                    my $Class_Id = $CompleteSignature{1}{$Symbol}{"Class"};
13669                    my $Class_Name = $TypeInfo{1}{$Class_Id}{"Name"};
13670                    if(defined $VirtualTable{1}{$Class_Name} and defined $VirtualTable{2}{$Class_Name}
13671                    and $VirtualTable{1}{$Class_Name}{$Symbol}!=$VirtualTable{2}{$Class_Name}{$Symbol})
13672                    { # check the absolute position of virtual method (including added and removed methods)
13673                        my %Class_Type = get_Type($Class_Id, 1);
13674                        my $ProblemType = "Virtual_Method_Position";
13675                        if($CompleteSignature{1}{$Symbol}{"PureVirt"}) {
13676                            $ProblemType = "Pure_Virtual_Method_Position";
13677                        }
13678                        if(isUsedClass($Class_Id, 1, $Level))
13679                        {
13680                            my @Affected = ($Symbol, keys(%{$OverriddenMethods{1}{$Symbol}}));
13681                            foreach my $ASymbol (@Affected)
13682                            {
13683                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
13684                                    next;
13685                                }
13686                                %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$MnglName}}}=(
13687                                    "Type_Name"=>$Class_Type{"Name"},
13688                                    "Old_Value"=>$CompleteSignature{1}{$Symbol}{"RelPos"},
13689                                    "New_Value"=>$CompleteSignature{2}{$PSymbol}{"RelPos"},
13690                                    "Target"=>get_Signature($Symbol, 1));
13691                            }
13692                            $VTableChanged_M{$Class_Type{"Name"}} = 1;
13693                        }
13694                    }
13695                }
13696            }
13697        }
13698        if($CompleteSignature{1}{$Symbol}{"PureVirt"}
13699        or $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13700        { # do NOT check type changes in pure virtuals
13701            next;
13702        }
13703        $CheckedSymbols{$Level}{$Symbol} = 1;
13704        if($Symbol=~/\A(_Z|\?)/
13705        or keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})==keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}}))
13706        { # C/C++: changes in parameters
13707            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13708            { # checking parameters
13709                mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 1);
13710            }
13711        }
13712        else
13713        { # C: added/removed parameters
13714            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}}))
13715            { # checking added parameters
13716                my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13717                my $PType2_Name = $TypeInfo{2}{$PType2_Id}{"Name"};
13718                last if($PType2_Name eq "...");
13719                my $PName = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
13720                my $PName_Old = (defined $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos})?$CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"}:"";
13721                my $ParamPos_Prev = "-1";
13722                if($PName=~/\Ap\d+\Z/i)
13723                { # added unnamed parameter ( pN )
13724                    my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 1);
13725                    my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 2);
13726                    if($#Positions1==-1 or $#Positions2>$#Positions1) {
13727                        $ParamPos_Prev = "lost";
13728                    }
13729                }
13730                else {
13731                    $ParamPos_Prev = find_ParamPair_Pos_byName($PName, $Symbol, 1);
13732                }
13733                if($ParamPos_Prev eq "lost")
13734                {
13735                    if($ParamPos>keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})-1)
13736                    {
13737                        my $ProblemType = "Added_Parameter";
13738                        if($PName=~/\Ap\d+\Z/) {
13739                            $ProblemType = "Added_Unnamed_Parameter";
13740                        }
13741                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13742                            "Target"=>$PName,
13743                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13744                            "Param_Type"=>$PType2_Name,
13745                            "New_Signature"=>get_Signature($Symbol, 2)  );
13746                    }
13747                    else
13748                    {
13749                        my %ParamType_Pure = get_PureType($PType2_Id, $TypeInfo{2});
13750                        my $PairType_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13751                        my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{1});
13752                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType2_Name eq $TypeInfo{1}{$PairType_Id}{"Name"})
13753                        and find_ParamPair_Pos_byName($PName_Old, $Symbol, 2) eq "lost")
13754                        {
13755                            if($PName_Old!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
13756                            {
13757                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
13758                                    "Target"=>$PName_Old,
13759                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13760                                    "Param_Type"=>$PType2_Name,
13761                                    "Old_Value"=>$PName_Old,
13762                                    "New_Value"=>$PName,
13763                                    "New_Signature"=>get_Signature($Symbol, 2)  );
13764                            }
13765                        }
13766                        else
13767                        {
13768                            my $ProblemType = "Added_Middle_Parameter";
13769                            if($PName=~/\Ap\d+\Z/) {
13770                                $ProblemType = "Added_Middle_Unnamed_Parameter";
13771                            }
13772                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13773                                "Target"=>$PName,
13774                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13775                                "Param_Type"=>$PType2_Name,
13776                                "New_Signature"=>get_Signature($Symbol, 2)  );
13777                        }
13778                    }
13779                }
13780            }
13781            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13782            { # check relevant parameters
13783                my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13784                my $ParamName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
13785                # FIXME: find relevant parameter by name
13786                if(defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos})
13787                {
13788                    my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13789                    my $ParamName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
13790                    if($TypeInfo{1}{$PType1_Id}{"Name"} eq $TypeInfo{2}{$PType2_Id}{"Name"}
13791                    or ($ParamName1!~/\Ap\d+\Z/i and $ParamName1 eq $ParamName2)) {
13792                        mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 0);
13793                    }
13794                }
13795            }
13796            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13797            { # checking removed parameters
13798                my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13799                my $PType1_Name = $TypeInfo{1}{$PType1_Id}{"Name"};
13800                last if($PType1_Name eq "...");
13801                my $PName = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
13802                my $PName_New = (defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos})?$CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}:"";
13803                my $ParamPos_New = "-1";
13804                if($PName=~/\Ap\d+\Z/i)
13805                { # removed unnamed parameter ( pN )
13806                    my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 1);
13807                    my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 2);
13808                    if($#Positions2==-1 or $#Positions2<$#Positions1) {
13809                        $ParamPos_New = "lost";
13810                    }
13811                }
13812                else {
13813                    $ParamPos_New = find_ParamPair_Pos_byName($PName, $Symbol, 2);
13814                }
13815                if($ParamPos_New eq "lost")
13816                {
13817                    if($ParamPos>keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}})-1)
13818                    {
13819                        my $ProblemType = "Removed_Parameter";
13820                        if($PName=~/\Ap\d+\Z/) {
13821                            $ProblemType = "Removed_Unnamed_Parameter";
13822                        }
13823                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13824                            "Target"=>$PName,
13825                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13826                            "Param_Type"=>$PType1_Name,
13827                            "New_Signature"=>get_Signature($Symbol, 2)  );
13828                    }
13829                    elsif($ParamPos<keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})-1)
13830                    {
13831                        my %ParamType_Pure = get_PureType($PType1_Id, $TypeInfo{1});
13832                        my $PairType_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13833                        my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{2});
13834                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType1_Name eq $TypeInfo{2}{$PairType_Id}{"Name"})
13835                        and find_ParamPair_Pos_byName($PName_New, $Symbol, 1) eq "lost")
13836                        {
13837                            if($PName_New!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
13838                            {
13839                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
13840                                    "Target"=>$PName,
13841                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13842                                    "Param_Type"=>$PType1_Name,
13843                                    "Old_Value"=>$PName,
13844                                    "New_Value"=>$PName_New,
13845                                    "New_Signature"=>get_Signature($Symbol, 2)  );
13846                            }
13847                        }
13848                        else
13849                        {
13850                            my $ProblemType = "Removed_Middle_Parameter";
13851                            if($PName=~/\Ap\d+\Z/) {
13852                                $ProblemType = "Removed_Middle_Unnamed_Parameter";
13853                            }
13854                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13855                                "Target"=>$PName,
13856                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13857                                "Param_Type"=>$PType1_Name,
13858                                "New_Signature"=>get_Signature($Symbol, 2)  );
13859                        }
13860                    }
13861                }
13862            }
13863        }
13864        # checking return type
13865        my $ReturnType1_Id = $CompleteSignature{1}{$Symbol}{"Return"};
13866        my $ReturnType2_Id = $CompleteSignature{2}{$PSymbol}{"Return"};
13867        my %RC_SubProblems = detectTypeChange($ReturnType1_Id, $ReturnType2_Id, "Return", $Level);
13868
13869        foreach my $SubProblemType (keys(%RC_SubProblems))
13870        {
13871            my $New_Value = $RC_SubProblems{$SubProblemType}{"New_Value"};
13872            my $Old_Value = $RC_SubProblems{$SubProblemType}{"Old_Value"};
13873            my %ProblemTypes = ();
13874
13875            if($CompleteSignature{1}{$Symbol}{"Data"})
13876            {
13877                if($SubProblemType eq "Return_Type_And_Size") {
13878                    $ProblemTypes{"Global_Data_Type_And_Size"} = 1;
13879                }
13880                elsif($SubProblemType eq "Return_Type_Format") {
13881                    $ProblemTypes{"Global_Data_Type_Format"} = 1;
13882                }
13883                else {
13884                    $ProblemTypes{"Global_Data_Type"} = 1;
13885                }
13886
13887                # quals
13888                if($SubProblemType eq "Return_Type"
13889                or $SubProblemType eq "Return_Type_And_Size"
13890                or $SubProblemType eq "Return_Type_Format")
13891                {
13892                    if(my $RR = removedQual($Old_Value, $New_Value, "const"))
13893                    { # const to non-const
13894                        if($RR==2) {
13895                            $ProblemTypes{"Global_Data_Removed_Const"} = 1;
13896                        }
13897                        else {
13898                            $ProblemTypes{"Global_Data_Became_Non_Const"} = 1;
13899                        }
13900                        $ProblemTypes{"Global_Data_Type"} = 1;
13901                    }
13902                    elsif(my $RA = addedQual($Old_Value, $New_Value, "const"))
13903                    { # non-const to const
13904                        if($RA==2) {
13905                            $ProblemTypes{"Global_Data_Added_Const"} = 1;
13906                        }
13907                        else {
13908                            $ProblemTypes{"Global_Data_Became_Const"} = 1;
13909                        }
13910                        $ProblemTypes{"Global_Data_Type"} = 1;
13911                    }
13912                }
13913            }
13914            else
13915            {
13916                # quals
13917                if($SubProblemType eq "Return_Type"
13918                or $SubProblemType eq "Return_Type_And_Size"
13919                or $SubProblemType eq "Return_Type_Format")
13920                {
13921                    if(checkDump(1, "2.6") and checkDump(2, "2.6"))
13922                    {
13923                        if(addedQual($Old_Value, $New_Value, "volatile"))
13924                        {
13925                            $ProblemTypes{"Return_Value_Became_Volatile"} = 1;
13926                            if($Level ne "Source"
13927                            or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
13928                                $ProblemTypes{"Return_Type"} = 1;
13929                            }
13930                        }
13931                    }
13932                    if(my $RA = addedQual($Old_Value, $New_Value, "const"))
13933                    {
13934                        if($RA==2) {
13935                            $ProblemTypes{"Return_Type_Added_Const"} = 1;
13936                        }
13937                        else {
13938                            $ProblemTypes{"Return_Type_Became_Const"} = 1;
13939                        }
13940                        if($Level ne "Source"
13941                        or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
13942                            $ProblemTypes{"Return_Type"} = 1;
13943                        }
13944                    }
13945                }
13946            }
13947            if($Level eq "Binary"
13948            and not $CompleteSignature{1}{$Symbol}{"Data"})
13949            {
13950                my ($Arch1, $Arch2) = (getArch(1), getArch(2));
13951                if($Arch1 eq "unknown" or $Arch2 eq "unknown")
13952                { # if one of the architectures is unknown
13953                    # then set other arhitecture to unknown too
13954                    ($Arch1, $Arch2) = ("unknown", "unknown");
13955                }
13956                my (%Conv1, %Conv2) = ();
13957                if($UseConv_Real{1}{"R"} and $UseConv_Real{2}{"R"})
13958                {
13959                    %Conv1 = callingConvention_R_Real($CompleteSignature{1}{$Symbol});
13960                    %Conv2 = callingConvention_R_Real($CompleteSignature{2}{$PSymbol});
13961                }
13962                else
13963                {
13964                    %Conv1 = callingConvention_R_Model($CompleteSignature{1}{$Symbol}, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1});
13965                    %Conv2 = callingConvention_R_Model($CompleteSignature{2}{$PSymbol}, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2});
13966                }
13967
13968                if($SubProblemType eq "Return_Type_Became_Void")
13969                {
13970                    if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13971                    { # parameters stack has been affected
13972                        if($Conv1{"Method"} eq "stack") {
13973                            $ProblemTypes{"Return_Type_Became_Void_And_Stack_Layout"} = 1;
13974                        }
13975                        elsif($Conv1{"Hidden"}) {
13976                            $ProblemTypes{"Return_Type_Became_Void_And_Register"} = 1;
13977                        }
13978                    }
13979                }
13980                elsif($SubProblemType eq "Return_Type_From_Void")
13981                {
13982                    if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13983                    { # parameters stack has been affected
13984                        if($Conv2{"Method"} eq "stack") {
13985                            $ProblemTypes{"Return_Type_From_Void_And_Stack_Layout"} = 1;
13986                        }
13987                        elsif($Conv2{"Hidden"}) {
13988                            $ProblemTypes{"Return_Type_From_Void_And_Register"} = 1;
13989                        }
13990                    }
13991                }
13992                elsif($SubProblemType eq "Return_Type"
13993                or $SubProblemType eq "Return_Type_And_Size"
13994                or $SubProblemType eq "Return_Type_Format")
13995                {
13996                    if($Conv1{"Method"} ne $Conv2{"Method"})
13997                    {
13998                        if($Conv1{"Method"} eq "stack")
13999                        { # returns in a register instead of a hidden first parameter
14000                            $ProblemTypes{"Return_Type_From_Stack_To_Register"} = 1;
14001                        }
14002                        else {
14003                            $ProblemTypes{"Return_Type_From_Register_To_Stack"} = 1;
14004                        }
14005                    }
14006                    else
14007                    {
14008                        if($Conv1{"Method"} eq "reg")
14009                        {
14010                            if($Conv1{"Registers"} ne $Conv2{"Registers"})
14011                            {
14012                                if($Conv1{"Hidden"}) {
14013                                    $ProblemTypes{"Return_Type_And_Register_Was_Hidden_Parameter"} = 1;
14014                                }
14015                                elsif($Conv2{"Hidden"}) {
14016                                    $ProblemTypes{"Return_Type_And_Register_Became_Hidden_Parameter"} = 1;
14017                                }
14018                                else {
14019                                    $ProblemTypes{"Return_Type_And_Register"} = 1;
14020                                }
14021                            }
14022                        }
14023                    }
14024                }
14025            }
14026
14027            if(not keys(%ProblemTypes))
14028            { # default
14029                $ProblemTypes{$SubProblemType} = 1;
14030            }
14031
14032            foreach my $ProblemType (keys(%ProblemTypes))
14033            { # additional
14034                $CompatProblems{$Level}{$Symbol}{$ProblemType}{"retval"} = $RC_SubProblems{$SubProblemType};
14035            }
14036        }
14037        if($ReturnType1_Id and $ReturnType2_Id)
14038        {
14039            @RecurTypes = ();
14040            my $Sub_SubProblems = mergeTypes($ReturnType1_Id, $ReturnType2_Id, $Level);
14041
14042            my $AddProblems = {};
14043
14044            if($CompleteSignature{1}{$Symbol}{"Data"})
14045            {
14046                if($Level eq "Binary")
14047                {
14048                    if(get_PLevel($ReturnType1_Id, 1)==0)
14049                    {
14050                        if(defined $Sub_SubProblems->{"DataType_Size"})
14051                        { # add "Global_Data_Size" problem
14052
14053                            foreach my $Loc (keys(%{$Sub_SubProblems->{"DataType_Size"}}))
14054                            {
14055                                if(index($Loc,"->")==-1)
14056                                {
14057                                    if($Loc eq $Sub_SubProblems->{"DataType_Size"}{$Loc}{"Type_Name"})
14058                                    {
14059                                        $AddProblems->{"Global_Data_Size"}{$Loc} = $Sub_SubProblems->{"DataType_Size"}{$Loc}; # add a new problem
14060                                        last;
14061                                    }
14062                                }
14063                            }
14064                        }
14065                    }
14066                    if(not defined $AddProblems->{"Global_Data_Size"})
14067                    {
14068                        if(defined $GlobalDataObject{1}{$Symbol}
14069                        and defined $GlobalDataObject{2}{$Symbol})
14070                        {
14071                            my $Old_Size = $GlobalDataObject{1}{$Symbol};
14072                            my $New_Size = $GlobalDataObject{2}{$Symbol};
14073                            if($Old_Size!=$New_Size)
14074                            {
14075                                $AddProblems->{"Global_Data_Size"}{"retval"} = {
14076                                    "Old_Size"=>$Old_Size*$BYTE_SIZE,
14077                                    "New_Size"=>$New_Size*$BYTE_SIZE };
14078                            }
14079                        }
14080                    }
14081                }
14082            }
14083
14084            foreach my $SubProblemType (keys(%{$AddProblems}))
14085            {
14086                foreach my $SubLocation (keys(%{$AddProblems->{$SubProblemType}}))
14087                {
14088                    my $NewLocation = "retval";
14089                    if($SubLocation and $SubLocation ne "retval") {
14090                        $NewLocation = "retval->".$SubLocation;
14091                    }
14092                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $AddProblems->{$SubProblemType}{$SubLocation};
14093                }
14094            }
14095
14096            foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
14097            {
14098                foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
14099                {
14100                    my $NewLocation = "retval";
14101                    if($SubLocation and $SubLocation ne "retval") {
14102                        $NewLocation = "retval->".$SubLocation;
14103                    }
14104                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
14105                }
14106            }
14107        }
14108
14109        # checking object type
14110        my $ObjTId1 = $CompleteSignature{1}{$Symbol}{"Class"};
14111        my $ObjTId2 = $CompleteSignature{2}{$PSymbol}{"Class"};
14112        if($ObjTId1 and $ObjTId2
14113        and not $CompleteSignature{1}{$Symbol}{"Static"})
14114        {
14115            my $ThisPtr1_Id = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}."*const", 1);
14116            my $ThisPtr2_Id = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}."*const", 2);
14117            if($ThisPtr1_Id and $ThisPtr2_Id)
14118            {
14119                @RecurTypes = ();
14120                my $Sub_SubProblems = mergeTypes($ThisPtr1_Id, $ThisPtr2_Id, $Level);
14121                foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
14122                {
14123                    foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
14124                    {
14125                        my $NewLocation = ($SubLocation)?"this->".$SubLocation:"this";
14126                        $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
14127                    }
14128                }
14129            }
14130        }
14131    }
14132    if($Level eq "Binary") {
14133        mergeVTables($Level);
14134    }
14135    foreach my $Symbol (keys(%{$CompatProblems{$Level}})) {
14136        $CheckedSymbols{$Level}{$Symbol} = 1;
14137    }
14138}
14139
14140sub rmQuals($$)
14141{
14142    my ($Value, $Qual) = @_;
14143    if(not $Qual) {
14144        return $Value;
14145    }
14146    if($Qual eq "all")
14147    { # all quals
14148        $Qual = "const|volatile|restrict";
14149    }
14150    while($Value=~s/\b$Qual\b//) {
14151        $Value = formatName($Value, "T");
14152    }
14153    return $Value;
14154}
14155
14156sub cmpBTypes($$$$)
14157{
14158    my ($T1, $T2, $V1, $V2) = @_;
14159    $T1 = uncover_typedefs($T1, $V1);
14160    $T2 = uncover_typedefs($T2, $V2);
14161    return (rmQuals($T1, "all") eq rmQuals($T2, "all"));
14162}
14163
14164sub addedQual($$$)
14165{
14166    my ($Old_Value, $New_Value, $Qual) = @_;
14167    return removedQual_I($New_Value, $Old_Value, 2, 1, $Qual);
14168}
14169
14170sub removedQual($$$)
14171{
14172    my ($Old_Value, $New_Value, $Qual) = @_;
14173    return removedQual_I($Old_Value, $New_Value, 1, 2, $Qual);
14174}
14175
14176sub removedQual_I($$$$$)
14177{
14178    my ($Old_Value, $New_Value, $V1, $V2, $Qual) = @_;
14179    $Old_Value = uncover_typedefs($Old_Value, $V1);
14180    $New_Value = uncover_typedefs($New_Value, $V2);
14181
14182    if($Old_Value eq $New_Value)
14183    { # equal types
14184        return 0;
14185    }
14186    if($Old_Value!~/\b$Qual\b/)
14187    { # without a qual
14188        return 0;
14189    }
14190    elsif($New_Value!~/\b$Qual\b/)
14191    { # became non-qual
14192        return 1;
14193    }
14194    else
14195    {
14196        my @BQ1 = getQualModel($Old_Value, $Qual);
14197        my @BQ2 = getQualModel($New_Value, $Qual);
14198        foreach (0 .. $#BQ1)
14199        { # removed qual
14200            if($BQ1[$_]==1
14201            and $BQ2[$_]!=1)
14202            {
14203                return 2;
14204            }
14205        }
14206    }
14207    return 0;
14208}
14209
14210sub getQualModel($$)
14211{
14212    my ($Value, $Qual) = @_;
14213    if(not $Qual) {
14214        return $Value;
14215    }
14216
14217    # cleaning
14218    while($Value=~/(\w+)/)
14219    {
14220        my $W = $1;
14221
14222        if($W eq $Qual) {
14223            $Value=~s/\b$W\b/\@/g;
14224        }
14225        else {
14226            $Value=~s/\b$W\b//g;
14227        }
14228    }
14229
14230    $Value=~s/\@/$Qual/g;
14231    $Value=~s/[^\*\&\w]+//g;
14232
14233    # modeling
14234    # int*const*const == 011
14235    # int**const == 001
14236    my @Model = ();
14237    my @Elems = split(/[\*\&]/, $Value);
14238    if(not @Elems) {
14239        return (0);
14240    }
14241    foreach (@Elems)
14242    {
14243        if($_ eq $Qual) {
14244            push(@Model, 1);
14245        }
14246        else {
14247            push(@Model, 0);
14248        }
14249    }
14250
14251    return @Model;
14252}
14253
14254my %StringTypes = map {$_=>1} (
14255    "char*",
14256    "char const*"
14257);
14258
14259my %CharTypes = map {$_=>1} (
14260    "char",
14261    "char const"
14262);
14263
14264sub showVal($$$)
14265{
14266    my ($Value, $TypeId, $LibVersion) = @_;
14267    my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion});
14268    my $TName = uncover_typedefs($PureType{"Name"}, $LibVersion);
14269    if(substr($Value, 0, 2) eq "_Z")
14270    {
14271        if(my $Unmangled = $tr_name{$Value}) {
14272            return $Unmangled;
14273        }
14274    }
14275    elsif(defined $StringTypes{$TName} or $TName=~/string/i)
14276    { # strings
14277        return "\"$Value\"";
14278    }
14279    elsif(defined $CharTypes{$TName})
14280    { # characters
14281        return "\'$Value\'";
14282    }
14283    if($Value eq "")
14284    { # other
14285        return "\'\'";
14286    }
14287    return $Value;
14288}
14289
14290sub getRegs($$$)
14291{
14292    my ($LibVersion, $Symbol, $Pos) = @_;
14293
14294    if(defined $CompleteSignature{$LibVersion}{$Symbol}{"Reg"})
14295    {
14296        my %Regs = ();
14297        foreach my $Elem (sort keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}}))
14298        {
14299            if($Elem=~/\A$Pos([\.\+]|\Z)/) {
14300                $Regs{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}{$Elem}} = 1;
14301            }
14302        }
14303
14304        return join(", ", sort keys(%Regs));
14305    }
14306    elsif(defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}
14307    and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{0}
14308    and not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{0}{"offset"})
14309    {
14310        return "unknown";
14311    }
14312
14313    return undef;
14314}
14315
14316sub mergeParameters($$$$$$)
14317{
14318    my ($Symbol, $PSymbol, $ParamPos1, $ParamPos2, $Level, $ChkRnmd) = @_;
14319    if(not $Symbol) {
14320        return;
14321    }
14322    my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"type"};
14323    my $PName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"name"};
14324    my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"type"};
14325    my $PName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"name"};
14326    if(not $PType1_Id
14327    or not $PType2_Id) {
14328        return;
14329    }
14330
14331    if($Symbol=~/\A(_Z|\?)/)
14332    { # do not merge "this"
14333        if($PName1 eq "this" or $PName2 eq "this") {
14334            return;
14335        }
14336    }
14337
14338    my %Type1 = get_Type($PType1_Id, 1);
14339    my %Type2 = get_Type($PType2_Id, 2);
14340
14341    my %PureType1 = get_PureType($PType1_Id, $TypeInfo{1});
14342
14343    my %BaseType1 = get_BaseType($PType1_Id, 1);
14344    my %BaseType2 = get_BaseType($PType2_Id, 2);
14345
14346    my $Parameter_Location = ($PName1)?$PName1:showPos($ParamPos1)." Parameter";
14347
14348    if($Level eq "Binary")
14349    {
14350        if(checkDump(1, "2.6.1") and checkDump(2, "2.6.1"))
14351        { # "reg" attribute added in ACC 1.95.1 (dump 2.6.1 format)
14352            if($CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
14353            and not $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
14354            {
14355                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Non_Register"}{$Parameter_Location}}=(
14356                    "Target"=>$PName1,
14357                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
14358            }
14359            elsif(not $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
14360            and $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
14361            {
14362                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Register"}{$Parameter_Location}}=(
14363                    "Target"=>$PName1,
14364                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
14365            }
14366        }
14367
14368        if(defined $UsedDump{1}{"DWARF"}
14369        and defined $UsedDump{2}{"DWARF"})
14370        {
14371            if(checkDump(1, "3.0") and checkDump(2, "3.0"))
14372            {
14373                my $Old_Regs = getRegs(1, $Symbol, $ParamPos1);
14374                my $New_Regs = getRegs(2, $PSymbol, $ParamPos2);
14375
14376                if($Old_Regs ne "unknown"
14377                and $New_Regs ne "unknown")
14378                {
14379                    if($Old_Regs and $New_Regs)
14380                    {
14381                        if($Old_Regs ne $New_Regs)
14382                        {
14383                            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Register"}{$Parameter_Location}}=(
14384                                "Target"=>$PName1,
14385                                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14386                                "Old_Value"=>$Old_Regs,
14387                                "New_Value"=>$New_Regs  );
14388                        }
14389                    }
14390                    elsif($Old_Regs and not $New_Regs)
14391                    {
14392                        %{$CompatProblems{$Level}{$Symbol}{"Parameter_From_Register"}{$Parameter_Location}}=(
14393                            "Target"=>$PName1,
14394                            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14395                            "Old_Value"=>$Old_Regs  );
14396                    }
14397                    elsif(not $Old_Regs and $New_Regs)
14398                    {
14399                        %{$CompatProblems{$Level}{$Symbol}{"Parameter_To_Register"}{$Parameter_Location}}=(
14400                            "Target"=>$PName1,
14401                            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14402                            "New_Value"=>$New_Regs  );
14403                    }
14404                }
14405
14406                if((my $Old_Offset = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"offset"}) ne ""
14407                and (my $New_Offset = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"offset"}) ne "")
14408                {
14409                    if($Old_Offset ne $New_Offset)
14410                    {
14411                        my $Start1 = $CompleteSignature{1}{$Symbol}{"Param"}{0}{"offset"};
14412                        my $Start2 = $CompleteSignature{2}{$Symbol}{"Param"}{0}{"offset"};
14413
14414                        $Old_Offset = $Old_Offset - $Start1;
14415                        $New_Offset = $New_Offset - $Start2;
14416
14417                        if($Old_Offset ne $New_Offset)
14418                        {
14419                            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Offset"}{$Parameter_Location}}=(
14420                                "Target"=>$PName1,
14421                                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14422                                "Old_Value"=>$Old_Offset,
14423                                "New_Value"=>$New_Offset  );
14424                        }
14425                    }
14426                }
14427            }
14428        }
14429    }
14430    if(checkDump(1, "2.0") and checkDump(2, "2.0")
14431    and $UsedDump{1}{"V"} ne "3.1" and $UsedDump{2}{"V"} ne "3.1")
14432    { # "default" attribute added in ACC 1.22 (dump 2.0 format)
14433      # broken in 3.1, fixed in 3.2
14434        my $Value_Old = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"default"};
14435        my $Value_New = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"default"};
14436        if(not checkDump(1, "2.13")
14437        and checkDump(2, "2.13"))
14438        { # support for old ABI dumps
14439            if(defined $Value_Old and defined $Value_New)
14440            {
14441                if($PureType1{"Name"} eq "bool"
14442                and $Value_Old eq "false" and $Value_New eq "0")
14443                { # int class::method ( bool p = 0 );
14444                  # old ABI dumps: "false"
14445                  # new ABI dumps: "0"
14446                    $Value_Old = "0";
14447                }
14448            }
14449        }
14450        if(not checkDump(1, "2.18")
14451        and checkDump(2, "2.18"))
14452        { # support for old ABI dumps
14453            if(not defined $Value_Old
14454            and substr($Value_New, 0, 2) eq "_Z") {
14455                $Value_Old = $Value_New;
14456            }
14457        }
14458        if(defined $Value_Old)
14459        {
14460            $Value_Old = showVal($Value_Old, $PType1_Id, 1);
14461            if(defined $Value_New)
14462            {
14463                $Value_New = showVal($Value_New, $PType2_Id, 2);
14464                if($Value_Old ne $Value_New)
14465                { # FIXME: how to distinguish "0" and 0 (NULL)
14466                    %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Changed"}{$Parameter_Location}}=(
14467                        "Target"=>$PName1,
14468                        "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14469                        "Old_Value"=>$Value_Old,
14470                        "New_Value"=>$Value_New  );
14471                }
14472            }
14473            else
14474            {
14475                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Removed"}{$Parameter_Location}}=(
14476                    "Target"=>$PName1,
14477                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14478                    "Old_Value"=>$Value_Old  );
14479            }
14480        }
14481        elsif(defined $Value_New)
14482        {
14483            $Value_New = showVal($Value_New, $PType2_Id, 2);
14484            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Added"}{$Parameter_Location}}=(
14485                "Target"=>$PName1,
14486                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14487                "New_Value"=>$Value_New  );
14488        }
14489    }
14490
14491    if($ChkRnmd)
14492    {
14493        if($PName1 and $PName2 and $PName1 ne $PName2
14494        and $PType1_Id!=-1 and $PType2_Id!=-1
14495        and $PName1!~/\Ap\d+\Z/ and $PName2!~/\Ap\d+\Z/)
14496        { # except unnamed "..." value list (Id=-1)
14497            %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos1)." Parameter"}}=(
14498                "Target"=>$PName1,
14499                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14500                "Param_Type"=>$TypeInfo{1}{$PType1_Id}{"Name"},
14501                "Old_Value"=>$PName1,
14502                "New_Value"=>$PName2,
14503                "New_Signature"=>get_Signature($Symbol, 2)  );
14504        }
14505    }
14506
14507    # checking type change (replace)
14508    my %SubProblems = detectTypeChange($PType1_Id, $PType2_Id, "Parameter", $Level);
14509
14510    foreach my $SubProblemType (keys(%SubProblems))
14511    { # add new problems, remove false alarms
14512        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14513        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14514
14515        # quals
14516        if($SubProblemType eq "Parameter_Type"
14517        or $SubProblemType eq "Parameter_Type_And_Size"
14518        or $SubProblemType eq "Parameter_Type_Format")
14519        {
14520            if(checkDump(1, "2.6") and checkDump(2, "2.6"))
14521            {
14522                if(addedQual($Old_Value, $New_Value, "restrict")) {
14523                    %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}};
14524                }
14525                elsif(removedQual($Old_Value, $New_Value, "restrict")) {
14526                    %{$SubProblems{"Parameter_Became_Non_Restrict"}} = %{$SubProblems{$SubProblemType}};
14527                }
14528            }
14529            if(checkDump(1, "2.6") and checkDump(2, "2.6"))
14530            {
14531                if(removedQual($Old_Value, $New_Value, "volatile")) {
14532                    %{$SubProblems{"Parameter_Became_Non_Volatile"}} = %{$SubProblems{$SubProblemType}};
14533                }
14534            }
14535            if($Type2{"Type"} eq "Const" and $BaseType2{"Name"} eq $Type1{"Name"}
14536            and $Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
14537            { # int to "int const"
14538                delete($SubProblems{$SubProblemType});
14539            }
14540            elsif($Type1{"Type"} eq "Const" and $BaseType1{"Name"} eq $Type2{"Name"}
14541            and $Type2{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
14542            { # "int const" to int
14543                delete($SubProblems{$SubProblemType});
14544            }
14545            elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
14546            { # "const" to non-"const"
14547                if($RR==2) {
14548                    %{$SubProblems{"Parameter_Removed_Const"}} = %{$SubProblems{$SubProblemType}};
14549                }
14550                else {
14551                    %{$SubProblems{"Parameter_Became_Non_Const"}} = %{$SubProblems{$SubProblemType}};
14552                }
14553            }
14554        }
14555    }
14556
14557    if($Level eq "Source")
14558    {
14559        foreach my $SubProblemType (keys(%SubProblems))
14560        {
14561            my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14562            my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14563
14564            if($SubProblemType eq "Parameter_Type")
14565            {
14566                if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
14567                    delete($SubProblems{$SubProblemType});
14568                }
14569            }
14570        }
14571    }
14572
14573    foreach my $SubProblemType (keys(%SubProblems))
14574    { # modify/register problems
14575        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14576        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14577        my $New_Size = $SubProblems{$SubProblemType}{"New_Size"};
14578        my $Old_Size = $SubProblems{$SubProblemType}{"Old_Size"};
14579
14580        my $NewProblemType = $SubProblemType;
14581        if($Old_Value eq "..." and $New_Value ne "...")
14582        { # change from "..." to "int"
14583            if($ParamPos1==0)
14584            { # ISO C requires a named argument before "..."
14585                next;
14586            }
14587            $NewProblemType = "Parameter_Became_Non_VaList";
14588        }
14589        elsif($New_Value eq "..." and $Old_Value ne "...")
14590        { # change from "int" to "..."
14591            if($ParamPos2==0)
14592            { # ISO C requires a named argument before "..."
14593                next;
14594            }
14595            $NewProblemType = "Parameter_Became_VaList";
14596        }
14597        elsif($Level eq "Binary" and ($SubProblemType eq "Parameter_Type_And_Size"
14598        or $SubProblemType eq "Parameter_Type" or $SubProblemType eq "Parameter_Type_Format"))
14599        {
14600            my ($Arch1, $Arch2) = (getArch(1), getArch(2));
14601            if($Arch1 eq "unknown"
14602            or $Arch2 eq "unknown")
14603            { # if one of the architectures is unknown
14604              # then set other arhitecture to unknown too
14605                ($Arch1, $Arch2) = ("unknown", "unknown");
14606            }
14607            my (%Conv1, %Conv2) = ();
14608            if($UseConv_Real{1}{"P"} and $UseConv_Real{2}{"P"})
14609            { # real
14610                %Conv1 = callingConvention_P_Real($CompleteSignature{1}{$Symbol}, $ParamPos1);
14611                %Conv2 = callingConvention_P_Real($CompleteSignature{2}{$Symbol}, $ParamPos2);
14612            }
14613            else
14614            { # model
14615                %Conv1 = callingConvention_P_Model($CompleteSignature{1}{$Symbol}, $ParamPos1, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1});
14616                %Conv2 = callingConvention_P_Model($CompleteSignature{2}{$Symbol}, $ParamPos2, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2});
14617            }
14618            if($Conv1{"Method"} eq $Conv2{"Method"})
14619            {
14620                if($Conv1{"Method"} eq "stack")
14621                {
14622                    if($Old_Size ne $New_Size) { # FIXME: isMemPadded, getOffset
14623                        $NewProblemType = "Parameter_Type_And_Stack";
14624                    }
14625                }
14626                elsif($Conv1{"Method"} eq "reg")
14627                {
14628                    if($Conv1{"Registers"} ne $Conv2{"Registers"}) {
14629                        $NewProblemType = "Parameter_Type_And_Register";
14630                    }
14631                }
14632            }
14633            elsif($Conv1{"Method"} ne "unknown"
14634            and $Conv2{"Method"} ne "unknown")
14635            {
14636                if($Conv1{"Method"} eq "stack") {
14637                    $NewProblemType = "Parameter_Type_From_Stack_To_Register";
14638                }
14639                elsif($Conv1{"Method"} eq "register") {
14640                    $NewProblemType = "Parameter_Type_From_Register_To_Stack";
14641                }
14642            }
14643            $SubProblems{$SubProblemType}{"Old_Reg"} = $Conv1{"Registers"};
14644            $SubProblems{$SubProblemType}{"New_Reg"} = $Conv2{"Registers"};
14645        }
14646        %{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}=(
14647            "Target"=>$PName1,
14648            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14649            "New_Signature"=>get_Signature($Symbol, 2) );
14650        @{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}};
14651    }
14652
14653    @RecurTypes = ();
14654
14655    # checking type definition changes
14656    my $Sub_SubProblems = mergeTypes($PType1_Id, $PType2_Id, $Level);
14657    foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
14658    {
14659        foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
14660        {
14661            my $NewProblemType = $SubProblemType;
14662            if($SubProblemType eq "DataType_Size")
14663            {
14664                if($PureType1{"Type"}!~/\A(Pointer|Ref)\Z/ and $SubLocation!~/\-\>/)
14665                { # stack has been affected
14666                    $NewProblemType = "DataType_Size_And_Stack";
14667                }
14668            }
14669            my $NewLocation = ($SubLocation)?$Parameter_Location."->".$SubLocation:$Parameter_Location;
14670            $CompatProblems{$Level}{$Symbol}{$NewProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
14671        }
14672    }
14673}
14674
14675sub find_ParamPair_Pos_byName($$$)
14676{
14677    my ($Name, $Symbol, $LibVersion) = @_;
14678    foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
14679    {
14680        next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos});
14681        if($CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"name"} eq $Name)
14682        {
14683            return $ParamPos;
14684        }
14685    }
14686    return "lost";
14687}
14688
14689sub find_ParamPair_Pos_byTypeAndPos($$$$$)
14690{
14691    my ($TypeName, $MediumPos, $Order, $Symbol, $LibVersion) = @_;
14692    my @Positions = ();
14693    foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
14694    {
14695        next if($Order eq "backward" and $ParamPos>$MediumPos);
14696        next if($Order eq "forward" and $ParamPos<$MediumPos);
14697        next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos});
14698        my $PTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"type"};
14699        if($TypeInfo{$LibVersion}{$PTypeId}{"Name"} eq $TypeName) {
14700            push(@Positions, $ParamPos);
14701        }
14702    }
14703    return @Positions;
14704}
14705
14706sub getTypeIdByName($$)
14707{
14708    my ($TypeName, $LibVersion) = @_;
14709    return $TName_Tid{$LibVersion}{formatName($TypeName, "T")};
14710}
14711
14712sub diffTypes($$$)
14713{
14714    if(defined $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]}) {
14715        return $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]};
14716    }
14717    if(isRecurType($_[0], $_[1], \@RecurTypes_Diff))
14718    { # skip recursive declarations
14719        return 0;
14720    }
14721
14722    pushType($_[0], $_[1], \@RecurTypes_Diff);
14723    my $Diff = diffTypes_I(@_);
14724    pop(@RecurTypes_Diff);
14725
14726    return ($Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]} = $Diff);
14727}
14728
14729sub diffTypes_I($$$)
14730{
14731    my ($Type1_Id, $Type2_Id, $Level) = @_;
14732
14733    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
14734    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
14735
14736    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
14737    { # equal types
14738        return 0;
14739    }
14740    if($Type1_Pure{"Name"} eq "void")
14741    { # from void* to something
14742        return 0;
14743    }
14744    if($Type2_Pure{"Name"} eq "void")
14745    { # from something to void*
14746        return 0;
14747    }
14748    if($Type1_Pure{"Name"}=~/\*/
14749    or $Type2_Pure{"Name"}=~/\*/)
14750    { # compared in detectTypeChange()
14751        return 0;
14752    }
14753
14754    my %FloatType = map {$_=>1} (
14755        "float",
14756        "double",
14757        "long double"
14758    );
14759
14760    my $T1 = $Type1_Pure{"Type"};
14761    my $T2 = $Type2_Pure{"Type"};
14762
14763    if($T1 eq "Struct"
14764    and $T2 eq "Class")
14765    { # compare as data structures
14766        $T2 = "Struct";
14767    }
14768
14769    if($T1 eq "Class"
14770    and $T2 eq "Struct")
14771    { # compare as data structures
14772        $T1 = "Struct";
14773    }
14774
14775    if($T1 ne $T2)
14776    { # different types
14777        if($T1 eq "Intrinsic"
14778        and $T2 eq "Enum")
14779        { # "int" to "enum"
14780            return 0;
14781        }
14782        elsif($T2 eq "Intrinsic"
14783        and $T1 eq "Enum")
14784        { # "enum" to "int"
14785            return 0;
14786        }
14787        else
14788        { # union to struct
14789          #  ...
14790            return 1;
14791        }
14792    }
14793    else
14794    {
14795        if($T1 eq "Intrinsic")
14796        {
14797            if($FloatType{$Type1_Pure{"Name"}}
14798            or $FloatType{$Type2_Pure{"Name"}})
14799            { # "float" to "double"
14800              # "float" to "int"
14801                if($Level eq "Source")
14802                { # Safe
14803                    return 0;
14804                }
14805                else {
14806                    return 1;
14807                }
14808            }
14809        }
14810        elsif($T1=~/Class|Struct|Union|Enum/)
14811        {
14812            my @Membs1 = keys(%{$Type1_Pure{"Memb"}});
14813            my @Membs2 = keys(%{$Type2_Pure{"Memb"}});
14814            if(not @Membs1
14815            or not @Membs2)
14816            { # private
14817                return 0;
14818            }
14819            if($#Membs1!=$#Membs2)
14820            { # different number of elements
14821                return 1;
14822            }
14823            if($T1 eq "Enum")
14824            {
14825                foreach my $Pos (@Membs1)
14826                { # compare elements by name and value
14827                    if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"}
14828                    or $Type1_Pure{"Memb"}{$Pos}{"value"} ne $Type2_Pure{"Memb"}{$Pos}{"value"})
14829                    { # different names
14830                        return 1;
14831                    }
14832                }
14833            }
14834            else
14835            {
14836                foreach my $Pos (@Membs1)
14837                {
14838                    if($Level eq "Source")
14839                    {
14840                        if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"})
14841                        { # different names
14842                            return 1;
14843                        }
14844                    }
14845
14846                    my %MT1 = %{$TypeInfo{1}{$Type1_Pure{"Memb"}{$Pos}{"type"}}};
14847                    my %MT2 = %{$TypeInfo{2}{$Type2_Pure{"Memb"}{$Pos}{"type"}}};
14848
14849                    if($MT1{"Name"} ne $MT2{"Name"}
14850                    or isAnon($MT1{"Name"}) or isAnon($MT2{"Name"}))
14851                    {
14852                        my $PL1 = get_PLevel($MT1{"Tid"}, 1);
14853                        my $PL2 = get_PLevel($MT2{"Tid"}, 2);
14854
14855                        if($PL1 ne $PL2)
14856                        { # different pointer level
14857                            return 1;
14858                        }
14859
14860                        # compare base types
14861                        my %BT1 = get_BaseType($MT1{"Tid"}, 1);
14862                        my %BT2 = get_BaseType($MT2{"Tid"}, 2);
14863
14864                        if(diffTypes($BT1{"Tid"}, $BT2{"Tid"}, $Level))
14865                        { # different types
14866                            return 1;
14867                        }
14868                    }
14869                }
14870            }
14871        }
14872        else
14873        {
14874            # TODO: arrays, etc.
14875        }
14876    }
14877    return 0;
14878}
14879
14880sub detectTypeChange($$$$)
14881{
14882    my ($Type1_Id, $Type2_Id, $Prefix, $Level) = @_;
14883    if(not $Type1_Id or not $Type2_Id) {
14884        return ();
14885    }
14886    my %LocalProblems = ();
14887    my %Type1 = get_Type($Type1_Id, 1);
14888    my %Type2 = get_Type($Type2_Id, 2);
14889    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
14890    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
14891
14892    if(defined $SkipTypedefUncover)
14893    {
14894        if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}) {
14895            return ();
14896        }
14897
14898        if(cmpBTypes($Type1_Pure{"Name"}, $Type2_Pure{"Name"}, 1, 2)) {
14899            return ();
14900        }
14901    }
14902
14903    my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type1_Pure{"Tid"}, $TypeInfo{1}):get_BaseType($Type1_Id, 1);
14904    my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type2_Pure{"Tid"}, $TypeInfo{2}):get_BaseType($Type2_Id, 2);
14905
14906    if(defined $UsedDump{1}{"DWARF"})
14907    {
14908        if($Type1_Pure{"Name"} eq "__unknown__"
14909        or $Type2_Pure{"Name"} eq "__unknown__"
14910        or $Type1_Base{"Name"} eq "__unknown__"
14911        or $Type2_Base{"Name"} eq "__unknown__")
14912        { # Error ABI dump
14913            return ();
14914        }
14915    }
14916
14917    my $Type1_PLevel = get_PLevel($Type1_Id, 1);
14918    my $Type2_PLevel = get_PLevel($Type2_Id, 2);
14919    return () if(not $Type1{"Name"} or not $Type2{"Name"});
14920    return () if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"});
14921    return () if($Type1_PLevel eq "" or $Type2_PLevel eq "");
14922    if($Type1_Base{"Name"} ne $Type2_Base{"Name"}
14923    and ($Type1{"Name"} eq $Type2{"Name"} or ($Type1_PLevel>=1 and $Type1_PLevel==$Type2_PLevel
14924    and $Type1_Base{"Name"} ne "void" and $Type2_Base{"Name"} ne "void")))
14925    { # base type change
14926        if($Type1{"Name"} eq $Type2{"Name"})
14927        {
14928            if($Type1{"Type"} eq "Typedef" and $Type2{"Type"} eq "Typedef")
14929            { # will be reported in mergeTypes() as typedef problem
14930                return ();
14931            }
14932            my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
14933            my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
14934            if(%Typedef_1 and %Typedef_2)
14935            {
14936                if($Typedef_1{"Name"} eq $Typedef_2{"Name"}
14937                and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef")
14938                { # const Typedef
14939                    return ();
14940                }
14941            }
14942        }
14943        if($Type1_Base{"Name"}!~/anon\-/ and $Type2_Base{"Name"}!~/anon\-/)
14944        {
14945            if($Level eq "Binary"
14946            and $Type1_Base{"Size"} and $Type2_Base{"Size"}
14947            and $Type1_Base{"Size"} ne $Type2_Base{"Size"})
14948            {
14949                %{$LocalProblems{$Prefix."_BaseType_And_Size"}}=(
14950                    "Old_Value"=>$Type1_Base{"Name"},
14951                    "New_Value"=>$Type2_Base{"Name"},
14952                    "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14953                    "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14954            }
14955            else
14956            {
14957                if(diffTypes($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Level))
14958                { # format change
14959                    %{$LocalProblems{$Prefix."_BaseType_Format"}}=(
14960                        "Old_Value"=>$Type1_Base{"Name"},
14961                        "New_Value"=>$Type2_Base{"Name"},
14962                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14963                        "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14964                }
14965                elsif(tNameLock($Type1_Base{"Tid"}, $Type2_Base{"Tid"}))
14966                {
14967                    %{$LocalProblems{$Prefix."_BaseType"}}=(
14968                        "Old_Value"=>$Type1_Base{"Name"},
14969                        "New_Value"=>$Type2_Base{"Name"},
14970                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14971                        "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14972                }
14973            }
14974        }
14975    }
14976    elsif($Type1{"Name"} ne $Type2{"Name"})
14977    { # type change
14978        if($Type1{"Name"}!~/anon\-/ and $Type2{"Name"}!~/anon\-/)
14979        {
14980            if($Prefix eq "Return"
14981            and $Type1_Pure{"Name"} eq "void")
14982            {
14983                %{$LocalProblems{"Return_Type_From_Void"}}=(
14984                    "New_Value"=>$Type2{"Name"},
14985                    "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14986            }
14987            elsif($Prefix eq "Return"
14988            and $Type2_Pure{"Name"} eq "void")
14989            {
14990                %{$LocalProblems{"Return_Type_Became_Void"}}=(
14991                    "Old_Value"=>$Type1{"Name"},
14992                    "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE);
14993            }
14994            else
14995            {
14996                if($Level eq "Binary"
14997                and $Type1{"Size"} and $Type2{"Size"}
14998                and $Type1{"Size"} ne $Type2{"Size"})
14999                {
15000                    %{$LocalProblems{$Prefix."_Type_And_Size"}}=(
15001                        "Old_Value"=>$Type1{"Name"},
15002                        "New_Value"=>$Type2{"Name"},
15003                        "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
15004                        "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
15005                }
15006                else
15007                {
15008                    if(diffTypes($Type1_Id, $Type2_Id, $Level))
15009                    { # format change
15010                        %{$LocalProblems{$Prefix."_Type_Format"}}=(
15011                            "Old_Value"=>$Type1{"Name"},
15012                            "New_Value"=>$Type2{"Name"},
15013                            "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
15014                            "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
15015                    }
15016                    elsif(tNameLock($Type1_Id, $Type2_Id))
15017                    { # FIXME: correct this condition
15018                        %{$LocalProblems{$Prefix."_Type"}}=(
15019                            "Old_Value"=>$Type1{"Name"},
15020                            "New_Value"=>$Type2{"Name"},
15021                            "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
15022                            "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
15023                    }
15024                }
15025            }
15026        }
15027    }
15028    if($Type1_PLevel!=$Type2_PLevel)
15029    {
15030        if($Type1{"Name"} ne "void" and $Type1{"Name"} ne "..."
15031        and $Type2{"Name"} ne "void" and $Type2{"Name"} ne "...")
15032        {
15033            if($Level eq "Source")
15034            {
15035                %{$LocalProblems{$Prefix."_PointerLevel"}}=(
15036                    "Old_Value"=>$Type1_PLevel,
15037                    "New_Value"=>$Type2_PLevel);
15038            }
15039            else
15040            {
15041                if($Type2_PLevel>$Type1_PLevel)
15042                {
15043                    %{$LocalProblems{$Prefix."_PointerLevel_Increased"}}=(
15044                        "Old_Value"=>$Type1_PLevel,
15045                        "New_Value"=>$Type2_PLevel);
15046                }
15047                else
15048                {
15049                    %{$LocalProblems{$Prefix."_PointerLevel_Decreased"}}=(
15050                        "Old_Value"=>$Type1_PLevel,
15051                        "New_Value"=>$Type2_PLevel);
15052                }
15053            }
15054        }
15055    }
15056    if($Type1_Pure{"Type"} eq "Array"
15057    and $Type1_Pure{"BaseType"})
15058    { # base_type[N] -> base_type[N]
15059      # base_type: older_structure -> typedef to newer_structure
15060        my %SubProblems = detectTypeChange($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Prefix, $Level);
15061        foreach my $SubProblemType (keys(%SubProblems))
15062        {
15063            $SubProblemType=~s/_Type/_BaseType/g;
15064            next if(defined $LocalProblems{$SubProblemType});
15065            foreach my $Attr (keys(%{$SubProblems{$SubProblemType}})) {
15066                $LocalProblems{$SubProblemType}{$Attr} = $SubProblems{$SubProblemType}{$Attr};
15067            }
15068        }
15069    }
15070    return %LocalProblems;
15071}
15072
15073sub tNameLock($$)
15074{
15075    my ($Tid1, $Tid2) = @_;
15076    my $Changed = 0;
15077    if(differentDumps("G"))
15078    { # different GCC versions
15079        $Changed = 1;
15080    }
15081    elsif(differentDumps("V"))
15082    { # different versions of ABI dumps
15083        if(not checkDump(1, "2.20")
15084        or not checkDump(2, "2.20"))
15085        { # latest names update
15086          # 2.6: added restrict qualifier
15087          # 2.13: added missed typedefs to qualified types
15088          # 2.20: prefix for struct, union and enum types
15089            $Changed = 1;
15090        }
15091    }
15092
15093    my $TN1 = $TypeInfo{1}{$Tid1}{"Name"};
15094    my $TN2 = $TypeInfo{2}{$Tid2}{"Name"};
15095
15096    my $TT1 = $TypeInfo{1}{$Tid1}{"Type"};
15097    my $TT2 = $TypeInfo{2}{$Tid2}{"Type"};
15098
15099    if($Changed)
15100    { # different formats
15101        my %Base1 = get_Type($Tid1, 1);
15102        while(defined $Base1{"Type"} and $Base1{"Type"} eq "Typedef") {
15103            %Base1 = get_OneStep_BaseType($Base1{"Tid"}, $TypeInfo{1});
15104        }
15105        my %Base2 = get_Type($Tid2, 2);
15106        while(defined $Base2{"Type"} and $Base2{"Type"} eq "Typedef") {
15107            %Base2 = get_OneStep_BaseType($Base2{"Tid"}, $TypeInfo{2});
15108        }
15109        my $BName1 = uncover_typedefs($Base1{"Name"}, 1);
15110        my $BName2 = uncover_typedefs($Base2{"Name"}, 2);
15111        if($BName1 eq $BName2)
15112        { # equal base types
15113            return 0;
15114        }
15115
15116        if(not checkDump(1, "2.13")
15117        or not checkDump(2, "2.13"))
15118        { # broken array names in ABI dumps < 2.13
15119            if($TT1 eq "Array"
15120            and $TT2 eq "Array") {
15121                return 0;
15122            }
15123        }
15124
15125        if(not checkDump(1, "2.6")
15126        or not checkDump(2, "2.6"))
15127        { # added restrict attribute in 2.6
15128            if($TN1!~/\brestrict\b/
15129            and $TN2=~/\brestrict\b/) {
15130                return 0;
15131            }
15132        }
15133
15134        if(not checkDump(1, "2.20")
15135        or not checkDump(2, "2.20"))
15136        { # added type prefix in 2.20
15137            if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/
15138            or $TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) {
15139                return 0;
15140            }
15141        }
15142    }
15143    else
15144    {
15145        # typedef struct {...} type_t
15146        # typedef struct type_t {...} type_t
15147        if(index($TN1, " ".$TN2)!=-1)
15148        {
15149            if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/) {
15150                return 0;
15151            }
15152        }
15153        if(index($TN2, " ".$TN1)!=-1)
15154        {
15155            if($TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) {
15156                return 0;
15157            }
15158        }
15159
15160        if($TT1 eq "FuncPtr"
15161        and $TT2 eq "FuncPtr")
15162        {
15163            my $TN1_C = $TN1;
15164            my $TN2_C = $TN2;
15165
15166            $TN1_C=~s/\b(struct|union) //g;
15167            $TN2_C=~s/\b(struct|union) //g;
15168
15169            if($TN1_C eq $TN2_C) {
15170                return 0;
15171            }
15172        }
15173    }
15174
15175    my ($N1, $N2) = ($TN1, $TN2);
15176    $N1=~s/\b(struct|union) //g;
15177    $N2=~s/\b(struct|union) //g;
15178
15179    if($N1 eq $N2)
15180    { # QList<struct QUrl> and QList<QUrl>
15181        return 0;
15182    }
15183
15184    return 1;
15185}
15186
15187sub differentDumps($)
15188{
15189    my $Check = $_[0];
15190    if(defined $Cache{"differentDumps"}{$Check}) {
15191        return $Cache{"differentDumps"}{$Check};
15192    }
15193    if($UsedDump{1}{"V"} and $UsedDump{2}{"V"})
15194    {
15195        if($Check eq "G")
15196        {
15197            if(getGccVersion(1) ne getGccVersion(2))
15198            { # different GCC versions
15199                return ($Cache{"differentDumps"}{$Check}=1);
15200            }
15201        }
15202        if($Check eq "V")
15203        {
15204            if(cmpVersions(formatVersion($UsedDump{1}{"V"}, 2),
15205            formatVersion($UsedDump{2}{"V"}, 2))!=0)
15206            { # different dump versions (skip micro version)
15207                return ($Cache{"differentDumps"}{$Check}=1);
15208            }
15209        }
15210    }
15211    return ($Cache{"differentDumps"}{$Check}=0);
15212}
15213
15214sub formatVersion($$)
15215{ # cut off version digits
15216    my ($V, $Digits) = @_;
15217    my @Elems = split(/\./, $V);
15218    return join(".", splice(@Elems, 0, $Digits));
15219}
15220
15221sub htmlSpecChars($)
15222{
15223    my $Str = $_[0];
15224    if(not $Str) {
15225        return $Str;
15226    }
15227    $Str=~s/\&([^#]|\Z)/&amp;$1/g;
15228    $Str=~s/</&lt;/g;
15229    $Str=~s/\-\>/&#45;&gt;/g; # &minus;
15230    $Str=~s/>/&gt;/g;
15231    $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g;
15232    $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g;
15233    $Str=~s/ /&#160;/g; # &nbsp;
15234    $Str=~s/\@SP\@/ /g;
15235    $Str=~s/\n/<br\/>/g;
15236    $Str=~s/\"/&quot;/g;
15237    $Str=~s/\'/&#39;/g;
15238    return $Str;
15239}
15240
15241sub xmlSpecChars($)
15242{
15243    my $Str = $_[0];
15244    if(not $Str) {
15245        return $Str;
15246    }
15247
15248    $Str=~s/\&([^#]|\Z)/&amp;$1/g;
15249    $Str=~s/</&lt;/g;
15250    $Str=~s/>/&gt;/g;
15251
15252    $Str=~s/\"/&quot;/g;
15253    $Str=~s/\'/&#39;/g;
15254
15255    return $Str;
15256}
15257
15258sub xmlSpecChars_R($)
15259{
15260    my $Str = $_[0];
15261    if(not $Str) {
15262        return $Str;
15263    }
15264
15265    $Str=~s/&amp;/&/g;
15266    $Str=~s/&lt;/</g;
15267    $Str=~s/&gt;/>/g;
15268
15269    $Str=~s/&quot;/"/g;
15270    $Str=~s/&#39;/'/g;
15271
15272    return $Str;
15273}
15274
15275sub black_name($)
15276{
15277    my $Name = $_[0];
15278    return "<span class='iname_b'>".highLight_Signature($Name)."</span>";
15279}
15280
15281sub highLight_Signature($)
15282{
15283    my $Signature = $_[0];
15284    return highLight_Signature_PPos_Italic($Signature, "", 0, 0, 0);
15285}
15286
15287sub highLight_Signature_Italic_Color($)
15288{
15289    my $Signature = $_[0];
15290    return highLight_Signature_PPos_Italic($Signature, "", 1, 1, 1);
15291}
15292
15293sub separate_symbol($)
15294{
15295    my $Symbol = $_[0];
15296    my ($Name, $Spec, $Ver) = ($Symbol, "", "");
15297    if($Symbol=~/\A([^\@\$\?]+)([\@\$]+)([^\@\$]+)\Z/) {
15298        ($Name, $Spec, $Ver) = ($1, $2, $3);
15299    }
15300    return ($Name, $Spec, $Ver);
15301}
15302
15303sub cut_f_attrs($)
15304{
15305    if($_[0]=~s/(\))((| (const volatile|const|volatile))(| \[static\]))\Z/$1/) {
15306        return $2;
15307    }
15308    return "";
15309}
15310
15311sub highLight_Signature_PPos_Italic($$$$$)
15312{
15313    my ($FullSignature, $Param_Pos, $ItalicParams, $ColorParams, $ShowReturn) = @_;
15314    $Param_Pos = "" if(not defined $Param_Pos);
15315    my ($Signature, $VersionSpec, $SymbolVersion) = separate_symbol($FullSignature);
15316    my $Return = "";
15317    if($ShowRetVal and $Signature=~s/([^:]):([^:].+?)\Z/$1/g) {
15318        $Return = $2;
15319    }
15320    my $SCenter = find_center($Signature, "(");
15321    if(not $SCenter)
15322    { # global data
15323        $Signature = htmlSpecChars($Signature);
15324        $Signature=~s!(\[data\])!<span class='attr'>$1</span>!g;
15325        $Signature .= (($SymbolVersion)?"<span class='sym_ver'>&#160;$VersionSpec&#160;$SymbolVersion</span>":"");
15326        if($Return and $ShowReturn) {
15327            $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
15328        }
15329        return $Signature;
15330    }
15331    my ($Begin, $End) = (substr($Signature, 0, $SCenter), "");
15332    $Begin.=" " if($Begin!~/ \Z/);
15333    $End = cut_f_attrs($Signature);
15334    my @Parts = ();
15335    my ($Short, $Params) = split_Signature($Signature);
15336    my @SParts = separate_Params($Params, 1, 1);
15337    foreach my $Pos (0 .. $#SParts)
15338    {
15339        my $Part = $SParts[$Pos];
15340        $Part=~s/\A\s+|\s+\Z//g;
15341        my ($Part_Styled, $ParamName) = (htmlSpecChars($Part), "");
15342        if($Part=~/\([\*]+(\w+)\)/i) {
15343            $ParamName = $1;#func-ptr
15344        }
15345        elsif($Part=~/(\w+)[\,\)]*\Z/i) {
15346            $ParamName = $1;
15347        }
15348        if(not $ParamName)
15349        {
15350            push(@Parts, $Part_Styled);
15351            next;
15352        }
15353        if($ItalicParams and not $TName_Tid{1}{$Part}
15354        and not $TName_Tid{2}{$Part})
15355        {
15356            my $Style = "<i>$ParamName</i>";
15357
15358            if($Param_Pos ne ""
15359            and $Pos==$Param_Pos) {
15360                $Style = "<span class=\'fp\'>$ParamName</span>";
15361            }
15362            elsif($ColorParams) {
15363                $Style = "<span class=\'color_p\'>$ParamName</span>";
15364            }
15365
15366            $Part_Styled=~s!(\W)$ParamName([\,\)]|\Z)!$1$Style$2!ig;
15367        }
15368        $Part_Styled=~s/,(\w)/, $1/g;
15369        push(@Parts, $Part_Styled);
15370    }
15371    if(@Parts)
15372    {
15373        foreach my $Num (0 .. $#Parts)
15374        {
15375            if($Num==$#Parts)
15376            { # add ")" to the last parameter
15377                $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]." )</span>";
15378            }
15379            elsif(length($Parts[$Num])<=45) {
15380                $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]."</span>";
15381            }
15382        }
15383        $Signature = htmlSpecChars($Begin)."<span class='sym_p'>(&#160;".join(" ", @Parts)."</span>".$End;
15384    }
15385    else {
15386        $Signature = htmlSpecChars($Begin)."<span class='sym_p'>(&#160;)</span>".$End;
15387    }
15388    if($Return and $ShowReturn) {
15389        $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
15390    }
15391    $Signature=~s!\[\]![&#160;]!g;
15392    $Signature=~s!operator=!operator&#160;=!g;
15393    $Signature=~s!(\[in-charge\]|\[not-in-charge\]|\[in-charge-deleting\]|\[static\])!<span class='attr'>$1</span>!g;
15394    if($SymbolVersion) {
15395        $Signature .= "<span class='sym_ver'>&#160;$VersionSpec&#160;$SymbolVersion</span>";
15396    }
15397    return $Signature;
15398}
15399
15400sub split_Signature($)
15401{
15402    my $Signature = $_[0];
15403    if(my $ShortName = substr($Signature, 0, find_center($Signature, "(")))
15404    {
15405        $Signature=~s/\A\Q$ShortName\E\(//g;
15406        cut_f_attrs($Signature);
15407        $Signature=~s/\)\Z//;
15408        return ($ShortName, $Signature);
15409    }
15410
15411    # error
15412    return ($Signature, "");
15413}
15414
15415sub separate_Params($$$)
15416{
15417    my ($Params, $Comma, $Sp) = @_;
15418    my @Parts = ();
15419    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
15420    my $Part = 0;
15421    foreach my $Pos (0 .. length($Params) - 1)
15422    {
15423        my $S = substr($Params, $Pos, 1);
15424        if(defined $B{$S}) {
15425            $B{$S} += 1;
15426        }
15427        if($S eq "," and
15428        $B{"("}==$B{")"} and $B{"<"}==$B{">"})
15429        {
15430            if($Comma)
15431            { # include comma
15432                $Parts[$Part] .= $S;
15433            }
15434            $Part += 1;
15435        }
15436        else {
15437            $Parts[$Part] .= $S;
15438        }
15439    }
15440    if(not $Sp)
15441    { # remove spaces
15442        foreach (@Parts)
15443        {
15444            s/\A //g;
15445            s/ \Z//g;
15446        }
15447    }
15448    return @Parts;
15449}
15450
15451sub find_center($$)
15452{
15453    my ($Sign, $Target) = @_;
15454    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
15455    my $Center = 0;
15456    if($Sign=~s/(operator([^\w\s\(\)]+|\(\)))//g)
15457    { # operators
15458        $Center+=length($1);
15459    }
15460    foreach my $Pos (0 .. length($Sign)-1)
15461    {
15462        my $S = substr($Sign, $Pos, 1);
15463        if($S eq $Target)
15464        {
15465            if($B{"("}==$B{")"}
15466            and $B{"<"}==$B{">"}) {
15467                return $Center;
15468            }
15469        }
15470        if(defined $B{$S}) {
15471            $B{$S}+=1;
15472        }
15473        $Center+=1;
15474    }
15475    return 0;
15476}
15477
15478sub appendFile($$)
15479{
15480    my ($Path, $Content) = @_;
15481    return if(not $Path);
15482    if(my $Dir = get_dirname($Path)) {
15483        mkpath($Dir);
15484    }
15485    open(FILE, ">>", $Path) || die ("can't open file \'$Path\': $!\n");
15486    print FILE $Content;
15487    close(FILE);
15488}
15489
15490sub writeFile($$)
15491{
15492    my ($Path, $Content) = @_;
15493    return if(not $Path);
15494    if(my $Dir = get_dirname($Path)) {
15495        mkpath($Dir);
15496    }
15497    open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n");
15498    print FILE $Content;
15499    close(FILE);
15500}
15501
15502sub readFile($)
15503{
15504    my $Path = $_[0];
15505    return "" if(not $Path or not -f $Path);
15506    open(FILE, $Path);
15507    local $/ = undef;
15508    my $Content = <FILE>;
15509    close(FILE);
15510    if($Path!~/\.(tu|class|abi)\Z/) {
15511        $Content=~s/\r/\n/g;
15512    }
15513    return $Content;
15514}
15515
15516sub get_filename($)
15517{ # much faster than basename() from File::Basename module
15518    if(defined $Cache{"get_filename"}{$_[0]}) {
15519        return $Cache{"get_filename"}{$_[0]};
15520    }
15521    if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) {
15522        return ($Cache{"get_filename"}{$_[0]}=$1);
15523    }
15524    return ($Cache{"get_filename"}{$_[0]}="");
15525}
15526
15527sub get_dirname($)
15528{ # much faster than dirname() from File::Basename module
15529    if(defined $Cache{"get_dirname"}{$_[0]}) {
15530        return $Cache{"get_dirname"}{$_[0]};
15531    }
15532    if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) {
15533        return ($Cache{"get_dirname"}{$_[0]}=$1);
15534    }
15535    return ($Cache{"get_dirname"}{$_[0]}="");
15536}
15537
15538sub separate_path($) {
15539    return (get_dirname($_[0]), get_filename($_[0]));
15540}
15541
15542sub esc($)
15543{
15544    my $Str = $_[0];
15545    $Str=~s/([()\[\]{}$ &'"`;,<>\+])/\\$1/g;
15546    return $Str;
15547}
15548
15549sub readLineNum($$)
15550{
15551    my ($Path, $Num) = @_;
15552    return "" if(not $Path or not -f $Path);
15553    open(FILE, $Path);
15554    foreach (1 ... $Num) {
15555        <FILE>;
15556    }
15557    my $Line = <FILE>;
15558    close(FILE);
15559    return $Line;
15560}
15561
15562sub readAttributes($$)
15563{
15564    my ($Path, $Num) = @_;
15565    return () if(not $Path or not -f $Path);
15566    my %Attributes = ();
15567    if(readLineNum($Path, $Num)=~/<!--\s+(.+)\s+-->/)
15568    {
15569        foreach my $AttrVal (split(/;/, $1))
15570        {
15571            if($AttrVal=~/(.+):(.+)/)
15572            {
15573                my ($Name, $Value) = ($1, $2);
15574                $Attributes{$Name} = $Value;
15575            }
15576        }
15577    }
15578    return \%Attributes;
15579}
15580
15581sub is_abs($) {
15582    return ($_[0]=~/\A(\/|\w+:[\/\\])/);
15583}
15584
15585sub get_abs_path($)
15586{ # abs_path() should NOT be called for absolute inputs
15587  # because it can change them
15588    my $Path = $_[0];
15589    if(not is_abs($Path)) {
15590        $Path = abs_path($Path);
15591    }
15592    return path_format($Path, $OSgroup);
15593}
15594
15595sub get_OSgroup()
15596{
15597    my $N = $Config{"osname"};
15598    if($N=~/macos|darwin|rhapsody/i) {
15599        return "macos";
15600    }
15601    elsif($N=~/freebsd|openbsd|netbsd/i) {
15602        return "bsd";
15603    }
15604    elsif($N=~/haiku|beos/i) {
15605        return "beos";
15606    }
15607    elsif($N=~/symbian|epoc/i) {
15608        return "symbian";
15609    }
15610    elsif($N=~/win/i) {
15611        return "windows";
15612    }
15613    else {
15614        return $N;
15615    }
15616}
15617
15618sub getGccVersion($)
15619{
15620    my $LibVersion = $_[0];
15621    if($GCC_VERSION{$LibVersion})
15622    { # dump version
15623        return $GCC_VERSION{$LibVersion};
15624    }
15625    elsif($UsedDump{$LibVersion}{"V"})
15626    { # old-version dumps
15627        return "unknown";
15628    }
15629    my $GccVersion = get_dumpversion($GCC_PATH); # host version
15630    if(not $GccVersion) {
15631        return "unknown";
15632    }
15633    return $GccVersion;
15634}
15635
15636sub showArch($)
15637{
15638    my $Arch = $_[0];
15639    if($Arch eq "arm"
15640    or $Arch eq "mips") {
15641        return uc($Arch);
15642    }
15643    return $Arch;
15644}
15645
15646sub getArch($)
15647{
15648    my $LibVersion = $_[0];
15649
15650    if($TargetArch) {
15651        return $TargetArch;
15652    }
15653    elsif($CPU_ARCH{$LibVersion})
15654    { # dump
15655        return $CPU_ARCH{$LibVersion};
15656    }
15657    elsif($UsedDump{$LibVersion}{"V"})
15658    { # old-version dumps
15659        return "unknown";
15660    }
15661
15662    return getArch_GCC($LibVersion);
15663}
15664
15665sub get_Report_Title($)
15666{
15667    my $Level = $_[0];
15668
15669    my $ArchInfo = " on <span style='color:Blue;'>".showArch(getArch(1))."</span>";
15670    if(getArch(1) ne getArch(2)
15671    or getArch(1) eq "unknown"
15672    or $Level eq "Source")
15673    { # don't show architecture in the header
15674        $ArchInfo="";
15675    }
15676    my $Title = "";
15677    if($Level eq "Source") {
15678        $Title .= "Source compatibility";
15679    }
15680    elsif($Level eq "Binary") {
15681        $Title .= "Binary compatibility";
15682    }
15683    else {
15684        $Title .= "API compatibility";
15685    }
15686
15687    my $V1 = $Descriptor{1}{"Version"};
15688    my $V2 = $Descriptor{2}{"Version"};
15689
15690    if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"})
15691    {
15692        my $M1 = $UsedDump{1}{"M"};
15693        my $M2 = $UsedDump{2}{"M"};
15694
15695        my $M1S = $M1;
15696        my $M2S = $M2;
15697
15698        $M1S=~s/(\.so|\.ko)\..+/$1/ig;
15699        $M2S=~s/(\.so|\.ko)\..+/$1/ig;
15700
15701        if($M1S eq $M2S
15702        and $V1 ne "X" and $V2 ne "Y")
15703        {
15704            $Title .= " report for the <span style='color:Blue;'>$M1S</span> $TargetComponent";
15705            $Title .= " between <span style='color:Red;'>".$V1."</span> and <span style='color:Red;'>".$V2."</span> versions";
15706        }
15707        else
15708        {
15709            $Title .= " report between <span style='color:Blue;'>$M1</span> (<span style='color:Red;'>".$V1."</span>)";
15710            $Title .= " and <span style='color:Blue;'>$M2</span> (<span style='color:Red;'>".$V2."</span>) objects";
15711        }
15712    }
15713    else
15714    {
15715        $Title .= " report for the <span style='color:Blue;'>$TargetTitle</span> $TargetComponent";
15716        $Title .= " between <span style='color:Red;'>".$V1."</span> and <span style='color:Red;'>".$V2."</span> versions";
15717    }
15718
15719    $Title .= $ArchInfo;
15720
15721    if($AppPath) {
15722        $Title .= " (relating to the portability of application <span style='color:Blue;'>".get_filename($AppPath)."</span>)";
15723    }
15724    $Title = "<h1>".$Title."</h1>\n";
15725    return $Title;
15726}
15727
15728sub get_CheckedHeaders($)
15729{
15730    my $LibVersion = $_[0];
15731
15732    my @Headers = ();
15733
15734    foreach my $Path (keys(%{$Registered_Headers{$LibVersion}}))
15735    {
15736        my $File = get_filename($Path);
15737
15738        if(not is_target_header($File, $LibVersion)) {
15739            next;
15740        }
15741
15742        if(skipHeader($File, $LibVersion)) {
15743            next;
15744        }
15745
15746        push(@Headers, $Path);
15747    }
15748
15749    return @Headers;
15750}
15751
15752sub get_SourceInfo()
15753{
15754    my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", "");
15755
15756    if(my @Headers = get_CheckedHeaders(1))
15757    {
15758        $CheckedHeaders = "<a name='Headers'></a>";
15759        if($OldStyle) {
15760            $CheckedHeaders .= "<h2>Header Files (".($#Headers+1).")</h2>";
15761        }
15762        else {
15763            $CheckedHeaders .= "<h2>Header Files <span class='gray'>&nbsp;".($#Headers+1)."&nbsp;</span></h2>";
15764        }
15765        $CheckedHeaders .= "<hr/>\n<div class='h_list'>\n";
15766        foreach my $Header_Path (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
15767        {
15768            my $Identity = $Registered_Headers{1}{$Header_Path}{"Identity"};
15769            my $Name = get_filename($Identity);
15770            my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15771            $CheckedHeaders .= $Name.$Comment."<br/>\n";
15772        }
15773        $CheckedHeaders .= "</div>\n";
15774        $CheckedHeaders .= "<br/>$TOP_REF<br/>\n";
15775    }
15776
15777    if(my @Sources = keys(%{$Registered_Sources{1}}))
15778    {
15779        $CheckedSources = "<a name='Sources'></a>";
15780        if($OldStyle) {
15781            $CheckedSources .= "<h2>Source Files (".($#Sources+1).")</h2>";
15782        }
15783        else {
15784            $CheckedSources .= "<h2>Source Files <span class='gray'>&nbsp;".($#Sources+1)."&nbsp;</span></h2>";
15785        }
15786        $CheckedSources .= "<hr/>\n<div class='h_list'>\n";
15787        foreach my $Header_Path (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
15788        {
15789            my $Identity = $Registered_Sources{1}{$Header_Path}{"Identity"};
15790            my $Name = get_filename($Identity);
15791            my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15792            $CheckedSources .= $Name.$Comment."<br/>\n";
15793        }
15794        $CheckedSources .= "</div>\n";
15795        $CheckedSources .= "<br/>$TOP_REF<br/>\n";
15796    }
15797
15798    if(not $CheckHeadersOnly)
15799    {
15800        $CheckedLibs = "<a name='Libs'></a>";
15801        if($OldStyle) {
15802            $CheckedLibs .= "<h2>".get_ObjTitle()." (".keys(%{$Library_Symbol{1}}).")</h2>";
15803        }
15804        else {
15805            $CheckedLibs .= "<h2>".get_ObjTitle()." <span class='gray'>&nbsp;".keys(%{$Library_Symbol{1}})."&nbsp;</span></h2>";
15806        }
15807        $CheckedLibs .= "<hr/>\n<div class='lib_list'>\n";
15808        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$Library_Symbol{1}})) {
15809            $CheckedLibs .= $Library."<br/>\n";
15810        }
15811        $CheckedLibs .= "</div>\n";
15812        $CheckedLibs .= "<br/>$TOP_REF<br/>\n";
15813    }
15814
15815    return $CheckedHeaders.$CheckedSources.$CheckedLibs;
15816}
15817
15818sub get_ObjTitle()
15819{
15820    if(defined $UsedDump{1}{"DWARF"}) {
15821        return "Objects";
15822    }
15823    else {
15824        return ucfirst($SLIB_TYPE)." Libraries";
15825    }
15826}
15827
15828sub get_TypeProblems_Count($$)
15829{
15830    my ($TargetSeverity, $Level) = @_;
15831    my $Type_Problems_Count = 0;
15832
15833    foreach my $Type_Name (sort keys(%{$TypeChanges{$Level}}))
15834    {
15835        my %Kinds_Target = ();
15836        foreach my $Kind (keys(%{$TypeChanges{$Level}{$Type_Name}}))
15837        {
15838            foreach my $Location (keys(%{$TypeChanges{$Level}{$Type_Name}{$Kind}}))
15839            {
15840                my $Target = $TypeChanges{$Level}{$Type_Name}{$Kind}{$Location}{"Target"};
15841                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15842
15843                if($Severity ne $TargetSeverity) {
15844                    next;
15845                }
15846
15847                if($Kinds_Target{$Kind}{$Target}) {
15848                    next;
15849                }
15850
15851                $Kinds_Target{$Kind}{$Target} = 1;
15852                $Type_Problems_Count += 1;
15853            }
15854        }
15855    }
15856    return $Type_Problems_Count;
15857}
15858
15859sub get_Summary($)
15860{
15861    my $Level = $_[0];
15862    my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High,
15863    $C_Problems_Low, $T_Problems_Medium, $T_Problems_Low, $I_Other, $T_Other, $C_Other) = (0,0,0,0,0,0,0,0,0,0,0,0);
15864    %{$RESULT{$Level}} = (
15865        "Problems"=>0,
15866        "Warnings"=>0,
15867        "Affected"=>0 );
15868    # check rules
15869    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15870    {
15871        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
15872        {
15873            if(not defined $CompatRules{$Level}{$Kind})
15874            { # unknown rule
15875                if(not $UnknownRules{$Level}{$Kind})
15876                { # only one warning
15877                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
15878                    $UnknownRules{$Level}{$Kind}=1;
15879                }
15880                delete($CompatProblems{$Level}{$Interface}{$Kind});
15881            }
15882        }
15883    }
15884    foreach my $Constant (sort keys(%{$CompatProblems_Constants{$Level}}))
15885    {
15886        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
15887        {
15888            if(not defined $CompatRules{$Level}{$Kind})
15889            { # unknown rule
15890                if(not $UnknownRules{$Level}{$Kind})
15891                { # only one warning
15892                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
15893                    $UnknownRules{$Level}{$Kind}=1;
15894                }
15895                delete($CompatProblems_Constants{$Level}{$Constant}{$Kind});
15896            }
15897        }
15898    }
15899    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15900    {
15901        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
15902        {
15903            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols")
15904            {
15905                foreach my $Location (sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
15906                {
15907                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15908                    if($Kind eq "Added_Symbol") {
15909                        $Added += 1;
15910                    }
15911                    elsif($Kind eq "Removed_Symbol")
15912                    {
15913                        $Removed += 1;
15914                        $TotalAffected{$Level}{$Interface} = $Severity;
15915                    }
15916                    else
15917                    {
15918                        if($Severity eq "Safe") {
15919                            $I_Other += 1;
15920                        }
15921                        elsif($Severity eq "High") {
15922                            $I_Problems_High += 1;
15923                        }
15924                        elsif($Severity eq "Medium") {
15925                            $I_Problems_Medium += 1;
15926                        }
15927                        elsif($Severity eq "Low") {
15928                            $I_Problems_Low += 1;
15929                        }
15930                        if(($Severity ne "Low" or $StrictCompat)
15931                        and $Severity ne "Safe") {
15932                            $TotalAffected{$Level}{$Interface} = $Severity;
15933                        }
15934                    }
15935                }
15936            }
15937        }
15938    }
15939
15940    my %MethodTypeIndex = ();
15941
15942    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15943    {
15944        my @Kinds = sort keys(%{$CompatProblems{$Level}{$Interface}});
15945        foreach my $Kind (@Kinds)
15946        {
15947            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
15948            {
15949                my @Locs = sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}});
15950                foreach my $Location (@Locs)
15951                {
15952                    my $Type_Name = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"};
15953                    my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"};
15954
15955                    if(defined $MethodTypeIndex{$Interface}{$Type_Name}{$Kind}{$Target})
15956                    { # one location for one type and target
15957                        next;
15958                    }
15959                    $MethodTypeIndex{$Interface}{$Type_Name}{$Kind}{$Target} = 1;
15960                    $TypeChanges{$Level}{$Type_Name}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location};
15961
15962                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15963
15964                    if(($Severity ne "Low" or $StrictCompat)
15965                    and $Severity ne "Safe")
15966                    {
15967                        if(my $Sev = $TotalAffected{$Level}{$Interface})
15968                        {
15969                            if($Severity_Val{$Severity}>$Severity_Val{$Sev}) {
15970                                $TotalAffected{$Level}{$Interface} = $Severity;
15971                            }
15972                        }
15973                        else {
15974                            $TotalAffected{$Level}{$Interface} = $Severity;
15975                        }
15976                    }
15977                }
15978            }
15979        }
15980    }
15981
15982    $T_Problems_High = get_TypeProblems_Count("High", $Level);
15983    $T_Problems_Medium = get_TypeProblems_Count("Medium", $Level);
15984    $T_Problems_Low = get_TypeProblems_Count("Low", $Level);
15985    $T_Other = get_TypeProblems_Count("Safe", $Level);
15986
15987    # changed and removed public symbols
15988    my $SCount = keys(%{$CheckedSymbols{$Level}});
15989    if($ExtendedCheck)
15990    { # don't count external_func_0 for constants
15991        $SCount-=1;
15992    }
15993    if($SCount)
15994    {
15995        my %Weight = (
15996            "High" => 100,
15997            "Medium" => 50,
15998            "Low" => 25
15999        );
16000        foreach (keys(%{$TotalAffected{$Level}})) {
16001            $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
16002        }
16003        $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
16004    }
16005    else {
16006        $RESULT{$Level}{"Affected"} = 0;
16007    }
16008
16009    $RESULT{$Level}{"Affected"} = show_number($RESULT{$Level}{"Affected"});
16010    if($RESULT{$Level}{"Affected"}>=100) {
16011        $RESULT{$Level}{"Affected"} = 100;
16012    }
16013
16014    $RESULT{$Level}{"Problems"} += $Removed;
16015    $RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High;
16016    $RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium;
16017    if($StrictCompat) {
16018        $RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low;
16019    }
16020    else {
16021        $RESULT{$Level}{"Warnings"} += $T_Problems_Low + $I_Problems_Low;
16022    }
16023
16024    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
16025    {
16026        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
16027        {
16028            my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16029            if($Severity eq "Safe")
16030            {
16031                $C_Other+=1;
16032            }
16033            elsif($Severity eq "Low")
16034            {
16035                $C_Problems_Low+=1;
16036            }
16037        }
16038    }
16039
16040    if($C_Problems_Low)
16041    {
16042        if($StrictCompat) {
16043            $RESULT{$Level}{"Problems"} += $C_Problems_Low;
16044        }
16045        else {
16046            $RESULT{$Level}{"Warnings"} += $C_Problems_Low;
16047        }
16048    }
16049    if($RESULT{$Level}{"Problems"}
16050    and $RESULT{$Level}{"Affected"}) {
16051        $RESULT{$Level}{"Verdict"} = "incompatible";
16052    }
16053    else {
16054        $RESULT{$Level}{"Verdict"} = "compatible";
16055    }
16056
16057    my $TotalTypes = keys(%{$CheckedTypes{$Level}});
16058    if(not $TotalTypes)
16059    { # list all the types
16060        $TotalTypes = keys(%{$TName_Tid{1}});
16061    }
16062
16063    my ($Arch1, $Arch2) = (getArch(1), getArch(2));
16064    my ($GccV1, $GccV2) = (getGccVersion(1), getGccVersion(2));
16065    my ($ClangV1, $ClangV2) = ($CLANG_VERSION{1}, $CLANG_VERSION{2});
16066
16067    my ($TestInfo, $TestResults, $Problem_Summary) = ();
16068
16069    if($ReportFormat eq "xml")
16070    { # XML
16071        # test info
16072        $TestInfo .= "  <library>$TargetLibraryName</library>\n";
16073        $TestInfo .= "  <version1>\n";
16074        $TestInfo .= "    <number>".$Descriptor{1}{"Version"}."</number>\n";
16075        $TestInfo .= "    <arch>$Arch1</arch>\n";
16076        if($GccV1) {
16077            $TestInfo .= "    <gcc>$GccV1</gcc>\n";
16078        }
16079        elsif($ClangV1) {
16080            $TestInfo .= "    <clang>$ClangV1</clang>\n";
16081        }
16082        $TestInfo .= "  </version1>\n";
16083
16084        $TestInfo .= "  <version2>\n";
16085        $TestInfo .= "    <number>".$Descriptor{2}{"Version"}."</number>\n";
16086        $TestInfo .= "    <arch>$Arch2</arch>\n";
16087        if($GccV2) {
16088            $TestInfo .= "    <gcc>$GccV2</gcc>\n";
16089        }
16090        elsif($ClangV2) {
16091            $TestInfo .= "    <clang>$ClangV2</clang>\n";
16092        }
16093        $TestInfo .= "  </version2>\n";
16094        $TestInfo = "<test_info>\n".$TestInfo."</test_info>\n\n";
16095
16096        # test results
16097        if(my @Headers = keys(%{$Registered_Headers{1}}))
16098        {
16099            $TestResults .= "  <headers>\n";
16100            foreach my $Name (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
16101            {
16102                my $Identity = $Registered_Headers{1}{$Name}{"Identity"};
16103                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
16104                $TestResults .= "    <name>".get_filename($Name).$Comment."</name>\n";
16105            }
16106            $TestResults .= "  </headers>\n";
16107        }
16108
16109        if(my @Sources = keys(%{$Registered_Sources{1}}))
16110        {
16111            $TestResults .= "  <sources>\n";
16112            foreach my $Name (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
16113            {
16114                my $Identity = $Registered_Sources{1}{$Name}{"Identity"};
16115                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
16116                $TestResults .= "    <name>".get_filename($Name).$Comment."</name>\n";
16117            }
16118            $TestResults .= "  </sources>\n";
16119        }
16120
16121        $TestResults .= "  <libs>\n";
16122        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$Library_Symbol{1}}))
16123        {
16124            # $Library .= " (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
16125            $TestResults .= "    <name>$Library</name>\n";
16126        }
16127        $TestResults .= "  </libs>\n";
16128
16129        $TestResults .= "  <symbols>".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))."</symbols>\n";
16130        $TestResults .= "  <types>".$TotalTypes."</types>\n";
16131
16132        $TestResults .= "  <verdict>".$RESULT{$Level}{"Verdict"}."</verdict>\n";
16133        $TestResults .= "  <affected>".$RESULT{$Level}{"Affected"}."</affected>\n";
16134        $TestResults = "<test_results>\n".$TestResults."</test_results>\n\n";
16135
16136        # problem summary
16137        $Problem_Summary .= "  <added_symbols>".$Added."</added_symbols>\n";
16138        $Problem_Summary .= "  <removed_symbols>".$Removed."</removed_symbols>\n";
16139
16140        $Problem_Summary .= "  <problems_with_types>\n";
16141        $Problem_Summary .= "    <high>$T_Problems_High</high>\n";
16142        $Problem_Summary .= "    <medium>$T_Problems_Medium</medium>\n";
16143        $Problem_Summary .= "    <low>$T_Problems_Low</low>\n";
16144        $Problem_Summary .= "    <safe>$T_Other</safe>\n";
16145        $Problem_Summary .= "  </problems_with_types>\n";
16146
16147        $Problem_Summary .= "  <problems_with_symbols>\n";
16148        $Problem_Summary .= "    <high>$I_Problems_High</high>\n";
16149        $Problem_Summary .= "    <medium>$I_Problems_Medium</medium>\n";
16150        $Problem_Summary .= "    <low>$I_Problems_Low</low>\n";
16151        $Problem_Summary .= "    <safe>$I_Other</safe>\n";
16152        $Problem_Summary .= "  </problems_with_symbols>\n";
16153
16154        $Problem_Summary .= "  <problems_with_constants>\n";
16155        $Problem_Summary .= "    <low>$C_Problems_Low</low>\n";
16156        $Problem_Summary .= "  </problems_with_constants>\n";
16157
16158        $Problem_Summary = "<problem_summary>\n".$Problem_Summary."</problem_summary>\n\n";
16159
16160        return ($TestInfo.$TestResults.$Problem_Summary, "");
16161    }
16162    else
16163    { # HTML
16164        # test info
16165        $TestInfo = "<h2>Test Info</h2><hr/>\n";
16166        $TestInfo .= "<table class='summary'>\n";
16167
16168        if($TargetComponent eq "library") {
16169            $TestInfo .= "<tr><th>Library Name</th><td>$TargetTitle</td></tr>\n";
16170        }
16171        else {
16172            $TestInfo .= "<tr><th>Module Name</th><td>$TargetTitle</td></tr>\n";
16173        }
16174
16175        my (@VInf1, @VInf2, $AddTestInfo) = ();
16176        if($Arch1 ne "unknown"
16177        and $Arch2 ne "unknown")
16178        { # CPU arch
16179            if($Arch1 eq $Arch2)
16180            { # go to the separate section
16181                $AddTestInfo .= "<tr><th>Arch</th><td>".showArch($Arch1)."</td></tr>\n";
16182            }
16183            else
16184            { # go to the version number
16185                push(@VInf1, showArch($Arch1));
16186                push(@VInf2, showArch($Arch2));
16187            }
16188        }
16189        if($Level eq "Binary"
16190        and $OStarget ne "windows")
16191        {
16192            if($GccV1 ne "unknown"
16193            and $GccV2 ne "unknown")
16194            { # GCC version
16195                if($GccV1 eq $GccV2)
16196                { # go to the separate section
16197                    $AddTestInfo .= "<tr><th>GCC Version</th><td>$GccV1</td></tr>\n";
16198                }
16199                else
16200                { # go to the version number
16201                    push(@VInf1, "gcc ".$GccV1);
16202                    push(@VInf2, "gcc ".$GccV2);
16203                }
16204            }
16205            elsif($ClangV1
16206            and $ClangV2)
16207            { # Clang version
16208                if($ClangV1 eq $ClangV2)
16209                { # go to the separate section
16210                    $AddTestInfo .= "<tr><th>Clang Version</th><td>$ClangV1</td></tr>\n";
16211                }
16212                else
16213                { # go to the version number
16214                    push(@VInf1, "clang ".$ClangV1);
16215                    push(@VInf2, "clang ".$ClangV2);
16216                }
16217            }
16218            elsif($GccV1 ne "unknown" and $ClangV2)
16219            {
16220                push(@VInf1, "gcc ".$GccV1);
16221                push(@VInf2, "clang ".$ClangV2);
16222            }
16223            elsif($ClangV1 and $GccV2 ne "unknown")
16224            {
16225                push(@VInf1, "clang ".$ClangV1);
16226                push(@VInf2, "gcc ".$GccV2);
16227            }
16228        }
16229        # show long version names with GCC version and CPU architecture name (if different)
16230        $TestInfo .= "<tr><th>Version #1</th><td>".$Descriptor{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."</td></tr>\n";
16231        $TestInfo .= "<tr><th>Version #2</th><td>".$Descriptor{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."</td></tr>\n";
16232        $TestInfo .= $AddTestInfo;
16233        #if($COMMON_LANGUAGE{1}) {
16234        #    $TestInfo .= "<tr><th>Language</th><td>".$COMMON_LANGUAGE{1}."</td></tr>\n";
16235        #}
16236        if($ExtendedCheck) {
16237            $TestInfo .= "<tr><th>Mode</th><td>Extended</td></tr>\n";
16238        }
16239        if($JoinReport)
16240        {
16241            if($Level eq "Binary") {
16242                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Binary Compatibility</td></tr>\n"; # Run-time
16243            }
16244            elsif($Level eq "Source") {
16245                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Source Compatibility</td></tr>\n"; # Build-time
16246            }
16247        }
16248        $TestInfo .= "</table>\n";
16249
16250        # test results
16251        $TestResults = "<h2>Test Results</h2><hr/>\n";
16252        $TestResults .= "<table class='summary'>";
16253
16254        if(my @Headers = get_CheckedHeaders(1))
16255        {
16256            my $Headers_Link = "<a href='#Headers' style='color:Blue;'>".($#Headers + 1)."</a>";
16257            $TestResults .= "<tr><th>Total Header Files</th><td>".$Headers_Link."</td></tr>\n";
16258        }
16259
16260        if(my @Sources = keys(%{$Registered_Sources{1}}))
16261        {
16262            my $Src_Link = "<a href='#Sources' style='color:Blue;'>".($#Sources + 1)."</a>";
16263            $TestResults .= "<tr><th>Total Source Files</th><td>".$Src_Link."</td></tr>\n";
16264        }
16265
16266        if(not $ExtendedCheck)
16267        {
16268            my $Libs_Link = "0";
16269            $Libs_Link = "<a href='#Libs' style='color:Blue;'>".keys(%{$Library_Symbol{1}})."</a>" if(keys(%{$Library_Symbol{1}})>0);
16270            $TestResults .= "<tr><th>Total ".get_ObjTitle()."</th><td>".($CheckHeadersOnly?"0&#160;(not&#160;analyzed)":$Libs_Link)."</td></tr>\n";
16271        }
16272
16273        $TestResults .= "<tr><th>Total Symbols / Types</th><td>".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))." / ".$TotalTypes."</td></tr>\n";
16274
16275        my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";";
16276        if($JoinReport) {
16277            $META_DATA = "kind:".lc($Level).";".$META_DATA;
16278        }
16279
16280        my $BC_Rate = show_number(100 - $RESULT{$Level}{"Affected"});
16281
16282        $TestResults .= "<tr><th>Compatibility</th>\n";
16283        if($RESULT{$Level}{"Verdict"} eq "incompatible")
16284        {
16285            my $Cl = "incompatible";
16286            if($BC_Rate>=90) {
16287                $Cl = "warning";
16288            }
16289            elsif($BC_Rate>=80) {
16290                $Cl = "almost_compatible";
16291            }
16292
16293            $TestResults .= "<td class=\'$Cl\'>".$BC_Rate."%</td>\n";
16294        }
16295        else {
16296            $TestResults .= "<td class=\'compatible\'>100%</td>\n";
16297        }
16298        $TestResults .= "</tr>\n";
16299        $TestResults .= "</table>\n";
16300
16301        $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
16302        # problem summary
16303        $Problem_Summary = "<h2>Problem Summary</h2><hr/>\n";
16304        $Problem_Summary .= "<table class='summary'>";
16305        $Problem_Summary .= "<tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr>";
16306
16307        my $Added_Link = "0";
16308        if($Added>0)
16309        {
16310            if($JoinReport) {
16311                $Added_Link = "<a href='#".$Level."_Added' style='color:Blue;'>$Added</a>";
16312            }
16313            else {
16314                $Added_Link = "<a href='#Added' style='color:Blue;'>$Added</a>";
16315            }
16316        }
16317        $META_DATA .= "added:$Added;";
16318        $Problem_Summary .= "<tr><th>Added Symbols</th><td>-</td><td".getStyle("I", "Added", $Added).">$Added_Link</td></tr>\n";
16319
16320        my $Removed_Link = "0";
16321        if($Removed>0)
16322        {
16323            if($JoinReport) {
16324                $Removed_Link = "<a href='#".$Level."_Removed' style='color:Blue;'>$Removed</a>"
16325            }
16326            else {
16327                $Removed_Link = "<a href='#Removed' style='color:Blue;'>$Removed</a>"
16328            }
16329        }
16330        $META_DATA .= "removed:$Removed;";
16331        $Problem_Summary .= "<tr><th>Removed Symbols</th>";
16332        $Problem_Summary .= "<td>High</td><td".getStyle("I", "Removed", $Removed).">$Removed_Link</td></tr>\n";
16333
16334        my $TH_Link = "0";
16335        $TH_Link = "<a href='#".get_Anchor("Type", $Level, "High")."' style='color:Blue;'>$T_Problems_High</a>" if($T_Problems_High>0);
16336        $META_DATA .= "type_problems_high:$T_Problems_High;";
16337        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Data Types</th>";
16338        $Problem_Summary .= "<td>High</td><td".getStyle("T", "High", $T_Problems_High).">$TH_Link</td></tr>\n";
16339
16340        my $TM_Link = "0";
16341        $TM_Link = "<a href='#".get_Anchor("Type", $Level, "Medium")."' style='color:Blue;'>$T_Problems_Medium</a>" if($T_Problems_Medium>0);
16342        $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
16343        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("T", "Medium", $T_Problems_Medium).">$TM_Link</td></tr>\n";
16344
16345        my $TL_Link = "0";
16346        $TL_Link = "<a href='#".get_Anchor("Type", $Level, "Low")."' style='color:Blue;'>$T_Problems_Low</a>" if($T_Problems_Low>0);
16347        $META_DATA .= "type_problems_low:$T_Problems_Low;";
16348        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("T", "Low", $T_Problems_Low).">$TL_Link</td></tr>\n";
16349
16350        my $IH_Link = "0";
16351        $IH_Link = "<a href='#".get_Anchor("Symbol", $Level, "High")."' style='color:Blue;'>$I_Problems_High</a>" if($I_Problems_High>0);
16352        $META_DATA .= "interface_problems_high:$I_Problems_High;";
16353        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Symbols</th>";
16354        $Problem_Summary .= "<td>High</td><td".getStyle("I", "High", $I_Problems_High).">$IH_Link</td></tr>\n";
16355
16356        my $IM_Link = "0";
16357        $IM_Link = "<a href='#".get_Anchor("Symbol", $Level, "Medium")."' style='color:Blue;'>$I_Problems_Medium</a>" if($I_Problems_Medium>0);
16358        $META_DATA .= "interface_problems_medium:$I_Problems_Medium;";
16359        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("I", "Medium", $I_Problems_Medium).">$IM_Link</td></tr>\n";
16360
16361        my $IL_Link = "0";
16362        $IL_Link = "<a href='#".get_Anchor("Symbol", $Level, "Low")."' style='color:Blue;'>$I_Problems_Low</a>" if($I_Problems_Low>0);
16363        $META_DATA .= "interface_problems_low:$I_Problems_Low;";
16364        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("I", "Low", $I_Problems_Low).">$IL_Link</td></tr>\n";
16365
16366        my $ChangedConstants_Link = "0";
16367        if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) {
16368            $ChangedConstants_Link = "<a href='#".get_Anchor("Constant", $Level, "Low")."' style='color:Blue;'>$C_Problems_Low</a>";
16369        }
16370        $META_DATA .= "changed_constants:$C_Problems_Low;";
16371        $Problem_Summary .= "<tr><th>Problems with<br/>Constants</th><td>Low</td><td".getStyle("C", "Low", $C_Problems_Low).">$ChangedConstants_Link</td></tr>\n";
16372
16373        # Safe Changes
16374        if($T_Other)
16375        {
16376            my $TS_Link = "<a href='#".get_Anchor("Type", $Level, "Safe")."' style='color:Blue;'>$T_Other</a>";
16377            $Problem_Summary .= "<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td".getStyle("T", "Safe", $T_Other).">$TS_Link</td></tr>\n";
16378            $META_DATA .= "type_changes_other:$T_Other;";
16379        }
16380
16381        if($I_Other)
16382        {
16383            my $IS_Link = "<a href='#".get_Anchor("Symbol", $Level, "Safe")."' style='color:Blue;'>$I_Other</a>";
16384            $Problem_Summary .= "<tr><th>Other Changes<br/>in Symbols</th><td>-</td><td".getStyle("I", "Safe", $I_Other).">$IS_Link</td></tr>\n";
16385            $META_DATA .= "interface_changes_other:$I_Other;";
16386        }
16387
16388        if($C_Other)
16389        {
16390            my $CS_Link = "<a href='#".get_Anchor("Constant", $Level, "Safe")."' style='color:Blue;'>$C_Other</a>";
16391            $Problem_Summary .= "<tr><th>Other Changes<br/>in Constants</th><td>-</td><td".getStyle("C", "Safe", $C_Other).">$CS_Link</td></tr>\n";
16392            $META_DATA .= "constant_changes_other:$C_Other;";
16393        }
16394
16395        $META_DATA .= "tool_version:$TOOL_VERSION";
16396        $Problem_Summary .= "</table>\n";
16397
16398        return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA);
16399    }
16400}
16401
16402sub getStyle($$$)
16403{
16404    my ($Subj, $Act, $Num) = @_;
16405    my %Style = (
16406        "Added"=>"new",
16407        "Removed"=>"failed",
16408        "Safe"=>"passed",
16409        "Low"=>"warning",
16410        "Medium"=>"failed",
16411        "High"=>"failed"
16412    );
16413
16414    if($Num>0) {
16415        return " class='".$Style{$Act}."'";
16416    }
16417
16418    return "";
16419}
16420
16421sub show_number($)
16422{
16423    if($_[0])
16424    {
16425        my $Num = cut_off_number($_[0], 2, 0);
16426        if($Num eq "0")
16427        {
16428            foreach my $P (3 .. 7)
16429            {
16430                $Num = cut_off_number($_[0], $P, 1);
16431                if($Num ne "0") {
16432                    last;
16433                }
16434            }
16435        }
16436        if($Num eq "0") {
16437            $Num = $_[0];
16438        }
16439        return $Num;
16440    }
16441    return $_[0];
16442}
16443
16444sub cut_off_number($$$)
16445{
16446    my ($num, $digs_to_cut, $z) = @_;
16447    if($num!~/\./)
16448    {
16449        $num .= ".";
16450        foreach (1 .. $digs_to_cut-1) {
16451            $num .= "0";
16452        }
16453    }
16454    elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
16455    {
16456        foreach (1 .. $digs_to_cut - 1 - length($1)) {
16457            $num .= "0";
16458        }
16459    }
16460    elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
16461      $num=sprintf("%.".($digs_to_cut-1)."f", $num);
16462    }
16463    $num=~s/\.[0]+\Z//g;
16464    if($z) {
16465        $num=~s/(\.[1-9]+)[0]+\Z/$1/g;
16466    }
16467    return $num;
16468}
16469
16470sub get_Report_ChangedConstants($$)
16471{
16472    my ($TargetSeverity, $Level) = @_;
16473    my $CHANGED_CONSTANTS = "";
16474
16475    my %ReportMap = ();
16476    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
16477    {
16478        my $Header = $Constants{1}{$Constant}{"Header"};
16479        if(not $Header)
16480        { # added
16481            $Header = $Constants{2}{$Constant}{"Header"}
16482        }
16483
16484        foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
16485        {
16486            if(not defined $CompatRules{$Level}{$Kind}) {
16487                next;
16488            }
16489            if($TargetSeverity ne $CompatRules{$Level}{$Kind}{"Severity"}) {
16490                next;
16491            }
16492            $ReportMap{$Header}{$Constant}{$Kind} = 1;
16493        }
16494    }
16495
16496    if($ReportFormat eq "xml")
16497    { # XML
16498        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16499        {
16500            $CHANGED_CONSTANTS .= "  <header name=\"$HeaderName\">\n";
16501            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16502            {
16503                $CHANGED_CONSTANTS .= "    <constant name=\"$Constant\">\n";
16504                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
16505                {
16506                    my $Change = $CompatRules{$Level}{$Kind}{"Change"};
16507                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16508                    my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
16509
16510                    $CHANGED_CONSTANTS .= "      <problem id=\"$Kind\">\n";
16511                    $CHANGED_CONSTANTS .= "        <change".getXmlParams($Change, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Change</change>\n";
16512                    $CHANGED_CONSTANTS .= "        <effect".getXmlParams($Effect, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Effect</effect>\n";
16513                    if($Overcome) {
16514                        $CHANGED_CONSTANTS .= "        <overcome".getXmlParams($Overcome, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Overcome</overcome>\n";
16515                    }
16516                    $CHANGED_CONSTANTS .= "      </problem>\n";
16517                }
16518                $CHANGED_CONSTANTS .= "    </constant>\n";
16519            }
16520            $CHANGED_CONSTANTS .= "    </header>\n";
16521        }
16522        $CHANGED_CONSTANTS = "<problems_with_constants severity=\"Low\">\n".$CHANGED_CONSTANTS."</problems_with_constants>\n\n";
16523    }
16524    else
16525    { # HTML
16526        my $ProblemsNum = 0;
16527        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16528        {
16529            $CHANGED_CONSTANTS .= "<span class='h_name'>$HeaderName</span><br/>\n";
16530            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16531            {
16532                my $Report = "";
16533
16534                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
16535                {
16536                    my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind});
16537                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16538                    $Report .= "<tr>\n<th>1</th>\n<td>".$Change."</td>\n<td>$Effect</td>\n</tr>\n";
16539                    $ProblemsNum += 1;
16540                }
16541                if($Report)
16542                {
16543                    $Report = $ContentDivStart."<table class='ptable'>\n<tr>\n<th width='2%'></th>\n<th width='47%'>Change</th>\n<th>Effect</th>\n</tr>\n".$Report."</table>\n<br/>\n$ContentDivEnd\n";
16544                    $Report = $ContentSpanStart."<span class='ext'>[+]</span> ".$Constant.$ContentSpanEnd."<br/>\n".$Report;
16545                    $Report = insertIDs($Report);
16546                }
16547                $CHANGED_CONSTANTS .= $Report;
16548            }
16549            $CHANGED_CONSTANTS .= "<br/>\n";
16550        }
16551        if($CHANGED_CONSTANTS)
16552        {
16553            my $Title = "Problems with Constants, $TargetSeverity Severity";
16554            if($TargetSeverity eq "Safe")
16555            { # Safe Changes
16556                $Title = "Other Changes in Constants";
16557            }
16558            if($OldStyle) {
16559                $CHANGED_CONSTANTS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$CHANGED_CONSTANTS;
16560            }
16561            else {
16562                $CHANGED_CONSTANTS = "<h2>$Title <span".getStyle("C", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$CHANGED_CONSTANTS;
16563            }
16564            $CHANGED_CONSTANTS = "<a name='".get_Anchor("Constant", $Level, $TargetSeverity)."'></a>\n".$CHANGED_CONSTANTS.$TOP_REF."<br/>\n";
16565        }
16566    }
16567    return $CHANGED_CONSTANTS;
16568}
16569
16570sub getTitle($$$)
16571{
16572    my ($Header, $Library, $NameSpace) = @_;
16573    my $Title = "";
16574
16575    # if($Library and $Library!~/\.\w+\Z/) {
16576    #     $Library .= " (.$LIB_EXT)";
16577    # }
16578
16579    if($Header and $Library)
16580    {
16581        $Title .= "<span class='h_name'>$Header</span>";
16582        $Title .= ", <span class='lib_name'>$Library</span><br/>\n";
16583    }
16584    elsif($Library) {
16585        $Title .= "<span class='lib_name'>$Library</span><br/>\n";
16586    }
16587    elsif($Header) {
16588        $Title .= "<span class='h_name'>$Header</span><br/>\n";
16589    }
16590
16591    if($NameSpace) {
16592        $Title .= "<span class='ns'>namespace <b>$NameSpace</b></span><br/>\n";
16593    }
16594
16595    return $Title;
16596}
16597
16598sub get_Report_Added($)
16599{
16600    my $Level = $_[0];
16601    my $ADDED_INTERFACES = "";
16602    my %ReportMap = ();
16603    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
16604    {
16605        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
16606        {
16607            if($Kind eq "Added_Symbol")
16608            {
16609                my $HeaderName = $CompleteSignature{2}{$Interface}{"Header"};
16610                my $DyLib = $Symbol_Library{2}{$Interface};
16611                if($Level eq "Source" and $ReportFormat eq "html")
16612                { # do not show library name in HTML report
16613                    $DyLib = "";
16614                }
16615                $ReportMap{$HeaderName}{$DyLib}{$Interface} = 1;
16616            }
16617        }
16618    }
16619    if($ReportFormat eq "xml")
16620    { # XML
16621        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16622        {
16623            $ADDED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
16624            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16625            {
16626                $ADDED_INTERFACES .= "    <library name=\"$DyLib\">\n";
16627                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16628                    $ADDED_INTERFACES .= "      <name>$Interface</name>\n";
16629                }
16630                $ADDED_INTERFACES .= "    </library>\n";
16631            }
16632            $ADDED_INTERFACES .= "  </header>\n";
16633        }
16634        $ADDED_INTERFACES = "<added_symbols>\n".$ADDED_INTERFACES."</added_symbols>\n\n";
16635    }
16636    else
16637    { # HTML
16638        my $Added_Number = 0;
16639        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16640        {
16641            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16642            {
16643                my %NameSpaceSymbols = ();
16644                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16645                    $NameSpaceSymbols{select_Symbol_NS($Interface, 2)}{$Interface} = 1;
16646                }
16647                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16648                {
16649                    $ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
16650                    my @SortedInterfaces = sort {lc(get_Signature($a, 2)) cmp lc(get_Signature($b, 2))} keys(%{$NameSpaceSymbols{$NameSpace}});
16651                    foreach my $Interface (@SortedInterfaces)
16652                    {
16653                        $Added_Number += 1;
16654                        my $Signature = get_Signature($Interface, 2);
16655                        if($NameSpace) {
16656                            $Signature=~s/\b\Q$NameSpace\E::\b//g;
16657                        }
16658                        if($Interface=~/\A(_Z|\?)/)
16659                        {
16660                            if($Signature) {
16661                                $ADDED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Interface</b>]</span>\n<br/>\n<br/>\n".$ContentDivEnd."\n");
16662                            }
16663                            else {
16664                                $ADDED_INTERFACES .= "<span class=\"iname\">".$Interface."</span><br/>\n";
16665                            }
16666                        }
16667                        else
16668                        {
16669                            if($Signature) {
16670                                $ADDED_INTERFACES .= "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
16671                            }
16672                            else {
16673                                $ADDED_INTERFACES .= "<span class=\"iname\">".$Interface."</span><br/>\n";
16674                            }
16675                        }
16676                    }
16677                    $ADDED_INTERFACES .= "<br/>\n";
16678                }
16679            }
16680        }
16681        if($ADDED_INTERFACES)
16682        {
16683            my $Anchor = "<a name='Added'></a>";
16684            if($JoinReport) {
16685                $Anchor = "<a name='".$Level."_Added'></a>";
16686            }
16687            if($OldStyle) {
16688                $ADDED_INTERFACES = "<h2>Added Symbols ($Added_Number)</h2><hr/>\n".$ADDED_INTERFACES;
16689            }
16690            else {
16691                $ADDED_INTERFACES = "<h2>Added Symbols <span".getStyle("I", "Added", $Added_Number).">&nbsp;$Added_Number&nbsp;</span></h2><hr/>\n".$ADDED_INTERFACES;
16692            }
16693            $ADDED_INTERFACES = $Anchor.$ADDED_INTERFACES.$TOP_REF."<br/>\n";
16694        }
16695    }
16696    return $ADDED_INTERFACES;
16697}
16698
16699sub get_Report_Removed($)
16700{
16701    my $Level = $_[0];
16702    my $REMOVED_INTERFACES = "";
16703    my %ReportMap = ();
16704    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
16705    {
16706        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
16707        {
16708            if($Kind eq "Removed_Symbol")
16709            {
16710                my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
16711                my $DyLib = $Symbol_Library{1}{$Symbol};
16712                if($Level eq "Source" and $ReportFormat eq "html")
16713                { # do not show library name in HTML report
16714                    $DyLib = "";
16715                }
16716                $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
16717            }
16718        }
16719    }
16720    if($ReportFormat eq "xml")
16721    { # XML
16722        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16723        {
16724            $REMOVED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
16725            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16726            {
16727                $REMOVED_INTERFACES .= "    <library name=\"$DyLib\">\n";
16728                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16729                    $REMOVED_INTERFACES .= "      <name>$Symbol</name>\n";
16730                }
16731                $REMOVED_INTERFACES .= "    </library>\n";
16732            }
16733            $REMOVED_INTERFACES .= "  </header>\n";
16734        }
16735        $REMOVED_INTERFACES = "<removed_symbols>\n".$REMOVED_INTERFACES."</removed_symbols>\n\n";
16736    }
16737    else
16738    { # HTML
16739        my $Removed_Number = 0;
16740        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16741        {
16742            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16743            {
16744                my %NameSpaceSymbols = ();
16745                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16746                    $NameSpaceSymbols{select_Symbol_NS($Interface, 1)}{$Interface} = 1;
16747                }
16748                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16749                {
16750                    $REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
16751                    my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NameSpaceSymbols{$NameSpace}});
16752                    foreach my $Symbol (@SortedInterfaces)
16753                    {
16754                        $Removed_Number += 1;
16755                        my $SubReport = "";
16756                        my $Signature = get_Signature($Symbol, 1);
16757                        if($NameSpace) {
16758                            $Signature=~s/\b\Q$NameSpace\E::\b//g;
16759                        }
16760                        if($Symbol=~/\A(_Z|\?)/)
16761                        {
16762                            if($Signature) {
16763                                $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Symbol</b>]</span>\n<br/>\n<br/>\n".$ContentDivEnd."\n");
16764                            }
16765                            else {
16766                                $REMOVED_INTERFACES .= "<span class=\"iname\">".$Symbol."</span><br/>\n";
16767                            }
16768                        }
16769                        else
16770                        {
16771                            if($Signature) {
16772                                $REMOVED_INTERFACES .= "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
16773                            }
16774                            else {
16775                                $REMOVED_INTERFACES .= "<span class=\"iname\">".$Symbol."</span><br/>\n";
16776                            }
16777                        }
16778                    }
16779                }
16780                $REMOVED_INTERFACES .= "<br/>\n";
16781            }
16782        }
16783        if($REMOVED_INTERFACES)
16784        {
16785            my $Anchor = "<a name='Removed'></a><a name='Withdrawn'></a>";
16786            if($JoinReport) {
16787                $Anchor = "<a name='".$Level."_Removed'></a><a name='".$Level."_Withdrawn'></a>";
16788            }
16789            if($OldStyle) {
16790                $REMOVED_INTERFACES = "<h2>Removed Symbols ($Removed_Number)</h2><hr/>\n".$REMOVED_INTERFACES;
16791            }
16792            else {
16793                $REMOVED_INTERFACES = "<h2>Removed Symbols <span".getStyle("I", "Removed", $Removed_Number).">&nbsp;$Removed_Number&nbsp;</span></h2><hr/>\n".$REMOVED_INTERFACES;
16794            }
16795
16796            $REMOVED_INTERFACES = $Anchor.$REMOVED_INTERFACES.$TOP_REF."<br/>\n";
16797        }
16798    }
16799    return $REMOVED_INTERFACES;
16800}
16801
16802sub getXmlParams($$)
16803{
16804    my ($Content, $Problem) = @_;
16805    return "" if(not $Content or not $Problem);
16806    my %XMLparams = ();
16807    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
16808    {
16809        my $Macro = "\@".lc($Attr);
16810        if($Content=~/\Q$Macro\E/) {
16811            $XMLparams{lc($Attr)} = $Problem->{$Attr};
16812        }
16813    }
16814    my @PString = ();
16815    foreach my $P (sort {$b cmp $a} keys(%XMLparams)) {
16816        push(@PString, $P."=\"".xmlSpecChars($XMLparams{$P})."\"");
16817    }
16818    if(@PString) {
16819        return " ".join(" ", @PString);
16820    }
16821    else {
16822        return "";
16823    }
16824}
16825
16826sub addMarkup($)
16827{
16828    my $Content = $_[0];
16829    # auto-markup
16830    $Content=~s/\n[ ]*//; # spaces
16831    $Content=~s!(\@\w+\s*\(\@\w+\))!<nowrap>$1</nowrap>!g; # @old_type (@old_size)
16832    $Content=~s!(... \(\w+\))!<nowrap><b>$1</b></nowrap>!g; # ... (va_list)
16833    $Content=~s!<nowrap>(.+?)</nowrap>!<span class='nowrap'>$1</span>!g;
16834    $Content=~s!([2-9]\))!<br/>$1!g; # 1), 2), ...
16835    if($Content=~/\ANOTE:/)
16836    { # notes
16837        $Content=~s!(NOTE):!<b>$1</b>:!g;
16838    }
16839    else {
16840        $Content=~s!(NOTE):!<br/><b>$1</b>:!g;
16841    }
16842    $Content=~s! (out)-! <b>$1</b>-!g; # out-parameters
16843    my @Keywords = (
16844        "void",
16845        "const",
16846        "static",
16847        "restrict",
16848        "volatile",
16849        "register",
16850        "virtual"
16851    );
16852    my $MKeys = join("|", @Keywords);
16853    foreach (@Keywords) {
16854        $MKeys .= "|non-".$_;
16855    }
16856    $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1<b>$2</b>$3!ig; # intrinsic types, modifiers
16857
16858    # Markdown
16859    $Content=~s!\*\*([\w\-]+)\*\*!<b>$1</b>!ig;
16860    $Content=~s!\*([\w\-]+)\*!<i>$1</i>!ig;
16861    return $Content;
16862}
16863
16864sub applyMacroses($$$$)
16865{
16866    my ($Level, $Kind, $Content, $Problem) = @_;
16867    return "" if(not $Content or not $Problem);
16868    $Problem->{"Word_Size"} = $WORD_SIZE{2};
16869    $Content = addMarkup($Content);
16870    # macros
16871    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
16872    {
16873        my $Macro = "\@".lc($Attr);
16874        my $Value = $Problem->{$Attr};
16875        if(not defined $Value
16876        or $Value eq "") {
16877            next;
16878        }
16879
16880        if(index($Content, $Macro)==-1) {
16881            next;
16882        }
16883
16884        if($Kind!~/\A(Changed|Added|Removed)_Constant\Z/
16885        and $Kind!~/_Type_/
16886        and $Value=~/\s\(/ and $Value!~/['"]/)
16887        { # functions
16888            $Value=~s/\s*\[[\w\-]+\]//g; # remove quals
16889            $Value=~s/\s[a-z]\w*(\)|,)/$1/ig; # remove parameter names
16890            $Value = black_name($Value);
16891        }
16892        elsif($Value=~/\s/) {
16893            $Value = "<span class='value'>".htmlSpecChars($Value)."</span>";
16894        }
16895        elsif($Value=~/\A\d+\Z/
16896        and ($Attr eq "Old_Size" or $Attr eq "New_Size"))
16897        { # bits to bytes
16898            if($Value % $BYTE_SIZE)
16899            { # bits
16900                if($Value==1) {
16901                    $Value = "<b>".$Value."</b> bit";
16902                }
16903                else {
16904                    $Value = "<b>".$Value."</b> bits";
16905                }
16906            }
16907            else
16908            { # bytes
16909                $Value /= $BYTE_SIZE;
16910                if($Value==1) {
16911                    $Value = "<b>".$Value."</b> byte";
16912                }
16913                else {
16914                    $Value = "<b>".$Value."</b> bytes";
16915                }
16916            }
16917        }
16918        else
16919        {
16920            $Value = "<b>".htmlSpecChars($Value)."</b>";
16921        }
16922        $Content=~s/\Q$Macro\E/$Value/g;
16923    }
16924
16925    if($Content=~/(\A|[^\@\w])\@\w/)
16926    {
16927        if(not $IncompleteRules{$Level}{$Kind})
16928        { # only one warning
16929            printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")");
16930            $IncompleteRules{$Level}{$Kind} = 1;
16931        }
16932    }
16933    return $Content;
16934}
16935
16936sub get_Report_SymbolProblems($$)
16937{
16938    my ($TargetSeverity, $Level) = @_;
16939    my $INTERFACE_PROBLEMS = "";
16940    my (%ReportMap, %SymbolChanges) = ();
16941
16942    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
16943    {
16944        my ($SN, $SS, $SV) = separate_symbol($Symbol);
16945        if($SV and defined $CompatProblems{$Level}{$SN}) {
16946            next;
16947        }
16948        my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
16949        my $DyLib = $Symbol_Library{1}{$Symbol};
16950        if(not $DyLib and my $VSym = $SymVer{1}{$Symbol})
16951        { # Symbol with Version
16952            $DyLib = $Symbol_Library{1}{$VSym};
16953        }
16954        if(not $DyLib)
16955        { # const global data
16956            $DyLib = "";
16957        }
16958        if($Level eq "Source" and $ReportFormat eq "html")
16959        { # do not show library name in HTML report
16960            $DyLib = "";
16961        }
16962
16963        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
16964        {
16965            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols"
16966            and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol")
16967            {
16968                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16969                foreach my $Location (sort keys(%{$CompatProblems{$Level}{$Symbol}{$Kind}}))
16970                {
16971                    if($Severity eq $TargetSeverity)
16972                    {
16973                        $SymbolChanges{$Symbol}{$Kind} = $CompatProblems{$Level}{$Symbol}{$Kind};
16974                        $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
16975                    }
16976                }
16977            }
16978        }
16979    }
16980
16981    if($ReportFormat eq "xml")
16982    { # XML
16983        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16984        {
16985            $INTERFACE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
16986            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16987            {
16988                $INTERFACE_PROBLEMS .= "    <library name=\"$DyLib\">\n";
16989                my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$ReportMap{$HeaderName}{$DyLib}});
16990                foreach my $Symbol (@SortedInterfaces)
16991                {
16992                    $INTERFACE_PROBLEMS .= "      <symbol name=\"$Symbol\">\n";
16993                    foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
16994                    {
16995                        foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16996                        {
16997                            my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
16998                            $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
16999
17000                            $INTERFACE_PROBLEMS .= "        <problem id=\"$Kind\">\n";
17001                            my $Change = $CompatRules{$Level}{$Kind}{"Change"};
17002                            $INTERFACE_PROBLEMS .= "          <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
17003                            my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
17004                            $INTERFACE_PROBLEMS .= "          <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
17005                            if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
17006                                $INTERFACE_PROBLEMS .= "          <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
17007                            }
17008                            $INTERFACE_PROBLEMS .= "        </problem>\n";
17009                        }
17010                    }
17011                    $INTERFACE_PROBLEMS .= "      </symbol>\n";
17012                }
17013                $INTERFACE_PROBLEMS .= "    </library>\n";
17014            }
17015            $INTERFACE_PROBLEMS .= "  </header>\n";
17016        }
17017        $INTERFACE_PROBLEMS = "<problems_with_symbols severity=\"$TargetSeverity\">\n".$INTERFACE_PROBLEMS."</problems_with_symbols>\n\n";
17018    }
17019    else
17020    { # HTML
17021        my $ProblemsNum = 0;
17022        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
17023        {
17024            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
17025            {
17026                my (%NameSpaceSymbols, %NewSignature) = ();
17027                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
17028                    $NameSpaceSymbols{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1;
17029                }
17030                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
17031                {
17032                    $INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace);
17033                    my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} sort keys(%{$NameSpaceSymbols{$NameSpace}});
17034                    foreach my $Symbol (@SortedInterfaces)
17035                    {
17036                        my $Signature = get_Signature($Symbol, 1);
17037                        my $SYMBOL_REPORT = "";
17038                        my $ProblemNum = 1;
17039                        foreach my $Kind (sort keys(%{$SymbolChanges{$Symbol}}))
17040                        {
17041                            foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
17042                            {
17043                                my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
17044                                $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
17045                                if($Problem{"New_Signature"}) {
17046                                    $NewSignature{$Symbol} = $Problem{"New_Signature"};
17047                                }
17048                                if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
17049                                {
17050                                    my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
17051                                    $SYMBOL_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td>".$Change."</td>\n<td>".$Effect."</td>\n</tr>\n";
17052                                    $ProblemNum += 1;
17053                                    $ProblemsNum += 1;
17054                                }
17055                            }
17056                        }
17057                        $ProblemNum -= 1;
17058                        if($SYMBOL_REPORT)
17059                        {
17060                            my $ShowSymbol = $Symbol;
17061                            if($Signature) {
17062                                $ShowSymbol = highLight_Signature_Italic_Color($Signature);
17063                            }
17064
17065                            if($NameSpace)
17066                            {
17067                                $SYMBOL_REPORT = cut_Namespace($SYMBOL_REPORT, $NameSpace);
17068                                $ShowSymbol = cut_Namespace($ShowSymbol, $NameSpace);
17069                            }
17070
17071                            $INTERFACE_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".$ShowSymbol;
17072                            if($OldStyle) {
17073                                $INTERFACE_PROBLEMS .= " ($ProblemNum)";
17074                            }
17075                            else {
17076                                $INTERFACE_PROBLEMS .= " <span".getStyle("I", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
17077                            }
17078                            $INTERFACE_PROBLEMS .= $ContentSpanEnd."<br/>\n";
17079                            $INTERFACE_PROBLEMS .= $ContentDivStart."\n";
17080
17081                            if(my $NSign = $NewSignature{$Symbol})
17082                            { # argument list changed to
17083                                if($NameSpace) {
17084                                    $NSign = cut_Namespace($NSign, $NameSpace);
17085                                }
17086                                $INTERFACE_PROBLEMS .= "\n<span class='new_sign_lbl'>changed to:</span>\n<br/>\n<span class='new_sign'>".highLight_Signature_Italic_Color($NSign)."</span><br/>\n";
17087                            }
17088
17089                            if($Symbol=~/\A(_Z|\?)/) {
17090                                $INTERFACE_PROBLEMS .= "<span class='mangled'>&#160;&#160;&#160;&#160;[symbol: <b>$Symbol</b>]</span><br/>\n";
17091                            }
17092
17093                            $INTERFACE_PROBLEMS .= "<table class='ptable'>\n<tr>\n<th width='2%'></th>\n<th width='47%'>Change</th>\n<th>Effect</th>\n</tr>\n$SYMBOL_REPORT</table>\n<br/>\n";
17094                            $INTERFACE_PROBLEMS .= $ContentDivEnd;
17095                        }
17096                    }
17097                    $INTERFACE_PROBLEMS .= "<br/>\n";
17098                }
17099            }
17100        }
17101
17102        if($INTERFACE_PROBLEMS)
17103        {
17104            $INTERFACE_PROBLEMS = insertIDs($INTERFACE_PROBLEMS);
17105            my $Title = "Problems with Symbols, $TargetSeverity Severity";
17106            if($TargetSeverity eq "Safe")
17107            { # Safe Changes
17108                $Title = "Other Changes in Symbols";
17109            }
17110            if($OldStyle) {
17111                $INTERFACE_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$INTERFACE_PROBLEMS;
17112            }
17113            else {
17114                $INTERFACE_PROBLEMS = "<h2>$Title <span".getStyle("I", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$INTERFACE_PROBLEMS;
17115            }
17116            $INTERFACE_PROBLEMS = "<a name=\'".get_Anchor("Symbol", $Level, $TargetSeverity)."\'></a><a name=\'".get_Anchor("Interface", $Level, $TargetSeverity)."\'></a>\n".$INTERFACE_PROBLEMS.$TOP_REF."<br/>\n";
17117        }
17118    }
17119    return $INTERFACE_PROBLEMS;
17120}
17121
17122sub cut_Namespace($$)
17123{
17124    my ($N, $Ns) = @_;
17125    $N=~s/\b\Q$Ns\E:://g;
17126    return $N;
17127}
17128
17129sub get_Report_TypeProblems($$)
17130{
17131    my ($TargetSeverity, $Level) = @_;
17132    my $TYPE_PROBLEMS = "";
17133
17134    my %ReportMap = ();
17135    my %TypeChanges_Sev = ();
17136
17137    foreach my $TypeName (keys(%{$TypeChanges{$Level}}))
17138    {
17139        my $HeaderName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Header"};
17140
17141        foreach my $Kind (keys(%{$TypeChanges{$Level}{$TypeName}}))
17142        {
17143            foreach my $Location (keys(%{$TypeChanges{$Level}{$TypeName}{$Kind}}))
17144            {
17145                my $Target = $TypeChanges{$Level}{$TypeName}{$Kind}{$Location}{"Target"};
17146                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
17147
17148                if($Severity eq $TargetSeverity)
17149                {
17150                    $ReportMap{$HeaderName}{$TypeName} = 1;
17151                    $TypeChanges_Sev{$TypeName}{$Kind}{$Location} = $TypeChanges{$Level}{$TypeName}{$Kind}{$Location};
17152                }
17153            }
17154        }
17155    }
17156
17157    if($ReportFormat eq "xml")
17158    { # XML
17159        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
17160        {
17161            $TYPE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
17162            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}}))
17163            {
17164                my (%Kinds_Locations, %Kinds_Target) = ();
17165                $TYPE_PROBLEMS .= "    <type name=\"".xmlSpecChars($TypeName)."\">\n";
17166                foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}}))
17167                {
17168                    foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
17169                    {
17170                        $Kinds_Locations{$Kind}{$Location} = 1;
17171
17172                        my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Location}{"Target"};
17173                        if($Kinds_Target{$Kind}{$Target}) {
17174                            next;
17175                        }
17176                        $Kinds_Target{$Kind}{$Target} = 1;
17177
17178                        my %Problem = %{$TypeChanges_Sev{$TypeName}{$Kind}{$Location}};
17179                        $TYPE_PROBLEMS .= "      <problem id=\"$Kind\">\n";
17180                        my $Change = $CompatRules{$Level}{$Kind}{"Change"};
17181                        $TYPE_PROBLEMS .= "        <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
17182                        my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
17183                        $TYPE_PROBLEMS .= "        <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
17184                        if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
17185                            $TYPE_PROBLEMS .= "        <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
17186                        }
17187                        $TYPE_PROBLEMS .= "      </problem>\n";
17188                    }
17189                }
17190                $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
17191                if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
17192                    $TYPE_PROBLEMS .= showVTables($TypeName);
17193                }
17194                $TYPE_PROBLEMS .= "    </type>\n";
17195            }
17196            $TYPE_PROBLEMS .= "  </header>\n";
17197        }
17198        $TYPE_PROBLEMS = "<problems_with_types severity=\"$TargetSeverity\">\n".$TYPE_PROBLEMS."</problems_with_types>\n\n";
17199    }
17200    else
17201    { # HTML
17202        my $ProblemsNum = 0;
17203        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
17204        {
17205            my (%NameSpace_Type) = ();
17206            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) {
17207                $NameSpace_Type{select_Type_NS($TypeName, 1)}{$TypeName} = 1;
17208            }
17209            foreach my $NameSpace (sort keys(%NameSpace_Type))
17210            {
17211                $TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace);
17212                my @SortedTypes = sort {lc(show_Type($a, 0, 1)) cmp lc(show_Type($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}});
17213                foreach my $TypeName (@SortedTypes)
17214                {
17215                    my $ProblemNum = 1;
17216                    my $TYPE_REPORT = "";
17217                    my (%Kinds_Locations, %Kinds_Target) = ();
17218
17219                    foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges_Sev{$TypeName}}))
17220                    {
17221                        foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges_Sev{$TypeName}{$Kind}}))
17222                        {
17223                            $Kinds_Locations{$Kind}{$Location} = 1;
17224
17225                            my $Target = $TypeChanges_Sev{$TypeName}{$Kind}{$Location}{"Target"};
17226                            if($Kinds_Target{$Kind}{$Target}) {
17227                                next;
17228                            }
17229                            $Kinds_Target{$Kind}{$Target} = 1;
17230
17231                            my %Problem = %{$TypeChanges_Sev{$TypeName}{$Kind}{$Location}};
17232                            if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
17233                            {
17234                                my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
17235                                $TYPE_REPORT .= "<tr>\n<th>$ProblemNum</th>\n<td>".$Change."</td>\n<td>$Effect</td>\n</tr>\n";
17236                                $ProblemNum += 1;
17237                                $ProblemsNum += 1;
17238                            }
17239                        }
17240                    }
17241                    $ProblemNum -= 1;
17242                    if($TYPE_REPORT)
17243                    {
17244                        my $Affected = getAffectedSymbols($Level, $TypeName, \%Kinds_Locations);
17245                        my $ShowVTables = "";
17246                        if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%Kinds_Locations)) {
17247                            $ShowVTables = showVTables($TypeName);
17248                        }
17249
17250                        my $ShowType = show_Type($TypeName, 1, 1);
17251
17252                        if($NameSpace)
17253                        {
17254                            $TYPE_REPORT = cut_Namespace($TYPE_REPORT, $NameSpace);
17255                            $ShowType = cut_Namespace($ShowType, $NameSpace);
17256                            $Affected = cut_Namespace($Affected, $NameSpace);
17257                            $ShowVTables = cut_Namespace($ShowVTables, $NameSpace);
17258                        }
17259
17260                        $TYPE_PROBLEMS .= $ContentSpanStart."<span class='ext'>[+]</span> ".$ShowType;
17261                        if($OldStyle) {
17262                            $TYPE_PROBLEMS .= " ($ProblemNum)";
17263                        }
17264                        else {
17265                            $TYPE_PROBLEMS .= " <span".getStyle("T", $TargetSeverity, $ProblemNum).">&nbsp;$ProblemNum&nbsp;</span>";
17266                        }
17267                        $TYPE_PROBLEMS .= $ContentSpanEnd;
17268                        $TYPE_PROBLEMS .= "<br/>\n".$ContentDivStart."<table class='ptable'><tr>\n";
17269                        $TYPE_PROBLEMS .= "<th width='2%'></th><th width='47%'>Change</th>\n";
17270                        $TYPE_PROBLEMS .= "<th>Effect</th></tr>".$TYPE_REPORT."</table>\n";
17271                        $TYPE_PROBLEMS .= $ShowVTables.$Affected."<br/><br/>".$ContentDivEnd."\n";
17272                    }
17273                }
17274                $TYPE_PROBLEMS .= "<br/>\n";
17275            }
17276        }
17277
17278        if($TYPE_PROBLEMS)
17279        {
17280            $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
17281            my $Title = "Problems with Data Types, $TargetSeverity Severity";
17282            if($TargetSeverity eq "Safe")
17283            { # Safe Changes
17284                $Title = "Other Changes in Data Types";
17285            }
17286            if($OldStyle) {
17287                $TYPE_PROBLEMS = "<h2>$Title ($ProblemsNum)</h2><hr/>\n".$TYPE_PROBLEMS;
17288            }
17289            else {
17290                $TYPE_PROBLEMS = "<h2>$Title <span".getStyle("T", $TargetSeverity, $ProblemsNum).">&nbsp;$ProblemsNum&nbsp;</span></h2><hr/>\n".$TYPE_PROBLEMS;
17291            }
17292            $TYPE_PROBLEMS = "<a name=\'".get_Anchor("Type", $Level, $TargetSeverity)."\'></a>\n".$TYPE_PROBLEMS.$TOP_REF."<br/>\n";
17293        }
17294    }
17295    return $TYPE_PROBLEMS;
17296}
17297
17298sub show_Type($$$)
17299{
17300    my ($Name, $Html, $LibVersion) = @_;
17301    my $TType = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$Name}}{"Type"};
17302    $TType = lc($TType);
17303    if($TType=~/struct|union|enum/) {
17304        $Name=~s/\A\Q$TType\E //g;
17305    }
17306    if($Html) {
17307        $Name = "<span class='ttype'>".$TType."</span> ".htmlSpecChars($Name);
17308    }
17309    else {
17310        $Name = $TType." ".$Name;
17311    }
17312    return $Name;
17313}
17314
17315sub get_Anchor($$$)
17316{
17317    my ($Kind, $Level, $Severity) = @_;
17318    if($JoinReport)
17319    {
17320        if($Severity eq "Safe") {
17321            return "Other_".$Level."_Changes_In_".$Kind."s";
17322        }
17323        else {
17324            return $Kind."_".$Level."_Problems_".$Severity;
17325        }
17326    }
17327    else
17328    {
17329        if($Severity eq "Safe") {
17330            return "Other_Changes_In_".$Kind."s";
17331        }
17332        else {
17333            return $Kind."_Problems_".$Severity;
17334        }
17335    }
17336}
17337
17338sub showVTables($)
17339{
17340    my $TypeName = $_[0];
17341    my $TypeId1 = $TName_Tid{1}{$TypeName};
17342    my %Type1 = get_Type($TypeId1, 1);
17343    if(defined $Type1{"VTable"}
17344    and keys(%{$Type1{"VTable"}}))
17345    {
17346        my $TypeId2 = $TName_Tid{2}{$TypeName};
17347        my %Type2 = get_Type($TypeId2, 2);
17348        if(defined $Type2{"VTable"}
17349        and keys(%{$Type2{"VTable"}}))
17350        {
17351            my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
17352            my %Entries = ();
17353            foreach my $Index (sort {int($a)<=>int($b)} (keys(%Indexes)))
17354            {
17355                $Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index});
17356                $Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index});
17357            }
17358            my $VTABLES = "";
17359            if($ReportFormat eq "xml")
17360            { # XML
17361                $VTABLES .= "      <vtable>\n";
17362                foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
17363                {
17364                    $VTABLES .= "        <entry offset=\"".$Index."\">\n";
17365                    $VTABLES .= "          <old>".xmlSpecChars($Entries{$Index}{"E1"})."</old>\n";
17366                    $VTABLES .= "          <new>".xmlSpecChars($Entries{$Index}{"E2"})."</new>\n";
17367                    $VTABLES .= "        </entry>\n";
17368                }
17369                $VTABLES .= "      </vtable>\n\n";
17370            }
17371            else
17372            { # HTML
17373                $VTABLES .= "<table class='vtable'>";
17374                $VTABLES .= "<tr><th>Offset</th>";
17375                $VTABLES .= "<th>Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries</th>";
17376                $VTABLES .= "<th>Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries</th></tr>";
17377                foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
17378                {
17379                    my ($Color1, $Color2) = ("", "");
17380
17381                    my $E1 = $Entries{$Index}{"E1"};
17382                    my $E2 = $Entries{$Index}{"E2"};
17383
17384                    if($E1 ne $E2
17385                    and $E1!~/ 0x/
17386                    and $E2!~/ 0x/)
17387                    {
17388                        if($Entries{$Index}{"E1"})
17389                        {
17390                            $Color1 = " class='failed'";
17391                            $Color2 = " class='failed'";
17392                        }
17393                        else {
17394                            $Color2 = " class='warning'";
17395                        }
17396                    }
17397                    $VTABLES .= "<tr><th>".$Index."</th>\n";
17398                    $VTABLES .= "<td$Color1>".htmlSpecChars($Entries{$Index}{"E1"})."</td>\n";
17399                    $VTABLES .= "<td$Color2>".htmlSpecChars($Entries{$Index}{"E2"})."</td></tr>\n";
17400                }
17401                $VTABLES .= "</table><br/>\n";
17402                $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd;
17403                $VTABLES = $ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."<br/>\n".$VTABLES;
17404            }
17405            return $VTABLES;
17406        }
17407    }
17408    return "";
17409}
17410
17411sub simpleVEntry($)
17412{
17413    my $VEntry = $_[0];
17414    if(not defined $VEntry
17415    or $VEntry eq "") {
17416        return "";
17417    }
17418
17419    $VEntry=~s/ \[.+?\]\Z//; # support for ABI Dumper
17420    $VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks
17421    $VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo
17422    if($VEntry=~/\A_ZThn.+\Z/) {
17423        $VEntry = "non-virtual thunk";
17424    }
17425    $VEntry=~s/\A\(int \(\*\)\(...\)\)\s*([a-z_])/$1/i;
17426    # support for old GCC versions
17427    $VEntry=~s/\A0u\Z/(int (*)(...))0/;
17428    $VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/;
17429    $VEntry=~s/\A&_Z\Z/& _Z/;
17430    $VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors
17431    return $VEntry;
17432}
17433
17434sub adjustParamPos($$$)
17435{
17436    my ($Pos, $Symbol, $LibVersion) = @_;
17437    if(defined $CompleteSignature{$LibVersion}{$Symbol})
17438    {
17439        if(not $CompleteSignature{$LibVersion}{$Symbol}{"Static"}
17440        and $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
17441        {
17442            return $Pos-1;
17443        }
17444
17445        return $Pos;
17446    }
17447
17448    return undef;
17449}
17450
17451sub getParamPos($$$)
17452{
17453    my ($Name, $Symbol, $LibVersion) = @_;
17454
17455    if(defined $CompleteSignature{$LibVersion}{$Symbol}
17456    and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"})
17457    {
17458        my $Info = $CompleteSignature{$LibVersion}{$Symbol};
17459        foreach (keys(%{$Info->{"Param"}}))
17460        {
17461            if($Info->{"Param"}{$_}{"name"} eq $Name)
17462            {
17463                return $_;
17464            }
17465        }
17466    }
17467
17468    return undef;
17469}
17470
17471sub getParamName($)
17472{
17473    my $Loc = $_[0];
17474    $Loc=~s/\->.*//g;
17475    return $Loc;
17476}
17477
17478sub getAffectedSymbols($$$)
17479{
17480    my ($Level, $Target_TypeName, $Kinds_Locations) = @_;
17481
17482    my $LIMIT = 10;
17483    if(defined $AffectLimit) {
17484        $LIMIT = $AffectLimit;
17485    }
17486
17487    my @Kinds = sort keys(%{$Kinds_Locations});
17488    my %KLocs = ();
17489    foreach my $Kind (@Kinds)
17490    {
17491        my @Locs = sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$Kinds_Locations->{$Kind}});
17492        $KLocs{$Kind} = \@Locs;
17493    }
17494
17495    my %SymLocKind = ();
17496    foreach my $Symbol (sort keys(%{$TypeProblemsIndex{$Level}{$Target_TypeName}}))
17497    {
17498        if(index($Symbol, "_Z")==0
17499        and $Symbol=~/(C2|D2|D0)[EI]/)
17500        { # duplicated problems for C2 constructors, D2 and D0 destructors
17501            next;
17502        }
17503
17504        foreach my $Kind (@Kinds)
17505        {
17506            foreach my $Loc (@{$KLocs{$Kind}})
17507            {
17508                if(not defined $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}) {
17509                    next;
17510                }
17511
17512                if(index($Symbol, "\@")!=-1
17513                or index($Symbol, "\$")!=-1)
17514                {
17515                    my ($SN, $SS, $SV) = separate_symbol($Symbol);
17516
17517                    if($Level eq "Source")
17518                    { # remove symbol version
17519                        $Symbol = $SN;
17520                    }
17521
17522                    if($SV and defined $CompatProblems{$Level}{$SN}
17523                    and defined $CompatProblems{$Level}{$SN}{$Kind}{$Loc})
17524                    { # duplicated problems for versioned symbols
17525                        next;
17526                    }
17527                }
17528
17529                my $Type_Name = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}{"Type_Name"};
17530                if($Type_Name ne $Target_TypeName) {
17531                    next;
17532                }
17533
17534                $SymLocKind{$Symbol}{$Loc}{$Kind} = 1;
17535                last;
17536            }
17537        }
17538    }
17539
17540    %KLocs = (); # clear
17541
17542    my %SymSel = ();
17543    my $Num = 0;
17544    foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymLocKind))
17545    {
17546        LOOP: foreach my $Loc (sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$SymLocKind{$Symbol}}))
17547        {
17548            foreach my $Kind (sort keys(%{$SymLocKind{$Symbol}{$Loc}}))
17549            {
17550                $SymSel{$Symbol}{"Loc"} = $Loc;
17551                $SymSel{$Symbol}{"Kind"} = $Kind;
17552                last LOOP;
17553            }
17554        }
17555
17556        $Num += 1;
17557
17558        if($Num>=$LIMIT) {
17559            last;
17560        }
17561    }
17562
17563    my $Affected = "";
17564
17565    if($ReportFormat eq "xml")
17566    { # XML
17567        $Affected .= "      <affected>\n";
17568
17569        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
17570        {
17571            my $Kind = $SymSel{$Symbol}{"Kind"};
17572            my $Loc = $SymSel{$Symbol}{"Loc"};
17573
17574            my $PName = getParamName($Loc);
17575            my $Desc = getAffectDesc($Level, $Symbol, $Kind, $Loc);
17576
17577            my $Target = "";
17578            if($PName)
17579            {
17580                $Target .= " param=\"$PName\"";
17581                $Desc=~s/parameter $PName /parameter \@param /;
17582            }
17583            elsif($Loc=~/\Aretval(\-|\Z)/i) {
17584                $Target .= " affected=\"retval\"";
17585            }
17586            elsif($Loc=~/\Athis(\-|\Z)/i) {
17587                $Target .= " affected=\"this\"";
17588            }
17589
17590            if($Desc=~s/\AField ([^\s]+) /Field \@field /) {
17591                $Target .= " field=\"$1\"";
17592            }
17593
17594            $Affected .= "        <symbol name=\"$Symbol\"$Target>\n";
17595            $Affected .= "          <comment>".xmlSpecChars($Desc)."</comment>\n";
17596            $Affected .= "        </symbol>\n";
17597        }
17598        $Affected .= "      </affected>\n";
17599    }
17600    else
17601    { # HTML
17602        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
17603        {
17604            my $Kind = $SymSel{$Symbol}{"Kind"};
17605            my $Loc = $SymSel{$Symbol}{"Loc"};
17606
17607            my $Desc = getAffectDesc($Level, $Symbol, $Kind, $Loc);
17608            my $S = get_Signature($Symbol, 1);
17609            my $PName = getParamName($Loc);
17610            my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1);
17611
17612            $Affected .= "<span class='iname_a'>".highLight_Signature_PPos_Italic($S, $Pos, 1, 0, 0)."</span><br/>\n";
17613            $Affected .= "<div class='affect'>".htmlSpecChars($Desc)."</div>\n";
17614        }
17615
17616        if(keys(%SymLocKind)>$LIMIT) {
17617            $Affected .= " <b>...</b>\n<br/>\n"; # and others ...
17618        }
17619
17620        $Affected = "<div class='affected'>".$Affected."</div>\n";
17621        if($Affected)
17622        {
17623            my $Num = keys(%SymLocKind);
17624            my $Per = show_number($Num*100/keys(%{$CheckedSymbols{$Level}}));
17625            $Affected = $ContentDivStart.$Affected.$ContentDivEnd;
17626            $Affected = $ContentSpanStart_Affected."[+] affected symbols: $Num ($Per\%)".$ContentSpanEnd.$Affected;
17627        }
17628    }
17629
17630    return $Affected;
17631}
17632
17633sub cmpLocations($$)
17634{
17635    my ($L1, $L2) = @_;
17636    if($L2=~/\A(retval|this)\b/
17637    and $L1!~/\A(retval|this)\b/)
17638    {
17639        if($L1!~/\-\>/) {
17640            return 1;
17641        }
17642        elsif($L2=~/\-\>/) {
17643            return 1;
17644        }
17645    }
17646    return 0;
17647}
17648
17649sub getAffectDesc($$$$)
17650{
17651    my ($Level, $Symbol, $Kind, $Location) = @_;
17652
17653    my %Problem = %{$CompatProblems{$Level}{$Symbol}{$Kind}{$Location}};
17654
17655    my $Location_I = $Location;
17656    $Location=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field
17657
17658    my @Sentence = ();
17659
17660    if($Kind eq "Overridden_Virtual_Method"
17661    or $Kind eq "Overridden_Virtual_Method_B") {
17662        push(@Sentence, "The method '".$Problem{"New_Value"}."' will be called instead of this method.");
17663    }
17664    elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
17665    {
17666        my %SymInfo = %{$CompleteSignature{1}{$Symbol}};
17667
17668        if($Location eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/)
17669        {
17670            my $METHOD_TYPE = $SymInfo{"Constructor"}?"constructor":"method";
17671            my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"};
17672
17673            if($ClassName eq $Problem{"Type_Name"}) {
17674                push(@Sentence, "This $METHOD_TYPE is from \'".$Problem{"Type_Name"}."\' class.");
17675            }
17676            else {
17677                push(@Sentence, "This $METHOD_TYPE is from derived class \'".$ClassName."\'.");
17678            }
17679        }
17680        else
17681        {
17682            my $TypeID = undef;
17683
17684            if($Location=~/retval/)
17685            { # return value
17686                if(index($Location, "->")!=-1) {
17687                    push(@Sentence, "Field \'".$Location."\' in return value");
17688                }
17689                else {
17690                    push(@Sentence, "Return value");
17691                }
17692
17693                $TypeID = $SymInfo{"Return"};
17694            }
17695            elsif($Location=~/this/)
17696            { # "this" pointer
17697                if(index($Location, "->")!=-1) {
17698                    push(@Sentence, "Field \'".$Location."\' in the object of this method");
17699                }
17700                else {
17701                    push(@Sentence, "\'this\' pointer");
17702                }
17703
17704                $TypeID = $SymInfo{"Class"};
17705            }
17706            else
17707            { # parameters
17708
17709                my $PName = getParamName($Location);
17710                my $PPos = getParamPos($PName, $Symbol, 1);
17711
17712                if(index($Location, "->")!=-1) {
17713                    push(@Sentence, "Field \'".$Location."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
17714                }
17715                else {
17716                    push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
17717                }
17718                if($PName) {
17719                    push(@Sentence, "\'".$PName."\'");
17720                }
17721
17722                $TypeID = $SymInfo{"Param"}{$PPos}{"type"};
17723            }
17724
17725            if($Location!~/this/)
17726            {
17727                if(my %PureType = get_PureType($TypeID, $TypeInfo{1}))
17728                {
17729                    if($PureType{"Type"} eq "Pointer") {
17730                        push(@Sentence, "(pointer)");
17731                    }
17732                    elsif($PureType{"Type"} eq "Ref") {
17733                        push(@Sentence, "(reference)");
17734                    }
17735                }
17736            }
17737
17738            if($Location eq "this") {
17739                push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
17740            }
17741            else
17742            {
17743                my $Location_T = $Location;
17744                $Location_T=~s/\A\w+(\->|\Z)//; # location in type
17745
17746                my $TypeID_Problem = $TypeID;
17747                if($Location_T) {
17748                    $TypeID_Problem = getFieldType($Location_T, $TypeID, 1);
17749                }
17750
17751                if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Problem{"Type_Name"}) {
17752                    push(@Sentence, "has type \'".$Problem{"Type_Name"}."\'.");
17753                }
17754                else {
17755                    push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
17756                }
17757            }
17758        }
17759    }
17760    if($ExtendedSymbols{$Symbol}) {
17761        push(@Sentence, " This is a symbol from an external library that may use the \'$TargetLibraryName\' library and change the ABI after recompiling.");
17762    }
17763
17764    my $Sent = join(" ", @Sentence);
17765
17766    $Sent=~s/->/./g;
17767
17768    if($ReportFormat eq "xml")
17769    {
17770        $Sent=~s/'//g;
17771    }
17772
17773    return $Sent;
17774}
17775
17776sub getFieldType($$$)
17777{
17778    my ($Location, $TypeId, $LibVersion) = @_;
17779
17780    my @Fields = split(/\->/, $Location);
17781
17782    foreach my $Name (@Fields)
17783    {
17784        my %Info = get_BaseType($TypeId, $LibVersion);
17785
17786        foreach my $Pos (keys(%{$Info{"Memb"}}))
17787        {
17788            if($Info{"Memb"}{$Pos}{"name"} eq $Name)
17789            {
17790                $TypeId = $Info{"Memb"}{$Pos}{"type"};
17791                last;
17792            }
17793        }
17794    }
17795
17796    return $TypeId;
17797}
17798
17799sub get_XmlSign($$)
17800{
17801    my ($Symbol, $LibVersion) = @_;
17802    my $Info = $CompleteSignature{$LibVersion}{$Symbol};
17803    my $Report = "";
17804    foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Info->{"Param"}}))
17805    {
17806        my $Name = $Info->{"Param"}{$Pos}{"name"};
17807        my $Type = $Info->{"Param"}{$Pos}{"type"};
17808        my $TypeName = $TypeInfo{$LibVersion}{$Type}{"Name"};
17809        foreach my $Typedef (keys(%ChangedTypedef))
17810        {
17811            if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) {
17812                $TypeName=~s/\b\Q$Typedef\E\b/$Base/g;
17813            }
17814        }
17815        $Report .= "    <param pos=\"$Pos\">\n";
17816        $Report .= "      <name>".$Name."</name>\n";
17817        $Report .= "      <type>".xmlSpecChars($TypeName)."</type>\n";
17818        $Report .= "    </param>\n";
17819    }
17820    if(my $Return = $Info->{"Return"})
17821    {
17822        my $RTName = $TypeInfo{$LibVersion}{$Return}{"Name"};
17823        $Report .= "    <retval>\n";
17824        $Report .= "      <type>".xmlSpecChars($RTName)."</type>\n";
17825        $Report .= "    </retval>\n";
17826    }
17827    return $Report;
17828}
17829
17830sub get_Report_SymbolsInfo($)
17831{
17832    my $Level = $_[0];
17833    my $Report = "<symbols_info>\n";
17834    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
17835    {
17836        my ($SN, $SS, $SV) = separate_symbol($Symbol);
17837        if($SV and defined $CompatProblems{$Level}{$SN}) {
17838            next;
17839        }
17840        $Report .= "  <symbol name=\"$Symbol\">\n";
17841        my ($S1, $P1, $S2, $P2) = ();
17842        if(not $AddedInt{$Level}{$Symbol})
17843        {
17844            if(defined $CompleteSignature{1}{$Symbol}
17845            and defined $CompleteSignature{1}{$Symbol}{"Header"})
17846            {
17847                $P1 = get_XmlSign($Symbol, 1);
17848                $S1 = get_Signature($Symbol, 1);
17849            }
17850            elsif($Symbol=~/\A(_Z|\?)/) {
17851                $S1 = $tr_name{$Symbol};
17852            }
17853        }
17854        if(not $RemovedInt{$Level}{$Symbol})
17855        {
17856            if(defined $CompleteSignature{2}{$Symbol}
17857            and defined $CompleteSignature{2}{$Symbol}{"Header"})
17858            {
17859                $P2 = get_XmlSign($Symbol, 2);
17860                $S2 = get_Signature($Symbol, 2);
17861            }
17862            elsif($Symbol=~/\A(_Z|\?)/) {
17863                $S2 = $tr_name{$Symbol};
17864            }
17865        }
17866        if($S1)
17867        {
17868            $Report .= "    <old signature=\"".xmlSpecChars($S1)."\">\n";
17869            $Report .= $P1;
17870            $Report .= "    </old>\n";
17871        }
17872        if($S2 and $S2 ne $S1)
17873        {
17874            $Report .= "    <new signature=\"".xmlSpecChars($S2)."\">\n";
17875            $Report .= $P2;
17876            $Report .= "    </new>\n";
17877        }
17878        $Report .= "  </symbol>\n";
17879    }
17880    $Report .= "</symbols_info>\n";
17881    return $Report;
17882}
17883
17884sub writeReport($$)
17885{
17886    my ($Level, $Report) = @_;
17887    if($ReportFormat eq "xml") {
17888        $Report = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".$Report;
17889    }
17890    if($StdOut)
17891    { # --stdout option
17892        print STDOUT $Report;
17893    }
17894    else
17895    {
17896        my $RPath = getReportPath($Level);
17897        mkpath(get_dirname($RPath));
17898
17899        open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n");
17900        print REPORT $Report;
17901        close(REPORT);
17902    }
17903}
17904
17905sub getReport($)
17906{
17907    my $Level = $_[0];
17908    if($ReportFormat eq "xml")
17909    { # XML
17910        if($Level eq "Join")
17911        {
17912            my $Report = "<reports>\n";
17913            $Report .= getReport("Binary");
17914            $Report .= getReport("Source");
17915            $Report .= "</reports>\n";
17916            return $Report;
17917        }
17918        else
17919        {
17920            my $Report = "<report kind=\"".lc($Level)."\" version=\"$XML_REPORT_VERSION\">\n\n";
17921            my ($Summary, $MetaData) = get_Summary($Level);
17922            $Report .= $Summary."\n";
17923            $Report .= get_Report_Added($Level).get_Report_Removed($Level);
17924            $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
17925
17926            # additional symbols info (if needed)
17927            # $Report .= get_Report_SymbolsInfo($Level);
17928
17929            $Report .= "</report>\n";
17930            return $Report;
17931        }
17932    }
17933    else
17934    { # HTML
17935        my $CssStyles = readModule("Styles", "Report.css");
17936        my $JScripts = readModule("Scripts", "Sections.js");
17937        if($Level eq "Join")
17938        {
17939            $CssStyles .= "\n".readModule("Styles", "Tabs.css");
17940            $JScripts .= "\n".readModule("Scripts", "Tabs.js");
17941            my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." compatibility report";
17942            my $Keywords = $TargetTitle.", compatibility, API, ABI, report";
17943            my $Description = "API/ABI compatibility report for the $TargetTitle $TargetComponent between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
17944            my ($BSummary, $BMetaData) = get_Summary("Binary");
17945            my ($SSummary, $SMetaData) = get_Summary("Source");
17946            my $Report = "<!-\- $BMetaData -\->\n<!-\- $SMetaData -\->\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."<body><a name='Source'></a><a name='Binary'></a><a name='Top'></a>";
17947            $Report .= get_Report_Title("Join")."
17948            <br/>
17949            <div class='tabset'>
17950            <a id='BinaryID' href='#BinaryTab' class='tab active'>Binary<br/>Compatibility</a>
17951            <a id='SourceID' href='#SourceTab' style='margin-left:3px' class='tab disabled'>Source<br/>Compatibility</a>
17952            </div>";
17953            $Report .= "<div id='BinaryTab' class='tab'>\n$BSummary\n".get_Report_Added("Binary").get_Report_Removed("Binary").get_Report_Problems("High", "Binary").get_Report_Problems("Medium", "Binary").get_Report_Problems("Low", "Binary").get_Report_Problems("Safe", "Binary").get_SourceInfo()."<br/><br/><br/></div>";
17954            $Report .= "<div id='SourceTab' class='tab'>\n$SSummary\n".get_Report_Added("Source").get_Report_Removed("Source").get_Report_Problems("High", "Source").get_Report_Problems("Medium", "Source").get_Report_Problems("Low", "Source").get_Report_Problems("Safe", "Source").get_SourceInfo()."<br/><br/><br/></div>";
17955            $Report .= getReportFooter();
17956            $Report .= "\n</body></html>\n";
17957            return $Report;
17958        }
17959        else
17960        {
17961            my ($Summary, $MetaData) = get_Summary($Level);
17962            my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." ".lc($Level)." compatibility report";
17963            my $Keywords = $TargetTitle.", ".lc($Level)." compatibility, API, report";
17964            my $Description = "$Level compatibility report for the ".$TargetTitle." ".$TargetComponent." between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
17965            if($Level eq "Binary")
17966            {
17967                if(getArch(1) eq getArch(2)
17968                and getArch(1) ne "unknown") {
17969                    $Description .= " on ".showArch(getArch(1));
17970                }
17971            }
17972            my $Report = "<!-\- $MetaData -\->\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."\n<body>\n<div><a name='Top'></a>\n";
17973            $Report .= get_Report_Title($Level)."\n".$Summary."\n";
17974            $Report .= get_Report_Added($Level).get_Report_Removed($Level);
17975            $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
17976            $Report .= get_SourceInfo();
17977            $Report .= "</div>\n<br/><br/><br/>\n";
17978            $Report .= getReportFooter();
17979            $Report .= "\n</body></html>\n";
17980            return $Report;
17981        }
17982    }
17983}
17984
17985sub createReport()
17986{
17987    if($JoinReport)
17988    { # --stdout
17989        writeReport("Join", getReport("Join"));
17990    }
17991    elsif($DoubleReport)
17992    { # default
17993        writeReport("Binary", getReport("Binary"));
17994        writeReport("Source", getReport("Source"));
17995    }
17996    elsif($BinaryOnly)
17997    { # --binary
17998        writeReport("Binary", getReport("Binary"));
17999    }
18000    elsif($SourceOnly)
18001    { # --source
18002        writeReport("Source", getReport("Source"));
18003    }
18004}
18005
18006sub getReportFooter()
18007{
18008    my $Footer = "";
18009
18010    $Footer .= "<hr/>\n";
18011    $Footer .= "<div class='footer' align='right'>";
18012    $Footer .= "<i>Generated by <a href='".$HomePage{"Dev"}."'>ABI Compliance Checker</a> $TOOL_VERSION &#160;</i>\n";
18013    $Footer .= "</div>\n";
18014    $Footer .= "<br/>\n";
18015
18016    return $Footer;
18017}
18018
18019sub get_Report_Problems($$)
18020{
18021    my ($Severity, $Level) = @_;
18022
18023    my $Report = get_Report_TypeProblems($Severity, $Level);
18024    if(my $SProblems = get_Report_SymbolProblems($Severity, $Level)) {
18025        $Report .= $SProblems;
18026    }
18027
18028    if($Severity eq "Low" or $Severity eq "Safe") {
18029        $Report .= get_Report_ChangedConstants($Severity, $Level);
18030    }
18031
18032    if($ReportFormat eq "html")
18033    {
18034        if($Report)
18035        { # add anchor
18036            if($JoinReport)
18037            {
18038                if($Severity eq "Safe") {
18039                    $Report = "<a name=\'Other_".$Level."_Changes\'></a>".$Report;
18040                }
18041                else {
18042                    $Report = "<a name=\'".$Severity."_Risk_".$Level."_Problems\'></a>".$Report;
18043                }
18044            }
18045            else
18046            {
18047                if($Severity eq "Safe") {
18048                    $Report = "<a name=\'Other_Changes\'></a>".$Report;
18049                }
18050                else {
18051                    $Report = "<a name=\'".$Severity."_Risk_Problems\'></a>".$Report;
18052                }
18053            }
18054        }
18055    }
18056    return $Report;
18057}
18058
18059sub composeHTML_Head($$$$$)
18060{
18061    my ($Title, $Keywords, $Description, $Styles, $Scripts) = @_;
18062
18063    my $Head = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
18064    $Head .= "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n";
18065    $Head .= "<head>\n";
18066    $Head .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
18067    $Head .= "<meta name=\"keywords\" content=\"$Keywords\" />\n";
18068    $Head .= "<meta name=\"description\" content=\"$Description\" />\n";
18069    $Head .= "<title>$Title</title>\n";
18070    $Head .= "<style type=\"text/css\">\n$Styles</style>\n";
18071    $Head .= "<script type=\"text/javascript\" language=\"JavaScript\">\n<!--\n$Scripts\n-->\n</script>\n";
18072    $Head .= "</head>\n";
18073
18074    return $Head;
18075}
18076
18077sub insertIDs($)
18078{
18079    my $Text = $_[0];
18080    while($Text=~/CONTENT_ID/)
18081    {
18082        if(int($Content_Counter)%2) {
18083            $ContentID -= 1;
18084        }
18085        $Text=~s/CONTENT_ID/c_$ContentID/;
18086        $ContentID += 1;
18087        $Content_Counter += 1;
18088    }
18089    return $Text;
18090}
18091
18092sub checkPreprocessedUnit($)
18093{
18094    my $Path = $_[0];
18095    my ($CurHeader, $CurHeaderName) = ("", "");
18096    my $CurClass = ""; # extra info
18097    open(PREPROC, $Path) || die ("can't open file \'$Path\': $!\n");
18098
18099    while(my $Line = <PREPROC>)
18100    { # detecting public and private constants
18101        if(substr($Line, 0, 1) eq "#")
18102        {
18103            chomp($Line);
18104            if($Line=~/\A\#\s+\d+\s+\"(.+)\"/)
18105            {
18106                $CurHeader = path_format($1, $OSgroup);
18107                $CurHeaderName = get_filename($CurHeader);
18108                $CurClass = "";
18109
18110                if(index($CurHeader, $TMP_DIR)==0) {
18111                    next;
18112                }
18113
18114                if(substr($CurHeaderName, 0, 1) eq "<")
18115                { # <built-in>, <command-line>, etc.
18116                    $CurHeaderName = "";
18117                    $CurHeader = "";
18118                }
18119
18120                if($ExtraInfo)
18121                {
18122                    if($CurHeaderName) {
18123                        $PreprocessedHeaders{$Version}{$CurHeader} = 1;
18124                    }
18125                }
18126            }
18127            if(not $ExtraDump)
18128            {
18129                if($CurHeaderName)
18130                {
18131                    if(not $Include_Neighbors{$Version}{$CurHeaderName}
18132                    and not $Registered_Headers{$Version}{$CurHeader})
18133                    { # not a target
18134                        next;
18135                    }
18136                    if(not is_target_header($CurHeaderName, 1)
18137                    and not is_target_header($CurHeaderName, 2))
18138                    { # user-defined header
18139                        next;
18140                    }
18141                }
18142            }
18143
18144            if($Line=~/\A\#\s*define\s+(\w+)\s+(.+)\s*\Z/)
18145            {
18146                my ($Name, $Value) = ($1, $2);
18147                if(not $Constants{$Version}{$Name}{"Access"})
18148                {
18149                    $Constants{$Version}{$Name}{"Access"} = "public";
18150                    $Constants{$Version}{$Name}{"Value"} = $Value;
18151                    if($CurHeaderName) {
18152                        $Constants{$Version}{$Name}{"Header"} = $CurHeaderName;
18153                    }
18154                }
18155            }
18156            elsif($Line=~/\A\#[ \t]*undef[ \t]+([_A-Z]+)[ \t]*/) {
18157                $Constants{$Version}{$1}{"Access"} = "private";
18158            }
18159        }
18160        else
18161        {
18162            if(defined $ExtraDump)
18163            {
18164                if($Line=~/(\w+)\s*\(/)
18165                { # functions
18166                    $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader;
18167                }
18168                #elsif($Line=~/(\w+)\s*;/)
18169                #{ # data
18170                #    $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader;
18171                #}
18172                elsif($Line=~/(\A|\s)class\s+(\w+)/) {
18173                    $CurClass = $2;
18174                }
18175            }
18176        }
18177    }
18178    close(PREPROC);
18179    foreach my $Constant (keys(%{$Constants{$Version}}))
18180    {
18181        if($Constants{$Version}{$Constant}{"Access"} eq "private")
18182        {
18183            delete($Constants{$Version}{$Constant});
18184            next;
18185        }
18186        if(not $ExtraDump and ($Constant=~/_h\Z/i
18187        or isBuiltIn($Constants{$Version}{$Constant}{"Header"})))
18188        { # skip
18189            delete($Constants{$Version}{$Constant});
18190        }
18191        else {
18192            delete($Constants{$Version}{$Constant}{"Access"});
18193        }
18194    }
18195    if($Debug)
18196    {
18197        mkpath($DEBUG_PATH{$Version});
18198        copy($Path, $DEBUG_PATH{$Version}."/preprocessor.txt");
18199    }
18200}
18201
18202sub uncoverConstant($$)
18203{
18204    my ($LibVersion, $Constant) = @_;
18205    return "" if(not $LibVersion or not $Constant);
18206    return $Constant if(isCyclical(\@RecurConstant, $Constant));
18207    if(defined $Cache{"uncoverConstant"}{$LibVersion}{$Constant}) {
18208        return $Cache{"uncoverConstant"}{$LibVersion}{$Constant};
18209    }
18210
18211    if(defined $Constants{$LibVersion}{$Constant})
18212    {
18213        my $Value = $Constants{$LibVersion}{$Constant}{"Value"};
18214        if(defined $Constants{$LibVersion}{$Value})
18215        {
18216            push(@RecurConstant, $Constant);
18217            my $Uncovered = uncoverConstant($LibVersion, $Value);
18218            if($Uncovered ne "") {
18219                $Value = $Uncovered;
18220            }
18221            pop(@RecurConstant);
18222        }
18223
18224        # FIXME: uncover $Value using all the enum constants
18225        # USE CASE: change of define NC_LONG from NC_INT (enum value) to NC_INT (define)
18226        return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = $Value);
18227    }
18228    return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = "");
18229}
18230
18231sub simpleConstant($$)
18232{
18233    my ($LibVersion, $Value) = @_;
18234    if($Value=~/\W/)
18235    {
18236        my $Value_Copy = $Value;
18237        while($Value_Copy=~s/([a-z_]\w+)/\@/i)
18238        {
18239            my $Word = $1;
18240            if($Value!~/$Word\s*\(/)
18241            {
18242                my $Val = uncoverConstant($LibVersion, $Word);
18243                if($Val ne "")
18244                {
18245                    $Value=~s/\b$Word\b/$Val/g;
18246                }
18247            }
18248        }
18249    }
18250    return $Value;
18251}
18252
18253sub computeValue($)
18254{
18255    my $Value = $_[0];
18256
18257    if($Value=~/\A\((-?[\d]+)\)\Z/) {
18258        return $1;
18259    }
18260
18261    if($Value=~/\A[\d\-\+()]+\Z/) {
18262        return eval($Value);
18263    }
18264
18265    return $Value;
18266}
18267
18268my %IgnoreConstant = map {$_=>1} (
18269    "VERSION",
18270    "VERSIONCODE",
18271    "VERNUM",
18272    "VERS_INFO",
18273    "PATCHLEVEL",
18274    "INSTALLPREFIX",
18275    "VBUILD",
18276    "VPATCH",
18277    "VMINOR",
18278    "BUILD_STRING",
18279    "BUILD_TIME",
18280    "PACKAGE_STRING",
18281    "PRODUCTION",
18282    "CONFIGURE_COMMAND",
18283    "INSTALLDIR",
18284    "BINDIR",
18285    "CONFIG_FILE_PATH",
18286    "DATADIR",
18287    "EXTENSION_DIR",
18288    "INCLUDE_PATH",
18289    "LIBDIR",
18290    "LOCALSTATEDIR",
18291    "SBINDIR",
18292    "SYSCONFDIR",
18293    "RELEASE",
18294    "SOURCE_ID",
18295    "SUBMINOR",
18296    "MINOR",
18297    "MINNOR",
18298    "MINORVERSION",
18299    "MAJOR",
18300    "MAJORVERSION",
18301    "MICRO",
18302    "MICROVERSION",
18303    "BINARY_AGE",
18304    "INTERFACE_AGE",
18305    "CORE_ABI",
18306    "PATCH",
18307    "COPYRIGHT",
18308    "TIMESTAMP",
18309    "REVISION",
18310    "PACKAGE_TAG",
18311    "PACKAGEDATE",
18312    "NUMVERSION",
18313    "Release",
18314    "Version"
18315);
18316
18317sub constantFilter($$$)
18318{
18319    my ($Name, $Value, $Level) = @_;
18320
18321    if($Level eq "Binary")
18322    {
18323        if($Name=~/_t\Z/)
18324        { # __malloc_ptr_t
18325            return 1;
18326        }
18327        foreach (keys(%IgnoreConstant))
18328        {
18329            if($Name=~/(\A|_)$_(_|\Z)/)
18330            { # version
18331                return 1;
18332            }
18333            if(/\A[A-Z].*[a-z]\Z/)
18334            {
18335                if($Name=~/(\A|[a-z])$_([A-Z]|\Z)/)
18336                { # version
18337                    return 1;
18338                }
18339            }
18340        }
18341        if($Name=~/(\A|_)(lib|open|)$TargetLibraryShortName(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i)
18342        { # version
18343            return 1;
18344        }
18345        if($Value=~/\A('|"|)[\/\\]\w+([\/\\]|:|('|"|)\Z)/ or $Value=~/[\/\\]\w+[\/\\]\w+/)
18346        { # /lib64:/usr/lib64:/lib:/usr/lib:/usr/X11R6/lib/Xaw3d ...
18347            return 1;
18348        }
18349
18350        if($Value=~/\A["'].*['"]/i)
18351        { # string
18352            return 0;
18353        }
18354
18355        if($Value=~/\A[({]*\s*[a-z_]+\w*(\s+|[\|,])/i)
18356        { # static int gcry_pth_init
18357          # extern ABC
18358          # (RE_BACKSLASH_ESCAPE_IN_LISTS | RE...
18359          # { H5FD_MEM_SUPER, H5FD_MEM_SUPER, ...
18360            return 1;
18361        }
18362        if($Value=~/\w+\s*\(/i)
18363        { # foo(p)
18364            return 1;
18365        }
18366        if($Value=~/\A[a-z_]+\w*\Z/i)
18367        { # asn1_node_st
18368          # __SMTH_P
18369            return 1;
18370        }
18371    }
18372
18373    return 0;
18374}
18375
18376sub mergeConstants($)
18377{
18378    my $Level = $_[0];
18379    foreach my $Constant (keys(%{$Constants{1}}))
18380    {
18381        if($SkipConstants{1}{$Constant})
18382        { # skipped by the user
18383            next;
18384        }
18385
18386        if(my $Header = $Constants{1}{$Constant}{"Header"})
18387        {
18388            if(not is_target_header($Header, 1)
18389            and not is_target_header($Header, 2))
18390            { # user-defined header
18391                next;
18392            }
18393        }
18394        else {
18395            next;
18396        }
18397
18398        my $Old_Value = uncoverConstant(1, $Constant);
18399
18400        if(constantFilter($Constant, $Old_Value, $Level))
18401        { # separate binary and source problems
18402            next;
18403        }
18404
18405        if(not defined $Constants{2}{$Constant}{"Value"})
18406        { # removed
18407            if(not defined $SkipRemovedConstants)
18408            {
18409                %{$CompatProblems_Constants{$Level}{$Constant}{"Removed_Constant"}} = (
18410                    "Target"=>$Constant,
18411                    "Old_Value"=>$Old_Value  );
18412            }
18413            next;
18414        }
18415
18416        if($Constants{2}{$Constant}{"Value"} eq "")
18417        { # empty value
18418          # TODO: implement a rule
18419            next;
18420        }
18421
18422        my $New_Value = uncoverConstant(2, $Constant);
18423
18424        my $Old_Value_Pure = $Old_Value;
18425        my $New_Value_Pure = $New_Value;
18426
18427        $Old_Value_Pure=~s/(\W)\s+/$1/g;
18428        $Old_Value_Pure=~s/\s+(\W)/$1/g;
18429        $New_Value_Pure=~s/(\W)\s+/$1/g;
18430        $New_Value_Pure=~s/\s+(\W)/$1/g;
18431
18432        next if($New_Value_Pure eq "" or $Old_Value_Pure eq "");
18433
18434        if($New_Value_Pure ne $Old_Value_Pure)
18435        { # different values
18436            if(simpleConstant(1, $Old_Value) eq simpleConstant(2, $New_Value))
18437            { # complex values
18438                next;
18439            }
18440            if(computeValue($Old_Value) eq computeValue($New_Value))
18441            { # expressions
18442                next;
18443            }
18444            if(convert_integer($Old_Value) eq convert_integer($New_Value))
18445            { # 0x0001 and 0x1, 0x1 and 1 equal constants
18446                next;
18447            }
18448            if($Old_Value eq "0" and $New_Value eq "NULL")
18449            { # 0 => NULL
18450                next;
18451            }
18452            if($Old_Value eq "NULL" and $New_Value eq "0")
18453            { # NULL => 0
18454                next;
18455            }
18456            %{$CompatProblems_Constants{$Level}{$Constant}{"Changed_Constant"}} = (
18457                "Target"=>$Constant,
18458                "Old_Value"=>$Old_Value,
18459                "New_Value"=>$New_Value  );
18460        }
18461    }
18462
18463    if(defined $SkipAddedConstants) {
18464        return;
18465    }
18466
18467    foreach my $Constant (keys(%{$Constants{2}}))
18468    {
18469        if(not defined $Constants{1}{$Constant}{"Value"})
18470        {
18471            if($SkipConstants{2}{$Constant})
18472            { # skipped by the user
18473                next;
18474            }
18475
18476            if(my $Header = $Constants{2}{$Constant}{"Header"})
18477            {
18478                if(not is_target_header($Header, 1)
18479                and not is_target_header($Header, 2))
18480                { # user-defined header
18481                    next;
18482                }
18483            }
18484            else {
18485                next;
18486            }
18487
18488            my $New_Value = uncoverConstant(2, $Constant);
18489            if(not defined $New_Value or $New_Value eq "") {
18490                next;
18491            }
18492
18493            if(constantFilter($Constant, $New_Value, $Level))
18494            { # separate binary and source problems
18495                next;
18496            }
18497
18498            %{$CompatProblems_Constants{$Level}{$Constant}{"Added_Constant"}} = (
18499                "Target"=>$Constant,
18500                "New_Value"=>$New_Value  );
18501        }
18502    }
18503}
18504
18505sub convert_integer($)
18506{
18507    my $Value = $_[0];
18508    if($Value=~/\A0x[a-f0-9]+\Z/)
18509    { # hexadecimal
18510        return hex($Value);
18511    }
18512    elsif($Value=~/\A0[0-7]+\Z/)
18513    { # octal
18514        return oct($Value);
18515    }
18516    elsif($Value=~/\A0b[0-1]+\Z/)
18517    { # binary
18518        return oct($Value);
18519    }
18520    else {
18521        return $Value;
18522    }
18523}
18524
18525sub readSymbols($)
18526{
18527    my $LibVersion = $_[0];
18528    my @LibPaths = getSOPaths($LibVersion);
18529    if($#LibPaths==-1 and not $CheckHeadersOnly)
18530    {
18531        if($LibVersion==1)
18532        {
18533            printMsg("WARNING", "checking headers only");
18534            $CheckHeadersOnly = 1;
18535        }
18536        else {
18537            exitStatus("Error", "$SLIB_TYPE libraries are not found in ".$Descriptor{$LibVersion}{"Version"});
18538        }
18539    }
18540
18541    foreach my $LibPath (@LibPaths) {
18542        readSymbols_Lib($LibVersion, $LibPath, 0, "+Weak", 1, 1);
18543    }
18544
18545    if($CheckUndefined)
18546    {
18547        my %UndefinedLibs = ();
18548
18549        my @Libs = (keys(%{$Library_Symbol{$LibVersion}}), keys(%{$DepLibrary_Symbol{$LibVersion}}));
18550
18551        foreach my $LibName (sort @Libs)
18552        {
18553            if(defined $UndefinedSymbols{$LibVersion}{$LibName})
18554            {
18555                foreach my $Symbol (keys(%{$UndefinedSymbols{$LibVersion}{$LibName}}))
18556                {
18557                    if($Symbol_Library{$LibVersion}{$Symbol}
18558                    or $DepSymbol_Library{$LibVersion}{$Symbol})
18559                    { # exported by target library
18560                        next;
18561                    }
18562                    if(index($Symbol, '@')!=-1)
18563                    { # exported default symbol version (@@)
18564                        $Symbol=~s/\@/\@\@/;
18565                        if($Symbol_Library{$LibVersion}{$Symbol}
18566                        or $DepSymbol_Library{$LibVersion}{$Symbol}) {
18567                            next;
18568                        }
18569                    }
18570                    foreach my $Path (find_SymbolLibs($LibVersion, $Symbol)) {
18571                        $UndefinedLibs{$Path} = 1;
18572                    }
18573                }
18574            }
18575        }
18576        if($ExtraInfo)
18577        { # extra information for other tools
18578            if(my @Paths = sort keys(%UndefinedLibs))
18579            {
18580                my $LibString = "";
18581                my %Dirs = ();
18582                foreach (@Paths)
18583                {
18584                    $KnownLibs{$_} = 1;
18585                    my ($Dir, $Name) = separate_path($_);
18586
18587                    if(not grep {$Dir eq $_} (@{$SystemPaths{"lib"}})) {
18588                        $Dirs{esc($Dir)} = 1;
18589                    }
18590
18591                    $Name = parse_libname($Name, "name", $OStarget);
18592                    $Name=~s/\Alib//;
18593
18594                    $LibString .= " -l$Name";
18595                }
18596
18597                foreach my $Dir (sort {$b cmp $a} keys(%Dirs))
18598                {
18599                    $LibString = " -L".esc($Dir).$LibString;
18600                }
18601
18602                writeFile($ExtraInfo."/libs-string", $LibString);
18603            }
18604        }
18605    }
18606
18607    if($ExtraInfo) {
18608        writeFile($ExtraInfo."/lib-paths", join("\n", sort keys(%KnownLibs)));
18609    }
18610
18611    if(not $CheckHeadersOnly)
18612    {
18613        if($#LibPaths!=-1)
18614        {
18615            if(not keys(%{$Symbol_Library{$LibVersion}}))
18616            {
18617                printMsg("WARNING", "the set of public symbols in library(ies) is empty ($LibVersion)");
18618                printMsg("WARNING", "checking headers only");
18619                $CheckHeadersOnly = 1;
18620            }
18621        }
18622    }
18623
18624   # clean memory
18625   %SystemObjects = ();
18626}
18627
18628my %Prefix_Lib_Map=(
18629 # symbols for autodetecting library dependencies (by prefix)
18630    "pthread_" => ["libpthread"],
18631    "g_" => ["libglib-2.0", "libgobject-2.0", "libgio-2.0"],
18632    "cairo_" => ["libcairo"],
18633    "gtk_" => ["libgtk-x11-2.0"],
18634    "atk_" => ["libatk-1.0"],
18635    "gdk_" => ["libgdk-x11-2.0"],
18636    "gl" => ["libGL"],
18637    "glu" => ["libGLU"],
18638    "popt" => ["libpopt"],
18639    "Py" => ["libpython"],
18640    "jpeg_" => ["libjpeg"],
18641    "BZ2_" => ["libbz2"],
18642    "Fc" => ["libfontconfig"],
18643    "Xft" => ["libXft"],
18644    "SSL_" => ["libssl"],
18645    "sem_" => ["libpthread"],
18646    "snd_" => ["libasound"],
18647    "art_" => ["libart_lgpl_2"],
18648    "dbus_g" => ["libdbus-glib-1"],
18649    "GOMP_" => ["libgomp"],
18650    "omp_" => ["libgomp"],
18651    "cms" => ["liblcms"]
18652);
18653
18654my %Pattern_Lib_Map=(
18655    "SL[a-z]" => ["libslang"]
18656);
18657
18658my %Symbol_Lib_Map=(
18659 # symbols for autodetecting library dependencies (by name)
18660    "pow" => "libm",
18661    "fmod" => "libm",
18662    "sin" => "libm",
18663    "floor" => "libm",
18664    "cos" => "libm",
18665    "dlopen" => "libdl",
18666    "deflate" => "libz",
18667    "inflate" => "libz",
18668    "move_panel" => "libpanel",
18669    "XOpenDisplay" => "libX11",
18670    "resize_term" => "libncurses",
18671    "clock_gettime" => "librt",
18672    "crypt" => "libcrypt"
18673);
18674
18675sub find_SymbolLibs($$)
18676{
18677    my ($LibVersion, $Symbol) = @_;
18678
18679    if(index($Symbol, "g_")==0 and $Symbol=~/[A-Z]/)
18680    { # debug symbols
18681        return ();
18682    }
18683
18684    my %Paths = ();
18685
18686    if(my $LibName = $Symbol_Lib_Map{$Symbol})
18687    {
18688        if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18689            $Paths{$Path} = 1;
18690        }
18691    }
18692
18693    if(my $SymbolPrefix = getPrefix($Symbol))
18694    {
18695        if(defined $Cache{"find_SymbolLibs"}{$SymbolPrefix}) {
18696            return @{$Cache{"find_SymbolLibs"}{$SymbolPrefix}};
18697        }
18698
18699        if(not keys(%Paths))
18700        {
18701            if(defined $Prefix_Lib_Map{$SymbolPrefix})
18702            {
18703                foreach my $LibName (@{$Prefix_Lib_Map{$SymbolPrefix}})
18704                {
18705                    if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18706                        $Paths{$Path} = 1;
18707                    }
18708                }
18709            }
18710        }
18711
18712        if(not keys(%Paths))
18713        {
18714            foreach my $Prefix (sort keys(%Pattern_Lib_Map))
18715            {
18716                if($Symbol=~/\A$Prefix/)
18717                {
18718                    foreach my $LibName (@{$Pattern_Lib_Map{$Prefix}})
18719                    {
18720                        if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18721                            $Paths{$Path} = 1;
18722                        }
18723                    }
18724                }
18725            }
18726        }
18727
18728        if(not keys(%Paths))
18729        {
18730            if($SymbolPrefix)
18731            { # try to find a library by symbol prefix
18732                if($SymbolPrefix eq "inotify" and
18733                index($Symbol, "\@GLIBC")!=-1)
18734                {
18735                    if(my $Path = get_LibPath($LibVersion, "libc.$LIB_EXT")) {
18736                        $Paths{$Path} = 1;
18737                    }
18738                }
18739                else
18740                {
18741                    if(my $Path = get_LibPath_Prefix($LibVersion, $SymbolPrefix)) {
18742                        $Paths{$Path} = 1;
18743                    }
18744                }
18745            }
18746        }
18747
18748        if(my @Paths = keys(%Paths)) {
18749            $Cache{"find_SymbolLibs"}{$SymbolPrefix} = \@Paths;
18750        }
18751    }
18752    return keys(%Paths);
18753}
18754
18755sub get_LibPath_Prefix($$)
18756{
18757    my ($LibVersion, $Prefix) = @_;
18758
18759    $Prefix = lc($Prefix);
18760    $Prefix=~s/[_]+\Z//g;
18761
18762    foreach ("-2", "2", "-1", "1", "")
18763    { # libgnome-2.so
18764      # libxml2.so
18765      # libdbus-1.so
18766        if(my $Path = get_LibPath($LibVersion, "lib".$Prefix.$_.".".$LIB_EXT)) {
18767            return $Path;
18768        }
18769    }
18770    return "";
18771}
18772
18773sub getPrefix($)
18774{
18775    my $Str = $_[0];
18776    if($Str=~/\A([_]*[A-Z][a-z]{1,5})[A-Z]/)
18777    { # XmuValidArea: Xmu
18778        return $1;
18779    }
18780    elsif($Str=~/\A([_]*[a-z]+)[A-Z]/)
18781    { # snfReadFont: snf
18782        return $1;
18783    }
18784    elsif($Str=~/\A([_]*[A-Z]{2,})[A-Z][a-z]+([A-Z][a-z]+|\Z)/)
18785    { # XRRTimes: XRR
18786        return $1;
18787    }
18788    elsif($Str=~/\A([_]*[a-z]{1,2}\d+)[a-z\d]*_[a-z]+/i)
18789    { # H5HF_delete: H5
18790        return $1;
18791    }
18792    elsif($Str=~/\A([_]*[a-z0-9]{2,}_)[a-z]+/i)
18793    { # alarm_event_add: alarm_
18794        return $1;
18795    }
18796    elsif($Str=~/\A(([a-z])\2{1,})/i)
18797    { # ffopen
18798        return $1;
18799    }
18800    return "";
18801}
18802
18803sub getSymbolSize($$)
18804{ # size from the shared library
18805    my ($Symbol, $LibVersion) = @_;
18806    return 0 if(not $Symbol);
18807    if(defined $Symbol_Library{$LibVersion}{$Symbol}
18808    and my $LibName = $Symbol_Library{$LibVersion}{$Symbol})
18809    {
18810        if(defined $Library_Symbol{$LibVersion}{$LibName}{$Symbol}
18811        and my $Size = $Library_Symbol{$LibVersion}{$LibName}{$Symbol})
18812        {
18813            if($Size<0) {
18814                return -$Size;
18815            }
18816        }
18817    }
18818    return 0;
18819}
18820
18821sub canonifyName($$)
18822{ # make TIFFStreamOpen(char const*, std::basic_ostream<char, std::char_traits<char> >*)
18823  # to be TIFFStreamOpen(char const*, std::basic_ostream<char>*)
18824    my ($Name, $Type) = @_;
18825
18826    # single
18827    while($Name=~/([^<>,]+),\s*$DEFAULT_STD_PARMS<([^<>,]+)>\s*/ and $1 eq $3)
18828    {
18829        my $P = $1;
18830        $Name=~s/\Q$P\E,\s*$DEFAULT_STD_PARMS<\Q$P\E>\s*/$P/g;
18831    }
18832
18833    # double
18834    if($Name=~/$DEFAULT_STD_PARMS/)
18835    {
18836        if($Type eq "S")
18837        {
18838            my ($ShortName, $FuncParams) = split_Signature($Name);
18839
18840            foreach my $FParam (separate_Params($FuncParams, 0, 0))
18841            {
18842                if(index($FParam, "<")!=-1)
18843                {
18844                    $FParam=~s/>([^<>]+)\Z/>/; # remove quals
18845                    my $FParam_N = canonifyName($FParam, "T");
18846                    if($FParam_N ne $FParam) {
18847                        $Name=~s/\Q$FParam\E/$FParam_N/g;
18848                    }
18849                }
18850            }
18851        }
18852        elsif($Type eq "T")
18853        {
18854            my ($ShortTmpl, $TmplParams) = template_Base($Name);
18855
18856            my @TParams = separate_Params($TmplParams, 0, 0);
18857            if($#TParams>=1)
18858            {
18859                my $FParam = $TParams[0];
18860                foreach my $Pos (1 .. $#TParams)
18861                {
18862                    my $TParam = $TParams[$Pos];
18863                    if($TParam=~/\A$DEFAULT_STD_PARMS<\Q$FParam\E\s*>\Z/) {
18864                        $Name=~s/\Q$FParam, $TParam\E\s*/$FParam/g;
18865                    }
18866                }
18867            }
18868        }
18869    }
18870    if($Type eq "S") {
18871        return formatName($Name, "S");
18872    }
18873    return $Name;
18874}
18875
18876sub translateSymbols(@)
18877{
18878    my $LibVersion = pop(@_);
18879    my (@MnglNames1, @MnglNames2, @UnmangledNames) = ();
18880    foreach my $Symbol (sort @_)
18881    {
18882        if(index($Symbol, "_Z")==0)
18883        {
18884            next if($tr_name{$Symbol});
18885            $Symbol=~s/[\@\$]+(.*)\Z//;
18886            push(@MnglNames1, $Symbol);
18887        }
18888        elsif(index($Symbol, "?")==0)
18889        {
18890            push(@MnglNames2, $Symbol);
18891        }
18892        else
18893        { # not mangled
18894            $tr_name{$Symbol} = $Symbol;
18895            $mangled_name_gcc{$Symbol} = $Symbol;
18896            $mangled_name{$LibVersion}{$Symbol} = $Symbol;
18897        }
18898    }
18899    if($#MnglNames1 > -1)
18900    { # GCC names
18901        @UnmangledNames = reverse(unmangleArray(@MnglNames1));
18902        foreach my $MnglName (@MnglNames1)
18903        {
18904            if(my $Unmangled = pop(@UnmangledNames))
18905            {
18906                $tr_name{$MnglName} = canonifyName($Unmangled, "S");
18907                if(not $mangled_name_gcc{$tr_name{$MnglName}}) {
18908                    $mangled_name_gcc{$tr_name{$MnglName}} = $MnglName;
18909                }
18910                if(index($MnglName, "_ZTV")==0
18911                and $tr_name{$MnglName}=~/vtable for (.+)/)
18912                { # bind class name and v-table symbol
18913                    my $ClassName = $1;
18914                    $ClassVTable{$ClassName} = $MnglName;
18915                    $VTableClass{$MnglName} = $ClassName;
18916                }
18917            }
18918        }
18919    }
18920    if($#MnglNames2 > -1)
18921    { # MSVC names
18922        @UnmangledNames = reverse(unmangleArray(@MnglNames2));
18923        foreach my $MnglName (@MnglNames2)
18924        {
18925            if(my $Unmangled = pop(@UnmangledNames))
18926            {
18927                $tr_name{$MnglName} = formatName($Unmangled, "S");
18928                $mangled_name{$LibVersion}{$tr_name{$MnglName}} = $MnglName;
18929            }
18930        }
18931    }
18932    return \%tr_name;
18933}
18934
18935sub link_symbol($$$)
18936{
18937    my ($Symbol, $RunWith, $Deps) = @_;
18938    if(link_symbol_internal($Symbol, $RunWith, \%Symbol_Library)) {
18939        return 1;
18940    }
18941    if($Deps eq "+Deps")
18942    { # check the dependencies
18943        if(link_symbol_internal($Symbol, $RunWith, \%DepSymbol_Library)) {
18944            return 1;
18945        }
18946    }
18947    return 0;
18948}
18949
18950sub link_symbol_internal($$$)
18951{
18952    my ($Symbol, $RunWith, $Where) = @_;
18953    return 0 if(not $Where or not $Symbol);
18954    if($Where->{$RunWith}{$Symbol})
18955    { # the exact match by symbol name
18956        return 1;
18957    }
18958    if(my $VSym = $SymVer{$RunWith}{$Symbol})
18959    { # indirect symbol version, i.e.
18960      # foo_old and its symlink foo@v (or foo@@v)
18961      # foo_old may be in symtab table
18962        if($Where->{$RunWith}{$VSym}) {
18963            return 1;
18964        }
18965    }
18966    my ($Sym, $Spec, $Ver) = separate_symbol($Symbol);
18967    if($Sym and $Ver)
18968    { # search for the symbol with the same version
18969      # or without version
18970        if($Where->{$RunWith}{$Sym})
18971        { # old: foo@v|foo@@v
18972          # new: foo
18973            return 1;
18974        }
18975        if($Where->{$RunWith}{$Sym."\@".$Ver})
18976        { # old: foo|foo@@v
18977          # new: foo@v
18978            return 1;
18979        }
18980        if($Where->{$RunWith}{$Sym."\@\@".$Ver})
18981        { # old: foo|foo@v
18982          # new: foo@@v
18983            return 1;
18984        }
18985    }
18986    return 0;
18987}
18988
18989sub readSymbols_App($)
18990{
18991    my $Path = $_[0];
18992    return () if(not $Path);
18993    my @Imported = ();
18994    if($OStarget eq "macos")
18995    {
18996        my $NM = get_CmdPath("nm");
18997        if(not $NM) {
18998            exitStatus("Not_Found", "can't find \"nm\"");
18999        }
19000        open(APP, "$NM -g \"$Path\" 2>\"$TMP_DIR/null\" |");
19001        while(<APP>)
19002        {
19003            if(/ U _([\w\$]+)\s*\Z/) {
19004                push(@Imported, $1);
19005            }
19006        }
19007        close(APP);
19008    }
19009    elsif($OStarget eq "windows")
19010    {
19011        my $DumpBinCmd = get_CmdPath("dumpbin");
19012        if(not $DumpBinCmd) {
19013            exitStatus("Not_Found", "can't find \"dumpbin.exe\"");
19014        }
19015        open(APP, "$DumpBinCmd /IMPORTS \"$Path\" 2>\"$TMP_DIR/null\" |");
19016        while(<APP>)
19017        {
19018            if(/\s*\w+\s+\w+\s+\w+\s+([\w\?\@]+)\s*/) {
19019                push(@Imported, $1);
19020            }
19021        }
19022        close(APP);
19023    }
19024    else
19025    {
19026        my $ReadelfCmd = get_CmdPath("readelf");
19027        if(not $ReadelfCmd) {
19028            exitStatus("Not_Found", "can't find \"readelf\"");
19029        }
19030        open(APP, "$ReadelfCmd -Ws \"$Path\" 2>\"$TMP_DIR/null\" |");
19031        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
19032        while(<APP>)
19033        {
19034            if(defined $symtab)
19035            { # do nothing with symtab
19036                if(index($_, "'.dynsym'")!=-1)
19037                { # dynamic table
19038                    $symtab = undef;
19039                }
19040            }
19041            elsif(index($_, "'.symtab'")!=-1)
19042            { # symbol table
19043                $symtab = 1;
19044            }
19045            elsif(my @Info = readline_ELF($_))
19046            {
19047                my ($Ndx, $Symbol) = ($Info[5], $Info[6]);
19048                if($Ndx eq "UND")
19049                { # only imported symbols
19050                    push(@Imported, $Symbol);
19051                }
19052            }
19053        }
19054        close(APP);
19055    }
19056    return @Imported;
19057}
19058
19059my %ELF_BIND = map {$_=>1} (
19060    "WEAK",
19061    "GLOBAL"
19062);
19063
19064my %ELF_TYPE = map {$_=>1} (
19065    "FUNC",
19066    "IFUNC",
19067    "OBJECT",
19068    "COMMON"
19069);
19070
19071my %ELF_VIS = map {$_=>1} (
19072    "DEFAULT",
19073    "PROTECTED"
19074);
19075
19076sub readline_ELF($)
19077{ # read the line of 'readelf' output corresponding to the symbol
19078    my @Info = split(/\s+/, $_[0]);
19079    #  Num:   Value      Size Type   Bind   Vis       Ndx  Name
19080    #  3629:  000b09c0   32   FUNC   GLOBAL DEFAULT   13   _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4
19081    #  135:   00000000    0   FUNC   GLOBAL DEFAULT   UND  av_image_fill_pointers@LIBAVUTIL_52 (3)
19082    shift(@Info); # spaces
19083    shift(@Info); # num
19084
19085    if($#Info==7)
19086    { # UND SYMBOL (N)
19087        if($Info[7]=~/\(\d+\)/) {
19088            pop(@Info);
19089        }
19090    }
19091
19092    if($#Info!=6)
19093    { # other lines
19094        return ();
19095    }
19096    return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UND");
19097    return () if(not defined $ELF_BIND{$Info[3]});
19098    return () if(not defined $ELF_VIS{$Info[4]});
19099    if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/)
19100    { # 1272: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS CXXABI_1.3
19101        return ();
19102    }
19103    if($OStarget eq "symbian")
19104    { # _ZN12CCTTokenType4NewLE4TUid3RFs@@ctfinder{000a0000}[102020e5].dll
19105        if(index($Info[6], "_._.absent_export_")!=-1)
19106        { # "_._.absent_export_111"@@libstdcpp{00010001}[10282872].dll
19107            return ();
19108        }
19109        $Info[6]=~s/\@.+//g; # remove version
19110    }
19111    if(index($Info[2], "0x") == 0)
19112    { # size == 0x3d158
19113        $Info[2] = hex($Info[2]);
19114    }
19115    return @Info;
19116}
19117
19118sub get_LibPath($$)
19119{
19120    my ($LibVersion, $Name) = @_;
19121    return "" if(not $LibVersion or not $Name);
19122    if(defined $Cache{"get_LibPath"}{$LibVersion}{$Name}) {
19123        return $Cache{"get_LibPath"}{$LibVersion}{$Name};
19124    }
19125    return ($Cache{"get_LibPath"}{$LibVersion}{$Name} = get_LibPath_I($LibVersion, $Name));
19126}
19127
19128sub get_LibPath_I($$)
19129{
19130    my ($LibVersion, $Name) = @_;
19131    if(is_abs($Name))
19132    {
19133        if(-f $Name)
19134        { # absolute path
19135            return $Name;
19136        }
19137        else
19138        { # broken
19139            return "";
19140        }
19141    }
19142    if(defined $RegisteredObjects{$LibVersion}{$Name})
19143    { # registered paths
19144        return $RegisteredObjects{$LibVersion}{$Name};
19145    }
19146    if(defined $RegisteredSONAMEs{$LibVersion}{$Name})
19147    { # registered paths
19148        return $RegisteredSONAMEs{$LibVersion}{$Name};
19149    }
19150    if(my $DefaultPath = $DyLib_DefaultPath{$Name})
19151    { # ldconfig default paths
19152        return $DefaultPath;
19153    }
19154    foreach my $Dir (@DefaultLibPaths, @{$SystemPaths{"lib"}})
19155    { # search in default linker directories
19156      # and then in all system paths
19157        if(-f $Dir."/".$Name) {
19158            return join_P($Dir,$Name);
19159        }
19160    }
19161    if(not defined $Cache{"checkSystemFiles"}) {
19162        checkSystemFiles();
19163    }
19164    if(my @AllObjects = keys(%{$SystemObjects{$Name}})) {
19165        return $AllObjects[0];
19166    }
19167    if(my $ShortName = parse_libname($Name, "name+ext", $OStarget))
19168    {
19169        if($ShortName ne $Name)
19170        { # FIXME: check this case
19171            if(my $Path = get_LibPath($LibVersion, $ShortName)) {
19172                return $Path;
19173            }
19174        }
19175    }
19176    # can't find
19177    return "";
19178}
19179
19180sub readSymbols_Lib($$$$$$)
19181{
19182    my ($LibVersion, $Lib_Path, $IsNeededLib, $Weak, $Deps, $Vers) = @_;
19183    return () if(not $LibVersion or not $Lib_Path);
19184
19185    my $Real_Path = realpath_F($Lib_Path);
19186
19187    if(not $Real_Path)
19188    { # broken link
19189        return ();
19190    }
19191
19192    my $Lib_Name = get_filename($Real_Path);
19193
19194    if($ExtraInfo)
19195    {
19196        $KnownLibs{$Real_Path} = 1;
19197        $KnownLibs{$Lib_Path} = 1; # links
19198    }
19199
19200    if($IsNeededLib)
19201    {
19202        if($CheckedDyLib{$LibVersion}{$Lib_Name}) {
19203            return ();
19204        }
19205    }
19206    return () if(isCyclical(\@RecurLib, $Lib_Name) or $#RecurLib>=1);
19207    $CheckedDyLib{$LibVersion}{$Lib_Name} = 1;
19208
19209    push(@RecurLib, $Lib_Name);
19210    my (%Value_Interface, %Interface_Value, %NeededLib) = ();
19211    my $Lib_ShortName = parse_libname($Lib_Name, "name+ext", $OStarget);
19212
19213    if(not $IsNeededLib)
19214    { # special cases: libstdc++ and libc
19215        if(my $ShortName = parse_libname($Lib_Name, "short", $OStarget))
19216        {
19217            if($ShortName eq "libstdc++")
19218            { # libstdc++.so.6
19219                $STDCXX_TESTING = 1;
19220            }
19221            elsif($ShortName eq "libc")
19222            { # libc-2.11.3.so
19223                $GLIBC_TESTING = 1;
19224            }
19225        }
19226    }
19227    my $DebugPath = "";
19228    if($Debug and not $DumpSystem)
19229    { # debug mode
19230        $DebugPath = $DEBUG_PATH{$LibVersion}."/libs/".get_filename($Lib_Path).".txt";
19231        mkpath(get_dirname($DebugPath));
19232    }
19233    if($OStarget eq "macos")
19234    { # Mac OS X: *.dylib, *.a
19235        my $NM = get_CmdPath("nm");
19236        if(not $NM) {
19237            exitStatus("Not_Found", "can't find \"nm\"");
19238        }
19239        $NM .= " -g \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
19240        if($DebugPath)
19241        { # debug mode
19242          # write to file
19243            system($NM." >\"$DebugPath\"");
19244            open(LIB, $DebugPath);
19245        }
19246        else
19247        { # write to pipe
19248            open(LIB, $NM." |");
19249        }
19250        while(<LIB>)
19251        {
19252            if($CheckUndefined)
19253            {
19254                if(not $IsNeededLib)
19255                {
19256                    if(/ U _([\w\$]+)\s*\Z/)
19257                    {
19258                        $UndefinedSymbols{$LibVersion}{$Lib_Name}{$1} = 0;
19259                        next;
19260                    }
19261                }
19262            }
19263
19264            if(/ [STD] _([\w\$]+)\s*\Z/)
19265            {
19266                my $Symbol = $1;
19267                if($IsNeededLib)
19268                {
19269                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
19270                    {
19271                        $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19272                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1;
19273                    }
19274                }
19275                else
19276                {
19277                    $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19278                    $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1;
19279                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
19280                    {
19281                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
19282                            setLanguage($LibVersion, "C++");
19283                        }
19284                    }
19285                }
19286            }
19287        }
19288        close(LIB);
19289
19290        if($Deps)
19291        {
19292            if($LIB_TYPE eq "dynamic")
19293            { # dependencies
19294
19295                my $OtoolCmd = get_CmdPath("otool");
19296                if(not $OtoolCmd) {
19297                    exitStatus("Not_Found", "can't find \"otool\"");
19298                }
19299
19300                open(LIB, "$OtoolCmd -L \"$Lib_Path\" 2>\"$TMP_DIR/null\" |");
19301                while(<LIB>)
19302                {
19303                    if(/\s*([\/\\].+\.$LIB_EXT)\s*/
19304                    and $1 ne $Lib_Path) {
19305                        $NeededLib{$1} = 1;
19306                    }
19307                }
19308                close(LIB);
19309            }
19310        }
19311    }
19312    elsif($OStarget eq "windows")
19313    { # Windows *.dll, *.lib
19314        my $DumpBinCmd = get_CmdPath("dumpbin");
19315        if(not $DumpBinCmd) {
19316            exitStatus("Not_Found", "can't find \"dumpbin\"");
19317        }
19318        $DumpBinCmd .= " /EXPORTS \"".$Lib_Path."\" 2>$TMP_DIR/null";
19319        if($DebugPath)
19320        { # debug mode
19321          # write to file
19322            system($DumpBinCmd." >\"$DebugPath\"");
19323            open(LIB, $DebugPath);
19324        }
19325        else
19326        { # write to pipe
19327            open(LIB, $DumpBinCmd." |");
19328        }
19329        while(<LIB>)
19330        {
19331            my $realname = undef;
19332            if($LIB_TYPE eq "dynamic")
19333            {
19334                # 1197 4AC 0000A620 SetThreadStackGuarantee
19335                # 1198 4AD          SetThreadToken (forwarded to ...)
19336                # 3368 _o2i_ECPublicKey
19337                # 1 0 00005B30 ??0?N = ... (with pdb)
19338                if(/\A\s*\d+\s+[a-f\d]+\s+[a-f\d]+\s+([\w\?\@]+)\s*(?:=.+)?\Z/i
19339                or /\A\s*\d+\s+[a-f\d]+\s+([\w\?\@]+)\s*\(\s*forwarded\s+/
19340                or /\A\s*\d+\s+_([\w\?\@]+)\s*(?:=.+)?\Z/)
19341                { # dynamic, static and forwarded symbols
19342                    $realname = $1;
19343                }
19344            }
19345            else
19346            { # static
19347                if(/\A\s{10,}\d*\s+([\w\?\@]+)\s*\Z/i)
19348                {
19349                    # 16 IID_ISecurityInformation
19350                    $realname = $1;
19351                }
19352            }
19353
19354            if($realname)
19355            {
19356                if($IsNeededLib)
19357                {
19358                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
19359                    {
19360                        $DepSymbol_Library{$LibVersion}{$realname} = $Lib_Name;
19361                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
19362                    }
19363                }
19364                else
19365                {
19366                    $Symbol_Library{$LibVersion}{$realname} = $Lib_Name;
19367                    $Library_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
19368                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
19369                    {
19370                        if(index($realname, "_Z")==0 or index($realname, "?")==0) {
19371                            setLanguage($LibVersion, "C++");
19372                        }
19373                    }
19374                }
19375            }
19376        }
19377        close(LIB);
19378
19379        if($Deps)
19380        {
19381            if($LIB_TYPE eq "dynamic")
19382            { # dependencies
19383                open(LIB, "$DumpBinCmd /DEPENDENTS \"$Lib_Path\" 2>\"$TMP_DIR/null\" |");
19384                while(<LIB>)
19385                {
19386                    if(/\s*([^\s]+?\.$LIB_EXT)\s*/i
19387                    and $1 ne $Lib_Path) {
19388                        $NeededLib{path_format($1, $OSgroup)} = 1;
19389                    }
19390                }
19391                close(LIB);
19392            }
19393        }
19394    }
19395    else
19396    { # Unix; *.so, *.a
19397      # Symbian: *.dso, *.lib
19398        my $ReadelfCmd = get_CmdPath("readelf");
19399        if(not $ReadelfCmd) {
19400            exitStatus("Not_Found", "can't find \"readelf\"");
19401        }
19402        my $Cmd = $ReadelfCmd." -Ws \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
19403        if($DebugPath)
19404        { # debug mode
19405          # write to file
19406            system($Cmd." >\"$DebugPath\"");
19407            open(LIB, $DebugPath);
19408        }
19409        else
19410        { # write to pipe
19411            open(LIB, $Cmd." |");
19412        }
19413        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
19414        while(<LIB>)
19415        {
19416            if($LIB_TYPE eq "dynamic")
19417            { # dynamic library specifics
19418                if(defined $symtab)
19419                {
19420                    if(index($_, "'.dynsym'")!=-1)
19421                    { # dynamic table
19422                        $symtab = undef;
19423                    }
19424                    # do nothing with symtab
19425                    next;
19426                }
19427                elsif(index($_, "'.symtab'")!=-1)
19428                { # symbol table
19429                    $symtab = 1;
19430                    next;
19431                }
19432            }
19433            if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_))
19434            { # read ELF entry
19435                if($Ndx eq "UND")
19436                { # ignore interfaces that are imported from somewhere else
19437                    if($CheckUndefined)
19438                    {
19439                        if(not $IsNeededLib) {
19440                            $UndefinedSymbols{$LibVersion}{$Lib_Name}{$Symbol} = 0;
19441                        }
19442                    }
19443                    next;
19444                }
19445                if($Bind eq "WEAK")
19446                {
19447                    $WeakSymbols{$LibVersion}{$Symbol} = 1;
19448                    if($Weak eq "-Weak")
19449                    { # skip WEAK symbols
19450                        next;
19451                    }
19452                }
19453                my $Short = $Symbol;
19454                $Short=~s/\@.+//g;
19455                if($Type eq "OBJECT")
19456                { # global data
19457                    $GlobalDataObject{$LibVersion}{$Symbol} = $Size;
19458                    $GlobalDataObject{$LibVersion}{$Short} = $Size;
19459                }
19460                if($IsNeededLib)
19461                {
19462                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
19463                    {
19464                        $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19465                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
19466                    }
19467                }
19468                else
19469                {
19470                    $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19471                    $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
19472                    if($Vers)
19473                    {
19474                        if($LIB_EXT eq "so")
19475                        { # value
19476                            $Interface_Value{$LibVersion}{$Symbol} = $Value;
19477                            $Value_Interface{$LibVersion}{$Value}{$Symbol} = 1;
19478                        }
19479                    }
19480                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
19481                    {
19482                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
19483                            setLanguage($LibVersion, "C++");
19484                        }
19485                    }
19486                }
19487            }
19488        }
19489        close(LIB);
19490
19491        if($Deps and $LIB_TYPE eq "dynamic")
19492        { # dynamic library specifics
19493            $Cmd = $ReadelfCmd." -Wd \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
19494            open(LIB, $Cmd." |");
19495
19496            while(<LIB>)
19497            {
19498                if(/NEEDED.+\[([^\[\]]+)\]/)
19499                { # dependencies:
19500                  # 0x00000001 (NEEDED) Shared library: [libc.so.6]
19501                    $NeededLib{$1} = 1;
19502                }
19503            }
19504
19505            close(LIB);
19506        }
19507    }
19508    if($Vers)
19509    {
19510        if(not $IsNeededLib and $LIB_EXT eq "so")
19511        { # get symbol versions
19512            my %Found = ();
19513
19514            # by value
19515            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19516            {
19517                next if(index($Symbol,"\@")==-1);
19518                if(my $Value = $Interface_Value{$LibVersion}{$Symbol})
19519                {
19520                    foreach my $Symbol_SameValue (keys(%{$Value_Interface{$LibVersion}{$Value}}))
19521                    {
19522                        if($Symbol_SameValue ne $Symbol
19523                        and index($Symbol_SameValue,"\@")==-1)
19524                        {
19525                            $SymVer{$LibVersion}{$Symbol_SameValue} = $Symbol;
19526                            $Found{$Symbol} = 1;
19527                            last;
19528                        }
19529                    }
19530                }
19531            }
19532
19533            # default
19534            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19535            {
19536                next if(defined $Found{$Symbol});
19537                next if(index($Symbol,"\@\@")==-1);
19538
19539                if($Symbol=~/\A([^\@]*)\@\@/
19540                and not $SymVer{$LibVersion}{$1})
19541                {
19542                    $SymVer{$LibVersion}{$1} = $Symbol;
19543                    $Found{$Symbol} = 1;
19544                }
19545            }
19546
19547            # non-default
19548            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19549            {
19550                next if(defined $Found{$Symbol});
19551                next if(index($Symbol,"\@")==-1);
19552
19553                if($Symbol=~/\A([^\@]*)\@([^\@]*)/
19554                and not $SymVer{$LibVersion}{$1})
19555                {
19556                    $SymVer{$LibVersion}{$1} = $Symbol;
19557                    $Found{$Symbol} = 1;
19558                }
19559            }
19560        }
19561    }
19562    if($Deps)
19563    {
19564        foreach my $DyLib (sort keys(%NeededLib))
19565        {
19566            $Library_Needed{$LibVersion}{$Lib_Name}{get_filename($DyLib)} = 1;
19567
19568            if(my $DepPath = get_LibPath($LibVersion, $DyLib))
19569            {
19570                if(not $CheckedDyLib{$LibVersion}{get_filename($DepPath)}) {
19571                    readSymbols_Lib($LibVersion, $DepPath, 1, "+Weak", $Deps, $Vers);
19572                }
19573            }
19574        }
19575    }
19576    pop(@RecurLib);
19577    return $Library_Symbol{$LibVersion};
19578}
19579
19580sub get_prefixes($)
19581{
19582    my %Prefixes = ();
19583    get_prefixes_I([$_[0]], \%Prefixes);
19584    return keys(%Prefixes);
19585}
19586
19587sub get_prefixes_I($$)
19588{
19589    foreach my $P (@{$_[0]})
19590    {
19591        my @Parts = reverse(split(/[\/\\]+/, $P));
19592        my $Name = $Parts[0];
19593        foreach (1 .. $#Parts)
19594        {
19595            $_[1]->{$Name}{$P} = 1;
19596            last if($_>4 or $Parts[$_] eq "include");
19597            $Name = $Parts[$_].$SLASH.$Name;
19598        }
19599    }
19600}
19601
19602sub checkSystemFiles()
19603{
19604    $Cache{"checkSystemFiles"} = 1;
19605
19606    my @SysHeaders = ();
19607
19608    foreach my $DevelPath (@{$SystemPaths{"lib"}})
19609    {
19610        next if(not -d $DevelPath);
19611
19612        my @Files = cmd_find($DevelPath,"f");
19613        foreach my $Link (cmd_find($DevelPath,"l"))
19614        { # add symbolic links
19615            if(-f $Link) {
19616                push(@Files, $Link);
19617            }
19618        }
19619
19620        # search for headers in /usr/lib
19621        my @Headers = grep { /\.h(pp|xx)?\Z|\/include\// } @Files;
19622        @Headers = grep { not /\/(gcc|jvm|syslinux|kbd|parrot|xemacs|perl|llvm)/ } @Headers;
19623        push(@SysHeaders, @Headers);
19624
19625        # search for libraries in /usr/lib (including symbolic links)
19626        my @Libs = grep { /\.$LIB_EXT[0-9.]*\Z/ } @Files;
19627        foreach my $Path (@Libs)
19628        {
19629            my $N = get_filename($Path);
19630            $SystemObjects{$N}{$Path} = 1;
19631            $SystemObjects{parse_libname($N, "name+ext", $OStarget)}{$Path} = 1;
19632        }
19633    }
19634
19635    foreach my $DevelPath (@{$SystemPaths{"include"}})
19636    {
19637        next if(not -d $DevelPath);
19638        # search for all header files in the /usr/include
19639        # with or without extension (ncurses.h, QtCore, ...)
19640        push(@SysHeaders, cmd_find($DevelPath,"f"));
19641        foreach my $Link (cmd_find($DevelPath,"l"))
19642        { # add symbolic links
19643            if(-f $Link) {
19644                push(@SysHeaders, $Link);
19645            }
19646        }
19647    }
19648    get_prefixes_I(\@SysHeaders, \%SystemHeaders);
19649}
19650
19651sub getSOPaths($)
19652{
19653    my $LibVersion = $_[0];
19654    my @Paths = ();
19655    foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Libs"}))
19656    {
19657        if(not -e $Dest) {
19658            exitStatus("Access_Error", "can't access \'$Dest\'");
19659        }
19660        $Dest = get_abs_path($Dest);
19661        my @SoPaths_Dest = getSOPaths_Dest($Dest, $LibVersion);
19662        foreach (@SoPaths_Dest) {
19663            push(@Paths, $_);
19664        }
19665    }
19666    return sort @Paths;
19667}
19668
19669sub skipLib($$)
19670{
19671    my ($Path, $LibVersion) = @_;
19672    return 1 if(not $Path or not $LibVersion);
19673    my $Name = get_filename($Path);
19674    if($SkipLibs{$LibVersion}{"Name"}{$Name}) {
19675        return 1;
19676    }
19677    my $ShortName = parse_libname($Name, "name+ext", $OStarget);
19678    if($SkipLibs{$LibVersion}{"Name"}{$ShortName}) {
19679        return 1;
19680    }
19681    foreach my $Dir (keys(%{$SkipLibs{$LibVersion}{"Path"}}))
19682    {
19683        if($Path=~/\Q$Dir\E([\/\\]|\Z)/) {
19684            return 1;
19685        }
19686    }
19687    foreach my $P (keys(%{$SkipLibs{$LibVersion}{"Pattern"}}))
19688    {
19689        if($Name=~/$P/) {
19690            return 1;
19691        }
19692        if($P=~/[\/\\]/ and $Path=~/$P/) {
19693            return 1;
19694        }
19695    }
19696    return 0;
19697}
19698
19699sub specificHeader($$)
19700{
19701    my ($Header, $Spec) = @_;
19702    my $Name = get_filename($Header);
19703
19704    if($Spec eq "windows")
19705    {# MS Windows
19706        return 1 if($Name=~/(\A|[._-])(win|wince|wnt)(\d\d|[._-]|\Z)/i);
19707        return 1 if($Name=~/([._-]w|win)(32|64)/i);
19708        return 1 if($Name=~/\A(Win|Windows)[A-Z]/);
19709        return 1 if($Name=~/\A(w|win|windows)(32|64|\.)/i);
19710        my @Dirs = (
19711            "win32",
19712            "win64",
19713            "win",
19714            "windows",
19715            "msvcrt"
19716        ); # /gsf-win32/
19717        if(my $DIRs = join("|", @Dirs)) {
19718            return 1 if($Header=~/[\/\\](|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i);
19719        }
19720    }
19721    elsif($Spec eq "macos")
19722    { # Mac OS
19723        return 1 if($Name=~/(\A|[_-])mac[._-]/i);
19724    }
19725
19726    return 0;
19727}
19728
19729sub skipAlienHeader($)
19730{
19731    my $Path = $_[0];
19732    my $Name = get_filename($Path);
19733    my $Dir = get_dirname($Path);
19734
19735    if($Tolerance=~/2/)
19736    { # 2 - skip internal headers
19737        my @Terms = (
19738            "p",
19739            "priv",
19740            "int",
19741            "impl",
19742            "implementation",
19743            "internal",
19744            "private",
19745            "old",
19746            "compat",
19747            "debug",
19748            "test",
19749            "gen"
19750        );
19751
19752        my @Dirs = (
19753            "private",
19754            "priv",
19755            "port",
19756            "impl",
19757            "internal",
19758            "detail",
19759            "details",
19760            "old",
19761            "compat",
19762            "debug",
19763            "config",
19764            "compiler",
19765            "platform",
19766            "test"
19767        );
19768
19769        if(my $TERMs = join("|", @Terms)) {
19770            return 1 if($Name=~/(\A|[._-])($TERMs)([._-]|\Z)/i);
19771        }
19772        if(my $DIRs = join("|", @Dirs)) {
19773            return 1 if($Dir=~/(\A|[\/\\])(|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i);
19774        }
19775
19776        return 1 if($Name=~/[a-z](Imp|Impl|I|P)(\.|\Z)/);
19777    }
19778
19779    if($Tolerance=~/1/)
19780    { # 1 - skip non-Linux headers
19781        if($OSgroup ne "windows")
19782        {
19783            if(specificHeader($Path, "windows")) {
19784                return 1;
19785            }
19786        }
19787        if($OSgroup ne "macos")
19788        {
19789            if(specificHeader($Path, "macos")) {
19790                return 1;
19791            }
19792        }
19793    }
19794
19795    # valid
19796    return 0;
19797}
19798
19799sub skipHeader($$)
19800{
19801    my ($Path, $LibVersion) = @_;
19802    return 1 if(not $Path or not $LibVersion);
19803    if(defined $Cache{"skipHeader"}{$Path}) {
19804        return $Cache{"skipHeader"}{$Path};
19805    }
19806    if(defined $Tolerance and $Tolerance=~/1|2/)
19807    { # --tolerant
19808        if(skipAlienHeader($Path)) {
19809            return ($Cache{"skipHeader"}{$Path} = 1);
19810        }
19811    }
19812    if(not keys(%{$SkipHeaders{$LibVersion}})) {
19813        return 0;
19814    }
19815    return ($Cache{"skipHeader"}{$Path} = skipHeader_I(@_));
19816}
19817
19818sub skipHeader_I($$)
19819{ # returns:
19820  #  1 - if header should NOT be included and checked
19821  #  2 - if header should NOT be included, but should be checked
19822    my ($Path, $LibVersion) = @_;
19823    my $Name = get_filename($Path);
19824    if(my $Kind = $SkipHeaders{$LibVersion}{"Name"}{$Name}) {
19825        return $Kind;
19826    }
19827    foreach my $D (sort {$SkipHeaders{$LibVersion}{"Path"}{$a} cmp $SkipHeaders{$LibVersion}{"Path"}{$b}}
19828    keys(%{$SkipHeaders{$LibVersion}{"Path"}}))
19829    {
19830        if(index($Path, $D)!=-1)
19831        {
19832            if($Path=~/\Q$D\E([\/\\]|\Z)/) {
19833                return $SkipHeaders{$LibVersion}{"Path"}{$D};
19834            }
19835        }
19836    }
19837    foreach my $P (sort {$SkipHeaders{$LibVersion}{"Pattern"}{$a} cmp $SkipHeaders{$LibVersion}{"Pattern"}{$b}}
19838    keys(%{$SkipHeaders{$LibVersion}{"Pattern"}}))
19839    {
19840        if(my $Kind = $SkipHeaders{$LibVersion}{"Pattern"}{$P})
19841        {
19842            if($Name=~/$P/) {
19843                return $Kind;
19844            }
19845            if($P=~/[\/\\]/ and $Path=~/$P/) {
19846                return $Kind;
19847            }
19848        }
19849    }
19850
19851    return 0;
19852}
19853
19854sub registerObject_Dir($$)
19855{
19856    my ($Dir, $LibVersion) = @_;
19857    if(grep {$_ eq $Dir} @{$SystemPaths{"lib"}})
19858    { # system directory
19859        return;
19860    }
19861    if($RegisteredObject_Dirs{$LibVersion}{$Dir})
19862    { # already registered
19863        return;
19864    }
19865    foreach my $Path (find_libs($Dir,"",1))
19866    {
19867        next if(ignore_path($Path));
19868        next if(skipLib($Path, $LibVersion));
19869        registerObject($Path, $LibVersion);
19870    }
19871    $RegisteredObject_Dirs{$LibVersion}{$Dir} = 1;
19872}
19873
19874sub registerObject($$)
19875{
19876    my ($Path, $LibVersion) = @_;
19877
19878    my $Name = get_filename($Path);
19879    $RegisteredObjects{$LibVersion}{$Name} = $Path;
19880    if($OStarget=~/linux|bsd|gnu|solaris/i)
19881    {
19882        if(my $SONAME = getSONAME($Path)) {
19883            $RegisteredSONAMEs{$LibVersion}{$SONAME} = $Path;
19884        }
19885    }
19886    if(my $Short = parse_libname($Name, "name+ext", $OStarget)) {
19887        $RegisteredObjects_Short{$LibVersion}{$Short} = $Path;
19888    }
19889
19890    if(not $CheckedArch{$LibVersion} and -f $Path)
19891    {
19892        if(my $ObjArch = getArch_Object($Path))
19893        {
19894            if($ObjArch ne getArch_GCC($LibVersion))
19895            { # translation unit dump generated by the GCC compiler should correspond to the input objects
19896                $CheckedArch{$LibVersion} = 1;
19897                printMsg("WARNING", "the architectures of input objects and the used GCC compiler are not equal, please change the compiler by --gcc-path=PATH option.");
19898            }
19899        }
19900    }
19901}
19902
19903sub getArch_Object($)
19904{
19905    my $Path = $_[0];
19906
19907    my %MachineType = (
19908        "14C" => "x86",
19909        "8664" => "x86_64",
19910        "1C0" => "arm",
19911        "200" => "ia64"
19912    );
19913
19914    my %ArchName = (
19915        "s390:31-bit" => "s390",
19916        "s390:64-bit" => "s390x",
19917        "powerpc:common" => "ppc32",
19918        "powerpc:common64" => "ppc64",
19919        "i386:x86-64" => "x86_64",
19920        "mips:3000" => "mips",
19921        "sparc:v8plus" => "sparcv9"
19922    );
19923
19924    if($OStarget eq "windows")
19925    {
19926        my $DumpbinCmd = get_CmdPath("dumpbin");
19927        if(not $DumpbinCmd) {
19928            exitStatus("Not_Found", "can't find \"dumpbin\"");
19929        }
19930
19931        my $Cmd = $DumpbinCmd." /headers \"$Path\"";
19932        my $Out = `$Cmd`;
19933
19934        if($Out=~/(\w+)\smachine/)
19935        {
19936            if(my $Type = $MachineType{uc($1)})
19937            {
19938                return $Type;
19939            }
19940        }
19941    }
19942    elsif($OStarget=~/macos/)
19943    {
19944        my $OtoolCmd = get_CmdPath("otool");
19945        if(not $OtoolCmd) {
19946            exitStatus("Not_Found", "can't find \"otool\"");
19947        }
19948
19949        my $Cmd = $OtoolCmd." -hv -arch all \"$Path\"";
19950        my $Out = qx/$Cmd/;
19951
19952        if($Out=~/X86_64/i) {
19953            return "x86_64";
19954        }
19955        elsif($Out=~/X86/i) {
19956            return "x86";
19957        }
19958    }
19959    else
19960    { # linux, bsd, gnu, solaris, ...
19961        my $ObjdumpCmd = get_CmdPath("objdump");
19962        if(not $ObjdumpCmd) {
19963            exitStatus("Not_Found", "can't find \"objdump\"");
19964        }
19965
19966        my $Cmd = $ObjdumpCmd." -f \"$Path\"";
19967
19968        if($OSgroup eq "windows") {
19969            $Cmd = "set LANG=$LOCALE & ".$Cmd;
19970        }
19971        else {
19972            $Cmd = "LANG=$LOCALE ".$Cmd;
19973        }
19974        my $Out = `$Cmd`;
19975
19976        if($Out=~/architecture:\s+([\w\-\:]+)/)
19977        {
19978            my $Arch = $1;
19979            if($Arch=~s/\:(.+)//)
19980            {
19981                my $Suffix = $1;
19982
19983                if(my $Name = $ArchName{$Arch.":".$Suffix})
19984                {
19985                    $Arch = $Name;
19986                }
19987            }
19988
19989            if($Arch=~/i[3-6]86/) {
19990                $Arch = "x86";
19991            }
19992
19993            if($Arch eq "x86-64") {
19994                $Arch = "x86_64";
19995            }
19996
19997            if($Arch eq "ia64-elf64") {
19998                $Arch = "ia64";
19999            }
20000
20001            return $Arch;
20002        }
20003    }
20004
20005    return undef;
20006}
20007
20008sub getSONAME($)
20009{
20010    my $Path = $_[0];
20011    return if(not $Path);
20012    if(defined $Cache{"getSONAME"}{$Path}) {
20013        return $Cache{"getSONAME"}{$Path};
20014    }
20015    my $ObjdumpCmd = get_CmdPath("objdump");
20016    if(not $ObjdumpCmd) {
20017        exitStatus("Not_Found", "can't find \"objdump\"");
20018    }
20019    my $SonameCmd = "$ObjdumpCmd -x \"$Path\" 2>$TMP_DIR/null";
20020    if($OSgroup eq "windows") {
20021        $SonameCmd .= " | find \"SONAME\"";
20022    }
20023    else {
20024        $SonameCmd .= " | grep SONAME";
20025    }
20026    if(my $SonameInfo = `$SonameCmd`)
20027    {
20028        if($SonameInfo=~/SONAME\s+([^\s]+)/) {
20029            return ($Cache{"getSONAME"}{$Path} = $1);
20030        }
20031    }
20032    return ($Cache{"getSONAME"}{$Path}="");
20033}
20034
20035sub getSOPaths_Dest($$)
20036{
20037    my ($Dest, $LibVersion) = @_;
20038    if(skipLib($Dest, $LibVersion)) {
20039        return ();
20040    }
20041    if(-f $Dest)
20042    {
20043        if(not parse_libname($Dest, "name", $OStarget)) {
20044            exitStatus("Error", "incorrect format of library (should be *.$LIB_EXT): \'$Dest\'");
20045        }
20046        registerObject($Dest, $LibVersion);
20047        registerObject_Dir(get_dirname($Dest), $LibVersion);
20048        return ($Dest);
20049    }
20050    elsif(-d $Dest)
20051    {
20052        $Dest=~s/[\/\\]+\Z//g;
20053        my %Libs = ();
20054        if(grep { $Dest eq $_ } @{$SystemPaths{"lib"}})
20055        { # you have specified /usr/lib as the search directory (<libs>) in the XML descriptor
20056          # and the real name of the library by -l option (bz2, stdc++, Xaw, ...)
20057            foreach my $Path (cmd_find($Dest,"","*".esc($TargetLibraryName)."*.$LIB_EXT*",2))
20058            { # all files and symlinks that match the name of a library
20059                if(get_filename($Path)=~/\A(|lib)\Q$TargetLibraryName\E[\d\-]*\.$LIB_EXT[\d\.]*\Z/i)
20060                {
20061                    registerObject($Path, $LibVersion);
20062                    $Libs{realpath_F($Path)} = 1;
20063                }
20064            }
20065        }
20066        else
20067        { # search for all files and symlinks
20068            foreach my $Path (find_libs($Dest,"",""))
20069            {
20070                next if(ignore_path($Path));
20071                next if(skipLib($Path, $LibVersion));
20072                registerObject($Path, $LibVersion);
20073                $Libs{realpath_F($Path)} = 1;
20074            }
20075            if($OSgroup eq "macos")
20076            { # shared libraries on MacOS X may have no extension
20077                foreach my $Path (cmd_find($Dest,"f"))
20078                {
20079                    next if(ignore_path($Path));
20080                    next if(skipLib($Path, $LibVersion));
20081                    if(get_filename($Path)!~/\./
20082                    and cmd_file($Path)=~/(shared|dynamic)\s+library/i)
20083                    {
20084                        registerObject($Path, $LibVersion);
20085                        $Libs{realpath_F($Path)} = 1;
20086                    }
20087                }
20088            }
20089        }
20090        return keys(%Libs);
20091    }
20092    else {
20093        return ();
20094    }
20095}
20096
20097sub realpath_F($)
20098{
20099    my $Path = $_[0];
20100    return path_format(realpath($Path), $OSgroup);
20101}
20102
20103sub isCyclical($$)
20104{
20105    my ($Stack, $Value) = @_;
20106    return (grep {$_ eq $Value} @{$Stack});
20107}
20108
20109sub getGCC_Opts($)
20110{ # to use in module
20111    my $LibVersion = $_[0];
20112
20113    my @Opts = ();
20114
20115    if($CompilerOptions{$LibVersion})
20116    { # user-defined options
20117        push(@Opts, $CompilerOptions{$LibVersion});
20118    }
20119    if($GccOptions)
20120    { # additional
20121        push(@Opts, $GccOptions);
20122    }
20123
20124    if(@Opts) {
20125        return join(" ", @Opts);
20126    }
20127
20128    return undef;
20129}
20130
20131sub getArch_GCC($)
20132{
20133    my $LibVersion = $_[0];
20134
20135    if(defined $Cache{"getArch_GCC"}{$LibVersion}) {
20136        return $Cache{"getArch_GCC"}{$LibVersion};
20137    }
20138
20139    if(not $GCC_PATH) {
20140        return undef;
20141    }
20142
20143    my $Arch = undef;
20144
20145    if(my $Target = get_dumpmachine($GCC_PATH))
20146    {
20147        if($Target=~/x86_64/) {
20148            $Arch = "x86_64";
20149        }
20150        elsif($Target=~/i[3-6]86/) {
20151            $Arch = "x86";
20152        }
20153        elsif($Target=~/\Aarm/i) {
20154            $Arch = "arm";
20155        }
20156    }
20157
20158    if(not $Arch)
20159    {
20160        writeFile("$TMP_DIR/test.c", "int main(){return 0;}\n");
20161
20162        my $Cmd = $GCC_PATH." test.c -o test";
20163        if(my $Opts = getGCC_Opts($LibVersion))
20164        { # user-defined options
20165            $Cmd .= " ".$Opts;
20166        }
20167
20168        chdir($TMP_DIR);
20169        system($Cmd);
20170        chdir($ORIG_DIR);
20171
20172        my $EX = join_P($TMP_DIR, "test");
20173
20174        if($OSgroup eq "windows") {
20175            $EX = join_P($TMP_DIR, "test.exe");
20176        }
20177
20178        $Arch = getArch_Object($EX);
20179
20180        unlink("$TMP_DIR/test.c");
20181        unlink($EX);
20182    }
20183
20184    if(not $Arch) {
20185        exitStatus("Error", "can't check ARCH type");
20186    }
20187
20188    return ($Cache{"getArch_GCC"}{$LibVersion} = $Arch);
20189}
20190
20191sub detectWordSize($)
20192{
20193    my $LibVersion = $_[0];
20194
20195    my $Size = undef;
20196
20197    # speed up detection
20198    if(my $Arch = getArch($LibVersion))
20199    {
20200        if($Arch=~/\A(x86_64|s390x|ppc64|ia64|alpha)\Z/) {
20201            $Size = "8";
20202        }
20203        elsif($Arch=~/\A(x86|s390|ppc32)\Z/) {
20204            $Size = "4";
20205        }
20206    }
20207
20208    if($GCC_PATH)
20209    {
20210        writeFile("$TMP_DIR/empty.h", "");
20211
20212        my $Cmd = $GCC_PATH." -E -dD empty.h";
20213        if(my $Opts = getGCC_Opts($LibVersion))
20214        { # user-defined options
20215            $Cmd .= " ".$Opts;
20216        }
20217
20218        chdir($TMP_DIR);
20219        my $Defines = `$Cmd`;
20220        chdir($ORIG_DIR);
20221
20222        unlink("$TMP_DIR/empty.h");
20223
20224        if($Defines=~/ __SIZEOF_POINTER__\s+(\d+)/)
20225        { # GCC 4
20226            $Size = $1;
20227        }
20228        elsif($Defines=~/ __PTRDIFF_TYPE__\s+(\w+)/)
20229        { # GCC 3
20230            my $PTRDIFF = $1;
20231            if($PTRDIFF=~/long/) {
20232                $Size = "8";
20233            }
20234            else {
20235                $Size = "4";
20236            }
20237        }
20238    }
20239
20240    if(not $Size) {
20241        exitStatus("Error", "can't check WORD size");
20242    }
20243
20244    return $Size;
20245}
20246
20247sub getWordSize($)
20248{ # to use in module
20249    return $WORD_SIZE{$_[0]};
20250}
20251
20252sub majorVersion($)
20253{
20254    my $V = $_[0];
20255    return 0 if(not $V);
20256    my @VParts = split(/\./, $V);
20257    return $VParts[0];
20258}
20259
20260sub cmpVersions($$)
20261{ # compare two versions in dotted-numeric format
20262    my ($V1, $V2) = @_;
20263    return 0 if($V1 eq $V2);
20264    my @V1Parts = split(/\./, $V1);
20265    my @V2Parts = split(/\./, $V2);
20266    for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++)
20267    {
20268        return -1 if(int($V1Parts[$i]) < int($V2Parts[$i]));
20269        return 1 if(int($V1Parts[$i]) > int($V2Parts[$i]));
20270    }
20271    return -1 if($#V1Parts < $#V2Parts);
20272    return 1 if($#V1Parts > $#V2Parts);
20273    return 0;
20274}
20275
20276sub read_ABI_Dump($$)
20277{
20278    my ($LibVersion, $Path) = @_;
20279    return if(not $LibVersion or not -e $Path);
20280    my $FilePath = "";
20281    if(isDump_U($Path))
20282    { # input *.abi
20283        $FilePath = $Path;
20284    }
20285    else
20286    { # input *.abi.tar.gz
20287        $FilePath = unpackDump($Path);
20288        if(not isDump_U($FilePath)) {
20289            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
20290        }
20291    }
20292
20293    my $ABI = {};
20294
20295    my $Line = readLineNum($FilePath, 0);
20296    if($Line=~/xml/)
20297    { # XML format
20298        loadModule("XmlDump");
20299        $ABI = readXmlDump($FilePath);
20300    }
20301    else
20302    { # Perl Data::Dumper format (default)
20303        open(DUMP, $FilePath);
20304        local $/ = undef;
20305        my $Content = <DUMP>;
20306        close(DUMP);
20307
20308        if(get_dirname($FilePath) eq $TMP_DIR."/unpack")
20309        { # remove temp file
20310            unlink($FilePath);
20311        }
20312        if($Content!~/};\s*\Z/) {
20313            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
20314        }
20315        $ABI = eval($Content);
20316        if(not $ABI) {
20317            exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
20318        }
20319    }
20320    # new dumps (>=1.22) have a personal versioning
20321    my $DVersion = $ABI->{"ABI_DUMP_VERSION"};
20322    my $ToolVersion = $ABI->{"ABI_COMPLIANCE_CHECKER_VERSION"};
20323    if(not $DVersion)
20324    { # old dumps (<=1.21.6) have been marked by the tool version
20325        $DVersion = $ToolVersion;
20326    }
20327    $UsedDump{$LibVersion}{"V"} = $DVersion;
20328    $UsedDump{$LibVersion}{"M"} = $ABI->{"LibraryName"};
20329
20330    if($ABI->{"PublicABI"}) {
20331        $UsedDump{$LibVersion}{"Public"} = 1;
20332    }
20333
20334    if($ABI->{"ABI_DUMP_VERSION"})
20335    {
20336        if(cmpVersions($DVersion, $ABI_DUMP_VERSION)>0)
20337        { # Don't know how to parse future dump formats
20338            exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $ABI_DUMP_VERSION)");
20339        }
20340    }
20341    else
20342    { # support for old ABI dumps
20343        if(cmpVersions($DVersion, $TOOL_VERSION)>0)
20344        { # Don't know how to parse future dump formats
20345            exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $TOOL_VERSION)");
20346        }
20347    }
20348
20349    if(majorVersion($DVersion)<2)
20350    {
20351        exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (allowed only 2.0<=V<=$ABI_DUMP_VERSION)");
20352    }
20353
20354    if(defined $ABI->{"ABI_DUMPER_VERSION"})
20355    { # DWARF ABI Dump
20356        $UseConv_Real{$LibVersion}{"P"} = 1;
20357        $UseConv_Real{$LibVersion}{"R"} = 0; # not implemented yet
20358
20359        $UsedDump{$LibVersion}{"DWARF"} = 1;
20360
20361        if(not $TargetComponent_Opt)
20362        {
20363            if($ABI->{"LibraryName"}=~/\.ko[\.\d]*\Z/) {
20364                $TargetComponent = "module";
20365            }
20366            else {
20367                $TargetComponent = "object";
20368            }
20369        }
20370    }
20371
20372    if(not checkDump($LibVersion, "2.11"))
20373    { # old ABI dumps
20374        $UsedDump{$LibVersion}{"BinOnly"} = 1;
20375    }
20376    elsif($ABI->{"BinOnly"})
20377    { # ABI dump created with --binary option
20378        $UsedDump{$LibVersion}{"BinOnly"} = 1;
20379    }
20380    else
20381    { # default
20382        $UsedDump{$LibVersion}{"SrcBin"} = 1;
20383    }
20384
20385    if(defined $ABI->{"Mode"}
20386    and $ABI->{"Mode"} eq "Extended")
20387    { # --ext option
20388        $ExtendedCheck = 1;
20389    }
20390    if($ABI->{"Extra"}) {
20391        $ExtraDump = 1;
20392    }
20393
20394    if(my $Lang = $ABI->{"Language"})
20395    {
20396        $UsedDump{$LibVersion}{"L"} = $Lang;
20397        setLanguage($LibVersion, $Lang);
20398    }
20399    if(checkDump($LibVersion, "2.15")) {
20400        $TypeInfo{$LibVersion} = $ABI->{"TypeInfo"};
20401    }
20402    else
20403    { # support for old ABI dumps
20404        my $TInfo = $ABI->{"TypeInfo"};
20405        if(not $TInfo)
20406        { # support for older ABI dumps
20407            $TInfo = $ABI->{"TypeDescr"};
20408        }
20409        my %Tid_TDid = ();
20410        foreach my $TDid (keys(%{$TInfo}))
20411        {
20412            foreach my $Tid (keys(%{$TInfo->{$TDid}}))
20413            {
20414                $MAX_ID = $Tid if($Tid>$MAX_ID);
20415                $MAX_ID = $TDid if($TDid and $TDid>$MAX_ID);
20416                $Tid_TDid{$Tid}{$TDid} = 1;
20417            }
20418        }
20419        my %NewID = ();
20420        foreach my $Tid (keys(%Tid_TDid))
20421        {
20422            my @TDids = keys(%{$Tid_TDid{$Tid}});
20423            if($#TDids>=1)
20424            {
20425                foreach my $TDid (@TDids)
20426                {
20427                    if($TDid) {
20428                        %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}};
20429                    }
20430                    else
20431                    {
20432                        my $ID = ++$MAX_ID;
20433
20434                        $NewID{$TDid}{$Tid} = $ID;
20435                        %{$TypeInfo{$LibVersion}{$ID}} = %{$TInfo->{$TDid}{$Tid}};
20436                        $TypeInfo{$LibVersion}{$ID}{"Tid"} = $ID;
20437                    }
20438                }
20439            }
20440            else
20441            {
20442                my $TDid = $TDids[0];
20443                %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}};
20444            }
20445        }
20446        foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
20447        {
20448            my %Info = %{$TypeInfo{$LibVersion}{$Tid}};
20449            if(defined $Info{"BaseType"})
20450            {
20451                my $Bid = $Info{"BaseType"}{"Tid"};
20452                my $BDid = $Info{"BaseType"}{"TDid"};
20453                $BDid="" if(not defined $BDid);
20454                delete($TypeInfo{$LibVersion}{$Tid}{"BaseType"}{"TDid"});
20455                if(defined $NewID{$BDid} and my $ID = $NewID{$BDid}{$Bid}) {
20456                    $TypeInfo{$LibVersion}{$Tid}{"BaseType"} = $ID;
20457                }
20458            }
20459            delete($TypeInfo{$LibVersion}{$Tid}{"TDid"});
20460        }
20461    }
20462    read_Machine_DumpInfo($ABI, $LibVersion);
20463    $SymbolInfo{$LibVersion} = $ABI->{"SymbolInfo"};
20464    if(not $SymbolInfo{$LibVersion})
20465    { # support for old dumps
20466        $SymbolInfo{$LibVersion} = $ABI->{"FuncDescr"};
20467    }
20468    if(not keys(%{$SymbolInfo{$LibVersion}}))
20469    { # validation of old-version dumps
20470        if(not $ExtendedCheck) {
20471            exitStatus("Invalid_Dump", "the input dump d$LibVersion is invalid");
20472        }
20473    }
20474    if(checkDump($LibVersion, "2.15")) {
20475        $DepLibrary_Symbol{$LibVersion} = $ABI->{"DepSymbols"};
20476    }
20477    else
20478    { # support for old ABI dumps
20479        my $DepSymbols = $ABI->{"DepSymbols"};
20480        if(not $DepSymbols) {
20481            $DepSymbols = $ABI->{"DepInterfaces"};
20482        }
20483        if(not $DepSymbols)
20484        { # Cannot reconstruct DepSymbols. This may result in false
20485          # positives if the old dump is for library 2. Not a problem if
20486          # old dumps are only from old libraries.
20487            $DepSymbols = {};
20488        }
20489        foreach my $Symbol (keys(%{$DepSymbols})) {
20490            $DepSymbol_Library{$LibVersion}{$Symbol} = 1;
20491        }
20492    }
20493    $SymVer{$LibVersion} = $ABI->{"SymbolVersion"};
20494
20495    if(my $V = $TargetVersion{$LibVersion}) {
20496        $Descriptor{$LibVersion}{"Version"} = $V;
20497    }
20498    else {
20499        $Descriptor{$LibVersion}{"Version"} = $ABI->{"LibraryVersion"};
20500    }
20501
20502    if(not keys(%{$SkipTypes{$LibVersion}}))
20503    { # if not defined by -skip-types option
20504        if(defined $ABI->{"SkipTypes"})
20505        {
20506            foreach my $TName (keys(%{$ABI->{"SkipTypes"}}))
20507            {
20508                $SkipTypes{$LibVersion}{$TName} = 1;
20509            }
20510        }
20511        if(defined $ABI->{"OpaqueTypes"})
20512        { # support for old dumps
20513            foreach my $TName (keys(%{$ABI->{"OpaqueTypes"}}))
20514            {
20515                $SkipTypes{$LibVersion}{$TName} = 1;
20516            }
20517        }
20518    }
20519
20520    if(not keys(%{$SkipSymbols{$LibVersion}}))
20521    { # if not defined by -skip-symbols option
20522        if(defined $ABI->{"SkipSymbols"}) {
20523            $SkipSymbols{$LibVersion} = $ABI->{"SkipSymbols"};
20524        }
20525        if(defined $ABI->{"SkipInterfaces"})
20526        { # support for old dumps
20527            $SkipSymbols{$LibVersion} = $ABI->{"SkipInterfaces"};
20528        }
20529        if(defined $ABI->{"InternalInterfaces"})
20530        { # support for old dumps
20531            $SkipSymbols{$LibVersion} = $ABI->{"InternalInterfaces"};
20532        }
20533    }
20534    $SkipNameSpaces{$LibVersion} = $ABI->{"SkipNameSpaces"};
20535
20536    if(not $TargetHeaders{$LibVersion})
20537    { # if not defined by -headers-list option
20538        $TargetHeaders{$LibVersion} = $ABI->{"TargetHeaders"};
20539    }
20540
20541    foreach my $Path (keys(%{$ABI->{"SkipHeaders"}}))
20542    {
20543        $SkipHeadersList{$LibVersion}{$Path} = $ABI->{"SkipHeaders"}{$Path};
20544
20545        my ($CPath, $Type) = classifyPath($Path);
20546        $SkipHeaders{$LibVersion}{$Type}{$CPath} = $ABI->{"SkipHeaders"}{$Path};
20547    }
20548
20549    read_Source_DumpInfo($ABI, $LibVersion);
20550    read_Libs_DumpInfo($ABI, $LibVersion);
20551
20552    if(not checkDump($LibVersion, "2.10.1")
20553    or not $TargetHeaders{$LibVersion})
20554    { # support for old ABI dumps: added target headers
20555        foreach (keys(%{$Registered_Headers{$LibVersion}})) {
20556            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20557        }
20558
20559        if(not $ABI->{"PublicABI"})
20560        {
20561            foreach (keys(%{$Registered_Sources{$LibVersion}})) {
20562                $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20563            }
20564        }
20565    }
20566    $Constants{$LibVersion} = $ABI->{"Constants"};
20567    if(defined $ABI->{"GccConstants"})
20568    { # 3.0
20569        foreach my $Name (keys(%{$ABI->{"GccConstants"}})) {
20570            $Constants{$LibVersion}{$Name}{"Value"} = $ABI->{"GccConstants"}{$Name};
20571        }
20572    }
20573
20574    $NestedNameSpaces{$LibVersion} = $ABI->{"NameSpaces"};
20575    if(not $NestedNameSpaces{$LibVersion})
20576    { # support for old dumps
20577      # Cannot reconstruct NameSpaces. This may affect design
20578      # of the compatibility report.
20579        $NestedNameSpaces{$LibVersion} = {};
20580    }
20581    # target system type
20582    # needed to adopt HTML report
20583    if(not $DumpSystem)
20584    { # to use in createSymbolsList(...)
20585        $OStarget = $ABI->{"Target"};
20586    }
20587    # recreate environment
20588    foreach my $Lib_Name (keys(%{$Library_Symbol{$LibVersion}}))
20589    {
20590        foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
20591        {
20592            $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
20593            if($Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol}<=-1)
20594            { # data marked as -size in the dump
20595                $GlobalDataObject{$LibVersion}{$Symbol} = -$Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol};
20596            }
20597            if($COMMON_LANGUAGE{$LibVersion} ne "C++")
20598            {
20599                if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
20600                    setLanguage($LibVersion, "C++");
20601                }
20602            }
20603        }
20604    }
20605    foreach my $Lib_Name (keys(%{$DepLibrary_Symbol{$LibVersion}}))
20606    {
20607        foreach my $Symbol (keys(%{$DepLibrary_Symbol{$LibVersion}{$Lib_Name}})) {
20608            $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
20609        }
20610    }
20611
20612    my @VFunc = ();
20613    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20614    {
20615        if(my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
20616        {
20617            if(not $Symbol_Library{$LibVersion}{$MnglName}
20618            and not $DepSymbol_Library{$LibVersion}{$MnglName}) {
20619                push(@VFunc, $MnglName);
20620            }
20621        }
20622    }
20623    translateSymbols(@VFunc, $LibVersion);
20624    translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
20625    translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion);
20626
20627    if(not checkDump($LibVersion, "3.0"))
20628    { # support for old ABI dumps
20629        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20630        {
20631            if(my $BaseType = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"})
20632            {
20633                if(ref($BaseType) eq "HASH") {
20634                    $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"}{"Tid"};
20635                }
20636            }
20637        }
20638    }
20639
20640    if(not checkDump($LibVersion, "3.2"))
20641    { # support for old ABI dumps
20642        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20643        {
20644            if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"})
20645            {
20646                foreach my $Offset (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"VTable"}})) {
20647                    $TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset} = simplifyVTable($TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset});
20648                }
20649            }
20650        }
20651
20652        # repair target headers list
20653        delete($TargetHeaders{$LibVersion});
20654        foreach (keys(%{$Registered_Headers{$LibVersion}})) {
20655            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20656        }
20657        foreach (keys(%{$Registered_Sources{$LibVersion}})) {
20658            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20659        }
20660
20661        # non-target constants from anon enums
20662        foreach my $Name (keys(%{$Constants{$LibVersion}}))
20663        {
20664            if(not $ExtraDump
20665            and not is_target_header($Constants{$LibVersion}{$Name}{"Header"}, $LibVersion))
20666            {
20667                delete($Constants{$LibVersion}{$Name});
20668            }
20669        }
20670    }
20671
20672    if(not checkDump($LibVersion, "2.20"))
20673    { # support for old ABI dumps
20674        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20675        {
20676            my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"};
20677
20678            if($TType=~/Struct|Union|Enum|Typedef/)
20679            { # repair complex types first
20680                next;
20681            }
20682
20683            if(my $BaseId = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"})
20684            {
20685                my $BType = lc($TypeInfo{$LibVersion}{$BaseId}{"Type"});
20686                if($BType=~/Struct|Union|Enum/i)
20687                {
20688                    my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"};
20689                    $TypeInfo{$LibVersion}{$TypeId}{"Name"}=~s/\A\Q$BName\E\b/$BType $BName/g;
20690                }
20691            }
20692        }
20693        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20694        {
20695            my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"};
20696            my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
20697            if($TType=~/Struct|Union|Enum/) {
20698                $TypeInfo{$LibVersion}{$TypeId}{"Name"} = lc($TType)." ".$TName;
20699            }
20700        }
20701    }
20702
20703    foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20704    { # NOTE: order is important
20705        if(defined $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"})
20706        { # support for old ABI dumps < 2.0 (ACC 1.22)
20707            foreach my $BId (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}}))
20708            {
20709                if(my $Access = $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}{$BId})
20710                {
20711                    if($Access ne "public") {
20712                        $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId}{"access"} = $Access;
20713                    }
20714                }
20715                $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId} = {};
20716            }
20717            delete($TypeInfo{$LibVersion}{$TypeId}{"BaseClass"});
20718        }
20719        if(my $Header = $TypeInfo{$LibVersion}{$TypeId}{"Header"})
20720        { # support for old ABI dumps
20721            $TypeInfo{$LibVersion}{$TypeId}{"Header"} = path_format($Header, $OSgroup);
20722        }
20723        elsif(my $Source = $TypeInfo{$LibVersion}{$TypeId}{"Source"})
20724        { # DWARF ABI Dumps
20725            $TypeInfo{$LibVersion}{$TypeId}{"Header"} = $Source;
20726        }
20727        if(not defined $TypeInfo{$LibVersion}{$TypeId}{"Tid"}) {
20728            $TypeInfo{$LibVersion}{$TypeId}{"Tid"} = $TypeId;
20729        }
20730
20731        # support for old formatting of type names
20732        $TypeInfo{$LibVersion}{$TypeId}{"Name"} = formatName($TypeInfo{$LibVersion}{$TypeId}{"Name"}, "T");
20733
20734        my %TInfo = %{$TypeInfo{$LibVersion}{$TypeId}};
20735        if(defined $TInfo{"Base"})
20736        {
20737            foreach my $SubId (keys(%{$TInfo{"Base"}}))
20738            {
20739                if($SubId eq $TypeId)
20740                { # Fix erroneus ABI dump
20741                    delete($TypeInfo{$LibVersion}{$TypeId}{"Base"}{$SubId});
20742                    next;
20743                }
20744
20745                $Class_SubClasses{$LibVersion}{$SubId}{$TypeId} = 1;
20746            }
20747        }
20748        if($TInfo{"Type"} eq "MethodPtr")
20749        {
20750            if(defined $TInfo{"Param"})
20751            { # support for old ABI dumps <= 1.17
20752                if(not defined $TInfo{"Param"}{"0"})
20753                {
20754                    my $Max = keys(%{$TInfo{"Param"}});
20755                    foreach my $Pos (1 .. $Max) {
20756                        $TInfo{"Param"}{$Pos-1} = $TInfo{"Param"}{$Pos};
20757                    }
20758                    delete($TInfo{"Param"}{$Max});
20759                    %{$TypeInfo{$LibVersion}{$TypeId}} = %TInfo;
20760                }
20761            }
20762        }
20763        if($TInfo{"BaseType"} eq $TypeId)
20764        { # fix ABI dump
20765            delete($TypeInfo{$LibVersion}{$TypeId}{"BaseType"});
20766        }
20767
20768        if($TInfo{"Type"} eq "Typedef" and not $TInfo{"Artificial"})
20769        {
20770            if(my $BTid = $TInfo{"BaseType"})
20771            {
20772                my $BName = $TypeInfo{$LibVersion}{$BTid}{"Name"};
20773                if(not $BName)
20774                { # broken type
20775                    next;
20776                }
20777                if($TInfo{"Name"} eq $BName)
20778                { # typedef to "class Class"
20779                  # should not be registered in TName_Tid
20780                    next;
20781                }
20782                if(not $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}}) {
20783                    $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}} = $BName;
20784                }
20785            }
20786        }
20787        if(not $TName_Tid{$LibVersion}{$TInfo{"Name"}})
20788        { # classes: class (id1), typedef (artificial, id2 > id1)
20789            $TName_Tid{$LibVersion}{$TInfo{"Name"}} = $TypeId;
20790        }
20791    }
20792
20793    if(not checkDump($LibVersion, "2.15"))
20794    { # support for old ABI dumps
20795        my %Dups = ();
20796        foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20797        {
20798            if(my $ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
20799            {
20800                if(not defined $TypeInfo{$LibVersion}{$ClassId})
20801                { # remove template decls
20802                    delete($SymbolInfo{$LibVersion}{$InfoId});
20803                    next;
20804                }
20805            }
20806            my $MName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
20807            if(not $MName and $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
20808            { # templates
20809                delete($SymbolInfo{$LibVersion}{$InfoId});
20810            }
20811        }
20812    }
20813
20814    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20815    {
20816        if(my $Class = $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
20817        and not $SymbolInfo{$LibVersion}{$InfoId}{"Static"}
20818        and not $SymbolInfo{$LibVersion}{$InfoId}{"Data"})
20819        { # support for old ABI dumps (< 3.1)
20820            if(not defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
20821            or $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this")
20822            { # add "this" first parameter
20823                my $ThisTid = getTypeIdByName($TypeInfo{$LibVersion}{$Class}{"Name"}."*const", $LibVersion);
20824                my %PInfo = ("name"=>"this", "type"=>"$ThisTid");
20825
20826                if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
20827                {
20828                    my @Pos = sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}});
20829                    foreach my $Pos (reverse(0 .. $#Pos)) {
20830                        %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos+1}} = %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos}};
20831                    }
20832                }
20833                $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{"0"} = \%PInfo;
20834            }
20835        }
20836
20837        if(not $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
20838        { # ABI dumps have no mangled names for C-functions
20839            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
20840        }
20841        if(my $Header = $SymbolInfo{$LibVersion}{$InfoId}{"Header"})
20842        { # support for old ABI dumps
20843            $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = path_format($Header, $OSgroup);
20844        }
20845        elsif(my $Source = $SymbolInfo{$LibVersion}{$InfoId}{"Source"})
20846        { # DWARF ABI Dumps
20847            $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = $Source;
20848        }
20849    }
20850
20851    $Descriptor{$LibVersion}{"Dump"} = 1;
20852}
20853
20854sub read_Machine_DumpInfo($$)
20855{
20856    my ($ABI, $LibVersion) = @_;
20857    if($ABI->{"Arch"}) {
20858        $CPU_ARCH{$LibVersion} = $ABI->{"Arch"};
20859    }
20860    if($ABI->{"WordSize"}) {
20861        $WORD_SIZE{$LibVersion} = $ABI->{"WordSize"};
20862    }
20863    else
20864    { # support for old dumps
20865        $WORD_SIZE{$LibVersion} = $ABI->{"SizeOfPointer"};
20866    }
20867    if(not $WORD_SIZE{$LibVersion})
20868    { # support for old dumps (<1.23)
20869        if(my $Tid = getTypeIdByName("char*", $LibVersion))
20870        { # size of char*
20871            $WORD_SIZE{$LibVersion} = $TypeInfo{$LibVersion}{$Tid}{"Size"};
20872        }
20873        else
20874        {
20875            my $PSize = 0;
20876            foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
20877            {
20878                if($TypeInfo{$LibVersion}{$Tid}{"Type"} eq "Pointer")
20879                { # any "pointer"-type
20880                    $PSize = $TypeInfo{$LibVersion}{$Tid}{"Size"};
20881                    last;
20882                }
20883            }
20884            if($PSize)
20885            { # a pointer type size
20886                $WORD_SIZE{$LibVersion} = $PSize;
20887            }
20888            else {
20889                printMsg("WARNING", "cannot identify a WORD size in the ABI dump (too old format)");
20890            }
20891        }
20892    }
20893    if($ABI->{"GccVersion"}) {
20894        $GCC_VERSION{$LibVersion} = $ABI->{"GccVersion"};
20895    }
20896    elsif($ABI->{"ClangVersion"}) {
20897        $CLANG_VERSION{$LibVersion} = $ABI->{"ClangVersion"};
20898    }
20899}
20900
20901sub read_Libs_DumpInfo($$)
20902{
20903    my ($ABI, $LibVersion) = @_;
20904    $Library_Symbol{$LibVersion} = $ABI->{"Symbols"};
20905    if(not $Library_Symbol{$LibVersion})
20906    { # support for old dumps
20907        $Library_Symbol{$LibVersion} = $ABI->{"Interfaces"};
20908    }
20909    if(keys(%{$Library_Symbol{$LibVersion}})
20910    and not $DumpAPI) {
20911        $Descriptor{$LibVersion}{"Libs"} = "OK";
20912    }
20913}
20914
20915sub read_Source_DumpInfo($$)
20916{
20917    my ($ABI, $LibVersion) = @_;
20918
20919    if(keys(%{$ABI->{"Headers"}})
20920    and not $DumpAPI) {
20921        $Descriptor{$LibVersion}{"Headers"} = "OK";
20922    }
20923    foreach my $Identity (sort {$ABI->{"Headers"}{$a}<=>$ABI->{"Headers"}{$b}} keys(%{$ABI->{"Headers"}}))
20924    {
20925        $Registered_Headers{$LibVersion}{$Identity}{"Identity"} = $Identity;
20926        $Registered_Headers{$LibVersion}{$Identity}{"Pos"} = $ABI->{"Headers"}{$Identity};
20927    }
20928
20929    if(keys(%{$ABI->{"Sources"}})
20930    and not $DumpAPI) {
20931        $Descriptor{$LibVersion}{"Sources"} = "OK";
20932    }
20933    foreach my $Name (sort {$ABI->{"Sources"}{$a}<=>$ABI->{"Sources"}{$b}} keys(%{$ABI->{"Sources"}}))
20934    {
20935        $Registered_Sources{$LibVersion}{$Name}{"Identity"} = $Name;
20936        $Registered_Sources{$LibVersion}{$Name}{"Pos"} = $ABI->{"Headers"}{$Name};
20937    }
20938}
20939
20940sub find_libs($$$)
20941{
20942    my ($Path, $Type, $MaxDepth) = @_;
20943    # FIXME: correct the search pattern
20944    return cmd_find($Path, $Type, '\.'.$LIB_EXT.'[0-9.]*\Z', $MaxDepth, 1);
20945}
20946
20947sub createDescriptor($$)
20948{
20949    my ($LibVersion, $Path) = @_;
20950    if(not $LibVersion or not $Path
20951    or not -e $Path) {
20952        return "";
20953    }
20954    if(-d $Path)
20955    { # directory with headers files and shared objects
20956        return "
20957            <version>
20958                ".$TargetVersion{$LibVersion}."
20959            </version>
20960
20961            <headers>
20962                $Path
20963            </headers>
20964
20965            <libs>
20966                $Path
20967            </libs>";
20968    }
20969    else
20970    { # files
20971        if($Path=~/\.(xml|desc)\Z/i)
20972        { # standard XML-descriptor
20973            return readFile($Path);
20974        }
20975        elsif(is_header($Path, 2, $LibVersion))
20976        { # header file
20977            $CheckHeadersOnly = 1;
20978
20979            if($LibVersion==1) {
20980                $TargetVersion{$LibVersion} = "X";
20981            }
20982
20983            if($LibVersion==2) {
20984                $TargetVersion{$LibVersion} = "Y";
20985            }
20986
20987            return "
20988                <version>
20989                    ".$TargetVersion{$LibVersion}."
20990                </version>
20991
20992                <headers>
20993                    $Path
20994                </headers>
20995
20996                <libs>
20997                    none
20998                </libs>";
20999        }
21000        else
21001        { # standard XML-descriptor
21002            return readFile($Path);
21003        }
21004    }
21005}
21006
21007sub detect_lib_default_paths()
21008{
21009    my %LPaths = ();
21010    if($OSgroup eq "bsd")
21011    {
21012        if(my $LdConfig = get_CmdPath("ldconfig"))
21013        {
21014            foreach my $Line (split(/\n/, `$LdConfig -r 2>\"$TMP_DIR/null\"`))
21015            {
21016                if($Line=~/\A[ \t]*\d+:\-l(.+) \=\> (.+)\Z/)
21017                {
21018                    my $Name = "lib".$1;
21019                    if(not defined $LPaths{$Name}) {
21020                        $LPaths{$Name} = $2;
21021                    }
21022                }
21023            }
21024        }
21025        else {
21026            printMsg("WARNING", "can't find ldconfig");
21027        }
21028    }
21029    else
21030    {
21031        if(my $LdConfig = get_CmdPath("ldconfig"))
21032        {
21033            if($SystemRoot and $OSgroup eq "linux")
21034            { # use host (x86) ldconfig with the target (arm) ld.so.conf
21035                if(-e $SystemRoot."/etc/ld.so.conf") {
21036                    $LdConfig .= " -f ".$SystemRoot."/etc/ld.so.conf";
21037                }
21038            }
21039            foreach my $Line (split(/\n/, `$LdConfig -p 2>\"$TMP_DIR/null\"`))
21040            {
21041                if($Line=~/\A[ \t]*([^ \t]+) .* \=\> (.+)\Z/)
21042                {
21043                    my ($Name, $Path) = ($1, $2);
21044                    $Path=~s/[\/]{2,}/\//;
21045                    if(not defined $LPaths{$Name})
21046                    { # get first element from the list of available paths
21047
21048                      # libstdc++.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
21049                      # libstdc++.so.6 (libc6) => /usr/lib/i386-linux-gnu/libstdc++.so.6
21050                      # libstdc++.so.6 (libc6) => /usr/lib32/libstdc++.so.6
21051
21052                        $LPaths{$Name} = $Path;
21053                    }
21054                }
21055            }
21056        }
21057        elsif($OSgroup eq "linux") {
21058            printMsg("WARNING", "can't find ldconfig");
21059        }
21060    }
21061    return \%LPaths;
21062}
21063
21064sub detect_bin_default_paths()
21065{
21066    my $EnvPaths = $ENV{"PATH"};
21067    if($OSgroup eq "beos") {
21068        $EnvPaths.=":".$ENV{"BETOOLS"};
21069    }
21070    my $Sep = ($OSgroup eq "windows")?";":":|;";
21071    foreach my $Path (split(/$Sep/, $EnvPaths))
21072    {
21073        $Path = path_format($Path, $OSgroup);
21074        next if(not $Path);
21075        if($SystemRoot
21076        and $Path=~/\A\Q$SystemRoot\E\//)
21077        { # do NOT use binaries from target system
21078            next;
21079        }
21080        push_U(\@DefaultBinPaths, $Path);
21081    }
21082}
21083
21084sub detect_inc_default_paths()
21085{
21086    my %DPaths = ("Cpp"=>[],"Gcc"=>[],"Inc"=>[]);
21087    writeFile("$TMP_DIR/empty.h", "");
21088    foreach my $Line (split(/\n/, `$GCC_PATH -v -x c++ -E \"$TMP_DIR/empty.h\" 2>&1`))
21089    { # detecting GCC default include paths
21090        next if(index($Line, "/cc1plus ")!=-1);
21091
21092        if($Line=~/\A[ \t]*((\/|\w+:\\).+)[ \t]*\Z/)
21093        {
21094            my $Path = realpath_F($1);
21095            if(index($Path, "c++")!=-1
21096            or index($Path, "/g++/")!=-1)
21097            {
21098                push_U($DPaths{"Cpp"}, $Path);
21099                if(not defined $MAIN_CPP_DIR
21100                or get_depth($MAIN_CPP_DIR)>get_depth($Path)) {
21101                    $MAIN_CPP_DIR = $Path;
21102                }
21103            }
21104            elsif(index($Path, "gcc")!=-1) {
21105                push_U($DPaths{"Gcc"}, $Path);
21106            }
21107            else
21108            {
21109                if($Path=~/local[\/\\]+include/)
21110                { # local paths
21111                    next;
21112                }
21113                if($SystemRoot
21114                and $Path!~/\A\Q$SystemRoot\E(\/|\Z)/)
21115                { # The GCC include path for user headers is not a part of the system root
21116                  # The reason: you are not specified the --cross-gcc option or selected a wrong compiler
21117                  # or it is the internal cross-GCC path like arm-linux-gnueabi/include
21118                    next;
21119                }
21120                push_U($DPaths{"Inc"}, $Path);
21121            }
21122        }
21123    }
21124    unlink("$TMP_DIR/empty.h");
21125    return %DPaths;
21126}
21127
21128sub detect_default_paths($)
21129{
21130    my ($HSearch, $LSearch, $BSearch, $GSearch) = (1, 1, 1, 1);
21131    my $Search = $_[0];
21132    if($Search!~/inc/) {
21133        $HSearch = 0;
21134    }
21135    if($Search!~/lib/) {
21136        $LSearch = 0;
21137    }
21138    if($Search!~/bin/) {
21139        $BSearch = 0;
21140    }
21141    if($Search!~/gcc/) {
21142        $GSearch = 0;
21143    }
21144    if(@{$SystemPaths{"include"}})
21145    { # <search_headers> section of the XML descriptor
21146      # do NOT search for systems headers
21147        $HSearch = 0;
21148    }
21149    if(@{$SystemPaths{"lib"}})
21150    { # <search_libs> section of the XML descriptor
21151      # do NOT search for systems libraries
21152        $LSearch = 0;
21153    }
21154    foreach my $Type (keys(%{$OS_AddPath{$OSgroup}}))
21155    { # additional search paths
21156        next if($Type eq "include" and not $HSearch);
21157        next if($Type eq "lib" and not $LSearch);
21158        next if($Type eq "bin" and not $BSearch);
21159        push_U($SystemPaths{$Type}, grep { -d $_ } @{$OS_AddPath{$OSgroup}{$Type}});
21160    }
21161    if($OSgroup ne "windows")
21162    { # unix-like
21163        foreach my $Type ("include", "lib", "bin")
21164        { # automatic detection of system "devel" directories
21165            next if($Type eq "include" and not $HSearch);
21166            next if($Type eq "lib" and not $LSearch);
21167            next if($Type eq "bin" and not $BSearch);
21168            my ($UsrDir, $RootDir) = ("/usr", "/");
21169            if($SystemRoot and $Type ne "bin")
21170            { # 1. search for target headers and libraries
21171              # 2. use host commands: ldconfig, readelf, etc.
21172                ($UsrDir, $RootDir) = ("$SystemRoot/usr", $SystemRoot);
21173            }
21174            push_U($SystemPaths{$Type}, cmd_find($RootDir,"d","*$Type*",1));
21175            if(-d $RootDir."/".$Type)
21176            { # if "/lib" is symbolic link
21177                if($RootDir eq "/") {
21178                    push_U($SystemPaths{$Type}, "/".$Type);
21179                }
21180                else {
21181                    push_U($SystemPaths{$Type}, $RootDir."/".$Type);
21182                }
21183            }
21184            if(-d $UsrDir)
21185            {
21186                push_U($SystemPaths{$Type}, cmd_find($UsrDir,"d","*$Type*",1));
21187                if(-d $UsrDir."/".$Type)
21188                { # if "/usr/lib" is symbolic link
21189                    push_U($SystemPaths{$Type}, $UsrDir."/".$Type);
21190                }
21191            }
21192        }
21193    }
21194    if($BSearch)
21195    {
21196        detect_bin_default_paths();
21197        push_U($SystemPaths{"bin"}, @DefaultBinPaths);
21198    }
21199    # check environment variables
21200    if($OSgroup eq "beos")
21201    {
21202        foreach (my @Paths = @{$SystemPaths{"bin"}})
21203        {
21204            if($_ eq ".") {
21205                next;
21206            }
21207            # search for /boot/develop/abi/x86/gcc4/tools/gcc-4.4.4-haiku-101111/bin/
21208            if(my @Dirs = sort cmd_find($_, "d", "bin")) {
21209                push_U($SystemPaths{"bin"}, sort {get_depth($a)<=>get_depth($b)} @Dirs);
21210            }
21211        }
21212        if($HSearch)
21213        {
21214            push_U(\@DefaultIncPaths, grep { is_abs($_) } (
21215                split(/:|;/, $ENV{"BEINCLUDES"})
21216                ));
21217        }
21218        if($LSearch)
21219        {
21220            push_U(\@DefaultLibPaths, grep { is_abs($_) } (
21221                split(/:|;/, $ENV{"BELIBRARIES"}),
21222                split(/:|;/, $ENV{"LIBRARY_PATH"})
21223                ));
21224        }
21225    }
21226    if($LSearch)
21227    { # using linker to get system paths
21228        if(my $LPaths = detect_lib_default_paths())
21229        { # unix-like
21230            my %Dirs = ();
21231            foreach my $Name (keys(%{$LPaths}))
21232            {
21233                if($SystemRoot
21234                and $LPaths->{$Name}!~/\A\Q$SystemRoot\E\//)
21235                { # wrong ldconfig configuration
21236                  # check your <sysroot>/etc/ld.so.conf
21237                    next;
21238                }
21239                $DyLib_DefaultPath{$Name} = $LPaths->{$Name};
21240                if(my $Dir = get_dirname($LPaths->{$Name})) {
21241                    $Dirs{$Dir} = 1;
21242                }
21243            }
21244            push_U(\@DefaultLibPaths, sort {get_depth($a)<=>get_depth($b)} sort keys(%Dirs));
21245        }
21246        push_U($SystemPaths{"lib"}, @DefaultLibPaths);
21247    }
21248    if($BSearch)
21249    {
21250        if($CrossGcc)
21251        { # --cross-gcc=arm-linux-gcc
21252            if(-e $CrossGcc)
21253            { # absolute or relative path
21254                $GCC_PATH = get_abs_path($CrossGcc);
21255            }
21256            elsif($CrossGcc!~/\// and get_CmdPath($CrossGcc))
21257            { # command name
21258                $GCC_PATH = $CrossGcc;
21259            }
21260            else {
21261                exitStatus("Access_Error", "can't access \'$CrossGcc\'");
21262            }
21263            if($GCC_PATH=~/\s/) {
21264                $GCC_PATH = "\"".$GCC_PATH."\"";
21265            }
21266        }
21267    }
21268    if($GSearch)
21269    { # GCC path and default include dirs
21270        if(not $CrossGcc)
21271        { # try default gcc
21272            $GCC_PATH = get_CmdPath("gcc");
21273        }
21274        if(not $GCC_PATH)
21275        { # try to find gcc-X.Y
21276            foreach my $Path (@{$SystemPaths{"bin"}})
21277            {
21278                if(my @GCCs = cmd_find($Path, "", '/gcc-[0-9.]*\Z', 1, 1))
21279                { # select the latest version
21280                    @GCCs = sort {$b cmp $a} @GCCs;
21281                    if(check_gcc($GCCs[0], "3"))
21282                    {
21283                        $GCC_PATH = $GCCs[0];
21284                        last;
21285                    }
21286                }
21287            }
21288        }
21289        if(not $GCC_PATH) {
21290            exitStatus("Not_Found", "can't find GCC>=3.0 in PATH");
21291        }
21292
21293        my $GCC_Ver = get_dumpversion($GCC_PATH);
21294        if($GCC_Ver eq "4.8")
21295        { # on Ubuntu -dumpversion returns 4.8 for gcc 4.8.4
21296            my $Info = `$GCC_PATH --version`;
21297
21298            if($Info=~/gcc\s+(|\([^()]+\)\s+)(\d+\.\d+\.\d+)/)
21299            { # gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
21300              # gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6)
21301                $GCC_Ver = $2;
21302            }
21303        }
21304
21305        if($OStarget=~/macos/)
21306        {
21307            my $Info = `$GCC_PATH --version`;
21308
21309            if($Info=~/clang/i) {
21310                printMsg("WARNING", "doesn't work with clang, please install GCC instead (and select it by -gcc-path option)");
21311            }
21312        }
21313
21314        if($GCC_Ver)
21315        {
21316            my $GccTarget = get_dumpmachine($GCC_PATH);
21317
21318            if($GccTarget=~/linux/)
21319            {
21320                $OStarget = "linux";
21321                $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget};
21322            }
21323            elsif($GccTarget=~/symbian/)
21324            {
21325                $OStarget = "symbian";
21326                $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget};
21327            }
21328
21329            printMsg("INFO", "Using GCC $GCC_Ver ($GccTarget, target: ".getArch_GCC(1).")");
21330
21331            # check GCC version
21332            if($GCC_Ver=~/\A4\.8(|\.[012])|6\.[12]\.0\Z/)
21333            { # GCC 4.8.[0-2]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57850
21334              # GCC 6.[1-2].0: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78040
21335
21336                printMsg("ERROR", "Not working properly with GCC $GCC_Ver. Please use other GCC version with the help of --gcc-path=PATH option. Not supported GCC versions: 4.8.0, 4.8.1, 4.8.2, 6.1.0, 6.2.0");
21337
21338                $GCC_MISSED_MNGL = 1;
21339            }
21340        }
21341        else {
21342            exitStatus("Error", "something is going wrong with the GCC compiler");
21343        }
21344    }
21345    if($HSearch)
21346    {
21347        # GCC standard paths
21348        if($GCC_PATH and not $NoStdInc)
21349        {
21350            my %DPaths = detect_inc_default_paths();
21351            @DefaultCppPaths = @{$DPaths{"Cpp"}};
21352            @DefaultGccPaths = @{$DPaths{"Gcc"}};
21353            @DefaultIncPaths = @{$DPaths{"Inc"}};
21354            push_U($SystemPaths{"include"}, @DefaultIncPaths);
21355        }
21356
21357        # users include paths
21358        my $IncPath = "/usr/include";
21359        if($SystemRoot) {
21360            $IncPath = $SystemRoot.$IncPath;
21361        }
21362        if(-d $IncPath) {
21363            push_U(\@UsersIncPath, $IncPath);
21364        }
21365    }
21366
21367    if($ExtraInfo)
21368    {
21369        writeFile($ExtraInfo."/default-libs", join("\n", @DefaultLibPaths));
21370        writeFile($ExtraInfo."/default-includes", join("\n", (@DefaultCppPaths, @DefaultGccPaths, @DefaultIncPaths)));
21371    }
21372}
21373
21374sub getLIB_EXT($)
21375{
21376    my $Target = $_[0];
21377    if(my $Ext = $OS_LibExt{$LIB_TYPE}{$Target}) {
21378        return $Ext;
21379    }
21380    return $OS_LibExt{$LIB_TYPE}{"default"};
21381}
21382
21383sub getAR_EXT($)
21384{
21385    my $Target = $_[0];
21386    if(my $Ext = $OS_Archive{$Target}) {
21387        return $Ext;
21388    }
21389    return $OS_Archive{"default"};
21390}
21391
21392sub get_dumpversion($)
21393{
21394    my $Cmd = $_[0];
21395    return "" if(not $Cmd);
21396    if($Cache{"get_dumpversion"}{$Cmd}) {
21397        return $Cache{"get_dumpversion"}{$Cmd};
21398    }
21399    my $V = `$Cmd -dumpversion 2>\"$TMP_DIR/null\"`;
21400    chomp($V);
21401    return ($Cache{"get_dumpversion"}{$Cmd} = $V);
21402}
21403
21404sub get_dumpmachine($)
21405{
21406    my $Cmd = $_[0];
21407    return "" if(not $Cmd);
21408    if($Cache{"get_dumpmachine"}{$Cmd}) {
21409        return $Cache{"get_dumpmachine"}{$Cmd};
21410    }
21411    my $Machine = `$Cmd -dumpmachine 2>\"$TMP_DIR/null\"`;
21412    chomp($Machine);
21413    return ($Cache{"get_dumpmachine"}{$Cmd} = $Machine);
21414}
21415
21416sub checkCmd($)
21417{
21418    my $Cmd = $_[0];
21419    return "" if(not $Cmd);
21420    my @Options = (
21421        "--version",
21422        "-help"
21423    );
21424    foreach my $Opt (@Options)
21425    {
21426        my $Info = `$Cmd $Opt 2>\"$TMP_DIR/null\"`;
21427        if($Info) {
21428            return 1;
21429        }
21430    }
21431    return 0;
21432}
21433
21434sub check_gcc($$)
21435{
21436    my ($Cmd, $ReqVer) = @_;
21437    return 0 if(not $Cmd or not $ReqVer);
21438    if(defined $Cache{"check_gcc"}{$Cmd}{$ReqVer}) {
21439        return $Cache{"check_gcc"}{$Cmd}{$ReqVer};
21440    }
21441    if(my $GccVer = get_dumpversion($Cmd))
21442    {
21443        $GccVer=~s/(-|_)[a-z_]+.*\Z//; # remove suffix (like "-haiku-100818")
21444        if(cmpVersions($GccVer, $ReqVer)>=0) {
21445            return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = $Cmd);
21446        }
21447    }
21448    return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = "");
21449}
21450
21451sub get_depth($)
21452{
21453    if(defined $Cache{"get_depth"}{$_[0]}) {
21454        return $Cache{"get_depth"}{$_[0]};
21455    }
21456    return ($Cache{"get_depth"}{$_[0]} = ($_[0]=~tr![\/\\]|\:\:!!));
21457}
21458
21459sub registerGccHeaders()
21460{
21461    return if($Cache{"registerGccHeaders"}); # this function should be called once
21462
21463    foreach my $Path (@DefaultGccPaths)
21464    {
21465        my @Headers = cmd_find($Path,"f");
21466        @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers;
21467        foreach my $HPath (@Headers)
21468        {
21469            my $FileName = get_filename($HPath);
21470            if(not defined $DefaultGccHeader{$FileName})
21471            { # skip duplicated
21472                $DefaultGccHeader{$FileName} = $HPath;
21473            }
21474        }
21475    }
21476    $Cache{"registerGccHeaders"} = 1;
21477}
21478
21479sub registerCppHeaders()
21480{
21481    return if($Cache{"registerCppHeaders"}); # this function should be called once
21482
21483    foreach my $CppDir (@DefaultCppPaths)
21484    {
21485        my @Headers = cmd_find($CppDir,"f");
21486        @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers;
21487        foreach my $Path (@Headers)
21488        {
21489            my $FileName = get_filename($Path);
21490            if(not defined $DefaultCppHeader{$FileName})
21491            { # skip duplicated
21492                $DefaultCppHeader{$FileName} = $Path;
21493            }
21494        }
21495    }
21496    $Cache{"registerCppHeaders"} = 1;
21497}
21498
21499sub parse_libname($$$)
21500{
21501    return "" if(not $_[0]);
21502    if(defined $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]}) {
21503        return $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]};
21504    }
21505    return ($Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]} = parse_libname_I(@_));
21506}
21507
21508sub parse_libname_I($$$)
21509{
21510    my ($Name, $Type, $Target) = @_;
21511
21512    if($Target eq "symbian") {
21513        return parse_libname_symbian($Name, $Type);
21514    }
21515    elsif($Target eq "windows") {
21516        return parse_libname_windows($Name, $Type);
21517    }
21518
21519    # unix
21520    my $Ext = getLIB_EXT($Target);
21521    if($Name=~/((((lib|).+?)([\-\_][\d\-\.\_]+.*?|))\.$Ext)(\.(.+)|)\Z/)
21522    { # libSDL-1.2.so.0.7.1
21523      # libwbxml2.so.0.0.18
21524      # libopcodes-2.21.53-system.20110810.so
21525        if($Type eq "name")
21526        { # libSDL-1.2
21527          # libwbxml2
21528            return $2;
21529        }
21530        elsif($Type eq "name+ext")
21531        { # libSDL-1.2.so
21532          # libwbxml2.so
21533            return $1;
21534        }
21535        elsif($Type eq "version")
21536        {
21537            if(defined $7
21538            and $7 ne "")
21539            { # 0.7.1
21540                return $7;
21541            }
21542            else
21543            { # libc-2.5.so (=>2.5 version)
21544                my $MV = $5;
21545                $MV=~s/\A[\-\_]+//g;
21546                return $MV;
21547            }
21548        }
21549        elsif($Type eq "short")
21550        { # libSDL
21551          # libwbxml2
21552            return $3;
21553        }
21554        elsif($Type eq "shortest")
21555        { # SDL
21556          # wbxml
21557            return shortest_name($3);
21558        }
21559    }
21560    return "";# error
21561}
21562
21563sub parse_libname_symbian($$)
21564{
21565    my ($Name, $Type) = @_;
21566    my $Ext = getLIB_EXT("symbian");
21567    if($Name=~/(((.+?)(\{.+\}|))\.$Ext)\Z/)
21568    { # libpthread{00010001}.dso
21569        if($Type eq "name")
21570        { # libpthread{00010001}
21571            return $2;
21572        }
21573        elsif($Type eq "name+ext")
21574        { # libpthread{00010001}.dso
21575            return $1;
21576        }
21577        elsif($Type eq "version")
21578        { # 00010001
21579            my $V = $4;
21580            $V=~s/\{(.+)\}/$1/;
21581            return $V;
21582        }
21583        elsif($Type eq "short")
21584        { # libpthread
21585            return $3;
21586        }
21587        elsif($Type eq "shortest")
21588        { # pthread
21589            return shortest_name($3);
21590        }
21591    }
21592    return "";# error
21593}
21594
21595sub parse_libname_windows($$)
21596{
21597    my ($Name, $Type) = @_;
21598    my $Ext = getLIB_EXT("windows");
21599    if($Name=~/((.+?)\.$Ext)\Z/)
21600    { # netapi32.dll
21601        if($Type eq "name")
21602        { # netapi32
21603            return $2;
21604        }
21605        elsif($Type eq "name+ext")
21606        { # netapi32.dll
21607            return $1;
21608        }
21609        elsif($Type eq "version")
21610        { # DLL version embedded
21611          # at binary-level
21612            return "";
21613        }
21614        elsif($Type eq "short")
21615        { # netapi32
21616            return $2;
21617        }
21618        elsif($Type eq "shortest")
21619        { # netapi
21620            return shortest_name($2);
21621        }
21622    }
21623    return "";# error
21624}
21625
21626sub shortest_name($)
21627{
21628    my $Name = $_[0];
21629    # remove prefix
21630    $Name=~s/\A(lib|open)//;
21631    # remove suffix
21632    $Name=~s/[\W\d_]+\Z//i;
21633    $Name=~s/([a-z]{2,})(lib)\Z/$1/i;
21634    return $Name;
21635}
21636
21637sub createSymbolsList($$$$$)
21638{
21639    my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_;
21640
21641    read_ABI_Dump(1, $DPath);
21642    prepareSymbols(1);
21643
21644    my %SymbolHeaderLib = ();
21645    my $Total = 0;
21646
21647    # Get List
21648    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
21649    {
21650        if(not link_symbol($Symbol, 1, "-Deps"))
21651        { # skip src only and all external functions
21652            next;
21653        }
21654        if(not symbolFilter($Symbol, 1, "Public", "Binary"))
21655        { # skip other symbols
21656            next;
21657        }
21658        my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
21659        if(not $HeaderName)
21660        { # skip src only and all external functions
21661            next;
21662        }
21663        my $DyLib = $Symbol_Library{1}{$Symbol};
21664        if(not $DyLib)
21665        { # skip src only and all external functions
21666            next;
21667        }
21668        $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1;
21669        $Total+=1;
21670    }
21671    # Draw List
21672    my $SYMBOLS_LIST = "<h1>Public symbols in <span style='color:Blue;'>$LName</span> (<span style='color:Red;'>$LVersion</span>)";
21673    $SYMBOLS_LIST .= " on <span style='color:Blue;'>".showArch($ArchName)."</span><br/>Total: $Total</h1><br/>";
21674    foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib))
21675    {
21676        foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}}))
21677        {
21678            my %NS_Symbol = ();
21679            foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) {
21680                $NS_Symbol{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1;
21681            }
21682            foreach my $NameSpace (sort keys(%NS_Symbol))
21683            {
21684                $SYMBOLS_LIST .= getTitle($HeaderName, $DyLib, $NameSpace);
21685                my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NS_Symbol{$NameSpace}});
21686                foreach my $Symbol (@SortedInterfaces)
21687                {
21688                    my $SubReport = "";
21689                    my $Signature = get_Signature($Symbol, 1);
21690                    if($NameSpace) {
21691                        $Signature=~s/\b\Q$NameSpace\E::\b//g;
21692                    }
21693                    if($Symbol=~/\A(_Z|\?)/)
21694                    {
21695                        if($Signature) {
21696                            $SubReport = insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Symbol</b>]</span><br/><br/>".$ContentDivEnd."\n");
21697                        }
21698                        else {
21699                            $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
21700                        }
21701                    }
21702                    else
21703                    {
21704                        if($Signature) {
21705                            $SubReport = "<span class='iname'>".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
21706                        }
21707                        else {
21708                            $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
21709                        }
21710                    }
21711                    $SYMBOLS_LIST .= $SubReport;
21712                }
21713            }
21714            $SYMBOLS_LIST .= "<br/>\n";
21715        }
21716    }
21717    # clear info
21718    (%TypeInfo, %SymbolInfo, %Library_Symbol, %DepSymbol_Library,
21719    %DepLibrary_Symbol, %SymVer, %SkipTypes, %SkipSymbols,
21720    %NestedNameSpaces, %ClassMethods, %AllocableClass, %ClassNames,
21721    %CompleteSignature, %SkipNameSpaces, %Symbol_Library, %Library_Symbol) = ();
21722    ($Content_Counter, $ContentID) = (0, 0);
21723    # print report
21724    my $CssStyles = readModule("Styles", "SymbolsList.css");
21725    my $JScripts = readModule("Scripts", "Sections.js");
21726    $SYMBOLS_LIST = "<a name='Top'></a>".$SYMBOLS_LIST.$TOP_REF."<br/>\n";
21727    my $Title = "$LName: public symbols";
21728    my $Keywords = "$LName, API, symbols";
21729    my $Description = "List of symbols in $LName ($LVersion) on ".showArch($ArchName);
21730    $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."
21731    <body><div>\n$SYMBOLS_LIST</div>
21732    <br/><br/>\n".getReportFooter()."
21733    </body></html>";
21734    writeFile($SaveTo, $SYMBOLS_LIST);
21735}
21736
21737sub add_target_libs($)
21738{
21739    foreach (@{$_[0]}) {
21740        $TargetLibs{$_} = 1;
21741    }
21742}
21743
21744sub is_target_lib($)
21745{
21746    my $LName = $_[0];
21747    if(not $LName) {
21748        return 0;
21749    }
21750    if($OSgroup eq "windows") {
21751        $LName = lc($LName);
21752    }
21753    if($TargetLibraryName
21754    and $LName!~/\Q$TargetLibraryName\E/) {
21755        return 0;
21756    }
21757    if(keys(%TargetLibs)
21758    and not $TargetLibs{$LName}
21759    and not $TargetLibs{parse_libname($LName, "name+ext", $OStarget)}) {
21760        return 0;
21761    }
21762    return 1;
21763}
21764
21765sub is_target_header($$)
21766{ # --header, --headers-list
21767    my ($H, $V) = @_;
21768    if(keys(%{$TargetHeaders{$V}}))
21769    {
21770        if($TargetHeaders{$V}{$H}) {
21771            return 1;
21772        }
21773    }
21774    return 0;
21775}
21776
21777sub readLibs($)
21778{
21779    my $LibVersion = $_[0];
21780    if($OStarget eq "windows")
21781    { # dumpbin.exe will crash
21782        # without VS Environment
21783        check_win32_env();
21784    }
21785    readSymbols($LibVersion);
21786    translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
21787    translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion);
21788}
21789
21790sub dump_sorting($)
21791{
21792    my $Hash = $_[0];
21793    return [] if(not $Hash);
21794    my @Keys = keys(%{$Hash});
21795    return [] if($#Keys<0);
21796    if($Keys[0]=~/\A\d+\Z/)
21797    { # numbers
21798        return [sort {int($a)<=>int($b)} @Keys];
21799    }
21800    else
21801    { # strings
21802        return [sort {$a cmp $b} @Keys];
21803    }
21804}
21805
21806sub printMsg($$)
21807{
21808    my ($Type, $Msg) = @_;
21809    if($Type!~/\AINFO/) {
21810        $Msg = $Type.": ".$Msg;
21811    }
21812    if($Type!~/_C\Z/) {
21813        $Msg .= "\n";
21814    }
21815    if($Quiet)
21816    { # --quiet option
21817        appendFile($COMMON_LOG_PATH, $Msg);
21818    }
21819    else
21820    {
21821        if($Type eq "ERROR") {
21822            print STDERR $Msg;
21823        }
21824        else {
21825            print $Msg;
21826        }
21827    }
21828}
21829
21830sub exitStatus($$)
21831{
21832    my ($Code, $Msg) = @_;
21833    printMsg("ERROR", $Msg);
21834    exit($ERROR_CODE{$Code});
21835}
21836
21837sub exitReport()
21838{ # the tool has run without any errors
21839    printReport();
21840    if($COMPILE_ERRORS)
21841    { # errors in headers may add false positives/negatives
21842        exit($ERROR_CODE{"Compile_Error"});
21843    }
21844    if($BinaryOnly and $RESULT{"Binary"}{"Problems"})
21845    { # --binary
21846        exit($ERROR_CODE{"Incompatible"});
21847    }
21848    elsif($SourceOnly and $RESULT{"Source"}{"Problems"})
21849    { # --source
21850        exit($ERROR_CODE{"Incompatible"});
21851    }
21852    elsif($RESULT{"Source"}{"Problems"}
21853    or $RESULT{"Binary"}{"Problems"})
21854    { # default
21855        exit($ERROR_CODE{"Incompatible"});
21856    }
21857    else {
21858        exit($ERROR_CODE{"Compatible"});
21859    }
21860}
21861
21862sub readRules($)
21863{
21864    my $Kind = $_[0];
21865    if(not -f $RULES_PATH{$Kind}) {
21866        exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'");
21867    }
21868    my $Content = readFile($RULES_PATH{$Kind});
21869    while(my $Rule = parseTag(\$Content, "rule"))
21870    {
21871        my $RId = parseTag(\$Rule, "id");
21872        my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind");
21873        foreach my $Prop (@Properties) {
21874            if(my $Value = parseTag(\$Rule, lc($Prop)))
21875            {
21876                $Value=~s/\n[ ]*//;
21877                $CompatRules{$Kind}{$RId}{$Prop} = $Value;
21878            }
21879        }
21880        if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) {
21881            $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols";
21882        }
21883        else {
21884            $CompatRules{$Kind}{$RId}{"Kind"} = "Types";
21885        }
21886    }
21887}
21888
21889sub getReportPath($)
21890{
21891    my $Level = $_[0];
21892    my $Dir = "compat_reports/$TargetLibraryName/".$Descriptor{1}{"Version"}."_to_".$Descriptor{2}{"Version"};
21893    if($Level eq "Binary")
21894    {
21895        if($BinaryReportPath)
21896        { # --bin-report-path
21897            return $BinaryReportPath;
21898        }
21899        elsif($OutputReportPath)
21900        { # --report-path
21901            return $OutputReportPath;
21902        }
21903        else
21904        { # default
21905            return $Dir."/abi_compat_report.$ReportFormat";
21906        }
21907    }
21908    elsif($Level eq "Source")
21909    {
21910        if($SourceReportPath)
21911        { # --src-report-path
21912            return $SourceReportPath;
21913        }
21914        elsif($OutputReportPath)
21915        { # --report-path
21916            return $OutputReportPath;
21917        }
21918        else
21919        { # default
21920            return $Dir."/src_compat_report.$ReportFormat";
21921        }
21922    }
21923    else
21924    {
21925        if($OutputReportPath)
21926        { # --report-path
21927            return $OutputReportPath;
21928        }
21929        else
21930        { # default
21931            return $Dir."/compat_report.$ReportFormat";
21932        }
21933    }
21934}
21935
21936sub printStatMsg($)
21937{
21938    my $Level = $_[0];
21939    printMsg("INFO", "total \"$Level\" compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"});
21940}
21941
21942sub listAffected($)
21943{
21944    my $Level = $_[0];
21945    my $List = "";
21946    foreach (keys(%{$TotalAffected{$Level}}))
21947    {
21948        if($StrictCompat and $TotalAffected{$Level}{$_} eq "Low")
21949        { # skip "Low"-severity problems
21950            next;
21951        }
21952        $List .= "$_\n";
21953    }
21954    my $Dir = get_dirname(getReportPath($Level));
21955    if($Level eq "Binary") {
21956        writeFile($Dir."/abi_affected.txt", $List);
21957    }
21958    elsif($Level eq "Source") {
21959        writeFile($Dir."/src_affected.txt", $List);
21960    }
21961}
21962
21963sub printReport()
21964{
21965    printMsg("INFO", "creating compatibility report ...");
21966    createReport();
21967    if($JoinReport or $DoubleReport)
21968    {
21969        if($RESULT{"Binary"}{"Problems"}
21970        or $RESULT{"Source"}{"Problems"}) {
21971            printMsg("INFO", "result: INCOMPATIBLE (Binary: ".$RESULT{"Binary"}{"Affected"}."\%, Source: ".$RESULT{"Source"}{"Affected"}."\%)");
21972        }
21973        else {
21974            printMsg("INFO", "result: COMPATIBLE");
21975        }
21976        printStatMsg("Binary");
21977        printStatMsg("Source");
21978        if($ListAffected)
21979        { # --list-affected
21980            listAffected("Binary");
21981            listAffected("Source");
21982        }
21983    }
21984    elsif($BinaryOnly)
21985    {
21986        if($RESULT{"Binary"}{"Problems"}) {
21987            printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Binary"}{"Affected"}."\%)");
21988        }
21989        else {
21990            printMsg("INFO", "result: COMPATIBLE");
21991        }
21992        printStatMsg("Binary");
21993        if($ListAffected)
21994        { # --list-affected
21995            listAffected("Binary");
21996        }
21997    }
21998    elsif($SourceOnly)
21999    {
22000        if($RESULT{"Source"}{"Problems"}) {
22001            printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Source"}{"Affected"}."\%)");
22002        }
22003        else {
22004            printMsg("INFO", "result: COMPATIBLE");
22005        }
22006        printStatMsg("Source");
22007        if($ListAffected)
22008        { # --list-affected
22009            listAffected("Source");
22010        }
22011    }
22012    if($StdOut)
22013    {
22014        if($JoinReport or not $DoubleReport)
22015        { # --binary or --source
22016            printMsg("INFO", "compatibility report has been generated to stdout");
22017        }
22018        else
22019        { # default
22020            printMsg("INFO", "compatibility reports have been generated to stdout");
22021        }
22022    }
22023    else
22024    {
22025        if($JoinReport)
22026        {
22027            printMsg("INFO", "see detailed report:\n  ".getReportPath("Join"));
22028        }
22029        elsif($DoubleReport)
22030        { # default
22031            printMsg("INFO", "see detailed reports:\n  ".getReportPath("Binary")."\n  ".getReportPath("Source"));
22032        }
22033        elsif($BinaryOnly)
22034        { # --binary
22035            printMsg("INFO", "see detailed report:\n  ".getReportPath("Binary"));
22036        }
22037        elsif($SourceOnly)
22038        { # --source
22039            printMsg("INFO", "see detailed report:\n  ".getReportPath("Source"));
22040        }
22041    }
22042}
22043
22044sub check_win32_env()
22045{
22046    if(not $ENV{"VCINSTALLDIR"}
22047    or not $ENV{"INCLUDE"}) {
22048        exitStatus("Error", "can't start without VC environment (vcvars64.bat)");
22049    }
22050}
22051
22052sub diffSets($$)
22053{
22054    my ($S1, $S2) = @_;
22055    my @SK1 = keys(%{$S1});
22056    my @SK2 = keys(%{$S2});
22057    if($#SK1!=$#SK2) {
22058        return 1;
22059    }
22060    foreach my $K1 (@SK1)
22061    {
22062        if(not defined $S2->{$K1}) {
22063            return 1;
22064        }
22065    }
22066    return 0;
22067}
22068
22069sub defaultDumpPath($$)
22070{
22071    my ($N, $V) = @_;
22072    return "abi_dumps/".$N."/".$N."_".$V.".abi.".$AR_EXT; # gzipped by default
22073}
22074
22075sub create_ABI_Dump()
22076{
22077    if(not -e $DumpAPI) {
22078        exitStatus("Access_Error", "can't access \'$DumpAPI\'");
22079    }
22080
22081    if(isDump($DumpAPI)) {
22082        read_ABI_Dump(1, $DumpAPI);
22083    }
22084    else {
22085        readDescriptor(1, createDescriptor(1, $DumpAPI));
22086    }
22087
22088    if(not $Descriptor{1}{"Version"})
22089    { # set to default: N
22090        $Descriptor{1}{"Version"} = "N";
22091    }
22092
22093    initLogging(1);
22094    detect_default_paths("inc|lib|bin|gcc"); # complete analysis
22095
22096    my $DumpPath = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"});
22097    if($OutputDumpPath)
22098    { # user defined path
22099        $DumpPath = $OutputDumpPath;
22100    }
22101    my $Archive = ($DumpPath=~s/\Q.$AR_EXT\E\Z//g);
22102
22103    if(not $Archive and not $StdOut)
22104    { # check archive utilities
22105        if($OSgroup eq "windows")
22106        { # using zip
22107            my $ZipCmd = get_CmdPath("zip");
22108            if(not $ZipCmd) {
22109                exitStatus("Not_Found", "can't find \"zip\"");
22110            }
22111        }
22112        else
22113        { # using tar and gzip
22114            my $TarCmd = get_CmdPath("tar");
22115            if(not $TarCmd) {
22116                exitStatus("Not_Found", "can't find \"tar\"");
22117            }
22118            my $GzipCmd = get_CmdPath("gzip");
22119            if(not $GzipCmd) {
22120                exitStatus("Not_Found", "can't find \"gzip\"");
22121            }
22122        }
22123    }
22124
22125    if(not $Descriptor{1}{"Dump"})
22126    {
22127        if(not $CheckHeadersOnly) {
22128            readLibs(1);
22129        }
22130        if($CheckHeadersOnly) {
22131            setLanguage(1, "C++");
22132        }
22133        searchForHeaders(1);
22134        $WORD_SIZE{1} = detectWordSize(1);
22135    }
22136    if(not $Descriptor{1}{"Dump"})
22137    {
22138        if($Descriptor{1}{"Headers"}) {
22139            readHeaders(1);
22140        }
22141    }
22142    cleanDump(1);
22143    if(not keys(%{$SymbolInfo{1}}))
22144    { # check if created dump is valid
22145        if(not $ExtendedCheck)
22146        {
22147            if($CheckHeadersOnly) {
22148                exitStatus("Empty_Set", "the set of public symbols is empty");
22149            }
22150            else {
22151                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection");
22152            }
22153        }
22154    }
22155    my %HeadersInfo = ();
22156    foreach my $HPath (keys(%{$Registered_Headers{1}})) {
22157        $HeadersInfo{$Registered_Headers{1}{$HPath}{"Identity"}} = $Registered_Headers{1}{$HPath}{"Pos"};
22158    }
22159    if($ExtraDump)
22160    { # add unmangled names to the ABI dump
22161        my @Names = ();
22162        foreach my $InfoId (keys(%{$SymbolInfo{1}}))
22163        {
22164            if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"}) {
22165                push(@Names, $MnglName);
22166            }
22167        }
22168        translateSymbols(@Names, 1);
22169        foreach my $InfoId (keys(%{$SymbolInfo{1}}))
22170        {
22171            if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"})
22172            {
22173                if(my $Unmangled = $tr_name{$MnglName})
22174                {
22175                    if($MnglName ne $Unmangled) {
22176                        $SymbolInfo{1}{$InfoId}{"Unmangled"} = $Unmangled;
22177                    }
22178                }
22179            }
22180        }
22181    }
22182
22183    my %GccConstants = (); # built-in GCC constants
22184    foreach my $Name (keys(%{$Constants{1}}))
22185    {
22186        if(not defined $Constants{1}{$Name}{"Header"})
22187        {
22188            $GccConstants{$Name} = $Constants{1}{$Name}{"Value"};
22189            delete($Constants{1}{$Name});
22190        }
22191    }
22192
22193    printMsg("INFO", "creating library ABI dump ...");
22194    my %ABI = (
22195        "TypeInfo" => $TypeInfo{1},
22196        "SymbolInfo" => $SymbolInfo{1},
22197        "Symbols" => $Library_Symbol{1},
22198        "DepSymbols" => $DepLibrary_Symbol{1},
22199        "SymbolVersion" => $SymVer{1},
22200        "LibraryVersion" => $Descriptor{1}{"Version"},
22201        "LibraryName" => $TargetLibraryName,
22202        "Language" => $COMMON_LANGUAGE{1},
22203        "SkipTypes" => $SkipTypes{1},
22204        "SkipSymbols" => $SkipSymbols{1},
22205        "SkipNameSpaces" => $SkipNameSpaces{1},
22206        "SkipHeaders" => $SkipHeadersList{1},
22207        "Headers" => \%HeadersInfo,
22208        "Constants" => $Constants{1},
22209        "GccConstants" => \%GccConstants,
22210        "NameSpaces" => $NestedNameSpaces{1},
22211        "Target" => $OStarget,
22212        "Arch" => getArch(1),
22213        "WordSize" => $WORD_SIZE{1},
22214        "GccVersion" => get_dumpversion($GCC_PATH),
22215        "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION,
22216        "ABI_COMPLIANCE_CHECKER_VERSION" => $TOOL_VERSION
22217    );
22218    if(diffSets($TargetHeaders{1}, \%HeadersInfo)) {
22219        $ABI{"TargetHeaders"} = $TargetHeaders{1};
22220    }
22221    if($UseXML) {
22222        $ABI{"XML_ABI_DUMP_VERSION"} = $XML_ABI_DUMP_VERSION;
22223    }
22224    if($ExtendedCheck)
22225    { # --ext option
22226        $ABI{"Mode"} = "Extended";
22227    }
22228    if($BinaryOnly)
22229    { # --binary
22230        $ABI{"BinOnly"} = 1;
22231    }
22232    if($ExtraDump)
22233    { # --extra-dump
22234        $ABI{"Extra"} = 1;
22235        $ABI{"UndefinedSymbols"} = $UndefinedSymbols{1};
22236        $ABI{"Needed"} = $Library_Needed{1};
22237    }
22238
22239    my $ABI_DUMP = "";
22240    if($UseXML)
22241    {
22242        loadModule("XmlDump");
22243        $ABI_DUMP = createXmlDump(\%ABI);
22244    }
22245    else
22246    { # default
22247        $ABI_DUMP = Dumper(\%ABI);
22248    }
22249    if($StdOut)
22250    { # --stdout option
22251        print STDOUT $ABI_DUMP;
22252        printMsg("INFO", "ABI dump has been generated to stdout");
22253        return;
22254    }
22255    else
22256    { # write to gzipped file
22257        my ($DDir, $DName) = separate_path($DumpPath);
22258        my $DPath = $TMP_DIR."/".$DName;
22259        if(not $Archive) {
22260            $DPath = $DumpPath;
22261        }
22262
22263        mkpath($DDir);
22264
22265        open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n");
22266        print DUMP $ABI_DUMP;
22267        close(DUMP);
22268
22269        if(not -s $DPath) {
22270            exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module");
22271        }
22272        if($Archive) {
22273            $DumpPath = createArchive($DPath, $DDir);
22274        }
22275
22276        if($OutputDumpPath) {
22277            printMsg("INFO", "dump path: $OutputDumpPath");
22278        }
22279        else {
22280            printMsg("INFO", "dump path: $DumpPath");
22281        }
22282        # printMsg("INFO", "you can transfer this dump everywhere and use instead of the ".$Descriptor{1}{"Version"}." version descriptor");
22283    }
22284}
22285
22286sub quickEmptyReports()
22287{ # Quick "empty" reports
22288  # ~4 times faster than merging equal dumps
22289  # NOTE: the dump contains the "LibraryVersion" attribute
22290  # if you change the version, then your dump will be different
22291  # OVERCOME: use -v1 and v2 options for comparing dumps
22292  # and don't change version in the XML descriptor (and dumps)
22293  # OVERCOME 2: separate meta info from the dumps in ACC 2.0
22294    if($Descriptor{1}{"Path"} eq $Descriptor{2}{"Path"}
22295    or -s $Descriptor{1}{"Path"} == -s $Descriptor{2}{"Path"})
22296    {
22297        my $FilePath1 = $Descriptor{1}{"Path"};
22298        my $FilePath2 = $Descriptor{2}{"Path"};
22299
22300        if(not isDump_U($FilePath1)) {
22301            $FilePath1 = unpackDump($FilePath1);
22302        }
22303
22304        if(not isDump_U($FilePath2)) {
22305            $FilePath2 = unpackDump($FilePath2);
22306        }
22307
22308        if($FilePath1 and $FilePath2)
22309        {
22310            my $Line = readLineNum($FilePath1, 0);
22311            if($Line=~/xml/)
22312            { # XML format
22313                # is not supported yet
22314                return;
22315            }
22316
22317            local $/ = undef;
22318
22319            open(DUMP1, $FilePath1);
22320            my $Content1 = <DUMP1>;
22321            close(DUMP1);
22322
22323            my $Eq = 0;
22324
22325            if($FilePath1 eq $FilePath2) {
22326                $Eq = 1;
22327            }
22328
22329            if(not $Eq)
22330            {
22331                open(DUMP2, $FilePath2);
22332                my $Content2 = <DUMP2>;
22333                close(DUMP2);
22334
22335                if($Content1 eq $Content2) {
22336                    $Eq = 1;
22337                }
22338
22339                # clean memory
22340                undef $Content2;
22341            }
22342
22343            if($Eq)
22344            {
22345                printMsg("INFO", "Input ABI dumps are equal, so generating quick empty report");
22346                # read a number of headers, libs, symbols and types
22347                my $ABIdump = eval($Content1);
22348
22349                # clean memory
22350                undef $Content1;
22351
22352                if(not $ABIdump) {
22353                    exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
22354                }
22355                if(not $ABIdump->{"TypeInfo"})
22356                { # support for old dumps
22357                    $ABIdump->{"TypeInfo"} = $ABIdump->{"TypeDescr"};
22358                }
22359                if(not $ABIdump->{"SymbolInfo"})
22360                { # support for old dumps
22361                    $ABIdump->{"SymbolInfo"} = $ABIdump->{"FuncDescr"};
22362                }
22363                read_Source_DumpInfo($ABIdump, 1);
22364
22365                foreach (keys(%{$Registered_Headers{1}})) {
22366                    $TargetHeaders{1}{$_} = 1;
22367                }
22368
22369                read_Libs_DumpInfo($ABIdump, 1);
22370                read_Machine_DumpInfo($ABIdump, 1);
22371                read_Machine_DumpInfo($ABIdump, 2);
22372
22373                %{$CheckedTypes{"Binary"}} = %{$ABIdump->{"TypeInfo"}};
22374                %{$CheckedTypes{"Source"}} = %{$ABIdump->{"TypeInfo"}};
22375
22376                foreach my $S (keys(%{$ABIdump->{"SymbolInfo"}}))
22377                {
22378                    if(my $Class = $ABIdump->{"SymbolInfo"}{$S}{"Class"})
22379                    {
22380                        if(defined $ABIdump->{"TypeInfo"}{$Class}{"PrivateABI"}) {
22381                            next;
22382                        }
22383                    }
22384
22385                    my $Access = $ABIdump->{"SymbolInfo"}{$S}{"Access"};
22386                    if($Access ne "private")
22387                    {
22388                        $CheckedSymbols{"Binary"}{$S} = 1;
22389                        $CheckedSymbols{"Source"}{$S} = 1;
22390                    }
22391                }
22392
22393                $Descriptor{1}{"Version"} = $TargetVersion{1}?$TargetVersion{1}:$ABIdump->{"LibraryVersion"};
22394                $Descriptor{2}{"Version"} = $TargetVersion{2}?$TargetVersion{2}:$ABIdump->{"LibraryVersion"};
22395
22396                if(not defined $Descriptor{1}{"Version"}) {
22397                    $Descriptor{1}{"Version"} = "X";
22398                }
22399
22400                if(not defined $Descriptor{2}{"Version"}) {
22401                    $Descriptor{2}{"Version"} = "Y";
22402                }
22403
22404                if(defined $ABIdump->{"ABI_DUMPER_VERSION"})
22405                {
22406                    $UsedDump{1}{"DWARF"} = 1;
22407                    $UsedDump{2}{"DWARF"} = 1;
22408
22409                    $UsedDump{1}{"M"} = $ABIdump->{"LibraryName"};
22410                    $UsedDump{2}{"M"} = $ABIdump->{"LibraryName"};
22411                }
22412
22413                exitReport();
22414            }
22415        }
22416    }
22417}
22418
22419sub initLogging($)
22420{
22421    my $LibVersion = $_[0];
22422    # create log directory
22423    my ($LOG_DIR, $LOG_FILE) = ("logs/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"}, "log.txt");
22424    if($OutputLogPath{$LibVersion})
22425    { # user-defined by -log-path option
22426        ($LOG_DIR, $LOG_FILE) = separate_path($OutputLogPath{$LibVersion});
22427    }
22428    if($LogMode ne "n") {
22429        mkpath($LOG_DIR);
22430    }
22431    $LOG_PATH{$LibVersion} = join_P(get_abs_path($LOG_DIR), $LOG_FILE);
22432    if($Debug)
22433    { # debug directory
22434        $DEBUG_PATH{$LibVersion} = "debug/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"};
22435
22436        if(not $ExtraInfo)
22437        { # enable --extra-info
22438            $ExtraInfo = $DEBUG_PATH{$LibVersion}."/extra-info";
22439        }
22440    }
22441    resetLogging($LibVersion);
22442}
22443
22444sub writeLog($$)
22445{
22446    my ($LibVersion, $Msg) = @_;
22447    if($LogMode ne "n") {
22448        appendFile($LOG_PATH{$LibVersion}, $Msg);
22449    }
22450}
22451
22452sub resetLogging($)
22453{
22454    my $LibVersion = $_[0];
22455    if($LogMode!~/a|n/)
22456    { # remove old log
22457        unlink($LOG_PATH{$LibVersion});
22458        if($Debug) {
22459            rmtree($DEBUG_PATH{$LibVersion});
22460        }
22461    }
22462}
22463
22464sub printErrorLog($)
22465{
22466    my $LibVersion = $_[0];
22467    if($LogMode ne "n") {
22468        printMsg("ERROR", "see log for details:\n  ".$LOG_PATH{$LibVersion}."\n");
22469    }
22470}
22471
22472sub isDump($)
22473{		# Modified to include "bdump" - binary dump.
22474    if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump|bdump)(\.tar\.gz(\.\w+|)|\.zip|\.xml|)\Z/)
22475    { # NOTE: name.abi.tar.gz.amd64 (dh & cdbs)
22476        return $1;
22477    }
22478    return 0;
22479}
22480
22481sub isDump_U($)
22482{		# Modified to include "bdump" - binary dump.
22483    if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump|bdump)(\.xml|)\Z/) {
22484        return $1;
22485    }
22486    return 0;
22487}
22488
22489sub compareInit()
22490{
22491    # read input XML descriptors or ABI dumps
22492    if(not $Descriptor{1}{"Path"}) {
22493        exitStatus("Error", "-old option is not specified");
22494    }
22495    if(not -e $Descriptor{1}{"Path"}) {
22496        exitStatus("Access_Error", "can't access \'".$Descriptor{1}{"Path"}."\'");
22497    }
22498
22499    if(not $Descriptor{2}{"Path"}) {
22500        exitStatus("Error", "-new option is not specified");
22501    }
22502    if(not -e $Descriptor{2}{"Path"}) {
22503        exitStatus("Access_Error", "can't access \'".$Descriptor{2}{"Path"}."\'");
22504    }
22505
22506    detect_default_paths("bin"); # to extract dumps
22507
22508    if(not defined $DisableQuickEmptyReport)
22509    {
22510        if(isDump($Descriptor{1}{"Path"})
22511        and isDump($Descriptor{2}{"Path"}))
22512        { # optimization: equal ABI dumps
22513            quickEmptyReports();
22514        }
22515    }
22516
22517    printMsg("INFO", "preparation, please wait ...");
22518
22519    if(isDump($Descriptor{1}{"Path"})) {
22520        read_ABI_Dump(1, $Descriptor{1}{"Path"});
22521    }
22522    else {
22523        readDescriptor(1, createDescriptor(1, $Descriptor{1}{"Path"}));
22524    }
22525
22526    if(isDump($Descriptor{2}{"Path"})) {
22527        read_ABI_Dump(2, $Descriptor{2}{"Path"});
22528    }
22529    else {
22530        readDescriptor(2, createDescriptor(2, $Descriptor{2}{"Path"}));
22531    }
22532
22533    if(not $Descriptor{1}{"Version"})
22534    { # set to default: X
22535        $Descriptor{1}{"Version"} = "X";
22536        print STDERR "WARNING: version number #1 is not set (use --v1=NUM option)\n";
22537    }
22538
22539    if(not $Descriptor{2}{"Version"})
22540    { # set to default: Y
22541        $Descriptor{2}{"Version"} = "Y";
22542        print STDERR "WARNING: version number #2 is not set (use --v2=NUM option)\n";
22543    }
22544
22545    if(not $UsedDump{1}{"V"}) {
22546        initLogging(1);
22547    }
22548
22549    if(not $UsedDump{2}{"V"}) {
22550        initLogging(2);
22551    }
22552
22553    # check input data
22554    if(not $Descriptor{1}{"Headers"}) {
22555        exitStatus("Error", "can't find header files info in descriptor d1");
22556    }
22557    if(not $Descriptor{2}{"Headers"}) {
22558        exitStatus("Error", "can't find header files info in descriptor d2");
22559    }
22560
22561    if(not $CheckHeadersOnly)
22562    {
22563        if(not $Descriptor{1}{"Libs"}) {
22564            exitStatus("Error", "can't find libraries info in descriptor d1");
22565        }
22566        if(not $Descriptor{2}{"Libs"}) {
22567            exitStatus("Error", "can't find libraries info in descriptor d2");
22568        }
22569    }
22570
22571    if($UseDumps)
22572    { # --use-dumps
22573      # parallel processing
22574        my $DumpPath1 = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"});
22575        my $DumpPath2 = defaultDumpPath($TargetLibraryName, $Descriptor{2}{"Version"});
22576
22577        unlink($DumpPath1);
22578        unlink($DumpPath2);
22579
22580        my $pid = fork();
22581        if($pid)
22582        { # dump on two CPU cores
22583            my @PARAMS = ("-dump", $Descriptor{1}{"Path"}, "-l", $TargetLibraryName);
22584            if($RelativeDirectory{1}) {
22585                @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{1});
22586            }
22587            if($OutputLogPath{1}) {
22588                @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{1});
22589            }
22590            if($CrossGcc) {
22591                @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
22592            }
22593            if($Quiet)
22594            {
22595                @PARAMS = (@PARAMS, "-quiet");
22596                @PARAMS = (@PARAMS, "-logging-mode", "a");
22597            }
22598            elsif($LogMode and $LogMode ne "w")
22599            { # "w" is default
22600                @PARAMS = (@PARAMS, "-logging-mode", $LogMode);
22601            }
22602            if($ExtendedCheck) {
22603                @PARAMS = (@PARAMS, "-extended");
22604            }
22605            if($UserLang) {
22606                @PARAMS = (@PARAMS, "-lang", $UserLang);
22607            }
22608            if($TargetVersion{1}) {
22609                @PARAMS = (@PARAMS, "-vnum", $TargetVersion{1});
22610            }
22611            if($BinaryOnly) {
22612                @PARAMS = (@PARAMS, "-binary");
22613            }
22614            if($SourceOnly) {
22615                @PARAMS = (@PARAMS, "-source");
22616            }
22617            if($SortDump) {
22618                @PARAMS = (@PARAMS, "-sort");
22619            }
22620            if($DumpFormat and $DumpFormat ne "perl") {
22621                @PARAMS = (@PARAMS, "-dump-format", $DumpFormat);
22622            }
22623            if($CheckHeadersOnly) {
22624                @PARAMS = (@PARAMS, "-headers-only");
22625            }
22626            if($Debug)
22627            {
22628                @PARAMS = (@PARAMS, "-debug");
22629                printMsg("INFO", "running perl $0 @PARAMS");
22630            }
22631            system("perl", $0, @PARAMS);
22632            if(not -f $DumpPath1) {
22633                exit(1);
22634            }
22635        }
22636        else
22637        { # child
22638            my @PARAMS = ("-dump", $Descriptor{2}{"Path"}, "-l", $TargetLibraryName);
22639            if($RelativeDirectory{2}) {
22640                @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{2});
22641            }
22642            if($OutputLogPath{2}) {
22643                @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{2});
22644            }
22645            if($CrossGcc) {
22646                @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
22647            }
22648            if($Quiet)
22649            {
22650                @PARAMS = (@PARAMS, "-quiet");
22651                @PARAMS = (@PARAMS, "-logging-mode", "a");
22652            }
22653            elsif($LogMode and $LogMode ne "w")
22654            { # "w" is default
22655                @PARAMS = (@PARAMS, "-logging-mode", $LogMode);
22656            }
22657            if($ExtendedCheck) {
22658                @PARAMS = (@PARAMS, "-extended");
22659            }
22660            if($UserLang) {
22661                @PARAMS = (@PARAMS, "-lang", $UserLang);
22662            }
22663            if($TargetVersion{2}) {
22664                @PARAMS = (@PARAMS, "-vnum", $TargetVersion{2});
22665            }
22666            if($BinaryOnly) {
22667                @PARAMS = (@PARAMS, "-binary");
22668            }
22669            if($SourceOnly) {
22670                @PARAMS = (@PARAMS, "-source");
22671            }
22672            if($SortDump) {
22673                @PARAMS = (@PARAMS, "-sort");
22674            }
22675            if($DumpFormat and $DumpFormat ne "perl") {
22676                @PARAMS = (@PARAMS, "-dump-format", $DumpFormat);
22677            }
22678            if($CheckHeadersOnly) {
22679                @PARAMS = (@PARAMS, "-headers-only");
22680            }
22681            if($Debug)
22682            {
22683                @PARAMS = (@PARAMS, "-debug");
22684                printMsg("INFO", "running perl $0 @PARAMS");
22685            }
22686            system("perl", $0, @PARAMS);
22687            if(not -f $DumpPath2) {
22688                exit(1);
22689            }
22690            else {
22691                exit(0);
22692            }
22693        }
22694        waitpid($pid, 0);
22695
22696        my @CMP_PARAMS = ("-l", $TargetLibraryName);
22697        @CMP_PARAMS = (@CMP_PARAMS, "-d1", $DumpPath1);
22698        @CMP_PARAMS = (@CMP_PARAMS, "-d2", $DumpPath2);
22699        if($TargetTitle ne $TargetLibraryName) {
22700            @CMP_PARAMS = (@CMP_PARAMS, "-title", $TargetTitle);
22701        }
22702        if($ShowRetVal) {
22703            @CMP_PARAMS = (@CMP_PARAMS, "-show-retval");
22704        }
22705        if($CrossGcc) {
22706            @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $CrossGcc);
22707        }
22708        @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a");
22709        if($Quiet) {
22710            @CMP_PARAMS = (@CMP_PARAMS, "-quiet");
22711        }
22712        if($ReportFormat and $ReportFormat ne "html")
22713        { # HTML is default format
22714            @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $ReportFormat);
22715        }
22716        if($OutputReportPath) {
22717            @CMP_PARAMS = (@CMP_PARAMS, "-report-path", $OutputReportPath);
22718        }
22719        if($BinaryReportPath) {
22720            @CMP_PARAMS = (@CMP_PARAMS, "-bin-report-path", $BinaryReportPath);
22721        }
22722        if($SourceReportPath) {
22723            @CMP_PARAMS = (@CMP_PARAMS, "-src-report-path", $SourceReportPath);
22724        }
22725        if($LoggingPath) {
22726            @CMP_PARAMS = (@CMP_PARAMS, "-log-path", $LoggingPath);
22727        }
22728        if($CheckHeadersOnly) {
22729            @CMP_PARAMS = (@CMP_PARAMS, "-headers-only");
22730        }
22731        if($BinaryOnly) {
22732            @CMP_PARAMS = (@CMP_PARAMS, "-binary");
22733        }
22734        if($SourceOnly) {
22735            @CMP_PARAMS = (@CMP_PARAMS, "-source");
22736        }
22737        if($Debug)
22738        {
22739            @CMP_PARAMS = (@CMP_PARAMS, "-debug");
22740            printMsg("INFO", "running perl $0 @CMP_PARAMS");
22741        }
22742        system("perl", $0, @CMP_PARAMS);
22743        exit($?>>8);
22744    }
22745    if(not $Descriptor{1}{"Dump"}
22746    or not $Descriptor{2}{"Dump"})
22747    { # need GCC toolchain to analyze
22748      # header files and libraries
22749        detect_default_paths("inc|lib|gcc");
22750    }
22751    if(not $Descriptor{1}{"Dump"})
22752    {
22753        if(not $CheckHeadersOnly) {
22754            readLibs(1);
22755        }
22756        if($CheckHeadersOnly) {
22757            setLanguage(1, "C++");
22758        }
22759        searchForHeaders(1);
22760        $WORD_SIZE{1} = detectWordSize(1);
22761    }
22762    if(not $Descriptor{2}{"Dump"})
22763    {
22764        if(not $CheckHeadersOnly) {
22765            readLibs(2);
22766        }
22767        if($CheckHeadersOnly) {
22768            setLanguage(2, "C++");
22769        }
22770        searchForHeaders(2);
22771        $WORD_SIZE{2} = detectWordSize(2);
22772    }
22773    if($WORD_SIZE{1} ne $WORD_SIZE{2})
22774    { # support for old ABI dumps
22775      # try to synch different WORD sizes
22776        if(not checkDump(1, "2.1"))
22777        {
22778            $WORD_SIZE{1} = $WORD_SIZE{2};
22779            printMsg("WARNING", "set WORD size to ".$WORD_SIZE{2}." bytes");
22780        }
22781        elsif(not checkDump(2, "2.1"))
22782        {
22783            $WORD_SIZE{2} = $WORD_SIZE{1};
22784            printMsg("WARNING", "set WORD size to ".$WORD_SIZE{1}." bytes");
22785        }
22786    }
22787    elsif(not $WORD_SIZE{1}
22788    and not $WORD_SIZE{2})
22789    { # support for old ABI dumps
22790        $WORD_SIZE{1} = "4";
22791        $WORD_SIZE{2} = "4";
22792    }
22793    if($Descriptor{1}{"Dump"})
22794    { # support for old ABI dumps
22795        prepareTypes(1);
22796    }
22797    if($Descriptor{2}{"Dump"})
22798    { # support for old ABI dumps
22799        prepareTypes(2);
22800    }
22801    if($AppPath and not keys(%{$Symbol_Library{1}})) {
22802        printMsg("WARNING", "the application ".get_filename($AppPath)." has no symbols imported from the $SLIB_TYPE libraries");
22803    }
22804    # process input data
22805    if($Descriptor{1}{"Headers"}
22806    and not $Descriptor{1}{"Dump"}) {
22807        readHeaders(1);
22808    }
22809    if($Descriptor{2}{"Headers"}
22810    and not $Descriptor{2}{"Dump"}) {
22811        readHeaders(2);
22812    }
22813
22814    # clean memory
22815    %SystemHeaders = ();
22816    %mangled_name_gcc = ();
22817
22818    prepareSymbols(1);
22819    prepareSymbols(2);
22820
22821    # clean memory
22822    %SymbolInfo = ();
22823
22824    # Virtual Tables
22825    registerVTable(1);
22826    registerVTable(2);
22827
22828    if(not checkDump(1, "1.22")
22829    and checkDump(2, "1.22"))
22830    { # support for old ABI dumps
22831        foreach my $ClassName (keys(%{$VirtualTable{2}}))
22832        {
22833            if($ClassName=~/</)
22834            { # templates
22835                if(not defined $VirtualTable{1}{$ClassName})
22836                { # synchronize
22837                    delete($VirtualTable{2}{$ClassName});
22838                }
22839            }
22840        }
22841    }
22842
22843    registerOverriding(1);
22844    registerOverriding(2);
22845
22846    setVirtFuncPositions(1);
22847    setVirtFuncPositions(2);
22848
22849    # Other
22850    addParamNames(1);
22851    addParamNames(2);
22852
22853    detectChangedTypedefs();
22854}
22855
22856sub compareAPIs($)
22857{
22858    my $Level = $_[0];
22859
22860    readRules($Level);
22861    loadModule("CallConv");
22862
22863    if($Level eq "Binary") {
22864        printMsg("INFO", "comparing ABIs ...");
22865    }
22866    else {
22867        printMsg("INFO", "comparing APIs ...");
22868    }
22869
22870    if($CheckHeadersOnly
22871    or $Level eq "Source")
22872    { # added/removed in headers
22873        detectAdded_H($Level);
22874        detectRemoved_H($Level);
22875    }
22876    else
22877    { # added/removed in libs
22878        detectAdded($Level);
22879        detectRemoved($Level);
22880    }
22881
22882    mergeSymbols($Level);
22883
22884    if(not defined $DisableConstantsCheck)
22885    {
22886        if(keys(%{$CheckedSymbols{$Level}})) {
22887            mergeConstants($Level);
22888        }
22889    }
22890
22891    $Cache{"mergeTypes"} = (); # free memory
22892
22893    if($CheckHeadersOnly
22894    or $Level eq "Source")
22895    { # added/removed in headers
22896        mergeHeaders($Level);
22897    }
22898    else
22899    { # added/removed in libs
22900        mergeLibs($Level);
22901    }
22902
22903    foreach my $S (keys(%{$CompatProblems{$Level}}))
22904    {
22905        foreach my $K (keys(%{$CompatProblems{$Level}{$S}}))
22906        {
22907            foreach my $L (keys(%{$CompatProblems{$Level}{$S}{$K}}))
22908            {
22909                if(my $T = $CompatProblems{$Level}{$S}{$K}{$L}{"Type_Name"}) {
22910                    $TypeProblemsIndex{$Level}{$T}{$S} = 1;
22911                }
22912            }
22913        }
22914    }
22915}
22916
22917sub getSysOpts()
22918{
22919    my %Opts = (
22920    "OStarget"=>$OStarget,
22921    "Debug"=>$Debug,
22922    "Quiet"=>$Quiet,
22923    "LogMode"=>$LogMode,
22924    "CheckHeadersOnly"=>$CheckHeadersOnly,
22925
22926    "SystemRoot"=>$SystemRoot,
22927    "GCC_PATH"=>$GCC_PATH,
22928    "TargetSysInfo"=>$TargetSysInfo,
22929    "CrossPrefix"=>$CrossPrefix,
22930    "TargetLibraryName"=>$TargetLibraryName,
22931    "CrossGcc"=>$CrossGcc,
22932    "UseStaticLibs"=>$UseStaticLibs,
22933    "NoStdInc"=>$NoStdInc,
22934    "CxxIncompat"=>$CxxIncompat,
22935    "SkipUnidentified"=>$SkipUnidentified,
22936    "DisableConstantsCheck"=>$DisableConstantsCheck,
22937
22938    "BinaryOnly" => $BinaryOnly,
22939    "SourceOnly" => $SourceOnly
22940    );
22941    return \%Opts;
22942}
22943
22944sub get_CodeError($)
22945{
22946    my %CODE_ERROR = reverse(%ERROR_CODE);
22947    return $CODE_ERROR{$_[0]};
22948}
22949
22950sub scenario()
22951{
22952    if($StdOut)
22953    { # enable quiet mode
22954        $Quiet = 1;
22955        $JoinReport = 1;
22956    }
22957    if(not $LogMode)
22958    { # default
22959        $LogMode = "w";
22960    }
22961    if($UserLang)
22962    { # --lang=C++
22963        $UserLang = uc($UserLang);
22964        $COMMON_LANGUAGE{1}=$UserLang;
22965        $COMMON_LANGUAGE{2}=$UserLang;
22966    }
22967    if($LoggingPath)
22968    {
22969        $OutputLogPath{1} = $LoggingPath;
22970        $OutputLogPath{2} = $LoggingPath;
22971        if($Quiet) {
22972            $COMMON_LOG_PATH = $LoggingPath;
22973        }
22974    }
22975
22976    if($Force) {
22977        $GCC_MISSED_MNGL = 1;
22978    }
22979
22980    if($Quick) {
22981        $ADD_TMPL_INSTANCES = 0;
22982    }
22983    if($OutputDumpPath)
22984    { # validate
22985        if(not isDump($OutputDumpPath)) {
22986            exitStatus("Error", "the dump path should be a path to *.abi.$AR_EXT or *.abi file");
22987        }
22988    }
22989    if($BinaryOnly and $SourceOnly)
22990    { # both --binary and --source
22991      # is the default mode
22992        if(not $CmpSystems)
22993        {
22994            $BinaryOnly = 0;
22995            $SourceOnly = 0;
22996        }
22997
22998        $DoubleReport = 1;
22999        $JoinReport = 0;
23000
23001        if($OutputReportPath)
23002        { # --report-path
23003            $DoubleReport = 0;
23004            $JoinReport = 1;
23005        }
23006    }
23007    elsif($BinaryOnly or $SourceOnly)
23008    { # --binary or --source
23009        $DoubleReport = 0;
23010        $JoinReport = 0;
23011    }
23012    if($UseXML)
23013    { # --xml option
23014        $ReportFormat = "xml";
23015        $DumpFormat = "xml";
23016    }
23017    if($ReportFormat)
23018    { # validate
23019        $ReportFormat = lc($ReportFormat);
23020        if($ReportFormat!~/\A(xml|html|htm)\Z/) {
23021            exitStatus("Error", "unknown report format \'$ReportFormat\'");
23022        }
23023        if($ReportFormat eq "htm")
23024        { # HTM == HTML
23025            $ReportFormat = "html";
23026        }
23027        elsif($ReportFormat eq "xml")
23028        { # --report-format=XML equal to --xml
23029            $UseXML = 1;
23030        }
23031    }
23032    else
23033    { # default: HTML
23034        $ReportFormat = "html";
23035    }
23036    if($DumpFormat)
23037    { # validate
23038        $DumpFormat = lc($DumpFormat);
23039        if($DumpFormat!~/\A(xml|perl)\Z/) {
23040            exitStatus("Error", "unknown ABI dump format \'$DumpFormat\'");
23041        }
23042        if($DumpFormat eq "xml")
23043        { # --dump-format=XML equal to --xml
23044            $UseXML = 1;
23045        }
23046    }
23047    else
23048    { # default: Perl Data::Dumper
23049        $DumpFormat = "perl";
23050    }
23051    if($Quiet and $LogMode!~/a|n/)
23052    { # --quiet log
23053        if(-f $COMMON_LOG_PATH) {
23054            unlink($COMMON_LOG_PATH);
23055        }
23056    }
23057    if($ExtraInfo) {
23058        $CheckUndefined = 1;
23059    }
23060    if($TestTool and $UseDumps)
23061    { # --test && --use-dumps == --test-dump
23062        $TestDump = 1;
23063    }
23064    if($Tolerant)
23065    { # enable all
23066        $Tolerance = 1234;
23067    }
23068    if($Help)
23069    {
23070        HELP_MESSAGE();
23071        exit(0);
23072    }
23073    if($InfoMsg)
23074    {
23075        INFO_MESSAGE();
23076        exit(0);
23077    }
23078    if($ShowVersion)
23079    {
23080        printMsg("INFO", "ABI Compliance Checker (ABICC) $TOOL_VERSION\nCopyright (C) 2016 Andrey Ponomarenko's ABI Laboratory\nLicense: LGPL or GPL <http://www.gnu.org/licenses/>\nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko.");
23081        exit(0);
23082    }
23083    if($DumpVersion)
23084    {
23085        printMsg("INFO", $TOOL_VERSION);
23086        exit(0);
23087    }
23088    if($ExtendedCheck) {
23089        $CheckHeadersOnly = 1;
23090    }
23091    if($SystemRoot_Opt)
23092    { # user defined root
23093        if(not -e $SystemRoot_Opt) {
23094            exitStatus("Access_Error", "can't access \'$SystemRoot\'");
23095        }
23096        $SystemRoot = $SystemRoot_Opt;
23097        $SystemRoot=~s/[\/]+\Z//g;
23098        if($SystemRoot) {
23099            $SystemRoot = get_abs_path($SystemRoot);
23100        }
23101    }
23102    $Data::Dumper::Sortkeys = 1;
23103
23104    if($SortDump)
23105    {
23106        $Data::Dumper::Useperl = 1;
23107        $Data::Dumper::Sortkeys = \&dump_sorting;
23108    }
23109
23110    if($TargetLibsPath)
23111    {
23112        if(not -f $TargetLibsPath) {
23113            exitStatus("Access_Error", "can't access file \'$TargetLibsPath\'");
23114        }
23115        foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath)))
23116        {
23117            if($OSgroup eq "windows") {
23118                $TargetLibs{lc($Lib)} = 1;
23119            }
23120            else {
23121                $TargetLibs{$Lib} = 1;
23122            }
23123        }
23124    }
23125    if($TargetHeadersPath)
23126    { # --headers-list
23127        if(not -f $TargetHeadersPath) {
23128            exitStatus("Access_Error", "can't access file \'$TargetHeadersPath\'");
23129        }
23130        foreach my $Header (split(/\s*\n\s*/, readFile($TargetHeadersPath)))
23131        {
23132            $TargetHeaders{1}{get_filename($Header)} = 1;
23133            $TargetHeaders{2}{get_filename($Header)} = 1;
23134        }
23135    }
23136    if($TargetHeader)
23137    { # --header
23138        $TargetHeaders{1}{get_filename($TargetHeader)} = 1;
23139        $TargetHeaders{2}{get_filename($TargetHeader)} = 1;
23140    }
23141    if($TestABIDumper)
23142    {
23143        if($OSgroup ne "linux") {
23144            exitStatus("Error", "-test-abi-dumper option is available on Linux only");
23145        }
23146    }
23147    if($TestTool
23148    or $TestDump
23149    or $TestABIDumper)
23150    { # --test, --test-dump
23151        detect_default_paths("bin|gcc"); # to compile libs
23152        loadModule("RegTests");
23153        testTool($TestDump, $Debug, $Quiet, $ExtendedCheck, $LogMode, $ReportFormat, $DumpFormat,
23154        $LIB_EXT, $GCC_PATH, $SortDump, $CheckHeadersOnly, $OldStyle, $TestABIDumper);
23155        exit(0);
23156    }
23157    if($DumpSystem)
23158    { # --dump-system
23159        if(-d $MODULES_DIR."/Targets/"
23160        and -d $MODULES_DIR."/Targets/".$OStarget) {
23161            $TargetSysInfo = $MODULES_DIR."/Targets/".$OStarget;
23162        }
23163        if(not $TargetSysInfo) {
23164            exitStatus("Error", "-sysinfo option should be specified to dump system ABI");
23165        }
23166
23167        if(not -d $TargetSysInfo) {
23168            exitStatus("Access_Error", "can't access \'$TargetSysInfo\'");
23169        }
23170
23171        loadModule("SysCheck");
23172        if($DumpSystem=~/\.(xml|desc)\Z/)
23173        { # system XML descriptor
23174            if(not -f $DumpSystem) {
23175                exitStatus("Access_Error", "can't access file \'$DumpSystem\'");
23176            }
23177
23178            my $SDesc = readFile($DumpSystem);
23179            if(my $RelDir = $RelativeDirectory{1}) {
23180                $SDesc =~ s/{RELPATH}/$RelDir/g;
23181            }
23182
23183            my $Ret = readSystemDescriptor($SDesc);
23184            foreach (@{$Ret->{"Tools"}})
23185            {
23186                push_U($SystemPaths{"bin"}, $_);
23187                $TargetTools{$_} = 1;
23188            }
23189            if($Ret->{"CrossPrefix"}) {
23190                $CrossPrefix = $Ret->{"CrossPrefix"};
23191            }
23192        }
23193        elsif($SystemRoot_Opt)
23194        { # -sysroot "/" option
23195          # default target: /usr/lib, /usr/include
23196          # search libs: /usr/lib and /lib
23197            if(not -e $SystemRoot."/usr/lib") {
23198                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/lib'");
23199            }
23200            if(not -e $SystemRoot."/lib") {
23201                exitStatus("Access_Error", "can't access '".$SystemRoot."/lib'");
23202            }
23203            if(not -e $SystemRoot."/usr/include") {
23204                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/include'");
23205            }
23206            readSystemDescriptor("
23207                <name>
23208                    $DumpSystem
23209                </name>
23210                <headers>
23211                    $SystemRoot/usr/include
23212                </headers>
23213                <libs>
23214                    $SystemRoot/usr/lib
23215                </libs>
23216                <search_libs>
23217                    $SystemRoot/lib
23218                </search_libs>");
23219        }
23220        else {
23221            exitStatus("Error", "-sysroot <dirpath> option should be specified, usually it's \"/\"");
23222        }
23223        detect_default_paths("bin|gcc"); # to check symbols
23224        if($OStarget eq "windows")
23225        { # to run dumpbin.exe
23226          # and undname.exe
23227            check_win32_env();
23228        }
23229        dumpSystem(getSysOpts());
23230        exit(0);
23231    }
23232
23233    if($CmpSystems)
23234    { # --cmp-systems
23235        detect_default_paths("bin"); # to extract dumps
23236        loadModule("SysCheck");
23237        cmpSystems($Descriptor{1}{"Path"}, $Descriptor{2}{"Path"}, getSysOpts());
23238        exit(0);
23239    }
23240
23241    if(not $CountSymbols)
23242    {
23243        if(not $TargetLibraryName) {
23244            exitStatus("Error", "library name is not selected (-l option)");
23245        }
23246        else
23247        { # validate library name
23248            if($TargetLibraryName=~/[\*\/\\]/) {
23249                exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name");
23250            }
23251        }
23252    }
23253
23254    if(not $TargetTitle) {
23255        $TargetTitle = $TargetLibraryName;
23256    }
23257
23258    if($SymbolsListPath)
23259    {
23260        if(not -f $SymbolsListPath) {
23261            exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'");
23262        }
23263        foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath))) {
23264            $SymbolsList{$Interface} = 1;
23265        }
23266    }
23267    if($TypesListPath)
23268    {
23269        if(not -f $TypesListPath) {
23270            exitStatus("Access_Error", "can't access file \'$TypesListPath\'");
23271        }
23272        foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) {
23273            $TypesList{$Type} = 1;
23274        }
23275    }
23276    if($SkipSymbolsListPath)
23277    {
23278        if(not -f $SkipSymbolsListPath) {
23279            exitStatus("Access_Error", "can't access file \'$SkipSymbolsListPath\'");
23280        }
23281        foreach my $Interface (split(/\s*\n\s*/, readFile($SkipSymbolsListPath)))
23282        {
23283            $SkipSymbols{1}{$Interface} = 1;
23284            $SkipSymbols{2}{$Interface} = 1;
23285        }
23286    }
23287    if($SkipTypesListPath)
23288    {
23289        if(not -f $SkipTypesListPath) {
23290            exitStatus("Access_Error", "can't access file \'$SkipTypesListPath\'");
23291        }
23292        foreach my $Type (split(/\s*\n\s*/, readFile($SkipTypesListPath)))
23293        {
23294            $SkipTypes{1}{$Type} = 1;
23295            $SkipTypes{2}{$Type} = 1;
23296        }
23297    }
23298    if($SkipHeadersPath)
23299    {
23300        if(not -f $SkipHeadersPath) {
23301            exitStatus("Access_Error", "can't access file \'$SkipHeadersPath\'");
23302        }
23303        foreach my $Path (split(/\s*\n\s*/, readFile($SkipHeadersPath)))
23304        { # register for both versions
23305            $SkipHeadersList{1}{$Path} = 1;
23306            $SkipHeadersList{2}{$Path} = 1;
23307
23308            my ($CPath, $Type) = classifyPath($Path);
23309            $SkipHeaders{1}{$Type}{$CPath} = 1;
23310            $SkipHeaders{2}{$Type}{$CPath} = 1;
23311        }
23312    }
23313    if($ParamNamesPath)
23314    {
23315        if(not -f $ParamNamesPath) {
23316            exitStatus("Access_Error", "can't access file \'$ParamNamesPath\'");
23317        }
23318        foreach my $Line (split(/\n/, readFile($ParamNamesPath)))
23319        {
23320            if($Line=~s/\A(\w+)\;//)
23321            {
23322                my $Interface = $1;
23323                if($Line=~/;(\d+);/)
23324                {
23325                    while($Line=~s/(\d+);(\w+)//) {
23326                        $AddIntParams{$Interface}{$1}=$2;
23327                    }
23328                }
23329                else
23330                {
23331                    my $Num = 0;
23332                    foreach my $Name (split(/;/, $Line)) {
23333                        $AddIntParams{$Interface}{$Num++}=$Name;
23334                    }
23335                }
23336            }
23337        }
23338    }
23339
23340    if($AppPath)
23341    {
23342        if(not -f $AppPath) {
23343            exitStatus("Access_Error", "can't access file \'$AppPath\'");
23344        }
23345
23346        detect_default_paths("bin|gcc");
23347        foreach my $Interface (readSymbols_App($AppPath)) {
23348            $SymbolsList_App{$Interface} = 1;
23349        }
23350    }
23351
23352    if($CountSymbols)
23353    {
23354        if(not -e $CountSymbols) {
23355            exitStatus("Access_Error", "can't access \'$CountSymbols\'");
23356        }
23357
23358        read_ABI_Dump(1, $CountSymbols);
23359
23360        foreach my $Id (keys(%{$SymbolInfo{1}}))
23361        {
23362            my $MnglName = $SymbolInfo{1}{$Id}{"MnglName"};
23363            if(not $MnglName) {
23364                $MnglName = $SymbolInfo{1}{$Id}{"ShortName"}
23365            }
23366
23367            if(my $SV = $SymVer{1}{$MnglName}) {
23368                $CompleteSignature{1}{$SV} = $SymbolInfo{1}{$Id};
23369            }
23370            else {
23371                $CompleteSignature{1}{$MnglName} = $SymbolInfo{1}{$Id};
23372            }
23373
23374            if(my $Alias = $CompleteSignature{1}{$MnglName}{"Alias"}) {
23375                $CompleteSignature{1}{$Alias} = $SymbolInfo{1}{$Id};
23376            }
23377        }
23378
23379        my $Count = 0;
23380        foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
23381        {
23382            if($CompleteSignature{1}{$Symbol}{"PureVirt"}) {
23383                next;
23384            }
23385            if($CompleteSignature{1}{$Symbol}{"Private"}) {
23386                next;
23387            }
23388            if(not $CompleteSignature{1}{$Symbol}{"Header"}) {
23389                next;
23390            }
23391
23392            $Count += symbolFilter($Symbol, 1, "Affected + InlineVirt", "Binary");
23393        }
23394
23395        printMsg("INFO", $Count);
23396        exit(0);
23397    }
23398
23399    if($DumpAPI)
23400    { # --dump-abi
23401      # make an API dump
23402        create_ABI_Dump();
23403        exit($COMPILE_ERRORS);
23404    }
23405    # default: compare APIs
23406    #  -d1 <path>
23407    #  -d2 <path>
23408    compareInit();
23409    if($JoinReport or $DoubleReport)
23410    {
23411        compareAPIs("Binary");
23412        compareAPIs("Source");
23413    }
23414    elsif($BinaryOnly) {
23415        compareAPIs("Binary");
23416    }
23417    elsif($SourceOnly) {
23418        compareAPIs("Source");
23419    }
23420    exitReport();
23421}
23422
23423scenario();
23424