abi-compliance-checker.pl revision 6ed91e7e3638a38533dcceda69075ae1d641770e
1#!/usr/bin/perl
2###########################################################################
3# ABI Compliance Checker (ABICC) 1.99.10
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) 2012-2013 ROSA Laboratory
9# Copyright (C) 2013-2015 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 (5.8 or newer)
23#    - Ctags (5.8 or newer)
24#
25#  Mac OS X
26#    - Xcode (g++, c++filt, otool, nm)
27#    - Ctags (5.8 or newer)
28#
29#  MS Windows
30#    - MinGW (3.0-4.7, 4.8.3, 4.9 or newer)
31#    - MS Visual C++ (dumpbin, undname, cl)
32#    - Active Perl 5 (5.8 or newer)
33#    - Sigcheck v1.71 or newer
34#    - Info-ZIP 3.0 (zip, unzip)
35#    - Ctags (5.8 or newer)
36#    - Add tool locations to the PATH environment variable
37#    - Run vsvars32.bat (C:\Microsoft Visual Studio 9.0\Common7\Tools\)
38#
39# COMPATIBILITY
40# =============
41#  ABI Dumper >= 0.99.9
42#
43#
44# This program is free software: you can redistribute it and/or modify
45# it under the terms of the GNU General Public License or the GNU Lesser
46# General Public License as published by the Free Software Foundation.
47#
48# This program is distributed in the hope that it will be useful,
49# but WITHOUT ANY WARRANTY; without even the implied warranty of
50# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
51# GNU General Public License for more details.
52#
53# You should have received a copy of the GNU General Public License
54# and the GNU Lesser General Public License along with this program.
55# If not, see <http://www.gnu.org/licenses/>.
56###########################################################################
57use Getopt::Long;
58Getopt::Long::Configure ("posix_default", "no_ignore_case");
59use File::Path qw(mkpath rmtree);
60use File::Temp qw(tempdir);
61use File::Copy qw(copy move);
62use Cwd qw(abs_path cwd realpath);
63use Storable qw(dclone);
64use Data::Dumper;
65use Config;
66
67my $TOOL_VERSION = "1.99.10";
68my $ABI_DUMP_VERSION = "3.2";
69my $XML_REPORT_VERSION = "1.2";
70my $XML_ABI_DUMP_VERSION = "1.2";
71my $OSgroup = get_OSgroup();
72my $ORIG_DIR = cwd();
73my $TMP_DIR = tempdir(CLEANUP=>1);
74
75# Internal modules
76my $MODULES_DIR = get_Modules();
77push(@INC, get_dirname($MODULES_DIR));
78# Rules DB
79my %RULES_PATH = (
80    "Binary" => $MODULES_DIR."/RulesBin.xml",
81    "Source" => $MODULES_DIR."/RulesSrc.xml");
82
83my ($Help, $ShowVersion, %Descriptor, $TargetLibraryName,
84$TestTool, $DumpAPI, $SymbolsListPath, $CheckHeadersOnly_Opt, $UseDumps,
85$CheckObjectsOnly_Opt, $AppPath, $StrictCompat, $DumpVersion, $ParamNamesPath,
86%RelativeDirectory, $TargetTitle, $TestDump, $LoggingPath,
87%TargetVersion, $InfoMsg, $CrossGcc, %OutputLogPath,
88$OutputReportPath, $OutputDumpPath, $ShowRetVal, $SystemRoot_Opt, $DumpSystem,
89$CmpSystems, $TargetLibsPath, $Debug, $CrossPrefix, $UseStaticLibs, $NoStdInc,
90$TargetComponent_Opt, $TargetSysInfo, $TargetHeader, $ExtendedCheck, $Quiet,
91$SkipHeadersPath, $CppCompat, $LogMode, $StdOut, $ListAffected, $ReportFormat,
92$UserLang, $TargetHeadersPath, $BinaryOnly, $SourceOnly, $BinaryReportPath,
93$SourceReportPath, $UseXML, $SortDump, $DumpFormat,
94$ExtraInfo, $ExtraDump, $Force, $Tolerance, $Tolerant, $SkipSymbolsListPath,
95$CheckInfo, $Quick, $AffectLimit, $AllAffected, $CppIncompat, $SkipInternal,
96$TargetArch, $GccOptions, $TypesListPath);
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 = "http://lvc.github.io/abi-compliance-checker/";
150
151my $ShortUsage = "ABI Compliance Checker (ABICC) $TOOL_VERSION
152A tool for checking backward compatibility of a C/C++ library API
153Copyright (C) 2015 Andrey Ponomarenko's ABI Laboratory
154License: GNU LGPL or GNU GPL
155
156Usage: $CmdName [options]
157Example: $CmdName -lib NAME -old OLD.xml -new NEW.xml
158
159OLD.xml and NEW.xml are XML-descriptors:
160
161    <version>
162        1.0
163    </version>
164
165    <headers>
166        /path/to/headers/
167    </headers>
168
169    <libs>
170        /path/to/libraries/
171    </libs>
172
173More info: $CmdName --help\n";
174
175if($#ARGV==-1)
176{
177    printMsg("INFO", $ShortUsage);
178    exit(0);
179}
180
181foreach (2 .. $#ARGV)
182{ # correct comma separated options
183    if($ARGV[$_-1] eq ",")
184    {
185        $ARGV[$_-2].=",".$ARGV[$_];
186        splice(@ARGV, $_-1, 2);
187    }
188    elsif($ARGV[$_-1]=~/,\Z/)
189    {
190        $ARGV[$_-1].=$ARGV[$_];
191        splice(@ARGV, $_, 1);
192    }
193    elsif($ARGV[$_]=~/\A,/
194    and $ARGV[$_] ne ",")
195    {
196        $ARGV[$_-1].=$ARGV[$_];
197        splice(@ARGV, $_, 1);
198    }
199}
200
201GetOptions("h|help!" => \$Help,
202  "i|info!" => \$InfoMsg,
203  "v|version!" => \$ShowVersion,
204  "dumpversion!" => \$DumpVersion,
205# general options
206  "l|lib|library=s" => \$TargetLibraryName,
207  "d1|old|o=s" => \$Descriptor{1}{"Path"},
208  "d2|new|n=s" => \$Descriptor{2}{"Path"},
209  "dump|dump-abi|dump_abi=s" => \$DumpAPI,
210# extra options
211  "app|application=s" => \$AppPath,
212  "static-libs!" => \$UseStaticLibs,
213  "gcc-path|cross-gcc=s" => \$CrossGcc,
214  "gcc-prefix|cross-prefix=s" => \$CrossPrefix,
215  "gcc-options=s" => \$GccOptions,
216  "sysroot=s" => \$SystemRoot_Opt,
217  "v1|version1|vnum=s" => \$TargetVersion{1},
218  "v2|version2=s" => \$TargetVersion{2},
219  "s|strict!" => \$StrictCompat,
220  "symbols-list=s" => \$SymbolsListPath,
221  "types-list=s" => \$TypesListPath,
222  "skip-symbols=s" => \$SkipSymbolsListPath,
223  "headers-list=s" => \$TargetHeadersPath,
224  "skip-headers=s" => \$SkipHeadersPath,
225  "header=s" => \$TargetHeader,
226  "headers-only|headers_only!" => \$CheckHeadersOnly_Opt,
227  "objects-only!" => \$CheckObjectsOnly_Opt,
228  "show-retval!" => \$ShowRetVal,
229  "use-dumps!" => \$UseDumps,
230  "nostdinc!" => \$NoStdInc,
231  "dump-system=s" => \$DumpSystem,
232  "sysinfo=s" => \$TargetSysInfo,
233  "cmp-systems!" => \$CmpSystems,
234  "libs-list=s" => \$TargetLibsPath,
235  "ext|extended!" => \$ExtendedCheck,
236  "q|quiet!" => \$Quiet,
237  "stdout!" => \$StdOut,
238  "report-format=s" => \$ReportFormat,
239  "dump-format=s" => \$DumpFormat,
240  "xml!" => \$UseXML,
241  "lang=s" => \$UserLang,
242  "arch=s" => \$TargetArch,
243  "binary|bin|abi!" => \$BinaryOnly,
244  "source|src|api!" => \$SourceOnly,
245  "limit-affected|affected-limit=s" => \$AffectLimit,
246# other options
247  "test!" => \$TestTool,
248  "test-dump!" => \$TestDump,
249  "debug!" => \$Debug,
250  "cpp-compatible!" => \$CppCompat,
251  "cpp-incompatible!" => \$CppIncompat,
252  "p|params=s" => \$ParamNamesPath,
253  "relpath1|relpath=s" => \$RelativeDirectory{1},
254  "relpath2=s" => \$RelativeDirectory{2},
255  "dump-path=s" => \$OutputDumpPath,
256  "sort!" => \$SortDump,
257  "report-path=s" => \$OutputReportPath,
258  "bin-report-path=s" => \$BinaryReportPath,
259  "src-report-path=s" => \$SourceReportPath,
260  "log-path=s" => \$LoggingPath,
261  "log1-path=s" => \$OutputLogPath{1},
262  "log2-path=s" => \$OutputLogPath{2},
263  "logging-mode=s" => \$LogMode,
264  "list-affected!" => \$ListAffected,
265  "title|l-full|lib-full=s" => \$TargetTitle,
266  "component=s" => \$TargetComponent_Opt,
267  "extra-info=s" => \$ExtraInfo,
268  "extra-dump!" => \$ExtraDump,
269  "force!" => \$Force,
270  "tolerance=s" => \$Tolerance,
271  "tolerant!" => \$Tolerant,
272  "check!" => \$CheckInfo,
273  "quick!" => \$Quick,
274  "all-affected!" => \$AllAffected,
275  "skip-internal=s" => \$SkipInternal
276) or ERR_MESSAGE();
277
278sub ERR_MESSAGE()
279{
280    printMsg("INFO", "\n".$ShortUsage);
281    exit($ERROR_CODE{"Error"});
282}
283
284my $LIB_TYPE = $UseStaticLibs?"static":"dynamic";
285my $SLIB_TYPE = $LIB_TYPE;
286if($OSgroup!~/macos|windows/ and $SLIB_TYPE eq "dynamic")
287{ # show as "shared" library
288    $SLIB_TYPE = "shared";
289}
290my $LIB_EXT = getLIB_EXT($OSgroup);
291my $AR_EXT = getAR_EXT($OSgroup);
292my $BYTE_SIZE = 8;
293my $COMMON_LOG_PATH = "logs/run.log";
294
295my $HelpMessage="
296NAME:
297  ABI Compliance Checker ($CmdName)
298  Check backward compatibility of a C/C++ library API
299
300DESCRIPTION:
301  ABI Compliance Checker (ABICC) is a tool for checking backward binary and
302  source-level compatibility of a $SLIB_TYPE C/C++ library. The tool checks
303  header files and $SLIB_TYPE libraries (*.$LIB_EXT) of old and new versions and
304  analyzes changes in API and ABI (ABI=API+compiler ABI) that may break binary
305  and/or source-level compatibility: changes in calling stack, v-table changes,
306  removed symbols, renamed fields, etc. Binary incompatibility may result in
307  crashing or incorrect behavior of applications built with an old version of
308  a library if they run on a new one. Source incompatibility may result in
309  recompilation errors with a new library version.
310
311  The tool is intended for developers of software libraries and maintainers
312  of operating systems who are interested in ensuring backward compatibility,
313  i.e. allow old applications to run or to be recompiled with newer library
314  versions.
315
316  Also the tool can be used by ISVs for checking applications portability to
317  new library versions. Found issues can be taken into account when adapting
318  the application to a new library version.
319
320  This tool is free software: you can redistribute it and/or modify it
321  under the terms of the GNU LGPL or GNU GPL.
322
323USAGE:
324  $CmdName [options]
325
326EXAMPLE:
327  $CmdName -lib NAME -old OLD.xml -new NEW.xml
328
329  OLD.xml and NEW.xml are XML-descriptors:
330
331    <version>
332        1.0
333    </version>
334
335    <headers>
336        /path1/to/header(s)/
337        /path2/to/header(s)/
338         ...
339    </headers>
340
341    <libs>
342        /path1/to/library(ies)/
343        /path2/to/library(ies)/
344         ...
345    </libs>
346
347INFORMATION OPTIONS:
348  -h|-help
349      Print this help.
350
351  -i|-info
352      Print complete info.
353
354  -v|-version
355      Print version information.
356
357  -dumpversion
358      Print the tool version ($TOOL_VERSION) and don't do anything else.
359
360GENERAL OPTIONS:
361  -l|-lib|-library NAME
362      Library name (without version).
363
364  -d1|-old|-o PATH
365      Descriptor of 1st (old) library version.
366      It may be one of the following:
367
368         1. XML-descriptor (VERSION.xml file):
369
370              <version>
371                  1.0
372              </version>
373
374              <headers>
375                  /path1/to/header(s)/
376                  /path2/to/header(s)/
377                   ...
378              </headers>
379
380              <libs>
381                  /path1/to/library(ies)/
382                  /path2/to/library(ies)/
383                   ...
384              </libs>
385
386                 ... (XML-descriptor template
387                         can be generated by -d option)
388
389         2. ABI dump generated by -dump option
390         3. Directory with headers and/or $SLIB_TYPE libraries
391         4. Single header file
392         5. Single $SLIB_TYPE library
393         6. Comma separated list of headers and/or libraries
394
395      If you are using an 2-6 descriptor types then you should
396      specify version numbers with -v1 and -v2 options too.
397
398      For more information, please see:
399        http://ispras.linuxbase.org/index.php/Library_Descriptor
400
401  -d2|-new|-n PATH
402      Descriptor of 2nd (new) library version.
403
404  -dump|-dump-abi PATH
405      Create library ABI dump for the input XML descriptor. You can
406      transfer it anywhere and pass instead of the descriptor. Also
407      it can be used for debugging the tool.
408
409      Supported versions of ABI dump: 2.0<=V<=$ABI_DUMP_VERSION\n";
410
411sub HELP_MESSAGE() {
412    printMsg("INFO", $HelpMessage."
413MORE INFO:
414     $CmdName --info\n");
415}
416
417sub INFO_MESSAGE()
418{
419    printMsg("INFO", "$HelpMessage
420EXTRA OPTIONS:
421  -app|-application PATH
422      This option allows to specify the application that should be checked
423      for portability to the new library version.
424
425  -static-libs
426      Check static libraries instead of the shared ones. The <libs> section
427      of the XML-descriptor should point to static libraries location.
428
429  -cross-gcc|-gcc-path PATH
430      Path to the cross GCC compiler to use instead of the usual (host) GCC.
431
432  -cross-prefix|-gcc-prefix PREFIX
433      GCC toolchain prefix.
434
435  -sysroot DIR
436      Specify the alternative root directory. The tool will search for include
437      paths in the DIR/usr/include and DIR/usr/lib directories.
438
439  -v1|-version1 NUM
440      Specify 1st library version outside the descriptor. This option is needed
441      if you have preferred an alternative descriptor type (see -d1 option).
442
443      In general case you should specify it in the XML-descriptor:
444          <version>
445              VERSION
446          </version>
447
448  -v2|-version2 NUM
449      Specify 2nd library version outside the descriptor.
450
451  -vnum NUM
452      Specify the library version in the generated ABI dump. The <version> section
453      of the input XML descriptor will be overwritten in this case.
454
455  -s|-strict
456      Treat all compatibility warnings as problems. Add a number of \"Low\"
457      severity problems to the return value of the tool.
458
459  -headers-only
460      Check header files without $SLIB_TYPE libraries. It is easy to run, but may
461      provide a low quality compatibility report with false positives and
462      without detecting of added/removed symbols.
463
464      Alternatively you can write \"none\" word to the <libs> section
465      in the XML-descriptor:
466          <libs>
467              none
468          </libs>
469
470  -objects-only
471      Check $SLIB_TYPE libraries without header files. It is easy to run, but may
472      provide a low quality compatibility report with false positives and
473      without analysis of changes in parameters and data types.
474
475      Alternatively you can write \"none\" word to the <headers> section
476      in the XML-descriptor:
477          <headers>
478              none
479          </headers>
480
481  -show-retval
482      Show the symbol's return type in the report.
483
484  -symbols-list PATH
485      This option allows to specify a file with a list of symbols (mangled
486      names in C++) that should be checked. Other symbols will not be checked.
487
488  -types-list PATH
489      This option allows to specify a file with a list of types that should
490      be checked. Other types will not be checked.
491
492  -skip-symbols PATH
493      The list of symbols that should NOT be checked.
494
495  -headers-list PATH
496      The file with a list of headers, that should be checked/dumped.
497
498  -skip-headers PATH
499      The file with the list of header files, that should not be checked.
500
501  -header NAME
502      Check/Dump ABI of this header only.
503
504  -use-dumps
505      Make dumps for two versions of a library and compare dumps. This should
506      increase the performance of the tool and decrease the system memory usage.
507
508  -nostdinc
509      Do not search in GCC standard system directories for header files.
510
511  -dump-system NAME -sysroot DIR
512      Find all the shared libraries and header files in DIR directory,
513      create XML descriptors and make ABI dumps for each library. The result
514      set of ABI dumps can be compared (--cmp-systems) with the other one
515      created for other version of operating system in order to check them for
516      compatibility. Do not forget to specify -cross-gcc option if your target
517      system requires some specific version of GCC compiler (different from
518      the host GCC). The system ABI dump will be generated to:
519          sys_dumps/NAME/ARCH
520
521  -dump-system DESCRIPTOR.xml
522      The same as the previous option but takes an XML descriptor of the target
523      system as input, where you should describe it:
524
525          /* Primary sections */
526
527          <name>
528              /* Name of the system */
529          </name>
530
531          <headers>
532              /* The list of paths to header files and/or
533                 directories with header files, one per line */
534          </headers>
535
536          <libs>
537              /* The list of paths to shared libraries and/or
538                 directories with shared libraries, one per line */
539          </libs>
540
541          /* Optional sections */
542
543          <search_headers>
544              /* List of directories to be searched
545                 for header files to automatically
546                 generate include paths, one per line */
547          </search_headers>
548
549          <search_libs>
550              /* List of directories to be searched
551                 for shared libraries to resolve
552                 dependencies, one per line */
553          </search_libs>
554
555          <tools>
556              /* List of directories with tools used
557                 for analysis (GCC toolchain), one per line */
558          </tools>
559
560          <cross_prefix>
561              /* GCC toolchain prefix.
562                 Examples:
563                     arm-linux-gnueabi
564                     arm-none-symbianelf */
565          </cross_prefix>
566
567          <gcc_options>
568              /* Additional GCC options, one per line */
569          </gcc_options>
570
571  -sysinfo DIR
572      This option should be used with -dump-system option to dump
573      ABI of operating systems and configure the dumping process.
574      You can find a sample in the package:
575          modules/Targets/{unix, symbian, windows}
576
577  -cmp-systems -d1 sys_dumps/NAME1/ARCH -d2 sys_dumps/NAME2/ARCH
578      Compare two system ABI dumps. Create compatibility reports for each
579      library and the common HTML report including the summary of test
580      results for all checked libraries. Report will be generated to:
581          sys_compat_reports/NAME1_to_NAME2/ARCH
582
583  -libs-list PATH
584      The file with a list of libraries, that should be dumped by
585      the -dump-system option or should be checked by the -cmp-systems option.
586
587  -ext|-extended
588      If your library A is supposed to be used by other library B and you
589      want to control the ABI of B, then you should enable this option. The
590      tool will check for changes in all data types, even if they are not
591      used by any function in the library A. Such data types are not part
592      of the A library ABI, but may be a part of the ABI of the B library.
593
594      The short scheme is:
595        app C (broken) -> lib B (broken ABI) -> lib A (stable ABI)
596
597  -q|-quiet
598      Print all messages to the file instead of stdout and stderr.
599      Default path (can be changed by -log-path option):
600          $COMMON_LOG_PATH
601
602  -stdout
603      Print analysis results (compatibility reports and ABI dumps) to stdout
604      instead of creating a file. This would allow piping data to other programs.
605
606  -report-format FMT
607      Change format of compatibility report.
608      Formats:
609        htm - HTML format (default)
610        xml - XML format
611
612  -dump-format FMT
613      Change format of ABI dump.
614      Formats:
615        perl - Data::Dumper format (default)
616        xml - XML format
617
618  -xml
619      Alias for: --report-format=xml or --dump-format=xml
620
621  -lang LANG
622      Set library language (C or C++). You can use this option if the tool
623      cannot auto-detect a language. This option may be useful for checking
624      C-library headers (--lang=C) in --headers-only or --extended modes.
625
626  -arch ARCH
627      Set library architecture (x86, x86_64, ia64, arm, ppc32, ppc64, s390,
628      ect.). The option is useful if the tool cannot detect correct architecture
629      of the input objects.
630
631  -binary|-bin|-abi
632      Show \"Binary\" compatibility problems only.
633      Generate report to:
634        compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
635
636  -source|-src|-api
637      Show \"Source\" compatibility problems only.
638      Generate report to:
639        compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
640
641  -limit-affected LIMIT
642      The maximum number of affected symbols listed under the description
643      of the changed type in the report.
644
645OTHER OPTIONS:
646  -test
647      Run internal tests. Create two binary incompatible versions of a sample
648      library and run the tool to check them for compatibility. This option
649      allows to check if the tool works correctly in the current environment.
650
651  -test-dump
652      Test ability to create, read and compare ABI dumps.
653
654  -debug
655      Debugging mode. Print debug info on the screen. Save intermediate
656      analysis stages in the debug directory:
657          debug/LIB_NAME/VERSION/
658
659      Also consider using --dump option for debugging the tool.
660
661  -cpp-compatible
662      If your header files are written in C language and can be compiled
663      by the G++ compiler (i.e. don't use C++ keywords), then you can tell
664      the tool about this and speedup the analysis.
665
666  -cpp-incompatible
667      Set this option if input C header files use C++ keywords.
668
669  -p|-params PATH
670      Path to file with the function parameter names. It can be used
671      for improving report view if the library header files have no
672      parameter names. File format:
673
674            func1;param1;param2;param3 ...
675            func2;param1;param2;param3 ...
676             ...
677
678  -relpath PATH
679      Replace {RELPATH} macros to PATH in the XML-descriptor used
680      for dumping the library ABI (see -dump option).
681
682  -relpath1 PATH
683      Replace {RELPATH} macros to PATH in the 1st XML-descriptor (-d1).
684
685  -relpath2 PATH
686      Replace {RELPATH} macros to PATH in the 2nd XML-descriptor (-d2).
687
688  -dump-path PATH
689      Specify a *.abi.$AR_EXT or *.abi file path where to generate an ABI dump.
690      Default:
691          abi_dumps/LIB_NAME/LIB_NAME_VERSION.abi.$AR_EXT
692
693  -sort
694      Enable sorting of data in ABI dumps.
695
696  -report-path PATH
697      Path to compatibility report.
698      Default:
699          compat_reports/LIB_NAME/V1_to_V2/compat_report.html
700
701  -bin-report-path PATH
702      Path to \"Binary\" compatibility report.
703      Default:
704          compat_reports/LIB_NAME/V1_to_V2/abi_compat_report.html
705
706  -src-report-path PATH
707      Path to \"Source\" compatibility report.
708      Default:
709          compat_reports/LIB_NAME/V1_to_V2/src_compat_report.html
710
711  -log-path PATH
712      Log path for all messages.
713      Default:
714          logs/LIB_NAME/VERSION/log.txt
715
716  -log1-path PATH
717      Log path for 1st version of a library.
718      Default:
719          logs/LIB_NAME/V1/log.txt
720
721  -log2-path PATH
722      Log path for 2nd version of a library.
723      Default:
724          logs/LIB_NAME/V2/log.txt
725
726  -logging-mode MODE
727      Change logging mode.
728      Modes:
729        w - overwrite old logs (default)
730        a - append old logs
731        n - do not write any logs
732
733  -list-affected
734      Generate file with the list of incompatible
735      symbols beside the HTML compatibility report.
736      Use 'c++filt \@file' command from GNU binutils
737      to unmangle C++ symbols in the generated file.
738      Default names:
739          abi_affected.txt
740          src_affected.txt
741
742  -component NAME
743      The component name in the title and summary of the HTML report.
744      Default:
745          library
746
747  -title NAME
748      Change library name in the report title to NAME. By default
749      will be displayed a name specified by -l option.
750
751  -extra-info DIR
752      Dump extra info to DIR.
753
754  -extra-dump
755      Create extended ABI dump containing all symbols
756      from the translation unit.
757
758  -force
759      Try to use this option if the tool doesn't work.
760
761  -tolerance LEVEL
762      Apply a set of heuristics to successfully compile input
763      header files. You can enable several tolerance levels by
764      joining them into one string (e.g. 13, 124, etc.).
765      Levels:
766          1 - skip non-Linux headers (e.g. win32_*.h, etc.)
767          2 - skip internal headers (e.g. *_p.h, impl/*.h, etc.)
768          3 - skip headers that iclude non-Linux headers
769          4 - skip headers included by others
770
771  -tolerant
772      Enable highest tolerance level [1234].
773
774  -check
775      Check completeness of the ABI dump.
776
777  -quick
778      Quick analysis. Disable check of some template instances.
779
780  -skip-internal PATTERN
781      Do not check internal interfaces matched by the pattern.
782
783REPORT:
784    Compatibility report will be generated to:
785        compat_reports/LIB_NAME/V1_to_V2/compat_report.html
786
787    Log will be generated to:
788        logs/LIB_NAME/V1/log.txt
789        logs/LIB_NAME/V2/log.txt
790
791EXIT CODES:
792    0 - Compatible. The tool has run without any errors.
793    non-zero - Incompatible or the tool has run with errors.
794
795MORE INFORMATION:
796    ".$HomePage."\n");
797}
798
799my %Operator_Indication = (
800    "not" => "~",
801    "assign" => "=",
802    "andassign" => "&=",
803    "orassign" => "|=",
804    "xorassign" => "^=",
805    "or" => "|",
806    "xor" => "^",
807    "addr" => "&",
808    "and" => "&",
809    "lnot" => "!",
810    "eq" => "==",
811    "ne" => "!=",
812    "lt" => "<",
813    "lshift" => "<<",
814    "lshiftassign" => "<<=",
815    "rshiftassign" => ">>=",
816    "call" => "()",
817    "mod" => "%",
818    "modassign" => "%=",
819    "subs" => "[]",
820    "land" => "&&",
821    "lor" => "||",
822    "rshift" => ">>",
823    "ref" => "->",
824    "le" => "<=",
825    "deref" => "*",
826    "mult" => "*",
827    "preinc" => "++",
828    "delete" => " delete",
829    "vecnew" => " new[]",
830    "vecdelete" => " delete[]",
831    "predec" => "--",
832    "postinc" => "++",
833    "postdec" => "--",
834    "plusassign" => "+=",
835    "plus" => "+",
836    "minus" => "-",
837    "minusassign" => "-=",
838    "gt" => ">",
839    "ge" => ">=",
840    "new" => " new",
841    "multassign" => "*=",
842    "divassign" => "/=",
843    "div" => "/",
844    "neg" => "-",
845    "pos" => "+",
846    "memref" => "->*",
847    "compound" => "," );
848
849my %UnknownOperator;
850
851my %NodeType= (
852  "array_type" => "Array",
853  "binfo" => "Other",
854  "boolean_type" => "Intrinsic",
855  "complex_type" => "Intrinsic",
856  "const_decl" => "Other",
857  "enumeral_type" => "Enum",
858  "field_decl" => "Other",
859  "function_decl" => "Other",
860  "function_type" => "FunctionType",
861  "identifier_node" => "Other",
862  "integer_cst" => "Other",
863  "integer_type" => "Intrinsic",
864  "vector_type" => "Vector",
865  "method_type" => "MethodType",
866  "namespace_decl" => "Other",
867  "parm_decl" => "Other",
868  "pointer_type" => "Pointer",
869  "real_cst" => "Other",
870  "real_type" => "Intrinsic",
871  "record_type" => "Struct",
872  "reference_type" => "Ref",
873  "string_cst" => "Other",
874  "template_decl" => "Other",
875  "template_type_parm" => "TemplateParam",
876  "typename_type" => "TypeName",
877  "sizeof_expr" => "SizeOf",
878  "tree_list" => "Other",
879  "tree_vec" => "Other",
880  "type_decl" => "Other",
881  "union_type" => "Union",
882  "var_decl" => "Other",
883  "void_type" => "Intrinsic",
884  "nop_expr" => "Other", #
885  "addr_expr" => "Other", #
886  "offset_type" => "Other" );
887
888my %CppKeywords_C = map {$_=>1} (
889    # C++ 2003 keywords
890    "public",
891    "protected",
892    "private",
893    "default",
894    "template",
895    "new",
896    #"asm",
897    "dynamic_cast",
898    "auto",
899    "try",
900    "namespace",
901    "typename",
902    "using",
903    "reinterpret_cast",
904    "friend",
905    "class",
906    "virtual",
907    "const_cast",
908    "mutable",
909    "static_cast",
910    "export",
911    # C++0x keywords
912    "noexcept",
913    "nullptr",
914    "constexpr",
915    "static_assert",
916    "explicit",
917    # cannot be used as a macro name
918    # as it is an operator in C++
919    "and",
920    #"and_eq",
921    "not",
922    #"not_eq",
923    "or"
924    #"or_eq",
925    #"bitand",
926    #"bitor",
927    #"xor",
928    #"xor_eq",
929    #"compl"
930);
931
932my %CppKeywords_F = map {$_=>1} (
933    "delete",
934    "catch",
935    "alignof",
936    "thread_local",
937    "decltype",
938    "typeid"
939);
940
941my %CppKeywords_O = map {$_=>1} (
942    "bool",
943    "register",
944    "inline",
945    "operator"
946);
947
948my %CppKeywords_A = map {$_=>1} (
949    "this",
950    "throw",
951    "template"
952);
953
954foreach (keys(%CppKeywords_C),
955keys(%CppKeywords_F),
956keys(%CppKeywords_O)) {
957    $CppKeywords_A{$_}=1;
958}
959
960# Header file extensions as described by gcc
961my $HEADER_EXT = "h|hh|hp|hxx|hpp|h\\+\\+";
962
963my %IntrinsicMangling = (
964    "void" => "v",
965    "bool" => "b",
966    "wchar_t" => "w",
967    "char" => "c",
968    "signed char" => "a",
969    "unsigned char" => "h",
970    "short" => "s",
971    "unsigned short" => "t",
972    "int" => "i",
973    "unsigned int" => "j",
974    "long" => "l",
975    "unsigned long" => "m",
976    "long long" => "x",
977    "__int64" => "x",
978    "unsigned long long" => "y",
979    "__int128" => "n",
980    "unsigned __int128" => "o",
981    "float" => "f",
982    "double" => "d",
983    "long double" => "e",
984    "__float80" => "e",
985    "__float128" => "g",
986    "..." => "z"
987);
988
989my %IntrinsicNames = map {$_=>1} keys(%IntrinsicMangling);
990
991my %StdcxxMangling = (
992    "3std"=>"St",
993    "3std9allocator"=>"Sa",
994    "3std12basic_string"=>"Sb",
995    "3std12basic_stringIcE"=>"Ss",
996    "3std13basic_istreamIcE"=>"Si",
997    "3std13basic_ostreamIcE"=>"So",
998    "3std14basic_iostreamIcE"=>"Sd"
999);
1000
1001my $DEFAULT_STD_PARMS = "std::(allocator|less|char_traits|regex_traits|istreambuf_iterator|ostreambuf_iterator)";
1002my %DEFAULT_STD_ARGS = map {$_=>1} ("_Alloc", "_Compare", "_Traits", "_Rx_traits", "_InIter", "_OutIter");
1003
1004my $ADD_TMPL_INSTANCES = 1;
1005my $EMERGENCY_MODE_48 = 0;
1006
1007my %ConstantSuffix = (
1008    "unsigned int"=>"u",
1009    "long"=>"l",
1010    "unsigned long"=>"ul",
1011    "long long"=>"ll",
1012    "unsigned long long"=>"ull"
1013);
1014
1015my %ConstantSuffixR =
1016reverse(%ConstantSuffix);
1017
1018my %OperatorMangling = (
1019    "~" => "co",
1020    "=" => "aS",
1021    "|" => "or",
1022    "^" => "eo",
1023    "&" => "an",#ad (addr)
1024    "==" => "eq",
1025    "!" => "nt",
1026    "!=" => "ne",
1027    "<" => "lt",
1028    "<=" => "le",
1029    "<<" => "ls",
1030    "<<=" => "lS",
1031    ">" => "gt",
1032    ">=" => "ge",
1033    ">>" => "rs",
1034    ">>=" => "rS",
1035    "()" => "cl",
1036    "%" => "rm",
1037    "[]" => "ix",
1038    "&&" => "aa",
1039    "||" => "oo",
1040    "*" => "ml",#de (deref)
1041    "++" => "pp",#
1042    "--" => "mm",#
1043    "new" => "nw",
1044    "delete" => "dl",
1045    "new[]" => "na",
1046    "delete[]" => "da",
1047    "+=" => "pL",
1048    "+" => "pl",#ps (pos)
1049    "-" => "mi",#ng (neg)
1050    "-=" => "mI",
1051    "*=" => "mL",
1052    "/=" => "dV",
1053    "&=" => "aN",
1054    "|=" => "oR",
1055    "%=" => "rM",
1056    "^=" => "eO",
1057    "/" => "dv",
1058    "->*" => "pm",
1059    "->" => "pt",#rf (ref)
1060    "," => "cm",
1061    "?" => "qu",
1062    "." => "dt",
1063    "sizeof"=> "sz"#st
1064);
1065
1066my %Intrinsic_Keywords = map {$_=>1} (
1067    "true",
1068    "false",
1069    "_Bool",
1070    "_Complex",
1071    "const",
1072    "int",
1073    "long",
1074    "void",
1075    "short",
1076    "float",
1077    "volatile",
1078    "restrict",
1079    "unsigned",
1080    "signed",
1081    "char",
1082    "double",
1083    "class",
1084    "struct",
1085    "union",
1086    "enum"
1087);
1088
1089my %GlibcHeader = map {$_=>1} (
1090    "aliases.h",
1091    "argp.h",
1092    "argz.h",
1093    "assert.h",
1094    "cpio.h",
1095    "ctype.h",
1096    "dirent.h",
1097    "envz.h",
1098    "errno.h",
1099    "error.h",
1100    "execinfo.h",
1101    "fcntl.h",
1102    "fstab.h",
1103    "ftw.h",
1104    "glob.h",
1105    "grp.h",
1106    "iconv.h",
1107    "ifaddrs.h",
1108    "inttypes.h",
1109    "langinfo.h",
1110    "limits.h",
1111    "link.h",
1112    "locale.h",
1113    "malloc.h",
1114    "math.h",
1115    "mntent.h",
1116    "monetary.h",
1117    "nl_types.h",
1118    "obstack.h",
1119    "printf.h",
1120    "pwd.h",
1121    "regex.h",
1122    "sched.h",
1123    "search.h",
1124    "setjmp.h",
1125    "shadow.h",
1126    "signal.h",
1127    "spawn.h",
1128    "stdarg.h",
1129    "stdint.h",
1130    "stdio.h",
1131    "stdlib.h",
1132    "string.h",
1133    "strings.h",
1134    "tar.h",
1135    "termios.h",
1136    "time.h",
1137    "ulimit.h",
1138    "unistd.h",
1139    "utime.h",
1140    "wchar.h",
1141    "wctype.h",
1142    "wordexp.h" );
1143
1144my %GlibcDir = map {$_=>1} (
1145    "arpa",
1146    "bits",
1147    "gnu",
1148    "netinet",
1149    "net",
1150    "nfs",
1151    "rpc",
1152    "sys",
1153    "linux" );
1154
1155my %WinHeaders = map {$_=>1} (
1156    "dos.h",
1157    "process.h",
1158    "winsock.h",
1159    "config-win.h",
1160    "mem.h",
1161    "windows.h",
1162    "winsock2.h",
1163    "crtdbg.h",
1164    "ws2tcpip.h"
1165);
1166
1167my %ObsoleteHeaders = map {$_=>1} (
1168    "iostream.h",
1169    "fstream.h"
1170);
1171
1172my %AlienHeaders = map {$_=>1} (
1173 # Solaris
1174    "thread.h",
1175    "sys/atomic.h",
1176 # HPUX
1177    "sys/stream.h",
1178 # Symbian
1179    "AknDoc.h",
1180 # Atari ST
1181    "ext.h",
1182    "tos.h",
1183 # MS-DOS
1184    "alloc.h",
1185 # Sparc
1186    "sys/atomic.h"
1187);
1188
1189my %ConfHeaders = map {$_=>1} (
1190    "atomic",
1191    "conf.h",
1192    "config.h",
1193    "configure.h",
1194    "build.h",
1195    "setup.h"
1196);
1197
1198my %LocalIncludes = map {$_=>1} (
1199    "/usr/local/include",
1200    "/usr/local" );
1201
1202my %OS_AddPath=(
1203# These paths are needed if the tool cannot detect them automatically
1204    "macos"=>{
1205        "include"=>[
1206            "/Library",
1207            "/Developer/usr/include"
1208        ],
1209        "lib"=>[
1210            "/Library",
1211            "/Developer/usr/lib"
1212        ],
1213        "bin"=>[
1214            "/Developer/usr/bin"
1215        ]
1216    },
1217    "beos"=>{
1218    # Haiku has GCC 2.95.3 by default
1219    # try to find GCC>=3.0 in /boot/develop/abi
1220        "include"=>[
1221            "/boot/common",
1222            "/boot/develop"
1223        ],
1224        "lib"=>[
1225            "/boot/common/lib",
1226            "/boot/system/lib",
1227            "/boot/apps"
1228        ],
1229        "bin"=>[
1230            "/boot/common/bin",
1231            "/boot/system/bin",
1232            "/boot/develop/abi"
1233        ]
1234    }
1235);
1236
1237my %Slash_Type=(
1238    "default"=>"/",
1239    "windows"=>"\\"
1240);
1241
1242my $SLASH = $Slash_Type{$OSgroup}?$Slash_Type{$OSgroup}:$Slash_Type{"default"};
1243
1244# Global Variables
1245my %COMMON_LANGUAGE=(
1246  1 => "C",
1247  2 => "C" );
1248
1249my $MAX_COMMAND_LINE_ARGUMENTS = 4096;
1250my $MAX_CPPFILT_FILE_SIZE = 50000;
1251my $CPPFILT_SUPPORT_FILE;
1252
1253my (%WORD_SIZE, %CPU_ARCH, %GCC_VERSION);
1254
1255my $STDCXX_TESTING = 0;
1256my $GLIBC_TESTING = 0;
1257my $CPP_HEADERS = 0;
1258
1259my $CheckHeadersOnly = $CheckHeadersOnly_Opt;
1260my $CheckObjectsOnly = $CheckObjectsOnly_Opt;
1261
1262my $TargetComponent;
1263
1264my $CheckUndefined = 0;
1265
1266# Set Target Component Name
1267if($TargetComponent_Opt) {
1268    $TargetComponent = lc($TargetComponent_Opt);
1269}
1270else
1271{ # default: library
1272  # other components: header, system, ...
1273    $TargetComponent = "library";
1274}
1275
1276my $TOP_REF = "<a class='top_ref' href='#Top'>to the top</a>";
1277
1278my $SystemRoot;
1279
1280my $MAIN_CPP_DIR;
1281my %RESULT;
1282my %LOG_PATH;
1283my %DEBUG_PATH;
1284my %Cache;
1285my %LibInfo;
1286my $COMPILE_ERRORS = 0;
1287my %CompilerOptions;
1288my %CheckedDyLib;
1289my $TargetLibraryShortName = parse_libname($TargetLibraryName, "shortest", $OSgroup);
1290
1291# Constants (#defines)
1292my %Constants;
1293my %SkipConstants;
1294my %EnumConstants;
1295
1296# Extra Info
1297my %SymbolHeader;
1298my %KnownLibs;
1299
1300# Templates
1301my %TemplateInstance;
1302my %BasicTemplate;
1303my %TemplateArg;
1304my %TemplateDecl;
1305my %TemplateMap;
1306
1307# Types
1308my %TypeInfo;
1309my %SkipTypes = (
1310  "1"=>{},
1311  "2"=>{} );
1312my %CheckedTypes;
1313my %TName_Tid;
1314my %EnumMembName_Id;
1315my %NestedNameSpaces = (
1316  "1"=>{},
1317  "2"=>{} );
1318my %VirtualTable;
1319my %VirtualTable_Model;
1320my %ClassVTable;
1321my %ClassVTable_Content;
1322my %VTableClass;
1323my %AllocableClass;
1324my %ClassMethods;
1325my %ClassNames;
1326my %Class_SubClasses;
1327my %OverriddenMethods;
1328my %TypedefToAnon;
1329my $MAX_ID = 0;
1330
1331my %CheckedTypeInfo;
1332
1333# Typedefs
1334my %Typedef_BaseName;
1335my %Typedef_Tr;
1336my %Typedef_Eq;
1337my %StdCxxTypedef;
1338my %MissedTypedef;
1339my %MissedBase;
1340my %MissedBase_R;
1341my %TypeTypedef;
1342
1343# Symbols
1344my %SymbolInfo;
1345my %tr_name;
1346my %mangled_name_gcc;
1347my %mangled_name;
1348my %SkipSymbols = (
1349  "1"=>{},
1350  "2"=>{} );
1351my %SkipNameSpaces = (
1352  "1"=>{},
1353  "2"=>{} );
1354my %AddNameSpaces = (
1355  "1"=>{},
1356  "2"=>{} );
1357my %SymbolsList;
1358my %TypesList;
1359my %SymbolsList_App;
1360my %CheckedSymbols;
1361my %Symbol_Library = (
1362  "1"=>{},
1363  "2"=>{} );
1364my %Library_Symbol = (
1365  "1"=>{},
1366  "2"=>{} );
1367my %DepSymbol_Library = (
1368  "1"=>{},
1369  "2"=>{} );
1370my %DepLibrary_Symbol = (
1371  "1"=>{},
1372  "2"=>{} );
1373my %MangledNames;
1374my %Func_ShortName;
1375my %AddIntParams;
1376my %GlobalDataObject;
1377my %WeakSymbols;
1378my %Library_Needed= (
1379  "1"=>{},
1380  "2"=>{} );
1381
1382# Extra Info
1383my %UndefinedSymbols;
1384my %PreprocessedHeaders;
1385
1386# Headers
1387my %Include_Preamble = (
1388    "1"=>[],
1389    "2"=>[] );
1390my %Registered_Headers;
1391my %Registered_Sources;
1392my %HeaderName_Paths;
1393my %Header_Dependency;
1394my %Include_Neighbors;
1395my %Include_Paths = (
1396    "1"=>[],
1397    "2"=>[] );
1398my %INC_PATH_AUTODETECT = (
1399  "1"=>1,
1400  "2"=>1 );
1401my %Add_Include_Paths = (
1402    "1"=>[],
1403    "2"=>[] );
1404my %Skip_Include_Paths;
1405my %RegisteredDirs;
1406my %Header_ErrorRedirect;
1407my %Header_Includes;
1408my %Header_Includes_R;
1409my %Header_ShouldNotBeUsed;
1410my %RecursiveIncludes;
1411my %Header_Include_Prefix;
1412my %SkipHeaders;
1413my %SkipHeadersList=(
1414  "1"=>{},
1415  "2"=>{} );
1416my %SkipLibs;
1417my %Include_Order;
1418my %TUnit_NameSpaces;
1419my %TUnit_Classes;
1420my %TUnit_Funcs;
1421my %TUnit_Vars;
1422
1423my %CppMode = (
1424  "1"=>0,
1425  "2"=>0 );
1426my %AutoPreambleMode = (
1427  "1"=>0,
1428  "2"=>0 );
1429my %MinGWMode = (
1430  "1"=>0,
1431  "2"=>0 );
1432my %Cpp0xMode = (
1433  "1"=>0,
1434  "2"=>0 );
1435
1436# Shared Objects
1437my %RegisteredObjects;
1438my %RegisteredObjects_Short;
1439my %RegisteredSONAMEs;
1440my %RegisteredObject_Dirs;
1441
1442my %CheckedArch;
1443
1444# System Objects
1445my %SystemObjects;
1446my @DefaultLibPaths;
1447my %DyLib_DefaultPath;
1448
1449# System Headers
1450my %SystemHeaders;
1451my @DefaultCppPaths;
1452my @DefaultGccPaths;
1453my @DefaultIncPaths;
1454my %DefaultCppHeader;
1455my %DefaultGccHeader;
1456my @UsersIncPath;
1457
1458# Merging
1459my %CompleteSignature;
1460my $Version;
1461my %AddedInt;
1462my %RemovedInt;
1463my %AddedInt_Virt;
1464my %RemovedInt_Virt;
1465my %VirtualReplacement;
1466my %ChangedTypedef;
1467my %CompatRules;
1468my %IncompleteRules;
1469my %UnknownRules;
1470my %VTableChanged_M;
1471my %ExtendedSymbols;
1472my %ReturnedClass;
1473my %ParamClass;
1474my %SourceAlternative;
1475my %SourceAlternative_B;
1476my %SourceReplacement;
1477my $CurrentSymbol; # for debugging
1478
1479# Calling Conventions
1480my %UseConv_Real = (
1481  1=>{ "R"=>0, "P"=>0 },
1482  2=>{ "R"=>0, "P"=>0 }
1483);
1484
1485# ABI Dump
1486my %UsedDump;
1487
1488# OS Compliance
1489my %TargetLibs;
1490my %TargetHeaders;
1491
1492# OS Specifics
1493my $OStarget = $OSgroup;
1494my %TargetTools;
1495
1496# Compliance Report
1497my %Type_MaxSeverity;
1498
1499# Recursion locks
1500my @RecurLib;
1501my @RecurTypes;
1502my @RecurTypes_Diff;
1503my @RecurInclude;
1504my @RecurConstant;
1505
1506# System
1507my %SystemPaths = (
1508    "include"=>[],
1509    "lib"=>[],
1510    "bin"=>[]
1511);
1512my @DefaultBinPaths;
1513my $GCC_PATH;
1514
1515# Symbols versioning
1516my %SymVer = (
1517  "1"=>{},
1518  "2"=>{} );
1519
1520# Problem descriptions
1521my %CompatProblems;
1522my %CompatProblems_Constants;
1523my %TotalAffected;
1524
1525# Reports
1526my $ContentID = 1;
1527my $ContentSpanStart = "<span class=\"section\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1528my $ContentSpanStart_Affected = "<span class=\"section_affected\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1529my $ContentSpanStart_Info = "<span class=\"section_info\" onclick=\"javascript:showContent(this, 'CONTENT_ID')\">\n";
1530my $ContentSpanEnd = "</span>\n";
1531my $ContentDivStart = "<div id=\"CONTENT_ID\" style=\"display:none;\">\n";
1532my $ContentDivEnd = "</div>\n";
1533my $Content_Counter = 0;
1534
1535# Modes
1536my $JoinReport = 1;
1537my $DoubleReport = 0;
1538
1539my %Severity_Val=(
1540    "High"=>3,
1541    "Medium"=>2,
1542    "Low"=>1,
1543    "Safe"=>-1
1544);
1545
1546sub get_Modules()
1547{
1548    my $TOOL_DIR = get_dirname($0);
1549    if(not $TOOL_DIR)
1550    { # patch for MS Windows
1551        $TOOL_DIR = ".";
1552    }
1553    my @SEARCH_DIRS = (
1554        # tool's directory
1555        abs_path($TOOL_DIR),
1556        # relative path to modules
1557        abs_path($TOOL_DIR)."/../share/abi-compliance-checker",
1558        # install path
1559        'MODULES_INSTALL_PATH'
1560    );
1561    foreach my $DIR (@SEARCH_DIRS)
1562    {
1563        if(not is_abs($DIR))
1564        { # relative path
1565            $DIR = abs_path($TOOL_DIR)."/".$DIR;
1566        }
1567        if(-d $DIR."/modules") {
1568            return $DIR."/modules";
1569        }
1570    }
1571    exitStatus("Module_Error", "can't find modules");
1572}
1573
1574my %LoadedModules = ();
1575
1576sub loadModule($)
1577{
1578    my $Name = $_[0];
1579    if(defined $LoadedModules{$Name}) {
1580        return;
1581    }
1582    my $Path = $MODULES_DIR."/Internals/$Name.pm";
1583    if(not -f $Path) {
1584        exitStatus("Module_Error", "can't access \'$Path\'");
1585    }
1586    require $Path;
1587    $LoadedModules{$Name} = 1;
1588}
1589
1590sub readModule($$)
1591{
1592    my ($Module, $Name) = @_;
1593    my $Path = $MODULES_DIR."/Internals/$Module/".$Name;
1594    if(not -f $Path) {
1595        exitStatus("Module_Error", "can't access \'$Path\'");
1596    }
1597    return readFile($Path);
1598}
1599
1600sub showPos($)
1601{
1602    my $Number = $_[0];
1603    if(not $Number) {
1604        $Number = 1;
1605    }
1606    else {
1607        $Number = int($Number)+1;
1608    }
1609    if($Number>3) {
1610        return $Number."th";
1611    }
1612    elsif($Number==1) {
1613        return "1st";
1614    }
1615    elsif($Number==2) {
1616        return "2nd";
1617    }
1618    elsif($Number==3) {
1619        return "3rd";
1620    }
1621    else {
1622        return $Number;
1623    }
1624}
1625
1626sub search_Tools($)
1627{
1628    my $Name = $_[0];
1629    return "" if(not $Name);
1630    if(my @Paths = keys(%TargetTools))
1631    {
1632        foreach my $Path (@Paths)
1633        {
1634            if(-f join_P($Path, $Name)) {
1635                return join_P($Path, $Name);
1636            }
1637            if($CrossPrefix)
1638            { # user-defined prefix (arm-none-symbianelf, ...)
1639                my $Candidate = join_P($Path, $CrossPrefix."-".$Name);
1640                if(-f $Candidate) {
1641                    return $Candidate;
1642                }
1643            }
1644        }
1645    }
1646    else {
1647        return "";
1648    }
1649}
1650
1651sub synch_Cmd($)
1652{
1653    my $Name = $_[0];
1654    if(not $GCC_PATH)
1655    { # GCC was not found yet
1656        return "";
1657    }
1658    my $Candidate = $GCC_PATH;
1659    if($Candidate=~s/\bgcc(|\.\w+)\Z/$Name$1/) {
1660        return $Candidate;
1661    }
1662    return "";
1663}
1664
1665sub get_CmdPath($)
1666{
1667    my $Name = $_[0];
1668    return "" if(not $Name);
1669    if(defined $Cache{"get_CmdPath"}{$Name}) {
1670        return $Cache{"get_CmdPath"}{$Name};
1671    }
1672    my %BinUtils = map {$_=>1} (
1673        "c++filt",
1674        "objdump",
1675        "readelf"
1676    );
1677    if($BinUtils{$Name} and $GCC_PATH)
1678    {
1679        if(my $Dir = get_dirname($GCC_PATH)) {
1680            $TargetTools{$Dir}=1;
1681        }
1682    }
1683    my $Path = search_Tools($Name);
1684    if(not $Path and $OSgroup eq "windows") {
1685        $Path = search_Tools($Name.".exe");
1686    }
1687    if(not $Path and $BinUtils{$Name})
1688    {
1689        if($CrossPrefix)
1690        { # user-defined prefix
1691            $Path = search_Cmd($CrossPrefix."-".$Name);
1692        }
1693    }
1694    if(not $Path and $BinUtils{$Name})
1695    {
1696        if(my $Candidate = synch_Cmd($Name))
1697        { # synch with GCC
1698            if($Candidate=~/[\/\\]/)
1699            { # command path
1700                if(-f $Candidate) {
1701                    $Path = $Candidate;
1702                }
1703            }
1704            elsif($Candidate = search_Cmd($Candidate))
1705            { # command name
1706                $Path = $Candidate;
1707            }
1708        }
1709    }
1710    if(not $Path) {
1711        $Path = search_Cmd($Name);
1712    }
1713    if(not $Path and $OSgroup eq "windows")
1714    { # search for *.exe file
1715        $Path=search_Cmd($Name.".exe");
1716    }
1717    if($Path=~/\s/) {
1718        $Path = "\"".$Path."\"";
1719    }
1720    return ($Cache{"get_CmdPath"}{$Name}=$Path);
1721}
1722
1723sub search_Cmd($)
1724{
1725    my $Name = $_[0];
1726    return "" if(not $Name);
1727    if(defined $Cache{"search_Cmd"}{$Name}) {
1728        return $Cache{"search_Cmd"}{$Name};
1729    }
1730    if(my $DefaultPath = get_CmdPath_Default($Name)) {
1731        return ($Cache{"search_Cmd"}{$Name} = $DefaultPath);
1732    }
1733    foreach my $Path (@{$SystemPaths{"bin"}})
1734    {
1735        my $CmdPath = join_P($Path,$Name);
1736        if(-f $CmdPath)
1737        {
1738            if($Name=~/gcc/) {
1739                next if(not check_gcc($CmdPath, "3"));
1740            }
1741            return ($Cache{"search_Cmd"}{$Name} = $CmdPath);
1742        }
1743    }
1744    return ($Cache{"search_Cmd"}{$Name} = "");
1745}
1746
1747sub get_CmdPath_Default($)
1748{ # search in PATH
1749    return "" if(not $_[0]);
1750    if(defined $Cache{"get_CmdPath_Default"}{$_[0]}) {
1751        return $Cache{"get_CmdPath_Default"}{$_[0]};
1752    }
1753    return ($Cache{"get_CmdPath_Default"}{$_[0]} = get_CmdPath_Default_I($_[0]));
1754}
1755
1756sub get_CmdPath_Default_I($)
1757{ # search in PATH
1758    my $Name = $_[0];
1759    if($Name=~/find/)
1760    { # special case: search for "find" utility
1761        if(`find \"$TMP_DIR\" -maxdepth 0 2>\"$TMP_DIR/null\"`) {
1762            return "find";
1763        }
1764    }
1765    elsif($Name=~/gcc/) {
1766        return check_gcc($Name, "3");
1767    }
1768    if(checkCmd($Name)) {
1769        return $Name;
1770    }
1771    if($OSgroup eq "windows")
1772    {
1773        if(`$Name /? 2>\"$TMP_DIR/null\"`) {
1774            return $Name;
1775        }
1776    }
1777    foreach my $Path (@DefaultBinPaths)
1778    {
1779        if(-f $Path."/".$Name) {
1780            return join_P($Path, $Name);
1781        }
1782    }
1783    return "";
1784}
1785
1786sub classifyPath($)
1787{
1788    my $Path = $_[0];
1789    if($Path=~/[\*\[]/)
1790    { # wildcard
1791        $Path=~s/\*/.*/g;
1792        $Path=~s/\\/\\\\/g;
1793        return ($Path, "Pattern");
1794    }
1795    elsif($Path=~/[\/\\]/)
1796    { # directory or relative path
1797        return (path_format($Path, $OSgroup), "Path");
1798    }
1799    else {
1800        return ($Path, "Name");
1801    }
1802}
1803
1804sub readDescriptor($$)
1805{
1806    my ($LibVersion, $Content) = @_;
1807    return if(not $LibVersion);
1808    my $DName = $DumpAPI?"descriptor":"descriptor \"d$LibVersion\"";
1809    if(not $Content) {
1810        exitStatus("Error", "$DName is empty");
1811    }
1812    if($Content!~/\</) {
1813        exitStatus("Error", "incorrect descriptor (see -d1 option)");
1814    }
1815    $Content=~s/\/\*(.|\n)+?\*\///g;
1816    $Content=~s/<\!--(.|\n)+?-->//g;
1817
1818    $Descriptor{$LibVersion}{"Version"} = parseTag(\$Content, "version");
1819    if($TargetVersion{$LibVersion}) {
1820        $Descriptor{$LibVersion}{"Version"} = $TargetVersion{$LibVersion};
1821    }
1822    if(not $Descriptor{$LibVersion}{"Version"}) {
1823        exitStatus("Error", "version in the $DName is not specified (<version> section)");
1824    }
1825    if($Content=~/{RELPATH}/)
1826    {
1827        if(my $RelDir = $RelativeDirectory{$LibVersion}) {
1828            $Content =~ s/{RELPATH}/$RelDir/g;
1829        }
1830        else
1831        {
1832            my $NeedRelpath = $DumpAPI?"-relpath":"-relpath$LibVersion";
1833            exitStatus("Error", "you have not specified $NeedRelpath option, but the $DName contains {RELPATH} macro");
1834        }
1835    }
1836
1837    if(not $CheckObjectsOnly_Opt)
1838    {
1839        my $DHeaders = parseTag(\$Content, "headers");
1840        if(not $DHeaders) {
1841            exitStatus("Error", "header files in the $DName are not specified (<headers> section)");
1842        }
1843        elsif(lc($DHeaders) ne "none")
1844        { # append the descriptor headers list
1845            if($Descriptor{$LibVersion}{"Headers"})
1846            { # multiple descriptors
1847                $Descriptor{$LibVersion}{"Headers"} .= "\n".$DHeaders;
1848            }
1849            else {
1850                $Descriptor{$LibVersion}{"Headers"} = $DHeaders;
1851            }
1852            foreach my $Path (split(/\s*\n\s*/, $DHeaders))
1853            {
1854                if(not -e $Path) {
1855                    exitStatus("Access_Error", "can't access \'$Path\'");
1856                }
1857            }
1858        }
1859    }
1860    if(not $CheckHeadersOnly_Opt)
1861    {
1862        my $DObjects = parseTag(\$Content, "libs");
1863        if(not $DObjects) {
1864            exitStatus("Error", "$SLIB_TYPE libraries in the $DName are not specified (<libs> section)");
1865        }
1866        elsif(lc($DObjects) ne "none")
1867        { # append the descriptor libraries list
1868            if($Descriptor{$LibVersion}{"Libs"})
1869            { # multiple descriptors
1870                $Descriptor{$LibVersion}{"Libs"} .= "\n".$DObjects;
1871            }
1872            else {
1873                $Descriptor{$LibVersion}{"Libs"} .= $DObjects;
1874            }
1875            foreach my $Path (split(/\s*\n\s*/, $DObjects))
1876            {
1877                if(not -e $Path) {
1878                    exitStatus("Access_Error", "can't access \'$Path\'");
1879                }
1880            }
1881        }
1882    }
1883    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_headers")))
1884    {
1885        if(not -d $Path) {
1886            exitStatus("Access_Error", "can't access directory \'$Path\'");
1887        }
1888        $Path = get_abs_path($Path);
1889        $Path = path_format($Path, $OSgroup);
1890        push_U($SystemPaths{"include"}, $Path);
1891    }
1892    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "search_libs")))
1893    {
1894        if(not -d $Path) {
1895            exitStatus("Access_Error", "can't access directory \'$Path\'");
1896        }
1897        $Path = get_abs_path($Path);
1898        $Path = path_format($Path, $OSgroup);
1899        push_U($SystemPaths{"lib"}, $Path);
1900    }
1901    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "tools")))
1902    {
1903        if(not -d $Path) {
1904            exitStatus("Access_Error", "can't access directory \'$Path\'");
1905        }
1906        $Path = get_abs_path($Path);
1907        $Path = path_format($Path, $OSgroup);
1908        push_U($SystemPaths{"bin"}, $Path);
1909        $TargetTools{$Path}=1;
1910    }
1911    if(my $Prefix = parseTag(\$Content, "cross_prefix")) {
1912        $CrossPrefix = $Prefix;
1913    }
1914    $Descriptor{$LibVersion}{"IncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"IncludePaths"}); # perl 5.8 doesn't support //=
1915    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "include_paths")))
1916    {
1917        if(not -d $Path) {
1918            exitStatus("Access_Error", "can't access directory \'$Path\'");
1919        }
1920        $Path = get_abs_path($Path);
1921        $Path = path_format($Path, $OSgroup);
1922        push(@{$Descriptor{$LibVersion}{"IncludePaths"}}, $Path);
1923    }
1924    $Descriptor{$LibVersion}{"AddIncludePaths"} = [] if(not defined $Descriptor{$LibVersion}{"AddIncludePaths"});
1925    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "add_include_paths")))
1926    {
1927        if(not -d $Path) {
1928            exitStatus("Access_Error", "can't access directory \'$Path\'");
1929        }
1930        $Path = get_abs_path($Path);
1931        $Path = path_format($Path, $OSgroup);
1932        push(@{$Descriptor{$LibVersion}{"AddIncludePaths"}}, $Path);
1933    }
1934    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_include_paths")))
1935    { # skip some auto-generated include paths
1936        if(not is_abs($Path))
1937        {
1938            if(my $P = abs_path($Path)) {
1939                $Path = $P;
1940            }
1941        }
1942        $Skip_Include_Paths{$LibVersion}{path_format($Path)} = 1;
1943    }
1944    foreach my $Path (split(/\s*\n\s*/, parseTag(\$Content, "skip_including")))
1945    { # skip direct including of some headers
1946        my ($CPath, $Type) = classifyPath($Path);
1947        $SkipHeaders{$LibVersion}{$Type}{$CPath} = 2;
1948    }
1949    $Descriptor{$LibVersion}{"GccOptions"} = parseTag(\$Content, "gcc_options");
1950    foreach my $Option (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"GccOptions"}))
1951    {
1952        if($Option!~/\A\-(Wl|l|L)/)
1953        { # skip linker options
1954            $CompilerOptions{$LibVersion} .= " ".$Option;
1955        }
1956    }
1957    $Descriptor{$LibVersion}{"SkipHeaders"} = parseTag(\$Content, "skip_headers");
1958    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipHeaders"}))
1959    {
1960        $SkipHeadersList{$LibVersion}{$Path} = 1;
1961        my ($CPath, $Type) = classifyPath($Path);
1962        $SkipHeaders{$LibVersion}{$Type}{$CPath} = 1;
1963    }
1964    $Descriptor{$LibVersion}{"SkipLibs"} = parseTag(\$Content, "skip_libs");
1965    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"SkipLibs"}))
1966    {
1967        my ($CPath, $Type) = classifyPath($Path);
1968        $SkipLibs{$LibVersion}{$Type}{$CPath} = 1;
1969    }
1970    if(my $DDefines = parseTag(\$Content, "defines"))
1971    {
1972        if($Descriptor{$LibVersion}{"Defines"})
1973        { # multiple descriptors
1974            $Descriptor{$LibVersion}{"Defines"} .= "\n".$DDefines;
1975        }
1976        else {
1977            $Descriptor{$LibVersion}{"Defines"} = $DDefines;
1978        }
1979    }
1980    foreach my $Order (split(/\s*\n\s*/, parseTag(\$Content, "include_order")))
1981    {
1982        if($Order=~/\A(.+):(.+)\Z/) {
1983            $Include_Order{$LibVersion}{$1} = $2;
1984        }
1985    }
1986    foreach my $Type_Name (split(/\s*\n\s*/, parseTag(\$Content, "opaque_types")),
1987    split(/\s*\n\s*/, parseTag(\$Content, "skip_types")))
1988    { # opaque_types renamed to skip_types (1.23.4)
1989        $SkipTypes{$LibVersion}{$Type_Name} = 1;
1990    }
1991    foreach my $Symbol (split(/\s*\n\s*/, parseTag(\$Content, "skip_interfaces")),
1992    split(/\s*\n\s*/, parseTag(\$Content, "skip_symbols")))
1993    { # skip_interfaces renamed to skip_symbols (1.22.1)
1994        $SkipSymbols{$LibVersion}{$Symbol} = 1;
1995    }
1996    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "skip_namespaces"))) {
1997        $SkipNameSpaces{$LibVersion}{$NameSpace} = 1;
1998    }
1999    foreach my $NameSpace (split(/\s*\n\s*/, parseTag(\$Content, "add_namespaces"))) {
2000        $AddNameSpaces{$LibVersion}{$NameSpace} = 1;
2001    }
2002    foreach my $Constant (split(/\s*\n\s*/, parseTag(\$Content, "skip_constants"))) {
2003        $SkipConstants{$LibVersion}{$Constant} = 1;
2004    }
2005    if(my $DIncPreamble = parseTag(\$Content, "include_preamble"))
2006    {
2007        if($Descriptor{$LibVersion}{"IncludePreamble"})
2008        { # multiple descriptors
2009            $Descriptor{$LibVersion}{"IncludePreamble"} .= "\n".$DIncPreamble;
2010        }
2011        else {
2012            $Descriptor{$LibVersion}{"IncludePreamble"} = $DIncPreamble;
2013        }
2014    }
2015}
2016
2017sub parseTag(@)
2018{
2019    my $CodeRef = shift(@_);
2020    my $Tag = shift(@_);
2021    if(not $Tag or not $CodeRef) {
2022        return undef;
2023    }
2024    my $Sp = 0;
2025    if(@_) {
2026        $Sp = shift(@_);
2027    }
2028    my $Start = index(${$CodeRef}, "<$Tag>");
2029    if($Start!=-1)
2030    {
2031        my $End = index(${$CodeRef}, "</$Tag>");
2032        if($End!=-1)
2033        {
2034            my $TS = length($Tag)+3;
2035            my $Content = substr(${$CodeRef}, $Start, $End-$Start+$TS, "");
2036            substr($Content, 0, $TS-1, ""); # cut start tag
2037            substr($Content, -$TS, $TS, ""); # cut end tag
2038            if(not $Sp)
2039            {
2040                $Content=~s/\A\s+//g;
2041                $Content=~s/\s+\Z//g;
2042            }
2043            if(substr($Content, 0, 1) ne "<") {
2044                $Content = xmlSpecChars_R($Content);
2045            }
2046            return $Content;
2047        }
2048    }
2049    return undef;
2050}
2051
2052sub getInfo($)
2053{
2054    my $DumpPath = $_[0];
2055    return if(not $DumpPath or not -f $DumpPath);
2056
2057    readTUDump($DumpPath);
2058
2059    # processing info
2060    setTemplateParams_All();
2061
2062    if($ExtraDump) {
2063        setAnonTypedef_All();
2064    }
2065
2066    getTypeInfo_All();
2067    simplifyNames();
2068    simplifyConstants();
2069    getVarInfo_All();
2070    getSymbolInfo_All();
2071
2072    # clean memory
2073    %LibInfo = ();
2074    %TemplateInstance = ();
2075    %BasicTemplate = ();
2076    %MangledNames = ();
2077    %TemplateDecl = ();
2078    %StdCxxTypedef = ();
2079    %MissedTypedef = ();
2080    %Typedef_Tr = ();
2081    %Typedef_Eq = ();
2082    %TypedefToAnon = ();
2083
2084    # clean cache
2085    delete($Cache{"getTypeAttr"});
2086    delete($Cache{"getTypeDeclId"});
2087
2088    if($ExtraDump)
2089    {
2090        remove_Unused($Version, "Extra");
2091    }
2092    else
2093    { # remove unused types
2094        if($BinaryOnly and not $ExtendedCheck)
2095        { # --binary
2096            remove_Unused($Version, "All");
2097        }
2098        else {
2099            remove_Unused($Version, "Extended");
2100        }
2101    }
2102
2103    if($CheckInfo)
2104    {
2105        foreach my $Tid (keys(%{$TypeInfo{$Version}})) {
2106            check_Completeness($TypeInfo{$Version}{$Tid}, $Version);
2107        }
2108
2109        foreach my $Sid (keys(%{$SymbolInfo{$Version}})) {
2110            check_Completeness($SymbolInfo{$Version}{$Sid}, $Version);
2111        }
2112    }
2113
2114    if($Debug) {
2115        # debugMangling($Version);
2116    }
2117}
2118
2119sub readTUDump($)
2120{
2121    my $DumpPath = $_[0];
2122
2123    open(TU_DUMP, $DumpPath);
2124    local $/ = undef;
2125    my $Content = <TU_DUMP>;
2126    close(TU_DUMP);
2127
2128    unlink($DumpPath);
2129
2130    $Content=~s/\n[ ]+/ /g;
2131    my @Lines = split(/\n/, $Content);
2132
2133    # clean memory
2134    undef $Content;
2135
2136    $MAX_ID = $#Lines+1; # number of lines == number of nodes
2137
2138    foreach (0 .. $#Lines)
2139    {
2140        if($Lines[$_]=~/\A\@(\d+)[ ]+([a-z_]+)[ ]+(.+)\Z/i)
2141        { # get a number and attributes of a node
2142            next if(not $NodeType{$2});
2143            $LibInfo{$Version}{"info_type"}{$1}=$2;
2144            $LibInfo{$Version}{"info"}{$1}=$3;
2145        }
2146
2147        # clean memory
2148        delete($Lines[$_]);
2149    }
2150
2151    # clean memory
2152    undef @Lines;
2153}
2154
2155sub simplifyConstants()
2156{
2157    foreach my $Constant (keys(%{$Constants{$Version}}))
2158    {
2159        if(defined $Constants{$Version}{$Constant}{"Header"})
2160        {
2161            my $Value = $Constants{$Version}{$Constant}{"Value"};
2162            if(defined $EnumConstants{$Version}{$Value}) {
2163                $Constants{$Version}{$Constant}{"Value"} = $EnumConstants{$Version}{$Value}{"Value"};
2164            }
2165        }
2166    }
2167}
2168
2169sub simplifyNames()
2170{
2171    foreach my $Base (keys(%{$Typedef_Tr{$Version}}))
2172    {
2173        if($Typedef_Eq{$Version}{$Base}) {
2174            next;
2175        }
2176        my @Translations = sort keys(%{$Typedef_Tr{$Version}{$Base}});
2177        if($#Translations==0)
2178        {
2179            if(length($Translations[0])<=length($Base)) {
2180                $Typedef_Eq{$Version}{$Base} = $Translations[0];
2181            }
2182        }
2183        else
2184        { # select most appropriate
2185            foreach my $Tr (@Translations)
2186            {
2187                if($Base=~/\A\Q$Tr\E/)
2188                {
2189                    $Typedef_Eq{$Version}{$Base} = $Tr;
2190                    last;
2191                }
2192            }
2193        }
2194    }
2195    foreach my $TypeId (keys(%{$TypeInfo{$Version}}))
2196    {
2197        my $TypeName = $TypeInfo{$Version}{$TypeId}{"Name"};
2198        if(not $TypeName) {
2199            next;
2200        }
2201        next if(index($TypeName,"<")==-1);# template instances only
2202        if($TypeName=~/>(::\w+)+\Z/)
2203        { # skip unused types
2204            next;
2205        }
2206        foreach my $Base (sort {length($b)<=>length($a)}
2207        sort {$b cmp $a} keys(%{$Typedef_Eq{$Version}}))
2208        {
2209            next if(not $Base);
2210            next if(index($TypeName,$Base)==-1);
2211            next if(length($TypeName) - length($Base) <= 3);
2212            if(my $Typedef = $Typedef_Eq{$Version}{$Base})
2213            {
2214                $TypeName=~s/(\<|\,)\Q$Base\E(\W|\Z)/$1$Typedef$2/g;
2215                $TypeName=~s/(\<|\,)\Q$Base\E(\w|\Z)/$1$Typedef $2/g;
2216                if(defined $TypeInfo{$Version}{$TypeId}{"TParam"})
2217                {
2218                    foreach my $TPos (keys(%{$TypeInfo{$Version}{$TypeId}{"TParam"}}))
2219                    {
2220                        if(my $TPName = $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"})
2221                        {
2222                            $TPName=~s/\A\Q$Base\E(\W|\Z)/$Typedef$1/g;
2223                            $TPName=~s/\A\Q$Base\E(\w|\Z)/$Typedef $1/g;
2224                            $TypeInfo{$Version}{$TypeId}{"TParam"}{$TPos}{"name"} = formatName($TPName, "T");
2225                        }
2226                    }
2227                }
2228            }
2229        }
2230        $TypeName = formatName($TypeName, "T");
2231        $TypeInfo{$Version}{$TypeId}{"Name"} = $TypeName;
2232        $TName_Tid{$Version}{$TypeName} = $TypeId;
2233    }
2234}
2235
2236sub setAnonTypedef_All()
2237{
2238    foreach my $InfoId (keys(%{$LibInfo{$Version}{"info"}}))
2239    {
2240        if($LibInfo{$Version}{"info_type"}{$InfoId} eq "type_decl")
2241        {
2242            if(isAnon(getNameByInfo($InfoId))) {
2243                $TypedefToAnon{getTypeId($InfoId)} = 1;
2244            }
2245        }
2246    }
2247}
2248
2249sub setTemplateParams_All()
2250{
2251    foreach (keys(%{$LibInfo{$Version}{"info"}}))
2252    {
2253        if($LibInfo{$Version}{"info_type"}{$_} eq "template_decl") {
2254            setTemplateParams($_);
2255        }
2256    }
2257}
2258
2259sub setTemplateParams($)
2260{
2261    my $Tid = getTypeId($_[0]);
2262    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
2263    {
2264        if($Info=~/(inst|spcs)[ ]*:[ ]*@(\d+) /)
2265        {
2266            my $TmplInst_Id = $2;
2267            setTemplateInstParams($_[0], $TmplInst_Id);
2268            while($TmplInst_Id = getNextElem($TmplInst_Id)) {
2269                setTemplateInstParams($_[0], $TmplInst_Id);
2270            }
2271        }
2272
2273        $BasicTemplate{$Version}{$Tid} = $_[0];
2274
2275        if(my $Prms = getTreeAttr_Prms($_[0]))
2276        {
2277            if(my $Valu = getTreeAttr_Valu($Prms))
2278            {
2279                my $Vector = getTreeVec($Valu);
2280                foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Vector}))
2281                {
2282                    if(my $Val = getTreeAttr_Valu($Vector->{$Pos}))
2283                    {
2284                        if(my $Name = getNameByInfo($Val))
2285                        {
2286                            $TemplateArg{$Version}{$_[0]}{$Pos} = $Name;
2287                            if($LibInfo{$Version}{"info_type"}{$Val} eq "parm_decl") {
2288                                $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = $Val;
2289                            }
2290                            else {
2291                                $TemplateInstance{$Version}{"Type"}{$Tid}{$Pos} = getTreeAttr_Type($Val);
2292                            }
2293                        }
2294                    }
2295                }
2296            }
2297        }
2298    }
2299    if(my $TypeId = getTreeAttr_Type($_[0]))
2300    {
2301        if(my $IType = $LibInfo{$Version}{"info_type"}{$TypeId})
2302        {
2303            if($IType eq "record_type") {
2304                $TemplateDecl{$Version}{$TypeId} = 1;
2305            }
2306        }
2307    }
2308}
2309
2310sub setTemplateInstParams($$)
2311{
2312    my ($Tmpl, $Inst) = @_;
2313
2314    if(my $Info = $LibInfo{$Version}{"info"}{$Inst})
2315    {
2316        my ($Params_InfoId, $ElemId) = ();
2317        if($Info=~/purp[ ]*:[ ]*@(\d+) /) {
2318            $Params_InfoId = $1;
2319        }
2320        if($Info=~/valu[ ]*:[ ]*@(\d+) /) {
2321            $ElemId = $1;
2322        }
2323        if($Params_InfoId and $ElemId)
2324        {
2325            my $Params_Info = $LibInfo{$Version}{"info"}{$Params_InfoId};
2326            while($Params_Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
2327            {
2328                my ($PPos, $PTypeId) = ($1, $2);
2329                if(my $PType = $LibInfo{$Version}{"info_type"}{$PTypeId})
2330                {
2331                    if($PType eq "template_type_parm") {
2332                        $TemplateDecl{$Version}{$ElemId} = 1;
2333                    }
2334                }
2335                if($LibInfo{$Version}{"info_type"}{$ElemId} eq "function_decl")
2336                { # functions
2337                    $TemplateInstance{$Version}{"Func"}{$ElemId}{$PPos} = $PTypeId;
2338                    $BasicTemplate{$Version}{$ElemId} = $Tmpl;
2339                }
2340                else
2341                { # types
2342                    $TemplateInstance{$Version}{"Type"}{$ElemId}{$PPos} = $PTypeId;
2343                    $BasicTemplate{$Version}{$ElemId} = $Tmpl;
2344                }
2345            }
2346        }
2347    }
2348}
2349
2350sub getTypeDeclId($)
2351{
2352    if($_[0])
2353    {
2354        if(defined $Cache{"getTypeDeclId"}{$Version}{$_[0]}) {
2355            return $Cache{"getTypeDeclId"}{$Version}{$_[0]};
2356        }
2357        if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
2358        {
2359            if($Info=~/name[ ]*:[ ]*@(\d+)/) {
2360                return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = $1);
2361            }
2362        }
2363    }
2364    return ($Cache{"getTypeDeclId"}{$Version}{$_[0]} = 0);
2365}
2366
2367sub getTypeInfo_All()
2368{
2369    if(not check_gcc($GCC_PATH, "4.5"))
2370    { # support for GCC < 4.5
2371      # missed typedefs: QStyle::State is typedef to QFlags<QStyle::StateFlag>
2372      # but QStyleOption.state is of type QFlags<QStyle::StateFlag> in the TU dump
2373      # FIXME: check GCC versions
2374        addMissedTypes_Pre();
2375    }
2376
2377    foreach (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2378    { # forward order only
2379        my $IType = $LibInfo{$Version}{"info_type"}{$_};
2380        if($IType=~/_type\Z/ and $IType ne "function_type"
2381        and $IType ne "method_type") {
2382            getTypeInfo("$_");
2383        }
2384    }
2385
2386    # add "..." type
2387    $TypeInfo{$Version}{"-1"} = {
2388        "Name" => "...",
2389        "Type" => "Intrinsic",
2390        "Tid" => "-1"
2391    };
2392    $TName_Tid{$Version}{"..."} = "-1";
2393
2394    if(not check_gcc($GCC_PATH, "4.5"))
2395    { # support for GCC < 4.5
2396        addMissedTypes_Post();
2397    }
2398
2399    if($ADD_TMPL_INSTANCES)
2400    {
2401        # templates
2402        foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}}))
2403        {
2404            if(defined $TemplateMap{$Version}{$Tid}
2405            and not defined $TypeInfo{$Version}{$Tid}{"Template"})
2406            {
2407                if(defined $TypeInfo{$Version}{$Tid}{"Memb"})
2408                {
2409                    foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Memb"}}))
2410                    {
2411                        if(my $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"})
2412                        {
2413                            if(my %MAttr = getTypeAttr($MembTypeId))
2414                            {
2415                                $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"algn"} = $MAttr{"Algn"};
2416                                $MembTypeId = $TypeInfo{$Version}{$Tid}{"Memb"}{$Pos}{"type"} = instType($TemplateMap{$Version}{$Tid}, $MembTypeId, $Version);
2417                            }
2418                        }
2419                    }
2420                }
2421                if(defined $TypeInfo{$Version}{$Tid}{"Base"})
2422                {
2423                    foreach my $Bid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$Version}{$Tid}{"Base"}}))
2424                    {
2425                        my $NBid = instType($TemplateMap{$Version}{$Tid}, $Bid, $Version);
2426
2427                        if($NBid ne $Bid)
2428                        {
2429                            %{$TypeInfo{$Version}{$Tid}{"Base"}{$NBid}} = %{$TypeInfo{$Version}{$Tid}{"Base"}{$Bid}};
2430                            delete($TypeInfo{$Version}{$Tid}{"Base"}{$Bid});
2431                        }
2432                    }
2433                }
2434            }
2435        }
2436    }
2437}
2438
2439sub createType($$)
2440{
2441    my ($Attr, $LibVersion) = @_;
2442    my $NewId = ++$MAX_ID;
2443
2444    $Attr->{"Tid"} = $NewId;
2445    $TypeInfo{$Version}{$NewId} = $Attr;
2446    $TName_Tid{$Version}{formatName($Attr->{"Name"}, "T")} = $NewId;
2447
2448    return "$NewId";
2449}
2450
2451sub instType($$$)
2452{ # create template instances
2453    my ($Map, $Tid, $LibVersion) = @_;
2454
2455    if(not $TypeInfo{$LibVersion}{$Tid}) {
2456        return undef;
2457    }
2458    my $Attr = dclone($TypeInfo{$LibVersion}{$Tid});
2459
2460    foreach my $Key (sort keys(%{$Map}))
2461    {
2462        if(my $Val = $Map->{$Key})
2463        {
2464            $Attr->{"Name"}=~s/\b$Key\b/$Val/g;
2465
2466            if(defined $Attr->{"NameSpace"}) {
2467                $Attr->{"NameSpace"}=~s/\b$Key\b/$Val/g;
2468            }
2469            foreach (keys(%{$Attr->{"TParam"}})) {
2470                $Attr->{"TParam"}{$_}{"name"}=~s/\b$Key\b/$Val/g;
2471            }
2472        }
2473        else
2474        { # remove absent
2475          # _Traits, etc.
2476            $Attr->{"Name"}=~s/,\s*\b$Key(,|>)/$1/g;
2477            if(defined $Attr->{"NameSpace"}) {
2478                $Attr->{"NameSpace"}=~s/,\s*\b$Key(,|>)/$1/g;
2479            }
2480            foreach (keys(%{$Attr->{"TParam"}}))
2481            {
2482                if($Attr->{"TParam"}{$_}{"name"} eq $Key) {
2483                    delete($Attr->{"TParam"}{$_});
2484                }
2485                else {
2486                    $Attr->{"TParam"}{$_}{"name"}=~s/,\s*\b$Key(,|>)/$1/g;
2487                }
2488            }
2489        }
2490    }
2491
2492    my $Tmpl = 0;
2493
2494    if(defined $Attr->{"TParam"})
2495    {
2496        foreach (sort {int($a)<=>int($b)} keys(%{$Attr->{"TParam"}}))
2497        {
2498            my $PName = $Attr->{"TParam"}{$_}{"name"};
2499
2500            if(my $PTid = $TName_Tid{$LibVersion}{$PName})
2501            {
2502                my %Base = get_BaseType($PTid, $LibVersion);
2503
2504                if($Base{"Type"} eq "TemplateParam"
2505                or defined $Base{"Template"})
2506                {
2507                    $Tmpl = 1;
2508                    last
2509                }
2510            }
2511        }
2512    }
2513
2514    if(my $Id = getTypeIdByName($Attr->{"Name"}, $LibVersion)) {
2515        return "$Id";
2516    }
2517    else
2518    {
2519        if(not $Tmpl) {
2520            delete($Attr->{"Template"});
2521        }
2522
2523        my $New = createType($Attr, $LibVersion);
2524
2525        my %EMap = ();
2526        if(defined $TemplateMap{$LibVersion}{$Tid}) {
2527            %EMap = %{$TemplateMap{$LibVersion}{$Tid}};
2528        }
2529        foreach (keys(%{$Map})) {
2530            $EMap{$_} = $Map->{$_};
2531        }
2532
2533        if(defined $TypeInfo{$LibVersion}{$New}{"BaseType"}) {
2534            $TypeInfo{$LibVersion}{$New}{"BaseType"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"BaseType"}, $LibVersion);
2535        }
2536        if(defined $TypeInfo{$LibVersion}{$New}{"Base"})
2537        {
2538            foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$New}{"Base"}}))
2539            {
2540                my $NBid = instType(\%EMap, $Bid, $LibVersion);
2541
2542                if($NBid ne $Bid)
2543                {
2544                    %{$TypeInfo{$LibVersion}{$New}{"Base"}{$NBid}} = %{$TypeInfo{$LibVersion}{$New}{"Base"}{$Bid}};
2545                    delete($TypeInfo{$LibVersion}{$New}{"Base"}{$Bid});
2546                }
2547            }
2548        }
2549
2550        if(defined $TypeInfo{$LibVersion}{$New}{"Memb"})
2551        {
2552            foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Memb"}}))
2553            {
2554                if(defined $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}) {
2555                    $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Memb"}{$_}{"type"}, $LibVersion);
2556                }
2557            }
2558        }
2559
2560        if(defined $TypeInfo{$LibVersion}{$New}{"Param"})
2561        {
2562            foreach (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}{$New}{"Param"}})) {
2563                $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Param"}{$_}{"type"}, $LibVersion);
2564            }
2565        }
2566
2567        if(defined $TypeInfo{$LibVersion}{$New}{"Return"}) {
2568            $TypeInfo{$LibVersion}{$New}{"Return"} = instType(\%EMap, $TypeInfo{$LibVersion}{$New}{"Return"}, $LibVersion);
2569        }
2570
2571        return $New;
2572    }
2573}
2574
2575sub addMissedTypes_Pre()
2576{
2577    my %MissedTypes = ();
2578    foreach my $MissedTDid (sort {int($a)<=>int($b)} keys(%{$LibInfo{$Version}{"info"}}))
2579    { # detecting missed typedefs
2580        if($LibInfo{$Version}{"info_type"}{$MissedTDid} eq "type_decl")
2581        {
2582            my $TypeId = getTreeAttr_Type($MissedTDid);
2583            next if(not $TypeId);
2584            my $TypeType = getTypeType($TypeId);
2585            if($TypeType eq "Unknown")
2586            { # template_type_parm
2587                next;
2588            }
2589            my $TypeDeclId = getTypeDeclId($TypeId);
2590            next if($TypeDeclId eq $MissedTDid);#or not $TypeDeclId
2591            my $TypedefName = getNameByInfo($MissedTDid);
2592            next if(not $TypedefName);
2593            next if($TypedefName eq "__float80");
2594            next if(isAnon($TypedefName));
2595            if(not $TypeDeclId
2596            or getNameByInfo($TypeDeclId) ne $TypedefName) {
2597                $MissedTypes{$Version}{$TypeId}{$MissedTDid} = 1;
2598            }
2599        }
2600    }
2601    my %AddTypes = ();
2602    foreach my $Tid (keys(%{$MissedTypes{$Version}}))
2603    { # add missed typedefs
2604        my @Missed = keys(%{$MissedTypes{$Version}{$Tid}});
2605        if(not @Missed or $#Missed>=1) {
2606            next;
2607        }
2608        my $MissedTDid = $Missed[0];
2609        my ($TypedefName, $TypedefNS) = getTrivialName($MissedTDid, $Tid);
2610        if(not $TypedefName) {
2611            next;
2612        }
2613        my $NewId = ++$MAX_ID;
2614        my %MissedInfo = ( # typedef info
2615            "Name" => $TypedefName,
2616            "NameSpace" => $TypedefNS,
2617            "BaseType" => $Tid,
2618            "Type" => "Typedef",
2619            "Tid" => "$NewId" );
2620        my ($H, $L) = getLocation($MissedTDid);
2621        $MissedInfo{"Header"} = $H;
2622        $MissedInfo{"Line"} = $L;
2623        if($TypedefName=~/\*|\&|\s/)
2624        { # other types
2625            next;
2626        }
2627        if($TypedefName=~/>(::\w+)+\Z/)
2628        { # QFlags<Qt::DropAction>::enum_type
2629            next;
2630        }
2631        if(getTypeType($Tid)=~/\A(Intrinsic|Union|Struct|Enum|Class)\Z/)
2632        { # double-check for the name of typedef
2633            my ($TName, $TNS) = getTrivialName(getTypeDeclId($Tid), $Tid); # base type info
2634            next if(not $TName);
2635            if(length($TypedefName)>=length($TName))
2636            { # too long typedef
2637                next;
2638            }
2639            if($TName=~/\A\Q$TypedefName\E</) {
2640                next;
2641            }
2642            if($TypedefName=~/\A\Q$TName\E/)
2643            { # QDateTimeEdit::Section and QDateTimeEdit::Sections::enum_type
2644                next;
2645            }
2646            if(get_depth($TypedefName)==0 and get_depth($TName)!=0)
2647            { # std::_Vector_base and std::vector::_Base
2648                next;
2649            }
2650        }
2651
2652        $AddTypes{$MissedInfo{"Tid"}} = \%MissedInfo;
2653
2654        # register typedef
2655        $MissedTypedef{$Version}{$Tid}{"Tid"} = $MissedInfo{"Tid"};
2656        $MissedTypedef{$Version}{$Tid}{"TDid"} = $MissedTDid;
2657        $TName_Tid{$Version}{$TypedefName} = $MissedInfo{"Tid"};
2658    }
2659
2660    # add missed & remove other
2661    $TypeInfo{$Version} = \%AddTypes;
2662    delete($Cache{"getTypeAttr"}{$Version});
2663}
2664
2665sub addMissedTypes_Post()
2666{
2667    foreach my $BaseId (keys(%{$MissedTypedef{$Version}}))
2668    {
2669        if(my $Tid = $MissedTypedef{$Version}{$BaseId}{"Tid"})
2670        {
2671            $TypeInfo{$Version}{$Tid}{"Size"} = $TypeInfo{$Version}{$BaseId}{"Size"};
2672            if(my $TName = $TypeInfo{$Version}{$Tid}{"Name"}) {
2673                $Typedef_BaseName{$Version}{$TName} = $TypeInfo{$Version}{$BaseId}{"Name"};
2674            }
2675        }
2676    }
2677}
2678
2679sub getTypeInfo($)
2680{
2681    my $TypeId = $_[0];
2682    %{$TypeInfo{$Version}{$TypeId}} = getTypeAttr($TypeId);
2683    my $TName = $TypeInfo{$Version}{$TypeId}{"Name"};
2684    if(not $TName) {
2685        delete($TypeInfo{$Version}{$TypeId});
2686    }
2687}
2688
2689sub getArraySize($$)
2690{
2691    my ($TypeId, $BaseName) = @_;
2692    if(my $Size = getSize($TypeId))
2693    {
2694        my $Elems = $Size/$BYTE_SIZE;
2695        while($BaseName=~s/\s*\[(\d+)\]//) {
2696            $Elems/=$1;
2697        }
2698        if(my $BasicId = $TName_Tid{$Version}{$BaseName})
2699        {
2700            if(my $BasicSize = $TypeInfo{$Version}{$BasicId}{"Size"}) {
2701                $Elems/=$BasicSize;
2702            }
2703        }
2704        return $Elems;
2705    }
2706    return 0;
2707}
2708
2709sub getTParams($$)
2710{
2711    my ($TypeId, $Kind) = @_;
2712    my @TmplParams = ();
2713    my @Positions = sort {int($a)<=>int($b)} keys(%{$TemplateInstance{$Version}{$Kind}{$TypeId}});
2714    foreach my $Pos (@Positions)
2715    {
2716        my $Param_TypeId = $TemplateInstance{$Version}{$Kind}{$TypeId}{$Pos};
2717        my $NodeType = $LibInfo{$Version}{"info_type"}{$Param_TypeId};
2718        if(not $NodeType)
2719        { # typename_type
2720            return ();
2721        }
2722        if($NodeType eq "tree_vec")
2723        {
2724            if($Pos!=$#Positions)
2725            { # select last vector of parameters ( ns<P1>::type<P2> )
2726                next;
2727            }
2728        }
2729        my @Params = get_TemplateParam($Pos, $Param_TypeId);
2730        foreach my $P (@Params)
2731        {
2732            if($P eq "") {
2733                return ();
2734            }
2735            elsif($P ne "\@skip\@") {
2736                @TmplParams = (@TmplParams, $P);
2737            }
2738        }
2739    }
2740    return @TmplParams;
2741}
2742
2743sub getTypeAttr($)
2744{
2745    my $TypeId = $_[0];
2746    my %TypeAttr = ();
2747    if(defined $TypeInfo{$Version}{$TypeId}
2748    and $TypeInfo{$Version}{$TypeId}{"Name"})
2749    { # already created
2750        return %{$TypeInfo{$Version}{$TypeId}};
2751    }
2752    elsif($Cache{"getTypeAttr"}{$Version}{$TypeId})
2753    { # incomplete type
2754        return ();
2755    }
2756    $Cache{"getTypeAttr"}{$Version}{$TypeId} = 1;
2757
2758    my $TypeDeclId = getTypeDeclId($TypeId);
2759    $TypeAttr{"Tid"} = $TypeId;
2760
2761    if(not $MissedBase{$Version}{$TypeId} and isTypedef($TypeId))
2762    {
2763        if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
2764        {
2765            if($Info=~/qual[ ]*:/)
2766            {
2767                my $NewId = ++$MAX_ID;
2768
2769                $MissedBase{$Version}{$TypeId} = "$NewId";
2770                $MissedBase_R{$Version}{$NewId} = $TypeId;
2771                $LibInfo{$Version}{"info"}{$NewId} = $LibInfo{$Version}{"info"}{$TypeId};
2772                $LibInfo{$Version}{"info_type"}{$NewId} = $LibInfo{$Version}{"info_type"}{$TypeId};
2773            }
2774        }
2775        $TypeAttr{"Type"} = "Typedef";
2776    }
2777    else {
2778        $TypeAttr{"Type"} = getTypeType($TypeId);
2779    }
2780
2781    if(my $ScopeId = getTreeAttr_Scpe($TypeDeclId))
2782    {
2783        if($LibInfo{$Version}{"info_type"}{$ScopeId} eq "function_decl")
2784        { # local code
2785            return ();
2786        }
2787    }
2788
2789    if($TypeAttr{"Type"} eq "Unknown") {
2790        return ();
2791    }
2792    elsif($TypeAttr{"Type"}=~/(Func|Method|Field)Ptr/)
2793    {
2794        %TypeAttr = getMemPtrAttr(pointTo($TypeId), $TypeId, $TypeAttr{"Type"});
2795        if(my $TName = $TypeAttr{"Name"})
2796        {
2797            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2798            $TName_Tid{$Version}{$TName} = $TypeId;
2799            return %TypeAttr;
2800        }
2801        else {
2802            return ();
2803        }
2804    }
2805    elsif($TypeAttr{"Type"} eq "Array")
2806    {
2807        my ($BTid, $BTSpec) = selectBaseType($TypeId);
2808        if(not $BTid) {
2809            return ();
2810        }
2811        if(my $Algn = getAlgn($TypeId)) {
2812            $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
2813        }
2814        $TypeAttr{"BaseType"} = $BTid;
2815        if(my %BTAttr = getTypeAttr($BTid))
2816        {
2817            if(not $BTAttr{"Name"}) {
2818                return ();
2819            }
2820            if(my $NElems = getArraySize($TypeId, $BTAttr{"Name"}))
2821            {
2822                if(my $Size = getSize($TypeId)) {
2823                    $TypeAttr{"Size"} = $Size/$BYTE_SIZE;
2824                }
2825                if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2826                    $TypeAttr{"Name"} = $1."[$NElems]".$2;
2827                }
2828                else {
2829                    $TypeAttr{"Name"} = $BTAttr{"Name"}."[$NElems]";
2830                }
2831            }
2832            else
2833            {
2834                $TypeAttr{"Size"} = $WORD_SIZE{$Version}; # pointer
2835                if($BTAttr{"Name"}=~/\A([^\[\]]+)(\[(\d+|)\].*)\Z/) {
2836                    $TypeAttr{"Name"} = $1."[]".$2;
2837                }
2838                else {
2839                    $TypeAttr{"Name"} = $BTAttr{"Name"}."[]";
2840                }
2841            }
2842            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
2843            if($BTAttr{"Header"})  {
2844                $TypeAttr{"Header"} = $BTAttr{"Header"};
2845            }
2846            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2847            $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2848            return %TypeAttr;
2849        }
2850        return ();
2851    }
2852    elsif($TypeAttr{"Type"}=~/\A(Intrinsic|Union|Struct|Enum|Class|Vector)\Z/)
2853    {
2854        %TypeAttr = getTrivialTypeAttr($TypeId);
2855        if($TypeAttr{"Name"})
2856        {
2857            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2858
2859            if(not defined $IntrinsicNames{$TypeAttr{"Name"}}
2860            or getTypeDeclId($TypeAttr{"Tid"}))
2861            { # NOTE: register only one int: with built-in decl
2862                if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2863                    $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2864                }
2865            }
2866            return %TypeAttr;
2867        }
2868        else {
2869            return ();
2870        }
2871    }
2872    elsif($TypeAttr{"Type"}=~/TemplateParam|TypeName/)
2873    {
2874        %TypeAttr = getTrivialTypeAttr($TypeId);
2875        if($TypeAttr{"Name"})
2876        {
2877            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2878            if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
2879                $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
2880            }
2881            return %TypeAttr;
2882        }
2883        else {
2884            return ();
2885        }
2886    }
2887    elsif($TypeAttr{"Type"} eq "SizeOf")
2888    {
2889        $TypeAttr{"BaseType"} = getTreeAttr_Type($TypeId);
2890        my %BTAttr = getTypeAttr($TypeAttr{"BaseType"});
2891        $TypeAttr{"Name"} = "sizeof(".$BTAttr{"Name"}.")";
2892        if($TypeAttr{"Name"})
2893        {
2894            %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
2895            return %TypeAttr;
2896        }
2897        else {
2898            return ();
2899        }
2900    }
2901    else
2902    { # derived types
2903        my ($BTid, $BTSpec) = selectBaseType($TypeId);
2904        if(not $BTid) {
2905            return ();
2906        }
2907        $TypeAttr{"BaseType"} = $BTid;
2908        if(defined $MissedTypedef{$Version}{$BTid})
2909        {
2910            if(my $MissedTDid = $MissedTypedef{$Version}{$BTid}{"TDid"})
2911            {
2912                if($MissedTDid ne $TypeDeclId) {
2913                    $TypeAttr{"BaseType"} = $MissedTypedef{$Version}{$BTid}{"Tid"};
2914                }
2915            }
2916        }
2917        my %BTAttr = getTypeAttr($TypeAttr{"BaseType"});
2918        if(not $BTAttr{"Name"})
2919        { # templates
2920            return ();
2921        }
2922        if($BTAttr{"Type"} eq "Typedef")
2923        { # relinking typedefs
2924            my %BaseBase = get_Type($BTAttr{"BaseType"}, $Version);
2925            if($BTAttr{"Name"} eq $BaseBase{"Name"}) {
2926                $TypeAttr{"BaseType"} = $BaseBase{"Tid"};
2927            }
2928        }
2929        if($BTSpec)
2930        {
2931            if($TypeAttr{"Type"} eq "Pointer"
2932            and $BTAttr{"Name"}=~/\([\*]+\)/)
2933            {
2934                $TypeAttr{"Name"} = $BTAttr{"Name"};
2935                $TypeAttr{"Name"}=~s/\(([*]+)\)/($1*)/g;
2936            }
2937            else {
2938                $TypeAttr{"Name"} = $BTAttr{"Name"}." ".$BTSpec;
2939            }
2940        }
2941        else {
2942            $TypeAttr{"Name"} = $BTAttr{"Name"};
2943        }
2944        if($TypeAttr{"Type"} eq "Typedef")
2945        {
2946            $TypeAttr{"Name"} = getNameByInfo($TypeDeclId);
2947
2948            if(index($TypeAttr{"Name"}, "tmp_add_type")==0) {
2949                return ();
2950            }
2951
2952            if(isAnon($TypeAttr{"Name"}))
2953            { # anon typedef to anon type: ._N
2954                return ();
2955            }
2956
2957            if($LibInfo{$Version}{"info"}{$TypeDeclId}=~/ artificial /i)
2958            { # artificial typedef of "struct X" to "X"
2959                $TypeAttr{"Artificial"} = 1;
2960            }
2961
2962            if(my $NS = getNameSpace($TypeDeclId))
2963            {
2964                my $TypeName = $TypeAttr{"Name"};
2965                if($NS=~/\A(struct |union |class |)((.+)::|)\Q$TypeName\E\Z/)
2966                { # "some_type" is the typedef to "struct some_type" in C++
2967                    if($3) {
2968                        $TypeAttr{"Name"} = $3."::".$TypeName;
2969                    }
2970                }
2971                else
2972                {
2973                    $TypeAttr{"NameSpace"} = $NS;
2974                    $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
2975
2976                    if($TypeAttr{"NameSpace"}=~/\Astd(::|\Z)/
2977                    and $TypeAttr{"Name"}!~/>(::\w+)+\Z/)
2978                    {
2979                        if($BTAttr{"NameSpace"}
2980                        and $BTAttr{"NameSpace"}=~/\Astd(::|\Z)/ and $BTAttr{"Name"}=~/</)
2981                        { # types like "std::fpos<__mbstate_t>" are
2982                          # not covered by typedefs in the TU dump
2983                          # so trying to add such typedefs manually
2984                            $StdCxxTypedef{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
2985                            if(length($TypeAttr{"Name"})<=length($BTAttr{"Name"}))
2986                            {
2987                                if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/))
2988                                { # skip "other" in "std" and "type" in "boost"
2989                                    $Typedef_Eq{$Version}{$BTAttr{"Name"}} = $TypeAttr{"Name"};
2990                                }
2991                            }
2992                        }
2993                    }
2994                }
2995            }
2996            if($TypeAttr{"Name"} ne $BTAttr{"Name"} and not $TypeAttr{"Artificial"}
2997            and $TypeAttr{"Name"}!~/>(::\w+)+\Z/ and $BTAttr{"Name"}!~/>(::\w+)+\Z/)
2998            {
2999                if(not defined $Typedef_BaseName{$Version}{$TypeAttr{"Name"}})
3000                { # typedef int*const TYPEDEF; // first
3001                  # int foo(TYPEDEF p); // const is optimized out
3002                    $Typedef_BaseName{$Version}{$TypeAttr{"Name"}} = $BTAttr{"Name"};
3003                    if($BTAttr{"Name"}=~/</)
3004                    {
3005                        if(($BTAttr{"Name"}!~/\A(std|boost)::/ or $TypeAttr{"Name"}!~/\A[a-z]+\Z/)) {
3006                            $Typedef_Tr{$Version}{$BTAttr{"Name"}}{$TypeAttr{"Name"}} = 1;
3007                        }
3008                    }
3009                }
3010            }
3011            ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeDeclId);
3012        }
3013        if(not $TypeAttr{"Size"})
3014        {
3015            if($TypeAttr{"Type"} eq "Pointer") {
3016                $TypeAttr{"Size"} = $WORD_SIZE{$Version};
3017            }
3018            elsif($BTAttr{"Size"}) {
3019                $TypeAttr{"Size"} = $BTAttr{"Size"};
3020            }
3021        }
3022        if(my $Algn = getAlgn($TypeId)) {
3023            $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
3024        }
3025        $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
3026        if(not $TypeAttr{"Header"} and $BTAttr{"Header"})  {
3027            $TypeAttr{"Header"} = $BTAttr{"Header"};
3028        }
3029        %{$TypeInfo{$Version}{$TypeId}} = %TypeAttr;
3030        if($TypeAttr{"Name"} ne $BTAttr{"Name"})
3031        { # typedef to "class Class"
3032          # should not be registered in TName_Tid
3033            if(not $TName_Tid{$Version}{$TypeAttr{"Name"}}) {
3034                $TName_Tid{$Version}{$TypeAttr{"Name"}} = $TypeId;
3035            }
3036        }
3037        return %TypeAttr;
3038    }
3039}
3040
3041sub getTreeVec($)
3042{
3043    my %Vector = ();
3044    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3045    {
3046        while($Info=~s/ (\d+)[ ]*:[ ]*\@(\d+) / /)
3047        { # string length is N-1 because of the null terminator
3048            $Vector{$1} = $2;
3049        }
3050    }
3051    return \%Vector;
3052}
3053
3054sub get_TemplateParam($$)
3055{
3056    my ($Pos, $Type_Id) = @_;
3057    return () if(not $Type_Id);
3058    my $NodeType = $LibInfo{$Version}{"info_type"}{$Type_Id};
3059    return () if(not $NodeType);
3060    if($NodeType eq "integer_cst")
3061    { # int (1), unsigned (2u), char ('c' as 99), ...
3062        my $CstTid = getTreeAttr_Type($Type_Id);
3063        my %CstType = getTypeAttr($CstTid); # without recursion
3064        my $Num = getNodeIntCst($Type_Id);
3065        if(my $CstSuffix = $ConstantSuffix{$CstType{"Name"}}) {
3066            return ($Num.$CstSuffix);
3067        }
3068        else {
3069            return ("(".$CstType{"Name"}.")".$Num);
3070        }
3071    }
3072    elsif($NodeType eq "string_cst") {
3073        return (getNodeStrCst($Type_Id));
3074    }
3075    elsif($NodeType eq "tree_vec")
3076    {
3077        my $Vector = getTreeVec($Type_Id);
3078        my @Params = ();
3079        foreach my $P1 (sort {int($a)<=>int($b)} keys(%{$Vector}))
3080        {
3081            foreach my $P2 (get_TemplateParam($Pos, $Vector->{$P1})) {
3082                push(@Params, $P2);
3083            }
3084        }
3085        return @Params;
3086    }
3087    elsif($NodeType eq "parm_decl")
3088    {
3089        (getNameByInfo($Type_Id));
3090    }
3091    else
3092    {
3093        my %ParamAttr = getTypeAttr($Type_Id);
3094        my $PName = $ParamAttr{"Name"};
3095        if(not $PName) {
3096            return ();
3097        }
3098        if($PName=~/\>/)
3099        {
3100            if(my $Cover = cover_stdcxx_typedef($PName)) {
3101                $PName = $Cover;
3102            }
3103        }
3104        if($Pos>=1 and
3105        $PName=~/\A$DEFAULT_STD_PARMS\</)
3106        { # template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
3107          # template<typename _Key, typename _Compare = std::less<_Key>
3108          # template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
3109          # template<typename _Ch_type, typename _Rx_traits = regex_traits<_Ch_type> >
3110          # template<typename _CharT, typename _InIter = istreambuf_iterator<_CharT> >
3111          # template<typename _CharT, typename _OutIter = ostreambuf_iterator<_CharT> >
3112            return ("\@skip\@");
3113        }
3114        return ($PName);
3115    }
3116}
3117
3118sub cover_stdcxx_typedef($)
3119{
3120    my $TypeName = $_[0];
3121    if(my @Covers = sort {length($a)<=>length($b)}
3122    sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
3123    { # take the shortest typedef
3124      # FIXME: there may be more than
3125      # one typedefs to the same type
3126        return $Covers[0];
3127    }
3128    my $Covered = $TypeName;
3129    while($TypeName=~s/(>)[ ]*(const|volatile|restrict| |\*|\&)\Z/$1/g){};
3130    if(my @Covers = sort {length($a)<=>length($b)} sort keys(%{$StdCxxTypedef{$Version}{$TypeName}}))
3131    {
3132        if(my $Cover = $Covers[0])
3133        {
3134            $Covered=~s/\b\Q$TypeName\E(\W|\Z)/$Cover$1/g;
3135            $Covered=~s/\b\Q$TypeName\E(\w|\Z)/$Cover $1/g;
3136        }
3137    }
3138    return formatName($Covered, "T");
3139}
3140
3141sub getNodeIntCst($)
3142{
3143    my $CstId = $_[0];
3144    my $CstTypeId = getTreeAttr_Type($CstId);
3145    if($EnumMembName_Id{$Version}{$CstId}) {
3146        return $EnumMembName_Id{$Version}{$CstId};
3147    }
3148    elsif((my $Value = getTreeValue($CstId)) ne "")
3149    {
3150        if($Value eq "0")
3151        {
3152            if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") {
3153                return "false";
3154            }
3155            else {
3156                return "0";
3157            }
3158        }
3159        elsif($Value eq "1")
3160        {
3161            if($LibInfo{$Version}{"info_type"}{$CstTypeId} eq "boolean_type") {
3162                return "true";
3163            }
3164            else {
3165                return "1";
3166            }
3167        }
3168        else {
3169            return $Value;
3170        }
3171    }
3172    return "";
3173}
3174
3175sub getNodeStrCst($)
3176{
3177    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3178    {
3179        if($Info=~/strg[ ]*: (.+) lngt:[ ]*(\d+)/)
3180        {
3181            if($LibInfo{$Version}{"info_type"}{$_[0]} eq "string_cst")
3182            { # string length is N-1 because of the null terminator
3183                return substr($1, 0, $2-1);
3184            }
3185            else
3186            { # identifier_node
3187                return substr($1, 0, $2);
3188            }
3189        }
3190    }
3191    return "";
3192}
3193
3194sub getMemPtrAttr($$$)
3195{ # function, method and field pointers
3196    my ($PtrId, $TypeId, $Type) = @_;
3197    my $MemInfo = $LibInfo{$Version}{"info"}{$PtrId};
3198    if($Type eq "FieldPtr") {
3199        $MemInfo = $LibInfo{$Version}{"info"}{$TypeId};
3200    }
3201    my $MemInfo_Type = $LibInfo{$Version}{"info_type"}{$PtrId};
3202    my $MemPtrName = "";
3203    my %TypeAttr = ("Size"=>$WORD_SIZE{$Version}, "Type"=>$Type, "Tid"=>$TypeId);
3204    if($Type eq "MethodPtr")
3205    { # size of "method pointer" may be greater than WORD size
3206        if(my $Size = getSize($TypeId))
3207        {
3208            $Size/=$BYTE_SIZE;
3209            $TypeAttr{"Size"} = "$Size";
3210        }
3211    }
3212    if(my $Algn = getAlgn($TypeId)) {
3213        $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
3214    }
3215    # Return
3216    if($Type eq "FieldPtr")
3217    {
3218        my %ReturnAttr = getTypeAttr($PtrId);
3219        if($ReturnAttr{"Name"}) {
3220            $MemPtrName .= $ReturnAttr{"Name"};
3221        }
3222        $TypeAttr{"Return"} = $PtrId;
3223    }
3224    else
3225    {
3226        if($MemInfo=~/retn[ ]*:[ ]*\@(\d+) /)
3227        {
3228            my $ReturnTypeId = $1;
3229            my %ReturnAttr = getTypeAttr($ReturnTypeId);
3230            if(not $ReturnAttr{"Name"})
3231            { # templates
3232                return ();
3233            }
3234            $MemPtrName .= $ReturnAttr{"Name"};
3235            $TypeAttr{"Return"} = $ReturnTypeId;
3236        }
3237    }
3238    # Class
3239    if($MemInfo=~/(clas|cls)[ ]*:[ ]*@(\d+) /)
3240    {
3241        $TypeAttr{"Class"} = $2;
3242        my %Class = getTypeAttr($TypeAttr{"Class"});
3243        if($Class{"Name"}) {
3244            $MemPtrName .= " (".$Class{"Name"}."\:\:*)";
3245        }
3246        else {
3247            $MemPtrName .= " (*)";
3248        }
3249    }
3250    else {
3251        $MemPtrName .= " (*)";
3252    }
3253    # Parameters
3254    if($Type eq "FuncPtr"
3255    or $Type eq "MethodPtr")
3256    {
3257        my @ParamTypeName = ();
3258        if($MemInfo=~/prms[ ]*:[ ]*@(\d+) /)
3259        {
3260            my $PTypeInfoId = $1;
3261            my ($Pos, $PPos) = (0, 0);
3262            while($PTypeInfoId)
3263            {
3264                my $PTypeInfo = $LibInfo{$Version}{"info"}{$PTypeInfoId};
3265                if($PTypeInfo=~/valu[ ]*:[ ]*@(\d+) /)
3266                {
3267                    my $PTypeId = $1;
3268                    my %ParamAttr = getTypeAttr($PTypeId);
3269                    if(not $ParamAttr{"Name"})
3270                    { # templates (template_type_parm), etc.
3271                        return ();
3272                    }
3273                    if($ParamAttr{"Name"} eq "void") {
3274                        last;
3275                    }
3276                    if($Pos!=0 or $Type ne "MethodPtr")
3277                    {
3278                        $TypeAttr{"Param"}{$PPos++}{"type"} = $PTypeId;
3279                        push(@ParamTypeName, $ParamAttr{"Name"});
3280                    }
3281                    if($PTypeInfoId = getNextElem($PTypeInfoId)) {
3282                        $Pos+=1;
3283                    }
3284                    else {
3285                        last;
3286                    }
3287                }
3288                else {
3289                    last;
3290                }
3291            }
3292        }
3293        $MemPtrName .= " (".join(", ", @ParamTypeName).")";
3294    }
3295    $TypeAttr{"Name"} = formatName($MemPtrName, "T");
3296    return %TypeAttr;
3297}
3298
3299sub getTreeTypeName($)
3300{
3301    my $TypeId = $_[0];
3302    if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
3303    {
3304        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "integer_type")
3305        {
3306            if(my $Name = getNameByInfo($TypeId))
3307            { # bit_size_type
3308                return $Name;
3309            }
3310            elsif($Info=~/unsigned/) {
3311                return "unsigned int";
3312            }
3313            else {
3314                return "int";
3315            }
3316        }
3317        elsif($Info=~/name[ ]*:[ ]*@(\d+) /) {
3318            return getNameByInfo($1);
3319        }
3320    }
3321    return "";
3322}
3323
3324sub isFuncPtr($)
3325{
3326    my $Ptd = pointTo($_[0]);
3327    return 0 if(not $Ptd);
3328    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3329    {
3330        if($Info=~/unql[ ]*:/ and $Info!~/qual[ ]*:/) {
3331            return 0;
3332        }
3333    }
3334    if(my $InfoT1 = $LibInfo{$Version}{"info_type"}{$_[0]}
3335    and my $InfoT2 = $LibInfo{$Version}{"info_type"}{$Ptd})
3336    {
3337        if($InfoT1 eq "pointer_type"
3338        and $InfoT2 eq "function_type") {
3339            return 1;
3340        }
3341    }
3342    return 0;
3343}
3344
3345sub isMethodPtr($)
3346{
3347    my $Ptd = pointTo($_[0]);
3348    return 0 if(not $Ptd);
3349    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3350    {
3351        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "record_type"
3352        and $LibInfo{$Version}{"info_type"}{$Ptd} eq "method_type"
3353        and $Info=~/ ptrmem /) {
3354            return 1;
3355        }
3356    }
3357    return 0;
3358}
3359
3360sub isFieldPtr($)
3361{
3362    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3363    {
3364        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "offset_type"
3365        and $Info=~/ ptrmem /) {
3366            return 1;
3367        }
3368    }
3369    return 0;
3370}
3371
3372sub pointTo($)
3373{
3374    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3375    {
3376        if($Info=~/ptd[ ]*:[ ]*@(\d+)/) {
3377            return $1;
3378        }
3379    }
3380    return "";
3381}
3382
3383sub getTypeTypeByTypeId($)
3384{
3385    my $TypeId = $_[0];
3386    if(my $TType = $LibInfo{$Version}{"info_type"}{$TypeId})
3387    {
3388        my $NType = $NodeType{$TType};
3389        if($NType eq "Intrinsic") {
3390            return $NType;
3391        }
3392        elsif(isFuncPtr($TypeId)) {
3393            return "FuncPtr";
3394        }
3395        elsif(isMethodPtr($TypeId)) {
3396            return "MethodPtr";
3397        }
3398        elsif(isFieldPtr($TypeId)) {
3399            return "FieldPtr";
3400        }
3401        elsif($NType ne "Other") {
3402            return $NType;
3403        }
3404    }
3405    return "Unknown";
3406}
3407
3408my %UnQual = (
3409    "r"=>"restrict",
3410    "v"=>"volatile",
3411    "c"=>"const",
3412    "cv"=>"const volatile"
3413);
3414
3415sub getQual($)
3416{
3417    my $TypeId = $_[0];
3418    if(my $Info = $LibInfo{$Version}{"info"}{$TypeId})
3419    {
3420        my ($Qual, $To) = ();
3421        if($Info=~/qual[ ]*:[ ]*(r|c|v|cv) /) {
3422            $Qual = $UnQual{$1};
3423        }
3424        if($Info=~/unql[ ]*:[ ]*\@(\d+)/) {
3425            $To = $1;
3426        }
3427        if($Qual and $To) {
3428            return ($Qual, $To);
3429        }
3430    }
3431    return ();
3432}
3433
3434sub getQualType($)
3435{
3436    if($_[0] eq "const volatile") {
3437        return "ConstVolatile";
3438    }
3439    return ucfirst($_[0]);
3440}
3441
3442sub getTypeType($)
3443{
3444    my $TypeId = $_[0];
3445    my $TypeDeclId = getTypeDeclId($TypeId);
3446    if(defined $MissedTypedef{$Version}{$TypeId})
3447    { # support for old GCC versions
3448        if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq $TypeDeclId) {
3449            return "Typedef";
3450        }
3451    }
3452    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
3453    my ($Qual, $To) = getQual($TypeId);
3454    if(($Qual or $To) and $TypeDeclId
3455    and (getTypeId($TypeDeclId) ne $TypeId))
3456    { # qualified types (special)
3457        return getQualType($Qual);
3458    }
3459    elsif(not $MissedBase_R{$Version}{$TypeId}
3460    and isTypedef($TypeId)) {
3461        return "Typedef";
3462    }
3463    elsif($Qual)
3464    { # qualified types
3465        return getQualType($Qual);
3466    }
3467
3468    if($Info=~/unql[ ]*:[ ]*\@(\d+)/)
3469    { # typedef struct { ... } name
3470        $TypeTypedef{$Version}{$TypeId} = $1;
3471    }
3472
3473    my $TypeType = getTypeTypeByTypeId($TypeId);
3474    if($TypeType eq "Struct")
3475    {
3476        if($TypeDeclId
3477        and $LibInfo{$Version}{"info_type"}{$TypeDeclId} eq "template_decl") {
3478            return "Template";
3479        }
3480    }
3481    return $TypeType;
3482}
3483
3484sub isTypedef($)
3485{
3486    if($_[0])
3487    {
3488        if($LibInfo{$Version}{"info_type"}{$_[0]} eq "vector_type")
3489        { # typedef float La_x86_64_xmm __attribute__ ((__vector_size__ (16)));
3490            return 0;
3491        }
3492        if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
3493        {
3494            if(my $TDid = getTypeDeclId($_[0]))
3495            {
3496                if(getTypeId($TDid) eq $_[0]
3497                and getNameByInfo($TDid))
3498                {
3499                    if($Info=~/unql[ ]*:[ ]*\@(\d+) /) {
3500                        return $1;
3501                    }
3502                }
3503            }
3504        }
3505    }
3506    return 0;
3507}
3508
3509sub selectBaseType($)
3510{
3511    my $TypeId = $_[0];
3512    if(defined $MissedTypedef{$Version}{$TypeId})
3513    { # add missed typedefs
3514        if($MissedTypedef{$Version}{$TypeId}{"TDid"} eq getTypeDeclId($TypeId)) {
3515            return ($TypeId, "");
3516        }
3517    }
3518    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
3519    my $InfoType = $LibInfo{$Version}{"info_type"}{$TypeId};
3520
3521    my $MB_R = $MissedBase_R{$Version}{$TypeId};
3522    my $MB = $MissedBase{$Version}{$TypeId};
3523
3524    my ($Qual, $To) = getQual($TypeId);
3525    if(($Qual or $To) and $Info=~/name[ ]*:[ ]*\@(\d+) /
3526    and (getTypeId($1) ne $TypeId)
3527    and (not $MB_R or getTypeId($1) ne $MB_R))
3528    { # qualified types (special)
3529        return (getTypeId($1), $Qual);
3530    }
3531    elsif($MB)
3532    { # add base
3533        return ($MB, "");
3534    }
3535    elsif(not $MB_R and my $Bid = isTypedef($TypeId))
3536    { # typedefs
3537        return ($Bid, "");
3538    }
3539    elsif($Qual or $To)
3540    { # qualified types
3541        return ($To, $Qual);
3542    }
3543    elsif($InfoType eq "reference_type")
3544    {
3545        if($Info=~/refd[ ]*:[ ]*@(\d+) /) {
3546            return ($1, "&");
3547        }
3548    }
3549    elsif($InfoType eq "array_type")
3550    {
3551        if($Info=~/elts[ ]*:[ ]*@(\d+) /) {
3552            return ($1, "");
3553        }
3554    }
3555    elsif($InfoType eq "pointer_type")
3556    {
3557        if($Info=~/ptd[ ]*:[ ]*@(\d+) /) {
3558            return ($1, "*");
3559        }
3560    }
3561
3562    return (0, "");
3563}
3564
3565sub getSymbolInfo_All()
3566{
3567    foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
3568    { # reverse order
3569        if($LibInfo{$Version}{"info_type"}{$_} eq "function_decl") {
3570            getSymbolInfo($_);
3571        }
3572    }
3573
3574    if($ADD_TMPL_INSTANCES)
3575    {
3576        # templates
3577        foreach my $Sid (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$Version}}))
3578        {
3579            my %Map = ();
3580
3581            if(my $ClassId = $SymbolInfo{$Version}{$Sid}{"Class"})
3582            {
3583                if(defined $TemplateMap{$Version}{$ClassId})
3584                {
3585                    foreach (keys(%{$TemplateMap{$Version}{$ClassId}})) {
3586                        $Map{$_} = $TemplateMap{$Version}{$ClassId}{$_};
3587                    }
3588                }
3589            }
3590
3591            if(defined $TemplateMap{$Version}{$Sid})
3592            {
3593                foreach (keys(%{$TemplateMap{$Version}{$Sid}})) {
3594                    $Map{$_} = $TemplateMap{$Version}{$Sid}{$_};
3595                }
3596            }
3597
3598            if(defined $SymbolInfo{$Version}{$Sid}{"Param"})
3599            {
3600                foreach (keys(%{$SymbolInfo{$Version}{$Sid}{"Param"}}))
3601                {
3602                    my $PTid = $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"};
3603                    $SymbolInfo{$Version}{$Sid}{"Param"}{$_}{"type"} = instType(\%Map, $PTid, $Version);
3604                }
3605            }
3606            if(my $Return = $SymbolInfo{$Version}{$Sid}{"Return"}) {
3607                $SymbolInfo{$Version}{$Sid}{"Return"} = instType(\%Map, $Return, $Version);
3608            }
3609        }
3610    }
3611}
3612
3613sub getVarInfo_All()
3614{
3615    foreach (sort {int($b)<=>int($a)} keys(%{$LibInfo{$Version}{"info"}}))
3616    { # reverse order
3617        if($LibInfo{$Version}{"info_type"}{$_} eq "var_decl") {
3618            getVarInfo($_);
3619        }
3620    }
3621}
3622
3623sub isBuiltIn($) {
3624    return ($_[0] and $_[0]=~/\<built\-in\>|\<internal\>|\A\./);
3625}
3626
3627sub getVarInfo($)
3628{
3629    my $InfoId = $_[0];
3630    if(my $NSid = getTreeAttr_Scpe($InfoId))
3631    {
3632        my $NSInfoType = $LibInfo{$Version}{"info_type"}{$NSid};
3633        if($NSInfoType and $NSInfoType eq "function_decl") {
3634            return;
3635        }
3636    }
3637    ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId);
3638    if(not $SymbolInfo{$Version}{$InfoId}{"Header"}
3639    or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"})) {
3640        delete($SymbolInfo{$Version}{$InfoId});
3641        return;
3642    }
3643    my $ShortName = getTreeStr(getTreeAttr_Name($InfoId));
3644    if(not $ShortName) {
3645        delete($SymbolInfo{$Version}{$InfoId});
3646        return;
3647    }
3648    if($ShortName=~/\Atmp_add_class_\d+\Z/) {
3649        delete($SymbolInfo{$Version}{$InfoId});
3650        return;
3651    }
3652    $SymbolInfo{$Version}{$InfoId}{"ShortName"} = $ShortName;
3653    if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId)))
3654    {
3655        if($OSgroup eq "windows")
3656        { # cut the offset
3657            $MnglName=~s/\@\d+\Z//g;
3658        }
3659        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName;
3660    }
3661    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}
3662    and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0)
3663    { # validate mangled name
3664        delete($SymbolInfo{$Version}{$InfoId});
3665        return;
3666    }
3667    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}
3668    and index($ShortName, "_Z")==0)
3669    { # _ZTS, etc.
3670        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3671    }
3672    if(isPrivateData($SymbolInfo{$Version}{$InfoId}{"MnglName"}))
3673    { # non-public global data
3674        delete($SymbolInfo{$Version}{$InfoId});
3675        return;
3676    }
3677    $SymbolInfo{$Version}{$InfoId}{"Data"} = 1;
3678    if(my $Rid = getTypeId($InfoId))
3679    {
3680        if(not defined $TypeInfo{$Version}{$Rid}
3681        or not $TypeInfo{$Version}{$Rid}{"Name"})
3682        {
3683            delete($SymbolInfo{$Version}{$InfoId});
3684            return;
3685        }
3686        $SymbolInfo{$Version}{$InfoId}{"Return"} = $Rid;
3687        my $Val = getDataVal($InfoId, $Rid);
3688        if(defined $Val) {
3689            $SymbolInfo{$Version}{$InfoId}{"Value"} = $Val;
3690        }
3691    }
3692    set_Class_And_Namespace($InfoId);
3693    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
3694    {
3695        if(not defined $TypeInfo{$Version}{$ClassId}
3696        or not $TypeInfo{$Version}{$ClassId}{"Name"})
3697        {
3698            delete($SymbolInfo{$Version}{$InfoId});
3699            return;
3700        }
3701    }
3702    if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i)
3703    { # extern "C"
3704        $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C";
3705        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3706    }
3707    if($UserLang and $UserLang eq "C")
3708    { # --lang=C option
3709        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3710    }
3711    if(not $CheckHeadersOnly)
3712    {
3713        if(not $SymbolInfo{$Version}{$InfoId}{"Class"})
3714        {
3715            if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}
3716            or not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps"))
3717            {
3718                if(link_symbol($ShortName, $Version, "-Deps"))
3719                { # "const" global data is mangled as _ZL... in the TU dump
3720                  # but not mangled when compiling a C shared library
3721                    $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3722                }
3723            }
3724        }
3725    }
3726    if($COMMON_LANGUAGE{$Version} eq "C++")
3727    {
3728        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3729        { # for some symbols (_ZTI) the short name is the mangled name
3730            if(index($ShortName, "_Z")==0) {
3731                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3732            }
3733        }
3734        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3735        { # try to mangle symbol (link with libraries)
3736            $SymbolInfo{$Version}{$InfoId}{"MnglName"} = linkSymbol($InfoId);
3737        }
3738        if($OStarget eq "windows")
3739        {
3740            if(my $Mangled = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")})
3741            { # link MS C++ symbols from library with GCC symbols from headers
3742                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled;
3743            }
3744        }
3745    }
3746    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"}) {
3747        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $ShortName;
3748    }
3749    if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"})
3750    {
3751        if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version))
3752        { # non-target symbols
3753            delete($SymbolInfo{$Version}{$InfoId});
3754            return;
3755        }
3756    }
3757    if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"})
3758    {
3759        if(defined $MissedTypedef{$Version}{$Rid})
3760        {
3761            if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) {
3762                $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid;
3763            }
3764        }
3765    }
3766    setFuncAccess($InfoId);
3767    if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_ZTV")==0) {
3768        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
3769    }
3770    if($ShortName=~/\A(_Z|\?)/) {
3771        delete($SymbolInfo{$Version}{$InfoId}{"ShortName"});
3772    }
3773
3774    if($ExtraDump) {
3775        $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId);
3776    }
3777}
3778
3779sub isConstType($$)
3780{
3781    my ($TypeId, $LibVersion) = @_;
3782    my %Base = get_Type($TypeId, $LibVersion);
3783    while(defined $Base{"Type"} and $Base{"Type"} eq "Typedef") {
3784        %Base = get_OneStep_BaseType($Base{"Tid"}, $TypeInfo{$LibVersion});
3785    }
3786    return ($Base{"Type"} eq "Const");
3787}
3788
3789sub getTrivialName($$)
3790{
3791    my ($TypeInfoId, $TypeId) = @_;
3792    my %TypeAttr = ();
3793    $TypeAttr{"Name"} = getNameByInfo($TypeInfoId);
3794    if(not $TypeAttr{"Name"}) {
3795        $TypeAttr{"Name"} = getTreeTypeName($TypeId);
3796    }
3797    ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId);
3798    $TypeAttr{"Type"} = getTypeType($TypeId);
3799    $TypeAttr{"Name"}=~s/<(.+)\Z//g; # GCC 3.4.4 add template params to the name
3800    if(isAnon($TypeAttr{"Name"}))
3801    {
3802        my $NameSpaceId = $TypeId;
3803        while(my $NSId = getTreeAttr_Scpe(getTypeDeclId($NameSpaceId)))
3804        { # searching for a first not anon scope
3805            if($NSId eq $NameSpaceId) {
3806                last;
3807            }
3808            else
3809            {
3810                $TypeAttr{"NameSpace"} = getNameSpace(getTypeDeclId($TypeId));
3811                if(not $TypeAttr{"NameSpace"}
3812                or not isAnon($TypeAttr{"NameSpace"})) {
3813                    last;
3814                }
3815            }
3816            $NameSpaceId = $NSId;
3817        }
3818    }
3819    else
3820    {
3821        if(my $NameSpaceId = getTreeAttr_Scpe($TypeInfoId))
3822        {
3823            if($NameSpaceId ne $TypeId) {
3824                $TypeAttr{"NameSpace"} = getNameSpace($TypeInfoId);
3825            }
3826        }
3827    }
3828    if($TypeAttr{"NameSpace"} and not isAnon($TypeAttr{"Name"})) {
3829        $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
3830    }
3831    $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}, "T");
3832    if(isAnon($TypeAttr{"Name"}))
3833    { # anon-struct-header.h-line
3834        $TypeAttr{"Name"} = "anon-".lc($TypeAttr{"Type"})."-";
3835        $TypeAttr{"Name"} .= $TypeAttr{"Header"}."-".$TypeAttr{"Line"};
3836        if($TypeAttr{"NameSpace"}) {
3837            $TypeAttr{"Name"} = $TypeAttr{"NameSpace"}."::".$TypeAttr{"Name"};
3838        }
3839    }
3840    if(defined $TemplateInstance{$Version}{"Type"}{$TypeId}
3841    and getTypeDeclId($TypeId) eq $TypeInfoId)
3842    {
3843        if(my @TParams = getTParams($TypeId, "Type")) {
3844            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."< ".join(", ", @TParams)." >", "T");
3845        }
3846        else {
3847            $TypeAttr{"Name"} = formatName($TypeAttr{"Name"}."<...>", "T");
3848        }
3849    }
3850    return ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"});
3851}
3852
3853sub getTrivialTypeAttr($)
3854{
3855    my $TypeId = $_[0];
3856    my $TypeInfoId = getTypeDeclId($_[0]);
3857
3858    my %TypeAttr = ();
3859
3860    if($TemplateDecl{$Version}{$TypeId})
3861    { # template_decl
3862        $TypeAttr{"Template"} = 1;
3863    }
3864
3865    setTypeAccess($TypeId, \%TypeAttr);
3866    ($TypeAttr{"Header"}, $TypeAttr{"Line"}) = getLocation($TypeInfoId);
3867    if(isBuiltIn($TypeAttr{"Header"}))
3868    {
3869        delete($TypeAttr{"Header"});
3870        delete($TypeAttr{"Line"});
3871    }
3872
3873    $TypeAttr{"Type"} = getTypeType($TypeId);
3874    ($TypeAttr{"Name"}, $TypeAttr{"NameSpace"}) = getTrivialName($TypeInfoId, $TypeId);
3875    if(not $TypeAttr{"Name"}) {
3876        return ();
3877    }
3878    if(not $TypeAttr{"NameSpace"}) {
3879        delete($TypeAttr{"NameSpace"});
3880    }
3881
3882    if($TypeAttr{"Type"} eq "Intrinsic")
3883    {
3884        if(defined $TypeAttr{"Header"})
3885        {
3886            if($TypeAttr{"Header"}=~/\Adump[1-2]\.[ih]\Z/)
3887            { # support for SUSE 11.2
3888              # integer_type has srcp dump{1-2}.i
3889                delete($TypeAttr{"Header"});
3890            }
3891        }
3892    }
3893
3894    my $Tmpl = undef;
3895
3896    if(defined $TemplateInstance{$Version}{"Type"}{$TypeId})
3897    {
3898        $Tmpl = $BasicTemplate{$Version}{$TypeId};
3899
3900        if(my @TParams = getTParams($TypeId, "Type"))
3901        {
3902            foreach my $Pos (0 .. $#TParams)
3903            {
3904                my $Val = $TParams[$Pos];
3905                $TypeAttr{"TParam"}{$Pos}{"name"} = $Val;
3906
3907                if(not defined $TypeAttr{"Template"})
3908                {
3909                    my %Base = get_BaseType($TemplateInstance{$Version}{"Type"}{$TypeId}{$Pos}, $Version);
3910
3911                    if($Base{"Type"} eq "TemplateParam"
3912                    or defined $Base{"Template"}) {
3913                        $TypeAttr{"Template"} = 1;
3914                    }
3915                }
3916
3917                if($Tmpl)
3918                {
3919                    if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos})
3920                    {
3921                        $TemplateMap{$Version}{$TypeId}{$Arg} = $Val;
3922
3923                        if($Val eq $Arg) {
3924                            $TypeAttr{"Template"} = 1;
3925                        }
3926                    }
3927                }
3928            }
3929
3930            if($Tmpl)
3931            {
3932                foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}}))
3933                {
3934                    if($Pos>$#TParams)
3935                    {
3936                        my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos};
3937                        $TemplateMap{$Version}{$TypeId}{$Arg} = "";
3938                    }
3939                }
3940            }
3941        }
3942
3943        if($ADD_TMPL_INSTANCES)
3944        {
3945            if($Tmpl)
3946            {
3947                if(my $MainInst = getTreeAttr_Type($Tmpl))
3948                {
3949                    if(not getTreeAttr_Flds($TypeId))
3950                    {
3951                        if(my $Flds = getTreeAttr_Flds($MainInst)) {
3952                            $LibInfo{$Version}{"info"}{$TypeId} .= " flds: \@$Flds ";
3953                        }
3954                    }
3955                    if(not getTreeAttr_Binf($TypeId))
3956                    {
3957                        if(my $Binf = getTreeAttr_Binf($MainInst)) {
3958                            $LibInfo{$Version}{"info"}{$TypeId} .= " binf: \@$Binf ";
3959                        }
3960                    }
3961                }
3962            }
3963        }
3964    }
3965
3966    my $StaticFields = setTypeMemb($TypeId, \%TypeAttr);
3967
3968    if(my $Size = getSize($TypeId))
3969    {
3970        $Size = $Size/$BYTE_SIZE;
3971        $TypeAttr{"Size"} = "$Size";
3972    }
3973    else
3974    {
3975        if($ExtraDump)
3976        {
3977            if(not defined $TypeAttr{"Memb"}
3978            and not $Tmpl)
3979            { # declaration only
3980                $TypeAttr{"Forward"} = 1;
3981            }
3982        }
3983    }
3984
3985    if($TypeAttr{"Type"} eq "Struct"
3986    and ($StaticFields or detect_lang($TypeId)))
3987    {
3988        $TypeAttr{"Type"} = "Class";
3989        $TypeAttr{"Copied"} = 1; # default, will be changed in getSymbolInfo()
3990    }
3991    if($TypeAttr{"Type"} eq "Struct"
3992    or $TypeAttr{"Type"} eq "Class")
3993    {
3994        my $Skip = setBaseClasses($TypeId, \%TypeAttr);
3995        if($Skip) {
3996            return ();
3997        }
3998    }
3999    if(my $Algn = getAlgn($TypeId)) {
4000        $TypeAttr{"Algn"} = $Algn/$BYTE_SIZE;
4001    }
4002    setSpec($TypeId, \%TypeAttr);
4003
4004    if($TypeAttr{"Type"}=~/\A(Struct|Union|Enum)\Z/)
4005    {
4006        if(not $TypedefToAnon{$TypeId}
4007        and not defined $TemplateInstance{$Version}{"Type"}{$TypeId})
4008        {
4009            if(not isAnon($TypeAttr{"Name"})) {
4010                $TypeAttr{"Name"} = lc($TypeAttr{"Type"})." ".$TypeAttr{"Name"};
4011            }
4012        }
4013    }
4014
4015    $TypeAttr{"Tid"} = $TypeId;
4016    if(my $VTable = $ClassVTable_Content{$Version}{$TypeAttr{"Name"}})
4017    {
4018        my @Entries = split(/\n/, $VTable);
4019        foreach (1 .. $#Entries)
4020        {
4021            my $Entry = $Entries[$_];
4022            if($Entry=~/\A(\d+)\s+(.+)\Z/) {
4023                $TypeAttr{"VTable"}{$1} = simplifyVTable($2);
4024            }
4025        }
4026    }
4027
4028    if($TypeAttr{"Type"} eq "Enum")
4029    {
4030        if(not $TypeAttr{"NameSpace"})
4031        {
4032            foreach my $Pos (keys(%{$TypeAttr{"Memb"}}))
4033            {
4034                my $MName = $TypeAttr{"Memb"}{$Pos}{"name"};
4035                my $MVal = $TypeAttr{"Memb"}{$Pos}{"value"};
4036                $EnumConstants{$Version}{$MName} = {
4037                    "Value"=>$MVal,
4038                    "Header"=>$TypeAttr{"Header"}
4039                };
4040                if(isAnon($TypeAttr{"Name"}))
4041                {
4042                    if($ExtraDump
4043                    or is_target_header($TypeAttr{"Header"}, $Version))
4044                    {
4045                        %{$Constants{$Version}{$MName}} = (
4046                            "Value" => $MVal,
4047                            "Header" => $TypeAttr{"Header"}
4048                        );
4049                    }
4050                }
4051            }
4052        }
4053    }
4054    if($ExtraDump)
4055    {
4056        if(defined $TypedefToAnon{$TypeId}) {
4057            $TypeAttr{"AnonTypedef"} = 1;
4058        }
4059    }
4060
4061    return %TypeAttr;
4062}
4063
4064sub simplifyVTable($)
4065{
4066    my $Content = $_[0];
4067    if($Content=~s/ \[with (.+)]//)
4068    { # std::basic_streambuf<_CharT, _Traits>::imbue [with _CharT = char, _Traits = std::char_traits<char>]
4069        if(my @Elems = separate_Params($1, 0, 0))
4070        {
4071            foreach my $Elem (@Elems)
4072            {
4073                if($Elem=~/\A(.+?)\s*=\s*(.+?)\Z/)
4074                {
4075                    my ($Arg, $Val) = ($1, $2);
4076
4077                    if(defined $DEFAULT_STD_ARGS{$Arg}) {
4078                        $Content=~s/,\s*$Arg\b//g;
4079                    }
4080                    else {
4081                        $Content=~s/\b$Arg\b/$Val/g;
4082                    }
4083                }
4084            }
4085        }
4086    }
4087
4088    return $Content;
4089}
4090
4091sub detect_lang($)
4092{
4093    my $TypeId = $_[0];
4094    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4095    if(check_gcc($GCC_PATH, "4"))
4096    { # GCC 4 fncs-node points to only non-artificial methods
4097        return ($Info=~/(fncs)[ ]*:[ ]*@(\d+) /);
4098    }
4099    else
4100    { # GCC 3
4101        my $Fncs = getTreeAttr_Fncs($TypeId);
4102        while($Fncs)
4103        {
4104            if($LibInfo{$Version}{"info"}{$Fncs}!~/artificial/) {
4105                return 1;
4106            }
4107            $Fncs = getTreeAttr_Chan($Fncs);
4108        }
4109    }
4110    return 0;
4111}
4112
4113sub setSpec($$)
4114{
4115    my ($TypeId, $TypeAttr) = @_;
4116    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4117    if($Info=~/\s+spec\s+/) {
4118        $TypeAttr->{"Spec"} = 1;
4119    }
4120}
4121
4122sub setBaseClasses($$)
4123{
4124    my ($TypeId, $TypeAttr) = @_;
4125    my $Info = $LibInfo{$Version}{"info"}{$TypeId};
4126    if(my $Binf = getTreeAttr_Binf($TypeId))
4127    {
4128        my $Info = $LibInfo{$Version}{"info"}{$Binf};
4129        my $Pos = 0;
4130        while($Info=~s/(pub|public|prot|protected|priv|private|)[ ]+binf[ ]*:[ ]*@(\d+) //)
4131        {
4132            my ($Access, $BInfoId) = ($1, $2);
4133            my $ClassId = getBinfClassId($BInfoId);
4134
4135            if($ClassId==$TypeId)
4136            { # class A<N>:public A<N-1>
4137                next;
4138            }
4139
4140            my $CType = $LibInfo{$Version}{"info_type"}{$ClassId};
4141            if(not $CType or $CType eq "template_type_parm"
4142            or $CType eq "typename_type")
4143            { # skip
4144                # return 1;
4145            }
4146            my $BaseInfo = $LibInfo{$Version}{"info"}{$BInfoId};
4147            if($Access=~/prot/) {
4148                $TypeAttr->{"Base"}{$ClassId}{"access"} = "protected";
4149            }
4150            elsif($Access=~/priv/) {
4151                $TypeAttr->{"Base"}{$ClassId}{"access"} = "private";
4152            }
4153            $TypeAttr->{"Base"}{$ClassId}{"pos"} = "$Pos";
4154            if($BaseInfo=~/virt/)
4155            { # virtual base
4156                $TypeAttr->{"Base"}{$ClassId}{"virtual"} = 1;
4157            }
4158            $Class_SubClasses{$Version}{$ClassId}{$TypeId}=1;
4159            $Pos += 1;
4160        }
4161    }
4162    return 0;
4163}
4164
4165sub getBinfClassId($)
4166{
4167    my $Info = $LibInfo{$Version}{"info"}{$_[0]};
4168    $Info=~/type[ ]*:[ ]*@(\d+) /;
4169    return $1;
4170}
4171
4172sub unmangledFormat($$)
4173{
4174    my ($Name, $LibVersion) = @_;
4175    $Name = uncover_typedefs($Name, $LibVersion);
4176    while($Name=~s/([^\w>*])(const|volatile)(,|>|\Z)/$1$3/g){};
4177    $Name=~s/\(\w+\)(\d)/$1/;
4178    return $Name;
4179}
4180
4181sub modelUnmangled($$)
4182{
4183    my ($InfoId, $Compiler) = @_;
4184    if($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId}) {
4185        return $Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId};
4186    }
4187    my $PureSignature = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
4188    if($SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
4189        $PureSignature = "~".$PureSignature;
4190    }
4191    if(not $SymbolInfo{$Version}{$InfoId}{"Data"})
4192    {
4193        my (@Params, @ParamTypes) = ();
4194        if(defined $SymbolInfo{$Version}{$InfoId}{"Param"}
4195        and not $SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
4196            @Params = keys(%{$SymbolInfo{$Version}{$InfoId}{"Param"}});
4197        }
4198        foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
4199        { # checking parameters
4200            my $PId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"type"};
4201            my $PName = $SymbolInfo{$Version}{$InfoId}{"Param"}{$ParamPos}{"name"};
4202            my %PType = get_PureType($PId, $TypeInfo{$Version});
4203            my $PTName = unmangledFormat($PType{"Name"}, $Version);
4204
4205            if($PName eq "this"
4206            and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method")
4207            {
4208                next;
4209            }
4210
4211            $PTName=~s/\b(restrict|register)\b//g;
4212            if($Compiler eq "MSVC") {
4213                $PTName=~s/\blong long\b/__int64/;
4214            }
4215            @ParamTypes = (@ParamTypes, $PTName);
4216        }
4217        if(@ParamTypes) {
4218            $PureSignature .= "(".join(", ", @ParamTypes).")";
4219        }
4220        else
4221        {
4222            if($Compiler eq "MSVC")
4223            {
4224                $PureSignature .= "(void)";
4225            }
4226            else
4227            { # GCC
4228                $PureSignature .= "()";
4229            }
4230        }
4231        $PureSignature = delete_keywords($PureSignature);
4232    }
4233    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
4234    {
4235        my $ClassName = unmangledFormat($TypeInfo{$Version}{$ClassId}{"Name"}, $Version);
4236        $PureSignature = $ClassName."::".$PureSignature;
4237    }
4238    elsif(my $NS = $SymbolInfo{$Version}{$InfoId}{"NameSpace"}) {
4239        $PureSignature = $NS."::".$PureSignature;
4240    }
4241    if($SymbolInfo{$Version}{$InfoId}{"Const"}) {
4242        $PureSignature .= " const";
4243    }
4244    if($SymbolInfo{$Version}{$InfoId}{"Volatile"}) {
4245        $PureSignature .= " volatile";
4246    }
4247    my $ShowReturn = 0;
4248    if($Compiler eq "MSVC"
4249    and $SymbolInfo{$Version}{$InfoId}{"Data"})
4250    {
4251        $ShowReturn=1;
4252    }
4253    elsif(defined $TemplateInstance{$Version}{"Func"}{$InfoId}
4254    and keys(%{$TemplateInstance{$Version}{"Func"}{$InfoId}}))
4255    {
4256        $ShowReturn=1;
4257    }
4258    if($ShowReturn)
4259    { # mangled names for template function specializations include return value
4260        if(my $ReturnId = $SymbolInfo{$Version}{$InfoId}{"Return"})
4261        {
4262            my %RType = get_PureType($ReturnId, $TypeInfo{$Version});
4263            my $ReturnName = unmangledFormat($RType{"Name"}, $Version);
4264            $PureSignature = $ReturnName." ".$PureSignature;
4265        }
4266    }
4267    return ($Cache{"modelUnmangled"}{$Version}{$Compiler}{$InfoId} = formatName($PureSignature, "S"));
4268}
4269
4270sub mangle_symbol($$$)
4271{ # mangling for simple methods
4272  # see gcc-4.6.0/gcc/cp/mangle.c
4273    my ($InfoId, $LibVersion, $Compiler) = @_;
4274    if($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler}) {
4275        return $Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler};
4276    }
4277    my $Mangled = "";
4278    if($Compiler eq "GCC") {
4279        $Mangled = mangle_symbol_GCC($InfoId, $LibVersion);
4280    }
4281    elsif($Compiler eq "MSVC") {
4282        $Mangled = mangle_symbol_MSVC($InfoId, $LibVersion);
4283    }
4284    return ($Cache{"mangle_symbol"}{$LibVersion}{$InfoId}{$Compiler} = $Mangled);
4285}
4286
4287sub mangle_symbol_MSVC($$)
4288{ # TODO
4289    my ($InfoId, $LibVersion) = @_;
4290    return "";
4291}
4292
4293sub mangle_symbol_GCC($$)
4294{ # see gcc-4.6.0/gcc/cp/mangle.c
4295    my ($InfoId, $LibVersion) = @_;
4296    my ($Mangled, $ClassId, $NameSpace) = ("_Z", 0, "");
4297    my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"};
4298    my %Repl = ();# SN_ replacements
4299    if($ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
4300    {
4301        my $MangledClass = mangle_param($ClassId, $LibVersion, \%Repl);
4302        if($MangledClass!~/\AN/) {
4303            $MangledClass = "N".$MangledClass;
4304        }
4305        else {
4306            $MangledClass=~s/E\Z//;
4307        }
4308        if($SymbolInfo{$LibVersion}{$InfoId}{"Volatile"}) {
4309            $MangledClass=~s/\AN/NV/;
4310        }
4311        if($SymbolInfo{$LibVersion}{$InfoId}{"Const"}) {
4312            $MangledClass=~s/\AN/NK/;
4313        }
4314        $Mangled .= $MangledClass;
4315    }
4316    elsif($NameSpace = $SymbolInfo{$LibVersion}{$InfoId}{"NameSpace"})
4317    { # mangled by name due to the absence of structured info
4318        my $MangledNS = mangle_ns($NameSpace, $LibVersion, \%Repl);
4319        if($MangledNS!~/\AN/) {
4320            $MangledNS = "N".$MangledNS;
4321        }
4322        else {
4323            $MangledNS=~s/E\Z//;
4324        }
4325        $Mangled .= $MangledNS;
4326    }
4327    my ($ShortName, $TmplParams) = template_Base($SymbolInfo{$LibVersion}{$InfoId}{"ShortName"});
4328    my @TParams = ();
4329    if(my @TPos = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}}))
4330    { # parsing mode
4331        foreach (@TPos) {
4332            push(@TParams, $SymbolInfo{$LibVersion}{$InfoId}{"TParam"}{$_}{"name"});
4333        }
4334    }
4335    elsif($TmplParams)
4336    { # remangling mode
4337      # support for old ABI dumps
4338        @TParams = separate_Params($TmplParams, 0, 0);
4339    }
4340    if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"}) {
4341        $Mangled .= "C1";
4342    }
4343    elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
4344        $Mangled .= "D0";
4345    }
4346    elsif($ShortName)
4347    {
4348        if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
4349        {
4350            if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
4351            and isConstType($Return, $LibVersion))
4352            { # "const" global data is mangled as _ZL...
4353                $Mangled .= "L";
4354            }
4355        }
4356        if($ShortName=~/\Aoperator(\W.*)\Z/)
4357        {
4358            my $Op = $1;
4359            $Op=~s/\A[ ]+//g;
4360            if(my $OpMngl = $OperatorMangling{$Op}) {
4361                $Mangled .= $OpMngl;
4362            }
4363            else { # conversion operator
4364                $Mangled .= "cv".mangle_param(getTypeIdByName($Op, $LibVersion), $LibVersion, \%Repl);
4365            }
4366        }
4367        else {
4368            $Mangled .= length($ShortName).$ShortName;
4369        }
4370        if(@TParams)
4371        { # templates
4372            $Mangled .= "I";
4373            foreach my $TParam (@TParams) {
4374                $Mangled .= mangle_template_param($TParam, $LibVersion, \%Repl);
4375            }
4376            $Mangled .= "E";
4377        }
4378        if(not $ClassId and @TParams) {
4379            add_substitution($ShortName, \%Repl, 0);
4380        }
4381    }
4382    if($ClassId or $NameSpace) {
4383        $Mangled .= "E";
4384    }
4385    if(@TParams)
4386    {
4387        if($Return) {
4388            $Mangled .= mangle_param($Return, $LibVersion, \%Repl);
4389        }
4390    }
4391    if(not $SymbolInfo{$LibVersion}{$InfoId}{"Data"})
4392    {
4393        my @Params = ();
4394        if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
4395        and not $SymbolInfo{$LibVersion}{$InfoId}{"Destructor"}) {
4396            @Params = keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}});
4397        }
4398        foreach my $ParamPos (sort {int($a) <=> int($b)} @Params)
4399        { # checking parameters
4400            my $ParamType_Id = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$ParamPos}{"type"};
4401            $Mangled .= mangle_param($ParamType_Id, $LibVersion, \%Repl);
4402        }
4403        if(not @Params) {
4404            $Mangled .= "v";
4405        }
4406    }
4407    $Mangled = correct_incharge($InfoId, $LibVersion, $Mangled);
4408    $Mangled = write_stdcxx_substitution($Mangled);
4409    if($Mangled eq "_Z") {
4410        return "";
4411    }
4412    return $Mangled;
4413}
4414
4415sub correct_incharge($$$)
4416{
4417    my ($InfoId, $LibVersion, $Mangled) = @_;
4418    if($SymbolInfo{$LibVersion}{$InfoId}{"Constructor"})
4419    {
4420        if($MangledNames{$LibVersion}{$Mangled}) {
4421            $Mangled=~s/C1([EI])/C2$1/;
4422        }
4423    }
4424    elsif($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"})
4425    {
4426        if($MangledNames{$LibVersion}{$Mangled}) {
4427            $Mangled=~s/D0([EI])/D1$1/;
4428        }
4429        if($MangledNames{$LibVersion}{$Mangled}) {
4430            $Mangled=~s/D1([EI])/D2$1/;
4431        }
4432    }
4433    return $Mangled;
4434}
4435
4436sub template_Base($)
4437{ # NOTE: std::_Vector_base<mysqlpp::mysql_type_info>::_Vector_impl
4438  # NOTE: operators: >>, <<
4439    my $Name = $_[0];
4440    if($Name!~/>\Z/ or $Name!~/</) {
4441        return $Name;
4442    }
4443    my $TParams = $Name;
4444    while(my $CPos = find_center($TParams, "<"))
4445    { # search for the last <T>
4446        $TParams = substr($TParams, $CPos);
4447    }
4448    if($TParams=~s/\A<(.+)>\Z/$1/) {
4449        $Name=~s/<\Q$TParams\E>\Z//;
4450    }
4451    else
4452    { # error
4453        $TParams = "";
4454    }
4455    return ($Name, $TParams);
4456}
4457
4458sub get_sub_ns($)
4459{
4460    my $Name = $_[0];
4461    my @NS = ();
4462    while(my $CPos = find_center($Name, ":"))
4463    {
4464        push(@NS, substr($Name, 0, $CPos));
4465        $Name = substr($Name, $CPos);
4466        $Name=~s/\A:://;
4467    }
4468    return (join("::", @NS), $Name);
4469}
4470
4471sub mangle_ns($$$)
4472{
4473    my ($Name, $LibVersion, $Repl) = @_;
4474    if(my $Tid = $TName_Tid{$LibVersion}{$Name})
4475    {
4476        my $Mangled = mangle_param($Tid, $LibVersion, $Repl);
4477        $Mangled=~s/\AN(.+)E\Z/$1/;
4478        return $Mangled;
4479
4480    }
4481    else
4482    {
4483        my ($MangledNS, $SubNS) = ("", "");
4484        ($SubNS, $Name) = get_sub_ns($Name);
4485        if($SubNS) {
4486            $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
4487        }
4488        $MangledNS .= length($Name).$Name;
4489        add_substitution($MangledNS, $Repl, 0);
4490        return $MangledNS;
4491    }
4492}
4493
4494sub mangle_param($$$)
4495{
4496    my ($PTid, $LibVersion, $Repl) = @_;
4497    my ($MPrefix, $Mangled) = ("", "");
4498    my %ReplCopy = %{$Repl};
4499    my %BaseType = get_BaseType($PTid, $LibVersion);
4500    my $BaseType_Name = $BaseType{"Name"};
4501    $BaseType_Name=~s/\A(struct|union|enum) //g;
4502    if(not $BaseType_Name) {
4503        return "";
4504    }
4505    my ($ShortName, $TmplParams) = template_Base($BaseType_Name);
4506    my $Suffix = get_BaseTypeQual($PTid, $LibVersion);
4507    while($Suffix=~s/\s*(const|volatile|restrict)\Z//g){};
4508    while($Suffix=~/(&|\*|const)\Z/)
4509    {
4510        if($Suffix=~s/[ ]*&\Z//) {
4511            $MPrefix .= "R";
4512        }
4513        if($Suffix=~s/[ ]*\*\Z//) {
4514            $MPrefix .= "P";
4515        }
4516        if($Suffix=~s/[ ]*const\Z//)
4517        {
4518            if($MPrefix=~/R|P/
4519            or $Suffix=~/&|\*/) {
4520                $MPrefix .= "K";
4521            }
4522        }
4523        if($Suffix=~s/[ ]*volatile\Z//) {
4524            $MPrefix .= "V";
4525        }
4526        #if($Suffix=~s/[ ]*restrict\Z//) {
4527            #$MPrefix .= "r";
4528        #}
4529    }
4530    if(my $Token = $IntrinsicMangling{$BaseType_Name}) {
4531        $Mangled .= $Token;
4532    }
4533    elsif($BaseType{"Type"}=~/(Class|Struct|Union|Enum)/)
4534    {
4535        my @TParams = ();
4536        if(my @TPos = keys(%{$BaseType{"TParam"}}))
4537        { # parsing mode
4538            foreach (@TPos) {
4539                push(@TParams, $BaseType{"TParam"}{$_}{"name"});
4540            }
4541        }
4542        elsif($TmplParams)
4543        { # remangling mode
4544          # support for old ABI dumps
4545            @TParams = separate_Params($TmplParams, 0, 0);
4546        }
4547        my $MangledNS = "";
4548        my ($SubNS, $SName) = get_sub_ns($ShortName);
4549        if($SubNS) {
4550            $MangledNS .= mangle_ns($SubNS, $LibVersion, $Repl);
4551        }
4552        $MangledNS .= length($SName).$SName;
4553        if(@TParams) {
4554            add_substitution($MangledNS, $Repl, 0);
4555        }
4556        $Mangled .= "N".$MangledNS;
4557        if(@TParams)
4558        { # templates
4559            $Mangled .= "I";
4560            foreach my $TParam (@TParams) {
4561                $Mangled .= mangle_template_param($TParam, $LibVersion, $Repl);
4562            }
4563            $Mangled .= "E";
4564        }
4565        $Mangled .= "E";
4566    }
4567    elsif($BaseType{"Type"}=~/(FuncPtr|MethodPtr)/)
4568    {
4569        if($BaseType{"Type"} eq "MethodPtr") {
4570            $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl)."F";
4571        }
4572        else {
4573            $Mangled .= "PF";
4574        }
4575        $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
4576        my @Params = keys(%{$BaseType{"Param"}});
4577        foreach my $Num (sort {int($a)<=>int($b)} @Params) {
4578            $Mangled .= mangle_param($BaseType{"Param"}{$Num}{"type"}, $LibVersion, $Repl);
4579        }
4580        if(not @Params) {
4581            $Mangled .= "v";
4582        }
4583        $Mangled .= "E";
4584    }
4585    elsif($BaseType{"Type"} eq "FieldPtr")
4586    {
4587        $Mangled .= "M".mangle_param($BaseType{"Class"}, $LibVersion, $Repl);
4588        $Mangled .= mangle_param($BaseType{"Return"}, $LibVersion, $Repl);
4589    }
4590    $Mangled = $MPrefix.$Mangled;# add prefix (RPK)
4591    if(my $Optimized = write_substitution($Mangled, \%ReplCopy))
4592    {
4593        if($Mangled eq $Optimized)
4594        {
4595            if($ShortName!~/::/)
4596            { # remove "N ... E"
4597                if($MPrefix) {
4598                    $Mangled=~s/\A($MPrefix)N(.+)E\Z/$1$2/g;
4599                }
4600                else {
4601                    $Mangled=~s/\AN(.+)E\Z/$1/g;
4602                }
4603            }
4604        }
4605        else {
4606            $Mangled = $Optimized;
4607        }
4608    }
4609    add_substitution($Mangled, $Repl, 1);
4610    return $Mangled;
4611}
4612
4613sub mangle_template_param($$$)
4614{ # types + literals
4615    my ($TParam, $LibVersion, $Repl) = @_;
4616    if(my $TPTid = $TName_Tid{$LibVersion}{$TParam}) {
4617        return mangle_param($TPTid, $LibVersion, $Repl);
4618    }
4619    elsif($TParam=~/\A(\d+)(\w+)\Z/)
4620    { # class_name<1u>::method(...)
4621        return "L".$IntrinsicMangling{$ConstantSuffixR{$2}}.$1."E";
4622    }
4623    elsif($TParam=~/\A\(([\w ]+)\)(\d+)\Z/)
4624    { # class_name<(signed char)1>::method(...)
4625        return "L".$IntrinsicMangling{$1}.$2."E";
4626    }
4627    elsif($TParam eq "true")
4628    { # class_name<true>::method(...)
4629        return "Lb1E";
4630    }
4631    elsif($TParam eq "false")
4632    { # class_name<true>::method(...)
4633        return "Lb0E";
4634    }
4635    else { # internal error
4636        return length($TParam).$TParam;
4637    }
4638}
4639
4640sub add_substitution($$$)
4641{
4642    my ($Value, $Repl, $Rec) = @_;
4643    if($Rec)
4644    { # subtypes
4645        my @Subs = ($Value);
4646        while($Value=~s/\A(R|P|K)//) {
4647            push(@Subs, $Value);
4648        }
4649        foreach (reverse(@Subs)) {
4650            add_substitution($_, $Repl, 0);
4651        }
4652        return;
4653    }
4654    return if($Value=~/\AS(\d*)_\Z/);
4655    $Value=~s/\AN(.+)E\Z/$1/g;
4656    return if(defined $Repl->{$Value});
4657    return if(length($Value)<=1);
4658    return if($StdcxxMangling{$Value});
4659    # check for duplicates
4660    my $Base = $Value;
4661    foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
4662    {
4663        my $Num = $Repl->{$Type};
4664        my $Replace = macro_mangle($Num);
4665        $Base=~s/\Q$Replace\E/$Type/;
4666    }
4667    if(my $OldNum = $Repl->{$Base})
4668    {
4669        $Repl->{$Value} = $OldNum;
4670        return;
4671    }
4672    my @Repls = sort {$b<=>$a} values(%{$Repl});
4673    if(@Repls) {
4674        $Repl->{$Value} = $Repls[0]+1;
4675    }
4676    else {
4677        $Repl->{$Value} = -1;
4678    }
4679    # register duplicates
4680    # upward
4681    $Base = $Value;
4682    foreach my $Type (sort {$Repl->{$a}<=>$Repl->{$b}} sort keys(%{$Repl}))
4683    {
4684        next if($Base eq $Type);
4685        my $Num = $Repl->{$Type};
4686        my $Replace = macro_mangle($Num);
4687        $Base=~s/\Q$Type\E/$Replace/;
4688        $Repl->{$Base} = $Repl->{$Value};
4689    }
4690}
4691
4692sub macro_mangle($)
4693{
4694    my $Num = $_[0];
4695    if($Num==-1) {
4696        return "S_";
4697    }
4698    else
4699    {
4700        my $Code = "";
4701        if($Num<10)
4702        { # S0_, S1_, S2_, ...
4703            $Code = $Num;
4704        }
4705        elsif($Num>=10 and $Num<=35)
4706        { # SA_, SB_, SC_, ...
4707            $Code = chr(55+$Num);
4708        }
4709        else
4710        { # S10_, S11_, S12_
4711            $Code = $Num-26; # 26 is length of english alphabet
4712        }
4713        return "S".$Code."_";
4714    }
4715}
4716
4717sub write_stdcxx_substitution($)
4718{
4719    my $Mangled = $_[0];
4720    if($StdcxxMangling{$Mangled}) {
4721        return $StdcxxMangling{$Mangled};
4722    }
4723    else
4724    {
4725        my @Repls = keys(%StdcxxMangling);
4726        @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
4727        foreach my $MangledType (@Repls)
4728        {
4729            my $Replace = $StdcxxMangling{$MangledType};
4730            #if($Mangled!~/$Replace/) {
4731                $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
4732                $Mangled=~s/\Q$MangledType\E/$Replace/g;
4733            #}
4734        }
4735    }
4736    return $Mangled;
4737}
4738
4739sub write_substitution($$)
4740{
4741    my ($Mangled, $Repl) = @_;
4742    if(defined $Repl->{$Mangled}
4743    and my $MnglNum = $Repl->{$Mangled}) {
4744        $Mangled = macro_mangle($MnglNum);
4745    }
4746    else
4747    {
4748        my @Repls = keys(%{$Repl});
4749        #@Repls = sort {$Repl->{$a}<=>$Repl->{$b}} @Repls;
4750        # FIXME: how to apply replacements? by num or by pos
4751        @Repls = sort {length($b)<=>length($a)} sort {$b cmp $a} @Repls;
4752        foreach my $MangledType (@Repls)
4753        {
4754            my $Replace = macro_mangle($Repl->{$MangledType});
4755            if($Mangled!~/$Replace/) {
4756                $Mangled=~s/N\Q$MangledType\EE/$Replace/g;
4757                $Mangled=~s/\Q$MangledType\E/$Replace/g;
4758            }
4759        }
4760    }
4761    return $Mangled;
4762}
4763
4764sub delete_keywords($)
4765{
4766    my $TypeName = $_[0];
4767    $TypeName=~s/\b(enum|struct|union|class) //g;
4768    return $TypeName;
4769}
4770
4771sub uncover_typedefs($$)
4772{
4773    my ($TypeName, $LibVersion) = @_;
4774    return "" if(not $TypeName);
4775    if(defined $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName}) {
4776        return $Cache{"uncover_typedefs"}{$LibVersion}{$TypeName};
4777    }
4778    my ($TypeName_New, $TypeName_Pre) = (formatName($TypeName, "T"), "");
4779    while($TypeName_New ne $TypeName_Pre)
4780    {
4781        $TypeName_Pre = $TypeName_New;
4782        my $TypeName_Copy = $TypeName_New;
4783        my %Words = ();
4784        while($TypeName_Copy=~s/\b([a-z_]([\w:]*\w|))\b//io)
4785        {
4786            if(not $Intrinsic_Keywords{$1}) {
4787                $Words{$1} = 1;
4788            }
4789        }
4790        foreach my $Word (keys(%Words))
4791        {
4792            my $BaseType_Name = $Typedef_BaseName{$LibVersion}{$Word};
4793            next if(not $BaseType_Name);
4794            next if($TypeName_New=~/\b(struct|union|enum)\s\Q$Word\E\b/);
4795            if($BaseType_Name=~/\([\*]+\)/)
4796            { # FuncPtr
4797                if($TypeName_New=~/\Q$Word\E(.*)\Z/)
4798                {
4799                    my $Type_Suffix = $1;
4800                    $TypeName_New = $BaseType_Name;
4801                    if($TypeName_New=~s/\(([\*]+)\)/($1 $Type_Suffix)/) {
4802                        $TypeName_New = formatName($TypeName_New, "T");
4803                    }
4804                }
4805            }
4806            else
4807            {
4808                if($TypeName_New=~s/\b\Q$Word\E\b/$BaseType_Name/g) {
4809                    $TypeName_New = formatName($TypeName_New, "T");
4810                }
4811            }
4812        }
4813    }
4814    return ($Cache{"uncover_typedefs"}{$LibVersion}{$TypeName} = $TypeName_New);
4815}
4816
4817sub isInternal($)
4818{
4819    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
4820    {
4821        if($Info=~/mngl[ ]*:[ ]*@(\d+) /)
4822        {
4823            if($LibInfo{$Version}{"info"}{$1}=~/\*[ ]*INTERNAL[ ]*\*/)
4824            { # _ZN7mysqlpp8DateTimeC1ERKS0_ *INTERNAL*
4825                return 1;
4826            }
4827        }
4828    }
4829    return 0;
4830}
4831
4832sub getDataVal($$)
4833{
4834    my ($InfoId, $TypeId) = @_;
4835    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4836    {
4837        if($Info=~/init[ ]*:[ ]*@(\d+) /)
4838        {
4839            if(defined $LibInfo{$Version}{"info_type"}{$1}
4840            and $LibInfo{$Version}{"info_type"}{$1} eq "nop_expr")
4841            {
4842                if(my $Nop = getTreeAttr_Op($1))
4843                {
4844                    if(defined $LibInfo{$Version}{"info_type"}{$Nop}
4845                    and $LibInfo{$Version}{"info_type"}{$Nop} eq "addr_expr")
4846                    {
4847                        if(my $Addr = getTreeAttr_Op($1)) {
4848                            return getInitVal($Addr, $TypeId);
4849                        }
4850                    }
4851                }
4852            }
4853            else {
4854                return getInitVal($1, $TypeId);
4855            }
4856        }
4857    }
4858    return undef;
4859}
4860
4861sub getInitVal($$)
4862{
4863    my ($InfoId, $TypeId) = @_;
4864    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4865    {
4866        if(my $InfoType = $LibInfo{$Version}{"info_type"}{$InfoId})
4867        {
4868            if($InfoType eq "integer_cst")
4869            {
4870                my $Val = getNodeIntCst($InfoId);
4871                if($TypeId and $TypeInfo{$Version}{$TypeId}{"Name"}=~/\Achar(| const)\Z/)
4872                { # characters
4873                    $Val = chr($Val);
4874                }
4875                return $Val;
4876            }
4877            elsif($InfoType eq "string_cst") {
4878                return getNodeStrCst($InfoId);
4879            }
4880            elsif($InfoType eq "var_decl")
4881            {
4882                if(my $Name = getNodeStrCst(getTreeAttr_Mngl($InfoId))) {
4883                    return $Name;
4884                }
4885            }
4886        }
4887    }
4888    return undef;
4889}
4890
4891sub set_Class_And_Namespace($)
4892{
4893    my $InfoId = $_[0];
4894    if(my $Info = $LibInfo{$Version}{"info"}{$InfoId})
4895    {
4896        if($Info=~/scpe[ ]*:[ ]*@(\d+) /)
4897        {
4898            my $NSInfoId = $1;
4899            if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId})
4900            {
4901                if($InfoType eq "namespace_decl") {
4902                    $SymbolInfo{$Version}{$InfoId}{"NameSpace"} = getNameSpace($InfoId);
4903                }
4904                elsif($InfoType eq "record_type") {
4905                    $SymbolInfo{$Version}{$InfoId}{"Class"} = $NSInfoId;
4906                }
4907            }
4908        }
4909    }
4910    if($SymbolInfo{$Version}{$InfoId}{"Class"}
4911    or $SymbolInfo{$Version}{$InfoId}{"NameSpace"})
4912    {
4913        if($COMMON_LANGUAGE{$Version} ne "C++")
4914        { # skip
4915            return 1;
4916        }
4917    }
4918
4919    return 0;
4920}
4921
4922sub debugMangling($)
4923{
4924    my $LibVersion = $_[0];
4925    my %Mangled = ();
4926    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
4927    {
4928        if(my $Mngl = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
4929        {
4930            if($Mngl=~/\A(_Z|\?)/) {
4931                $Mangled{$Mngl}=$InfoId;
4932            }
4933        }
4934    }
4935    translateSymbols(keys(%Mangled), $LibVersion);
4936    foreach my $Mngl (keys(%Mangled))
4937    {
4938        my $U1 = modelUnmangled($Mangled{$Mngl}, "GCC");
4939        my $U2 = $tr_name{$Mngl};
4940        if($U1 ne $U2) {
4941            printMsg("INFO", "INCORRECT MANGLING:\n  $Mngl\n  $U1\n  $U2\n");
4942        }
4943    }
4944}
4945
4946sub linkSymbol($)
4947{ # link symbols from shared libraries
4948  # with the symbols from header files
4949    my $InfoId = $_[0];
4950    # try to mangle symbol
4951    if((not check_gcc($GCC_PATH, "4") and $SymbolInfo{$Version}{$InfoId}{"Class"})
4952    or (check_gcc($GCC_PATH, "4") and not $SymbolInfo{$Version}{$InfoId}{"Class"})
4953    or $EMERGENCY_MODE_48)
4954    { # GCC 3.x doesn't mangle class methods names in the TU dump (only functions and global data)
4955      # GCC 4.x doesn't mangle C++ functions in the TU dump (only class methods) except extern "C" functions
4956      # GCC 4.8 doesn't mangle anything
4957        if(not $CheckHeadersOnly)
4958        {
4959            if(my $Mangled = $mangled_name_gcc{modelUnmangled($InfoId, "GCC")}) {
4960                return correct_incharge($InfoId, $Version, $Mangled);
4961            }
4962        }
4963        if($CheckHeadersOnly
4964        or not $BinaryOnly
4965        or $EMERGENCY_MODE_48)
4966        { # 1. --headers-only mode
4967          # 2. not mangled src-only symbols
4968            if(my $Mangled = mangle_symbol($InfoId, $Version, "GCC")) {
4969                return $Mangled;
4970            }
4971        }
4972    }
4973    return "";
4974}
4975
4976sub setLanguage($$)
4977{
4978    my ($LibVersion, $Lang) = @_;
4979    if(not $UserLang) {
4980        $COMMON_LANGUAGE{$LibVersion} = $Lang;
4981    }
4982}
4983
4984sub getSymbolInfo($)
4985{
4986    my $InfoId = $_[0];
4987    if(isInternal($InfoId)) {
4988        return;
4989    }
4990    ($SymbolInfo{$Version}{$InfoId}{"Header"}, $SymbolInfo{$Version}{$InfoId}{"Line"}) = getLocation($InfoId);
4991    if(not $SymbolInfo{$Version}{$InfoId}{"Header"}
4992    or isBuiltIn($SymbolInfo{$Version}{$InfoId}{"Header"}))
4993    {
4994        delete($SymbolInfo{$Version}{$InfoId});
4995        return;
4996    }
4997    setFuncAccess($InfoId);
4998    setFuncKind($InfoId);
4999    if($SymbolInfo{$Version}{$InfoId}{"PseudoTemplate"})
5000    {
5001        delete($SymbolInfo{$Version}{$InfoId});
5002        return;
5003    }
5004
5005    $SymbolInfo{$Version}{$InfoId}{"Type"} = getFuncType($InfoId);
5006    if(my $Return = getFuncReturn($InfoId))
5007    {
5008        if(not defined $TypeInfo{$Version}{$Return}
5009        or not $TypeInfo{$Version}{$Return}{"Name"})
5010        {
5011            delete($SymbolInfo{$Version}{$InfoId});
5012            return;
5013        }
5014        $SymbolInfo{$Version}{$InfoId}{"Return"} = $Return;
5015    }
5016    if(my $Rid = $SymbolInfo{$Version}{$InfoId}{"Return"})
5017    {
5018        if(defined $MissedTypedef{$Version}{$Rid})
5019        {
5020            if(my $AddedTid = $MissedTypedef{$Version}{$Rid}{"Tid"}) {
5021                $SymbolInfo{$Version}{$InfoId}{"Return"} = $AddedTid;
5022            }
5023        }
5024    }
5025    if(not $SymbolInfo{$Version}{$InfoId}{"Return"}) {
5026        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
5027    }
5028    my $Orig = getFuncOrig($InfoId);
5029    $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getFuncShortName($Orig);
5030    if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "\._")!=-1)
5031    {
5032        delete($SymbolInfo{$Version}{$InfoId});
5033        return;
5034    }
5035
5036    if(index($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "tmp_add_func")==0)
5037    {
5038        delete($SymbolInfo{$Version}{$InfoId});
5039        return;
5040    }
5041
5042    if(defined $TemplateInstance{$Version}{"Func"}{$Orig})
5043    {
5044        my $Tmpl = $BasicTemplate{$Version}{$InfoId};
5045
5046        my @TParams = getTParams($Orig, "Func");
5047        if(not @TParams)
5048        {
5049            delete($SymbolInfo{$Version}{$InfoId});
5050            return;
5051        }
5052        foreach my $Pos (0 .. $#TParams)
5053        {
5054            my $Val = $TParams[$Pos];
5055            $SymbolInfo{$Version}{$InfoId}{"TParam"}{$Pos}{"name"} = $Val;
5056
5057            if($Tmpl)
5058            {
5059                if(my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos})
5060                {
5061                    $TemplateMap{$Version}{$InfoId}{$Arg} = $Val;
5062                }
5063            }
5064        }
5065
5066        if($Tmpl)
5067        {
5068            foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$TemplateArg{$Version}{$Tmpl}}))
5069            {
5070                if($Pos>$#TParams)
5071                {
5072                    my $Arg = $TemplateArg{$Version}{$Tmpl}{$Pos};
5073                    $TemplateMap{$Version}{$InfoId}{$Arg} = "";
5074                }
5075            }
5076        }
5077
5078        if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\Aoperator\W+\Z/)
5079        { # operator<< <T>, operator>> <T>
5080            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= " ";
5081        }
5082        if(@TParams) {
5083            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<".join(", ", @TParams).">";
5084        }
5085        else {
5086            $SymbolInfo{$Version}{$InfoId}{"ShortName"} .= "<...>";
5087        }
5088        $SymbolInfo{$Version}{$InfoId}{"ShortName"} = formatName($SymbolInfo{$Version}{$InfoId}{"ShortName"}, "S");
5089    }
5090    else
5091    { # support for GCC 3.4
5092        $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//;
5093    }
5094    if(my $MnglName = getTreeStr(getTreeAttr_Mngl($InfoId)))
5095    {
5096        if($OSgroup eq "windows")
5097        { # cut the offset
5098            $MnglName=~s/\@\d+\Z//g;
5099        }
5100        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $MnglName;
5101
5102        # NOTE: mangling of some symbols may change depending on GCC version
5103        # GCC 4.6: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2IT_EERKS_IT_E
5104        # GCC 4.7: _ZN28QExplicitlySharedDataPointerI11QPixmapDataEC2ERKS1_
5105    }
5106
5107    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}
5108    and index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")!=0)
5109    {
5110        delete($SymbolInfo{$Version}{$InfoId});
5111        return;
5112    }
5113    if(not $SymbolInfo{$Version}{$InfoId}{"Destructor"})
5114    { # destructors have an empty parameter list
5115        my $Skip = setFuncParams($InfoId);
5116        if($Skip)
5117        {
5118            delete($SymbolInfo{$Version}{$InfoId});
5119            return;
5120        }
5121    }
5122    if($LibInfo{$Version}{"info"}{$InfoId}=~/ artificial /i) {
5123        $SymbolInfo{$Version}{$InfoId}{"Artificial"} = 1;
5124    }
5125
5126    if(set_Class_And_Namespace($InfoId))
5127    {
5128        delete($SymbolInfo{$Version}{$InfoId});
5129        return;
5130    }
5131
5132    if(my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
5133    {
5134        if(not defined $TypeInfo{$Version}{$ClassId}
5135        or not $TypeInfo{$Version}{$ClassId}{"Name"})
5136        {
5137            delete($SymbolInfo{$Version}{$InfoId});
5138            return;
5139        }
5140    }
5141    if($LibInfo{$Version}{"info"}{$InfoId}=~/ lang:[ ]*C /i)
5142    { # extern "C"
5143        $SymbolInfo{$Version}{$InfoId}{"Lang"} = "C";
5144        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5145    }
5146    if($UserLang and $UserLang eq "C")
5147    { # --lang=C option
5148        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5149    }
5150    if($COMMON_LANGUAGE{$Version} eq "C++")
5151    { # correct mangled & short names
5152      # C++ or --headers-only mode
5153        if($SymbolInfo{$Version}{$InfoId}{"ShortName"}=~/\A__(comp|base|deleting)_(c|d)tor\Z/)
5154        { # support for old GCC versions: reconstruct real names for constructors and destructors
5155            $SymbolInfo{$Version}{$InfoId}{"ShortName"} = getNameByInfo(getTypeDeclId($SymbolInfo{$Version}{$InfoId}{"Class"}));
5156            $SymbolInfo{$Version}{$InfoId}{"ShortName"}=~s/<.+>\Z//;
5157        }
5158        if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5159        { # try to mangle symbol (link with libraries)
5160            if(my $Mangled = linkSymbol($InfoId)) {
5161                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled;
5162            }
5163        }
5164        if($OStarget eq "windows")
5165        { # link MS C++ symbols from library with GCC symbols from headers
5166            if(my $Mangled1 = $mangled_name{$Version}{modelUnmangled($InfoId, "MSVC")})
5167            { # exported symbols
5168                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled1;
5169            }
5170            elsif(my $Mangled2 = mangle_symbol($InfoId, $Version, "MSVC"))
5171            { # pure virtual symbols
5172                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $Mangled2;
5173            }
5174        }
5175    }
5176    else
5177    { # not mangled in C
5178        $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5179    }
5180    if(not $CheckHeadersOnly
5181    and $SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function"
5182    and not $SymbolInfo{$Version}{$InfoId}{"Class"})
5183    {
5184        my $Incorrect = 0;
5185
5186        if($SymbolInfo{$Version}{$InfoId}{"MnglName"})
5187        {
5188            if(index($SymbolInfo{$Version}{$InfoId}{"MnglName"}, "_Z")==0
5189            and not link_symbol($SymbolInfo{$Version}{$InfoId}{"MnglName"}, $Version, "-Deps"))
5190            { # mangled in the TU dump, but not mangled in the library
5191                $Incorrect = 1;
5192            }
5193        }
5194        else
5195        {
5196            if($SymbolInfo{$Version}{$InfoId}{"Lang"} ne "C")
5197            { # all C++ functions are not mangled in the TU dump
5198                $Incorrect = 1;
5199            }
5200        }
5201        if($Incorrect)
5202        {
5203            if(link_symbol($SymbolInfo{$Version}{$InfoId}{"ShortName"}, $Version, "-Deps")) {
5204                $SymbolInfo{$Version}{$InfoId}{"MnglName"} = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5205            }
5206        }
5207    }
5208    if(not $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5209    { # can't detect symbol name
5210        delete($SymbolInfo{$Version}{$InfoId});
5211        return;
5212    }
5213    if(not $SymbolInfo{$Version}{$InfoId}{"Constructor"}
5214    and my $Spec = getVirtSpec($Orig))
5215    { # identify virtual and pure virtual functions
5216      # NOTE: constructors cannot be virtual
5217      # NOTE: in GCC 4.7 D1 destructors have no virtual spec
5218      # in the TU dump, so taking it from the original symbol
5219        if(not ($SymbolInfo{$Version}{$InfoId}{"Destructor"}
5220        and $SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/D2E/))
5221        { # NOTE: D2 destructors are not present in a v-table
5222            $SymbolInfo{$Version}{$InfoId}{$Spec} = 1;
5223        }
5224    }
5225    if(isInline($InfoId)) {
5226        $SymbolInfo{$Version}{$InfoId}{"InLine"} = 1;
5227    }
5228    if(hasThrow($InfoId)) {
5229        $SymbolInfo{$Version}{$InfoId}{"Throw"} = 1;
5230    }
5231    if($SymbolInfo{$Version}{$InfoId}{"Constructor"}
5232    and my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"})
5233    {
5234        if(not $SymbolInfo{$Version}{$InfoId}{"InLine"}
5235        and not $SymbolInfo{$Version}{$InfoId}{"Artificial"})
5236        { # inline or auto-generated constructor
5237            delete($TypeInfo{$Version}{$ClassId}{"Copied"});
5238        }
5239    }
5240    if(my $Symbol = $SymbolInfo{$Version}{$InfoId}{"MnglName"})
5241    {
5242        if(not $ExtraDump)
5243        {
5244            if(not selectSymbol($Symbol, $SymbolInfo{$Version}{$InfoId}, "Dump", $Version))
5245            { # non-target symbols
5246                delete($SymbolInfo{$Version}{$InfoId});
5247                return;
5248            }
5249        }
5250    }
5251    if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Method"
5252    or $SymbolInfo{$Version}{$InfoId}{"Constructor"}
5253    or $SymbolInfo{$Version}{$InfoId}{"Destructor"}
5254    or $SymbolInfo{$Version}{$InfoId}{"Class"})
5255    {
5256        if($SymbolInfo{$Version}{$InfoId}{"MnglName"}!~/\A(_Z|\?)/)
5257        {
5258            delete($SymbolInfo{$Version}{$InfoId});
5259            return;
5260        }
5261    }
5262    if($SymbolInfo{$Version}{$InfoId}{"MnglName"})
5263    {
5264        if($MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}})
5265        { # one instance for one mangled name only
5266            delete($SymbolInfo{$Version}{$InfoId});
5267            return;
5268        }
5269        else {
5270            $MangledNames{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}} = 1;
5271        }
5272    }
5273    if($SymbolInfo{$Version}{$InfoId}{"Constructor"}
5274    or $SymbolInfo{$Version}{$InfoId}{"Destructor"}) {
5275        delete($SymbolInfo{$Version}{$InfoId}{"Return"});
5276    }
5277    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/
5278    and $SymbolInfo{$Version}{$InfoId}{"Class"})
5279    {
5280        if($SymbolInfo{$Version}{$InfoId}{"Type"} eq "Function")
5281        { # static methods
5282            $SymbolInfo{$Version}{$InfoId}{"Static"} = 1;
5283        }
5284    }
5285    if(getFuncLink($InfoId) eq "Static") {
5286        $SymbolInfo{$Version}{$InfoId}{"Static"} = 1;
5287    }
5288    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A(_Z|\?)/)
5289    {
5290        if(my $Unmangled = $tr_name{$SymbolInfo{$Version}{$InfoId}{"MnglName"}})
5291        {
5292            if($Unmangled=~/\.\_\d/)
5293            {
5294                delete($SymbolInfo{$Version}{$InfoId});
5295                return;
5296            }
5297        }
5298    }
5299
5300    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(V|)K/) {
5301        $SymbolInfo{$Version}{$InfoId}{"Const"} = 1;
5302    }
5303    if($SymbolInfo{$Version}{$InfoId}{"MnglName"}=~/\A_ZN(K|)V/) {
5304        $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1;
5305    }
5306
5307    if($WeakSymbols{$Version}{$SymbolInfo{$Version}{$InfoId}{"MnglName"}}) {
5308        $SymbolInfo{$Version}{$InfoId}{"Weak"} = 1;
5309    }
5310
5311    if($ExtraDump) {
5312        $SymbolInfo{$Version}{$InfoId}{"Header"} = guessHeader($InfoId);
5313    }
5314}
5315
5316sub guessHeader($)
5317{
5318    my $InfoId = $_[0];
5319    my $ShortName = $SymbolInfo{$Version}{$InfoId}{"ShortName"};
5320    my $ClassId = $SymbolInfo{$Version}{$InfoId}{"Class"};
5321    my $ClassName = $ClassId?get_ShortClass($ClassId, $Version):"";
5322    my $Header = $SymbolInfo{$Version}{$InfoId}{"Header"};
5323    if(my $HPath = $SymbolHeader{$Version}{$ClassName}{$ShortName})
5324    {
5325        if(get_filename($HPath) eq $Header)
5326        {
5327            my $HDir = get_filename(get_dirname($HPath));
5328            if($HDir ne "include"
5329            and $HDir=~/\A[a-z]+\Z/i) {
5330                return join_P($HDir, $Header);
5331            }
5332        }
5333    }
5334    return $Header;
5335}
5336
5337sub isInline($)
5338{ # "body: undefined" in the tree
5339  # -fkeep-inline-functions GCC option should be specified
5340    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5341    {
5342        if($Info=~/ undefined /i) {
5343            return 0;
5344        }
5345    }
5346    return 1;
5347}
5348
5349sub hasThrow($)
5350{
5351    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5352    {
5353        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5354            return getTreeAttr_Unql($1, "unql");
5355        }
5356    }
5357    return 1;
5358}
5359
5360sub getTypeId($)
5361{
5362    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5363    {
5364        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5365            return $1;
5366        }
5367    }
5368    return "";
5369}
5370
5371sub setTypeMemb($$)
5372{
5373    my ($TypeId, $TypeAttr) = @_;
5374    my $TypeType = $TypeAttr->{"Type"};
5375    my ($Pos, $UnnamedPos) = (0, 0);
5376    my $StaticFields = 0;
5377    if($TypeType eq "Enum")
5378    {
5379        my $MInfoId = getTreeAttr_Csts($TypeId);
5380        while($MInfoId)
5381        {
5382            $TypeAttr->{"Memb"}{$Pos}{"value"} = getEnumMembVal($MInfoId);
5383            my $MembName = getTreeStr(getTreeAttr_Purp($MInfoId));
5384            $TypeAttr->{"Memb"}{$Pos}{"name"} = $MembName;
5385            $EnumMembName_Id{$Version}{getTreeAttr_Valu($MInfoId)} = ($TypeAttr->{"NameSpace"})?$TypeAttr->{"NameSpace"}."::".$MembName:$MembName;
5386            $MInfoId = getNextElem($MInfoId);
5387            $Pos += 1;
5388        }
5389    }
5390    elsif($TypeType=~/\A(Struct|Class|Union)\Z/)
5391    {
5392        my $MInfoId = getTreeAttr_Flds($TypeId);
5393        while($MInfoId)
5394        {
5395            my $IType = $LibInfo{$Version}{"info_type"}{$MInfoId};
5396            my $MInfo = $LibInfo{$Version}{"info"}{$MInfoId};
5397            if(not $IType or $IType ne "field_decl")
5398            { # search for fields, skip other stuff in the declaration
5399
5400                if($IType eq "var_decl")
5401                { # static field
5402                    $StaticFields = 1;
5403                }
5404
5405                $MInfoId = getNextElem($MInfoId);
5406                next;
5407            }
5408            my $StructMembName = getTreeStr(getTreeAttr_Name($MInfoId));
5409            if(index($StructMembName, "_vptr.")==0)
5410            { # virtual tables
5411                $StructMembName = "_vptr";
5412            }
5413            if(not $StructMembName)
5414            { # unnamed fields
5415                if(index($TypeAttr->{"Name"}, "_type_info_pseudo")==-1)
5416                {
5417                    my $UnnamedTid = getTreeAttr_Type($MInfoId);
5418                    my $UnnamedTName = getNameByInfo(getTypeDeclId($UnnamedTid));
5419                    if(isAnon($UnnamedTName))
5420                    { # rename unnamed fields to unnamed0, unnamed1, ...
5421                        $StructMembName = "unnamed".($UnnamedPos++);
5422                    }
5423                }
5424            }
5425            if(not $StructMembName)
5426            { # unnamed fields and base classes
5427                $MInfoId = getNextElem($MInfoId);
5428                next;
5429            }
5430            my $MembTypeId = getTreeAttr_Type($MInfoId);
5431            if(defined $MissedTypedef{$Version}{$MembTypeId})
5432            {
5433                if(my $AddedTid = $MissedTypedef{$Version}{$MembTypeId}{"Tid"}) {
5434                    $MembTypeId = $AddedTid;
5435                }
5436            }
5437
5438            $TypeAttr->{"Memb"}{$Pos}{"type"} = $MembTypeId;
5439            $TypeAttr->{"Memb"}{$Pos}{"name"} = $StructMembName;
5440            if((my $Access = getTreeAccess($MInfoId)) ne "public")
5441            { # marked only protected and private, public by default
5442                $TypeAttr->{"Memb"}{$Pos}{"access"} = $Access;
5443            }
5444            if($MInfo=~/spec:\s*mutable /)
5445            { # mutable fields
5446                $TypeAttr->{"Memb"}{$Pos}{"mutable"} = 1;
5447            }
5448            if(my $Algn = getAlgn($MInfoId)) {
5449                $TypeAttr->{"Memb"}{$Pos}{"algn"} = $Algn;
5450            }
5451            if(my $BFSize = getBitField($MInfoId))
5452            { # in bits
5453                $TypeAttr->{"Memb"}{$Pos}{"bitfield"} = $BFSize;
5454            }
5455            else
5456            { # in bytes
5457                if($TypeAttr->{"Memb"}{$Pos}{"algn"}==1)
5458                { # template
5459                    delete($TypeAttr->{"Memb"}{$Pos}{"algn"});
5460                }
5461                else {
5462                    $TypeAttr->{"Memb"}{$Pos}{"algn"} /= $BYTE_SIZE;
5463                }
5464            }
5465
5466            $MInfoId = getNextElem($MInfoId);
5467            $Pos += 1;
5468        }
5469    }
5470
5471    return $StaticFields;
5472}
5473
5474sub setFuncParams($)
5475{
5476    my $InfoId = $_[0];
5477    my $ParamInfoId = getTreeAttr_Args($InfoId);
5478
5479    my $FType = getFuncType($InfoId);
5480
5481    if($FType eq "Method")
5482    { # check type of "this" pointer
5483        my $ObjectTypeId = getTreeAttr_Type($ParamInfoId);
5484        if(my $ObjectName = $TypeInfo{$Version}{$ObjectTypeId}{"Name"})
5485        {
5486            if($ObjectName=~/\bconst(| volatile)\*const\b/) {
5487                $SymbolInfo{$Version}{$InfoId}{"Const"} = 1;
5488            }
5489            if($ObjectName=~/\bvolatile\b/) {
5490                $SymbolInfo{$Version}{$InfoId}{"Volatile"} = 1;
5491            }
5492        }
5493        else
5494        { # skip
5495            return 1;
5496        }
5497        # skip "this"-parameter
5498        # $ParamInfoId = getNextElem($ParamInfoId);
5499    }
5500    my ($Pos, $PPos, $Vtt_Pos) = (0, 0, -1);
5501    while($ParamInfoId)
5502    { # formal args
5503        my $ParamTypeId = getTreeAttr_Type($ParamInfoId);
5504        my $ParamName = getTreeStr(getTreeAttr_Name($ParamInfoId));
5505        if(not $ParamName)
5506        { # unnamed
5507            $ParamName = "p".($PPos+1);
5508        }
5509        if(defined $MissedTypedef{$Version}{$ParamTypeId})
5510        {
5511            if(my $AddedTid = $MissedTypedef{$Version}{$ParamTypeId}{"Tid"}) {
5512                $ParamTypeId = $AddedTid;
5513            }
5514        }
5515        my $PType = $TypeInfo{$Version}{$ParamTypeId}{"Type"};
5516        if(not $PType or $PType eq "Unknown") {
5517            return 1;
5518        }
5519        my $PTName = $TypeInfo{$Version}{$ParamTypeId}{"Name"};
5520        if(not $PTName) {
5521            return 1;
5522        }
5523        if($PTName eq "void") {
5524            last;
5525        }
5526        if($ParamName eq "__vtt_parm"
5527        and $TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void const**")
5528        {
5529            $Vtt_Pos = $Pos;
5530            $ParamInfoId = getNextElem($ParamInfoId);
5531            next;
5532        }
5533        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5534
5535        if(my %Base = get_BaseType($ParamTypeId, $Version))
5536        {
5537            if(defined $Base{"Template"}) {
5538                return 1;
5539            }
5540        }
5541
5542        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = $ParamName;
5543        if(my $Algn = getAlgn($ParamInfoId)) {
5544            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"algn"} = $Algn/$BYTE_SIZE;
5545        }
5546        if($LibInfo{$Version}{"info"}{$ParamInfoId}=~/spec:\s*register /)
5547        { # foo(register type arg)
5548            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"reg"} = 1;
5549        }
5550        $ParamInfoId = getNextElem($ParamInfoId);
5551        $Pos += 1;
5552        if($ParamName ne "this" or $FType ne "Method") {
5553            $PPos += 1;
5554        }
5555    }
5556    if(setFuncArgs($InfoId, $Vtt_Pos)) {
5557        $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = "-1";
5558    }
5559    return 0;
5560}
5561
5562sub setFuncArgs($$)
5563{
5564    my ($InfoId, $Vtt_Pos) = @_;
5565    my $FuncTypeId = getFuncTypeId($InfoId);
5566    my $ParamListElemId = getTreeAttr_Prms($FuncTypeId);
5567    my $FType = getFuncType($InfoId);
5568
5569    if($FType eq "Method")
5570    {
5571        # skip "this"-parameter
5572        # $ParamListElemId = getNextElem($ParamListElemId);
5573    }
5574    if(not $ParamListElemId)
5575    { # foo(...)
5576        return 1;
5577    }
5578    my $HaveVoid = 0;
5579    my ($Pos, $PPos) = (0, 0);
5580    while($ParamListElemId)
5581    { # actual params: may differ from formal args
5582      # formal int*const
5583      # actual: int*
5584        if($Vtt_Pos!=-1 and $Pos==$Vtt_Pos)
5585        {
5586            $Vtt_Pos=-1;
5587            $ParamListElemId = getNextElem($ParamListElemId);
5588            next;
5589        }
5590        my $ParamTypeId = getTreeAttr_Valu($ParamListElemId);
5591        if($TypeInfo{$Version}{$ParamTypeId}{"Name"} eq "void")
5592        {
5593            $HaveVoid = 1;
5594            last;
5595        }
5596        else
5597        {
5598            if(not defined $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"})
5599            {
5600                $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5601                if(not $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"})
5602                { # unnamed
5603                    $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"name"} = "p".($PPos+1);
5604                }
5605            }
5606            elsif(my $OldId = $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"})
5607            {
5608                if($Pos>0 or getFuncType($InfoId) ne "Method")
5609                { # params
5610                    if($OldId ne $ParamTypeId)
5611                    {
5612                        my %Old_Pure = get_PureType($OldId, $TypeInfo{$Version});
5613                        my %New_Pure = get_PureType($ParamTypeId, $TypeInfo{$Version});
5614
5615                        if($Old_Pure{"Name"} ne $New_Pure{"Name"}) {
5616                            $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"type"} = $ParamTypeId;
5617                        }
5618                    }
5619                }
5620            }
5621        }
5622        if(my $PurpId = getTreeAttr_Purp($ParamListElemId))
5623        { # default arguments
5624            if(my $PurpType = $LibInfo{$Version}{"info_type"}{$PurpId})
5625            {
5626                if($PurpType eq "nop_expr")
5627                { # func ( const char* arg = (const char*)(void*)0 )
5628                    $PurpId = getTreeAttr_Op($PurpId);
5629                }
5630                my $Val = getInitVal($PurpId, $ParamTypeId);
5631                if(defined $Val) {
5632                    $SymbolInfo{$Version}{$InfoId}{"Param"}{$Pos}{"default"} = $Val;
5633                }
5634            }
5635        }
5636        $ParamListElemId = getNextElem($ParamListElemId);
5637        if($Pos!=0 or $FType ne "Method") {
5638            $PPos += 1;
5639        }
5640        $Pos += 1;
5641    }
5642    return ($Pos>=1 and not $HaveVoid);
5643}
5644
5645sub getTreeAttr_Chan($)
5646{
5647    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5648    {
5649        if($Info=~/chan[ ]*:[ ]*@(\d+) /) {
5650            return $1;
5651        }
5652    }
5653    return "";
5654}
5655
5656sub getTreeAttr_Chain($)
5657{
5658    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5659    {
5660        if($Info=~/chain[ ]*:[ ]*@(\d+) /) {
5661            return $1;
5662        }
5663    }
5664    return "";
5665}
5666
5667sub getTreeAttr_Unql($)
5668{
5669    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5670    {
5671        if($Info=~/unql[ ]*:[ ]*@(\d+) /) {
5672            return $1;
5673        }
5674    }
5675    return "";
5676}
5677
5678sub getTreeAttr_Scpe($)
5679{
5680    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5681    {
5682        if($Info=~/scpe[ ]*:[ ]*@(\d+) /) {
5683            return $1;
5684        }
5685    }
5686    return "";
5687}
5688
5689sub getTreeAttr_Type($)
5690{
5691    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5692    {
5693        if($Info=~/type[ ]*:[ ]*@(\d+) /) {
5694            return $1;
5695        }
5696    }
5697    return "";
5698}
5699
5700sub getTreeAttr_Name($)
5701{
5702    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5703    {
5704        if($Info=~/name[ ]*:[ ]*@(\d+) /) {
5705            return $1;
5706        }
5707    }
5708    return "";
5709}
5710
5711sub getTreeAttr_Mngl($)
5712{
5713    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5714    {
5715        if($Info=~/mngl[ ]*:[ ]*@(\d+) /) {
5716            return $1;
5717        }
5718    }
5719    return "";
5720}
5721
5722sub getTreeAttr_Prms($)
5723{
5724    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5725    {
5726        if($Info=~/prms[ ]*:[ ]*@(\d+) /) {
5727            return $1;
5728        }
5729    }
5730    return "";
5731}
5732
5733sub getTreeAttr_Fncs($)
5734{
5735    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5736    {
5737        if($Info=~/fncs[ ]*:[ ]*@(\d+) /) {
5738            return $1;
5739        }
5740    }
5741    return "";
5742}
5743
5744sub getTreeAttr_Csts($)
5745{
5746    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5747    {
5748        if($Info=~/csts[ ]*:[ ]*@(\d+) /) {
5749            return $1;
5750        }
5751    }
5752    return "";
5753}
5754
5755sub getTreeAttr_Purp($)
5756{
5757    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5758    {
5759        if($Info=~/purp[ ]*:[ ]*@(\d+) /) {
5760            return $1;
5761        }
5762    }
5763    return "";
5764}
5765
5766sub getTreeAttr_Op($)
5767{
5768    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5769    {
5770        if($Info=~/op 0[ ]*:[ ]*@(\d+) /) {
5771            return $1;
5772        }
5773    }
5774    return "";
5775}
5776
5777sub getTreeAttr_Valu($)
5778{
5779    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5780    {
5781        if($Info=~/valu[ ]*:[ ]*@(\d+) /) {
5782            return $1;
5783        }
5784    }
5785    return "";
5786}
5787
5788sub getTreeAttr_Flds($)
5789{
5790    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5791    {
5792        if($Info=~/flds[ ]*:[ ]*@(\d+) /) {
5793            return $1;
5794        }
5795    }
5796    return "";
5797}
5798
5799sub getTreeAttr_Binf($)
5800{
5801    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5802    {
5803        if($Info=~/binf[ ]*:[ ]*@(\d+) /) {
5804            return $1;
5805        }
5806    }
5807    return "";
5808}
5809
5810sub getTreeAttr_Args($)
5811{
5812    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5813    {
5814        if($Info=~/args[ ]*:[ ]*@(\d+) /) {
5815            return $1;
5816        }
5817    }
5818    return "";
5819}
5820
5821sub getTreeValue($)
5822{
5823    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5824    {
5825        if($Info=~/low[ ]*:[ ]*([^ ]+) /) {
5826            return $1;
5827        }
5828    }
5829    return "";
5830}
5831
5832sub getTreeAccess($)
5833{
5834    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5835    {
5836        if($Info=~/accs[ ]*:[ ]*([a-zA-Z]+) /)
5837        {
5838            my $Access = $1;
5839            if($Access eq "prot") {
5840                return "protected";
5841            }
5842            elsif($Access eq "priv") {
5843                return "private";
5844            }
5845        }
5846        elsif($Info=~/ protected /)
5847        { # support for old GCC versions
5848            return "protected";
5849        }
5850        elsif($Info=~/ private /)
5851        { # support for old GCC versions
5852            return "private";
5853        }
5854    }
5855    return "public";
5856}
5857
5858sub setFuncAccess($)
5859{
5860    my $Access = getTreeAccess($_[0]);
5861    if($Access eq "protected") {
5862        $SymbolInfo{$Version}{$_[0]}{"Protected"} = 1;
5863    }
5864    elsif($Access eq "private") {
5865        $SymbolInfo{$Version}{$_[0]}{"Private"} = 1;
5866    }
5867}
5868
5869sub setTypeAccess($$)
5870{
5871    my ($TypeId, $TypeAttr) = @_;
5872    my $Access = getTreeAccess($TypeId);
5873    if($Access eq "protected") {
5874        $TypeAttr->{"Protected"} = 1;
5875    }
5876    elsif($Access eq "private") {
5877        $TypeAttr->{"Private"} = 1;
5878    }
5879}
5880
5881sub setFuncKind($)
5882{
5883    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5884    {
5885        if($Info=~/pseudo tmpl/) {
5886            $SymbolInfo{$Version}{$_[0]}{"PseudoTemplate"} = 1;
5887        }
5888        elsif($Info=~/ constructor /) {
5889            $SymbolInfo{$Version}{$_[0]}{"Constructor"} = 1;
5890        }
5891        elsif($Info=~/ destructor /) {
5892            $SymbolInfo{$Version}{$_[0]}{"Destructor"} = 1;
5893        }
5894    }
5895}
5896
5897sub getVirtSpec($)
5898{
5899    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5900    {
5901        if($Info=~/spec[ ]*:[ ]*pure /) {
5902            return "PureVirt";
5903        }
5904        elsif($Info=~/spec[ ]*:[ ]*virt /) {
5905            return "Virt";
5906        }
5907        elsif($Info=~/ pure\s+virtual /)
5908        { # support for old GCC versions
5909            return "PureVirt";
5910        }
5911        elsif($Info=~/ virtual /)
5912        { # support for old GCC versions
5913            return "Virt";
5914        }
5915    }
5916    return "";
5917}
5918
5919sub getFuncLink($)
5920{
5921    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
5922    {
5923        if($Info=~/link[ ]*:[ ]*static /) {
5924            return "Static";
5925        }
5926        elsif($Info=~/link[ ]*:[ ]*([a-zA-Z]+) /) {
5927            return $1;
5928        }
5929    }
5930    return "";
5931}
5932
5933sub select_Symbol_NS($$)
5934{
5935    my ($Symbol, $LibVersion) = @_;
5936    return "" if(not $Symbol or not $LibVersion);
5937    my $NS = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"};
5938    if(not $NS)
5939    {
5940        if(my $Class = $CompleteSignature{$LibVersion}{$Symbol}{"Class"}) {
5941            $NS = $TypeInfo{$LibVersion}{$Class}{"NameSpace"};
5942        }
5943    }
5944    if($NS)
5945    {
5946        if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5947            return $NS;
5948        }
5949        else
5950        {
5951            while($NS=~s/::[^:]+\Z//)
5952            {
5953                if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5954                    return $NS;
5955                }
5956            }
5957        }
5958    }
5959
5960    return "";
5961}
5962
5963sub select_Type_NS($$)
5964{
5965    my ($TypeName, $LibVersion) = @_;
5966    return "" if(not $TypeName or not $LibVersion);
5967    if(my $NS = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$TypeName}}{"NameSpace"})
5968    {
5969        if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5970            return $NS;
5971        }
5972        else
5973        {
5974            while($NS=~s/::[^:]+\Z//)
5975            {
5976                if(defined $NestedNameSpaces{$LibVersion}{$NS}) {
5977                    return $NS;
5978                }
5979            }
5980        }
5981    }
5982    return "";
5983}
5984
5985sub getNameSpace($)
5986{
5987    my $InfoId = $_[0];
5988    if(my $NSInfoId = getTreeAttr_Scpe($InfoId))
5989    {
5990        if(my $InfoType = $LibInfo{$Version}{"info_type"}{$NSInfoId})
5991        {
5992            if($InfoType eq "namespace_decl")
5993            {
5994                if($LibInfo{$Version}{"info"}{$NSInfoId}=~/name[ ]*:[ ]*@(\d+) /)
5995                {
5996                    my $NameSpace = getTreeStr($1);
5997                    if($NameSpace eq "::")
5998                    { # global namespace
5999                        return "";
6000                    }
6001                    if(my $BaseNameSpace = getNameSpace($NSInfoId)) {
6002                        $NameSpace = $BaseNameSpace."::".$NameSpace;
6003                    }
6004                    $NestedNameSpaces{$Version}{$NameSpace} = 1;
6005                    return $NameSpace;
6006                }
6007                else {
6008                    return "";
6009                }
6010            }
6011            elsif($InfoType ne "function_decl")
6012            { # inside data type
6013                my ($Name, $NameNS) = getTrivialName(getTypeDeclId($NSInfoId), $NSInfoId);
6014                return $Name;
6015            }
6016        }
6017    }
6018    return "";
6019}
6020
6021sub getEnumMembVal($)
6022{
6023    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6024    {
6025        if($Info=~/valu[ ]*:[ ]*\@(\d+)/)
6026        {
6027            if(my $VInfo = $LibInfo{$Version}{"info"}{$1})
6028            {
6029                if($VInfo=~/cnst[ ]*:[ ]*\@(\d+)/)
6030                { # in newer versions of GCC the value is in the "const_decl->cnst" node
6031                    return getTreeValue($1);
6032                }
6033                else
6034                { # some old versions of GCC (3.3) have the value in the "integer_cst" node
6035                    return getTreeValue($1);
6036                }
6037            }
6038        }
6039    }
6040    return "";
6041}
6042
6043sub getSize($)
6044{
6045    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6046    {
6047        if($Info=~/size[ ]*:[ ]*\@(\d+)/) {
6048            return getTreeValue($1);
6049        }
6050    }
6051    return 0;
6052}
6053
6054sub getAlgn($)
6055{
6056    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6057    {
6058        if($Info=~/algn[ ]*:[ ]*(\d+) /) {
6059            return $1;
6060        }
6061    }
6062    return "";
6063}
6064
6065sub getBitField($)
6066{
6067    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
6068    {
6069        if($Info=~/ bitfield /) {
6070            return getSize($_[0]);
6071        }
6072    }
6073    return 0;
6074}
6075
6076sub getNextElem($)
6077{
6078    if(my $Chan = getTreeAttr_Chan($_[0])) {
6079        return $Chan;
6080    }
6081    elsif(my $Chain = getTreeAttr_Chain($_[0])) {
6082        return $Chain;
6083    }
6084    return "";
6085}
6086
6087sub registerHeader($$)
6088{ # input: absolute path of header, relative path or name
6089    my ($Header, $LibVersion) = @_;
6090    if(not $Header) {
6091        return "";
6092    }
6093    if(is_abs($Header) and not -f $Header)
6094    { # incorrect absolute path
6095        exitStatus("Access_Error", "can't access \'$Header\'");
6096    }
6097    if(skipHeader($Header, $LibVersion))
6098    { # skip
6099        return "";
6100    }
6101    if(my $Header_Path = identifyHeader($Header, $LibVersion))
6102    {
6103        detect_header_includes($Header_Path, $LibVersion);
6104
6105        if(defined $Tolerance and $Tolerance=~/3/)
6106        { # 3 - skip headers that include non-Linux headers
6107            if($OSgroup ne "windows")
6108            {
6109                foreach my $Inc (keys(%{$Header_Includes{$LibVersion}{$Header_Path}}))
6110                {
6111                    if(specificHeader($Inc, "windows")) {
6112                        return "";
6113                    }
6114                }
6115            }
6116        }
6117
6118        if(my $RHeader_Path = $Header_ErrorRedirect{$LibVersion}{$Header_Path})
6119        { # redirect
6120            if($Registered_Headers{$LibVersion}{$RHeader_Path}{"Identity"}
6121            or skipHeader($RHeader_Path, $LibVersion))
6122            { # skip
6123                return "";
6124            }
6125            $Header_Path = $RHeader_Path;
6126        }
6127        elsif($Header_ShouldNotBeUsed{$LibVersion}{$Header_Path})
6128        { # skip
6129            return "";
6130        }
6131
6132        if(my $HName = get_filename($Header_Path))
6133        { # register
6134            $Registered_Headers{$LibVersion}{$Header_Path}{"Identity"} = $HName;
6135            $HeaderName_Paths{$LibVersion}{$HName}{$Header_Path} = 1;
6136        }
6137
6138        if(($Header=~/\.(\w+)\Z/ and $1 ne "h")
6139        or $Header!~/\.(\w+)\Z/)
6140        { # hpp, hh, etc.
6141            setLanguage($LibVersion, "C++");
6142            $CPP_HEADERS = 1;
6143        }
6144
6145        if($CheckHeadersOnly
6146        and $Header=~/(\A|\/)c\+\+(\/|\Z)/)
6147        { # /usr/include/c++/4.6.1/...
6148            $STDCXX_TESTING = 1;
6149        }
6150
6151        return $Header_Path;
6152    }
6153    return "";
6154}
6155
6156sub registerDir($$$)
6157{
6158    my ($Dir, $WithDeps, $LibVersion) = @_;
6159    $Dir=~s/[\/\\]+\Z//g;
6160    return if(not $LibVersion or not $Dir or not -d $Dir);
6161    $Dir = get_abs_path($Dir);
6162
6163    my $Mode = "All";
6164    if($WithDeps)
6165    {
6166        if($RegisteredDirs{$LibVersion}{$Dir}{1}) {
6167            return;
6168        }
6169        elsif($RegisteredDirs{$LibVersion}{$Dir}{0}) {
6170            $Mode = "DepsOnly";
6171        }
6172    }
6173    else
6174    {
6175        if($RegisteredDirs{$LibVersion}{$Dir}{1}
6176        or $RegisteredDirs{$LibVersion}{$Dir}{0}) {
6177            return;
6178        }
6179    }
6180    $Header_Dependency{$LibVersion}{$Dir} = 1;
6181    $RegisteredDirs{$LibVersion}{$Dir}{$WithDeps} = 1;
6182    if($Mode eq "DepsOnly")
6183    {
6184        foreach my $Path (cmd_find($Dir,"d")) {
6185            $Header_Dependency{$LibVersion}{$Path} = 1;
6186        }
6187        return;
6188    }
6189    foreach my $Path (sort {length($b)<=>length($a)} cmd_find($Dir,"f"))
6190    {
6191        if($WithDeps)
6192        {
6193            my $SubDir = $Path;
6194            while(($SubDir = get_dirname($SubDir)) ne $Dir)
6195            { # register all sub directories
6196                $Header_Dependency{$LibVersion}{$SubDir} = 1;
6197            }
6198        }
6199        next if(is_not_header($Path));
6200        next if(ignore_path($Path));
6201        # Neighbors
6202        foreach my $Part (get_prefixes($Path)) {
6203            $Include_Neighbors{$LibVersion}{$Part} = $Path;
6204        }
6205    }
6206    if(get_filename($Dir) eq "include")
6207    { # search for "lib/include/" directory
6208        my $LibDir = $Dir;
6209        if($LibDir=~s/([\/\\])include\Z/$1lib/g and -d $LibDir) {
6210            registerDir($LibDir, $WithDeps, $LibVersion);
6211        }
6212    }
6213}
6214
6215sub parse_redirect($$$)
6216{
6217    my ($Content, $Path, $LibVersion) = @_;
6218    my @Errors = ();
6219    while($Content=~s/#\s*error\s+([^\n]+?)\s*(\n|\Z)//) {
6220        push(@Errors, $1);
6221    }
6222    my $Redirect = "";
6223    foreach (@Errors)
6224    {
6225        s/\s{2,}/ /g;
6226        if(/(only|must\ include
6227        |update\ to\ include
6228        |replaced\ with
6229        |replaced\ by|renamed\ to
6230        |\ is\ in|\ use)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))/ix)
6231        {
6232            $Redirect = $2;
6233            last;
6234        }
6235        elsif(/(include|use|is\ in)\ (<[^<>]+>|[\w\-\/\\]+\.($HEADER_EXT))\ instead/i)
6236        {
6237            $Redirect = $2;
6238            last;
6239        }
6240        elsif(/this\ header\ should\ not\ be\ used
6241         |programs\ should\ not\ directly\ include
6242         |you\ should\ not\ (include|be\ (including|using)\ this\ (file|header))
6243         |is\ not\ supported\ API\ for\ general\ use
6244         |do\ not\ use
6245         |should\ not\ be\ (used|using)
6246         |cannot\ be\ included\ directly/ix and not /\ from\ /i) {
6247            $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1;
6248        }
6249    }
6250    if($Redirect)
6251    {
6252        $Redirect=~s/\A<//g;
6253        $Redirect=~s/>\Z//g;
6254    }
6255    return $Redirect;
6256}
6257
6258sub parse_includes($$)
6259{
6260    my ($Content, $Path) = @_;
6261    my %Includes = ();
6262    while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]*([<"].+?[">])[ \t]*//m)
6263    { # C/C++: include, Objective C/C++: import directive
6264        my $Header = $2;
6265        my $Method = substr($Header, 0, 1, "");
6266        substr($Header, length($Header)-1, 1, "");
6267        $Header = path_format($Header, $OSgroup);
6268        if($Method eq "\"" or is_abs($Header))
6269        {
6270            if(-e join_P(get_dirname($Path), $Header))
6271            { # relative path exists
6272                $Includes{$Header} = -1;
6273            }
6274            else
6275            { # include "..." that doesn't exist is equal to include <...>
6276                $Includes{$Header} = 2;
6277            }
6278        }
6279        else {
6280            $Includes{$Header} = 1;
6281        }
6282    }
6283    if($ExtraInfo)
6284    {
6285        while($Content=~s/^[ \t]*#[ \t]*(include|include_next|import)[ \t]+(\w+)[ \t]*//m)
6286        { # FT_FREETYPE_H
6287            $Includes{$2} = 0;
6288        }
6289    }
6290    return \%Includes;
6291}
6292
6293sub ignore_path($)
6294{
6295    my $Path = $_[0];
6296    if($Path=~/\~\Z/)
6297    {# skipping system backup files
6298        return 1;
6299    }
6300    if($Path=~/(\A|[\/\\]+)(\.(svn|git|bzr|hg)|CVS)([\/\\]+|\Z)/)
6301    {# skipping hidden .svn, .git, .bzr, .hg and CVS directories
6302        return 1;
6303    }
6304    return 0;
6305}
6306
6307sub sortByWord($$)
6308{
6309    my ($ArrRef, $W) = @_;
6310    return if(length($W)<2);
6311    @{$ArrRef} = sort {get_filename($b)=~/\Q$W\E/i<=>get_filename($a)=~/\Q$W\E/i} @{$ArrRef};
6312}
6313
6314sub sortHeaders($$)
6315{
6316    my ($H1, $H2) = @_;
6317
6318    $H1=~s/\.[a-z]+\Z//ig;
6319    $H2=~s/\.[a-z]+\Z//ig;
6320
6321    my $Hname1 = get_filename($H1);
6322    my $Hname2 = get_filename($H2);
6323    my $HDir1 = get_dirname($H1);
6324    my $HDir2 = get_dirname($H2);
6325    my $Dirname1 = get_filename($HDir1);
6326    my $Dirname2 = get_filename($HDir2);
6327
6328    $HDir1=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
6329    $HDir2=~s/\A.*[\/\\]+([^\/\\]+[\/\\]+[^\/\\]+)\Z/$1/;
6330
6331    if($_[0] eq $_[1]
6332    or $H1 eq $H2) {
6333        return 0;
6334    }
6335    elsif($H1=~/\A\Q$H2\E/) {
6336        return 1;
6337    }
6338    elsif($H2=~/\A\Q$H1\E/) {
6339        return -1;
6340    }
6341    elsif($HDir1=~/\Q$Hname1\E/i
6342    and $HDir2!~/\Q$Hname2\E/i)
6343    { # include/glib-2.0/glib.h
6344        return -1;
6345    }
6346    elsif($HDir2=~/\Q$Hname2\E/i
6347    and $HDir1!~/\Q$Hname1\E/i)
6348    { # include/glib-2.0/glib.h
6349        return 1;
6350    }
6351    elsif($Hname1=~/\Q$Dirname1\E/i
6352    and $Hname2!~/\Q$Dirname2\E/i)
6353    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
6354        return -1;
6355    }
6356    elsif($Hname2=~/\Q$Dirname2\E/i
6357    and $Hname1!~/\Q$Dirname1\E/i)
6358    { # include/hildon-thumbnail/hildon-thumbnail-factory.h
6359        return 1;
6360    }
6361    elsif($Hname1=~/(config|lib|util)/i
6362    and $Hname2!~/(config|lib|util)/i)
6363    { # include/alsa/asoundlib.h
6364        return -1;
6365    }
6366    elsif($Hname2=~/(config|lib|util)/i
6367    and $Hname1!~/(config|lib|util)/i)
6368    { # include/alsa/asoundlib.h
6369        return 1;
6370    }
6371    else
6372    {
6373        my $R1 = checkRelevance($H1);
6374        my $R2 = checkRelevance($H2);
6375        if($R1 and not $R2)
6376        { # libebook/e-book.h
6377            return -1;
6378        }
6379        elsif($R2 and not $R1)
6380        { # libebook/e-book.h
6381            return 1;
6382        }
6383        else
6384        {
6385            return (lc($H1) cmp lc($H2));
6386        }
6387    }
6388}
6389
6390sub searchForHeaders($)
6391{
6392    my $LibVersion = $_[0];
6393
6394    # gcc standard include paths
6395    registerGccHeaders();
6396
6397    if($COMMON_LANGUAGE{$LibVersion} eq "C++" and not $STDCXX_TESTING)
6398    { # c++ standard include paths
6399        registerCppHeaders();
6400    }
6401
6402    # processing header paths
6403    foreach my $Path (@{$Descriptor{$LibVersion}{"IncludePaths"}},
6404    @{$Descriptor{$LibVersion}{"AddIncludePaths"}})
6405    {
6406        my $IPath = $Path;
6407        if($SystemRoot)
6408        {
6409            if(is_abs($Path)) {
6410                $Path = $SystemRoot.$Path;
6411            }
6412        }
6413        if(not -e $Path) {
6414            exitStatus("Access_Error", "can't access \'$Path\'");
6415        }
6416        elsif(-f $Path) {
6417            exitStatus("Access_Error", "\'$Path\' - not a directory");
6418        }
6419        elsif(-d $Path)
6420        {
6421            $Path = get_abs_path($Path);
6422            registerDir($Path, 0, $LibVersion);
6423            if(grep {$IPath eq $_} @{$Descriptor{$LibVersion}{"AddIncludePaths"}}) {
6424                push(@{$Add_Include_Paths{$LibVersion}}, $Path);
6425            }
6426            else {
6427                push(@{$Include_Paths{$LibVersion}}, $Path);
6428            }
6429        }
6430    }
6431    if(@{$Include_Paths{$LibVersion}}) {
6432        $INC_PATH_AUTODETECT{$LibVersion} = 0;
6433    }
6434
6435    # registering directories
6436    foreach my $Path (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
6437    {
6438        next if(not -e $Path);
6439        $Path = get_abs_path($Path);
6440        $Path = path_format($Path, $OSgroup);
6441        if(-d $Path) {
6442            registerDir($Path, 1, $LibVersion);
6443        }
6444        elsif(-f $Path)
6445        {
6446            my $Dir = get_dirname($Path);
6447            if(not grep { $Dir eq $_ } (@{$SystemPaths{"include"}})
6448            and not $LocalIncludes{$Dir})
6449            {
6450                registerDir($Dir, 1, $LibVersion);
6451                # if(my $OutDir = get_dirname($Dir))
6452                # { # registering the outer directory
6453                #     if(not grep { $OutDir eq $_ } (@{$SystemPaths{"include"}})
6454                #     and not $LocalIncludes{$OutDir}) {
6455                #         registerDir($OutDir, 0, $LibVersion);
6456                #     }
6457                # }
6458            }
6459        }
6460    }
6461
6462    # clean memory
6463    %RegisteredDirs = ();
6464
6465    # registering headers
6466    my $Position = 0;
6467    foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Headers"}))
6468    {
6469        if(is_abs($Dest) and not -e $Dest) {
6470            exitStatus("Access_Error", "can't access \'$Dest\'");
6471        }
6472        $Dest = path_format($Dest, $OSgroup);
6473        if(is_header($Dest, 1, $LibVersion))
6474        {
6475            if(my $HPath = registerHeader($Dest, $LibVersion)) {
6476                $Registered_Headers{$LibVersion}{$HPath}{"Pos"} = $Position++;
6477            }
6478        }
6479        elsif(-d $Dest)
6480        {
6481            my @Registered = ();
6482            foreach my $Path (cmd_find($Dest,"f"))
6483            {
6484                next if(ignore_path($Path));
6485                next if(not is_header($Path, 0, $LibVersion));
6486                if(my $HPath = registerHeader($Path, $LibVersion)) {
6487                    push(@Registered, $HPath);
6488                }
6489            }
6490            @Registered = sort {sortHeaders($a, $b)} @Registered;
6491            sortByWord(\@Registered, $TargetLibraryShortName);
6492            foreach my $Path (@Registered) {
6493                $Registered_Headers{$LibVersion}{$Path}{"Pos"} = $Position++;
6494            }
6495        }
6496        else {
6497            exitStatus("Access_Error", "can't identify \'$Dest\' as a header file");
6498        }
6499    }
6500
6501    if(defined $Tolerance and $Tolerance=~/4/)
6502    { # 4 - skip headers included by others
6503        foreach my $Path (keys(%{$Registered_Headers{$LibVersion}}))
6504        {
6505            if(defined $Header_Includes_R{$LibVersion}{$Path}) {
6506                delete($Registered_Headers{$LibVersion}{$Path});
6507            }
6508        }
6509    }
6510
6511    if(my $HList = $Descriptor{$LibVersion}{"IncludePreamble"})
6512    { # preparing preamble headers
6513        foreach my $Header (split(/\s*\n\s*/, $HList))
6514        {
6515            if(is_abs($Header) and not -f $Header) {
6516                exitStatus("Access_Error", "can't access file \'$Header\'");
6517            }
6518            $Header = path_format($Header, $OSgroup);
6519            if(my $Header_Path = is_header($Header, 1, $LibVersion))
6520            {
6521                next if(skipHeader($Header_Path, $LibVersion));
6522                push_U($Include_Preamble{$LibVersion}, $Header_Path);
6523            }
6524            else {
6525                exitStatus("Access_Error", "can't identify \'$Header\' as a header file");
6526            }
6527        }
6528    }
6529    foreach my $Header_Name (keys(%{$HeaderName_Paths{$LibVersion}}))
6530    { # set relative paths (for duplicates)
6531        if(keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})>=2)
6532        { # search for duplicates
6533            my $FirstPath = (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))[0];
6534            my $Prefix = get_dirname($FirstPath);
6535            while($Prefix=~/\A(.+)[\/\\]+[^\/\\]+\Z/)
6536            { # detect a shortest distinguishing prefix
6537                my $NewPrefix = $1;
6538                my %Identity = ();
6539                foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
6540                {
6541                    if($Path=~/\A\Q$Prefix\E[\/\\]+(.*)\Z/) {
6542                        $Identity{$Path} = $1;
6543                    }
6544                }
6545                if(keys(%Identity)==keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}}))
6546                { # all names are differend with current prefix
6547                    foreach my $Path (keys(%{$HeaderName_Paths{$LibVersion}{$Header_Name}})) {
6548                        $Registered_Headers{$LibVersion}{$Path}{"Identity"} = $Identity{$Path};
6549                    }
6550                    last;
6551                }
6552                $Prefix = $NewPrefix; # increase prefix
6553            }
6554        }
6555    }
6556
6557    # clean memory
6558    %HeaderName_Paths = ();
6559
6560    foreach my $HeaderName (keys(%{$Include_Order{$LibVersion}}))
6561    { # ordering headers according to descriptor
6562        my $PairName = $Include_Order{$LibVersion}{$HeaderName};
6563        my ($Pos, $PairPos) = (-1, -1);
6564        my ($Path, $PairPath) = ();
6565        my @Paths = keys(%{$Registered_Headers{$LibVersion}});
6566        @Paths = sort {int($Registered_Headers{$LibVersion}{$a}{"Pos"})<=>int($Registered_Headers{$LibVersion}{$b}{"Pos"})} @Paths;
6567        foreach my $Header_Path (@Paths)
6568        {
6569            if(get_filename($Header_Path) eq $PairName)
6570            {
6571                $PairPos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
6572                $PairPath = $Header_Path;
6573            }
6574            if(get_filename($Header_Path) eq $HeaderName)
6575            {
6576                $Pos = $Registered_Headers{$LibVersion}{$Header_Path}{"Pos"};
6577                $Path = $Header_Path;
6578            }
6579        }
6580        if($PairPos!=-1 and $Pos!=-1
6581        and int($PairPos)<int($Pos))
6582        {
6583            my %Tmp = %{$Registered_Headers{$LibVersion}{$Path}};
6584            %{$Registered_Headers{$LibVersion}{$Path}} = %{$Registered_Headers{$LibVersion}{$PairPath}};
6585            %{$Registered_Headers{$LibVersion}{$PairPath}} = %Tmp;
6586        }
6587    }
6588    if(not keys(%{$Registered_Headers{$LibVersion}})) {
6589        exitStatus("Error", "header files are not found in the ".$Descriptor{$LibVersion}{"Version"});
6590    }
6591}
6592
6593sub detect_real_includes($$)
6594{
6595    my ($AbsPath, $LibVersion) = @_;
6596    return () if(not $LibVersion or not $AbsPath or not -e $AbsPath);
6597    if($Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}
6598    or keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
6599        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6600    }
6601    $Cache{"detect_real_includes"}{$LibVersion}{$AbsPath}=1;
6602
6603    my $Path = callPreprocessor($AbsPath, "", $LibVersion);
6604    return () if(not $Path);
6605    open(PREPROC, $Path);
6606    while(<PREPROC>)
6607    {
6608        if(/#\s+\d+\s+"([^"]+)"[\s\d]*\n/)
6609        {
6610            my $Include = path_format($1, $OSgroup);
6611            if($Include=~/\<(built\-in|internal|command(\-|\s)line)\>|\A\./) {
6612                next;
6613            }
6614            if($Include eq $AbsPath) {
6615                next;
6616            }
6617            $RecursiveIncludes{$LibVersion}{$AbsPath}{$Include} = 1;
6618        }
6619    }
6620    close(PREPROC);
6621    return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6622}
6623
6624sub detect_header_includes($$)
6625{
6626    my ($Path, $LibVersion) = @_;
6627    return if(not $LibVersion or not $Path);
6628    if(defined $Cache{"detect_header_includes"}{$LibVersion}{$Path}) {
6629        return;
6630    }
6631    $Cache{"detect_header_includes"}{$LibVersion}{$Path}=1;
6632
6633    if(not -e $Path) {
6634        return;
6635    }
6636
6637    my $Content = readFile($Path);
6638    if(my $Redirect = parse_redirect($Content, $Path, $LibVersion))
6639    { # detect error directive in headers
6640        if(my $RedirectPath = identifyHeader($Redirect, $LibVersion))
6641        {
6642            if($RedirectPath=~/\/usr\/include\// and $Path!~/\/usr\/include\//) {
6643                $RedirectPath = identifyHeader(get_filename($Redirect), $LibVersion);
6644            }
6645            if($RedirectPath ne $Path) {
6646                $Header_ErrorRedirect{$LibVersion}{$Path} = $RedirectPath;
6647            }
6648        }
6649        else
6650        { # can't find
6651            $Header_ShouldNotBeUsed{$LibVersion}{$Path} = 1;
6652        }
6653    }
6654    if(my $Inc = parse_includes($Content, $Path))
6655    {
6656        foreach my $Include (keys(%{$Inc}))
6657        { # detect includes
6658            $Header_Includes{$LibVersion}{$Path}{$Include} = $Inc->{$Include};
6659
6660            if(defined $Tolerance and $Tolerance=~/4/)
6661            {
6662                if(my $HPath = identifyHeader($Include, $LibVersion))
6663                {
6664                    $Header_Includes_R{$LibVersion}{$HPath}{$Path} = 1;
6665                }
6666            }
6667        }
6668    }
6669}
6670
6671sub fromLibc($)
6672{ # system GLIBC header
6673    my $Path = $_[0];
6674    my ($Dir, $Name) = separate_path($Path);
6675    if($OStarget eq "symbian")
6676    {
6677        if(get_filename($Dir) eq "libc" and $GlibcHeader{$Name})
6678        { # epoc32/include/libc/{stdio, ...}.h
6679            return 1;
6680        }
6681    }
6682    else
6683    {
6684        if($Dir eq "/usr/include" and $GlibcHeader{$Name})
6685        { # /usr/include/{stdio, ...}.h
6686            return 1;
6687        }
6688    }
6689    return 0;
6690}
6691
6692sub isLibcDir($)
6693{ # system GLIBC directory
6694    my $Dir = $_[0];
6695    my ($OutDir, $Name) = separate_path($Dir);
6696    if($OStarget eq "symbian")
6697    {
6698        if(get_filename($OutDir) eq "libc"
6699        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
6700        { # epoc32/include/libc/{sys,bits,asm,asm-*}/*.h
6701            return 1;
6702        }
6703    }
6704    else
6705    { # linux
6706        if($OutDir eq "/usr/include"
6707        and ($Name=~/\Aasm(|-.+)\Z/ or $GlibcDir{$Name}))
6708        { # /usr/include/{sys,bits,asm,asm-*}/*.h
6709            return 1;
6710        }
6711    }
6712    return 0;
6713}
6714
6715sub detect_recursive_includes($$)
6716{
6717    my ($AbsPath, $LibVersion) = @_;
6718    return () if(not $AbsPath);
6719    if(isCyclical(\@RecurInclude, $AbsPath)) {
6720        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6721    }
6722    my ($AbsDir, $Name) = separate_path($AbsPath);
6723    if(isLibcDir($AbsDir))
6724    { # system GLIBC internals
6725        return () if(not $ExtraInfo);
6726    }
6727    if(keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}})) {
6728        return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6729    }
6730    return () if($OSgroup ne "windows" and $Name=~/windows|win32|win64/i);
6731
6732    if($MAIN_CPP_DIR and $AbsPath=~/\A\Q$MAIN_CPP_DIR\E/ and not $STDCXX_TESTING)
6733    { # skip /usr/include/c++/*/ headers
6734        return () if(not $ExtraInfo);
6735    }
6736
6737    push(@RecurInclude, $AbsPath);
6738    if(grep { $AbsDir eq $_ } @DefaultGccPaths
6739    or (grep { $AbsDir eq $_ } @DefaultIncPaths and fromLibc($AbsPath)))
6740    { # check "real" (non-"model") include paths
6741        my @Paths = detect_real_includes($AbsPath, $LibVersion);
6742        pop(@RecurInclude);
6743        return @Paths;
6744    }
6745    if(not keys(%{$Header_Includes{$LibVersion}{$AbsPath}})) {
6746        detect_header_includes($AbsPath, $LibVersion);
6747    }
6748    foreach my $Include (keys(%{$Header_Includes{$LibVersion}{$AbsPath}}))
6749    {
6750        my $IncType = $Header_Includes{$LibVersion}{$AbsPath}{$Include};
6751        my $HPath = "";
6752        if($IncType<0)
6753        { # for #include "..."
6754            my $Candidate = join_P($AbsDir, $Include);
6755            if(-f $Candidate) {
6756                $HPath = realpath($Candidate);
6757            }
6758        }
6759        elsif($IncType>0
6760        and $Include=~/[\/\\]/) # and not find_in_defaults($Include)
6761        { # search for the nearest header
6762          # QtCore/qabstractanimation.h includes <QtCore/qobject.h>
6763            my $Candidate = join_P(get_dirname($AbsDir), $Include);
6764            if(-f $Candidate) {
6765                $HPath = $Candidate;
6766            }
6767        }
6768        if(not $HPath) {
6769            $HPath = identifyHeader($Include, $LibVersion);
6770        }
6771        next if(not $HPath);
6772        if($HPath eq $AbsPath) {
6773            next;
6774        }
6775
6776        if($Debug)
6777        { # boundary headers
6778#             if($HPath=~/vtk/ and $AbsPath!~/vtk/)
6779#             {
6780#                 print STDERR "$AbsPath -> $HPath\n";
6781#             }
6782        }
6783
6784        $RecursiveIncludes{$LibVersion}{$AbsPath}{$HPath} = $IncType;
6785        if($IncType>0)
6786        { # only include <...>, skip include "..." prefixes
6787            $Header_Include_Prefix{$LibVersion}{$AbsPath}{$HPath}{get_dirname($Include)} = 1;
6788        }
6789        foreach my $IncPath (detect_recursive_includes($HPath, $LibVersion))
6790        {
6791            if($IncPath eq $AbsPath) {
6792                next;
6793            }
6794            my $RIncType = $RecursiveIncludes{$LibVersion}{$HPath}{$IncPath};
6795            if($RIncType==-1)
6796            { # include "..."
6797                $RIncType = $IncType;
6798            }
6799            elsif($RIncType==2)
6800            {
6801                if($IncType!=-1) {
6802                    $RIncType = $IncType;
6803                }
6804            }
6805            $RecursiveIncludes{$LibVersion}{$AbsPath}{$IncPath} = $RIncType;
6806            foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$HPath}{$IncPath}})) {
6807                $Header_Include_Prefix{$LibVersion}{$AbsPath}{$IncPath}{$Prefix} = 1;
6808            }
6809        }
6810        foreach my $Dep (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}}))
6811        {
6812            if($GlibcHeader{get_filename($Dep)} and keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}})>=2
6813            and defined $Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""})
6814            { # distinguish math.h from glibc and math.h from the tested library
6815                delete($Header_Include_Prefix{$LibVersion}{$AbsPath}{$Dep}{""});
6816                last;
6817            }
6818        }
6819    }
6820    pop(@RecurInclude);
6821    return keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}});
6822}
6823
6824sub find_in_framework($$$)
6825{
6826    my ($Header, $Framework, $LibVersion) = @_;
6827    return "" if(not $Header or not $Framework or not $LibVersion);
6828    if(defined $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header}) {
6829        return $Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header};
6830    }
6831    foreach my $Dependency (sort {get_depth($a)<=>get_depth($b)} keys(%{$Header_Dependency{$LibVersion}}))
6832    {
6833        if(get_filename($Dependency) eq $Framework
6834        and -f get_dirname($Dependency)."/".$Header) {
6835            return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = get_dirname($Dependency));
6836        }
6837    }
6838    return ($Cache{"find_in_framework"}{$LibVersion}{$Framework}{$Header} = "");
6839}
6840
6841sub find_in_defaults($)
6842{
6843    my $Header = $_[0];
6844    return "" if(not $Header);
6845    if(defined $Cache{"find_in_defaults"}{$Header}) {
6846        return $Cache{"find_in_defaults"}{$Header};
6847    }
6848    foreach my $Dir (@DefaultIncPaths,
6849                     @DefaultGccPaths,
6850                     @DefaultCppPaths,
6851                     @UsersIncPath)
6852    {
6853        next if(not $Dir);
6854        if(-f $Dir."/".$Header) {
6855            return ($Cache{"find_in_defaults"}{$Header}=$Dir);
6856        }
6857    }
6858    return ($Cache{"find_in_defaults"}{$Header}="");
6859}
6860
6861sub cmp_paths($$)
6862{
6863    my ($Path1, $Path2) = @_;
6864    my @Parts1 = split(/[\/\\]/, $Path1);
6865    my @Parts2 = split(/[\/\\]/, $Path2);
6866    foreach my $Num (0 .. $#Parts1)
6867    {
6868        my $Part1 = $Parts1[$Num];
6869        my $Part2 = $Parts2[$Num];
6870        if($GlibcDir{$Part1}
6871        and not $GlibcDir{$Part2}) {
6872            return 1;
6873        }
6874        elsif($GlibcDir{$Part2}
6875        and not $GlibcDir{$Part1}) {
6876            return -1;
6877        }
6878        elsif($Part1=~/glib/
6879        and $Part2!~/glib/) {
6880            return 1;
6881        }
6882        elsif($Part1!~/glib/
6883        and $Part2=~/glib/) {
6884            return -1;
6885        }
6886        elsif(my $CmpRes = ($Part1 cmp $Part2)) {
6887            return $CmpRes;
6888        }
6889    }
6890    return 0;
6891}
6892
6893sub checkRelevance($)
6894{
6895    my $Path = $_[0];
6896    return 0 if(not $Path);
6897
6898    if($SystemRoot) {
6899        $Path = cut_path_prefix($Path, $SystemRoot);
6900    }
6901
6902    my $Name = lc(get_filename($Path));
6903    my $Dir = lc(get_dirname($Path));
6904
6905    $Name=~s/\.\w+\Z//g; # remove extension (.h)
6906
6907    foreach my $Token (split(/[_\d\W]+/, $Name))
6908    {
6909        my $Len = length($Token);
6910        next if($Len<=1);
6911        if($Dir=~/(\A|lib|[_\d\W])\Q$Token\E([_\d\W]|lib|\Z)/)
6912        { # include/evolution-data-server-1.4/libebook/e-book.h
6913            return 1;
6914        }
6915        if($Len>=4 and index($Dir, $Token)!=-1)
6916        { # include/gupnp-1.0/libgupnp/gupnp-context.h
6917            return 1;
6918        }
6919    }
6920    return 0;
6921}
6922
6923sub checkFamily(@)
6924{
6925    my @Paths = @_;
6926    return 1 if($#Paths<=0);
6927    my %Prefix = ();
6928    foreach my $Path (@Paths)
6929    {
6930        if($SystemRoot) {
6931            $Path = cut_path_prefix($Path, $SystemRoot);
6932        }
6933        if(my $Dir = get_dirname($Path))
6934        {
6935            $Dir=~s/(\/[^\/]+?)[\d\.\-\_]+\Z/$1/g; # remove version suffix
6936            $Prefix{$Dir} += 1;
6937            $Prefix{get_dirname($Dir)} += 1;
6938        }
6939    }
6940    foreach (sort keys(%Prefix))
6941    {
6942        if(get_depth($_)>=3
6943        and $Prefix{$_}==$#Paths+1) {
6944            return 1;
6945        }
6946    }
6947    return 0;
6948}
6949
6950sub isAcceptable($$$)
6951{
6952    my ($Header, $Candidate, $LibVersion) = @_;
6953    my $HName = get_filename($Header);
6954    if(get_dirname($Header))
6955    { # with prefix
6956        return 1;
6957    }
6958    if($HName=~/config|setup/i and $Candidate=~/[\/\\]lib\d*[\/\\]/)
6959    { # allow to search for glibconfig.h in /usr/lib/glib-2.0/include/
6960        return 1;
6961    }
6962    if(checkRelevance($Candidate))
6963    { # allow to search for atk.h in /usr/include/atk-1.0/atk/
6964        return 1;
6965    }
6966    if(checkFamily(getSystemHeaders($HName, $LibVersion)))
6967    { # /usr/include/qt4/QtNetwork/qsslconfiguration.h
6968      # /usr/include/qt4/Qt/qsslconfiguration.h
6969        return 1;
6970    }
6971    if($OStarget eq "symbian")
6972    {
6973        if($Candidate=~/[\/\\]stdapis[\/\\]/) {
6974            return 1;
6975        }
6976    }
6977    return 0;
6978}
6979
6980sub isRelevant($$$)
6981{ # disallow to search for "abstract" headers in too deep directories
6982    my ($Header, $Candidate, $LibVersion) = @_;
6983    my $HName = get_filename($Header);
6984    if($OStarget eq "symbian")
6985    {
6986        if($Candidate=~/[\/\\](tools|stlportv5)[\/\\]/) {
6987            return 0;
6988        }
6989    }
6990    if($OStarget ne "bsd")
6991    {
6992        if($Candidate=~/[\/\\]include[\/\\]bsd[\/\\]/)
6993        { # openssh: skip /usr/lib/bcc/include/bsd/signal.h
6994            return 0;
6995        }
6996    }
6997    if($OStarget ne "windows")
6998    {
6999        if($Candidate=~/[\/\\](wine|msvcrt|windows)[\/\\]/)
7000        { # skip /usr/include/wine/msvcrt
7001            return 0;
7002        }
7003    }
7004    if(not get_dirname($Header)
7005    and $Candidate=~/[\/\\]wx[\/\\]/)
7006    { # do NOT search in system /wx/ directory
7007      # for headers without a prefix: sstream.h
7008        return 0;
7009    }
7010    if($Candidate=~/c\+\+[\/\\]\d+/ and $MAIN_CPP_DIR
7011    and $Candidate!~/\A\Q$MAIN_CPP_DIR\E/)
7012    { # skip ../c++/3.3.3/ if using ../c++/4.5/
7013        return 0;
7014    }
7015    if($Candidate=~/[\/\\]asm-/
7016    and (my $Arch = getArch($LibVersion)) ne "unknown")
7017    { # arch-specific header files
7018        if($Candidate!~/[\/\\]asm-\Q$Arch\E/)
7019        {# skip ../asm-arm/ if using x86 architecture
7020            return 0;
7021        }
7022    }
7023    my @Candidates = getSystemHeaders($HName, $LibVersion);
7024    if($#Candidates==1)
7025    { # unique header
7026        return 1;
7027    }
7028    my @SCandidates = getSystemHeaders($Header, $LibVersion);
7029    if($#SCandidates==1)
7030    { # unique name
7031        return 1;
7032    }
7033    my $SystemDepth = $SystemRoot?get_depth($SystemRoot):0;
7034    if(get_depth($Candidate)-$SystemDepth>=5)
7035    { # abstract headers in too deep directories
7036      # sstream.h or typeinfo.h in /usr/include/wx-2.9/wx/
7037        if(not isAcceptable($Header, $Candidate, $LibVersion)) {
7038            return 0;
7039        }
7040    }
7041    if($Header eq "parser.h"
7042    and $Candidate!~/\/libxml2\//)
7043    { # select parser.h from xml2 library
7044        return 0;
7045    }
7046    if(not get_dirname($Header)
7047    and keys(%{$SystemHeaders{$HName}})>=3)
7048    { # many headers with the same name
7049      # like thread.h included without a prefix
7050        if(not checkFamily(@Candidates)) {
7051            return 0;
7052        }
7053    }
7054    return 1;
7055}
7056
7057sub selectSystemHeader($$)
7058{ # cache function
7059    if(defined $Cache{"selectSystemHeader"}{$_[1]}{$_[0]}) {
7060        return $Cache{"selectSystemHeader"}{$_[1]}{$_[0]};
7061    }
7062    return ($Cache{"selectSystemHeader"}{$_[1]}{$_[0]} = selectSystemHeader_I(@_));
7063}
7064
7065sub selectSystemHeader_I($$)
7066{
7067    my ($Header, $LibVersion) = @_;
7068    if(-f $Header) {
7069        return $Header;
7070    }
7071    if(is_abs($Header) and not -f $Header)
7072    { # incorrect absolute path
7073        return "";
7074    }
7075    if(defined $ConfHeaders{lc($Header)})
7076    { # too abstract configuration headers
7077        return "";
7078    }
7079    my $HName = get_filename($Header);
7080    if($OSgroup ne "windows")
7081    {
7082        if(defined $WinHeaders{lc($HName)}
7083        or $HName=~/windows|win32|win64/i)
7084        { # windows headers
7085            return "";
7086        }
7087    }
7088    if($OSgroup ne "macos")
7089    {
7090        if($HName eq "fp.h")
7091        { # pngconf.h includes fp.h in Mac OS
7092            return "";
7093        }
7094    }
7095
7096    if(defined $ObsoleteHeaders{$HName})
7097    { # obsolete headers
7098        return "";
7099    }
7100    if($OSgroup eq "linux" or $OSgroup eq "bsd")
7101    {
7102        if(defined $AlienHeaders{$HName}
7103        or defined $AlienHeaders{$Header})
7104        { # alien headers from other systems
7105            return "";
7106        }
7107    }
7108
7109    foreach my $Path (@{$SystemPaths{"include"}})
7110    { # search in default paths
7111        if(-f $Path."/".$Header) {
7112            return join_P($Path,$Header);
7113        }
7114    }
7115    if(not defined $Cache{"checkSystemFiles"})
7116    { # register all headers in system include dirs
7117        checkSystemFiles();
7118    }
7119    foreach my $Candidate (sort {get_depth($a)<=>get_depth($b)}
7120    sort {cmp_paths($b, $a)} getSystemHeaders($Header, $LibVersion))
7121    {
7122        if(isRelevant($Header, $Candidate, $LibVersion)) {
7123            return $Candidate;
7124        }
7125    }
7126    # error
7127    return "";
7128}
7129
7130sub getSystemHeaders($$)
7131{
7132    my ($Header, $LibVersion) = @_;
7133    my @Candidates = ();
7134    foreach my $Candidate (sort keys(%{$SystemHeaders{$Header}}))
7135    {
7136        if(skipHeader($Candidate, $LibVersion)) {
7137            next;
7138        }
7139        push(@Candidates, $Candidate);
7140    }
7141    return @Candidates;
7142}
7143
7144sub cut_path_prefix($$)
7145{
7146    my ($Path, $Prefix) = @_;
7147    return $Path if(not $Prefix);
7148    $Prefix=~s/[\/\\]+\Z//;
7149    $Path=~s/\A\Q$Prefix\E([\/\\]+|\Z)//;
7150    return $Path;
7151}
7152
7153sub is_default_include_dir($)
7154{
7155    my $Dir = $_[0];
7156    $Dir=~s/[\/\\]+\Z//;
7157    return grep { $Dir eq $_ } (@DefaultGccPaths, @DefaultCppPaths, @DefaultIncPaths);
7158}
7159
7160sub identifyHeader($$)
7161{ # cache function
7162    my ($Header, $LibVersion) = @_;
7163    if(not $Header) {
7164        return "";
7165    }
7166    $Header=~s/\A(\.\.[\\\/])+//g;
7167    if(defined $Cache{"identifyHeader"}{$LibVersion}{$Header}) {
7168        return $Cache{"identifyHeader"}{$LibVersion}{$Header};
7169    }
7170    return ($Cache{"identifyHeader"}{$LibVersion}{$Header} = identifyHeader_I($Header, $LibVersion));
7171}
7172
7173sub identifyHeader_I($$)
7174{ # search for header by absolute path, relative path or name
7175    my ($Header, $LibVersion) = @_;
7176    if(-f $Header)
7177    { # it's relative or absolute path
7178        return get_abs_path($Header);
7179    }
7180    elsif($GlibcHeader{$Header} and not $GLIBC_TESTING
7181    and my $HeaderDir = find_in_defaults($Header))
7182    { # search for libc headers in the /usr/include
7183      # for non-libc target library before searching
7184      # in the library paths
7185        return join_P($HeaderDir,$Header);
7186    }
7187    elsif(my $Path = $Include_Neighbors{$LibVersion}{$Header})
7188    { # search in the target library paths
7189        return $Path;
7190    }
7191    elsif(defined $DefaultGccHeader{$Header})
7192    { # search in the internal GCC include paths
7193        return $DefaultGccHeader{$Header};
7194    }
7195    elsif(my $DefaultDir = find_in_defaults($Header))
7196    { # search in the default GCC include paths
7197        return join_P($DefaultDir,$Header);
7198    }
7199    elsif(defined $DefaultCppHeader{$Header})
7200    { # search in the default G++ include paths
7201        return $DefaultCppHeader{$Header};
7202    }
7203    elsif(my $AnyPath = selectSystemHeader($Header, $LibVersion))
7204    { # search everywhere in the system
7205        return $AnyPath;
7206    }
7207    elsif($OSgroup eq "macos")
7208    { # search in frameworks: "OpenGL/gl.h" is "OpenGL.framework/Headers/gl.h"
7209        if(my $Dir = get_dirname($Header))
7210        {
7211            my $RelPath = "Headers\/".get_filename($Header);
7212            if(my $HeaderDir = find_in_framework($RelPath, $Dir.".framework", $LibVersion)) {
7213                return join_P($HeaderDir, $RelPath);
7214            }
7215        }
7216    }
7217    # cannot find anything
7218    return "";
7219}
7220
7221sub getLocation($)
7222{
7223    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7224    {
7225        if($Info=~/srcp[ ]*:[ ]*([\w\-\<\>\.\+\/\\]+):(\d+) /) {
7226            return (path_format($1, $OSgroup), $2);
7227        }
7228    }
7229    return ();
7230}
7231
7232sub getNameByInfo($)
7233{
7234    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7235    {
7236        if($Info=~/name[ ]*:[ ]*@(\d+) /)
7237        {
7238            if(my $NInfo = $LibInfo{$Version}{"info"}{$1})
7239            {
7240                if($NInfo=~/strg[ ]*:[ ]*(.*?)[ ]+lngt/)
7241                { # short unsigned int (may include spaces)
7242                    my $Str = $1;
7243                    if($CppMode{$Version}
7244                    and $Str=~/\Ac99_(.+)\Z/)
7245                    {
7246                        if($CppKeywords_A{$1}) {
7247                            $Str=$1;
7248                        }
7249                    }
7250                    return $Str;
7251                }
7252            }
7253        }
7254    }
7255    return "";
7256}
7257
7258sub getTreeStr($)
7259{
7260    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7261    {
7262        if($Info=~/strg[ ]*:[ ]*([^ ]*)/)
7263        {
7264            my $Str = $1;
7265            if($CppMode{$Version}
7266            and $Str=~/\Ac99_(.+)\Z/)
7267            {
7268                if($CppKeywords_A{$1}) {
7269                    $Str=$1;
7270                }
7271            }
7272            return $Str;
7273        }
7274    }
7275    return "";
7276}
7277
7278sub getFuncShortName($)
7279{
7280    if(my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7281    {
7282        if(index($Info, " operator ")!=-1)
7283        {
7284            if(index($Info, " conversion ")!=-1)
7285            {
7286                if(my $Rid = $SymbolInfo{$Version}{$_[0]}{"Return"})
7287                {
7288                    if(my $RName = $TypeInfo{$Version}{$Rid}{"Name"}) {
7289                        return "operator ".$RName;
7290                    }
7291                }
7292            }
7293            else
7294            {
7295                if($Info=~/ operator[ ]+([a-zA-Z]+) /)
7296                {
7297                    if(my $Ind = $Operator_Indication{$1}) {
7298                        return "operator".$Ind;
7299                    }
7300                    elsif(not $UnknownOperator{$1})
7301                    {
7302                        printMsg("WARNING", "unknown operator $1");
7303                        $UnknownOperator{$1} = 1;
7304                    }
7305                }
7306            }
7307        }
7308        else
7309        {
7310            if($Info=~/name[ ]*:[ ]*@(\d+) /) {
7311                return getTreeStr($1);
7312            }
7313        }
7314    }
7315    return "";
7316}
7317
7318sub getFuncReturn($)
7319{
7320    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7321    {
7322        if($Info=~/type[ ]*:[ ]*@(\d+) /)
7323        {
7324            if($LibInfo{$Version}{"info"}{$1}=~/retn[ ]*:[ ]*@(\d+) /) {
7325                return $1;
7326            }
7327        }
7328    }
7329    return "";
7330}
7331
7332sub getFuncOrig($)
7333{
7334    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7335    {
7336        if($Info=~/orig[ ]*:[ ]*@(\d+) /) {
7337            return $1;
7338        }
7339    }
7340    return $_[0];
7341}
7342
7343sub unmangleArray(@)
7344{
7345    if($_[0]=~/\A\?/)
7346    { # MSVC mangling
7347        my $UndNameCmd = get_CmdPath("undname");
7348        if(not $UndNameCmd) {
7349            exitStatus("Not_Found", "can't find \"undname\"");
7350        }
7351        writeFile("$TMP_DIR/unmangle", join("\n", @_));
7352        return split(/\n/, `$UndNameCmd 0x8386 \"$TMP_DIR/unmangle\"`);
7353    }
7354    else
7355    { # GCC mangling
7356        my $CppFiltCmd = get_CmdPath("c++filt");
7357        if(not $CppFiltCmd) {
7358            exitStatus("Not_Found", "can't find c++filt in PATH");
7359        }
7360        if(not defined $CPPFILT_SUPPORT_FILE)
7361        {
7362            my $Info = `$CppFiltCmd -h 2>&1`;
7363            $CPPFILT_SUPPORT_FILE = $Info=~/\@<file>/;
7364        }
7365        my $NoStrip = ($OSgroup=~/macos|windows/)?"-n":"";
7366        if($CPPFILT_SUPPORT_FILE)
7367        { # new versions of c++filt can take a file
7368            if($#_>$MAX_CPPFILT_FILE_SIZE)
7369            { # c++filt <= 2.22 may crash on large files (larger than 8mb)
7370              # this is fixed in the oncoming version of Binutils
7371                my @Half = splice(@_, 0, ($#_+1)/2);
7372                return (unmangleArray(@Half), unmangleArray(@_))
7373            }
7374            else
7375            {
7376                writeFile("$TMP_DIR/unmangle", join("\n", @_));
7377                my $Res = `$CppFiltCmd $NoStrip \@\"$TMP_DIR/unmangle\"`;
7378                if($?==139)
7379                { # segmentation fault
7380                    printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_CPPFILT_FILE_SIZE constant");
7381                }
7382                return split(/\n/, $Res);
7383            }
7384        }
7385        else
7386        { # old-style unmangling
7387            if($#_>$MAX_COMMAND_LINE_ARGUMENTS)
7388            {
7389                my @Half = splice(@_, 0, ($#_+1)/2);
7390                return (unmangleArray(@Half), unmangleArray(@_))
7391            }
7392            else
7393            {
7394                my $Strings = join(" ", @_);
7395                my $Res = `$CppFiltCmd $NoStrip $Strings`;
7396                if($?==139)
7397                { # segmentation fault
7398                    printMsg("ERROR", "internal error - c++filt crashed, try to reduce MAX_COMMAND_LINE_ARGUMENTS constant");
7399                }
7400                return split(/\n/, $Res);
7401            }
7402        }
7403    }
7404}
7405
7406sub get_SignatureNoInfo($$)
7407{
7408    my ($Symbol, $LibVersion) = @_;
7409    if($Cache{"get_SignatureNoInfo"}{$LibVersion}{$Symbol}) {
7410        return $Cache{"get_SignatureNoInfo"}{$LibVersion}{$Symbol};
7411    }
7412    my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
7413    my $Signature = $tr_name{$MnglName}?$tr_name{$MnglName}:$MnglName;
7414    if($Symbol=~/\A(_Z|\?)/)
7415    { # C++
7416        # some standard typedefs
7417        $Signature=~s/\Qstd::basic_string<char, std::char_traits<char>, std::allocator<char> >\E/std::string/g;
7418        $Signature=~s/\Qstd::map<std::string, std::string, std::less<std::string >, std::allocator<std::pair<std::string const, std::string > > >\E/std::map<std::string, std::string>/g;
7419    }
7420    if(not $CheckObjectsOnly or $OSgroup=~/linux|bsd|beos/i)
7421    { # ELF format marks data as OBJECT
7422        if($GlobalDataObject{$LibVersion}{$Symbol}) {
7423            $Signature .= " [data]";
7424        }
7425        elsif($Symbol!~/\A(_Z|\?)/) {
7426            $Signature .= " (...)";
7427        }
7428    }
7429    if(my $ChargeLevel = get_ChargeLevel($Symbol, $LibVersion))
7430    {
7431        my $ShortName = substr($Signature, 0, find_center($Signature, "("));
7432        $Signature=~s/\A\Q$ShortName\E/$ShortName $ChargeLevel/g;
7433    }
7434    if($SymbolVersion) {
7435        $Signature .= $VersionSpec.$SymbolVersion;
7436    }
7437    return ($Cache{"get_SignatureNoInfo"}{$LibVersion}{$Symbol} = $Signature);
7438}
7439
7440sub get_ChargeLevel($$)
7441{
7442    my ($Symbol, $LibVersion) = @_;
7443    return "" if($Symbol!~/\A(_Z|\?)/);
7444    if(defined $CompleteSignature{$LibVersion}{$Symbol}
7445    and $CompleteSignature{$LibVersion}{$Symbol}{"Header"})
7446    {
7447        if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"})
7448        {
7449            if($Symbol=~/C1[EI]/) {
7450                return "[in-charge]";
7451            }
7452            elsif($Symbol=~/C2[EI]/) {
7453                return "[not-in-charge]";
7454            }
7455        }
7456        elsif($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"})
7457        {
7458            if($Symbol=~/D1[EI]/) {
7459                return "[in-charge]";
7460            }
7461            elsif($Symbol=~/D2[EI]/) {
7462                return "[not-in-charge]";
7463            }
7464            elsif($Symbol=~/D0[EI]/) {
7465                return "[in-charge-deleting]";
7466            }
7467        }
7468    }
7469    else
7470    {
7471        if($Symbol=~/C1[EI]/) {
7472            return "[in-charge]";
7473        }
7474        elsif($Symbol=~/C2[EI]/) {
7475            return "[not-in-charge]";
7476        }
7477        elsif($Symbol=~/D1[EI]/) {
7478            return "[in-charge]";
7479        }
7480        elsif($Symbol=~/D2[EI]/) {
7481            return "[not-in-charge]";
7482        }
7483        elsif($Symbol=~/D0[EI]/) {
7484            return "[in-charge-deleting]";
7485        }
7486    }
7487    return "";
7488}
7489
7490sub get_Signature_M($$)
7491{
7492    my ($Symbol, $LibVersion) = @_;
7493    my $Signature_M = $tr_name{$Symbol};
7494    if(my $RTid = $CompleteSignature{$LibVersion}{$Symbol}{"Return"})
7495    { # add return type name
7496        $Signature_M = $TypeInfo{$LibVersion}{$RTid}{"Name"}." ".$Signature_M;
7497    }
7498    return $Signature_M;
7499}
7500
7501sub get_Signature($$)
7502{
7503    my ($Symbol, $LibVersion) = @_;
7504    if($Cache{"get_Signature"}{$LibVersion}{$Symbol}) {
7505        return $Cache{"get_Signature"}{$LibVersion}{$Symbol};
7506    }
7507    my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
7508    if(isPrivateData($MnglName) or not $CompleteSignature{$LibVersion}{$Symbol}{"Header"})
7509    { # non-public global data
7510        return get_SignatureNoInfo($Symbol, $LibVersion);
7511    }
7512    my ($Signature, @Param_Types_FromUnmangledName) = ();
7513    my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"};
7514    if($Symbol=~/\A(_Z|\?)/)
7515    {
7516        if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
7517        {
7518            $Signature .= $TypeInfo{$LibVersion}{$ClassId}{"Name"}."::";
7519            if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}) {
7520                $Signature .= "~";
7521            }
7522            $Signature .= $ShortName;
7523        }
7524        elsif(my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"}) {
7525            $Signature .= $NameSpace."::".$ShortName;
7526        }
7527        else {
7528            $Signature .= $ShortName;
7529        }
7530        my ($Short, $Params) = split_Signature($tr_name{$MnglName});
7531        @Param_Types_FromUnmangledName = separate_Params($Params, 0, 1);
7532    }
7533    else
7534    {
7535        $Signature .= $MnglName;
7536    }
7537    my @ParamArray = ();
7538    foreach my $Pos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
7539    {
7540        next if($Pos eq "");
7541        my $ParamTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"};
7542        next if(not $ParamTypeId);
7543        my $ParamTypeName = $TypeInfo{$LibVersion}{$ParamTypeId}{"Name"};
7544        if(not $ParamTypeName) {
7545            $ParamTypeName = $Param_Types_FromUnmangledName[$Pos];
7546        }
7547        foreach my $Typedef (keys(%ChangedTypedef))
7548        {
7549            if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) {
7550                $ParamTypeName=~s/\b\Q$Typedef\E\b/$Base/g;
7551            }
7552        }
7553        if(my $ParamName = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"name"})
7554        {
7555            if($ParamName eq "this"
7556            and $Symbol=~/\A(_Z|\?)/)
7557            { # do NOT show first hidded "this"-parameter
7558                next;
7559            }
7560            push(@ParamArray, create_member_decl($ParamTypeName, $ParamName));
7561        }
7562        else {
7563            push(@ParamArray, $ParamTypeName);
7564        }
7565    }
7566    if($CompleteSignature{$LibVersion}{$Symbol}{"Data"}
7567    or $GlobalDataObject{$LibVersion}{$Symbol}) {
7568        $Signature .= " [data]";
7569    }
7570    else
7571    {
7572        if(my $ChargeLevel = get_ChargeLevel($Symbol, $LibVersion))
7573        { # add [in-charge]
7574            $Signature .= " ".$ChargeLevel;
7575        }
7576        $Signature .= " (".join(", ", @ParamArray).")";
7577        if($CompleteSignature{$LibVersion}{$Symbol}{"Const"}
7578        or $Symbol=~/\A_ZN(V|)K/) {
7579            $Signature .= " const";
7580        }
7581        if($CompleteSignature{$LibVersion}{$Symbol}{"Volatile"}
7582        or $Symbol=~/\A_ZN(K|)V/) {
7583            $Signature .= " volatile";
7584        }
7585        if($CompleteSignature{$LibVersion}{$Symbol}{"Static"}
7586        and $Symbol=~/\A(_Z|\?)/)
7587        { # for static methods
7588            $Signature .= " [static]";
7589        }
7590    }
7591    if(defined $ShowRetVal
7592    and my $ReturnTId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"}) {
7593        $Signature .= ":".$TypeInfo{$LibVersion}{$ReturnTId}{"Name"};
7594    }
7595    if($SymbolVersion) {
7596        $Signature .= $VersionSpec.$SymbolVersion;
7597    }
7598    return ($Cache{"get_Signature"}{$LibVersion}{$Symbol} = $Signature);
7599}
7600
7601sub create_member_decl($$)
7602{
7603    my ($TName, $Member) = @_;
7604    if($TName=~/\([\*]+\)/)
7605    {
7606        $TName=~s/\(([\*]+)\)/\($1$Member\)/;
7607        return $TName;
7608    }
7609    else
7610    {
7611        my @ArraySizes = ();
7612        while($TName=~s/(\[[^\[\]]*\])\Z//) {
7613            push(@ArraySizes, $1);
7614        }
7615        return $TName." ".$Member.join("", @ArraySizes);
7616    }
7617}
7618
7619sub getFuncType($)
7620{
7621    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7622    {
7623        if($Info=~/type[ ]*:[ ]*@(\d+) /)
7624        {
7625            if(my $Type = $LibInfo{$Version}{"info_type"}{$1})
7626            {
7627                if($Type eq "method_type") {
7628                    return "Method";
7629                }
7630                elsif($Type eq "function_type") {
7631                    return "Function";
7632                }
7633                else {
7634                    return "Other";
7635                }
7636            }
7637        }
7638    }
7639    return "";
7640}
7641
7642sub getFuncTypeId($)
7643{
7644    if($_[0] and my $Info = $LibInfo{$Version}{"info"}{$_[0]})
7645    {
7646        if($Info=~/type[ ]*:[ ]*@(\d+)( |\Z)/) {
7647            return $1;
7648        }
7649    }
7650    return 0;
7651}
7652
7653sub isAnon($)
7654{ # "._N" or "$_N" in older GCC versions
7655    return ($_[0] and $_[0]=~/(\.|\$)\_\d+|anon\-/);
7656}
7657
7658sub formatName($$)
7659{ # type name correction
7660    if(defined $Cache{"formatName"}{$_[1]}{$_[0]}) {
7661        return $Cache{"formatName"}{$_[1]}{$_[0]};
7662    }
7663
7664    my $N = $_[0];
7665
7666    if($_[1] ne "S")
7667    {
7668        $N=~s/\A[ ]+//g;
7669        $N=~s/[ ]+\Z//g;
7670        $N=~s/[ ]{2,}/ /g;
7671    }
7672
7673    $N=~s/[ ]*(\W)[ ]*/$1/g; # std::basic_string<char> const
7674
7675    $N=~s/\bvolatile const\b/const volatile/g;
7676
7677    $N=~s/\b(long long|short|long) unsigned\b/unsigned $1/g;
7678    $N=~s/\b(short|long) int\b/$1/g;
7679
7680    $N=~s/([\)\]])(const|volatile)\b/$1 $2/g;
7681
7682    while($N=~s/>>/> >/g) {};
7683
7684    if($_[1] eq "S")
7685    {
7686        if(index($N, "operator")!=-1) {
7687            $N=~s/\b(operator[ ]*)> >/$1>>/;
7688        }
7689    }
7690
7691    return ($Cache{"formatName"}{$_[1]}{$_[0]} = $N);
7692}
7693
7694sub get_HeaderDeps($$)
7695{
7696    my ($AbsPath, $LibVersion) = @_;
7697    return () if(not $AbsPath or not $LibVersion);
7698    if(defined $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}) {
7699        return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
7700    }
7701    my %IncDir = ();
7702    detect_recursive_includes($AbsPath, $LibVersion);
7703    foreach my $HeaderPath (keys(%{$RecursiveIncludes{$LibVersion}{$AbsPath}}))
7704    {
7705        next if(not $HeaderPath);
7706        next if($MAIN_CPP_DIR and $HeaderPath=~/\A\Q$MAIN_CPP_DIR\E([\/\\]|\Z)/);
7707        my $Dir = get_dirname($HeaderPath);
7708        foreach my $Prefix (keys(%{$Header_Include_Prefix{$LibVersion}{$AbsPath}{$HeaderPath}}))
7709        {
7710            my $Dep = $Dir;
7711            if($Prefix)
7712            {
7713                if($OSgroup eq "windows")
7714                { # case insensitive seach on windows
7715                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//ig) {
7716                        next;
7717                    }
7718                }
7719                elsif($OSgroup eq "macos")
7720                { # seach in frameworks
7721                    if(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
7722                    {
7723                        if($HeaderPath=~/(.+\.framework)\/Headers\/([^\/]+)/)
7724                        {# frameworks
7725                            my ($HFramework, $HName) = ($1, $2);
7726                            $Dep = $HFramework;
7727                        }
7728                        else
7729                        {# mismatch
7730                            next;
7731                        }
7732                    }
7733                }
7734                elsif(not $Dep=~s/[\/\\]+\Q$Prefix\E\Z//g)
7735                { # Linux, FreeBSD
7736                    next;
7737                }
7738            }
7739            if(not $Dep)
7740            { # nothing to include
7741                next;
7742            }
7743            if(is_default_include_dir($Dep))
7744            { # included by the compiler
7745                next;
7746            }
7747            if(get_depth($Dep)==1)
7748            { # too short
7749                next;
7750            }
7751            if(isLibcDir($Dep))
7752            { # do NOT include /usr/include/{sys,bits}
7753                next;
7754            }
7755            $IncDir{$Dep} = 1;
7756        }
7757    }
7758    $Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath} = sortIncPaths([keys(%IncDir)], $LibVersion);
7759    return @{$Cache{"get_HeaderDeps"}{$LibVersion}{$AbsPath}};
7760}
7761
7762sub sortIncPaths($$)
7763{
7764    my ($ArrRef, $LibVersion) = @_;
7765    if(not $ArrRef or $#{$ArrRef}<0) {
7766        return $ArrRef;
7767    }
7768    @{$ArrRef} = sort {$b cmp $a} @{$ArrRef};
7769    @{$ArrRef} = sort {get_depth($a)<=>get_depth($b)} @{$ArrRef};
7770    @{$ArrRef} = sort {sortDeps($b, $a, $LibVersion)} @{$ArrRef};
7771    return $ArrRef;
7772}
7773
7774sub sortDeps($$$)
7775{
7776    if($Header_Dependency{$_[2]}{$_[0]}
7777    and not $Header_Dependency{$_[2]}{$_[1]}) {
7778        return 1;
7779    }
7780    elsif(not $Header_Dependency{$_[2]}{$_[0]}
7781    and $Header_Dependency{$_[2]}{$_[1]}) {
7782        return -1;
7783    }
7784    return 0;
7785}
7786
7787sub join_P($$)
7788{
7789    my $S = "/";
7790    if($OSgroup eq "windows") {
7791        $S = "\\";
7792    }
7793    return join($S, @_);
7794}
7795
7796sub get_namespace_additions($)
7797{
7798    my $NameSpaces = $_[0];
7799    my ($Additions, $AddNameSpaceId) = ("", 1);
7800    foreach my $NS (sort {$a=~/_/ <=> $b=~/_/} sort {lc($a) cmp lc($b)} keys(%{$NameSpaces}))
7801    {
7802        next if($SkipNameSpaces{$Version}{$NS});
7803        next if(not $NS or $NameSpaces->{$NS}==-1);
7804        next if($NS=~/(\A|::)iterator(::|\Z)/i);
7805        next if($NS=~/\A__/i);
7806        next if(($NS=~/\Astd::/ or $NS=~/\A(std|tr1|rel_ops|fcntl)\Z/) and not $STDCXX_TESTING);
7807        $NestedNameSpaces{$Version}{$NS} = 1; # for future use in reports
7808        my ($TypeDecl_Prefix, $TypeDecl_Suffix) = ();
7809        my @NS_Parts = split(/::/, $NS);
7810        next if($#NS_Parts==-1);
7811        next if($NS_Parts[0]=~/\A(random|or)\Z/);
7812        foreach my $NS_Part (@NS_Parts)
7813        {
7814            $TypeDecl_Prefix .= "namespace $NS_Part\{";
7815            $TypeDecl_Suffix .= "}";
7816        }
7817        my $TypeDecl = $TypeDecl_Prefix."typedef int tmp_add_type_".$AddNameSpaceId.";".$TypeDecl_Suffix;
7818        my $FuncDecl = "$NS\:\:tmp_add_type_$AddNameSpaceId tmp_add_func_$AddNameSpaceId(){return 0;};";
7819        $Additions.="  $TypeDecl\n  $FuncDecl\n";
7820        $AddNameSpaceId+=1;
7821    }
7822    return $Additions;
7823}
7824
7825sub path_format($$)
7826{
7827    my ($Path, $Fmt) = @_;
7828    $Path=~s/[\/\\]+\.?\Z//g;
7829    if($Fmt eq "windows")
7830    {
7831        $Path=~s/\//\\/g;
7832        $Path=lc($Path);
7833    }
7834    else
7835    { # forward slash to pass into MinGW GCC
7836        $Path=~s/\\/\//g;
7837    }
7838    return $Path;
7839}
7840
7841sub inc_opt($$)
7842{
7843    my ($Path, $Style) = @_;
7844    if($Style eq "GCC")
7845    { # GCC options
7846        if($OSgroup eq "windows")
7847        { # to MinGW GCC
7848            return "-I\"".path_format($Path, "unix")."\"";
7849        }
7850        elsif($OSgroup eq "macos"
7851        and $Path=~/\.framework\Z/)
7852        { # to Apple's GCC
7853            return "-F".esc(get_dirname($Path));
7854        }
7855        else {
7856            return "-I".esc($Path);
7857        }
7858    }
7859    elsif($Style eq "CL") {
7860        return "/I \"".$Path."\"";
7861    }
7862    return "";
7863}
7864
7865sub platformSpecs($)
7866{
7867    my $LibVersion = $_[0];
7868    my $Arch = getArch($LibVersion);
7869    if($OStarget eq "symbian")
7870    { # options for GCCE compiler
7871        my %Symbian_Opts = map {$_=>1} (
7872            "-D__GCCE__",
7873            "-DUNICODE",
7874            "-fexceptions",
7875            "-D__SYMBIAN32__",
7876            "-D__MARM_INTERWORK__",
7877            "-D_UNICODE",
7878            "-D__S60_50__",
7879            "-D__S60_3X__",
7880            "-D__SERIES60_3X__",
7881            "-D__EPOC32__",
7882            "-D__MARM__",
7883            "-D__EABI__",
7884            "-D__MARM_ARMV5__",
7885            "-D__SUPPORT_CPP_EXCEPTIONS__",
7886            "-march=armv5t",
7887            "-mapcs",
7888            "-mthumb-interwork",
7889            "-DEKA2",
7890            "-DSYMBIAN_ENABLE_SPLIT_HEADERS"
7891        );
7892        return join(" ", keys(%Symbian_Opts));
7893    }
7894    elsif($OSgroup eq "windows"
7895    and get_dumpmachine($GCC_PATH)=~/mingw/i)
7896    { # add options to MinGW compiler
7897      # to simulate the MSVC compiler
7898        my %MinGW_Opts = map {$_=>1} (
7899            "-D_WIN32",
7900            "-D_STDCALL_SUPPORTED",
7901            "-D__int64=\"long long\"",
7902            "-D__int32=int",
7903            "-D__int16=short",
7904            "-D__int8=char",
7905            "-D__possibly_notnullterminated=\" \"",
7906            "-D__nullterminated=\" \"",
7907            "-D__nullnullterminated=\" \"",
7908            "-D__w64=\" \"",
7909            "-D__ptr32=\" \"",
7910            "-D__ptr64=\" \"",
7911            "-D__forceinline=inline",
7912            "-D__inline=inline",
7913            "-D__uuidof(x)=IID()",
7914            "-D__try=",
7915            "-D__except(x)=",
7916            "-D__declspec(x)=__attribute__((x))",
7917            "-D__pragma(x)=",
7918            "-D_inline=inline",
7919            "-D__forceinline=__inline",
7920            "-D__stdcall=__attribute__((__stdcall__))",
7921            "-D__cdecl=__attribute__((__cdecl__))",
7922            "-D__fastcall=__attribute__((__fastcall__))",
7923            "-D__thiscall=__attribute__((__thiscall__))",
7924            "-D_stdcall=__attribute__((__stdcall__))",
7925            "-D_cdecl=__attribute__((__cdecl__))",
7926            "-D_fastcall=__attribute__((__fastcall__))",
7927            "-D_thiscall=__attribute__((__thiscall__))",
7928            "-DSHSTDAPI_(x)=x",
7929            "-D_MSC_EXTENSIONS",
7930            "-DSECURITY_WIN32",
7931            "-D_MSC_VER=1500",
7932            "-D_USE_DECLSPECS_FOR_SAL",
7933            "-D__noop=\" \"",
7934            "-DDECLSPEC_DEPRECATED=\" \"",
7935            "-D__builtin_alignof(x)=__alignof__(x)",
7936            "-DSORTPP_PASS");
7937        if($Arch eq "x86") {
7938            $MinGW_Opts{"-D_M_IX86=300"}=1;
7939        }
7940        elsif($Arch eq "x86_64") {
7941            $MinGW_Opts{"-D_M_AMD64=300"}=1;
7942        }
7943        elsif($Arch eq "ia64") {
7944            $MinGW_Opts{"-D_M_IA64=300"}=1;
7945        }
7946        return join(" ", keys(%MinGW_Opts));
7947    }
7948    return "";
7949}
7950
7951my %C_Structure = map {$_=>1} (
7952# FIXME: Can't separate union and struct data types before dumping,
7953# so it sometimes cause compilation errors for unknown reason
7954# when trying to declare TYPE* tmp_add_class_N
7955# This is a list of such structures + list of other C structures
7956    "sigval",
7957    "sigevent",
7958    "sigaction",
7959    "sigvec",
7960    "sigstack",
7961    "timeval",
7962    "timezone",
7963    "rusage",
7964    "rlimit",
7965    "wait",
7966    "flock",
7967    "stat",
7968    "_stat",
7969    "stat32",
7970    "_stat32",
7971    "stat64",
7972    "_stat64",
7973    "_stati64",
7974    "if_nameindex",
7975    "usb_device",
7976    "sigaltstack",
7977    "sysinfo",
7978    "timeLocale",
7979    "tcp_debug",
7980    "rpc_createerr",
7981 # Other
7982    "timespec",
7983    "random_data",
7984    "drand48_data",
7985    "_IO_marker",
7986    "_IO_FILE",
7987    "lconv",
7988    "sched_param",
7989    "tm",
7990    "itimerspec",
7991    "_pthread_cleanup_buffer",
7992    "fd_set",
7993    "siginfo",
7994    "mallinfo",
7995    "timex",
7996    "sigcontext",
7997    "ucontext",
7998 # Mac
7999    "_timex",
8000    "_class_t",
8001    "_category_t",
8002    "_class_ro_t",
8003    "_protocol_t",
8004    "_message_ref_t",
8005    "_super_message_ref_t",
8006    "_ivar_t",
8007    "_ivar_list_t"
8008);
8009
8010sub getCompileCmd($$$)
8011{
8012    my ($Path, $Opt, $Inc) = @_;
8013    my $GccCall = $GCC_PATH;
8014    if($Opt) {
8015        $GccCall .= " ".$Opt;
8016    }
8017    $GccCall .= " -x ";
8018    if($OSgroup eq "macos") {
8019        $GccCall .= "objective-";
8020    }
8021
8022    if($EMERGENCY_MODE_48)
8023    { # workaround for GCC 4.8 (C only)
8024        $GccCall .= "c++";
8025    }
8026    elsif(check_gcc($GCC_PATH, "4"))
8027    { # compile as "C++" header
8028      # to obtain complete dump using GCC 4.0
8029        $GccCall .= "c++-header";
8030    }
8031    else
8032    { # compile as "C++" source
8033      # GCC 3.3 cannot compile headers
8034        $GccCall .= "c++";
8035    }
8036    if(my $Opts = platformSpecs($Version))
8037    { # platform-specific options
8038        $GccCall .= " ".$Opts;
8039    }
8040    # allow extra qualifications
8041    # and other nonconformant code
8042    $GccCall .= " -fpermissive";
8043    $GccCall .= " -w";
8044    if($NoStdInc)
8045    {
8046        $GccCall .= " -nostdinc";
8047        $GccCall .= " -nostdinc++";
8048    }
8049    if(my $Opts_GCC = getGCC_Opts($Version))
8050    { # user-defined options
8051        $GccCall .= " ".$Opts_GCC;
8052    }
8053    $GccCall .= " \"$Path\"";
8054    if($Inc)
8055    { # include paths
8056        $GccCall .= " ".$Inc;
8057    }
8058    return $GccCall;
8059}
8060
8061sub detectPreamble($$)
8062{
8063    my ($Content, $LibVersion) = @_;
8064    my %HeaderElems = (
8065        # Types
8066        "stdio.h" => ["FILE", "va_list"],
8067        "stddef.h" => ["NULL", "ptrdiff_t"],
8068        "stdint.h" => ["uint8_t", "uint16_t", "uint32_t", "uint64_t",
8069                       "int8_t", "int16_t", "int32_t", "int64_t"],
8070        "time.h" => ["time_t"],
8071        "sys/types.h" => ["ssize_t", "u_int32_t", "u_short", "u_char",
8072                          "u_int", "off_t", "u_quad_t", "u_long", "mode_t"],
8073        "unistd.h" => ["gid_t", "uid_t", "socklen_t"],
8074        "stdbool.h" => ["_Bool"],
8075        "rpc/xdr.h" => ["bool_t"],
8076        "in_systm.h" => ["n_long", "n_short"],
8077        # Fields
8078        "arpa/inet.h" => ["fw_src", "ip_src"],
8079        # Functions
8080        "stdlib.h" => ["free", "malloc", "size_t"],
8081        "string.h" => ["memmove", "strcmp"]
8082    );
8083    my %AutoPreamble = ();
8084    foreach (keys(%HeaderElems))
8085    {
8086        foreach my $Elem (@{$HeaderElems{$_}}) {
8087            $AutoPreamble{$Elem} = $_;
8088        }
8089    }
8090    my %Types = ();
8091    while($Content=~s/error\:\s*(field\s*|)\W+(.+?)\W+//)
8092    { # error: 'FILE' has not been declared
8093        $Types{$2} = 1;
8094    }
8095    if(keys(%Types))
8096    {
8097        my %AddHeaders = ();
8098        foreach my $Type (keys(%Types))
8099        {
8100            if(my $Header = $AutoPreamble{$Type})
8101            {
8102                if(my $Path = identifyHeader($Header, $LibVersion))
8103                {
8104                    if(skipHeader($Path, $LibVersion)) {
8105                        next;
8106                    }
8107                    $Path = path_format($Path, $OSgroup);
8108                    $AddHeaders{$Path}{"Type"} = $Type;
8109                    $AddHeaders{$Path}{"Header"} = $Header;
8110                }
8111            }
8112        }
8113        if(keys(%AddHeaders)) {
8114            return \%AddHeaders;
8115        }
8116    }
8117    return undef;
8118}
8119
8120sub checkCTags($)
8121{
8122    my $Path = $_[0];
8123    if(not $Path) {
8124        return;
8125    }
8126    my $CTags = undef;
8127
8128    if($OSgroup eq "bsd")
8129    { # use ectags on BSD
8130        $CTags = get_CmdPath("ectags");
8131        if(not $CTags) {
8132            printMsg("WARNING", "can't find \'ectags\' program");
8133        }
8134    }
8135    if(not $CTags) {
8136        $CTags = get_CmdPath("ctags");
8137    }
8138    if(not $CTags)
8139    {
8140        printMsg("WARNING", "can't find \'ctags\' program");
8141        return;
8142    }
8143
8144    if($OSgroup ne "linux")
8145    { # macos, freebsd, etc.
8146        my $Info = `$CTags --version 2>\"$TMP_DIR/null\"`;
8147        if($Info!~/exuberant/i)
8148        {
8149            printMsg("WARNING", "incompatible version of \'ctags\' program");
8150            return;
8151        }
8152    }
8153
8154    my $Out = $TMP_DIR."/ctags.txt";
8155    system("$CTags --c-kinds=pxn -f \"$Out\" \"$Path\" 2>\"$TMP_DIR/null\"");
8156    if($Debug) {
8157        copy($Out, $DEBUG_PATH{$Version}."/ctags.txt");
8158    }
8159    open(CTAGS, "<", $Out);
8160    while(my $Line = <CTAGS>)
8161    {
8162        chomp($Line);
8163        my ($Name, $Header, $Def, $Type, $Scpe) = split(/\t/, $Line);
8164        if(defined $Intrinsic_Keywords{$Name})
8165        { # noise
8166            next;
8167        }
8168        if($Type eq "n")
8169        {
8170            if(index($Scpe, "class:")==0) {
8171                next;
8172            }
8173            if(index($Scpe, "struct:")==0) {
8174                next;
8175            }
8176            if(index($Scpe, "namespace:")==0)
8177            {
8178                if($Scpe=~s/\Anamespace://) {
8179                    $Name = $Scpe."::".$Name;
8180                }
8181            }
8182            $TUnit_NameSpaces{$Version}{$Name} = 1;
8183        }
8184        elsif($Type eq "p")
8185        {
8186            if(not $Scpe or index($Scpe, "namespace:")==0) {
8187                $TUnit_Funcs{$Version}{$Name} = 1;
8188            }
8189        }
8190        elsif($Type eq "x")
8191        {
8192            if(not $Scpe or index($Scpe, "namespace:")==0) {
8193                $TUnit_Vars{$Version}{$Name} = 1;
8194            }
8195        }
8196    }
8197    close(CTAGS);
8198}
8199
8200sub preChange($$)
8201{
8202    my ($HeaderPath, $IncStr) = @_;
8203
8204    my $PreprocessCmd = getCompileCmd($HeaderPath, "-E", $IncStr);
8205    my $Content = undef;
8206
8207    if($OStarget eq "windows"
8208    and get_dumpmachine($GCC_PATH)=~/mingw/i
8209    and $MinGWMode{$Version}!=-1)
8210    { # modify headers to compile by MinGW
8211        if(not $Content)
8212        { # preprocessing
8213            $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`;
8214        }
8215        if($Content=~s/__asm\s*(\{[^{}]*?\}|[^{};]*)//g)
8216        { # __asm { ... }
8217            $MinGWMode{$Version}=1;
8218        }
8219        if($Content=~s/\s+(\/ \/.*?)\n/\n/g)
8220        { # comments after preprocessing
8221            $MinGWMode{$Version}=1;
8222        }
8223        if($Content=~s/(\W)(0x[a-f]+|\d+)(i|ui)(8|16|32|64)(\W)/$1$2$5/g)
8224        { # 0xffui8
8225            $MinGWMode{$Version}=1;
8226        }
8227
8228        if($MinGWMode{$Version}) {
8229            printMsg("INFO", "Using MinGW compatibility mode");
8230        }
8231    }
8232
8233    if(($COMMON_LANGUAGE{$Version} eq "C" or $CheckHeadersOnly)
8234    and $CppMode{$Version}!=-1 and not $CppCompat and not $CPP_HEADERS)
8235    { # rename C++ keywords in C code
8236      # disable this code by -cpp-compatible option
8237        if(not $Content)
8238        { # preprocessing
8239            $Content = `$PreprocessCmd 2>\"$TMP_DIR/null\"`;
8240        }
8241        my $RegExp_C = join("|", keys(%CppKeywords_C));
8242        my $RegExp_F = join("|", keys(%CppKeywords_F));
8243        my $RegExp_O = join("|", keys(%CppKeywords_O));
8244
8245        my $Detected = undef;
8246
8247        while($Content=~s/(\A|\n[^\#\/\n][^\n]*?|\n)(\*\s*|\s+|\@|\,|\()($RegExp_C|$RegExp_F)(\s*([\,\)\;\.\[]|\-\>|\:\s*\d))/$1$2c99_$3$4/g)
8248        { # MATCH:
8249          # int foo(int new, int class, int (*new)(int));
8250          # int foo(char template[], char*);
8251          # unsigned private: 8;
8252          # DO NOT MATCH:
8253          # #pragma GCC visibility push(default)
8254            $CppMode{$Version} = 1;
8255            $Detected = "$1$2$3$4" if(not defined $Detected);
8256        }
8257        if($Content=~s/([^\w\s]|\w\s+)(?<!operator )(delete)(\s*\()/$1c99_$2$3/g)
8258        { # MATCH:
8259          # int delete(...);
8260          # int explicit(...);
8261          # DO NOT MATCH:
8262          # void operator delete(...)
8263            $CppMode{$Version} = 1;
8264            $Detected = "$1$2$3" if(not defined $Detected);
8265        }
8266        if($Content=~s/(\s+)($RegExp_O)(\s*(\;|\:))/$1c99_$2$3/g)
8267        { # MATCH:
8268          # int bool;
8269          # DO NOT MATCH:
8270          # bool X;
8271          # return *this;
8272          # throw;
8273            $CppMode{$Version} = 1;
8274            $Detected = "$1$2$3" if(not defined $Detected);
8275        }
8276        if($Content=~s/(\s+)(operator)(\s*(\(\s*\)\s*[^\(\s]|\(\s*[^\)\s]))/$1c99_$2$3/g)
8277        { # MATCH:
8278          # int operator(...);
8279          # DO NOT MATCH:
8280          # int operator()(...);
8281            $CppMode{$Version} = 1;
8282            $Detected = "$1$2$3" if(not defined $Detected);
8283        }
8284        if($Content=~s/([^\w\(\,\s]\s*|\s+)(operator)(\s*(\,\s*[^\(\s]|\)))/$1c99_$2$3/g)
8285        { # MATCH:
8286          # int foo(int operator);
8287          # int foo(int operator, int other);
8288          # DO NOT MATCH:
8289          # int operator,(...);
8290            $CppMode{$Version} = 1;
8291            $Detected = "$1$2$3" if(not defined $Detected);
8292        }
8293        if($Content=~s/(\*\s*|\w\s+)(bool)(\s*(\,|\)))/$1c99_$2$3/g)
8294        { # MATCH:
8295          # int foo(gboolean *bool);
8296          # DO NOT MATCH:
8297          # void setTabEnabled(int index, bool);
8298            $CppMode{$Version} = 1;
8299            $Detected = "$1$2$3" if(not defined $Detected);
8300        }
8301        if($Content=~s/(\w)(\s*[^\w\(\,\s]\s*|\s+)(this|throw)(\s*[\,\)])/$1$2c99_$3$4/g)
8302        { # MATCH:
8303          # int foo(int* this);
8304          # int bar(int this);
8305          # int baz(int throw);
8306          # DO NOT MATCH:
8307          # foo(X, this);
8308            $CppMode{$Version} = 1;
8309            $Detected = "$1$2$3$4" if(not defined $Detected);
8310        }
8311        if($Content=~s/(struct |extern )(template) /$1c99_$2 /g)
8312        { # MATCH:
8313          # struct template {...};
8314          # extern template foo(...);
8315            $CppMode{$Version} = 1;
8316            $Detected = "$1$2" if(not defined $Detected);
8317        }
8318
8319        if($CppMode{$Version} == 1)
8320        {
8321            if($Debug)
8322            {
8323                $Detected=~s/\A\s+//g;
8324                printMsg("INFO", "Detected code: \"$Detected\"");
8325            }
8326        }
8327
8328        # remove typedef enum NAME NAME;
8329        my @FwdTypedefs = $Content=~/typedef\s+enum\s+(\w+)\s+(\w+);/g;
8330        my $N = 0;
8331        while($N<=$#FwdTypedefs-1)
8332        {
8333            my $S = $FwdTypedefs[$N];
8334            if($S eq $FwdTypedefs[$N+1])
8335            {
8336                $Content=~s/typedef\s+enum\s+\Q$S\E\s+\Q$S\E;//g;
8337                $CppMode{$Version} = 1;
8338
8339                if($Debug) {
8340                    printMsg("INFO", "Detected code: \"typedef enum $S $S;\"");
8341                }
8342            }
8343            $N+=2;
8344        }
8345
8346        if($CppMode{$Version}==1) {
8347            printMsg("INFO", "Using C++ compatibility mode");
8348        }
8349    }
8350
8351    if($CppMode{$Version}==1
8352    or $MinGWMode{$Version}==1)
8353    {
8354        my $IPath = $TMP_DIR."/dump$Version.i";
8355        writeFile($IPath, $Content);
8356        return $IPath;
8357    }
8358
8359    return undef;
8360}
8361
8362sub getDump()
8363{
8364    if(not $GCC_PATH) {
8365        exitStatus("Error", "internal error - GCC path is not set");
8366    }
8367
8368    my @Headers = keys(%{$Registered_Headers{$Version}});
8369    @Headers = sort {int($Registered_Headers{$Version}{$a}{"Pos"})<=>int($Registered_Headers{$Version}{$b}{"Pos"})} @Headers;
8370
8371    my $IncludeString = getIncString(getIncPaths(@{$Include_Preamble{$Version}}, @Headers), "GCC");
8372
8373    my $TmpHeaderPath = $TMP_DIR."/dump".$Version.".h";
8374    my $HeaderPath = $TmpHeaderPath;
8375
8376    # write tmp-header
8377    open(TMP_HEADER, ">", $TmpHeaderPath) || die ("can't open file \'$TmpHeaderPath\': $!\n");
8378    if(my $AddDefines = $Descriptor{$Version}{"Defines"})
8379    {
8380        $AddDefines=~s/\n\s+/\n  /g;
8381        print TMP_HEADER "\n  // add defines\n  ".$AddDefines."\n";
8382    }
8383    print TMP_HEADER "\n  // add includes\n";
8384    foreach my $HPath (@{$Include_Preamble{$Version}}) {
8385        print TMP_HEADER "  #include \"".path_format($HPath, "unix")."\"\n";
8386    }
8387    foreach my $HPath (@Headers)
8388    {
8389        if(not grep {$HPath eq $_} (@{$Include_Preamble{$Version}})) {
8390            print TMP_HEADER "  #include \"".path_format($HPath, "unix")."\"\n";
8391        }
8392    }
8393    close(TMP_HEADER);
8394
8395    if($ExtraInfo)
8396    { # extra information for other tools
8397        if($IncludeString) {
8398            writeFile($ExtraInfo."/include-string", $IncludeString);
8399        }
8400        writeFile($ExtraInfo."/recursive-includes", Dumper($RecursiveIncludes{$Version}));
8401        writeFile($ExtraInfo."/direct-includes", Dumper($Header_Includes{$Version}));
8402
8403        if(my @Redirects = keys(%{$Header_ErrorRedirect{$Version}}))
8404        {
8405            my $REDIR = "";
8406            foreach my $P1 (sort @Redirects) {
8407                $REDIR .= $P1.";".$Header_ErrorRedirect{$Version}{$P1}."\n";
8408            }
8409            writeFile($ExtraInfo."/include-redirect", $REDIR);
8410        }
8411    }
8412
8413    if(not keys(%{$TargetHeaders{$Version}}))
8414    { # Target headers
8415        addTargetHeaders($Version);
8416    }
8417
8418    # clean memory
8419    %RecursiveIncludes = ();
8420    %Header_Include_Prefix = ();
8421    %Header_Includes = ();
8422
8423    # clean cache
8424    delete($Cache{"identifyHeader"});
8425    delete($Cache{"detect_header_includes"});
8426    delete($Cache{"selectSystemHeader"});
8427
8428    # preprocessing stage
8429    my $Pre = callPreprocessor($TmpHeaderPath, $IncludeString, $Version);
8430    checkPreprocessedUnit($Pre);
8431
8432    if($ExtraInfo)
8433    { # extra information for other tools
8434        writeFile($ExtraInfo."/header-paths", join("\n", sort keys(%{$PreprocessedHeaders{$Version}})));
8435    }
8436
8437    # clean memory
8438    delete($Include_Neighbors{$Version});
8439    delete($PreprocessedHeaders{$Version});
8440
8441    if($COMMON_LANGUAGE{$Version} eq "C++") {
8442        checkCTags($Pre);
8443    }
8444
8445    if(my $PrePath = preChange($TmpHeaderPath, $IncludeString))
8446    { # try to correct the preprocessor output
8447        $HeaderPath = $PrePath;
8448    }
8449
8450    if($COMMON_LANGUAGE{$Version} eq "C++")
8451    { # add classes and namespaces to the dump
8452        my $CHdump = "-fdump-class-hierarchy -c";
8453        if($CppMode{$Version}==1
8454        or $MinGWMode{$Version}==1) {
8455            $CHdump .= " -fpreprocessed";
8456        }
8457        my $ClassHierarchyCmd = getCompileCmd($HeaderPath, $CHdump, $IncludeString);
8458        chdir($TMP_DIR);
8459        system($ClassHierarchyCmd." >null 2>&1");
8460        chdir($ORIG_DIR);
8461        if(my $ClassDump = (cmd_find($TMP_DIR,"f","*.class",1))[0])
8462        {
8463            my $Content = readFile($ClassDump);
8464            foreach my $ClassInfo (split(/\n\n/, $Content))
8465            {
8466                if($ClassInfo=~/\AClass\s+(.+)\s*/i)
8467                {
8468                    my $CName = $1;
8469                    next if($CName=~/\A(__|_objc_|_opaque_)/);
8470                    $TUnit_NameSpaces{$Version}{$CName} = -1;
8471                    if($CName=~/\A[\w:]+\Z/)
8472                    { # classes
8473                        $TUnit_Classes{$Version}{$CName} = 1;
8474                    }
8475                    if($CName=~/(\w[\w:]*)::/)
8476                    { # namespaces
8477                        my $NS = $1;
8478                        if(not defined $TUnit_NameSpaces{$Version}{$NS}) {
8479                            $TUnit_NameSpaces{$Version}{$NS} = 1;
8480                        }
8481                    }
8482                }
8483                elsif($ClassInfo=~/\AVtable\s+for\s+(.+)\n((.|\n)+)\Z/i)
8484                { # read v-tables (advanced approach)
8485                    my ($CName, $VTable) = ($1, $2);
8486                    $ClassVTable_Content{$Version}{$CName} = $VTable;
8487                }
8488            }
8489            foreach my $NS (keys(%{$AddNameSpaces{$Version}}))
8490            { # add user-defined namespaces
8491                $TUnit_NameSpaces{$Version}{$NS} = 1;
8492            }
8493            if($Debug)
8494            { # debug mode
8495                mkpath($DEBUG_PATH{$Version});
8496                copy($ClassDump, $DEBUG_PATH{$Version}."/class-hierarchy-dump.txt");
8497            }
8498            unlink($ClassDump);
8499        }
8500
8501        # add namespaces and classes
8502        if(my $NS_Add = get_namespace_additions($TUnit_NameSpaces{$Version}))
8503        { # GCC on all supported platforms does not include namespaces to the dump by default
8504            appendFile($HeaderPath, "\n  // add namespaces\n".$NS_Add);
8505        }
8506        # some GCC versions don't include class methods to the TU dump by default
8507        my ($AddClass, $ClassNum) = ("", 0);
8508        my $GCC_44 = check_gcc($GCC_PATH, "4.4"); # support for old GCC versions
8509        foreach my $CName (sort keys(%{$TUnit_Classes{$Version}}))
8510        {
8511            next if($C_Structure{$CName});
8512            next if(not $STDCXX_TESTING and $CName=~/\Astd::/);
8513            next if($SkipTypes{$Version}{$CName});
8514            if(not $Force and $GCC_44
8515            and $OSgroup eq "linux")
8516            { # optimization for linux with GCC >= 4.4
8517              # disable this code by -force option
8518                if(index($CName, "::")!=-1)
8519                { # should be added by name space
8520                    next;
8521                }
8522            }
8523            else
8524            {
8525                if($CName=~/\A(.+)::[^:]+\Z/
8526                and $TUnit_Classes{$Version}{$1})
8527                { # classes inside other classes
8528                    next;
8529                }
8530            }
8531            if(defined $TUnit_Funcs{$Version}{$CName})
8532            { # the same name for a function and type
8533                next;
8534            }
8535            if(defined $TUnit_Vars{$Version}{$CName})
8536            { # the same name for a variable and type
8537                next;
8538            }
8539            $AddClass .= "  $CName* tmp_add_class_".($ClassNum++).";\n";
8540        }
8541        if($AddClass) {
8542            appendFile($HeaderPath, "\n  // add classes\n".$AddClass);
8543        }
8544    }
8545    writeLog($Version, "Temporary header file \'$TmpHeaderPath\' with the following content will be compiled to create GCC translation unit dump:\n".readFile($TmpHeaderPath)."\n");
8546    # create TU dump
8547    my $TUdump = "-fdump-translation-unit -fkeep-inline-functions -c";
8548    if($UserLang eq "C") {
8549        $TUdump .= " -U__cplusplus -D_Bool=\"bool\"";
8550    }
8551    if($CppMode{$Version}==1
8552    or $MinGWMode{$Version}==1) {
8553        $TUdump .= " -fpreprocessed";
8554    }
8555    my $SyntaxTreeCmd = getCompileCmd($HeaderPath, $TUdump, $IncludeString);
8556    writeLog($Version, "The GCC parameters:\n  $SyntaxTreeCmd\n\n");
8557    chdir($TMP_DIR);
8558    system($SyntaxTreeCmd." >\"$TMP_DIR/tu_errors\" 2>&1");
8559    my $Errors = "";
8560    if($?)
8561    { # failed to compile, but the TU dump still can be created
8562        if($Errors = readFile($TMP_DIR."/tu_errors"))
8563        { # try to recompile
8564          # FIXME: handle other errors and try to recompile
8565            if($CppMode{$Version}==1
8566            and index($Errors, "c99_")!=-1
8567            and not defined $CppIncompat)
8568            { # disable c99 mode and try again
8569                $CppMode{$Version}=-1;
8570
8571                if($Debug)
8572                {
8573                    # printMsg("INFO", $Errors);
8574                }
8575
8576                printMsg("INFO", "Disabling C++ compatibility mode");
8577                resetLogging($Version);
8578                $TMP_DIR = tempdir(CLEANUP=>1);
8579                return getDump();
8580            }
8581            elsif($AutoPreambleMode{$Version}!=-1
8582            and my $AddHeaders = detectPreamble($Errors, $Version))
8583            { # add auto preamble headers and try again
8584                $AutoPreambleMode{$Version}=-1;
8585                my @Headers = sort {$b cmp $a} keys(%{$AddHeaders}); # sys/types.h should be the first
8586                foreach my $Num (0 .. $#Headers)
8587                {
8588                    my $Path = $Headers[$Num];
8589                    if(not grep {$Path eq $_} (@{$Include_Preamble{$Version}}))
8590                    {
8591                        push_U($Include_Preamble{$Version}, $Path);
8592                        printMsg("INFO", "Add \'".$AddHeaders->{$Path}{"Header"}."\' preamble header for \'".$AddHeaders->{$Path}{"Type"}."\'");
8593                    }
8594                }
8595                resetLogging($Version);
8596                $TMP_DIR = tempdir(CLEANUP=>1);
8597                return getDump();
8598            }
8599            elsif($Cpp0xMode{$Version}!=-1
8600            and ($Errors=~/\Q-std=c++0x\E/
8601            or $Errors=~/is not a class or namespace/))
8602            { # c++0x: enum class
8603                if(check_gcc($GCC_PATH, "4.6"))
8604                {
8605                    $Cpp0xMode{$Version}=-1;
8606                    printMsg("INFO", "Enabling c++0x mode");
8607                    resetLogging($Version);
8608                    $TMP_DIR = tempdir(CLEANUP=>1);
8609                    $CompilerOptions{$Version} .= " -std=c++0x";
8610                    return getDump();
8611                }
8612                else {
8613                    printMsg("WARNING", "Probably c++0x construction detected");
8614                }
8615
8616            }
8617            elsif($MinGWMode{$Version}==1)
8618            { # disable MinGW mode and try again
8619                $MinGWMode{$Version}=-1;
8620                resetLogging($Version);
8621                $TMP_DIR = tempdir(CLEANUP=>1);
8622                return getDump();
8623            }
8624            writeLog($Version, $Errors);
8625        }
8626        else {
8627            writeLog($Version, "$!: $?\n");
8628        }
8629        printMsg("ERROR", "some errors occurred when compiling headers");
8630        printErrorLog($Version);
8631        $COMPILE_ERRORS = $ERROR_CODE{"Compile_Error"};
8632        writeLog($Version, "\n"); # new line
8633    }
8634    chdir($ORIG_DIR);
8635    unlink($TmpHeaderPath);
8636    unlink($HeaderPath);
8637
8638    if(my @TUs = cmd_find($TMP_DIR,"f","*.tu",1)) {
8639        return $TUs[0];
8640    }
8641    else
8642    {
8643        my $Msg = "can't compile header(s)";
8644        if($Errors=~/error trying to exec \W+cc1plus\W+/) {
8645            $Msg .= "\nDid you install G++?";
8646        }
8647        exitStatus("Cannot_Compile", $Msg);
8648    }
8649}
8650
8651sub cmd_file($)
8652{
8653    my $Path = $_[0];
8654    return "" if(not $Path or not -e $Path);
8655    if(my $CmdPath = get_CmdPath("file")) {
8656        return `$CmdPath -b \"$Path\"`;
8657    }
8658    return "";
8659}
8660
8661sub getIncString($$)
8662{
8663    my ($ArrRef, $Style) = @_;
8664    return "" if(not $ArrRef or $#{$ArrRef}<0);
8665    my $String = "";
8666    foreach (@{$ArrRef}) {
8667        $String .= " ".inc_opt($_, $Style);
8668    }
8669    return $String;
8670}
8671
8672sub getIncPaths(@)
8673{
8674    my @HeaderPaths = @_;
8675    my @IncPaths = @{$Add_Include_Paths{$Version}};
8676    if($INC_PATH_AUTODETECT{$Version})
8677    { # auto-detecting dependencies
8678        my %Includes = ();
8679        foreach my $HPath (@HeaderPaths)
8680        {
8681            foreach my $Dir (get_HeaderDeps($HPath, $Version))
8682            {
8683                if($Skip_Include_Paths{$Version}{$Dir}) {
8684                    next;
8685                }
8686                if($SystemRoot)
8687                {
8688                    if($Skip_Include_Paths{$Version}{$SystemRoot.$Dir}) {
8689                        next;
8690                    }
8691                }
8692                $Includes{$Dir} = 1;
8693            }
8694        }
8695        foreach my $Dir (@{sortIncPaths([keys(%Includes)], $Version)}) {
8696            push_U(\@IncPaths, $Dir);
8697        }
8698    }
8699    else
8700    { # user-defined paths
8701        @IncPaths = @{$Include_Paths{$Version}};
8702    }
8703    return \@IncPaths;
8704}
8705
8706sub push_U($@)
8707{ # push unique
8708    if(my $Array = shift @_)
8709    {
8710        if(@_)
8711        {
8712            my %Exist = map {$_=>1} @{$Array};
8713            foreach my $Elem (@_)
8714            {
8715                if(not defined $Exist{$Elem})
8716                {
8717                    push(@{$Array}, $Elem);
8718                    $Exist{$Elem} = 1;
8719                }
8720            }
8721        }
8722    }
8723}
8724
8725sub callPreprocessor($$$)
8726{
8727    my ($Path, $Inc, $LibVersion) = @_;
8728    return "" if(not $Path or not -f $Path);
8729    my $IncludeString=$Inc;
8730    if(not $Inc) {
8731        $IncludeString = getIncString(getIncPaths($Path), "GCC");
8732    }
8733    my $Cmd = getCompileCmd($Path, "-dD -E", $IncludeString);
8734    my $Out = $TMP_DIR."/preprocessed.h";
8735    system($Cmd." >\"$Out\" 2>\"$TMP_DIR/null\"");
8736    return $Out;
8737}
8738
8739sub cmd_find($;$$$$)
8740{ # native "find" is much faster than File::Find (~6x)
8741  # also the File::Find doesn't support --maxdepth N option
8742  # so using the cross-platform wrapper for the native one
8743    my ($Path, $Type, $Name, $MaxDepth, $UseRegex) = @_;
8744    return () if(not $Path or not -e $Path);
8745    if($OSgroup eq "windows")
8746    {
8747        my $DirCmd = get_CmdPath("dir");
8748        if(not $DirCmd) {
8749            exitStatus("Not_Found", "can't find \"dir\" command");
8750        }
8751        $Path = get_abs_path($Path);
8752        $Path = path_format($Path, $OSgroup);
8753        my $Cmd = $DirCmd." \"$Path\" /B /O";
8754        if($MaxDepth!=1) {
8755            $Cmd .= " /S";
8756        }
8757        if($Type eq "d") {
8758            $Cmd .= " /AD";
8759        }
8760        elsif($Type eq "f") {
8761            $Cmd .= " /A-D";
8762        }
8763        my @Files = split(/\n/, `$Cmd 2>\"$TMP_DIR/null\"`);
8764        if($Name)
8765        {
8766            if(not $UseRegex)
8767            { # FIXME: how to search file names in MS shell?
8768              # wildcard to regexp
8769                $Name=~s/\*/.*/g;
8770                $Name='\A'.$Name.'\Z';
8771            }
8772            @Files = grep { /$Name/i } @Files;
8773        }
8774        my @AbsPaths = ();
8775        foreach my $File (@Files)
8776        {
8777            if(not is_abs($File)) {
8778                $File = join_P($Path, $File);
8779            }
8780            if($Type eq "f" and not -f $File)
8781            { # skip dirs
8782                next;
8783            }
8784            push(@AbsPaths, path_format($File, $OSgroup));
8785        }
8786        if($Type eq "d") {
8787            push(@AbsPaths, $Path);
8788        }
8789        return @AbsPaths;
8790    }
8791    else
8792    {
8793        my $FindCmd = get_CmdPath("find");
8794        if(not $FindCmd) {
8795            exitStatus("Not_Found", "can't find a \"find\" command");
8796        }
8797        $Path = get_abs_path($Path);
8798        if(-d $Path and -l $Path
8799        and $Path!~/\/\Z/)
8800        { # for directories that are symlinks
8801            $Path.="/";
8802        }
8803        my $Cmd = $FindCmd." \"$Path\"";
8804        if($MaxDepth) {
8805            $Cmd .= " -maxdepth $MaxDepth";
8806        }
8807        if($Type) {
8808            $Cmd .= " -type $Type";
8809        }
8810        if($Name and not $UseRegex)
8811        { # wildcards
8812            $Cmd .= " -name \"$Name\"";
8813        }
8814        my $Res = `$Cmd 2>\"$TMP_DIR/null\"`;
8815        if($? and $!) {
8816            printMsg("ERROR", "problem with \'find\' utility ($?): $!");
8817        }
8818        my @Files = split(/\n/, $Res);
8819        if($Name and $UseRegex)
8820        { # regex
8821            @Files = grep { /$Name/ } @Files;
8822        }
8823        return @Files;
8824    }
8825}
8826
8827sub unpackDump($)
8828{
8829    my $Path = $_[0];
8830    return "" if(not $Path or not -e $Path);
8831    $Path = get_abs_path($Path);
8832    $Path = path_format($Path, $OSgroup);
8833    my ($Dir, $FileName) = separate_path($Path);
8834    my $UnpackDir = $TMP_DIR."/unpack";
8835    rmtree($UnpackDir);
8836    mkpath($UnpackDir);
8837    if($FileName=~s/\Q.zip\E\Z//g)
8838    { # *.zip
8839        my $UnzipCmd = get_CmdPath("unzip");
8840        if(not $UnzipCmd) {
8841            exitStatus("Not_Found", "can't find \"unzip\" command");
8842        }
8843        chdir($UnpackDir);
8844        system("$UnzipCmd \"$Path\" >\"$TMP_DIR/null\"");
8845        if($?) {
8846            exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8847        }
8848        chdir($ORIG_DIR);
8849        my @Contents = cmd_find($UnpackDir, "f");
8850        if(not @Contents) {
8851            exitStatus("Error", "can't extract \'$Path\'");
8852        }
8853        return $Contents[0];
8854    }
8855    elsif($FileName=~s/\Q.tar.gz\E(\.\w+|)\Z//g)
8856    { # *.tar.gz
8857      # *.tar.gz.amd64 (dh & cdbs)
8858        if($OSgroup eq "windows")
8859        { # -xvzf option is not implemented in tar.exe (2003)
8860          # use "gzip.exe -k -d -f" + "tar.exe -xvf" instead
8861            my $TarCmd = get_CmdPath("tar");
8862            if(not $TarCmd) {
8863                exitStatus("Not_Found", "can't find \"tar\" command");
8864            }
8865            my $GzipCmd = get_CmdPath("gzip");
8866            if(not $GzipCmd) {
8867                exitStatus("Not_Found", "can't find \"gzip\" command");
8868            }
8869            chdir($UnpackDir);
8870            system("$GzipCmd -k -d -f \"$Path\""); # keep input files (-k)
8871            if($?) {
8872                exitStatus("Error", "can't extract \'$Path\'");
8873            }
8874            system("$TarCmd -xvf \"$Dir\\$FileName.tar\" >\"$TMP_DIR/null\"");
8875            if($?) {
8876                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8877            }
8878            chdir($ORIG_DIR);
8879            unlink($Dir."/".$FileName.".tar");
8880            my @Contents = cmd_find($UnpackDir, "f");
8881            if(not @Contents) {
8882                exitStatus("Error", "can't extract \'$Path\'");
8883            }
8884            return $Contents[0];
8885        }
8886        else
8887        { # Unix, Mac
8888            my $TarCmd = get_CmdPath("tar");
8889            if(not $TarCmd) {
8890                exitStatus("Not_Found", "can't find \"tar\" command");
8891            }
8892            chdir($UnpackDir);
8893            system("$TarCmd -xvzf \"$Path\" >\"$TMP_DIR/null\"");
8894            if($?) {
8895                exitStatus("Error", "can't extract \'$Path\' ($?): $!");
8896            }
8897            chdir($ORIG_DIR);
8898            my @Contents = cmd_find($UnpackDir, "f");
8899            if(not @Contents) {
8900                exitStatus("Error", "can't extract \'$Path\'");
8901            }
8902            return $Contents[0];
8903        }
8904    }
8905}
8906
8907sub createArchive($$)
8908{
8909    my ($Path, $To) = @_;
8910    if(not $To) {
8911        $To = ".";
8912    }
8913    if(not $Path or not -e $Path
8914    or not -d $To) {
8915        return "";
8916    }
8917    my ($From, $Name) = separate_path($Path);
8918    if($OSgroup eq "windows")
8919    { # *.zip
8920        my $ZipCmd = get_CmdPath("zip");
8921        if(not $ZipCmd) {
8922            exitStatus("Not_Found", "can't find \"zip\"");
8923        }
8924        my $Pkg = $To."/".$Name.".zip";
8925        unlink($Pkg);
8926        chdir($To);
8927        system("$ZipCmd -j \"$Name.zip\" \"$Path\" >\"$TMP_DIR/null\"");
8928        if($?)
8929        { # cannot allocate memory (or other problems with "zip")
8930            unlink($Path);
8931            exitStatus("Error", "can't pack the ABI dump: ".$!);
8932        }
8933        chdir($ORIG_DIR);
8934        unlink($Path);
8935        return $Pkg;
8936    }
8937    else
8938    { # *.tar.gz
8939        my $TarCmd = get_CmdPath("tar");
8940        if(not $TarCmd) {
8941            exitStatus("Not_Found", "can't find \"tar\"");
8942        }
8943        my $GzipCmd = get_CmdPath("gzip");
8944        if(not $GzipCmd) {
8945            exitStatus("Not_Found", "can't find \"gzip\"");
8946        }
8947        my $Pkg = abs_path($To)."/".$Name.".tar.gz";
8948        unlink($Pkg);
8949        chdir($From);
8950        system($TarCmd, "-czf", $Pkg, $Name);
8951        if($?)
8952        { # cannot allocate memory (or other problems with "tar")
8953            unlink($Path);
8954            exitStatus("Error", "can't pack the ABI dump: ".$!);
8955        }
8956        chdir($ORIG_DIR);
8957        unlink($Path);
8958        return $To."/".$Name.".tar.gz";
8959    }
8960}
8961
8962sub is_header_file($)
8963{
8964    if($_[0]=~/\.($HEADER_EXT)\Z/i) {
8965        return $_[0];
8966    }
8967    return 0;
8968}
8969
8970sub is_not_header($)
8971{
8972    if($_[0]=~/\.\w+\Z/
8973    and $_[0]!~/\.($HEADER_EXT)\Z/i) {
8974        return 1;
8975    }
8976    return 0;
8977}
8978
8979sub is_header($$$)
8980{
8981    my ($Header, $UserDefined, $LibVersion) = @_;
8982    return 0 if(-d $Header);
8983    if(-f $Header) {
8984        $Header = get_abs_path($Header);
8985    }
8986    else
8987    {
8988        if(is_abs($Header))
8989        { # incorrect absolute path
8990            return 0;
8991        }
8992        if(my $HPath = identifyHeader($Header, $LibVersion)) {
8993            $Header = $HPath;
8994        }
8995        else
8996        { # can't find header
8997            return 0;
8998        }
8999    }
9000    if($Header=~/\.\w+\Z/)
9001    { # have an extension
9002        return is_header_file($Header);
9003    }
9004    else
9005    {
9006        if($UserDefined==2)
9007        { # specified on the command line
9008            if(cmd_file($Header)!~/HTML|XML/i) {
9009                return $Header;
9010            }
9011        }
9012        elsif($UserDefined)
9013        { # specified in the XML-descriptor
9014          # header file without an extension
9015            return $Header;
9016        }
9017        else
9018        {
9019            if(index($Header, "/include/")!=-1
9020            or cmd_file($Header)=~/C[\+]*\s+program/i)
9021            { # !~/HTML|XML|shared|dynamic/i
9022                return $Header;
9023            }
9024        }
9025    }
9026    return 0;
9027}
9028
9029sub addTargetHeaders($)
9030{
9031    my $LibVersion = $_[0];
9032    foreach my $RegHeader (keys(%{$Registered_Headers{$LibVersion}}))
9033    {
9034        my $RegDir = get_dirname($RegHeader);
9035        $TargetHeaders{$LibVersion}{get_filename($RegHeader)} = 1;
9036
9037        if(not $INC_PATH_AUTODETECT{$LibVersion}) {
9038            detect_recursive_includes($RegHeader, $LibVersion);
9039        }
9040
9041        foreach my $RecInc (keys(%{$RecursiveIncludes{$LibVersion}{$RegHeader}}))
9042        {
9043            my $Dir = get_dirname($RecInc);
9044
9045            if(familiarDirs($RegDir, $Dir)
9046            or $RecursiveIncludes{$LibVersion}{$RegHeader}{$RecInc}!=1)
9047            { # in the same directory or included by #include "..."
9048                $TargetHeaders{$LibVersion}{get_filename($RecInc)} = 1;
9049            }
9050        }
9051    }
9052}
9053
9054sub familiarDirs($$)
9055{
9056    my ($D1, $D2) = @_;
9057    if($D1 eq $D2) {
9058        return 1;
9059    }
9060
9061    my $U1 = index($D1, "/usr/");
9062    my $U2 = index($D2, "/usr/");
9063
9064    if($U1==0 and $U2!=0) {
9065        return 0;
9066    }
9067
9068    if($U2==0 and $U1!=0) {
9069        return 0;
9070    }
9071
9072    if(index($D2, $D1."/")==0) {
9073        return 1;
9074    }
9075
9076    # /usr/include/DIR
9077    # /home/user/DIR
9078
9079    my $DL = get_depth($D1);
9080
9081    my @Dirs1 = ($D1);
9082    while($DL - get_depth($D1)<=2
9083    and get_depth($D1)>=4
9084    and $D1=~s/[\/\\]+[^\/\\]*?\Z//) {
9085        push(@Dirs1, $D1);
9086    }
9087
9088    my @Dirs2 = ($D2);
9089    while(get_depth($D2)>=4
9090    and $D2=~s/[\/\\]+[^\/\\]*?\Z//) {
9091        push(@Dirs2, $D2);
9092    }
9093
9094    foreach my $P1 (@Dirs1)
9095    {
9096        foreach my $P2 (@Dirs2)
9097        {
9098
9099            if($P1 eq $P2) {
9100                return 1;
9101            }
9102        }
9103    }
9104    return 0;
9105}
9106
9107sub readHeaders($)
9108{
9109    $Version = $_[0];
9110    printMsg("INFO", "checking header(s) ".$Descriptor{$Version}{"Version"}." ...");
9111    my $DumpPath = getDump();
9112    if($Debug)
9113    { # debug mode
9114        mkpath($DEBUG_PATH{$Version});
9115        copy($DumpPath, $DEBUG_PATH{$Version}."/translation-unit-dump.txt");
9116    }
9117    getInfo($DumpPath);
9118}
9119
9120sub prepareTypes($)
9121{
9122    my $LibVersion = $_[0];
9123    if(not checkDump($LibVersion, "2.0"))
9124    { # support for old ABI dumps
9125      # type names have been corrected in ACC 1.22 (dump 2.0 format)
9126        foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}}))
9127        {
9128            my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
9129            if($TName=~/\A(\w+)::(\w+)/) {
9130                my ($P1, $P2) = ($1, $2);
9131                if($P1 eq $P2) {
9132                    $TName=~s/\A$P1:\:$P1(\W)/$P1$1/;
9133                }
9134                else {
9135                    $TName=~s/\A(\w+:\:)$P2:\:$P2(\W)/$1$P2$2/;
9136                }
9137            }
9138            $TypeInfo{$LibVersion}{$TypeId}{"Name"} = $TName;
9139        }
9140    }
9141    if(not checkDump($LibVersion, "2.5"))
9142    { # support for old ABI dumps
9143      # V < 2.5: array size == "number of elements"
9144      # V >= 2.5: array size in bytes
9145        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9146        {
9147            my %Type = get_PureType($TypeId, $TypeInfo{$LibVersion});
9148            if($Type{"Type"} eq "Array")
9149            {
9150                if(my $Size = $Type{"Size"})
9151                { # array[N]
9152                    my %Base = get_OneStep_BaseType($Type{"Tid"}, $TypeInfo{$LibVersion});
9153                    $Size *= $Base{"Size"};
9154                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = "$Size";
9155                }
9156                else
9157                { # array[] is a pointer
9158                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $WORD_SIZE{$LibVersion};
9159                }
9160            }
9161        }
9162    }
9163    my $V2 = ($LibVersion==1)?2:1;
9164    if(not checkDump($LibVersion, "2.7"))
9165    { # support for old ABI dumps
9166      # size of "method ptr" corrected in 2.7
9167        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9168        {
9169            my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion});
9170            if($PureType{"Type"} eq "MethodPtr")
9171            {
9172                my %Type = get_Type($TypeId, $LibVersion);
9173                my $TypeId_2 = getTypeIdByName($PureType{"Name"}, $V2);
9174                my %Type2 = get_Type($TypeId_2, $V2);
9175                if($Type{"Size"} ne $Type2{"Size"}) {
9176                    $TypeInfo{$LibVersion}{$TypeId}{"Size"} = $Type2{"Size"};
9177                }
9178            }
9179        }
9180    }
9181}
9182
9183sub prepareSymbols($)
9184{
9185    my $LibVersion = $_[0];
9186
9187    if(not keys(%{$SymbolInfo{$LibVersion}}))
9188    { # check if input is valid
9189        if(not $ExtendedCheck and not $CheckObjectsOnly)
9190        {
9191            if($CheckHeadersOnly) {
9192                exitStatus("Empty_Set", "the set of public symbols is empty (".$Descriptor{$LibVersion}{"Version"}.")");
9193            }
9194            else {
9195                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection (".$Descriptor{$LibVersion}{"Version"}.")");
9196            }
9197        }
9198    }
9199
9200    my $Remangle = 0;
9201    if(not checkDump(1, "2.10")
9202    or not checkDump(2, "2.10"))
9203    { # different formats
9204        $Remangle = 1;
9205    }
9206    if($CheckHeadersOnly)
9207    { # different languages
9208        if($UserLang)
9209        { # --lang=LANG for both versions
9210            if(($UsedDump{1}{"V"} and $UserLang ne $UsedDump{1}{"L"})
9211            or ($UsedDump{2}{"V"} and $UserLang ne $UsedDump{2}{"L"}))
9212            {
9213                if($UserLang eq "C++")
9214                { # remangle symbols
9215                    $Remangle = 1;
9216                }
9217                elsif($UserLang eq "C")
9218                { # remove mangling
9219                    $Remangle = -1;
9220                }
9221            }
9222        }
9223    }
9224
9225    foreach my $InfoId (sort {int($b)<=>int($a)} keys(%{$SymbolInfo{$LibVersion}}))
9226    { # reverse order: D0, D1, D2, D0 (artificial, GCC < 4.5), C1, C2
9227        if(not checkDump($LibVersion, "2.13"))
9228        { # support for old ABI dumps
9229            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
9230            {
9231                foreach my $P (keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}}))
9232                {
9233                    my $TypeId = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"type"};
9234                    my $DVal = $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"};
9235                    my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
9236                    if(defined $DVal and $DVal ne "")
9237                    {
9238                        if($TName eq "char") {
9239                            $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = chr($DVal);
9240                        }
9241                        elsif($TName eq "bool") {
9242                            $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$P}{"default"} = $DVal?"true":"false";
9243                        }
9244                    }
9245                }
9246            }
9247        }
9248        if($SymbolInfo{$LibVersion}{$InfoId}{"Destructor"})
9249        {
9250            if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
9251            and keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})
9252            and $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this")
9253            { # support for old GCC < 4.5: skip artificial ~dtor(int __in_chrg)
9254              # + support for old ABI dumps
9255                next;
9256            }
9257        }
9258        my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
9259        my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
9260        my $ClassID = $SymbolInfo{$LibVersion}{$InfoId}{"Class"};
9261        my $Return = $SymbolInfo{$LibVersion}{$InfoId}{"Return"};
9262
9263        my $SRemangle = 0;
9264        if(not checkDump(1, "2.12")
9265        or not checkDump(2, "2.12"))
9266        { # support for old ABI dumps
9267            if($ShortName eq "operator>>")
9268            {
9269                if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
9270                { # corrected mangling of operator>>
9271                    $SRemangle = 1;
9272                }
9273            }
9274            if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
9275            {
9276                if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
9277                and isConstType($Return, $LibVersion) and $MnglName!~/L\d+$ShortName/)
9278                { # corrected mangling of const global data
9279                  # some global data is not mangled in the TU dump: qt_sine_table (Qt 4.8)
9280                  # and incorrectly mangled by old ACC versions
9281                    $SRemangle = 1;
9282                }
9283            }
9284        }
9285        if(not $CheckHeadersOnly)
9286        { # support for old ABI dumps
9287            if(not checkDump(1, "2.17")
9288            or not checkDump(2, "2.17"))
9289            {
9290                if($SymbolInfo{$LibVersion}{$InfoId}{"Data"})
9291                {
9292                    if(not $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
9293                    {
9294                        if(link_symbol($ShortName, $LibVersion, "-Deps"))
9295                        {
9296                            $MnglName = $ShortName;
9297                            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName;
9298                        }
9299                    }
9300                }
9301            }
9302        }
9303        if($Remangle==1 or $SRemangle==1)
9304        { # support for old ABI dumps: some symbols are not mangled in old dumps
9305          # mangle both sets of symbols (old and new)
9306          # NOTE: remangling all symbols by the same mangler
9307            if($MnglName=~/\A_ZN(V|)K/)
9308            { # mangling may be incorrect on old ABI dumps
9309              # because of absent "Const" attribute
9310                $SymbolInfo{$LibVersion}{$InfoId}{"Const"} = 1;
9311            }
9312            if($MnglName=~/\A_ZN(K|)V/)
9313            { # mangling may be incorrect on old ABI dumps
9314              # because of absent "Volatile" attribute
9315                $SymbolInfo{$LibVersion}{$InfoId}{"Volatile"} = 1;
9316            }
9317            if(($ClassID and $MnglName!~/\A(_Z|\?)/)
9318            or (not $ClassID and $CheckHeadersOnly)
9319            or (not $ClassID and not link_symbol($MnglName, $LibVersion, "-Deps")))
9320            { # support for old ABI dumps, GCC >= 4.0
9321              # remangling all manually mangled symbols
9322                if($MnglName = mangle_symbol($InfoId, $LibVersion, "GCC"))
9323                {
9324                    $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $MnglName;
9325                    $MangledNames{$LibVersion}{$MnglName} = 1;
9326                }
9327            }
9328        }
9329        elsif($Remangle==-1)
9330        { # remove mangling
9331            $MnglName = "";
9332            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = "";
9333        }
9334        if(not $MnglName) {
9335            next;
9336        }
9337        if(not $CompleteSignature{$LibVersion}{$MnglName}{"MnglName"})
9338        { # NOTE: global data may enter here twice
9339            %{$CompleteSignature{$LibVersion}{$MnglName}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9340
9341        }
9342        if(not checkDump($LibVersion, "2.6"))
9343        { # support for old dumps
9344          # add "Volatile" attribute
9345            if($MnglName=~/_Z(K|)V/) {
9346                $CompleteSignature{$LibVersion}{$MnglName}{"Volatile"}=1;
9347            }
9348        }
9349        # symbol and its symlink have same signatures
9350        if($SymVer{$LibVersion}{$MnglName}) {
9351            %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$MnglName}}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9352        }
9353
9354        if(my $Alias = $CompleteSignature{$LibVersion}{$MnglName}{"Alias"})
9355        {
9356            %{$CompleteSignature{$LibVersion}{$Alias}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9357            if($SymVer{$LibVersion}{$Alias}) {
9358                %{$CompleteSignature{$LibVersion}{$SymVer{$LibVersion}{$Alias}}} = %{$SymbolInfo{$LibVersion}{$InfoId}};
9359            }
9360        }
9361
9362        # clean memory
9363        delete($SymbolInfo{$LibVersion}{$InfoId});
9364    }
9365    if($COMMON_LANGUAGE{$LibVersion} eq "C++" or $OSgroup eq "windows") {
9366        translateSymbols(keys(%{$CompleteSignature{$LibVersion}}), $LibVersion);
9367    }
9368    if($ExtendedCheck)
9369    { # --ext option
9370        addExtension($LibVersion);
9371    }
9372
9373    # clean memory
9374    delete($SymbolInfo{$LibVersion});
9375
9376    foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}}))
9377    { # detect allocable classes with public exported constructors
9378      # or classes with auto-generated or inline-only constructors
9379      # and other temp info
9380        if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
9381        {
9382            my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
9383            if($CompleteSignature{$LibVersion}{$Symbol}{"Constructor"}
9384            and not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"})
9385            { # Class() { ... } will not be exported
9386                if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"})
9387                {
9388                    if($CheckHeadersOnly or link_symbol($Symbol, $LibVersion, "-Deps")) {
9389                        $AllocableClass{$LibVersion}{$ClassName} = 1;
9390                    }
9391                }
9392            }
9393            if(not $CompleteSignature{$LibVersion}{$Symbol}{"Private"})
9394            { # all imported class methods
9395                if(symbolFilter($Symbol, $LibVersion, "Affected", "Binary"))
9396                {
9397                    if($CheckHeadersOnly)
9398                    {
9399                        if(not $CompleteSignature{$LibVersion}{$Symbol}{"InLine"}
9400                        or $CompleteSignature{$LibVersion}{$Symbol}{"Virt"})
9401                        { # all symbols except non-virtual inline
9402                            $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9403                        }
9404                    }
9405                    else {
9406                        $ClassMethods{"Binary"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9407                    }
9408                }
9409                if(symbolFilter($Symbol, $LibVersion, "Affected", "Source")) {
9410                    $ClassMethods{"Source"}{$LibVersion}{$ClassName}{$Symbol} = 1;
9411                }
9412            }
9413            $ClassNames{$LibVersion}{$ClassName} = 1;
9414        }
9415        if(my $RetId = $CompleteSignature{$LibVersion}{$Symbol}{"Return"})
9416        {
9417            my %Base = get_BaseType($RetId, $LibVersion);
9418            if(defined $Base{"Type"}
9419            and $Base{"Type"}=~/Struct|Class/)
9420            {
9421                my $Name = $TypeInfo{$LibVersion}{$Base{"Tid"}}{"Name"};
9422                if($Name=~/<([^<>\s]+)>/)
9423                {
9424                    if(my $Tid = getTypeIdByName($1, $LibVersion)) {
9425                        $ReturnedClass{$LibVersion}{$Tid} = 1;
9426                    }
9427                }
9428                else {
9429                    $ReturnedClass{$LibVersion}{$Base{"Tid"}} = 1;
9430                }
9431            }
9432        }
9433        foreach my $Num (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
9434        {
9435            my $PId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Num}{"type"};
9436            if(get_PLevel($PId, $LibVersion)>=1)
9437            {
9438                if(my %Base = get_BaseType($PId, $LibVersion))
9439                {
9440                    if($Base{"Type"}=~/Struct|Class/)
9441                    {
9442                        $ParamClass{$LibVersion}{$Base{"Tid"}}{$Symbol} = 1;
9443                        foreach my $SubId (get_sub_classes($Base{"Tid"}, $LibVersion, 1))
9444                        { # mark all derived classes
9445                            $ParamClass{$LibVersion}{$SubId}{$Symbol} = 1;
9446                        }
9447                    }
9448                }
9449            }
9450        }
9451
9452        # mapping {short name => symbols}
9453        $Func_ShortName{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"ShortName"}}{$Symbol} = 1;
9454    }
9455    foreach my $MnglName (keys(%VTableClass))
9456    { # reconstruct attributes of v-tables
9457        if(index($MnglName, "_ZTV")==0)
9458        {
9459            if(my $ClassName = $VTableClass{$MnglName})
9460            {
9461                if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName})
9462                {
9463                    $CompleteSignature{$LibVersion}{$MnglName}{"Header"} = $TypeInfo{$LibVersion}{$ClassId}{"Header"};
9464                    $CompleteSignature{$LibVersion}{$MnglName}{"Class"} = $ClassId;
9465                }
9466            }
9467        }
9468    }
9469
9470    # types
9471    foreach my $TypeId (keys(%{$TypeInfo{$LibVersion}}))
9472    {
9473        if(my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"})
9474        {
9475            if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"}) {
9476                $ClassNames{$LibVersion}{$TName} = 1;
9477            }
9478            if(defined $TypeInfo{$LibVersion}{$TypeId}{"Base"})
9479            {
9480                $ClassNames{$LibVersion}{$TName} = 1;
9481                foreach my $Bid (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"Base"}}))
9482                {
9483                    if(my $BName = $TypeInfo{$LibVersion}{$Bid}{"Name"}) {
9484                        $ClassNames{$LibVersion}{$BName} = 1;
9485                    }
9486                }
9487            }
9488        }
9489    }
9490}
9491
9492sub getFirst($$)
9493{
9494    my ($Tid, $LibVersion) = @_;
9495    if(not $Tid) {
9496        return $Tid;
9497    }
9498
9499    if(my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"})
9500    {
9501        if($TName_Tid{$LibVersion}{$Name}) {
9502            return $TName_Tid{$LibVersion}{$Name};
9503        }
9504    }
9505
9506    return $Tid;
9507}
9508
9509sub register_SymbolUsage($$$)
9510{
9511    my ($InfoId, $UsedType, $LibVersion) = @_;
9512
9513    my %FuncInfo = %{$SymbolInfo{$LibVersion}{$InfoId}};
9514    if(my $RTid = getFirst($FuncInfo{"Return"}, $LibVersion))
9515    {
9516        register_TypeUsage($RTid, $UsedType, $LibVersion);
9517        $SymbolInfo{$LibVersion}{$InfoId}{"Return"} = $RTid;
9518    }
9519    if(my $FCid = getFirst($FuncInfo{"Class"}, $LibVersion))
9520    {
9521        register_TypeUsage($FCid, $UsedType, $LibVersion);
9522        $SymbolInfo{$LibVersion}{$InfoId}{"Class"} = $FCid;
9523
9524        if(my $ThisId = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."*const", $LibVersion))
9525        { # register "this" pointer
9526            register_TypeUsage($ThisId, $UsedType, $LibVersion);
9527        }
9528        if(my $ThisId_C = getTypeIdByName($TypeInfo{$LibVersion}{$FCid}{"Name"}."const*const", $LibVersion))
9529        { # register "this" pointer (const method)
9530            register_TypeUsage($ThisId_C, $UsedType, $LibVersion);
9531        }
9532    }
9533    foreach my $PPos (keys(%{$FuncInfo{"Param"}}))
9534    {
9535        if(my $PTid = getFirst($FuncInfo{"Param"}{$PPos}{"type"}, $LibVersion))
9536        {
9537            register_TypeUsage($PTid, $UsedType, $LibVersion);
9538            $FuncInfo{"Param"}{$PPos}{"type"} = $PTid;
9539        }
9540    }
9541    foreach my $TPos (keys(%{$FuncInfo{"TParam"}}))
9542    {
9543        my $TPName = $FuncInfo{"TParam"}{$TPos}{"name"};
9544        if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) {
9545            register_TypeUsage($TTid, $UsedType, $LibVersion);
9546        }
9547    }
9548}
9549
9550sub register_TypeUsage($$$)
9551{
9552    my ($TypeId, $UsedType, $LibVersion) = @_;
9553    if(not $TypeId) {
9554        return;
9555    }
9556    if($UsedType->{$TypeId})
9557    { # already registered
9558        return;
9559    }
9560
9561    my %TInfo = get_Type($TypeId, $LibVersion);
9562    if($TInfo{"Type"})
9563    {
9564        if(my $NS = $TInfo{"NameSpace"})
9565        {
9566            if(my $NSTid = $TName_Tid{$LibVersion}{$NS}) {
9567                register_TypeUsage($NSTid, $UsedType, $LibVersion);
9568            }
9569        }
9570
9571        if($TInfo{"Type"}=~/\A(Struct|Union|Class|FuncPtr|Func|MethodPtr|FieldPtr|Enum)\Z/)
9572        {
9573            $UsedType->{$TypeId} = 1;
9574            if($TInfo{"Type"}=~/\A(Struct|Class)\Z/)
9575            {
9576                foreach my $BaseId (keys(%{$TInfo{"Base"}})) {
9577                    register_TypeUsage($BaseId, $UsedType, $LibVersion);
9578                }
9579                foreach my $TPos (keys(%{$TInfo{"TParam"}}))
9580                {
9581                    my $TPName = $TInfo{"TParam"}{$TPos}{"name"};
9582                    if(my $TTid = $TName_Tid{$LibVersion}{$TPName}) {
9583                        register_TypeUsage($TTid, $UsedType, $LibVersion);
9584                    }
9585                }
9586            }
9587            foreach my $Memb_Pos (keys(%{$TInfo{"Memb"}}))
9588            {
9589                if(my $MTid = getFirst($TInfo{"Memb"}{$Memb_Pos}{"type"}, $LibVersion))
9590                {
9591                    register_TypeUsage($MTid, $UsedType, $LibVersion);
9592                    $TInfo{"Memb"}{$Memb_Pos}{"type"} = $MTid;
9593                }
9594            }
9595            if($TInfo{"Type"} eq "FuncPtr"
9596            or $TInfo{"Type"} eq "MethodPtr"
9597            or $TInfo{"Type"} eq "Func")
9598            {
9599                if(my $RTid = $TInfo{"Return"}) {
9600                    register_TypeUsage($RTid, $UsedType, $LibVersion);
9601                }
9602                foreach my $PPos (keys(%{$TInfo{"Param"}}))
9603                {
9604                    if(my $PTid = $TInfo{"Param"}{$PPos}{"type"}) {
9605                        register_TypeUsage($PTid, $UsedType, $LibVersion);
9606                    }
9607                }
9608            }
9609            if($TInfo{"Type"} eq "FieldPtr")
9610            {
9611                if(my $RTid = $TInfo{"Return"}) {
9612                    register_TypeUsage($RTid, $UsedType, $LibVersion);
9613                }
9614                if(my $CTid = $TInfo{"Class"}) {
9615                    register_TypeUsage($CTid, $UsedType, $LibVersion);
9616                }
9617            }
9618            if($TInfo{"Type"} eq "MethodPtr")
9619            {
9620                if(my $CTid = $TInfo{"Class"}) {
9621                    register_TypeUsage($CTid, $UsedType, $LibVersion);
9622                }
9623            }
9624        }
9625        elsif($TInfo{"Type"}=~/\A(Const|ConstVolatile|Volatile|Pointer|Ref|Restrict|Array|Typedef)\Z/)
9626        {
9627            $UsedType->{$TypeId} = 1;
9628            if(my $BTid = getFirst($TInfo{"BaseType"}, $LibVersion))
9629            {
9630                register_TypeUsage($BTid, $UsedType, $LibVersion);
9631                $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $BTid;
9632            }
9633        }
9634        else
9635        { # Intrinsic, TemplateParam, TypeName, SizeOf, etc.
9636            $UsedType->{$TypeId} = 1;
9637        }
9638    }
9639}
9640
9641sub selectSymbol($$$$)
9642{ # select symbol to check or to dump
9643    my ($Symbol, $SInfo, $Level, $LibVersion) = @_;
9644
9645    if($Level eq "Dump")
9646    {
9647        if($SInfo->{"Virt"} or $SInfo->{"PureVirt"})
9648        { # TODO: check if this symbol is from
9649          # base classes of other target symbols
9650            return 1;
9651        }
9652    }
9653
9654    if(not $STDCXX_TESTING and $Symbol=~/\A(_ZS|_ZNS|_ZNKS)/)
9655    { # stdc++ interfaces
9656        return 0;
9657    }
9658
9659    my $Target = 0;
9660    if(my $Header = $SInfo->{"Header"}) {
9661        $Target = (is_target_header($Header, 1) or is_target_header($Header, 2));
9662    }
9663    if($ExtendedCheck)
9664    {
9665        if(index($Symbol, "external_func_")==0) {
9666            $Target = 1;
9667        }
9668    }
9669    if($CheckHeadersOnly or $Level eq "Source")
9670    {
9671        if($Target)
9672        {
9673            if($Level eq "Dump")
9674            { # dumped
9675                if($BinaryOnly)
9676                {
9677                    if(not $SInfo->{"InLine"} or $SInfo->{"Data"}) {
9678                        return 1;
9679                    }
9680                }
9681                else {
9682                    return 1;
9683                }
9684            }
9685            elsif($Level eq "Source")
9686            { # checked
9687                return 1;
9688            }
9689            elsif($Level eq "Binary")
9690            { # checked
9691                if(not $SInfo->{"InLine"} or $SInfo->{"Data"}
9692                or $SInfo->{"Virt"} or $SInfo->{"PureVirt"}) {
9693                    return 1;
9694                }
9695            }
9696        }
9697    }
9698    else
9699    { # library is available
9700        if(link_symbol($Symbol, $LibVersion, "-Deps"))
9701        { # exported symbols
9702            return 1;
9703        }
9704        if($Level eq "Dump")
9705        { # dumped
9706            if($BinaryOnly)
9707            {
9708                if($SInfo->{"Data"})
9709                {
9710                    if($Target) {
9711                        return 1;
9712                    }
9713                }
9714            }
9715            else
9716            { # SrcBin
9717                if($Target) {
9718                    return 1;
9719                }
9720            }
9721        }
9722        elsif($Level eq "Source")
9723        { # checked
9724            if($SInfo->{"PureVirt"} or $SInfo->{"Data"} or $SInfo->{"InLine"}
9725            or isInLineInst($Symbol, $SInfo, $LibVersion))
9726            { # skip LOCAL symbols
9727                if($Target) {
9728                    return 1;
9729                }
9730            }
9731        }
9732        elsif($Level eq "Binary")
9733        { # checked
9734            if($SInfo->{"PureVirt"} or $SInfo->{"Data"})
9735            {
9736                if($Target) {
9737                    return 1;
9738                }
9739            }
9740        }
9741    }
9742    return 0;
9743}
9744
9745sub cleanDump($)
9746{ # clean data
9747    my $LibVersion = $_[0];
9748    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
9749    {
9750        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}}))
9751        {
9752            delete($SymbolInfo{$LibVersion}{$InfoId});
9753            next;
9754        }
9755        my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
9756        if(not $MnglName)
9757        {
9758            delete($SymbolInfo{$LibVersion}{$InfoId});
9759            next;
9760        }
9761        my $ShortName = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
9762        if(not $ShortName)
9763        {
9764            delete($SymbolInfo{$LibVersion}{$InfoId});
9765            next;
9766        }
9767        if($MnglName eq $ShortName)
9768        { # remove duplicate data
9769            delete($SymbolInfo{$LibVersion}{$InfoId}{"MnglName"});
9770        }
9771        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}})) {
9772            delete($SymbolInfo{$LibVersion}{$InfoId}{"Param"});
9773        }
9774        if(not keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"TParam"}})) {
9775            delete($SymbolInfo{$LibVersion}{$InfoId}{"TParam"});
9776        }
9777        delete($SymbolInfo{$LibVersion}{$InfoId}{"Type"});
9778    }
9779    foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
9780    {
9781        if(not keys(%{$TypeInfo{$LibVersion}{$Tid}}))
9782        {
9783            delete($TypeInfo{$LibVersion}{$Tid});
9784            next;
9785        }
9786        delete($TypeInfo{$LibVersion}{$Tid}{"Tid"});
9787        foreach my $Attr ("Header", "Line", "Size", "NameSpace")
9788        {
9789            if(not $TypeInfo{$LibVersion}{$Tid}{$Attr}) {
9790                delete($TypeInfo{$LibVersion}{$Tid}{$Attr});
9791            }
9792        }
9793        if(not keys(%{$TypeInfo{$LibVersion}{$Tid}{"TParam"}})) {
9794            delete($TypeInfo{$LibVersion}{$Tid}{"TParam"});
9795        }
9796    }
9797}
9798
9799sub selectType($$)
9800{
9801    my ($Tid, $LibVersion) = @_;
9802
9803    if(my $Dupl = $TypeTypedef{$LibVersion}{$Tid})
9804    {
9805        if(defined $TypeInfo{$LibVersion}{$Dupl})
9806        {
9807            if($TypeInfo{$LibVersion}{$Dupl}{"Name"} eq $TypeInfo{$LibVersion}{$Tid}{"Name"})
9808            { # duplicate
9809                return 0;
9810            }
9811        }
9812    }
9813
9814    if(my $THeader = $TypeInfo{$LibVersion}{$Tid}{"Header"})
9815    {
9816        if(not isBuiltIn($THeader))
9817        {
9818            if($TypeInfo{$LibVersion}{$Tid}{"Type"}=~/Class|Struct|Union|Enum|Typedef/)
9819            {
9820                if(not isAnon($TypeInfo{$LibVersion}{$Tid}{"Name"}))
9821                {
9822                    if(is_target_header($THeader, $LibVersion))
9823                    { # from target headers
9824                        if(not selfTypedef($Tid, $LibVersion)) {
9825                            return 1;
9826                        }
9827                    }
9828                }
9829            }
9830        }
9831    }
9832    return 0;
9833}
9834
9835sub remove_Unused($$)
9836{ # remove unused data types from the ABI dump
9837    my ($LibVersion, $Kind) = @_;
9838
9839    my %UsedType = ();
9840
9841    foreach my $InfoId (sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}}))
9842    {
9843        register_SymbolUsage($InfoId, \%UsedType, $LibVersion);
9844    }
9845    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9846    {
9847        if($UsedType{$Tid})
9848        { # All & Extended
9849            next;
9850        }
9851
9852        if($Kind eq "Extended")
9853        {
9854            if(selectType($Tid, $LibVersion))
9855            {
9856                my %Tree = ();
9857                register_TypeUsage($Tid, \%Tree, $LibVersion);
9858
9859                my $Tmpl = 0;
9860                foreach (sort {int($a)<=>int($b)} keys(%Tree))
9861                {
9862                    if(defined $TypeInfo{$LibVersion}{$_}{"Template"}
9863                    or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam")
9864                    {
9865                        $Tmpl = 1;
9866                        last;
9867                    }
9868                }
9869                if(not $Tmpl)
9870                {
9871                    foreach (keys(%Tree)) {
9872                        $UsedType{$_} = 1;
9873                    }
9874                }
9875            }
9876        }
9877    }
9878
9879    my %Delete = ();
9880
9881    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9882    { # remove unused types
9883        if($UsedType{$Tid})
9884        { # All & Extended
9885            next;
9886        }
9887
9888        if($Kind eq "Extra")
9889        {
9890            my %Tree = ();
9891            register_TypeUsage($Tid, \%Tree, $LibVersion);
9892
9893            foreach (sort {int($a)<=>int($b)} keys(%Tree))
9894            {
9895                if(defined $TypeInfo{$LibVersion}{$_}{"Template"}
9896                or $TypeInfo{$LibVersion}{$_}{"Type"} eq "TemplateParam")
9897                {
9898                    $Delete{$Tid} = 1;
9899                    last;
9900                }
9901            }
9902        }
9903        else
9904        {
9905            # remove type
9906            delete($TypeInfo{$LibVersion}{$Tid});
9907        }
9908    }
9909
9910    if($Kind eq "Extra")
9911    { # remove duplicates
9912        foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
9913        {
9914            if($UsedType{$Tid})
9915            { # All & Extended
9916                next;
9917            }
9918
9919            my $Name = $TypeInfo{$LibVersion}{$Tid}{"Name"};
9920
9921            if($TName_Tid{$LibVersion}{$Name} ne $Tid) {
9922                delete($TypeInfo{$LibVersion}{$Tid});
9923            }
9924        }
9925    }
9926
9927    foreach my $Tid (keys(%Delete))
9928    {
9929        delete($TypeInfo{$LibVersion}{$Tid});
9930    }
9931}
9932
9933sub check_Completeness($$)
9934{
9935    my ($Info, $LibVersion) = @_;
9936
9937    # data types
9938    if(defined $Info->{"Memb"})
9939    {
9940        foreach my $Pos (keys(%{$Info->{"Memb"}}))
9941        {
9942            if(defined $Info->{"Memb"}{$Pos}{"type"}) {
9943                check_TypeInfo($Info->{"Memb"}{$Pos}{"type"}, $LibVersion);
9944            }
9945        }
9946    }
9947    if(defined $Info->{"Base"})
9948    {
9949        foreach my $Bid (keys(%{$Info->{"Base"}})) {
9950            check_TypeInfo($Bid, $LibVersion);
9951        }
9952    }
9953    if(defined $Info->{"BaseType"}) {
9954        check_TypeInfo($Info->{"BaseType"}, $LibVersion);
9955    }
9956    if(defined $Info->{"TParam"})
9957    {
9958        foreach my $Pos (keys(%{$Info->{"TParam"}}))
9959        {
9960            my $TName = $Info->{"TParam"}{$Pos}{"name"};
9961            if($TName=~/\A\(.+\)(true|false|\d.*)\Z/) {
9962                next;
9963            }
9964            if($TName eq "_BoolType") {
9965                next;
9966            }
9967            if($TName=~/\Asizeof\(/) {
9968                next;
9969            }
9970            if(my $Tid = $TName_Tid{$LibVersion}{$TName}) {
9971                check_TypeInfo($Tid, $LibVersion);
9972            }
9973            else
9974            {
9975                if(defined $Debug) {
9976                    printMsg("WARNING", "missed type $TName");
9977                }
9978            }
9979        }
9980    }
9981
9982    # symbols
9983    if(defined $Info->{"Param"})
9984    {
9985        foreach my $Pos (keys(%{$Info->{"Param"}}))
9986        {
9987            if(defined $Info->{"Param"}{$Pos}{"type"}) {
9988                check_TypeInfo($Info->{"Param"}{$Pos}{"type"}, $LibVersion);
9989            }
9990        }
9991    }
9992    if(defined $Info->{"Return"}) {
9993        check_TypeInfo($Info->{"Return"}, $LibVersion);
9994    }
9995    if(defined $Info->{"Class"}) {
9996        check_TypeInfo($Info->{"Class"}, $LibVersion);
9997    }
9998}
9999
10000sub check_TypeInfo($$)
10001{
10002    my ($Tid, $LibVersion) = @_;
10003
10004    if(defined $CheckedTypeInfo{$LibVersion}{$Tid}) {
10005        return;
10006    }
10007    $CheckedTypeInfo{$LibVersion}{$Tid} = 1;
10008
10009    if(defined $TypeInfo{$LibVersion}{$Tid})
10010    {
10011        if(not $TypeInfo{$LibVersion}{$Tid}{"Name"}) {
10012            printMsg("ERROR", "missed type name ($Tid)");
10013        }
10014        check_Completeness($TypeInfo{$LibVersion}{$Tid}, $LibVersion);
10015    }
10016    else {
10017        printMsg("ERROR", "missed type id $Tid");
10018    }
10019}
10020
10021sub selfTypedef($$)
10022{
10023    my ($TypeId, $LibVersion) = @_;
10024    my %Type = get_Type($TypeId, $LibVersion);
10025    if($Type{"Type"} eq "Typedef")
10026    {
10027        my %Base = get_OneStep_BaseType($TypeId, $TypeInfo{$LibVersion});
10028        if($Base{"Type"}=~/Class|Struct/)
10029        {
10030            if($Type{"Name"} eq $Base{"Name"}) {
10031                return 1;
10032            }
10033            elsif($Type{"Name"}=~/::(\w+)\Z/)
10034            {
10035                if($Type{"Name"} eq $Base{"Name"}."::".$1)
10036                { # QPointer<QWidget>::QPointer
10037                    return 1;
10038                }
10039            }
10040        }
10041    }
10042    return 0;
10043}
10044
10045sub addExtension($)
10046{
10047    my $LibVersion = $_[0];
10048    foreach my $Tid (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
10049    {
10050        if(selectType($Tid, $LibVersion))
10051        {
10052            my $TName = $TypeInfo{$LibVersion}{$Tid}{"Name"};
10053            $TName=~s/\A(struct|union|class|enum) //;
10054            my $Symbol = "external_func_".$TName;
10055
10056            %{$CompleteSignature{$LibVersion}{$Symbol}} = (
10057                "Header" => "extended.h",
10058                "ShortName" => $Symbol,
10059                "MnglName" => $Symbol,
10060                "Param" => { 0 => { "type"=>$Tid, "name"=>"p1" } }
10061            );
10062
10063            $ExtendedSymbols{$Symbol} = 1;
10064            $CheckedSymbols{"Binary"}{$Symbol} = 1;
10065            $CheckedSymbols{"Source"}{$Symbol} = 1;
10066        }
10067    }
10068    $ExtendedSymbols{"external_func_0"} = 1;
10069    $CheckedSymbols{"Binary"}{"external_func_0"} = 1;
10070    $CheckedSymbols{"Source"}{"external_func_0"} = 1;
10071}
10072
10073sub findMethod($$$)
10074{
10075    my ($VirtFunc, $ClassId, $LibVersion) = @_;
10076    foreach my $BaseClass_Id (keys(%{$TypeInfo{$LibVersion}{$ClassId}{"Base"}}))
10077    {
10078        if(my $VirtMethodInClass = findMethod_Class($VirtFunc, $BaseClass_Id, $LibVersion)) {
10079            return $VirtMethodInClass;
10080        }
10081        elsif(my $VirtMethodInBaseClasses = findMethod($VirtFunc, $BaseClass_Id, $LibVersion)) {
10082            return $VirtMethodInBaseClasses;
10083        }
10084    }
10085    return "";
10086}
10087
10088sub findMethod_Class($$$)
10089{
10090    my ($VirtFunc, $ClassId, $LibVersion) = @_;
10091    my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
10092    return "" if(not defined $VirtualTable{$LibVersion}{$ClassName});
10093    my $TargetSuffix = get_symbol_suffix($VirtFunc, 1);
10094    my $TargetShortName = $CompleteSignature{$LibVersion}{$VirtFunc}{"ShortName"};
10095    foreach my $Candidate (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10096    { # search for interface with the same parameters suffix (overridden)
10097        if($TargetSuffix eq get_symbol_suffix($Candidate, 1))
10098        {
10099            if($CompleteSignature{$LibVersion}{$VirtFunc}{"Destructor"})
10100            {
10101                if($CompleteSignature{$LibVersion}{$Candidate}{"Destructor"})
10102                {
10103                    if(($VirtFunc=~/D0E/ and $Candidate=~/D0E/)
10104                    or ($VirtFunc=~/D1E/ and $Candidate=~/D1E/)
10105                    or ($VirtFunc=~/D2E/ and $Candidate=~/D2E/)) {
10106                        return $Candidate;
10107                    }
10108                }
10109            }
10110            else
10111            {
10112                if($TargetShortName eq $CompleteSignature{$LibVersion}{$Candidate}{"ShortName"}) {
10113                    return $Candidate;
10114                }
10115            }
10116        }
10117    }
10118    return "";
10119}
10120
10121sub registerVTable($)
10122{
10123    my $LibVersion = $_[0];
10124    foreach my $Symbol (keys(%{$CompleteSignature{$LibVersion}}))
10125    {
10126        if($CompleteSignature{$LibVersion}{$Symbol}{"Virt"}
10127        or $CompleteSignature{$LibVersion}{$Symbol}{"PureVirt"})
10128        {
10129            my $ClassName = $TypeInfo{$LibVersion}{$CompleteSignature{$LibVersion}{$Symbol}{"Class"}}{"Name"};
10130            next if(not $STDCXX_TESTING and $ClassName=~/\A(std::|__cxxabi)/);
10131            if($CompleteSignature{$LibVersion}{$Symbol}{"Destructor"}
10132            and $Symbol=~/D2E/)
10133            { # pure virtual D2-destructors are marked as "virt" in the dump
10134              # virtual D2-destructors are NOT marked as "virt" in the dump
10135              # both destructors are not presented in the v-table
10136                next;
10137            }
10138            my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
10139            $VirtualTable{$LibVersion}{$ClassName}{$MnglName} = 1;
10140        }
10141    }
10142}
10143
10144sub registerOverriding($)
10145{
10146    my $LibVersion = $_[0];
10147    my @Classes = keys(%{$VirtualTable{$LibVersion}});
10148    @Classes = sort {int($TName_Tid{$LibVersion}{$a})<=>int($TName_Tid{$LibVersion}{$b})} @Classes;
10149    foreach my $ClassName (@Classes)
10150    {
10151        foreach my $VirtFunc (keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10152        {
10153            if($CompleteSignature{$LibVersion}{$VirtFunc}{"PureVirt"})
10154            { # pure virtuals
10155                next;
10156            }
10157            my $ClassId = $TName_Tid{$LibVersion}{$ClassName};
10158            if(my $Overridden = findMethod($VirtFunc, $ClassId, $LibVersion))
10159            {
10160                if($CompleteSignature{$LibVersion}{$Overridden}{"Virt"}
10161                or $CompleteSignature{$LibVersion}{$Overridden}{"PureVirt"})
10162                { # both overridden virtual methods
10163                  # and implemented pure virtual methods
10164                    $CompleteSignature{$LibVersion}{$VirtFunc}{"Override"} = $Overridden;
10165                    $OverriddenMethods{$LibVersion}{$Overridden}{$VirtFunc} = 1;
10166                    delete($VirtualTable{$LibVersion}{$ClassName}{$VirtFunc}); # remove from v-table model
10167                }
10168            }
10169        }
10170        if(not keys(%{$VirtualTable{$LibVersion}{$ClassName}})) {
10171            delete($VirtualTable{$LibVersion}{$ClassName});
10172        }
10173    }
10174}
10175
10176sub setVirtFuncPositions($)
10177{
10178    my $LibVersion = $_[0];
10179    foreach my $ClassName (keys(%{$VirtualTable{$LibVersion}}))
10180    {
10181        my ($Num, $Rel) = (1, 0);
10182
10183        if(my @Funcs = sort keys(%{$VirtualTable{$LibVersion}{$ClassName}}))
10184        {
10185            if($UsedDump{$LibVersion}{"DWARF"}) {
10186                @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @Funcs;
10187            }
10188            else {
10189                @Funcs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @Funcs;
10190            }
10191            foreach my $VirtFunc (@Funcs)
10192            {
10193                if($UsedDump{$LibVersion}{"DWARF"}) {
10194                    $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $CompleteSignature{$LibVersion}{$VirtFunc}{"VirtPos"};
10195                }
10196                else {
10197                    $VirtualTable{$LibVersion}{$ClassName}{$VirtFunc} = $Num++;
10198                }
10199
10200                # set relative positions
10201                if(defined $VirtualTable{1}{$ClassName} and defined $VirtualTable{1}{$ClassName}{$VirtFunc}
10202                and defined $VirtualTable{2}{$ClassName} and defined $VirtualTable{2}{$ClassName}{$VirtFunc})
10203                { # relative position excluding added and removed virtual functions
10204                    if(not $CompleteSignature{1}{$VirtFunc}{"Override"}
10205                    and not $CompleteSignature{2}{$VirtFunc}{"Override"}) {
10206                        $CompleteSignature{$LibVersion}{$VirtFunc}{"RelPos"} = $Rel++;
10207                    }
10208                }
10209            }
10210        }
10211    }
10212    foreach my $ClassName (keys(%{$ClassNames{$LibVersion}}))
10213    {
10214        my $AbsNum = 1;
10215        foreach my $VirtFunc (getVTable_Model($TName_Tid{$LibVersion}{$ClassName}, $LibVersion)) {
10216            $VirtualTable_Model{$LibVersion}{$ClassName}{$VirtFunc} = $AbsNum++;
10217        }
10218    }
10219}
10220
10221sub get_sub_classes($$$)
10222{
10223    my ($ClassId, $LibVersion, $Recursive) = @_;
10224    return () if(not defined $Class_SubClasses{$LibVersion}{$ClassId});
10225    my @Subs = ();
10226    foreach my $SubId (keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
10227    {
10228        if($Recursive)
10229        {
10230            foreach my $SubSubId (get_sub_classes($SubId, $LibVersion, $Recursive)) {
10231                push(@Subs, $SubSubId);
10232            }
10233        }
10234        push(@Subs, $SubId);
10235    }
10236    return @Subs;
10237}
10238
10239sub get_base_classes($$$)
10240{
10241    my ($ClassId, $LibVersion, $Recursive) = @_;
10242    my %ClassType = get_Type($ClassId, $LibVersion);
10243    return () if(not defined $ClassType{"Base"});
10244    my @Bases = ();
10245    foreach my $BaseId (sort {int($ClassType{"Base"}{$a}{"pos"})<=>int($ClassType{"Base"}{$b}{"pos"})}
10246    keys(%{$ClassType{"Base"}}))
10247    {
10248        if($Recursive)
10249        {
10250            foreach my $SubBaseId (get_base_classes($BaseId, $LibVersion, $Recursive)) {
10251                push(@Bases, $SubBaseId);
10252            }
10253        }
10254        push(@Bases, $BaseId);
10255    }
10256    return @Bases;
10257}
10258
10259sub getVTable_Model($$)
10260{ # return an ordered list of v-table elements
10261    my ($ClassId, $LibVersion) = @_;
10262    my @Bases = get_base_classes($ClassId, $LibVersion, 1);
10263    my @Elements = ();
10264    foreach my $BaseId (@Bases, $ClassId)
10265    {
10266        if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"})
10267        {
10268            if(defined $VirtualTable{$LibVersion}{$BName})
10269            {
10270                my @VFuncs = keys(%{$VirtualTable{$LibVersion}{$BName}});
10271                if($UsedDump{$LibVersion}{"DWARF"}) {
10272                    @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"VirtPos"}) <=> int($CompleteSignature{$LibVersion}{$b}{"VirtPos"})} @VFuncs;
10273                }
10274                else {
10275                    @VFuncs = sort {int($CompleteSignature{$LibVersion}{$a}{"Line"}) <=> int($CompleteSignature{$LibVersion}{$b}{"Line"})} @VFuncs;
10276                }
10277                foreach my $VFunc (@VFuncs) {
10278                    push(@Elements, $VFunc);
10279                }
10280            }
10281        }
10282    }
10283    return @Elements;
10284}
10285
10286sub getVShift($$)
10287{
10288    my ($ClassId, $LibVersion) = @_;
10289    my @Bases = get_base_classes($ClassId, $LibVersion, 1);
10290    my $VShift = 0;
10291    foreach my $BaseId (@Bases)
10292    {
10293        if(my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"})
10294        {
10295            if(defined $VirtualTable{$LibVersion}{$BName}) {
10296                $VShift+=keys(%{$VirtualTable{$LibVersion}{$BName}});
10297            }
10298        }
10299    }
10300    return $VShift;
10301}
10302
10303sub getShift($$)
10304{
10305    my ($ClassId, $LibVersion) = @_;
10306    my @Bases = get_base_classes($ClassId, $LibVersion, 0);
10307    my $Shift = 0;
10308    foreach my $BaseId (@Bases)
10309    {
10310        if(my $Size = $TypeInfo{$LibVersion}{$BaseId}{"Size"})
10311        {
10312            if($Size!=1)
10313            { # not empty base class
10314                $Shift+=$Size;
10315            }
10316        }
10317    }
10318    return $Shift;
10319}
10320
10321sub getVTable_Size($$)
10322{ # number of v-table elements
10323    my ($ClassName, $LibVersion) = @_;
10324    my $Size = 0;
10325    # three approaches
10326    if(not $Size)
10327    { # real size
10328        if(my %VTable = getVTable_Real($ClassName, $LibVersion)) {
10329            $Size = keys(%VTable);
10330        }
10331    }
10332    if(not $Size)
10333    { # shared library symbol size
10334        if($Size = getSymbolSize($ClassVTable{$ClassName}, $LibVersion)) {
10335            $Size /= $WORD_SIZE{$LibVersion};
10336        }
10337    }
10338    if(not $Size)
10339    { # model size
10340        if(defined $VirtualTable_Model{$LibVersion}{$ClassName}) {
10341            $Size = keys(%{$VirtualTable_Model{$LibVersion}{$ClassName}}) + 2;
10342        }
10343    }
10344    return $Size;
10345}
10346
10347sub isCopyingClass($$)
10348{
10349    my ($TypeId, $LibVersion) = @_;
10350    return $TypeInfo{$LibVersion}{$TypeId}{"Copied"};
10351}
10352
10353sub isLeafClass($$)
10354{
10355    my ($ClassId, $LibVersion) = @_;
10356    return (not keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}));
10357}
10358
10359sub havePubFields($)
10360{ # check structured type for public fields
10361    return isAccessible($_[0], {}, 0, -1);
10362}
10363
10364sub isAccessible($$$$)
10365{ # check interval in structured type for public fields
10366    my ($TypePtr, $Skip, $Start, $End) = @_;
10367    return 0 if(not $TypePtr);
10368    if($End==-1) {
10369        $End = keys(%{$TypePtr->{"Memb"}})-1;
10370    }
10371    foreach my $MemPos (keys(%{$TypePtr->{"Memb"}}))
10372    {
10373        if($Skip and $Skip->{$MemPos})
10374        { # skip removed/added fields
10375            next;
10376        }
10377        if(int($MemPos)>=$Start and int($MemPos)<=$End)
10378        {
10379            if(isPublic($TypePtr, $MemPos)) {
10380                return ($MemPos+1);
10381            }
10382        }
10383    }
10384    return 0;
10385}
10386
10387sub isReserved($)
10388{ # reserved fields == private
10389    my $MName = $_[0];
10390    if($MName=~/reserved|padding|f_spare/i) {
10391        return 1;
10392    }
10393    if($MName=~/\A[_]*(spare|pad|unused|dummy)[_\d]*\Z/i) {
10394        return 1;
10395    }
10396    if($MName=~/(pad\d+)/i) {
10397        return 1;
10398    }
10399    return 0;
10400}
10401
10402sub isPublic($$)
10403{
10404    my ($TypePtr, $FieldPos) = @_;
10405    return 0 if(not $TypePtr);
10406    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos});
10407    return 0 if(not defined $TypePtr->{"Memb"}{$FieldPos}{"name"});
10408    if(not $TypePtr->{"Memb"}{$FieldPos}{"access"})
10409    { # by name in C language
10410      # FIXME: add other methods to detect private members
10411        my $MName = $TypePtr->{"Memb"}{$FieldPos}{"name"};
10412        if($MName=~/priv|abidata|parent_object/i)
10413        { # C-styled private data
10414            return 0;
10415        }
10416        if(lc($MName) eq "abi")
10417        { # ABI information/reserved field
10418            return 0;
10419        }
10420        if(isReserved($MName))
10421        { # reserved fields
10422            return 0;
10423        }
10424        return 1;
10425    }
10426    elsif($TypePtr->{"Memb"}{$FieldPos}{"access"} ne "private")
10427    { # by access in C++ language
10428        return 1;
10429    }
10430    return 0;
10431}
10432
10433sub getVTable_Real($$)
10434{
10435    my ($ClassName, $LibVersion) = @_;
10436    if(my $ClassId = $TName_Tid{$LibVersion}{$ClassName})
10437    {
10438        my %Type = get_Type($ClassId, $LibVersion);
10439        if(defined $Type{"VTable"}) {
10440            return %{$Type{"VTable"}};
10441        }
10442    }
10443    return ();
10444}
10445
10446sub cmpVTables($)
10447{
10448    my $ClassName = $_[0];
10449    my $Res = cmpVTables_Real($ClassName, 1);
10450    if($Res==-1) {
10451        $Res = cmpVTables_Model($ClassName);
10452    }
10453    return $Res;
10454}
10455
10456sub cmpVTables_Model($)
10457{
10458    my $ClassName = $_[0];
10459    foreach my $Symbol (keys(%{$VirtualTable_Model{1}{$ClassName}}))
10460    {
10461        if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol}) {
10462            return 1;
10463        }
10464    }
10465    return 0;
10466}
10467
10468sub cmpVTables_Real($$)
10469{
10470    my ($ClassName, $Strong) = @_;
10471    if(defined $Cache{"cmpVTables_Real"}{$Strong}{$ClassName}) {
10472        return $Cache{"cmpVTables_Real"}{$Strong}{$ClassName};
10473    }
10474    my %VTable_Old = getVTable_Real($ClassName, 1);
10475    my %VTable_New = getVTable_Real($ClassName, 2);
10476    if(not %VTable_Old or not %VTable_New)
10477    { # old ABI dumps
10478        return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = -1);
10479    }
10480    my %Indexes = map {$_=>1} (keys(%VTable_Old), keys(%VTable_New));
10481    foreach my $Offset (sort {int($a)<=>int($b)} keys(%Indexes))
10482    {
10483        if(not defined $VTable_Old{$Offset})
10484        { # v-table v.1 < v-table v.2
10485            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = $Strong);
10486        }
10487        my $Entry1 = $VTable_Old{$Offset};
10488        if(not defined $VTable_New{$Offset})
10489        { # v-table v.1 > v-table v.2
10490            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = ($Strong or $Entry1!~/__cxa_pure_virtual/));
10491        }
10492        my $Entry2 = $VTable_New{$Offset};
10493
10494        $Entry1 = simpleVEntry($Entry1);
10495        $Entry2 = simpleVEntry($Entry2);
10496        if($Entry1 ne $Entry2)
10497        { # register as changed
10498            if($Entry1=~/::([^:]+)\Z/)
10499            {
10500                my $M1 = $1;
10501                if($Entry2=~/::([^:]+)\Z/)
10502                {
10503                    my $M2 = $1;
10504                    if($M1 eq $M2)
10505                    { # overridden
10506                        next;
10507                    }
10508                }
10509            }
10510            if(differentDumps("G"))
10511            {
10512                if($Entry1=~/\A\-(0x|\d+)/ and $Entry2=~/\A\-(0x|\d+)/)
10513                {
10514                    # GCC 4.6.1: -0x00000000000000010
10515                    # GCC 4.7.0: -16
10516                    next;
10517                }
10518            }
10519            return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 1);
10520        }
10521    }
10522    return ($Cache{"cmpVTables_Real"}{$Strong}{$ClassName} = 0);
10523}
10524
10525sub mergeVTables($)
10526{ # merging v-tables without diagnostics
10527    my $Level = $_[0];
10528    foreach my $ClassName (keys(%{$VirtualTable{1}}))
10529    {
10530        if($VTableChanged_M{$ClassName})
10531        { # already registered
10532            next;
10533        }
10534        if(cmpVTables_Real($ClassName, 0)==1)
10535        {
10536            my @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
10537            foreach my $Symbol (@Affected)
10538            {
10539                %{$CompatProblems{$Level}{$Symbol}{"Virtual_Table_Changed_Unknown"}{$ClassName}}=(
10540                    "Type_Name"=>$ClassName,
10541                    "Target"=>$ClassName);
10542            }
10543        }
10544    }
10545}
10546
10547sub mergeBases($)
10548{
10549    my $Level = $_[0];
10550    foreach my $ClassName (keys(%{$ClassNames{1}}))
10551    { # detect added and removed virtual functions
10552        my $ClassId = $TName_Tid{1}{$ClassName};
10553        next if(not $ClassId);
10554        if(defined $VirtualTable{2}{$ClassName})
10555        {
10556            foreach my $Symbol (keys(%{$VirtualTable{2}{$ClassName}}))
10557            {
10558                if($TName_Tid{1}{$ClassName}
10559                and not defined $VirtualTable{1}{$ClassName}{$Symbol})
10560                { # added to v-table
10561                    if(defined $CompleteSignature{1}{$Symbol}
10562                    and $CompleteSignature{1}{$Symbol}{"Virt"})
10563                    { # override some method in v.1
10564                        next;
10565                    }
10566                    $AddedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
10567                }
10568            }
10569        }
10570        if(defined $VirtualTable{1}{$ClassName})
10571        {
10572            foreach my $Symbol (keys(%{$VirtualTable{1}{$ClassName}}))
10573            {
10574                if($TName_Tid{2}{$ClassName}
10575                and not defined $VirtualTable{2}{$ClassName}{$Symbol})
10576                { # removed from v-table
10577                    if(defined $CompleteSignature{2}{$Symbol}
10578                    and $CompleteSignature{2}{$Symbol}{"Virt"})
10579                    { # override some method in v.2
10580                        next;
10581                    }
10582                    $RemovedInt_Virt{$Level}{$ClassName}{$Symbol} = 1;
10583                }
10584            }
10585        }
10586        if($Level eq "Binary")
10587        { # Binary-level
10588            my %Class_Type = get_Type($ClassId, 1);
10589            foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$ClassName}}))
10590            { # check replacements, including pure virtual methods
10591                my $AddedPos = $VirtualTable{2}{$ClassName}{$AddedVFunc};
10592                foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$ClassName}}))
10593                {
10594                    my $RemovedPos = $VirtualTable{1}{$ClassName}{$RemovedVFunc};
10595                    if($AddedPos==$RemovedPos)
10596                    {
10597                        $VirtualReplacement{$AddedVFunc} = $RemovedVFunc;
10598                        $VirtualReplacement{$RemovedVFunc} = $AddedVFunc;
10599                        last; # other methods will be reported as "added" or "removed"
10600                    }
10601                }
10602                if(my $RemovedVFunc = $VirtualReplacement{$AddedVFunc})
10603                {
10604                    if(lc($AddedVFunc) eq lc($RemovedVFunc))
10605                    { # skip: DomUi => DomUI parameter (Qt 4.2.3 to 4.3.0)
10606                        next;
10607                    }
10608                    my $ProblemType = "Virtual_Replacement";
10609                    my @Affected = ($RemovedVFunc);
10610                    if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
10611                    { # pure methods
10612                        if(not isUsedClass($ClassId, 1, $Level))
10613                        { # not a parameter of some exported method
10614                            next;
10615                        }
10616                        $ProblemType = "Pure_Virtual_Replacement";
10617
10618                        # affected all methods (both virtual and non-virtual ones)
10619                        @Affected = (keys(%{$ClassMethods{$Level}{1}{$ClassName}}));
10620                        push(@Affected, keys(%{$OverriddenMethods{1}{$RemovedVFunc}}));
10621                    }
10622                    $VTableChanged_M{$ClassName}=1;
10623                    foreach my $AffectedInt (@Affected)
10624                    {
10625                        if($CompleteSignature{1}{$AffectedInt}{"PureVirt"})
10626                        { # affected exported methods only
10627                            next;
10628                        }
10629                        if(not symbolFilter($AffectedInt, 1, "Affected", $Level)) {
10630                            next;
10631                        }
10632                        %{$CompatProblems{$Level}{$AffectedInt}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
10633                            "Type_Name"=>$Class_Type{"Name"},
10634                            "Target"=>get_Signature($AddedVFunc, 2),
10635                            "Old_Value"=>get_Signature($RemovedVFunc, 1));
10636                    }
10637                }
10638            }
10639        }
10640    }
10641    if(not checkDump(1, "2.0")
10642    or not checkDump(2, "2.0"))
10643    { # support for old ABI dumps
10644      # "Base" attribute introduced in ACC 1.22 (ABI dump 2.0 format)
10645        return;
10646    }
10647    foreach my $ClassName (sort keys(%{$ClassNames{1}}))
10648    {
10649        my $ClassId_Old = $TName_Tid{1}{$ClassName};
10650        next if(not $ClassId_Old);
10651        if(not isCreatable($ClassId_Old, 1))
10652        { # skip classes without public constructors (including auto-generated)
10653          # example: class has only a private exported or private inline constructor
10654            next;
10655        }
10656        if($ClassName=~/>/)
10657        { # skip affected template instances
10658            next;
10659        }
10660        my %Class_Old = get_Type($ClassId_Old, 1);
10661        my $ClassId_New = $TName_Tid{2}{$ClassName};
10662        if(not $ClassId_New) {
10663            next;
10664        }
10665        my %Class_New = get_Type($ClassId_New, 2);
10666        if($Class_New{"Type"}!~/Class|Struct/)
10667        { # became typedef
10668            if($Level eq "Binary") {
10669                next;
10670            }
10671            if($Level eq "Source")
10672            {
10673                %Class_New = get_PureType($ClassId_New, $TypeInfo{2});
10674                if($Class_New{"Type"}!~/Class|Struct/) {
10675                    next;
10676                }
10677                $ClassId_New = $Class_New{"Tid"};
10678            }
10679        }
10680
10681        if(not $Class_New{"Size"} or not $Class_Old{"Size"})
10682        { # incomplete info in the ABI dump
10683            next;
10684        }
10685
10686
10687        my @Bases_Old = sort {$Class_Old{"Base"}{$a}{"pos"}<=>$Class_Old{"Base"}{$b}{"pos"}} keys(%{$Class_Old{"Base"}});
10688        my @Bases_New = sort {$Class_New{"Base"}{$a}{"pos"}<=>$Class_New{"Base"}{$b}{"pos"}} keys(%{$Class_New{"Base"}});
10689
10690        my %Tr_Old = map {$TypeInfo{1}{$_}{"Name"} => uncover_typedefs($TypeInfo{1}{$_}{"Name"}, 1)} @Bases_Old;
10691        my %Tr_New = map {$TypeInfo{2}{$_}{"Name"} => uncover_typedefs($TypeInfo{2}{$_}{"Name"}, 2)} @Bases_New;
10692
10693        my ($BNum1, $BNum2) = (1, 1);
10694        my %BasePos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @Bases_Old;
10695        my %BasePos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @Bases_New;
10696        my %ShortBase_Old = map {get_ShortClass($_, 1) => 1} @Bases_Old;
10697        my %ShortBase_New = map {get_ShortClass($_, 2) => 1} @Bases_New;
10698        my $Shift_Old = getShift($ClassId_Old, 1);
10699        my $Shift_New = getShift($ClassId_New, 2);
10700        my %BaseId_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @Bases_New;
10701        my ($Added, $Removed) = (0, 0);
10702        my @StableBases_Old = ();
10703        foreach my $BaseId (@Bases_Old)
10704        {
10705            my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
10706            if($BasePos_New{$Tr_Old{$BaseName}}) {
10707                push(@StableBases_Old, $BaseId);
10708            }
10709            elsif(not $ShortBase_New{$Tr_Old{$BaseName}}
10710            and not $ShortBase_New{get_ShortClass($BaseId, 1)})
10711            { # removed base
10712              # excluding namespace::SomeClass to SomeClass renaming
10713                my $ProblemKind = "Removed_Base_Class";
10714                if($Level eq "Binary")
10715                { # Binary-level
10716                    if($Shift_Old ne $Shift_New)
10717                    { # affected fields
10718                        if(havePubFields(\%Class_Old)) {
10719                            $ProblemKind .= "_And_Shift";
10720                        }
10721                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
10722                            $ProblemKind .= "_And_Size";
10723                        }
10724                    }
10725                    if(keys(%{$VirtualTable_Model{1}{$BaseName}})
10726                    and cmpVTables($ClassName)==1)
10727                    { # affected v-table
10728                        $ProblemKind .= "_And_VTable";
10729                        $VTableChanged_M{$ClassName}=1;
10730                    }
10731                }
10732                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
10733                foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
10734                {
10735                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
10736                    {
10737                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
10738                        if($ProblemKind=~/VTable/) {
10739                            $VTableChanged_M{$SubName}=1;
10740                        }
10741                    }
10742                }
10743                foreach my $Interface (@Affected)
10744                {
10745                    if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10746                        next;
10747                    }
10748                    %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
10749                        "Type_Name"=>$ClassName,
10750                        "Target"=>$BaseName,
10751                        "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
10752                        "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
10753                        "Shift"=>abs($Shift_New-$Shift_Old)  );
10754                }
10755                $Removed+=1;
10756            }
10757        }
10758        my @StableBases_New = ();
10759        foreach my $BaseId (@Bases_New)
10760        {
10761            my $BaseName = $TypeInfo{2}{$BaseId}{"Name"};
10762            if($BasePos_Old{$Tr_New{$BaseName}}) {
10763                push(@StableBases_New, $BaseId);
10764            }
10765            elsif(not $ShortBase_Old{$Tr_New{$BaseName}}
10766            and not $ShortBase_Old{get_ShortClass($BaseId, 2)})
10767            { # added base
10768              # excluding namespace::SomeClass to SomeClass renaming
10769                my $ProblemKind = "Added_Base_Class";
10770                if($Level eq "Binary")
10771                { # Binary-level
10772                    if($Shift_Old ne $Shift_New)
10773                    { # affected fields
10774                        if(havePubFields(\%Class_Old)) {
10775                            $ProblemKind .= "_And_Shift";
10776                        }
10777                        elsif($Class_Old{"Size"} ne $Class_New{"Size"}) {
10778                            $ProblemKind .= "_And_Size";
10779                        }
10780                    }
10781                    if(keys(%{$VirtualTable_Model{2}{$BaseName}})
10782                    and cmpVTables($ClassName)==1)
10783                    { # affected v-table
10784                        $ProblemKind .= "_And_VTable";
10785                        $VTableChanged_M{$ClassName}=1;
10786                    }
10787                }
10788                my @Affected = keys(%{$ClassMethods{$Level}{1}{$ClassName}});
10789                foreach my $SubId (get_sub_classes($ClassId_Old, 1, 1))
10790                {
10791                    if(my $SubName = $TypeInfo{1}{$SubId}{"Name"})
10792                    {
10793                        push(@Affected, keys(%{$ClassMethods{$Level}{1}{$SubName}}));
10794                        if($ProblemKind=~/VTable/) {
10795                            $VTableChanged_M{$SubName}=1;
10796                        }
10797                    }
10798                }
10799                foreach my $Interface (@Affected)
10800                {
10801                    if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10802                        next;
10803                    }
10804                    %{$CompatProblems{$Level}{$Interface}{$ProblemKind}{"this"}}=(
10805                        "Type_Name"=>$ClassName,
10806                        "Target"=>$BaseName,
10807                        "Old_Size"=>$Class_Old{"Size"}*$BYTE_SIZE,
10808                        "New_Size"=>$Class_New{"Size"}*$BYTE_SIZE,
10809                        "Shift"=>abs($Shift_New-$Shift_Old)  );
10810                }
10811                $Added+=1;
10812            }
10813        }
10814        if($Level eq "Binary")
10815        { # Binary-level
10816            ($BNum1, $BNum2) = (1, 1);
10817            my %BaseRelPos_Old = map {$Tr_Old{$TypeInfo{1}{$_}{"Name"}} => $BNum1++} @StableBases_Old;
10818            my %BaseRelPos_New = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $BNum2++} @StableBases_New;
10819            foreach my $BaseId (@Bases_Old)
10820            {
10821                my $BaseName = $TypeInfo{1}{$BaseId}{"Name"};
10822                if(my $NewPos = $BaseRelPos_New{$Tr_Old{$BaseName}})
10823                {
10824                    my $BaseNewId = $BaseId_New{$Tr_Old{$BaseName}};
10825                    my $OldPos = $BaseRelPos_Old{$Tr_Old{$BaseName}};
10826                    if($NewPos!=$OldPos)
10827                    { # changed position of the base class
10828                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10829                        {
10830                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10831                                next;
10832                            }
10833                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Position"}{"this"}}=(
10834                                "Type_Name"=>$ClassName,
10835                                "Target"=>$BaseName,
10836                                "Old_Value"=>$OldPos-1,
10837                                "New_Value"=>$NewPos-1  );
10838                        }
10839                    }
10840                    if($Class_Old{"Base"}{$BaseId}{"virtual"}
10841                    and not $Class_New{"Base"}{$BaseNewId}{"virtual"})
10842                    { # became non-virtual base
10843                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10844                        {
10845                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10846                                next;
10847                            }
10848                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Non_Virtually_Inherited"}{"this->".$BaseName}}=(
10849                                "Type_Name"=>$ClassName,
10850                                "Target"=>$BaseName  );
10851                        }
10852                    }
10853                    elsif(not $Class_Old{"Base"}{$BaseId}{"virtual"}
10854                    and $Class_New{"Base"}{$BaseNewId}{"virtual"})
10855                    { # became virtual base
10856                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10857                        {
10858                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10859                                next;
10860                            }
10861                            %{$CompatProblems{$Level}{$Interface}{"Base_Class_Became_Virtually_Inherited"}{"this->".$BaseName}}=(
10862                                "Type_Name"=>$ClassName,
10863                                "Target"=>$BaseName  );
10864                        }
10865                    }
10866                }
10867            }
10868            # detect size changes in base classes
10869            if($Shift_Old!=$Shift_New)
10870            { # size of allocable class
10871                foreach my $BaseId (@StableBases_Old)
10872                { # search for changed base
10873                    my %BaseType = get_Type($BaseId, 1);
10874                    my $Size_Old = $TypeInfo{1}{$BaseId}{"Size"};
10875                    my $Size_New = $TypeInfo{2}{$BaseId_New{$Tr_Old{$BaseType{"Name"}}}}{"Size"};
10876                    if($Size_Old ne $Size_New
10877                    and $Size_Old and $Size_New)
10878                    {
10879                        my $ProblemType = undef;
10880                        if(isCopyingClass($BaseId, 1)) {
10881                            $ProblemType = "Size_Of_Copying_Class";
10882                        }
10883                        elsif($AllocableClass{1}{$BaseType{"Name"}})
10884                        {
10885                            if($Size_New>$Size_Old)
10886                            { # increased size
10887                                $ProblemType = "Size_Of_Allocable_Class_Increased";
10888                            }
10889                            else
10890                            { # decreased size
10891                                $ProblemType = "Size_Of_Allocable_Class_Decreased";
10892                                if(not havePubFields(\%Class_Old))
10893                                { # affected class has no public members
10894                                    next;
10895                                }
10896                            }
10897                        }
10898                        next if(not $ProblemType);
10899                        foreach my $Interface (keys(%{$ClassMethods{$Level}{1}{$ClassName}}))
10900                        { # base class size changes affecting current class
10901                            if(not symbolFilter($Interface, 1, "Affected", $Level)) {
10902                                next;
10903                            }
10904                            %{$CompatProblems{$Level}{$Interface}{$ProblemType}{"this->".$BaseType{"Name"}}}=(
10905                                "Type_Name"=>$BaseType{"Name"},
10906                                "Target"=>$BaseType{"Name"},
10907                                "Old_Size"=>$Size_Old*$BYTE_SIZE,
10908                                "New_Size"=>$Size_New*$BYTE_SIZE  );
10909                        }
10910                    }
10911                }
10912            }
10913            if(defined $VirtualTable_Model{1}{$ClassName}
10914            and cmpVTables_Real($ClassName, 1)==1
10915            and my @VFunctions = keys(%{$VirtualTable_Model{1}{$ClassName}}))
10916            { # compare virtual tables size in base classes
10917                my $VShift_Old = getVShift($ClassId_Old, 1);
10918                my $VShift_New = getVShift($ClassId_New, 2);
10919                if($VShift_Old ne $VShift_New)
10920                { # changes in the base class or changes in the list of base classes
10921                    my @AllBases_Old = get_base_classes($ClassId_Old, 1, 1);
10922                    my @AllBases_New = get_base_classes($ClassId_New, 2, 1);
10923                    ($BNum1, $BNum2) = (1, 1);
10924                    my %StableBase = map {$Tr_New{$TypeInfo{2}{$_}{"Name"}} => $_} @AllBases_New;
10925                    foreach my $BaseId (@AllBases_Old)
10926                    {
10927                        my %BaseType = get_Type($BaseId, 1);
10928                        if(not $StableBase{$Tr_Old{$BaseType{"Name"}}})
10929                        { # lost base
10930                            next;
10931                        }
10932                        my $VSize_Old = getVTable_Size($BaseType{"Name"}, 1);
10933                        my $VSize_New = getVTable_Size($BaseType{"Name"}, 2);
10934                        if($VSize_Old!=$VSize_New)
10935                        {
10936                            foreach my $Symbol (@VFunctions)
10937                            { # TODO: affected non-virtual methods?
10938                                if(not defined $VirtualTable_Model{2}{$ClassName}{$Symbol})
10939                                { # Removed_Virtual_Method, will be registered in mergeVirtualTables()
10940                                    next;
10941                                }
10942                                if($VirtualTable_Model{2}{$ClassName}{$Symbol}-$VirtualTable_Model{1}{$ClassName}{$Symbol}==0)
10943                                { # skip interfaces that have not changed the absolute virtual position
10944                                    next;
10945                                }
10946                                if(not symbolFilter($Symbol, 1, "Affected", $Level)) {
10947                                    next;
10948                                }
10949                                $VTableChanged_M{$BaseType{"Name"}} = 1;
10950                                $VTableChanged_M{$ClassName} = 1;
10951                                foreach my $VirtFunc (keys(%{$AddedInt_Virt{$Level}{$BaseType{"Name"}}}))
10952                                { # the reason of the layout change: added virtual functions
10953                                    next if($VirtualReplacement{$VirtFunc});
10954                                    my $ProblemType = "Added_Virtual_Method";
10955                                    if($CompleteSignature{2}{$VirtFunc}{"PureVirt"}) {
10956                                        $ProblemType = "Added_Pure_Virtual_Method";
10957                                    }
10958                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 2)}}=(
10959                                        "Type_Name"=>$BaseType{"Name"},
10960                                        "Target"=>get_Signature($VirtFunc, 2)  );
10961                                }
10962                                foreach my $VirtFunc (keys(%{$RemovedInt_Virt{$Level}{$BaseType{"Name"}}}))
10963                                { # the reason of the layout change: removed virtual functions
10964                                    next if($VirtualReplacement{$VirtFunc});
10965                                    my $ProblemType = "Removed_Virtual_Method";
10966                                    if($CompleteSignature{1}{$VirtFunc}{"PureVirt"}) {
10967                                        $ProblemType = "Removed_Pure_Virtual_Method";
10968                                    }
10969                                    %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{get_Signature($VirtFunc, 1)}}=(
10970                                        "Type_Name"=>$BaseType{"Name"},
10971                                        "Target"=>get_Signature($VirtFunc, 1)  );
10972                                }
10973                            }
10974                        }
10975                    }
10976                }
10977            }
10978        }
10979    }
10980}
10981
10982sub isCreatable($$)
10983{
10984    my ($ClassId, $LibVersion) = @_;
10985    if($AllocableClass{$LibVersion}{$TypeInfo{$LibVersion}{$ClassId}{"Name"}}
10986    or isCopyingClass($ClassId, $LibVersion)) {
10987        return 1;
10988    }
10989    if(keys(%{$Class_SubClasses{$LibVersion}{$ClassId}}))
10990    { # Fix for incomplete data: if this class has
10991      # a base class then it should also has a constructor
10992        return 1;
10993    }
10994    if($ReturnedClass{$LibVersion}{$ClassId})
10995    { # returned by some method of this class
10996      # or any other class
10997        return 1;
10998    }
10999    return 0;
11000}
11001
11002sub isUsedClass($$$)
11003{
11004    my ($ClassId, $LibVersion, $Level) = @_;
11005    if(keys(%{$ParamClass{$LibVersion}{$ClassId}}))
11006    { # parameter of some exported method
11007        return 1;
11008    }
11009    my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
11010    if(keys(%{$ClassMethods{$Level}{$LibVersion}{$CName}}))
11011    { # method from target class
11012        return 1;
11013    }
11014    return 0;
11015}
11016
11017sub mergeVirtualTables($$)
11018{ # check for changes in the virtual table
11019    my ($Interface, $Level) = @_;
11020    # affected methods:
11021    #  - virtual
11022    #  - pure-virtual
11023    #  - non-virtual
11024    if($CompleteSignature{1}{$Interface}{"Data"})
11025    { # global data is not affected
11026        return;
11027    }
11028    my $Class_Id = $CompleteSignature{1}{$Interface}{"Class"};
11029    if(not $Class_Id) {
11030        return;
11031    }
11032    my $CName = $TypeInfo{1}{$Class_Id}{"Name"};
11033    if(cmpVTables_Real($CName, 1)==0)
11034    { # no changes
11035        return;
11036    }
11037    $CheckedTypes{$Level}{$CName} = 1;
11038    if($Level eq "Binary")
11039    { # Binary-level
11040        if($CompleteSignature{1}{$Interface}{"PureVirt"}
11041        and not isUsedClass($Class_Id, 1, $Level))
11042        { # pure virtuals should not be affected
11043          # if there are no exported methods using this class
11044            return;
11045        }
11046    }
11047    foreach my $Func (keys(%{$VirtualTable{1}{$CName}}))
11048    {
11049        if(defined $VirtualTable{2}{$CName}{$Func}
11050        and defined $CompleteSignature{2}{$Func})
11051        {
11052            if(not $CompleteSignature{1}{$Func}{"PureVirt"}
11053            and $CompleteSignature{2}{$Func}{"PureVirt"})
11054            { # became pure virtual
11055                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Pure"}{$tr_name{$Func}}}=(
11056                    "Type_Name"=>$CName,
11057                    "Target"=>get_Signature_M($Func, 1)  );
11058                $VTableChanged_M{$CName} = 1;
11059            }
11060            elsif($CompleteSignature{1}{$Func}{"PureVirt"}
11061            and not $CompleteSignature{2}{$Func}{"PureVirt"})
11062            { # became non-pure virtual
11063                %{$CompatProblems{$Level}{$Interface}{"Virtual_Method_Became_Non_Pure"}{$tr_name{$Func}}}=(
11064                    "Type_Name"=>$CName,
11065                    "Target"=>get_Signature_M($Func, 1)  );
11066                $VTableChanged_M{$CName} = 1;
11067            }
11068        }
11069    }
11070    if($Level eq "Binary")
11071    { # Binary-level
11072        # check virtual table structure
11073        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
11074        {
11075            next if($Interface eq $AddedVFunc);
11076            next if($VirtualReplacement{$AddedVFunc});
11077            my $VPos_Added = $VirtualTable{2}{$CName}{$AddedVFunc};
11078            if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"})
11079            { # pure virtual methods affect all others (virtual and non-virtual)
11080                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11081                    "Type_Name"=>$CName,
11082                    "Target"=>get_Signature($AddedVFunc, 2)  );
11083                $VTableChanged_M{$CName} = 1;
11084            }
11085            elsif(not defined $VirtualTable{1}{$CName}
11086            or $VPos_Added>keys(%{$VirtualTable{1}{$CName}}))
11087            { # added virtual function at the end of v-table
11088                if(not keys(%{$VirtualTable_Model{1}{$CName}}))
11089                { # became polymorphous class, added v-table pointer
11090                    %{$CompatProblems{$Level}{$Interface}{"Added_First_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11091                        "Type_Name"=>$CName,
11092                        "Target"=>get_Signature($AddedVFunc, 2)  );
11093                    $VTableChanged_M{$CName} = 1;
11094                }
11095                else
11096                {
11097                    my $VSize_Old = getVTable_Size($CName, 1);
11098                    my $VSize_New = getVTable_Size($CName, 2);
11099                    next if($VSize_Old==$VSize_New); # exception: register as removed and added virtual method
11100                    if(isCopyingClass($Class_Id, 1))
11101                    { # class has no constructors and v-table will be copied by applications, this may affect all methods
11102                        my $ProblemType = "Added_Virtual_Method";
11103                        if(isLeafClass($Class_Id, 1)) {
11104                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Copying_Class";
11105                        }
11106                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
11107                            "Type_Name"=>$CName,
11108                            "Target"=>get_Signature($AddedVFunc, 2)  );
11109                        $VTableChanged_M{$CName} = 1;
11110                    }
11111                    else
11112                    {
11113                        my $ProblemType = "Added_Virtual_Method";
11114                        if(isLeafClass($Class_Id, 1)) {
11115                            $ProblemType = "Added_Virtual_Method_At_End_Of_Leaf_Allocable_Class";
11116                        }
11117                        %{$CompatProblems{$Level}{$Interface}{$ProblemType}{$tr_name{$AddedVFunc}}}=(
11118                            "Type_Name"=>$CName,
11119                            "Target"=>get_Signature($AddedVFunc, 2)  );
11120                        $VTableChanged_M{$CName} = 1;
11121                    }
11122                }
11123            }
11124            elsif($CompleteSignature{1}{$Interface}{"Virt"}
11125            or $CompleteSignature{1}{$Interface}{"PureVirt"})
11126            {
11127                if(defined $VirtualTable{1}{$CName}
11128                and defined $VirtualTable{2}{$CName})
11129                {
11130                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
11131                    my $VPos_New = $VirtualTable{2}{$CName}{$Interface};
11132
11133                    if($VPos_Added<=$VPos_Old and $VPos_Old!=$VPos_New)
11134                    {
11135                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
11136                        foreach my $ASymbol (@Affected)
11137                        {
11138                            if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"})
11139                            {
11140                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
11141                                    next;
11142                                }
11143                            }
11144                            $CheckedSymbols{$Level}{$ASymbol} = 1;
11145                            %{$CompatProblems{$Level}{$ASymbol}{"Added_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11146                                "Type_Name"=>$CName,
11147                                "Target"=>get_Signature($AddedVFunc, 2)  );
11148                            $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
11149                        }
11150                    }
11151                }
11152            }
11153            else {
11154                # safe
11155            }
11156        }
11157        foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}}))
11158        {
11159            next if($VirtualReplacement{$RemovedVFunc});
11160            if($RemovedVFunc eq $Interface
11161            and $CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
11162            { # This case is for removed virtual methods
11163              # implemented in both versions of a library
11164                next;
11165            }
11166            if(not keys(%{$VirtualTable_Model{2}{$CName}}))
11167            { # became non-polymorphous class, removed v-table pointer
11168                %{$CompatProblems{$Level}{$Interface}{"Removed_Last_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=(
11169                    "Type_Name"=>$CName,
11170                    "Target"=>get_Signature($RemovedVFunc, 1)  );
11171                $VTableChanged_M{$CName} = 1;
11172            }
11173            elsif($CompleteSignature{1}{$Interface}{"Virt"}
11174            or $CompleteSignature{1}{$Interface}{"PureVirt"})
11175            {
11176                if(defined $VirtualTable{1}{$CName} and defined $VirtualTable{2}{$CName})
11177                {
11178                    if(not defined $VirtualTable{1}{$CName}{$Interface}) {
11179                        next;
11180                    }
11181                    my $VPos_New = -1;
11182                    if(defined $VirtualTable{2}{$CName}{$Interface})
11183                    {
11184                        $VPos_New = $VirtualTable{2}{$CName}{$Interface};
11185                    }
11186                    else
11187                    {
11188                        if($Interface ne $RemovedVFunc) {
11189                            next;
11190                        }
11191                    }
11192                    my $VPos_Removed = $VirtualTable{1}{$CName}{$RemovedVFunc};
11193                    my $VPos_Old = $VirtualTable{1}{$CName}{$Interface};
11194                    if($VPos_Removed<=$VPos_Old and $VPos_Old!=$VPos_New)
11195                    {
11196                        my @Affected = ($Interface, keys(%{$OverriddenMethods{1}{$Interface}}));
11197                        foreach my $ASymbol (@Affected)
11198                        {
11199                            if(not $CompleteSignature{1}{$ASymbol}{"PureVirt"})
11200                            {
11201                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
11202                                    next;
11203                                }
11204                            }
11205                            my $ProblemType = "Removed_Virtual_Method";
11206                            if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"}) {
11207                                $ProblemType = "Removed_Pure_Virtual_Method";
11208                            }
11209                            $CheckedSymbols{$Level}{$ASymbol} = 1;
11210                            %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$RemovedVFunc}}}=(
11211                                "Type_Name"=>$CName,
11212                                "Target"=>get_Signature($RemovedVFunc, 1)  );
11213                            $VTableChanged_M{$TypeInfo{1}{$CompleteSignature{1}{$ASymbol}{"Class"}}{"Name"}} = 1;
11214                        }
11215                    }
11216                }
11217            }
11218        }
11219    }
11220    else
11221    { # Source-level
11222        foreach my $AddedVFunc (keys(%{$AddedInt_Virt{$Level}{$CName}}))
11223        {
11224            next if($Interface eq $AddedVFunc);
11225            if($CompleteSignature{2}{$AddedVFunc}{"PureVirt"})
11226            {
11227                %{$CompatProblems{$Level}{$Interface}{"Added_Pure_Virtual_Method"}{$tr_name{$AddedVFunc}}}=(
11228                    "Type_Name"=>$CName,
11229                    "Target"=>get_Signature($AddedVFunc, 2)  );
11230            }
11231        }
11232        foreach my $RemovedVFunc (keys(%{$RemovedInt_Virt{$Level}{$CName}}))
11233        {
11234            if($CompleteSignature{1}{$RemovedVFunc}{"PureVirt"})
11235            {
11236                %{$CompatProblems{$Level}{$Interface}{"Removed_Pure_Virtual_Method"}{$tr_name{$RemovedVFunc}}}=(
11237                    "Type_Name"=>$CName,
11238                    "Target"=>get_Signature($RemovedVFunc, 1)  );
11239            }
11240        }
11241    }
11242}
11243
11244sub find_MemberPair_Pos_byName($$)
11245{
11246    my ($Member_Name, $Pair_Type) = @_;
11247    $Member_Name=~s/\A[_]+|[_]+\Z//g;
11248    foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
11249    {
11250        if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos})
11251        {
11252            my $Name = $Pair_Type->{"Memb"}{$MemberPair_Pos}{"name"};
11253            $Name=~s/\A[_]+|[_]+\Z//g;
11254            if($Name eq $Member_Name) {
11255                return $MemberPair_Pos;
11256            }
11257        }
11258    }
11259    return "lost";
11260}
11261
11262sub find_MemberPair_Pos_byVal($$)
11263{
11264    my ($Member_Value, $Pair_Type) = @_;
11265    foreach my $MemberPair_Pos (sort {int($a)<=>int($b)} keys(%{$Pair_Type->{"Memb"}}))
11266    {
11267        if(defined $Pair_Type->{"Memb"}{$MemberPair_Pos}
11268        and $Pair_Type->{"Memb"}{$MemberPair_Pos}{"value"} eq $Member_Value) {
11269            return $MemberPair_Pos;
11270        }
11271    }
11272    return "lost";
11273}
11274
11275sub isRecurType($$$)
11276{
11277    foreach (@{$_[2]})
11278    {
11279        if( $_->{"T1"} eq $_[0]
11280        and $_->{"T2"} eq $_[1] )
11281        {
11282            return 1;
11283        }
11284    }
11285    return 0;
11286}
11287
11288sub pushType($$$)
11289{
11290    my %IDs = (
11291        "T1" => $_[0],
11292        "T2" => $_[1]
11293    );
11294    push(@{$_[2]}, \%IDs);
11295}
11296
11297sub isRenamed($$$$$)
11298{
11299    my ($MemPos, $Type1, $LVersion1, $Type2, $LVersion2) = @_;
11300    my $Member_Name = $Type1->{"Memb"}{$MemPos}{"name"};
11301    my $MemberType_Id = $Type1->{"Memb"}{$MemPos}{"type"};
11302    my %MemberType_Pure = get_PureType($MemberType_Id, $TypeInfo{$LVersion1});
11303    if(not defined $Type2->{"Memb"}{$MemPos}) {
11304        return "";
11305    }
11306    my $PairType_Id = $Type2->{"Memb"}{$MemPos}{"type"};
11307    my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{$LVersion2});
11308
11309    my $Pair_Name = $Type2->{"Memb"}{$MemPos}{"name"};
11310    my $MemberPair_Pos_Rev = ($Member_Name eq $Pair_Name)?$MemPos:find_MemberPair_Pos_byName($Pair_Name, $Type1);
11311    if($MemberPair_Pos_Rev eq "lost")
11312    {
11313        if($MemberType_Pure{"Name"} eq $PairType_Pure{"Name"})
11314        { # base type match
11315            return $Pair_Name;
11316        }
11317        if($TypeInfo{$LVersion1}{$MemberType_Id}{"Name"} eq $TypeInfo{$LVersion2}{$PairType_Id}{"Name"})
11318        { # exact type match
11319            return $Pair_Name;
11320        }
11321        if($MemberType_Pure{"Size"} eq $PairType_Pure{"Size"})
11322        { # size match
11323            return $Pair_Name;
11324        }
11325        if(isReserved($Pair_Name))
11326        { # reserved fields
11327            return $Pair_Name;
11328        }
11329    }
11330    return "";
11331}
11332
11333sub isLastElem($$)
11334{
11335    my ($Pos, $TypeRef) = @_;
11336    my $Name = $TypeRef->{"Memb"}{$Pos}{"name"};
11337    if($Name=~/last|count|max|total/i)
11338    { # GST_LEVEL_COUNT, GST_RTSP_ELAST
11339        return 1;
11340    }
11341    elsif($Name=~/END|NLIMITS\Z/)
11342    { # __RLIMIT_NLIMITS
11343        return 1;
11344    }
11345    elsif($Name=~/\AN[A-Z](.+)[a-z]+s\Z/
11346    and $Pos+1==keys(%{$TypeRef->{"Memb"}}))
11347    { # NImageFormats, NColorRoles
11348        return 1;
11349    }
11350    return 0;
11351}
11352
11353sub nonComparable($$)
11354{
11355    my ($T1, $T2) = @_;
11356
11357    my $N1 = $T1->{"Name"};
11358    my $N2 = $T2->{"Name"};
11359
11360    $N1=~s/\A(struct|union|enum) //;
11361    $N2=~s/\A(struct|union|enum) //;
11362
11363    if($N1 ne $N2
11364    and not isAnon($N1)
11365    and not isAnon($N2))
11366    { # different names
11367        if($T1->{"Type"} ne "Pointer"
11368        or $T2->{"Type"} ne "Pointer")
11369        { # compare base types
11370            return 1;
11371        }
11372        if($N1!~/\Avoid\s*\*/
11373        and $N2=~/\Avoid\s*\*/)
11374        {
11375            return 1;
11376        }
11377    }
11378    elsif($T1->{"Type"} ne $T2->{"Type"})
11379    { # different types
11380        if($T1->{"Type"} eq "Class"
11381        and $T2->{"Type"} eq "Struct")
11382        { # "class" to "struct"
11383            return 0;
11384        }
11385        elsif($T2->{"Type"} eq "Class"
11386        and $T1->{"Type"} eq "Struct")
11387        { # "struct" to "class"
11388            return 0;
11389        }
11390        else
11391        { # "class" to "enum"
11392          # "union" to "class"
11393          #  ...
11394            return 1;
11395        }
11396    }
11397    return 0;
11398}
11399
11400sub isOpaque($)
11401{
11402    my $T = $_[0];
11403    if(not defined $T->{"Memb"})
11404    {
11405        return 1;
11406    }
11407    return 0;
11408}
11409
11410sub removeVPtr($)
11411{ # support for old ABI dumps
11412    my $TPtr = $_[0];
11413    my @Pos = sort {int($a)<=>int($b)} keys(%{$TPtr->{"Memb"}});
11414    if($#Pos>=1)
11415    {
11416        foreach my $Pos (0 .. $#Pos-1)
11417        {
11418            %{$TPtr->{"Memb"}{$Pos}} = %{$TPtr->{"Memb"}{$Pos+1}};
11419        }
11420        delete($TPtr->{"Memb"}{$#Pos});
11421    }
11422}
11423
11424sub mergeTypes($$$)
11425{
11426    my ($Type1_Id, $Type2_Id, $Level) = @_;
11427    return {} if(not $Type1_Id or not $Type2_Id);
11428
11429    if($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id})
11430    { # already merged
11431        return $Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id};
11432    }
11433
11434    my %Type1 = get_Type($Type1_Id, 1);
11435    my %Type2 = get_Type($Type2_Id, 2);
11436    if(not $Type1{"Name"} or not $Type2{"Name"}) {
11437        return {};
11438    }
11439
11440    $CheckedTypes{$Level}{$Type1{"Name"}} = 1;
11441    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
11442    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
11443
11444    $CheckedTypes{$Level}{$Type1_Pure{"Name"}} = 1;
11445
11446    my %SubProblems = ();
11447
11448    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
11449    {
11450        if($Type1_Pure{"Type"}=~/Struct|Union/
11451        and $Type2_Pure{"Type"}=~/Struct|Union/)
11452        {
11453            if(isOpaque(\%Type2_Pure) and not isOpaque(\%Type1_Pure))
11454            {
11455                %{$SubProblems{"Type_Became_Opaque"}{$Type1_Pure{"Name"}}}=(
11456                    "Target"=>$Type1_Pure{"Name"},
11457                    "Type_Name"=>$Type1_Pure{"Name"}  );
11458
11459                return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
11460            }
11461        }
11462    }
11463
11464    if(not $Type1_Pure{"Size"}
11465    or not $Type2_Pure{"Size"})
11466    { # including a case when "class Class { ... };" changed to "class Class;"
11467        if(not defined $Type1_Pure{"Memb"} or not defined $Type2_Pure{"Memb"}
11468        or index($Type1_Pure{"Name"}, "<")==-1 or index($Type2_Pure{"Name"}, "<")==-1)
11469        { # NOTE: template instances have no size
11470            return {};
11471        }
11472    }
11473    if(isRecurType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes))
11474    { # skip recursive declarations
11475        return {};
11476    }
11477    return {} if(not $Type1_Pure{"Name"} or not $Type2_Pure{"Name"});
11478    return {} if($SkipTypes{1}{$Type1_Pure{"Name"}});
11479    return {} if($SkipTypes{1}{$Type1{"Name"}});
11480
11481    if($Type1_Pure{"Type"}=~/Class|Struct/ and $Type2_Pure{"Type"}=~/Class|Struct/)
11482    { # support for old ABI dumps
11483      # _vptr field added in 3.0
11484        if(not checkDump(1, "3.0") and checkDump(2, "3.0"))
11485        {
11486            if(defined $Type2_Pure{"Memb"}
11487            and $Type2_Pure{"Memb"}{0}{"name"} eq "_vptr")
11488            {
11489                if(keys(%{$Type2_Pure{"Memb"}})==1) {
11490                    delete($Type2_Pure{"Memb"}{0});
11491                }
11492                else {
11493                    removeVPtr(\%Type2_Pure);
11494                }
11495            }
11496        }
11497        if(checkDump(1, "3.0") and not checkDump(2, "3.0"))
11498        {
11499            if(defined $Type1_Pure{"Memb"}
11500            and $Type1_Pure{"Memb"}{0}{"name"} eq "_vptr")
11501            {
11502                if(keys(%{$Type1_Pure{"Memb"}})==1) {
11503                    delete($Type1_Pure{"Memb"}{0});
11504                }
11505                else {
11506                    removeVPtr(\%Type1_Pure);
11507                }
11508            }
11509        }
11510    }
11511
11512    my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
11513    my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
11514
11515    if(%Typedef_1 and %Typedef_2
11516    and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef"
11517    and $Typedef_1{"Name"} eq $Typedef_2{"Name"})
11518    {
11519        my %Base_1 = get_OneStep_BaseType($Typedef_1{"Tid"}, $TypeInfo{1});
11520        my %Base_2 = get_OneStep_BaseType($Typedef_2{"Tid"}, $TypeInfo{2});
11521        if($Base_1{"Name"} ne $Base_2{"Name"})
11522        {
11523            if(differentDumps("G")
11524            or differentDumps("V"))
11525            { # different GCC versions or different dumps
11526                $Base_1{"Name"} = uncover_typedefs($Base_1{"Name"}, 1);
11527                $Base_2{"Name"} = uncover_typedefs($Base_2{"Name"}, 2);
11528                # std::__va_list and __va_list
11529                $Base_1{"Name"}=~s/\A(\w+::)+//;
11530                $Base_2{"Name"}=~s/\A(\w+::)+//;
11531                $Base_1{"Name"} = formatName($Base_1{"Name"}, "T");
11532                $Base_2{"Name"} = formatName($Base_2{"Name"}, "T");
11533            }
11534        }
11535        if($Base_1{"Name"}!~/anon\-/ and $Base_2{"Name"}!~/anon\-/
11536        and $Base_1{"Name"} ne $Base_2{"Name"})
11537        {
11538            if($Level eq "Binary"
11539            and $Type1{"Size"} and $Type2{"Size"}
11540            and $Type1{"Size"} ne $Type2{"Size"})
11541            {
11542                %{$SubProblems{"DataType_Size"}{$Typedef_1{"Name"}}}=(
11543                    "Target"=>$Typedef_1{"Name"},
11544                    "Type_Name"=>$Typedef_1{"Name"},
11545                    "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
11546                    "New_Size"=>$Type2{"Size"}*$BYTE_SIZE  );
11547            }
11548            my %Base1_Pure = get_PureType($Base_1{"Tid"}, $TypeInfo{1});
11549            my %Base2_Pure = get_PureType($Base_2{"Tid"}, $TypeInfo{2});
11550            if(tNameLock($Base_1{"Tid"}, $Base_2{"Tid"}))
11551            {
11552                if(diffTypes($Base1_Pure{"Tid"}, $Base2_Pure{"Tid"}, $Level))
11553                {
11554                    %{$SubProblems{"Typedef_BaseType_Format"}{$Typedef_1{"Name"}}}=(
11555                        "Target"=>$Typedef_1{"Name"},
11556                        "Type_Name"=>$Typedef_1{"Name"},
11557                        "Old_Value"=>$Base_1{"Name"},
11558                        "New_Value"=>$Base_2{"Name"}  );
11559                }
11560                else
11561                {
11562                    %{$SubProblems{"Typedef_BaseType"}{$Typedef_1{"Name"}}}=(
11563                        "Target"=>$Typedef_1{"Name"},
11564                        "Type_Name"=>$Typedef_1{"Name"},
11565                        "Old_Value"=>$Base_1{"Name"},
11566                        "New_Value"=>$Base_2{"Name"}  );
11567                }
11568            }
11569        }
11570    }
11571    if(nonComparable(\%Type1_Pure, \%Type2_Pure))
11572    { # different types (reported in detectTypeChange(...))
11573        my $TT1 = $Type1_Pure{"Type"};
11574        my $TT2 = $Type2_Pure{"Type"};
11575
11576        if($TT1 ne $TT2
11577        and $TT1!~/Intrinsic|Pointer|Ref|Typedef/)
11578        { # different type of the type
11579            my $Short1 = $Type1_Pure{"Name"};
11580            my $Short2 = $Type2_Pure{"Name"};
11581
11582            $Short1=~s/\A\Q$TT1\E //ig;
11583            $Short2=~s/\A\Q$TT2\E //ig;
11584
11585            if($Short1 eq $Short2)
11586            {
11587                %{$SubProblems{"DataType_Type"}{$Type1_Pure{"Name"}}}=(
11588                    "Target"=>$Type1_Pure{"Name"},
11589                    "Type_Name"=>$Type1_Pure{"Name"},
11590                    "Old_Value"=>lc($Type1_Pure{"Type"}),
11591                    "New_Value"=>lc($Type2_Pure{"Type"})  );
11592            }
11593        }
11594        return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
11595    }
11596    pushType($Type1_Pure{"Tid"}, $Type2_Pure{"Tid"}, \@RecurTypes);
11597    if(($Type1_Pure{"Name"} eq $Type2_Pure{"Name"}
11598    or (isAnon($Type1_Pure{"Name"}) and isAnon($Type2_Pure{"Name"})))
11599    and $Type1_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11600    { # checking size
11601        if($Level eq "Binary"
11602        and $Type1_Pure{"Size"} and $Type2_Pure{"Size"}
11603        and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11604        {
11605            my $ProblemKind = "DataType_Size";
11606            if($Type1_Pure{"Type"} eq "Class"
11607            and keys(%{$ClassMethods{$Level}{1}{$Type1_Pure{"Name"}}}))
11608            {
11609                if(isCopyingClass($Type1_Pure{"Tid"}, 1)) {
11610                    $ProblemKind = "Size_Of_Copying_Class";
11611                }
11612                elsif($AllocableClass{1}{$Type1_Pure{"Name"}})
11613                {
11614                    if(int($Type2_Pure{"Size"})>int($Type1_Pure{"Size"})) {
11615                        $ProblemKind = "Size_Of_Allocable_Class_Increased";
11616                    }
11617                    else
11618                    {
11619                        # descreased size of allocable class
11620                        # it has no special effects
11621                    }
11622                }
11623            }
11624            %{$SubProblems{$ProblemKind}{$Type1_Pure{"Name"}}}=(
11625                "Target"=>$Type1_Pure{"Name"},
11626                "Type_Name"=>$Type1_Pure{"Name"},
11627                "Old_Size"=>$Type1_Pure{"Size"}*$BYTE_SIZE,
11628                "New_Size"=>$Type2_Pure{"Size"}*$BYTE_SIZE);
11629        }
11630    }
11631    if(defined $Type1_Pure{"BaseType"}
11632    and defined $Type2_Pure{"BaseType"})
11633    { # checking base types
11634        my $Sub_SubProblems = mergeTypes($Type1_Pure{"BaseType"}, $Type2_Pure{"BaseType"}, $Level);
11635        foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems}))
11636        {
11637            foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}})) {
11638                $SubProblems{$Sub_SubProblemType}{$Sub_SubLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
11639            }
11640        }
11641    }
11642    my (%AddedField, %RemovedField, %RenamedField, %RenamedField_Rev, %RelatedField, %RelatedField_Rev) = ();
11643    my %NameToPosA = map {$Type1_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type1_Pure{"Memb"}});
11644    my %NameToPosB = map {$Type2_Pure{"Memb"}{$_}{"name"}=>$_} keys(%{$Type2_Pure{"Memb"}});
11645    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11646    { # detect removed and renamed fields
11647        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11648        next if(not $Member_Name);
11649        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);
11650        if($MemberPair_Pos eq "lost")
11651        {
11652            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11653            {
11654                if(isUnnamed($Member_Name))
11655                { # support for old-version dumps
11656                  # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
11657                    if(not checkDump(2, "2.1")) {
11658                        next;
11659                    }
11660                }
11661                if(my $RenamedTo = isRenamed($Member_Pos, \%Type1_Pure, 1, \%Type2_Pure, 2))
11662                { # renamed
11663                    $RenamedField{$Member_Pos} = $RenamedTo;
11664                    $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
11665                }
11666                else
11667                { # removed
11668                    $RemovedField{$Member_Pos} = 1;
11669                }
11670            }
11671            elsif($Type1_Pure{"Type"} eq "Enum")
11672            {
11673                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
11674                next if($Member_Value1 eq "");
11675                $MemberPair_Pos = find_MemberPair_Pos_byVal($Member_Value1, \%Type2_Pure);
11676                if($MemberPair_Pos ne "lost")
11677                { # renamed
11678                    my $RenamedTo = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"name"};
11679                    my $MemberPair_Pos_Rev = find_MemberPair_Pos_byName($RenamedTo, \%Type1_Pure);
11680                    if($MemberPair_Pos_Rev eq "lost")
11681                    {
11682                        $RenamedField{$Member_Pos} = $RenamedTo;
11683                        $RenamedField_Rev{$NameToPosB{$RenamedTo}} = $Member_Name;
11684                    }
11685                    else {
11686                        $RemovedField{$Member_Pos} = 1;
11687                    }
11688                }
11689                else
11690                { # removed
11691                    $RemovedField{$Member_Pos} = 1;
11692                }
11693            }
11694        }
11695        else
11696        { # related
11697            $RelatedField{$Member_Pos} = $MemberPair_Pos;
11698            $RelatedField_Rev{$MemberPair_Pos} = $Member_Pos;
11699        }
11700    }
11701    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
11702    { # detect added fields
11703        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
11704        next if(not $Member_Name);
11705        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);
11706        if($MemberPair_Pos eq "lost")
11707        {
11708            if(isUnnamed($Member_Name))
11709            { # support for old-version dumps
11710            # unnamed fields have been introduced in the ACC 1.23 (dump 2.1 format)
11711                if(not checkDump(1, "2.1")) {
11712                    next;
11713                }
11714            }
11715            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union|Enum)\Z/)
11716            {
11717                if(not $RenamedField_Rev{$Member_Pos})
11718                { # added
11719                    $AddedField{$Member_Pos}=1;
11720                }
11721            }
11722        }
11723    }
11724    if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
11725    { # detect moved fields
11726        my (%RelPos, %RelPosName, %AbsPos) = ();
11727        my $Pos = 0;
11728        foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11729        { # relative positions in 1st version
11730            my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11731            next if(not $Member_Name);
11732            if(not $RemovedField{$Member_Pos})
11733            { # old type without removed fields
11734                $RelPos{1}{$Member_Name} = $Pos;
11735                $RelPosName{1}{$Pos} = $Member_Name;
11736                $AbsPos{1}{$Pos++} = $Member_Pos;
11737            }
11738        }
11739        $Pos = 0;
11740        foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
11741        { # relative positions in 2nd version
11742            my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
11743            next if(not $Member_Name);
11744            if(not $AddedField{$Member_Pos})
11745            { # new type without added fields
11746                $RelPos{2}{$Member_Name} = $Pos;
11747                $RelPosName{2}{$Pos} = $Member_Name;
11748                $AbsPos{2}{$Pos++} = $Member_Pos;
11749            }
11750        }
11751        foreach my $Member_Name (keys(%{$RelPos{1}}))
11752        {
11753            my $RPos1 = $RelPos{1}{$Member_Name};
11754            my $AbsPos1 = $NameToPosA{$Member_Name};
11755            my $Member_Name2 = $Member_Name;
11756            if(my $RenamedTo = $RenamedField{$AbsPos1})
11757            { # renamed
11758                $Member_Name2 = $RenamedTo;
11759            }
11760            my $RPos2 = $RelPos{2}{$Member_Name2};
11761            if($RPos2 ne "" and $RPos1 ne $RPos2)
11762            { # different relative positions
11763                my $AbsPos2 = $NameToPosB{$Member_Name2};
11764                if($AbsPos1 ne $AbsPos2)
11765                { # different absolute positions
11766                    my $ProblemType = "Moved_Field";
11767                    if(not isPublic(\%Type1_Pure, $AbsPos1))
11768                    { # may change layout and size of type
11769                        if($Level eq "Source") {
11770                            next;
11771                        }
11772                        $ProblemType = "Moved_Private_Field";
11773                    }
11774                    if($Level eq "Binary"
11775                    and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11776                    { # affected size
11777                        my $MemSize1 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$AbsPos1}{"type"}}{"Size"};
11778                        my $MovedAbsPos = $AbsPos{1}{$RPos2};
11779                        my $MemSize2 = $TypeInfo{1}{$Type1_Pure{"Memb"}{$MovedAbsPos}{"type"}}{"Size"};
11780                        if($MemSize1 ne $MemSize2) {
11781                            $ProblemType .= "_And_Size";
11782                        }
11783                    }
11784                    if($ProblemType eq "Moved_Private_Field") {
11785                        next;
11786                    }
11787                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
11788                        "Target"=>$Member_Name,
11789                        "Type_Name"=>$Type1_Pure{"Name"},
11790                        "Old_Value"=>$RPos1,
11791                        "New_Value"=>$RPos2 );
11792                }
11793            }
11794        }
11795    }
11796    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type1_Pure{"Memb"}}))
11797    { # check older fields, public and private
11798        my $Member_Name = $Type1_Pure{"Memb"}{$Member_Pos}{"name"};
11799        next if(not $Member_Name);
11800        next if($Member_Name eq "_vptr");
11801        if(my $RenamedTo = $RenamedField{$Member_Pos})
11802        { # renamed
11803            if(defined $Constants{2}{$Member_Name})
11804            {
11805                if($Constants{2}{$Member_Name}{"Value"} eq $RenamedTo)
11806                { # define OLD NEW
11807                    next; # Safe
11808                }
11809            }
11810
11811            if($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11812            {
11813                if(isPublic(\%Type1_Pure, $Member_Pos))
11814                {
11815                    %{$SubProblems{"Renamed_Field"}{$Member_Name}}=(
11816                        "Target"=>$Member_Name,
11817                        "Type_Name"=>$Type1_Pure{"Name"},
11818                        "Old_Value"=>$Member_Name,
11819                        "New_Value"=>$RenamedTo  );
11820                }
11821                elsif(isReserved($Member_Name))
11822                {
11823                    %{$SubProblems{"Used_Reserved_Field"}{$Member_Name}}=(
11824                        "Target"=>$Member_Name,
11825                        "Type_Name"=>$Type1_Pure{"Name"},
11826                        "Old_Value"=>$Member_Name,
11827                        "New_Value"=>$RenamedTo  );
11828                }
11829            }
11830            elsif($Type1_Pure{"Type"} eq "Enum")
11831            {
11832                %{$SubProblems{"Enum_Member_Name"}{$Type1_Pure{"Memb"}{$Member_Pos}{"value"}}}=(
11833                    "Target"=>$Type1_Pure{"Memb"}{$Member_Pos}{"value"},
11834                    "Type_Name"=>$Type1_Pure{"Name"},
11835                    "Old_Value"=>$Member_Name,
11836                    "New_Value"=>$RenamedTo  );
11837            }
11838        }
11839        elsif($RemovedField{$Member_Pos})
11840        { # removed
11841            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
11842            {
11843                my $ProblemType = "Removed_Field";
11844                if(not isPublic(\%Type1_Pure, $Member_Pos)
11845                or isUnnamed($Member_Name))
11846                {
11847                    if($Level eq "Source") {
11848                        next;
11849                    }
11850                    $ProblemType = "Removed_Private_Field";
11851                }
11852                if($Level eq "Binary"
11853                and not isMemPadded($Member_Pos, -1, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
11854                {
11855                    if(my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
11856                    { # affected fields
11857                        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}))
11858                        { # changed offset
11859                            $ProblemType .= "_And_Layout";
11860                        }
11861                    }
11862                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11863                    { # affected size
11864                        $ProblemType .= "_And_Size";
11865                    }
11866                }
11867                if($ProblemType eq "Removed_Private_Field") {
11868                    next;
11869                }
11870                %{$SubProblems{$ProblemType}{$Member_Name}}=(
11871                    "Target"=>$Member_Name,
11872                    "Type_Name"=>$Type1_Pure{"Name"}  );
11873            }
11874            elsif($Type2_Pure{"Type"} eq "Union")
11875            {
11876                if($Level eq "Binary"
11877                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
11878                {
11879                    %{$SubProblems{"Removed_Union_Field_And_Size"}{$Member_Name}}=(
11880                        "Target"=>$Member_Name,
11881                        "Type_Name"=>$Type1_Pure{"Name"}  );
11882                }
11883                else
11884                {
11885                    %{$SubProblems{"Removed_Union_Field"}{$Member_Name}}=(
11886                        "Target"=>$Member_Name,
11887                        "Type_Name"=>$Type1_Pure{"Name"}  );
11888                }
11889            }
11890            elsif($Type1_Pure{"Type"} eq "Enum")
11891            {
11892                %{$SubProblems{"Enum_Member_Removed"}{$Member_Name}}=(
11893                    "Target"=>$Member_Name,
11894                    "Type_Name"=>$Type1_Pure{"Name"},
11895                    "Old_Value"=>$Member_Name  );
11896            }
11897        }
11898        else
11899        { # changed
11900            my $MemberPair_Pos = $RelatedField{$Member_Pos};
11901            if($Type1_Pure{"Type"} eq "Enum")
11902            {
11903                my $Member_Value1 = $Type1_Pure{"Memb"}{$Member_Pos}{"value"};
11904                next if($Member_Value1 eq "");
11905                my $Member_Value2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"value"};
11906                next if($Member_Value2 eq "");
11907                if($Member_Value1 ne $Member_Value2)
11908                {
11909                    my $ProblemType = "Enum_Member_Value";
11910                    if(isLastElem($Member_Pos, \%Type1_Pure)) {
11911                        $ProblemType = "Enum_Last_Member_Value";
11912                    }
11913                    if($SkipConstants{1}{$Member_Name}) {
11914                        $ProblemType = "Enum_Private_Member_Value";
11915                    }
11916                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
11917                        "Target"=>$Member_Name,
11918                        "Type_Name"=>$Type1_Pure{"Name"},
11919                        "Old_Value"=>$Member_Value1,
11920                        "New_Value"=>$Member_Value2  );
11921                }
11922            }
11923            elsif($Type2_Pure{"Type"}=~/\A(Struct|Class|Union)\Z/)
11924            {
11925                my $Access1 = $Type1_Pure{"Memb"}{$Member_Pos}{"access"};
11926                my $Access2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"access"};
11927
11928                if($Access1 ne "private"
11929                and $Access2 eq "private")
11930                {
11931                    %{$SubProblems{"Field_Became_Private"}{$Member_Name}}=(
11932                        "Target"=>$Member_Name,
11933                        "Type_Name"=>$Type1_Pure{"Name"});
11934                }
11935                elsif($Access1 ne "protected"
11936                and $Access1 ne "private"
11937                and $Access2 eq "protected")
11938                {
11939                    %{$SubProblems{"Field_Became_Protected"}{$Member_Name}}=(
11940                        "Target"=>$Member_Name,
11941                        "Type_Name"=>$Type1_Pure{"Name"});
11942                }
11943
11944                my $MemberType1_Id = $Type1_Pure{"Memb"}{$Member_Pos}{"type"};
11945                my $MemberType2_Id = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"type"};
11946                my $SizeV1 = $TypeInfo{1}{$MemberType1_Id}{"Size"}*$BYTE_SIZE;
11947                if(my $BSize1 = $Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}) {
11948                    $SizeV1 = $BSize1;
11949                }
11950                my $SizeV2 = $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE;
11951                if(my $BSize2 = $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}) {
11952                    $SizeV2 = $BSize2;
11953                }
11954                my $MemberType1_Name = $TypeInfo{1}{$MemberType1_Id}{"Name"};
11955                my $MemberType2_Name = $TypeInfo{2}{$MemberType2_Id}{"Name"};
11956                if($Level eq "Binary"
11957                and $SizeV1 and $SizeV2
11958                and $SizeV1 ne $SizeV2)
11959                {
11960                    if($MemberType1_Name eq $MemberType2_Name or (isAnon($MemberType1_Name) and isAnon($MemberType2_Name))
11961                    or ($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"} and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"}))
11962                    { # field size change (including anon-structures and unions)
11963                      # - same types
11964                      # - unnamed types
11965                      # - bitfields
11966                        my $ProblemType = "Field_Size";
11967                        if(not isPublic(\%Type1_Pure, $Member_Pos)
11968                        or isUnnamed($Member_Name))
11969                        { # should not be accessed by applications, goes to "Low Severity"
11970                          # example: "abidata" members in GStreamer types
11971                            $ProblemType = "Private_".$ProblemType;
11972                        }
11973                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
11974                        { # check an effect
11975                            if($Type2_Pure{"Type"} ne "Union"
11976                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
11977                            { # public fields after the current
11978                                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}))
11979                                { # changed offset
11980                                    $ProblemType .= "_And_Layout";
11981                                }
11982                            }
11983                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
11984                                $ProblemType .= "_And_Type_Size";
11985                            }
11986                        }
11987                        if($ProblemType eq "Private_Field_Size")
11988                        { # private field size with no effect
11989                        }
11990                        if($ProblemType eq "Field_Size")
11991                        {
11992                            if($Type1_Pure{"Type"}=~/Union|Struct/ and $SizeV1<$SizeV2)
11993                            { # Low severity
11994                                $ProblemType = "Struct_Field_Size_Increased";
11995                            }
11996                        }
11997                        if($ProblemType)
11998                        { # register a problem
11999                            %{$SubProblems{$ProblemType}{$Member_Name}}=(
12000                                "Target"=>$Member_Name,
12001                                "Type_Name"=>$Type1_Pure{"Name"},
12002                                "Old_Size"=>$SizeV1,
12003                                "New_Size"=>$SizeV2);
12004                        }
12005                    }
12006                }
12007                if($Type1_Pure{"Memb"}{$Member_Pos}{"bitfield"}
12008                or $Type2_Pure{"Memb"}{$MemberPair_Pos}{"bitfield"})
12009                { # do NOT check bitfield type changes
12010                    next;
12011                }
12012                if(checkDump(1, "2.13") and checkDump(2, "2.13"))
12013                {
12014                    if(not $Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
12015                    and $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
12016                    {
12017                        %{$SubProblems{"Field_Became_Mutable"}{$Member_Name}}=(
12018                            "Target"=>$Member_Name,
12019                            "Type_Name"=>$Type1_Pure{"Name"});
12020                    }
12021                    elsif($Type1_Pure{"Memb"}{$Member_Pos}{"mutable"}
12022                    and not $Type2_Pure{"Memb"}{$MemberPair_Pos}{"mutable"})
12023                    {
12024                        %{$SubProblems{"Field_Became_Non_Mutable"}{$Member_Name}}=(
12025                            "Target"=>$Member_Name,
12026                            "Type_Name"=>$Type1_Pure{"Name"});
12027                    }
12028                }
12029                my %Sub_SubChanges = detectTypeChange($MemberType1_Id, $MemberType2_Id, "Field", $Level);
12030                foreach my $ProblemType (keys(%Sub_SubChanges))
12031                {
12032                    my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
12033                    my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
12034
12035                    # quals
12036                    if($ProblemType eq "Field_Type"
12037                    or $ProblemType eq "Field_Type_And_Size"
12038                    or $ProblemType eq "Field_Type_Format")
12039                    {
12040                        if(checkDump(1, "2.6") and checkDump(2, "2.6"))
12041                        {
12042                            if(addedQual($Old_Value, $New_Value, "volatile")) {
12043                                %{$Sub_SubChanges{"Field_Became_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
12044                            }
12045                            elsif(removedQual($Old_Value, $New_Value, "volatile")) {
12046                                %{$Sub_SubChanges{"Field_Became_Non_Volatile"}} = %{$Sub_SubChanges{$ProblemType}};
12047                            }
12048                        }
12049                        if(my $RA = addedQual($Old_Value, $New_Value, "const"))
12050                        {
12051                            if($RA==2) {
12052                                %{$Sub_SubChanges{"Field_Added_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12053                            }
12054                            else {
12055                                %{$Sub_SubChanges{"Field_Became_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12056                            }
12057                        }
12058                        elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
12059                        {
12060                            if($RR==2) {
12061                                %{$Sub_SubChanges{"Field_Removed_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12062                            }
12063                            else {
12064                                %{$Sub_SubChanges{"Field_Became_Non_Const"}} = %{$Sub_SubChanges{$ProblemType}};
12065                            }
12066                        }
12067                    }
12068                }
12069
12070                if($Level eq "Source")
12071                {
12072                    foreach my $ProblemType (keys(%Sub_SubChanges))
12073                    {
12074                        my $Old_Value = $Sub_SubChanges{$ProblemType}{"Old_Value"};
12075                        my $New_Value = $Sub_SubChanges{$ProblemType}{"New_Value"};
12076
12077                        if($ProblemType eq "Field_Type")
12078                        {
12079                            if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
12080                                delete($Sub_SubChanges{$ProblemType});
12081                            }
12082                        }
12083                    }
12084                }
12085
12086                foreach my $ProblemType (keys(%Sub_SubChanges))
12087                {
12088                    my $ProblemType_Init = $ProblemType;
12089                    if($ProblemType eq "Field_Type_And_Size")
12090                    { # Binary
12091                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12092                        or isUnnamed($Member_Name)) {
12093                            $ProblemType = "Private_".$ProblemType;
12094                        }
12095                        if(not isMemPadded($Member_Pos, $TypeInfo{2}{$MemberType2_Id}{"Size"}*$BYTE_SIZE, \%Type1_Pure, \%RemovedField, $TypeInfo{1}, getArch(1), $WORD_SIZE{1}))
12096                        { # check an effect
12097                            if($Type2_Pure{"Type"} ne "Union"
12098                            and my $MNum = isAccessible(\%Type1_Pure, \%RemovedField, $Member_Pos+1, -1))
12099                            { # public fields after the current
12100                                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}))
12101                                { # changed offset
12102                                    $ProblemType .= "_And_Layout";
12103                                }
12104                            }
12105                            if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12106                                $ProblemType .= "_And_Type_Size";
12107                            }
12108                        }
12109                    }
12110                    else
12111                    {
12112                        # TODO: Private_Field_Type rule?
12113
12114                        if(not isPublic(\%Type1_Pure, $Member_Pos)
12115                        or isUnnamed($Member_Name)) {
12116                            next;
12117                        }
12118                    }
12119                    if($ProblemType eq "Private_Field_Type_And_Size")
12120                    { # private field change with no effect
12121                    }
12122                    %{$SubProblems{$ProblemType}{$Member_Name}}=(
12123                        "Target"=>$Member_Name,
12124                        "Type_Name"=>$Type1_Pure{"Name"});
12125
12126                    foreach my $Attr (keys(%{$Sub_SubChanges{$ProblemType_Init}}))
12127                    { # other properties
12128                        $SubProblems{$ProblemType}{$Member_Name}{$Attr} = $Sub_SubChanges{$ProblemType_Init}{$Attr};
12129                    }
12130                }
12131                if(not isPublic(\%Type1_Pure, $Member_Pos))
12132                { # do NOT check internal type changes
12133                    next;
12134                }
12135                if($MemberType1_Id and $MemberType2_Id)
12136                { # checking member type changes
12137                    my $Sub_SubProblems = mergeTypes($MemberType1_Id, $MemberType2_Id, $Level);
12138
12139                    my %DupProblems = ();
12140
12141                    foreach my $Sub_SubProblemType (keys(%{$Sub_SubProblems}))
12142                    {
12143                        foreach my $Sub_SubLocation (keys(%{$Sub_SubProblems->{$Sub_SubProblemType}}))
12144                        {
12145                            if(not defined $AllAffected)
12146                            {
12147                                if(defined $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}}) {
12148                                    next;
12149                                }
12150                            }
12151
12152                            my $NewLocation = ($Sub_SubLocation)?$Member_Name."->".$Sub_SubLocation:$Member_Name;
12153                            $SubProblems{$Sub_SubProblemType}{$NewLocation} = $Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation};
12154
12155                            if(not defined $AllAffected)
12156                            {
12157                                $DupProblems{$Sub_SubProblems->{$Sub_SubProblemType}{$Sub_SubLocation}} = 1;
12158                            }
12159                        }
12160                    }
12161
12162                    %DupProblems = ();
12163                }
12164            }
12165        }
12166    }
12167    foreach my $Member_Pos (sort {int($a) <=> int($b)} keys(%{$Type2_Pure{"Memb"}}))
12168    { # checking added members, public and private
12169        my $Member_Name = $Type2_Pure{"Memb"}{$Member_Pos}{"name"};
12170        next if(not $Member_Name);
12171        next if($Member_Name eq "_vptr");
12172        if($AddedField{$Member_Pos})
12173        { # added
12174            if($Type2_Pure{"Type"}=~/\A(Struct|Class)\Z/)
12175            {
12176                my $ProblemType = "Added_Field";
12177                if(not isPublic(\%Type2_Pure, $Member_Pos)
12178                or isUnnamed($Member_Name))
12179                {
12180                    if($Level eq "Source") {
12181                        next;
12182                    }
12183                    $ProblemType = "Added_Private_Field";
12184                }
12185                if($Level eq "Binary"
12186                and not isMemPadded($Member_Pos, -1, \%Type2_Pure, \%AddedField, $TypeInfo{2}, getArch(2), $WORD_SIZE{2}))
12187                {
12188                    if(my $MNum = isAccessible(\%Type2_Pure, \%AddedField, $Member_Pos, -1))
12189                    { # public fields after the current
12190                        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}))
12191                        { # changed offset
12192                            $ProblemType .= "_And_Layout";
12193                        }
12194                    }
12195                    if($Type1_Pure{"Size"} ne $Type2_Pure{"Size"}) {
12196                        $ProblemType .= "_And_Size";
12197                    }
12198                }
12199                if($ProblemType eq "Added_Private_Field")
12200                { # skip added private fields
12201                    next;
12202                }
12203                %{$SubProblems{$ProblemType}{$Member_Name}}=(
12204                    "Target"=>$Member_Name,
12205                    "Type_Name"=>$Type1_Pure{"Name"});
12206            }
12207            elsif($Type2_Pure{"Type"} eq "Union")
12208            {
12209                if($Level eq "Binary"
12210                and $Type1_Pure{"Size"} ne $Type2_Pure{"Size"})
12211                {
12212                    %{$SubProblems{"Added_Union_Field_And_Size"}{$Member_Name}}=(
12213                        "Target"=>$Member_Name,
12214                        "Type_Name"=>$Type1_Pure{"Name"});
12215                }
12216                else
12217                {
12218                    %{$SubProblems{"Added_Union_Field"}{$Member_Name}}=(
12219                        "Target"=>$Member_Name,
12220                        "Type_Name"=>$Type1_Pure{"Name"});
12221                }
12222            }
12223            elsif($Type2_Pure{"Type"} eq "Enum")
12224            {
12225                my $Member_Value = $Type2_Pure{"Memb"}{$Member_Pos}{"value"};
12226                next if($Member_Value eq "");
12227                %{$SubProblems{"Added_Enum_Member"}{$Member_Name}}=(
12228                    "Target"=>$Member_Name,
12229                    "Type_Name"=>$Type2_Pure{"Name"},
12230                    "New_Value"=>$Member_Value);
12231            }
12232        }
12233    }
12234
12235    pop(@RecurTypes);
12236    return ($Cache{"mergeTypes"}{$Level}{$Type1_Id}{$Type2_Id} = \%SubProblems);
12237}
12238
12239sub isUnnamed($) {
12240    return $_[0]=~/\Aunnamed\d+\Z/;
12241}
12242
12243sub get_ShortClass($$)
12244{
12245    my ($TypeId, $LibVersion) = @_;
12246    my $TypeName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
12247    if($TypeInfo{$LibVersion}{$TypeId}{"Type"}!~/Intrinsic|Class|Struct|Union|Enum/) {
12248        $TypeName = uncover_typedefs($TypeName, $LibVersion);
12249    }
12250    if(my $NameSpace = $TypeInfo{$LibVersion}{$TypeId}{"NameSpace"}) {
12251        $TypeName=~s/\A(struct |)\Q$NameSpace\E\:\://g;
12252    }
12253    return $TypeName;
12254}
12255
12256sub goToFirst($$$)
12257{
12258    my ($TypeId, $LibVersion, $Type_Type) = @_;
12259    return () if(not $TypeId);
12260    if(defined $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}) {
12261        return %{$Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type}};
12262    }
12263    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12264    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12265    return () if(not $Type{"Type"});
12266    if($Type{"Type"} ne $Type_Type)
12267    {
12268        return () if(not $Type{"BaseType"});
12269        %Type = goToFirst($Type{"BaseType"}, $LibVersion, $Type_Type);
12270    }
12271    $Cache{"goToFirst"}{$TypeId}{$LibVersion}{$Type_Type} = \%Type;
12272    return %Type;
12273}
12274
12275my %TypeSpecAttributes = (
12276    "Const" => 1,
12277    "Volatile" => 1,
12278    "ConstVolatile" => 1,
12279    "Restrict" => 1,
12280    "Typedef" => 1
12281);
12282
12283sub get_PureType($$)
12284{
12285    my ($TypeId, $Info) = @_;
12286    if(not $TypeId or not $Info
12287    or not $Info->{$TypeId}) {
12288        return ();
12289    }
12290    if(defined $Cache{"get_PureType"}{$TypeId}{$Info}) {
12291        return %{$Cache{"get_PureType"}{$TypeId}{$Info}};
12292    }
12293    my %Type = %{$Info->{$TypeId}};
12294    return %Type if(not $Type{"BaseType"});
12295    if($TypeSpecAttributes{$Type{"Type"}}) {
12296        %Type = get_PureType($Type{"BaseType"}, $Info);
12297    }
12298    $Cache{"get_PureType"}{$TypeId}{$Info} = \%Type;
12299    return %Type;
12300}
12301
12302sub get_PLevel($$)
12303{
12304    my ($TypeId, $LibVersion) = @_;
12305    return 0 if(not $TypeId);
12306    if(defined $Cache{"get_PLevel"}{$TypeId}{$LibVersion}) {
12307        return $Cache{"get_PLevel"}{$TypeId}{$LibVersion};
12308    }
12309    return 0 if(not $TypeInfo{$LibVersion}{$TypeId});
12310    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12311    return 1 if($Type{"Type"}=~/FuncPtr|FieldPtr/);
12312    my $PLevel = 0;
12313    if($Type{"Type"} =~/Pointer|Ref|FuncPtr|FieldPtr/) {
12314        $PLevel += 1;
12315    }
12316    return $PLevel if(not $Type{"BaseType"});
12317    $PLevel += get_PLevel($Type{"BaseType"}, $LibVersion);
12318    $Cache{"get_PLevel"}{$TypeId}{$LibVersion} = $PLevel;
12319    return $PLevel;
12320}
12321
12322sub get_BaseType($$)
12323{
12324    my ($TypeId, $LibVersion) = @_;
12325    return () if(not $TypeId);
12326    if(defined $Cache{"get_BaseType"}{$TypeId}{$LibVersion}) {
12327        return %{$Cache{"get_BaseType"}{$TypeId}{$LibVersion}};
12328    }
12329    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12330    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12331    return %Type if(not $Type{"BaseType"});
12332    %Type = get_BaseType($Type{"BaseType"}, $LibVersion);
12333    $Cache{"get_BaseType"}{$TypeId}{$LibVersion} = \%Type;
12334    return %Type;
12335}
12336
12337sub get_BaseTypeQual($$)
12338{
12339    my ($TypeId, $LibVersion) = @_;
12340    return "" if(not $TypeId);
12341    return "" if(not $TypeInfo{$LibVersion}{$TypeId});
12342    my %Type = %{$TypeInfo{$LibVersion}{$TypeId}};
12343    return "" if(not $Type{"BaseType"});
12344    my $Qual = "";
12345    if($Type{"Type"} eq "Pointer") {
12346        $Qual .= "*";
12347    }
12348    elsif($Type{"Type"} eq "Ref") {
12349        $Qual .= "&";
12350    }
12351    elsif($Type{"Type"} eq "ConstVolatile") {
12352        $Qual .= "const volatile";
12353    }
12354    elsif($Type{"Type"} eq "Const"
12355    or $Type{"Type"} eq "Volatile"
12356    or $Type{"Type"} eq "Restrict") {
12357        $Qual .= lc($Type{"Type"});
12358    }
12359    my $BQual = get_BaseTypeQual($Type{"BaseType"}, $LibVersion);
12360    return $BQual.$Qual;
12361}
12362
12363sub get_OneStep_BaseType($$)
12364{
12365    my ($TypeId, $Info) = @_;
12366    if(not $TypeId or not $Info
12367    or not $Info->{$TypeId}) {
12368        return ();
12369    }
12370    my %Type = %{$Info->{$TypeId}};
12371    return %Type if(not $Type{"BaseType"});
12372    if(my $BTid = $Type{"BaseType"})
12373    {
12374        if($Info->{$BTid}) {
12375            return %{$Info->{$BTid}};
12376        }
12377        else { # something is going wrong
12378            return ();
12379        }
12380    }
12381    else {
12382        return %Type;
12383    }
12384}
12385
12386sub get_Type($$)
12387{
12388    my ($TypeId, $LibVersion) = @_;
12389    return () if(not $TypeId);
12390    return () if(not $TypeInfo{$LibVersion}{$TypeId});
12391    return %{$TypeInfo{$LibVersion}{$TypeId}};
12392}
12393
12394sub isPrivateData($)
12395{ # non-public global data
12396    my $Symbol = $_[0];
12397    return ($Symbol=~/\A(_ZGV|_ZTI|_ZTS|_ZTT|_ZTV|_ZTC|_ZThn|_ZTv0_n)/);
12398}
12399
12400sub isInLineInst($$$) {
12401    return (isTemplateInstance(@_) and not isTemplateSpec(@_));
12402}
12403
12404sub isTemplateInstance($$$)
12405{
12406    my ($Symbol, $SInfo, $LibVersion) = @_;
12407    if($CheckObjectsOnly)
12408    {
12409        if($Symbol!~/\A(_Z|\?)/) {
12410            return 0;
12411        }
12412        if(my $Signature = $tr_name{$Symbol})
12413        {
12414            if(index($Signature,">")==-1) {
12415                return 0;
12416            }
12417            if(my $ShortName = substr($Signature, 0, find_center($Signature, "(")))
12418            {
12419                if(index($ShortName,"<")!=-1
12420                and index($ShortName,">")!=-1) {
12421                    return 1;
12422                }
12423            }
12424        }
12425    }
12426    else
12427    {
12428        if(my $ClassId = $SInfo->{"Class"})
12429        {
12430            if(my $ClassName = $TypeInfo{$LibVersion}{$ClassId}{"Name"})
12431            {
12432                if(index($ClassName,"<")!=-1) {
12433                    return 1;
12434                }
12435            }
12436        }
12437        if(my $ShortName = $SInfo->{"ShortName"})
12438        {
12439            if(index($ShortName,"<")!=-1
12440            and index($ShortName,">")!=-1) {
12441                return 1;
12442            }
12443        }
12444    }
12445    return 0;
12446}
12447
12448sub isTemplateSpec($$$)
12449{
12450    my ($Symbol, $SInfo, $LibVersion) = @_;
12451    if(my $ClassId = $SInfo->{"Class"})
12452    {
12453        if($TypeInfo{$LibVersion}{$ClassId}{"Spec"})
12454        { # class specialization
12455            return 1;
12456        }
12457        elsif($SInfo->{"Spec"})
12458        { # method specialization
12459            return 1;
12460        }
12461    }
12462    return 0;
12463}
12464
12465sub symbolFilter($$$$)
12466{ # some special cases when the symbol cannot be imported
12467    my ($Symbol, $LibVersion, $Type, $Level) = @_;
12468    if(isPrivateData($Symbol))
12469    { # non-public global data
12470        return 0;
12471    }
12472
12473    if(defined $SkipInternal)
12474    {
12475        return 0 if($Symbol=~/($SkipInternal)/);
12476    }
12477
12478    if($CheckObjectsOnly) {
12479        return 0 if($Symbol=~/\A(_init|_fini)\Z/);
12480    }
12481    if($CheckHeadersOnly and not checkDump($LibVersion, "2.7"))
12482    { # support for old ABI dumps in --headers-only mode
12483        foreach my $Pos (keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
12484        {
12485            if(my $Pid = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$Pos}{"type"})
12486            {
12487                my $PType = $TypeInfo{$LibVersion}{$Pid}{"Type"};
12488                if(not $PType or $PType eq "Unknown") {
12489                    return 0;
12490                }
12491            }
12492        }
12493    }
12494    if($Type=~/Affected/)
12495    {
12496        if($SkipSymbols{$LibVersion}{$Symbol})
12497        { # user defined symbols to ignore
12498            return 0;
12499        }
12500        if($SymbolsListPath and not $SymbolsList{$Symbol})
12501        { # user defined symbols
12502            return 0;
12503        }
12504        if($AppPath and not $SymbolsList_App{$Symbol})
12505        { # user defined symbols (in application)
12506            return 0;
12507        }
12508
12509        my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"};
12510
12511        my $NameSpace = $CompleteSignature{$LibVersion}{$Symbol}{"NameSpace"};
12512        if(not $NameSpace and $ClassId)
12513        { # class methods have no "NameSpace" attribute
12514            $NameSpace = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"};
12515        }
12516        if($NameSpace)
12517        { # user defined namespaces to ignore
12518            if($SkipNameSpaces{$LibVersion}{$NameSpace}) {
12519                return 0;
12520            }
12521            foreach my $NS (keys(%{$SkipNameSpaces{$LibVersion}}))
12522            { # nested namespaces
12523                if($NameSpace=~/\A\Q$NS\E(\:\:|\Z)/) {
12524                    return 0;
12525                }
12526            }
12527        }
12528        if(my $Header = $CompleteSignature{$LibVersion}{$Symbol}{"Header"})
12529        {
12530            if(my $Skip = skipHeader($Header, $LibVersion))
12531            { # --skip-headers or <skip_headers> (not <skip_including>)
12532                if($Skip==1) {
12533                    return 0;
12534                }
12535            }
12536        }
12537        if($TypesListPath and $ClassId)
12538        { # user defined types
12539            my $CName = $TypeInfo{$LibVersion}{$ClassId}{"Name"};
12540
12541            if(not $TypesList{$CName})
12542            {
12543                if(my $NS = $TypeInfo{$LibVersion}{$ClassId}{"NameSpace"})
12544                {
12545                    $CName=~s/\A\Q$NS\E\:\://g;
12546                }
12547
12548                if(not $TypesList{$CName})
12549                {
12550                    my $Found = 0;
12551
12552                    while($CName=~s/\:\:.+?\Z//)
12553                    {
12554                        if($TypesList{$CName})
12555                        {
12556                            $Found = 1;
12557                            last;
12558                        }
12559                    }
12560
12561                    if(not $Found) {
12562                        return 0;
12563                    }
12564                }
12565            }
12566        }
12567
12568        if(not selectSymbol($Symbol, $CompleteSignature{$LibVersion}{$Symbol}, $Level, $LibVersion))
12569        { # non-target symbols
12570            return 0;
12571        }
12572        if($Level eq "Binary")
12573        {
12574            if($CheckObjectsOnly)
12575            {
12576                if(isTemplateInstance($Symbol, $CompleteSignature{$LibVersion}{$Symbol}, $LibVersion)) {
12577                    return 0;
12578                }
12579            }
12580            else
12581            {
12582                if($CompleteSignature{$LibVersion}{$Symbol}{"InLine"}
12583                or isInLineInst($Symbol, $CompleteSignature{$LibVersion}{$Symbol}, $LibVersion))
12584                {
12585                    if($ClassId and $CompleteSignature{$LibVersion}{$Symbol}{"Virt"})
12586                    { # inline virtual methods
12587                        if($Type=~/InlineVirt/) {
12588                            return 1;
12589                        }
12590                        my $Allocable = (not isCopyingClass($ClassId, $LibVersion));
12591                        if(not $Allocable)
12592                        { # check bases
12593                            foreach my $DCId (get_sub_classes($ClassId, $LibVersion, 1))
12594                            {
12595                                if(not isCopyingClass($DCId, $LibVersion))
12596                                { # exists a derived class without default c-tor
12597                                    $Allocable=1;
12598                                    last;
12599                                }
12600                            }
12601                        }
12602                        if(not $Allocable) {
12603                            return 0;
12604                        }
12605                    }
12606                    else
12607                    { # inline non-virtual methods
12608                        return 0;
12609                    }
12610                }
12611            }
12612        }
12613    }
12614    return 1;
12615}
12616
12617sub detectAdded($)
12618{
12619    my $Level = $_[0];
12620    foreach my $Symbol (keys(%{$Symbol_Library{2}}))
12621    {
12622        if(link_symbol($Symbol, 1, "+Deps"))
12623        { # linker can find a new symbol
12624          # in the old-version library
12625          # So, it's not a new symbol
12626            next;
12627        }
12628        if(my $VSym = $SymVer{2}{$Symbol}
12629        and index($Symbol,"\@")==-1) {
12630            next;
12631        }
12632        $AddedInt{$Level}{$Symbol} = 1;
12633    }
12634}
12635
12636sub detectRemoved($)
12637{
12638    my $Level = $_[0];
12639    foreach my $Symbol (keys(%{$Symbol_Library{1}}))
12640    {
12641        if($CheckObjectsOnly) {
12642            $CheckedSymbols{"Binary"}{$Symbol} = 1;
12643        }
12644        if(link_symbol($Symbol, 2, "+Deps"))
12645        { # linker can find an old symbol
12646          # in the new-version library
12647            next;
12648        }
12649        if(my $VSym = $SymVer{1}{$Symbol}
12650        and index($Symbol,"\@")==-1) {
12651            next;
12652        }
12653        $RemovedInt{$Level}{$Symbol} = 1;
12654    }
12655}
12656
12657sub mergeLibs($)
12658{
12659    my $Level = $_[0];
12660    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
12661    { # checking added symbols
12662        next if($CompleteSignature{2}{$Symbol}{"Private"});
12663        next if(not $CompleteSignature{2}{$Symbol}{"Header"} and not $CheckObjectsOnly);
12664        next if(not symbolFilter($Symbol, 2, "Affected + InlineVirt", $Level));
12665        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=();
12666    }
12667    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
12668    { # checking removed symbols
12669        next if($CompleteSignature{1}{$Symbol}{"Private"});
12670        next if(not $CompleteSignature{1}{$Symbol}{"Header"} and not $CheckObjectsOnly);
12671        if(index($Symbol, "_ZTV")==0)
12672        { # skip v-tables for templates, that should not be imported by applications
12673            next if($tr_name{$Symbol}=~/</);
12674            if(my $CName = $VTableClass{$Symbol})
12675            {
12676                if(not keys(%{$ClassMethods{$Level}{1}{$CName}}))
12677                { # vtables for "private" classes
12678                  # use case: vtable for QDragManager (Qt 4.5.3 to 4.6.0) became HIDDEN symbol
12679                    next;
12680                }
12681            }
12682        }
12683        else {
12684            next if(not symbolFilter($Symbol, 1, "Affected + InlineVirt", $Level));
12685        }
12686        if($CompleteSignature{1}{$Symbol}{"PureVirt"})
12687        { # symbols for pure virtual methods cannot be called by clients
12688            next;
12689        }
12690        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=();
12691    }
12692}
12693
12694sub checkDump($$)
12695{
12696    my ($LibVersion, $V) = @_;
12697    if(defined $Cache{"checkDump"}{$LibVersion}{$V}) {
12698        return $Cache{"checkDump"}{$LibVersion}{$V};
12699    }
12700    return ($Cache{"checkDump"}{$LibVersion}{$V} = (not $UsedDump{$LibVersion}{"V"} or cmpVersions($UsedDump{$LibVersion}{"V"}, $V)>=0));
12701}
12702
12703sub detectAdded_H($)
12704{
12705    my $Level = $_[0];
12706    foreach my $Symbol (sort keys(%{$CompleteSignature{2}}))
12707    {
12708        if($Level eq "Source")
12709        { # remove symbol version
12710            my ($SN, $SS, $SV) = separate_symbol($Symbol);
12711            $Symbol=$SN;
12712
12713            if($CompleteSignature{2}{$Symbol}{"Artificial"})
12714            { # skip artificial constructors
12715                next;
12716            }
12717        }
12718        if(not $CompleteSignature{2}{$Symbol}{"Header"}
12719        or not $CompleteSignature{2}{$Symbol}{"MnglName"}) {
12720            next;
12721        }
12722        if($ExtendedSymbols{$Symbol}) {
12723            next;
12724        }
12725        if(not defined $CompleteSignature{1}{$Symbol}
12726        or not $CompleteSignature{1}{$Symbol}{"MnglName"})
12727        {
12728            if($UsedDump{2}{"SrcBin"})
12729            {
12730                if($UsedDump{1}{"BinOnly"} or not checkDump(1, "2.11"))
12731                { # support for old and different (!) ABI dumps
12732                    if(not $CompleteSignature{2}{$Symbol}{"Virt"}
12733                    and not $CompleteSignature{2}{$Symbol}{"PureVirt"})
12734                    {
12735                        if($CheckHeadersOnly)
12736                        {
12737                            if(my $Lang = $CompleteSignature{2}{$Symbol}{"Lang"})
12738                            {
12739                                if($Lang eq "C")
12740                                { # support for old ABI dumps: missed extern "C" functions
12741                                    next;
12742                                }
12743                            }
12744                        }
12745                        else
12746                        {
12747                            if(not link_symbol($Symbol, 2, "-Deps"))
12748                            { # skip added inline symbols and const global data
12749                                next;
12750                            }
12751                        }
12752                    }
12753                }
12754            }
12755            $AddedInt{$Level}{$Symbol} = 1;
12756        }
12757    }
12758}
12759
12760sub detectRemoved_H($)
12761{
12762    my $Level = $_[0];
12763    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
12764    {
12765        if($Level eq "Source")
12766        { # remove symbol version
12767            my ($SN, $SS, $SV) = separate_symbol($Symbol);
12768            $Symbol=$SN;
12769        }
12770        if(not $CompleteSignature{1}{$Symbol}{"Header"}
12771        or not $CompleteSignature{1}{$Symbol}{"MnglName"}) {
12772            next;
12773        }
12774        if($ExtendedSymbols{$Symbol}) {
12775            next;
12776        }
12777        if(not defined $CompleteSignature{2}{$Symbol}
12778        or not $CompleteSignature{2}{$Symbol}{"MnglName"})
12779        {
12780            if($UsedDump{1}{"SrcBin"})
12781            {
12782                if($UsedDump{2}{"BinOnly"} or not checkDump(2, "2.11"))
12783                { # support for old and different (!) ABI dumps
12784                    if(not $CompleteSignature{1}{$Symbol}{"Virt"}
12785                    and not $CompleteSignature{1}{$Symbol}{"PureVirt"})
12786                    {
12787                        if($CheckHeadersOnly)
12788                        { # skip all removed symbols
12789                            if(my $Lang = $CompleteSignature{1}{$Symbol}{"Lang"})
12790                            {
12791                                if($Lang eq "C")
12792                                { # support for old ABI dumps: missed extern "C" functions
12793                                    next;
12794                                }
12795                            }
12796                        }
12797                        else
12798                        {
12799                            if(not link_symbol($Symbol, 1, "-Deps"))
12800                            { # skip removed inline symbols
12801                                next;
12802                            }
12803                        }
12804                    }
12805                }
12806            }
12807            if(not checkDump(1, "2.15"))
12808            {
12809                if($Symbol=~/_IT_E\Z/)
12810                { # _ZN28QExplicitlySharedDataPointerI22QSslCertificatePrivateEC1IT_EERKS_IT_E
12811                    next;
12812                }
12813            }
12814            if(not $CompleteSignature{1}{$Symbol}{"Class"})
12815            {
12816                if(my $Short = $CompleteSignature{1}{$Symbol}{"ShortName"})
12817                {
12818                    if(defined $Constants{2}{$Short})
12819                    {
12820                        my $Val = $Constants{2}{$Short}{"Value"};
12821                        if(defined $Func_ShortName{2}{$Val})
12822                        { # old name defined to new
12823                            next;
12824                        }
12825                    }
12826                }
12827
12828            }
12829            $RemovedInt{$Level}{$Symbol} = 1;
12830            if($Level eq "Source")
12831            { # search for a source-compatible equivalent
12832                setAlternative($Symbol, $Level);
12833            }
12834        }
12835    }
12836}
12837
12838sub mergeHeaders($)
12839{
12840    my $Level = $_[0];
12841    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
12842    { # checking added symbols
12843        next if($CompleteSignature{2}{$Symbol}{"PureVirt"});
12844        next if($CompleteSignature{2}{$Symbol}{"Private"});
12845        next if(not symbolFilter($Symbol, 2, "Affected", $Level));
12846        if($Level eq "Binary")
12847        {
12848            if($CompleteSignature{2}{$Symbol}{"InLine"})
12849            {
12850                if(not $CompleteSignature{2}{$Symbol}{"Virt"})
12851                { # skip inline non-virtual functions
12852                    next;
12853                }
12854            }
12855        }
12856        else
12857        { # Source
12858            if($SourceAlternative_B{$Symbol}) {
12859                next;
12860            }
12861        }
12862        %{$CompatProblems{$Level}{$Symbol}{"Added_Symbol"}{""}}=();
12863    }
12864    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
12865    { # checking removed symbols
12866        next if($CompleteSignature{1}{$Symbol}{"PureVirt"});
12867        next if($CompleteSignature{1}{$Symbol}{"Private"});
12868        next if(not symbolFilter($Symbol, 1, "Affected", $Level));
12869        if($Level eq "Binary")
12870        {
12871            if($CompleteSignature{1}{$Symbol}{"InLine"})
12872            {
12873                if(not $CompleteSignature{1}{$Symbol}{"Virt"})
12874                { # skip inline non-virtual functions
12875                    next;
12876                }
12877            }
12878        }
12879        else
12880        { # Source
12881            if(my $Alt = $SourceAlternative{$Symbol})
12882            {
12883                if(defined $CompleteSignature{1}{$Alt}
12884                and $CompleteSignature{1}{$Symbol}{"Const"})
12885                {
12886                    my $Cid = $CompleteSignature{1}{$Symbol}{"Class"};
12887                    %{$CompatProblems{$Level}{$Symbol}{"Removed_Const_Overload"}{"this"}}=(
12888                        "Type_Name"=>$TypeInfo{1}{$Cid}{"Name"},
12889                        "Target"=>get_Signature($Alt, 1));
12890                }
12891                else
12892                { # do NOT show removed symbol
12893                    next;
12894                }
12895            }
12896        }
12897        %{$CompatProblems{$Level}{$Symbol}{"Removed_Symbol"}{""}}=();
12898    }
12899}
12900
12901sub addParamNames($)
12902{
12903    my $LibraryVersion = $_[0];
12904    return if(not keys(%AddIntParams));
12905    my $SecondVersion = $LibraryVersion==1?2:1;
12906    foreach my $Interface (sort keys(%{$CompleteSignature{$LibraryVersion}}))
12907    {
12908        next if(not keys(%{$AddIntParams{$Interface}}));
12909        foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibraryVersion}{$Interface}{"Param"}}))
12910        { # add absent parameter names
12911            my $ParamName = $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"};
12912            if($ParamName=~/\Ap\d+\Z/ and my $NewParamName = $AddIntParams{$Interface}{$ParamPos})
12913            { # names from the external file
12914                if(defined $CompleteSignature{$SecondVersion}{$Interface}
12915                and defined $CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos})
12916                {
12917                    if($CompleteSignature{$SecondVersion}{$Interface}{"Param"}{$ParamPos}{"name"}=~/\Ap\d+\Z/) {
12918                        $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
12919                    }
12920                }
12921                else {
12922                    $CompleteSignature{$LibraryVersion}{$Interface}{"Param"}{$ParamPos}{"name"} = $NewParamName;
12923                }
12924            }
12925        }
12926    }
12927}
12928
12929sub detectChangedTypedefs()
12930{ # detect changed typedefs to show
12931  # correct function signatures
12932    foreach my $Typedef (keys(%{$Typedef_BaseName{1}}))
12933    {
12934        next if(not $Typedef);
12935        my $BName1 = $Typedef_BaseName{1}{$Typedef};
12936        if(not $BName1 or isAnon($BName1)) {
12937            next;
12938        }
12939        my $BName2 = $Typedef_BaseName{2}{$Typedef};
12940        if(not $BName2 or isAnon($BName2)) {
12941            next;
12942        }
12943        if($BName1 ne $BName2) {
12944            $ChangedTypedef{$Typedef} = 1;
12945        }
12946    }
12947}
12948
12949sub get_symbol_suffix($$)
12950{
12951    my ($Symbol, $Full) = @_;
12952    my ($SN, $SO, $SV) = separate_symbol($Symbol);
12953    $Symbol=$SN; # remove version
12954    my $Signature = $tr_name{$Symbol};
12955    my $Suffix = substr($Signature, find_center($Signature, "("));
12956    if(not $Full) {
12957        $Suffix=~s/(\))\s*(const volatile|volatile const|const|volatile)\Z/$1/g;
12958    }
12959    return $Suffix;
12960}
12961
12962sub get_symbol_prefix($$)
12963{
12964    my ($Symbol, $LibVersion) = @_;
12965    my $ShortName = $CompleteSignature{$LibVersion}{$Symbol}{"ShortName"};
12966    if(my $ClassId = $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
12967    { # methods
12968        $ShortName = $TypeInfo{$LibVersion}{$ClassId}{"Name"}."::".$ShortName;
12969    }
12970    return $ShortName;
12971}
12972
12973sub setAlternative($)
12974{
12975    my $Symbol = $_[0];
12976    my $PSymbol = $Symbol;
12977    if(not defined $CompleteSignature{2}{$PSymbol}
12978    or (not $CompleteSignature{2}{$PSymbol}{"MnglName"}
12979    and not $CompleteSignature{2}{$PSymbol}{"ShortName"}))
12980    { # search for a pair
12981        if(my $ShortName = $CompleteSignature{1}{$PSymbol}{"ShortName"})
12982        {
12983            if($CompleteSignature{1}{$PSymbol}{"Data"})
12984            {
12985                if($PSymbol=~s/L(\d+$ShortName(E)\Z)/$1/
12986                or $PSymbol=~s/(\d+$ShortName(E)\Z)/L$1/)
12987                {
12988                    if(defined $CompleteSignature{2}{$PSymbol}
12989                    and $CompleteSignature{2}{$PSymbol}{"MnglName"})
12990                    {
12991                        $SourceAlternative{$Symbol} = $PSymbol;
12992                        $SourceAlternative_B{$PSymbol} = $Symbol;
12993                        if(not defined $CompleteSignature{1}{$PSymbol}
12994                        or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) {
12995                            $SourceReplacement{$Symbol} = $PSymbol;
12996                        }
12997                    }
12998                }
12999            }
13000            else
13001            {
13002                foreach my $Sp ("KV", "VK", "K", "V")
13003                {
13004                    if($PSymbol=~s/\A_ZN$Sp/_ZN/
13005                    or $PSymbol=~s/\A_ZN/_ZN$Sp/)
13006                    {
13007                        if(defined $CompleteSignature{2}{$PSymbol}
13008                        and $CompleteSignature{2}{$PSymbol}{"MnglName"})
13009                        {
13010                            $SourceAlternative{$Symbol} = $PSymbol;
13011                            $SourceAlternative_B{$PSymbol} = $Symbol;
13012                            if(not defined $CompleteSignature{1}{$PSymbol}
13013                            or not $CompleteSignature{1}{$PSymbol}{"MnglName"}) {
13014                                $SourceReplacement{$Symbol} = $PSymbol;
13015                            }
13016                        }
13017                    }
13018                    $PSymbol = $Symbol;
13019                }
13020            }
13021        }
13022    }
13023    return "";
13024}
13025
13026sub getSymKind($$)
13027{
13028    my ($Symbol, $LibVersion) = @_;
13029    if($CompleteSignature{$LibVersion}{$Symbol}{"Data"})
13030    {
13031        return "Global_Data";
13032    }
13033    elsif($CompleteSignature{$LibVersion}{$Symbol}{"Class"})
13034    {
13035        return "Method";
13036    }
13037    return "Function";
13038}
13039
13040sub mergeSymbols($)
13041{
13042    my $Level = $_[0];
13043    my %SubProblems = ();
13044
13045    mergeBases($Level);
13046
13047    my %AddedOverloads = ();
13048    foreach my $Symbol (sort keys(%{$AddedInt{$Level}}))
13049    { # check all added exported symbols
13050        if(not $CompleteSignature{2}{$Symbol}{"Header"}) {
13051            next;
13052        }
13053        if(defined $CompleteSignature{1}{$Symbol}
13054        and $CompleteSignature{1}{$Symbol}{"Header"})
13055        { # double-check added symbol
13056            next;
13057        }
13058        if(not symbolFilter($Symbol, 2, "Affected", $Level)) {
13059            next;
13060        }
13061        if($Symbol=~/\A(_Z|\?)/)
13062        { # C++
13063            $AddedOverloads{get_symbol_prefix($Symbol, 2)}{get_symbol_suffix($Symbol, 1)} = $Symbol;
13064        }
13065        if(my $OverriddenMethod = $CompleteSignature{2}{$Symbol}{"Override"})
13066        { # register virtual overridings
13067            my $Cid = $CompleteSignature{2}{$Symbol}{"Class"};
13068            my $AffectedClass_Name = $TypeInfo{2}{$Cid}{"Name"};
13069            if(defined $CompleteSignature{1}{$OverriddenMethod} and $CompleteSignature{1}{$OverriddenMethod}{"Virt"}
13070            and not $CompleteSignature{1}{$OverriddenMethod}{"Private"})
13071            {
13072                if($TName_Tid{1}{$AffectedClass_Name})
13073                { # class should exist in previous version
13074                    if(not isCopyingClass($TName_Tid{1}{$AffectedClass_Name}, 1))
13075                    { # old v-table is NOT copied by old applications
13076                        %{$CompatProblems{$Level}{$OverriddenMethod}{"Overridden_Virtual_Method"}{$tr_name{$Symbol}}}=(
13077                            "Type_Name"=>$AffectedClass_Name,
13078                            "Target"=>get_Signature($Symbol, 2),
13079                            "Old_Value"=>get_Signature($OverriddenMethod, 2),
13080                            "New_Value"=>get_Signature($Symbol, 2));
13081                    }
13082                }
13083            }
13084        }
13085    }
13086    foreach my $Symbol (sort keys(%{$RemovedInt{$Level}}))
13087    { # check all removed exported symbols
13088        if(not $CompleteSignature{1}{$Symbol}{"Header"}) {
13089            next;
13090        }
13091        if(defined $CompleteSignature{2}{$Symbol}
13092        and $CompleteSignature{2}{$Symbol}{"Header"})
13093        { # double-check removed symbol
13094            next;
13095        }
13096        if($CompleteSignature{1}{$Symbol}{"Private"})
13097        { # skip private methods
13098            next;
13099        }
13100        if(not symbolFilter($Symbol, 1, "Affected", $Level)) {
13101            next;
13102        }
13103        $CheckedSymbols{$Level}{$Symbol} = 1;
13104        if(my $OverriddenMethod = $CompleteSignature{1}{$Symbol}{"Override"})
13105        { # register virtual overridings
13106            my $Cid = $CompleteSignature{1}{$Symbol}{"Class"};
13107            my $AffectedClass_Name = $TypeInfo{1}{$Cid}{"Name"};
13108            if(defined $CompleteSignature{2}{$OverriddenMethod}
13109            and $CompleteSignature{2}{$OverriddenMethod}{"Virt"})
13110            {
13111                if($TName_Tid{2}{$AffectedClass_Name})
13112                { # class should exist in newer version
13113                    if(not isCopyingClass($CompleteSignature{1}{$Symbol}{"Class"}, 1))
13114                    { # old v-table is NOT copied by old applications
13115                        %{$CompatProblems{$Level}{$Symbol}{"Overridden_Virtual_Method_B"}{$tr_name{$OverriddenMethod}}}=(
13116                            "Type_Name"=>$AffectedClass_Name,
13117                            "Target"=>get_Signature($OverriddenMethod, 1),
13118                            "Old_Value"=>get_Signature($Symbol, 1),
13119                            "New_Value"=>get_Signature($OverriddenMethod, 1));
13120                    }
13121                }
13122            }
13123        }
13124        if($Level eq "Binary"
13125        and $OSgroup eq "windows")
13126        { # register the reason of symbol name change
13127            if(my $NewSym = $mangled_name{2}{$tr_name{$Symbol}})
13128            {
13129                if($AddedInt{$Level}{$NewSym})
13130                {
13131                    if($CompleteSignature{1}{$Symbol}{"Static"} ne $CompleteSignature{2}{$NewSym}{"Static"})
13132                    {
13133                        if($CompleteSignature{2}{$NewSym}{"Static"})
13134                        {
13135                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Static"}{$tr_name{$Symbol}}}=(
13136                                "Target"=>$tr_name{$Symbol},
13137                                "Old_Value"=>$Symbol,
13138                                "New_Value"=>$NewSym  );
13139                        }
13140                        else
13141                        {
13142                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Static"}{$tr_name{$Symbol}}}=(
13143                                "Target"=>$tr_name{$Symbol},
13144                                "Old_Value"=>$Symbol,
13145                                "New_Value"=>$NewSym  );
13146                        }
13147                    }
13148                    if($CompleteSignature{1}{$Symbol}{"Virt"} ne $CompleteSignature{2}{$NewSym}{"Virt"})
13149                    {
13150                        if($CompleteSignature{2}{$NewSym}{"Virt"})
13151                        {
13152                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Virtual"}{$tr_name{$Symbol}}}=(
13153                                "Target"=>$tr_name{$Symbol},
13154                                "Old_Value"=>$Symbol,
13155                                "New_Value"=>$NewSym  );
13156                        }
13157                        else
13158                        {
13159                            %{$CompatProblems{$Level}{$Symbol}{"Symbol_Became_Non_Virtual"}{$tr_name{$Symbol}}}=(
13160                                "Target"=>$tr_name{$Symbol},
13161                                "Old_Value"=>$Symbol,
13162                                "New_Value"=>$NewSym  );
13163                        }
13164                    }
13165                    my $RTId1 = $CompleteSignature{1}{$Symbol}{"Return"};
13166                    my $RTId2 = $CompleteSignature{2}{$NewSym}{"Return"};
13167                    my $RTName1 = $TypeInfo{1}{$RTId1}{"Name"};
13168                    my $RTName2 = $TypeInfo{2}{$RTId2}{"Name"};
13169                    if($RTName1 ne $RTName2)
13170                    {
13171                        my $ProblemType = "Symbol_Changed_Return";
13172                        if($CompleteSignature{1}{$Symbol}{"Data"}) {
13173                            $ProblemType = "Global_Data_Symbol_Changed_Type";
13174                        }
13175                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{$tr_name{$Symbol}}}=(
13176                            "Target"=>$tr_name{$Symbol},
13177                            "Old_Type"=>$RTName1,
13178                            "New_Type"=>$RTName2,
13179                            "Old_Value"=>$Symbol,
13180                            "New_Value"=>$NewSym  );
13181                    }
13182                }
13183            }
13184        }
13185        if($Symbol=~/\A(_Z|\?)/)
13186        { # C++
13187            my $Prefix = get_symbol_prefix($Symbol, 1);
13188            if(my @Overloads = sort keys(%{$AddedOverloads{$Prefix}})
13189            and not $AddedOverloads{$Prefix}{get_symbol_suffix($Symbol, 1)})
13190            { # changed signature: params, "const"-qualifier
13191                my $NewSym = $AddedOverloads{$Prefix}{$Overloads[0]};
13192                if($CompleteSignature{1}{$Symbol}{"Constructor"})
13193                {
13194                    if($Symbol=~/(C[1-2][EI])/)
13195                    {
13196                        my $CtorType = $1;
13197                        $NewSym=~s/(C[1-2][EI])/$CtorType/g;
13198                    }
13199                }
13200                elsif($CompleteSignature{1}{$Symbol}{"Destructor"})
13201                {
13202                    if($Symbol=~/(D[0-2][EI])/)
13203                    {
13204                        my $DtorType = $1;
13205                        $NewSym=~s/(D[0-2][EI])/$DtorType/g;
13206                    }
13207                }
13208                my $NS1 = $CompleteSignature{1}{$Symbol}{"NameSpace"};
13209                my $NS2 = $CompleteSignature{2}{$NewSym}{"NameSpace"};
13210                if((not $NS1 and not $NS2) or ($NS1 and $NS2 and $NS1 eq $NS2))
13211                { # from the same class and namespace
13212                    if($CompleteSignature{1}{$Symbol}{"Const"}
13213                    and not $CompleteSignature{2}{$NewSym}{"Const"})
13214                    { # "const" to non-"const"
13215                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Const"}{$tr_name{$Symbol}}}=(
13216                            "Type_Name"=>$TypeInfo{1}{$CompleteSignature{1}{$Symbol}{"Class"}}{"Name"},
13217                            "Target"=>$tr_name{$Symbol},
13218                            "New_Signature"=>get_Signature($NewSym, 2),
13219                            "Old_Value"=>$Symbol,
13220                            "New_Value"=>$NewSym  );
13221                    }
13222                    elsif(not $CompleteSignature{1}{$Symbol}{"Const"}
13223                    and $CompleteSignature{2}{$NewSym}{"Const"})
13224                    { # non-"const" to "const"
13225                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Const"}{$tr_name{$Symbol}}}=(
13226                            "Target"=>$tr_name{$Symbol},
13227                            "New_Signature"=>get_Signature($NewSym, 2),
13228                            "Old_Value"=>$Symbol,
13229                            "New_Value"=>$NewSym  );
13230                    }
13231                    if($CompleteSignature{1}{$Symbol}{"Volatile"}
13232                    and not $CompleteSignature{2}{$NewSym}{"Volatile"})
13233                    { # "volatile" to non-"volatile"
13234
13235                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Volatile"}{$tr_name{$Symbol}}}=(
13236                            "Target"=>$tr_name{$Symbol},
13237                            "New_Signature"=>get_Signature($NewSym, 2),
13238                            "Old_Value"=>$Symbol,
13239                            "New_Value"=>$NewSym  );
13240                    }
13241                    elsif(not $CompleteSignature{1}{$Symbol}{"Volatile"}
13242                    and $CompleteSignature{2}{$NewSym}{"Volatile"})
13243                    { # non-"volatile" to "volatile"
13244                        %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Volatile"}{$tr_name{$Symbol}}}=(
13245                            "Target"=>$tr_name{$Symbol},
13246                            "New_Signature"=>get_Signature($NewSym, 2),
13247                            "Old_Value"=>$Symbol,
13248                            "New_Value"=>$NewSym  );
13249                    }
13250                    if(get_symbol_suffix($Symbol, 0) ne get_symbol_suffix($NewSym, 0))
13251                    { # params list
13252                        %{$CompatProblems{$Level}{$Symbol}{"Symbol_Changed_Parameters"}{$tr_name{$Symbol}}}=(
13253                            "Target"=>$tr_name{$Symbol},
13254                            "New_Signature"=>get_Signature($NewSym, 2),
13255                            "Old_Value"=>$Symbol,
13256                            "New_Value"=>$NewSym  );
13257                    }
13258                }
13259            }
13260        }
13261    }
13262    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
13263    { # checking symbols
13264        $CurrentSymbol = $Symbol;
13265
13266        my ($SN, $SS, $SV) = separate_symbol($Symbol);
13267        if($Level eq "Source")
13268        { # remove symbol version
13269            $Symbol=$SN;
13270        }
13271        else
13272        { # Binary
13273            if(not $SV)
13274            { # symbol without version
13275                if(my $VSym = $SymVer{1}{$Symbol})
13276                { # the symbol is linked with versioned symbol
13277                    if($CompleteSignature{2}{$VSym}{"MnglName"})
13278                    { # show report for symbol@ver only
13279                        next;
13280                    }
13281                    elsif(not link_symbol($VSym, 2, "-Deps"))
13282                    { # changed version: sym@v1 to sym@v2
13283                      # do NOT show report for symbol
13284                        next;
13285                    }
13286                }
13287            }
13288        }
13289        my $PSymbol = $Symbol;
13290        if($Level eq "Source"
13291        and my $S = $SourceReplacement{$Symbol})
13292        { # take a source-compatible replacement function
13293            $PSymbol = $S;
13294        }
13295        if($CompleteSignature{1}{$Symbol}{"Private"})
13296        { # private symbols
13297            next;
13298        }
13299        if(not defined $CompleteSignature{1}{$Symbol}
13300        or not defined $CompleteSignature{2}{$PSymbol})
13301        { # no info
13302            next;
13303        }
13304        if(not $CompleteSignature{1}{$Symbol}{"MnglName"}
13305        or not $CompleteSignature{2}{$PSymbol}{"MnglName"})
13306        { # no mangled name
13307            next;
13308        }
13309        if(not $CompleteSignature{1}{$Symbol}{"Header"}
13310        or not $CompleteSignature{2}{$PSymbol}{"Header"})
13311        { # without a header
13312            next;
13313        }
13314
13315        if(not $CompleteSignature{1}{$Symbol}{"PureVirt"}
13316        and $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13317        { # became pure
13318            next;
13319        }
13320        if($CompleteSignature{1}{$Symbol}{"PureVirt"}
13321        and not $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13322        { # became non-pure
13323            next;
13324        }
13325
13326        if(not symbolFilter($Symbol, 1, "Affected + InlineVirt", $Level))
13327        { # exported, target, inline virtual and pure virtual
13328            next;
13329        }
13330        if(not symbolFilter($PSymbol, 2, "Affected + InlineVirt", $Level))
13331        { # exported, target, inline virtual and pure virtual
13332            next;
13333        }
13334
13335        if(checkDump(1, "2.13") and checkDump(2, "2.13"))
13336        {
13337            if($CompleteSignature{1}{$Symbol}{"Data"}
13338            and $CompleteSignature{2}{$PSymbol}{"Data"})
13339            {
13340                my $Value1 = $CompleteSignature{1}{$Symbol}{"Value"};
13341                my $Value2 = $CompleteSignature{2}{$PSymbol}{"Value"};
13342                if(defined $Value1)
13343                {
13344                    $Value1 = showVal($Value1, $CompleteSignature{1}{$Symbol}{"Return"}, 1);
13345                    if(defined $Value2)
13346                    {
13347                        $Value2 = showVal($Value2, $CompleteSignature{2}{$PSymbol}{"Return"}, 2);
13348                        if($Value1 ne $Value2)
13349                        {
13350                            %{$CompatProblems{$Level}{$Symbol}{"Global_Data_Value_Changed"}{""}}=(
13351                                "Old_Value"=>$Value1,
13352                                "New_Value"=>$Value2,
13353                                "Target"=>get_Signature($Symbol, 1)  );
13354                        }
13355                    }
13356                }
13357            }
13358        }
13359
13360        if($CompleteSignature{2}{$PSymbol}{"Private"})
13361        {
13362            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Private"}{""}}=(
13363                "Target"=>get_Signature_M($PSymbol, 2)  );
13364        }
13365        elsif(not $CompleteSignature{1}{$Symbol}{"Protected"}
13366        and $CompleteSignature{2}{$PSymbol}{"Protected"})
13367        {
13368            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Protected"}{""}}=(
13369                "Target"=>get_Signature_M($PSymbol, 2)  );
13370        }
13371        elsif($CompleteSignature{1}{$Symbol}{"Protected"}
13372        and not $CompleteSignature{2}{$PSymbol}{"Protected"})
13373        {
13374            %{$CompatProblems{$Level}{$Symbol}{getSymKind($Symbol, 1)."_Became_Public"}{""}}=(
13375                "Target"=>get_Signature_M($PSymbol, 2)  );
13376        }
13377
13378        # checking virtual table
13379        mergeVirtualTables($Symbol, $Level);
13380
13381        if($COMPILE_ERRORS)
13382        { # if some errors occurred at the compiling stage
13383          # then some false positives can be skipped here
13384            if(not $CompleteSignature{1}{$Symbol}{"Data"} and $CompleteSignature{2}{$PSymbol}{"Data"}
13385            and not $GlobalDataObject{2}{$Symbol})
13386            { # missed information about parameters in newer version
13387                next;
13388            }
13389            if($CompleteSignature{1}{$Symbol}{"Data"} and not $GlobalDataObject{1}{$Symbol}
13390            and not $CompleteSignature{2}{$PSymbol}{"Data"})
13391            { # missed information about parameters in older version
13392                next;
13393            }
13394        }
13395        my ($MnglName, $VersionSpec, $SymbolVersion) = separate_symbol($Symbol);
13396        # checking attributes
13397        if($CompleteSignature{2}{$PSymbol}{"Static"}
13398        and not $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
13399        {
13400            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Static"}{""}}=(
13401                "Target"=>get_Signature($Symbol, 1)
13402            );
13403        }
13404        elsif(not $CompleteSignature{2}{$PSymbol}{"Static"}
13405        and $CompleteSignature{1}{$Symbol}{"Static"} and $Symbol=~/\A(_Z|\?)/)
13406        {
13407            %{$CompatProblems{$Level}{$Symbol}{"Method_Became_Non_Static"}{""}}=(
13408                "Target"=>get_Signature($Symbol, 1)
13409            );
13410        }
13411        if(($CompleteSignature{1}{$Symbol}{"Virt"} and $CompleteSignature{2}{$PSymbol}{"Virt"})
13412        or ($CompleteSignature{1}{$Symbol}{"PureVirt"} and $CompleteSignature{2}{$PSymbol}{"PureVirt"}))
13413        { # relative position of virtual and pure virtual methods
13414            if($Level eq "Binary")
13415            {
13416                if(defined $CompleteSignature{1}{$Symbol}{"RelPos"} and defined $CompleteSignature{2}{$PSymbol}{"RelPos"}
13417                and $CompleteSignature{1}{$Symbol}{"RelPos"}!=$CompleteSignature{2}{$PSymbol}{"RelPos"})
13418                { # top-level virtual methods only
13419                    my $Class_Id = $CompleteSignature{1}{$Symbol}{"Class"};
13420                    my $Class_Name = $TypeInfo{1}{$Class_Id}{"Name"};
13421                    if(defined $VirtualTable{1}{$Class_Name} and defined $VirtualTable{2}{$Class_Name}
13422                    and $VirtualTable{1}{$Class_Name}{$Symbol}!=$VirtualTable{2}{$Class_Name}{$Symbol})
13423                    { # check the absolute position of virtual method (including added and removed methods)
13424                        my %Class_Type = get_Type($Class_Id, 1);
13425                        my $ProblemType = "Virtual_Method_Position";
13426                        if($CompleteSignature{1}{$Symbol}{"PureVirt"}) {
13427                            $ProblemType = "Pure_Virtual_Method_Position";
13428                        }
13429                        if(isUsedClass($Class_Id, 1, $Level))
13430                        {
13431                            my @Affected = ($Symbol, keys(%{$OverriddenMethods{1}{$Symbol}}));
13432                            foreach my $ASymbol (@Affected)
13433                            {
13434                                if(not symbolFilter($ASymbol, 1, "Affected", $Level)) {
13435                                    next;
13436                                }
13437                                %{$CompatProblems{$Level}{$ASymbol}{$ProblemType}{$tr_name{$MnglName}}}=(
13438                                    "Type_Name"=>$Class_Type{"Name"},
13439                                    "Old_Value"=>$CompleteSignature{1}{$Symbol}{"RelPos"},
13440                                    "New_Value"=>$CompleteSignature{2}{$PSymbol}{"RelPos"},
13441                                    "Target"=>get_Signature($Symbol, 1));
13442                            }
13443                            $VTableChanged_M{$Class_Type{"Name"}} = 1;
13444                        }
13445                    }
13446                }
13447            }
13448        }
13449        if($CompleteSignature{1}{$Symbol}{"PureVirt"}
13450        or $CompleteSignature{2}{$PSymbol}{"PureVirt"})
13451        { # do NOT check type changes in pure virtuals
13452            next;
13453        }
13454        $CheckedSymbols{$Level}{$Symbol} = 1;
13455        if($Symbol=~/\A(_Z|\?)/
13456        or keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})==keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}}))
13457        { # C/C++: changes in parameters
13458            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13459            { # checking parameters
13460                mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 1);
13461            }
13462        }
13463        else
13464        { # C: added/removed parameters
13465            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}}))
13466            { # checking added parameters
13467                my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13468                my $PType2_Name = $TypeInfo{2}{$PType2_Id}{"Name"};
13469                last if($PType2_Name eq "...");
13470                my $PName = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
13471                my $PName_Old = (defined $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos})?$CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"}:"";
13472                my $ParamPos_Prev = "-1";
13473                if($PName=~/\Ap\d+\Z/i)
13474                { # added unnamed parameter ( pN )
13475                    my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 1);
13476                    my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType2_Name, $ParamPos, "backward", $Symbol, 2);
13477                    if($#Positions1==-1 or $#Positions2>$#Positions1) {
13478                        $ParamPos_Prev = "lost";
13479                    }
13480                }
13481                else {
13482                    $ParamPos_Prev = find_ParamPair_Pos_byName($PName, $Symbol, 1);
13483                }
13484                if($ParamPos_Prev eq "lost")
13485                {
13486                    if($ParamPos>keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})-1)
13487                    {
13488                        my $ProblemType = "Added_Parameter";
13489                        if($PName=~/\Ap\d+\Z/) {
13490                            $ProblemType = "Added_Unnamed_Parameter";
13491                        }
13492                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13493                            "Target"=>$PName,
13494                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13495                            "Param_Type"=>$PType2_Name,
13496                            "New_Signature"=>get_Signature($Symbol, 2)  );
13497                    }
13498                    else
13499                    {
13500                        my %ParamType_Pure = get_PureType($PType2_Id, $TypeInfo{2});
13501                        my $PairType_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13502                        my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{1});
13503                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType2_Name eq $TypeInfo{1}{$PairType_Id}{"Name"})
13504                        and find_ParamPair_Pos_byName($PName_Old, $Symbol, 2) eq "lost")
13505                        {
13506                            if($PName_Old!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
13507                            {
13508                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
13509                                    "Target"=>$PName_Old,
13510                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13511                                    "Param_Type"=>$PType2_Name,
13512                                    "Old_Value"=>$PName_Old,
13513                                    "New_Value"=>$PName,
13514                                    "New_Signature"=>get_Signature($Symbol, 2)  );
13515                            }
13516                        }
13517                        else
13518                        {
13519                            my $ProblemType = "Added_Middle_Parameter";
13520                            if($PName=~/\Ap\d+\Z/) {
13521                                $ProblemType = "Added_Middle_Unnamed_Parameter";
13522                            }
13523                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13524                                "Target"=>$PName,
13525                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13526                                "Param_Type"=>$PType2_Name,
13527                                "New_Signature"=>get_Signature($Symbol, 2)  );
13528                        }
13529                    }
13530                }
13531            }
13532            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13533            { # check relevant parameters
13534                my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13535                my $ParamName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
13536                # FIXME: find relevant parameter by name
13537                if(defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos})
13538                {
13539                    my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13540                    my $ParamName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"};
13541                    if($TypeInfo{1}{$PType1_Id}{"Name"} eq $TypeInfo{2}{$PType2_Id}{"Name"}
13542                    or ($ParamName1!~/\Ap\d+\Z/i and $ParamName1 eq $ParamName2)) {
13543                        mergeParameters($Symbol, $PSymbol, $ParamPos, $ParamPos, $Level, 0);
13544                    }
13545                }
13546            }
13547            foreach my $ParamPos (sort {int($a) <=> int($b)} keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13548            { # checking removed parameters
13549                my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"type"};
13550                my $PType1_Name = $TypeInfo{1}{$PType1_Id}{"Name"};
13551                last if($PType1_Name eq "...");
13552                my $PName = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos}{"name"};
13553                my $PName_New = (defined $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos})?$CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"name"}:"";
13554                my $ParamPos_New = "-1";
13555                if($PName=~/\Ap\d+\Z/i)
13556                { # removed unnamed parameter ( pN )
13557                    my @Positions1 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 1);
13558                    my @Positions2 = find_ParamPair_Pos_byTypeAndPos($PType1_Name, $ParamPos, "forward", $Symbol, 2);
13559                    if($#Positions2==-1 or $#Positions2<$#Positions1) {
13560                        $ParamPos_New = "lost";
13561                    }
13562                }
13563                else {
13564                    $ParamPos_New = find_ParamPair_Pos_byName($PName, $Symbol, 2);
13565                }
13566                if($ParamPos_New eq "lost")
13567                {
13568                    if($ParamPos>keys(%{$CompleteSignature{2}{$PSymbol}{"Param"}})-1)
13569                    {
13570                        my $ProblemType = "Removed_Parameter";
13571                        if($PName=~/\Ap\d+\Z/) {
13572                            $ProblemType = "Removed_Unnamed_Parameter";
13573                        }
13574                        %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13575                            "Target"=>$PName,
13576                            "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13577                            "Param_Type"=>$PType1_Name,
13578                            "New_Signature"=>get_Signature($Symbol, 2)  );
13579                    }
13580                    elsif($ParamPos<keys(%{$CompleteSignature{1}{$Symbol}{"Param"}})-1)
13581                    {
13582                        my %ParamType_Pure = get_PureType($PType1_Id, $TypeInfo{1});
13583                        my $PairType_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos}{"type"};
13584                        my %PairType_Pure = get_PureType($PairType_Id, $TypeInfo{2});
13585                        if(($ParamType_Pure{"Name"} eq $PairType_Pure{"Name"} or $PType1_Name eq $TypeInfo{2}{$PairType_Id}{"Name"})
13586                        and find_ParamPair_Pos_byName($PName_New, $Symbol, 1) eq "lost")
13587                        {
13588                            if($PName_New!~/\Ap\d+\Z/ and $PName!~/\Ap\d+\Z/)
13589                            {
13590                                %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos)." Parameter"}}=(
13591                                    "Target"=>$PName,
13592                                    "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13593                                    "Param_Type"=>$PType1_Name,
13594                                    "Old_Value"=>$PName,
13595                                    "New_Value"=>$PName_New,
13596                                    "New_Signature"=>get_Signature($Symbol, 2)  );
13597                            }
13598                        }
13599                        else
13600                        {
13601                            my $ProblemType = "Removed_Middle_Parameter";
13602                            if($PName=~/\Ap\d+\Z/) {
13603                                $ProblemType = "Removed_Middle_Unnamed_Parameter";
13604                            }
13605                            %{$CompatProblems{$Level}{$Symbol}{$ProblemType}{showPos($ParamPos)." Parameter"}}=(
13606                                "Target"=>$PName,
13607                                "Param_Pos"=>adjustParamPos($ParamPos, $Symbol, 2),
13608                                "Param_Type"=>$PType1_Name,
13609                                "New_Signature"=>get_Signature($Symbol, 2)  );
13610                        }
13611                    }
13612                }
13613            }
13614        }
13615        # checking return type
13616        my $ReturnType1_Id = $CompleteSignature{1}{$Symbol}{"Return"};
13617        my $ReturnType2_Id = $CompleteSignature{2}{$PSymbol}{"Return"};
13618        my %RC_SubProblems = detectTypeChange($ReturnType1_Id, $ReturnType2_Id, "Return", $Level);
13619
13620        foreach my $SubProblemType (keys(%RC_SubProblems))
13621        {
13622            my $New_Value = $RC_SubProblems{$SubProblemType}{"New_Value"};
13623            my $Old_Value = $RC_SubProblems{$SubProblemType}{"Old_Value"};
13624            my %ProblemTypes = ();
13625
13626            if($CompleteSignature{1}{$Symbol}{"Data"})
13627            {
13628                if($SubProblemType eq "Return_Type_And_Size") {
13629                    $ProblemTypes{"Global_Data_Type_And_Size"} = 1;
13630                }
13631                elsif($SubProblemType eq "Return_Type_Format") {
13632                    $ProblemTypes{"Global_Data_Type_Format"} = 1;
13633                }
13634                else {
13635                    $ProblemTypes{"Global_Data_Type"} = 1;
13636                }
13637
13638                # quals
13639                if($SubProblemType eq "Return_Type"
13640                or $SubProblemType eq "Return_Type_And_Size"
13641                or $SubProblemType eq "Return_Type_Format")
13642                {
13643                    if(my $RR = removedQual($Old_Value, $New_Value, "const"))
13644                    { # const to non-const
13645                        if($RR==2) {
13646                            $ProblemTypes{"Global_Data_Removed_Const"} = 1;
13647                        }
13648                        else {
13649                            $ProblemTypes{"Global_Data_Became_Non_Const"} = 1;
13650                        }
13651                        $ProblemTypes{"Global_Data_Type"} = 1;
13652                    }
13653                    elsif(my $RA = addedQual($Old_Value, $New_Value, "const"))
13654                    { # non-const to const
13655                        if($RA==2) {
13656                            $ProblemTypes{"Global_Data_Added_Const"} = 1;
13657                        }
13658                        else {
13659                            $ProblemTypes{"Global_Data_Became_Const"} = 1;
13660                        }
13661                        $ProblemTypes{"Global_Data_Type"} = 1;
13662                    }
13663                }
13664            }
13665            else
13666            {
13667                # quals
13668                if($SubProblemType eq "Return_Type"
13669                or $SubProblemType eq "Return_Type_And_Size"
13670                or $SubProblemType eq "Return_Type_Format")
13671                {
13672                    if(checkDump(1, "2.6") and checkDump(2, "2.6"))
13673                    {
13674                        if(addedQual($Old_Value, $New_Value, "volatile"))
13675                        {
13676                            $ProblemTypes{"Return_Value_Became_Volatile"} = 1;
13677                            if($Level ne "Source"
13678                            or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
13679                                $ProblemTypes{"Return_Type"} = 1;
13680                            }
13681                        }
13682                    }
13683                    if(my $RA = addedQual($Old_Value, $New_Value, "const"))
13684                    {
13685                        if($RA==2) {
13686                            $ProblemTypes{"Return_Type_Added_Const"} = 1;
13687                        }
13688                        else {
13689                            $ProblemTypes{"Return_Type_Became_Const"} = 1;
13690                        }
13691                        if($Level ne "Source"
13692                        or not cmpBTypes($Old_Value, $New_Value, 1, 2)) {
13693                            $ProblemTypes{"Return_Type"} = 1;
13694                        }
13695                    }
13696                }
13697            }
13698            if($Level eq "Binary"
13699            and not $CompleteSignature{1}{$Symbol}{"Data"})
13700            {
13701                my ($Arch1, $Arch2) = (getArch(1), getArch(2));
13702                if($Arch1 eq "unknown" or $Arch2 eq "unknown")
13703                { # if one of the architectures is unknown
13704                    # then set other arhitecture to unknown too
13705                    ($Arch1, $Arch2) = ("unknown", "unknown");
13706                }
13707                my (%Conv1, %Conv2) = ();
13708                if($UseConv_Real{1}{"R"} and $UseConv_Real{2}{"R"})
13709                {
13710                    %Conv1 = callingConvention_R_Real($CompleteSignature{1}{$Symbol});
13711                    %Conv2 = callingConvention_R_Real($CompleteSignature{2}{$PSymbol});
13712                }
13713                else
13714                {
13715                    %Conv1 = callingConvention_R_Model($CompleteSignature{1}{$Symbol}, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1});
13716                    %Conv2 = callingConvention_R_Model($CompleteSignature{2}{$PSymbol}, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2});
13717                }
13718
13719                if($SubProblemType eq "Return_Type_Became_Void")
13720                {
13721                    if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13722                    { # parameters stack has been affected
13723                        if($Conv1{"Method"} eq "stack") {
13724                            $ProblemTypes{"Return_Type_Became_Void_And_Stack_Layout"} = 1;
13725                        }
13726                        elsif($Conv1{"Hidden"}) {
13727                            $ProblemTypes{"Return_Type_Became_Void_And_Register"} = 1;
13728                        }
13729                    }
13730                }
13731                elsif($SubProblemType eq "Return_Type_From_Void")
13732                {
13733                    if(keys(%{$CompleteSignature{1}{$Symbol}{"Param"}}))
13734                    { # parameters stack has been affected
13735                        if($Conv2{"Method"} eq "stack") {
13736                            $ProblemTypes{"Return_Type_From_Void_And_Stack_Layout"} = 1;
13737                        }
13738                        elsif($Conv2{"Hidden"}) {
13739                            $ProblemTypes{"Return_Type_From_Void_And_Register"} = 1;
13740                        }
13741                    }
13742                }
13743                elsif($SubProblemType eq "Return_Type"
13744                or $SubProblemType eq "Return_Type_And_Size"
13745                or $SubProblemType eq "Return_Type_Format")
13746                {
13747                    if($Conv1{"Method"} ne $Conv2{"Method"})
13748                    {
13749                        if($Conv1{"Method"} eq "stack")
13750                        { # returns in a register instead of a hidden first parameter
13751                            $ProblemTypes{"Return_Type_From_Stack_To_Register"} = 1;
13752                        }
13753                        else {
13754                            $ProblemTypes{"Return_Type_From_Register_To_Stack"} = 1;
13755                        }
13756                    }
13757                    else
13758                    {
13759                        if($Conv1{"Method"} eq "reg")
13760                        {
13761                            if($Conv1{"Registers"} ne $Conv2{"Registers"})
13762                            {
13763                                if($Conv1{"Hidden"}) {
13764                                    $ProblemTypes{"Return_Type_And_Register_Was_Hidden_Parameter"} = 1;
13765                                }
13766                                elsif($Conv2{"Hidden"}) {
13767                                    $ProblemTypes{"Return_Type_And_Register_Became_Hidden_Parameter"} = 1;
13768                                }
13769                                else {
13770                                    $ProblemTypes{"Return_Type_And_Register"} = 1;
13771                                }
13772                            }
13773                        }
13774                    }
13775                }
13776            }
13777
13778            if(not keys(%ProblemTypes))
13779            { # default
13780                $ProblemTypes{$SubProblemType} = 1;
13781            }
13782
13783            foreach my $ProblemType (keys(%ProblemTypes))
13784            { # additional
13785                $CompatProblems{$Level}{$Symbol}{$ProblemType}{"retval"} = $RC_SubProblems{$SubProblemType};
13786            }
13787        }
13788        if($ReturnType1_Id and $ReturnType2_Id)
13789        {
13790            @RecurTypes = ();
13791            my $Sub_SubProblems = mergeTypes($ReturnType1_Id, $ReturnType2_Id, $Level);
13792
13793            my $AddProblems = {};
13794
13795            if($CompleteSignature{1}{$Symbol}{"Data"})
13796            {
13797                if($Level eq "Binary")
13798                {
13799                    if(get_PLevel($ReturnType1_Id, 1)==0)
13800                    {
13801                        if(defined $Sub_SubProblems->{"DataType_Size"})
13802                        { # add "Global_Data_Size" problem
13803
13804                            foreach my $Loc (keys(%{$Sub_SubProblems->{"DataType_Size"}}))
13805                            {
13806                                if(index($Loc,"->")==-1)
13807                                {
13808                                    if($Loc eq $Sub_SubProblems->{"DataType_Size"}{$Loc}{"Type_Name"})
13809                                    {
13810                                        $AddProblems->{"Global_Data_Size"}{$Loc} = $Sub_SubProblems->{"DataType_Size"}{$Loc}; # add a new problem
13811                                        last;
13812                                    }
13813                                }
13814                            }
13815                        }
13816                    }
13817                    if(not defined $AddProblems->{"Global_Data_Size"})
13818                    {
13819                        if(defined $GlobalDataObject{1}{$Symbol}
13820                        and defined $GlobalDataObject{2}{$Symbol})
13821                        {
13822                            my $Old_Size = $GlobalDataObject{1}{$Symbol};
13823                            my $New_Size = $GlobalDataObject{2}{$Symbol};
13824                            if($Old_Size!=$New_Size)
13825                            {
13826                                $AddProblems->{"Global_Data_Size"}{"retval"} = {
13827                                    "Old_Size"=>$Old_Size*$BYTE_SIZE,
13828                                    "New_Size"=>$New_Size*$BYTE_SIZE };
13829                            }
13830                        }
13831                    }
13832                }
13833            }
13834
13835            foreach my $SubProblemType (keys(%{$AddProblems}))
13836            {
13837                foreach my $SubLocation (keys(%{$AddProblems->{$SubProblemType}}))
13838                {
13839                    my $NewLocation = "retval";
13840                    if($SubLocation and $SubLocation ne "retval") {
13841                        $NewLocation = "retval->".$SubLocation;
13842                    }
13843                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $AddProblems->{$SubProblemType}{$SubLocation};
13844                }
13845            }
13846
13847            foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
13848            {
13849                foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
13850                {
13851                    my $NewLocation = "retval";
13852                    if($SubLocation and $SubLocation ne "retval") {
13853                        $NewLocation = "retval->".$SubLocation;
13854                    }
13855                    $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
13856                }
13857            }
13858        }
13859
13860        # checking object type
13861        my $ObjTId1 = $CompleteSignature{1}{$Symbol}{"Class"};
13862        my $ObjTId2 = $CompleteSignature{2}{$PSymbol}{"Class"};
13863        if($ObjTId1 and $ObjTId2
13864        and not $CompleteSignature{1}{$Symbol}{"Static"})
13865        {
13866            my $ThisPtr1_Id = getTypeIdByName($TypeInfo{1}{$ObjTId1}{"Name"}."*const", 1);
13867            my $ThisPtr2_Id = getTypeIdByName($TypeInfo{2}{$ObjTId2}{"Name"}."*const", 2);
13868            if($ThisPtr1_Id and $ThisPtr2_Id)
13869            {
13870                @RecurTypes = ();
13871                my $Sub_SubProblems = mergeTypes($ThisPtr1_Id, $ThisPtr2_Id, $Level);
13872                foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
13873                {
13874                    foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
13875                    {
13876                        my $NewLocation = ($SubLocation)?"this->".$SubLocation:"this";
13877                        $CompatProblems{$Level}{$Symbol}{$SubProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
13878                    }
13879                }
13880            }
13881        }
13882    }
13883    if($Level eq "Binary") {
13884        mergeVTables($Level);
13885    }
13886    foreach my $Symbol (keys(%{$CompatProblems{$Level}})) {
13887        $CheckedSymbols{$Level}{$Symbol} = 1;
13888    }
13889}
13890
13891sub rmQuals($$)
13892{
13893    my ($Value, $Qual) = @_;
13894    if(not $Qual) {
13895        return $Value;
13896    }
13897    if($Qual eq "all")
13898    { # all quals
13899        $Qual = "const|volatile|restrict";
13900    }
13901    while($Value=~s/\b$Qual\b//) {
13902        $Value = formatName($Value, "T");
13903    }
13904    return $Value;
13905}
13906
13907sub cmpBTypes($$$$)
13908{
13909    my ($T1, $T2, $V1, $V2) = @_;
13910    $T1 = uncover_typedefs($T1, $V1);
13911    $T2 = uncover_typedefs($T2, $V2);
13912    return (rmQuals($T1, "all") eq rmQuals($T2, "all"));
13913}
13914
13915sub addedQual($$$)
13916{
13917    my ($Old_Value, $New_Value, $Qual) = @_;
13918    return removedQual_I($New_Value, $Old_Value, 2, 1, $Qual);
13919}
13920
13921sub removedQual($$$)
13922{
13923    my ($Old_Value, $New_Value, $Qual) = @_;
13924    return removedQual_I($Old_Value, $New_Value, 1, 2, $Qual);
13925}
13926
13927sub removedQual_I($$$$$)
13928{
13929    my ($Old_Value, $New_Value, $V1, $V2, $Qual) = @_;
13930    $Old_Value = uncover_typedefs($Old_Value, $V1);
13931    $New_Value = uncover_typedefs($New_Value, $V2);
13932    if($Old_Value eq $New_Value)
13933    { # equal types
13934        return 0;
13935    }
13936    if($Old_Value!~/\b$Qual\b/)
13937    { # without a qual
13938        return 0;
13939    }
13940    elsif($New_Value!~/\b$Qual\b/)
13941    { # became non-qual
13942        return 1;
13943    }
13944    else
13945    {
13946        my @BQ1 = getQualModel($Old_Value, $Qual);
13947        my @BQ2 = getQualModel($New_Value, $Qual);
13948        foreach (0 .. $#BQ1)
13949        { # removed qual
13950            if($BQ1[$_]==1
13951            and $BQ2[$_]!=1)
13952            {
13953                return 2;
13954            }
13955        }
13956    }
13957    return 0;
13958}
13959
13960sub getQualModel($$)
13961{
13962    my ($Value, $Qual) = @_;
13963    if(not $Qual) {
13964        return $Value;
13965    }
13966
13967    # cleaning
13968    while($Value=~/(\w+)/ and $1 ne $Qual) {
13969        $Value=~s/\b$1\b//g;
13970    }
13971    $Value=~s/[^\*\&\w]+//g;
13972
13973    # modeling
13974    # int*const*const == 011
13975    # int**const == 001
13976    my @Model = ();
13977    my @Elems = split(/[\*\&]/, $Value);
13978    if(not @Elems) {
13979        return (0);
13980    }
13981    foreach (@Elems)
13982    {
13983        if($_ eq $Qual) {
13984            push(@Model, 1);
13985        }
13986        else {
13987            push(@Model, 0);
13988        }
13989    }
13990
13991    return @Model;
13992}
13993
13994my %StringTypes = map {$_=>1} (
13995    "char*",
13996    "char const*"
13997);
13998
13999my %CharTypes = map {$_=>1} (
14000    "char",
14001    "char const"
14002);
14003
14004sub showVal($$$)
14005{
14006    my ($Value, $TypeId, $LibVersion) = @_;
14007    my %PureType = get_PureType($TypeId, $TypeInfo{$LibVersion});
14008    my $TName = uncover_typedefs($PureType{"Name"}, $LibVersion);
14009    if(substr($Value, 0, 2) eq "_Z")
14010    {
14011        if(my $Unmangled = $tr_name{$Value}) {
14012            return $Unmangled;
14013        }
14014    }
14015    elsif(defined $StringTypes{$TName} or $TName=~/string/i)
14016    { # strings
14017        return "\"$Value\"";
14018    }
14019    elsif(defined $CharTypes{$TName})
14020    { # characters
14021        return "\'$Value\'";
14022    }
14023    if($Value eq "")
14024    { # other
14025        return "\'\'";
14026    }
14027    return $Value;
14028}
14029
14030sub getRegs($$$)
14031{
14032    my ($LibVersion, $Symbol, $Pos) = @_;
14033
14034    if(defined $CompleteSignature{$LibVersion}{$Symbol}{"Reg"})
14035    {
14036        my %Regs = ();
14037        foreach my $Elem (sort keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}}))
14038        {
14039            if($Elem=~/\A$Pos([\.\+]|\Z)/) {
14040                $Regs{$CompleteSignature{$LibVersion}{$Symbol}{"Reg"}{$Elem}} = 1;
14041            }
14042        }
14043
14044        return join(", ", sort keys(%Regs));
14045    }
14046
14047    return undef;
14048}
14049
14050sub mergeParameters($$$$$$)
14051{
14052    my ($Symbol, $PSymbol, $ParamPos1, $ParamPos2, $Level, $ChkRnmd) = @_;
14053    if(not $Symbol) {
14054        return;
14055    }
14056    my $PType1_Id = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"type"};
14057    my $PName1 = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"name"};
14058    my $PType2_Id = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"type"};
14059    my $PName2 = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"name"};
14060    if(not $PType1_Id
14061    or not $PType2_Id) {
14062        return;
14063    }
14064
14065    if($Symbol=~/\A(_Z|\?)/)
14066    { # do not merge "this"
14067        if($PName1 eq "this" or $PName2 eq "this") {
14068            return;
14069        }
14070    }
14071
14072    my %Type1 = get_Type($PType1_Id, 1);
14073    my %Type2 = get_Type($PType2_Id, 2);
14074
14075    my %PureType1 = get_PureType($PType1_Id, $TypeInfo{1});
14076
14077    my %BaseType1 = get_BaseType($PType1_Id, 1);
14078    my %BaseType2 = get_BaseType($PType2_Id, 2);
14079
14080    my $Parameter_Location = ($PName1)?$PName1:showPos($ParamPos1)." Parameter";
14081
14082    if($Level eq "Binary")
14083    {
14084        if(checkDump(1, "2.6.1") and checkDump(2, "2.6.1"))
14085        { # "reg" attribute added in ACC 1.95.1 (dump 2.6.1 format)
14086            if($CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
14087            and not $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
14088            {
14089                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Non_Register"}{$Parameter_Location}}=(
14090                    "Target"=>$PName1,
14091                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
14092            }
14093            elsif(not $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"reg"}
14094            and $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"reg"})
14095            {
14096                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Became_Register"}{$Parameter_Location}}=(
14097                    "Target"=>$PName1,
14098                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1)  );
14099            }
14100        }
14101
14102        if(defined $UsedDump{1}{"DWARF"}
14103        and defined $UsedDump{2}{"DWARF"})
14104        {
14105            if(checkDump(1, "3.0") and checkDump(2, "3.0"))
14106            {
14107                my $Old_Regs = getRegs(1, $Symbol, $ParamPos1);
14108                my $New_Regs = getRegs(2, $PSymbol, $ParamPos2);
14109                if($Old_Regs and $New_Regs)
14110                {
14111                    if($Old_Regs ne $New_Regs)
14112                    {
14113                        %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Register"}{$Parameter_Location}}=(
14114                            "Target"=>$PName1,
14115                            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14116                            "Old_Value"=>$Old_Regs,
14117                            "New_Value"=>$New_Regs  );
14118                    }
14119                }
14120                elsif($Old_Regs and not $New_Regs)
14121                {
14122                    %{$CompatProblems{$Level}{$Symbol}{"Parameter_From_Register"}{$Parameter_Location}}=(
14123                        "Target"=>$PName1,
14124                        "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14125                        "Old_Value"=>$Old_Regs  );
14126                }
14127                elsif(not $Old_Regs and $New_Regs)
14128                {
14129                    %{$CompatProblems{$Level}{$Symbol}{"Parameter_To_Register"}{$Parameter_Location}}=(
14130                        "Target"=>$PName1,
14131                        "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14132                        "New_Value"=>$New_Regs  );
14133                }
14134                if((my $Old_Offset = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"offset"}) ne ""
14135                and (my $New_Offset = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"offset"}) ne "")
14136                {
14137                    if($Old_Offset ne $New_Offset)
14138                    {
14139                        my $Start1 = $CompleteSignature{1}{$Symbol}{"Param"}{0}{"offset"};
14140                        my $Start2 = $CompleteSignature{2}{$Symbol}{"Param"}{0}{"offset"};
14141
14142                        $Old_Offset = $Old_Offset - $Start1;
14143                        $New_Offset = $New_Offset - $Start2;
14144
14145                        if($Old_Offset ne $New_Offset)
14146                        {
14147                            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Changed_Offset"}{$Parameter_Location}}=(
14148                                "Target"=>$PName1,
14149                                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14150                                "Old_Value"=>$Old_Offset,
14151                                "New_Value"=>$New_Offset  );
14152                        }
14153                    }
14154                }
14155            }
14156        }
14157    }
14158    if(checkDump(1, "2.0") and checkDump(2, "2.0")
14159    and $UsedDump{1}{"V"} ne "3.1" and $UsedDump{2}{"V"} ne "3.1")
14160    { # "default" attribute added in ACC 1.22 (dump 2.0 format)
14161      # broken in 3.1, fixed in 3.2
14162        my $Value_Old = $CompleteSignature{1}{$Symbol}{"Param"}{$ParamPos1}{"default"};
14163        my $Value_New = $CompleteSignature{2}{$PSymbol}{"Param"}{$ParamPos2}{"default"};
14164        if(not checkDump(1, "2.13")
14165        and checkDump(2, "2.13"))
14166        { # support for old ABI dumps
14167            if(defined $Value_Old and defined $Value_New)
14168            {
14169                if($PureType1{"Name"} eq "bool"
14170                and $Value_Old eq "false" and $Value_New eq "0")
14171                { # int class::method ( bool p = 0 );
14172                  # old ABI dumps: "false"
14173                  # new ABI dumps: "0"
14174                    $Value_Old = "0";
14175                }
14176            }
14177        }
14178        if(not checkDump(1, "2.18")
14179        and checkDump(2, "2.18"))
14180        { # support for old ABI dumps
14181            if(not defined $Value_Old
14182            and substr($Value_New, 0, 2) eq "_Z") {
14183                $Value_Old = $Value_New;
14184            }
14185        }
14186        if(defined $Value_Old)
14187        {
14188            $Value_Old = showVal($Value_Old, $PType1_Id, 1);
14189            if(defined $Value_New)
14190            {
14191                $Value_New = showVal($Value_New, $PType2_Id, 2);
14192                if($Value_Old ne $Value_New)
14193                { # FIXME: how to distinguish "0" and 0 (NULL)
14194                    %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Changed"}{$Parameter_Location}}=(
14195                        "Target"=>$PName1,
14196                        "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14197                        "Old_Value"=>$Value_Old,
14198                        "New_Value"=>$Value_New  );
14199                }
14200            }
14201            else
14202            {
14203                %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Removed"}{$Parameter_Location}}=(
14204                    "Target"=>$PName1,
14205                    "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14206                    "Old_Value"=>$Value_Old  );
14207            }
14208        }
14209        elsif(defined $Value_New)
14210        {
14211            $Value_New = showVal($Value_New, $PType2_Id, 2);
14212            %{$CompatProblems{$Level}{$Symbol}{"Parameter_Default_Value_Added"}{$Parameter_Location}}=(
14213                "Target"=>$PName1,
14214                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14215                "New_Value"=>$Value_New  );
14216        }
14217    }
14218
14219    if($ChkRnmd)
14220    {
14221        if($PName1 and $PName2 and $PName1 ne $PName2
14222        and $PType1_Id!=-1 and $PType2_Id!=-1
14223        and $PName1!~/\Ap\d+\Z/ and $PName2!~/\Ap\d+\Z/)
14224        { # except unnamed "..." value list (Id=-1)
14225            %{$CompatProblems{$Level}{$Symbol}{"Renamed_Parameter"}{showPos($ParamPos1)." Parameter"}}=(
14226                "Target"=>$PName1,
14227                "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14228                "Param_Type"=>$TypeInfo{1}{$PType1_Id}{"Name"},
14229                "Old_Value"=>$PName1,
14230                "New_Value"=>$PName2,
14231                "New_Signature"=>get_Signature($Symbol, 2)  );
14232        }
14233    }
14234
14235    # checking type change (replace)
14236    my %SubProblems = detectTypeChange($PType1_Id, $PType2_Id, "Parameter", $Level);
14237
14238    foreach my $SubProblemType (keys(%SubProblems))
14239    { # add new problems, remove false alarms
14240        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14241        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14242
14243        # quals
14244        if($SubProblemType eq "Parameter_Type"
14245        or $SubProblemType eq "Parameter_Type_And_Size"
14246        or $SubProblemType eq "Parameter_Type_Format")
14247        {
14248            if(checkDump(1, "2.6") and checkDump(2, "2.6"))
14249            {
14250                if(addedQual($Old_Value, $New_Value, "restrict")) {
14251                    %{$SubProblems{"Parameter_Became_Restrict"}} = %{$SubProblems{$SubProblemType}};
14252                }
14253                elsif(removedQual($Old_Value, $New_Value, "restrict")) {
14254                    %{$SubProblems{"Parameter_Became_Non_Restrict"}} = %{$SubProblems{$SubProblemType}};
14255                }
14256            }
14257            if(checkDump(1, "2.6") and checkDump(2, "2.6"))
14258            {
14259                if(removedQual($Old_Value, $New_Value, "volatile")) {
14260                    %{$SubProblems{"Parameter_Became_Non_Volatile"}} = %{$SubProblems{$SubProblemType}};
14261                }
14262            }
14263            if($Type2{"Type"} eq "Const" and $BaseType2{"Name"} eq $Type1{"Name"}
14264            and $Type1{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
14265            { # int to "int const"
14266                delete($SubProblems{$SubProblemType});
14267            }
14268            elsif($Type1{"Type"} eq "Const" and $BaseType1{"Name"} eq $Type2{"Name"}
14269            and $Type2{"Type"}=~/Intrinsic|Class|Struct|Union|Enum/)
14270            { # "int const" to int
14271                delete($SubProblems{$SubProblemType});
14272            }
14273            elsif(my $RR = removedQual($Old_Value, $New_Value, "const"))
14274            { # "const" to non-"const"
14275                if($RR==2) {
14276                    %{$SubProblems{"Parameter_Removed_Const"}} = %{$SubProblems{$SubProblemType}};
14277                }
14278                else {
14279                    %{$SubProblems{"Parameter_Became_Non_Const"}} = %{$SubProblems{$SubProblemType}};
14280                }
14281            }
14282        }
14283    }
14284
14285    if($Level eq "Source")
14286    {
14287        foreach my $SubProblemType (keys(%SubProblems))
14288        {
14289            my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14290            my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14291
14292            if($SubProblemType eq "Parameter_Type")
14293            {
14294                if(cmpBTypes($Old_Value, $New_Value, 1, 2)) {
14295                    delete($SubProblems{$SubProblemType});
14296                }
14297            }
14298        }
14299    }
14300
14301    foreach my $SubProblemType (keys(%SubProblems))
14302    { # modify/register problems
14303        my $New_Value = $SubProblems{$SubProblemType}{"New_Value"};
14304        my $Old_Value = $SubProblems{$SubProblemType}{"Old_Value"};
14305        my $New_Size = $SubProblems{$SubProblemType}{"New_Size"};
14306        my $Old_Size = $SubProblems{$SubProblemType}{"Old_Size"};
14307
14308        my $NewProblemType = $SubProblemType;
14309        if($Old_Value eq "..." and $New_Value ne "...")
14310        { # change from "..." to "int"
14311            if($ParamPos1==0)
14312            { # ISO C requires a named argument before "..."
14313                next;
14314            }
14315            $NewProblemType = "Parameter_Became_Non_VaList";
14316        }
14317        elsif($New_Value eq "..." and $Old_Value ne "...")
14318        { # change from "int" to "..."
14319            if($ParamPos2==0)
14320            { # ISO C requires a named argument before "..."
14321                next;
14322            }
14323            $NewProblemType = "Parameter_Became_VaList";
14324        }
14325        elsif($Level eq "Binary" and ($SubProblemType eq "Parameter_Type_And_Size"
14326        or $SubProblemType eq "Parameter_Type" or $SubProblemType eq "Parameter_Type_Format"))
14327        {
14328            my ($Arch1, $Arch2) = (getArch(1), getArch(2));
14329            if($Arch1 eq "unknown"
14330            or $Arch2 eq "unknown")
14331            { # if one of the architectures is unknown
14332              # then set other arhitecture to unknown too
14333                ($Arch1, $Arch2) = ("unknown", "unknown");
14334            }
14335            my (%Conv1, %Conv2) = ();
14336            if($UseConv_Real{1}{"P"} and $UseConv_Real{2}{"P"})
14337            { # real
14338                %Conv1 = callingConvention_P_Real($CompleteSignature{1}{$Symbol}, $ParamPos1);
14339                %Conv2 = callingConvention_P_Real($CompleteSignature{2}{$Symbol}, $ParamPos2);
14340            }
14341            else
14342            { # model
14343                %Conv1 = callingConvention_P_Model($CompleteSignature{1}{$Symbol}, $ParamPos1, $TypeInfo{1}, $Arch1, $OStarget, $WORD_SIZE{1});
14344                %Conv2 = callingConvention_P_Model($CompleteSignature{2}{$Symbol}, $ParamPos2, $TypeInfo{2}, $Arch2, $OStarget, $WORD_SIZE{2});
14345            }
14346            if($Conv1{"Method"} eq $Conv2{"Method"})
14347            {
14348                if($Conv1{"Method"} eq "stack")
14349                {
14350                    if($Old_Size ne $New_Size) { # FIXME: isMemPadded, getOffset
14351                        $NewProblemType = "Parameter_Type_And_Stack";
14352                    }
14353                }
14354                elsif($Conv1{"Method"} eq "reg")
14355                {
14356                    if($Conv1{"Registers"} ne $Conv2{"Registers"}) {
14357                        $NewProblemType = "Parameter_Type_And_Register";
14358                    }
14359                }
14360            }
14361            else
14362            {
14363                if($Conv1{"Method"} eq "stack") {
14364                    $NewProblemType = "Parameter_Type_From_Stack_To_Register";
14365                }
14366                elsif($Conv1{"Method"} eq "register") {
14367                    $NewProblemType = "Parameter_Type_From_Register_To_Stack";
14368                }
14369            }
14370            $SubProblems{$SubProblemType}{"Old_Reg"} = $Conv1{"Registers"};
14371            $SubProblems{$SubProblemType}{"New_Reg"} = $Conv2{"Registers"};
14372        }
14373        %{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}=(
14374            "Target"=>$PName1,
14375            "Param_Pos"=>adjustParamPos($ParamPos1, $Symbol, 1),
14376            "New_Signature"=>get_Signature($Symbol, 2) );
14377        @{$CompatProblems{$Level}{$Symbol}{$NewProblemType}{$Parameter_Location}}{keys(%{$SubProblems{$SubProblemType}})} = values %{$SubProblems{$SubProblemType}};
14378    }
14379
14380    @RecurTypes = ();
14381
14382    # checking type definition changes
14383    my $Sub_SubProblems = mergeTypes($PType1_Id, $PType2_Id, $Level);
14384    foreach my $SubProblemType (keys(%{$Sub_SubProblems}))
14385    {
14386        foreach my $SubLocation (keys(%{$Sub_SubProblems->{$SubProblemType}}))
14387        {
14388            my $NewProblemType = $SubProblemType;
14389            if($SubProblemType eq "DataType_Size")
14390            {
14391                if($PureType1{"Type"}!~/\A(Pointer|Ref)\Z/ and $SubLocation!~/\-\>/)
14392                { # stack has been affected
14393                    $NewProblemType = "DataType_Size_And_Stack";
14394                }
14395            }
14396            my $NewLocation = ($SubLocation)?$Parameter_Location."->".$SubLocation:$Parameter_Location;
14397            $CompatProblems{$Level}{$Symbol}{$NewProblemType}{$NewLocation} = $Sub_SubProblems->{$SubProblemType}{$SubLocation};
14398        }
14399    }
14400}
14401
14402sub find_ParamPair_Pos_byName($$$)
14403{
14404    my ($Name, $Symbol, $LibVersion) = @_;
14405    foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
14406    {
14407        next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos});
14408        if($CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"name"} eq $Name)
14409        {
14410            return $ParamPos;
14411        }
14412    }
14413    return "lost";
14414}
14415
14416sub find_ParamPair_Pos_byTypeAndPos($$$$$)
14417{
14418    my ($TypeName, $MediumPos, $Order, $Symbol, $LibVersion) = @_;
14419    my @Positions = ();
14420    foreach my $ParamPos (sort {int($a)<=>int($b)} keys(%{$CompleteSignature{$LibVersion}{$Symbol}{"Param"}}))
14421    {
14422        next if($Order eq "backward" and $ParamPos>$MediumPos);
14423        next if($Order eq "forward" and $ParamPos<$MediumPos);
14424        next if(not defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos});
14425        my $PTypeId = $CompleteSignature{$LibVersion}{$Symbol}{"Param"}{$ParamPos}{"type"};
14426        if($TypeInfo{$LibVersion}{$PTypeId}{"Name"} eq $TypeName) {
14427            push(@Positions, $ParamPos);
14428        }
14429    }
14430    return @Positions;
14431}
14432
14433sub getTypeIdByName($$)
14434{
14435    my ($TypeName, $LibVersion) = @_;
14436    return $TName_Tid{$LibVersion}{formatName($TypeName, "T")};
14437}
14438
14439sub diffTypes($$$)
14440{
14441    if(defined $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]}) {
14442        return $Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]};
14443    }
14444    if(isRecurType($_[0], $_[1], \@RecurTypes_Diff))
14445    { # skip recursive declarations
14446        return 0;
14447    }
14448
14449    pushType($_[0], $_[1], \@RecurTypes_Diff);
14450    my $Diff = diffTypes_I(@_);
14451    pop(@RecurTypes_Diff);
14452
14453    return ($Cache{"diffTypes"}{$_[2]}{$_[0]}{$_[1]} = $Diff);
14454}
14455
14456sub diffTypes_I($$$)
14457{
14458    my ($Type1_Id, $Type2_Id, $Level) = @_;
14459
14460    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
14461    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
14462
14463    if($Type1_Pure{"Name"} eq $Type2_Pure{"Name"})
14464    { # equal types
14465        return 0;
14466    }
14467    if($Type1_Pure{"Name"} eq "void")
14468    { # from void* to something
14469        return 0;
14470    }
14471    if($Type1_Pure{"Name"}=~/\*/
14472    or $Type2_Pure{"Name"}=~/\*/)
14473    { # compared in detectTypeChange()
14474        return 0;
14475    }
14476
14477    my %FloatType = map {$_=>1} (
14478        "float",
14479        "double",
14480        "long double"
14481    );
14482
14483    my $T1 = $Type1_Pure{"Type"};
14484    my $T2 = $Type2_Pure{"Type"};
14485
14486    if($T1 eq "Struct"
14487    and $T2 eq "Class")
14488    { # compare as data structures
14489        $T2 = "Struct";
14490    }
14491
14492    if($T1 eq "Class"
14493    and $T2 eq "Struct")
14494    { # compare as data structures
14495        $T1 = "Struct";
14496    }
14497
14498    if($T1 ne $T2)
14499    { # different types
14500        if($T1 eq "Intrinsic"
14501        and $T2 eq "Enum")
14502        { # "int" to "enum"
14503            return 0;
14504        }
14505        elsif($T2 eq "Intrinsic"
14506        and $T1 eq "Enum")
14507        { # "enum" to "int"
14508            return 0;
14509        }
14510        else
14511        { # union to struct
14512          #  ...
14513            return 1;
14514        }
14515    }
14516    else
14517    {
14518        if($T1 eq "Intrinsic")
14519        {
14520            if($FloatType{$Type1_Pure{"Name"}}
14521            or $FloatType{$Type2_Pure{"Name"}})
14522            { # "float" to "double"
14523              # "float" to "int"
14524                if($Level eq "Source")
14525                { # Safe
14526                    return 0;
14527                }
14528                else {
14529                    return 1;
14530                }
14531            }
14532        }
14533        elsif($T1=~/Class|Struct|Union|Enum/)
14534        {
14535            my @Membs1 = keys(%{$Type1_Pure{"Memb"}});
14536            my @Membs2 = keys(%{$Type2_Pure{"Memb"}});
14537            if(not @Membs1
14538            or not @Membs2)
14539            { # private
14540                return 0;
14541            }
14542            if($#Membs1!=$#Membs2)
14543            { # different number of elements
14544                return 1;
14545            }
14546            if($T1 eq "Enum")
14547            {
14548                foreach my $Pos (@Membs1)
14549                { # compare elements by name and value
14550                    if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"}
14551                    or $Type1_Pure{"Memb"}{$Pos}{"value"} ne $Type2_Pure{"Memb"}{$Pos}{"value"})
14552                    { # different names
14553                        return 1;
14554                    }
14555                }
14556            }
14557            else
14558            {
14559                foreach my $Pos (@Membs1)
14560                {
14561                    if($Level eq "Source")
14562                    {
14563                        if($Type1_Pure{"Memb"}{$Pos}{"name"} ne $Type2_Pure{"Memb"}{$Pos}{"name"})
14564                        { # different names
14565                            return 1;
14566                        }
14567                    }
14568
14569                    my %MT1 = %{$TypeInfo{1}{$Type1_Pure{"Memb"}{$Pos}{"type"}}};
14570                    my %MT2 = %{$TypeInfo{2}{$Type2_Pure{"Memb"}{$Pos}{"type"}}};
14571
14572                    if($MT1{"Name"} ne $MT2{"Name"}
14573                    or isAnon($MT1{"Name"}) or isAnon($MT2{"Name"}))
14574                    {
14575                        my $PL1 = get_PLevel($MT1{"Tid"}, 1);
14576                        my $PL2 = get_PLevel($MT2{"Tid"}, 2);
14577
14578                        if($PL1 ne $PL2)
14579                        { # different pointer level
14580                            return 1;
14581                        }
14582
14583                        # compare base types
14584                        my %BT1 = get_BaseType($MT1{"Tid"}, 1);
14585                        my %BT2 = get_BaseType($MT2{"Tid"}, 2);
14586
14587                        if(diffTypes($BT1{"Tid"}, $BT2{"Tid"}, $Level))
14588                        { # different types
14589                            return 1;
14590                        }
14591                    }
14592                }
14593            }
14594        }
14595        else
14596        {
14597            # TODO: arrays, etc.
14598        }
14599    }
14600    return 0;
14601}
14602
14603sub detectTypeChange($$$$)
14604{
14605    my ($Type1_Id, $Type2_Id, $Prefix, $Level) = @_;
14606    if(not $Type1_Id or not $Type2_Id) {
14607        return ();
14608    }
14609    my %LocalProblems = ();
14610    my %Type1 = get_Type($Type1_Id, 1);
14611    my %Type2 = get_Type($Type2_Id, 2);
14612    my %Type1_Pure = get_PureType($Type1_Id, $TypeInfo{1});
14613    my %Type2_Pure = get_PureType($Type2_Id, $TypeInfo{2});
14614    my %Type1_Base = ($Type1_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type1_Pure{"Tid"}, $TypeInfo{1}):get_BaseType($Type1_Id, 1);
14615    my %Type2_Base = ($Type2_Pure{"Type"} eq "Array")?get_OneStep_BaseType($Type2_Pure{"Tid"}, $TypeInfo{2}):get_BaseType($Type2_Id, 2);
14616
14617    my $Type1_PLevel = get_PLevel($Type1_Id, 1);
14618    my $Type2_PLevel = get_PLevel($Type2_Id, 2);
14619    return () if(not $Type1{"Name"} or not $Type2{"Name"});
14620    return () if(not $Type1_Base{"Name"} or not $Type2_Base{"Name"});
14621    return () if($Type1_PLevel eq "" or $Type2_PLevel eq "");
14622    if($Type1_Base{"Name"} ne $Type2_Base{"Name"}
14623    and ($Type1{"Name"} eq $Type2{"Name"} or ($Type1_PLevel>=1 and $Type1_PLevel==$Type2_PLevel
14624    and $Type1_Base{"Name"} ne "void" and $Type2_Base{"Name"} ne "void")))
14625    { # base type change
14626        if($Type1{"Name"} eq $Type2{"Name"})
14627        {
14628            if($Type1{"Type"} eq "Typedef" and $Type2{"Type"} eq "Typedef")
14629            { # will be reported in mergeTypes() as typedef problem
14630                return ();
14631            }
14632            my %Typedef_1 = goToFirst($Type1{"Tid"}, 1, "Typedef");
14633            my %Typedef_2 = goToFirst($Type2{"Tid"}, 2, "Typedef");
14634            if(%Typedef_1 and %Typedef_2)
14635            {
14636                if($Typedef_1{"Name"} eq $Typedef_2{"Name"}
14637                and $Typedef_1{"Type"} eq "Typedef" and $Typedef_2{"Type"} eq "Typedef")
14638                { # const Typedef
14639                    return ();
14640                }
14641            }
14642        }
14643        if($Type1_Base{"Name"}!~/anon\-/ and $Type2_Base{"Name"}!~/anon\-/)
14644        {
14645            if($Level eq "Binary"
14646            and $Type1_Base{"Size"} and $Type2_Base{"Size"}
14647            and $Type1_Base{"Size"} ne $Type2_Base{"Size"})
14648            {
14649                %{$LocalProblems{$Prefix."_BaseType_And_Size"}}=(
14650                    "Old_Value"=>$Type1_Base{"Name"},
14651                    "New_Value"=>$Type2_Base{"Name"},
14652                    "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14653                    "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14654            }
14655            else
14656            {
14657                if(diffTypes($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Level))
14658                { # format change
14659                    %{$LocalProblems{$Prefix."_BaseType_Format"}}=(
14660                        "Old_Value"=>$Type1_Base{"Name"},
14661                        "New_Value"=>$Type2_Base{"Name"},
14662                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14663                        "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14664                }
14665                elsif(tNameLock($Type1_Base{"Tid"}, $Type2_Base{"Tid"}))
14666                {
14667                    %{$LocalProblems{$Prefix."_BaseType"}}=(
14668                        "Old_Value"=>$Type1_Base{"Name"},
14669                        "New_Value"=>$Type2_Base{"Name"},
14670                        "Old_Size"=>$Type1_Base{"Size"}*$BYTE_SIZE,
14671                        "New_Size"=>$Type2_Base{"Size"}*$BYTE_SIZE);
14672                }
14673            }
14674        }
14675    }
14676    elsif($Type1{"Name"} ne $Type2{"Name"})
14677    { # type change
14678        if($Type1{"Name"}!~/anon\-/ and $Type2{"Name"}!~/anon\-/)
14679        {
14680            if($Prefix eq "Return"
14681            and $Type1_Pure{"Name"} eq "void")
14682            {
14683                %{$LocalProblems{"Return_Type_From_Void"}}=(
14684                    "New_Value"=>$Type2{"Name"},
14685                    "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14686            }
14687            elsif($Prefix eq "Return"
14688            and $Type2_Pure{"Name"} eq "void")
14689            {
14690                %{$LocalProblems{"Return_Type_Became_Void"}}=(
14691                    "Old_Value"=>$Type1{"Name"},
14692                    "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE);
14693            }
14694            else
14695            {
14696                if($Level eq "Binary"
14697                and $Type1{"Size"} and $Type2{"Size"}
14698                and $Type1{"Size"} ne $Type2{"Size"})
14699                {
14700                    %{$LocalProblems{$Prefix."_Type_And_Size"}}=(
14701                        "Old_Value"=>$Type1{"Name"},
14702                        "New_Value"=>$Type2{"Name"},
14703                        "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
14704                        "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14705                }
14706                else
14707                {
14708                    if(diffTypes($Type1_Id, $Type2_Id, $Level))
14709                    { # format change
14710                        %{$LocalProblems{$Prefix."_Type_Format"}}=(
14711                            "Old_Value"=>$Type1{"Name"},
14712                            "New_Value"=>$Type2{"Name"},
14713                            "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
14714                            "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14715                    }
14716                    elsif(tNameLock($Type1_Id, $Type2_Id))
14717                    { # FIXME: correct this condition
14718                        %{$LocalProblems{$Prefix."_Type"}}=(
14719                            "Old_Value"=>$Type1{"Name"},
14720                            "New_Value"=>$Type2{"Name"},
14721                            "Old_Size"=>$Type1{"Size"}*$BYTE_SIZE,
14722                            "New_Size"=>$Type2{"Size"}*$BYTE_SIZE);
14723                    }
14724                }
14725            }
14726        }
14727    }
14728    if($Type1_PLevel!=$Type2_PLevel)
14729    {
14730        if($Type1{"Name"} ne "void" and $Type1{"Name"} ne "..."
14731        and $Type2{"Name"} ne "void" and $Type2{"Name"} ne "...")
14732        {
14733            if($Level eq "Source")
14734            {
14735                %{$LocalProblems{$Prefix."_PointerLevel"}}=(
14736                    "Old_Value"=>$Type1_PLevel,
14737                    "New_Value"=>$Type2_PLevel);
14738            }
14739            else
14740            {
14741                if($Type2_PLevel>$Type1_PLevel)
14742                {
14743                    %{$LocalProblems{$Prefix."_PointerLevel_Increased"}}=(
14744                        "Old_Value"=>$Type1_PLevel,
14745                        "New_Value"=>$Type2_PLevel);
14746                }
14747                else
14748                {
14749                    %{$LocalProblems{$Prefix."_PointerLevel_Decreased"}}=(
14750                        "Old_Value"=>$Type1_PLevel,
14751                        "New_Value"=>$Type2_PLevel);
14752                }
14753            }
14754        }
14755    }
14756    if($Type1_Pure{"Type"} eq "Array"
14757    and $Type1_Pure{"BaseType"})
14758    { # base_type[N] -> base_type[N]
14759      # base_type: older_structure -> typedef to newer_structure
14760        my %SubProblems = detectTypeChange($Type1_Base{"Tid"}, $Type2_Base{"Tid"}, $Prefix, $Level);
14761        foreach my $SubProblemType (keys(%SubProblems))
14762        {
14763            $SubProblemType=~s/_Type/_BaseType/g;
14764            next if(defined $LocalProblems{$SubProblemType});
14765            foreach my $Attr (keys(%{$SubProblems{$SubProblemType}})) {
14766                $LocalProblems{$SubProblemType}{$Attr} = $SubProblems{$SubProblemType}{$Attr};
14767            }
14768        }
14769    }
14770    return %LocalProblems;
14771}
14772
14773sub tNameLock($$)
14774{
14775    my ($Tid1, $Tid2) = @_;
14776    my $Changed = 0;
14777    if(differentDumps("G"))
14778    { # different GCC versions
14779        $Changed = 1;
14780    }
14781    elsif(differentDumps("V"))
14782    { # different versions of ABI dumps
14783        if(not checkDump(1, "2.20")
14784        or not checkDump(2, "2.20"))
14785        { # latest names update
14786          # 2.6: added restrict qualifier
14787          # 2.13: added missed typedefs to qualified types
14788          # 2.20: prefix for struct, union and enum types
14789            $Changed = 1;
14790        }
14791    }
14792
14793    my $TN1 = $TypeInfo{1}{$Tid1}{"Name"};
14794    my $TN2 = $TypeInfo{2}{$Tid2}{"Name"};
14795
14796    my $TT1 = $TypeInfo{1}{$Tid1}{"Type"};
14797    my $TT2 = $TypeInfo{2}{$Tid2}{"Type"};
14798
14799    if($Changed)
14800    { # different formats
14801        my %Base1 = get_Type($Tid1, 1);
14802        while(defined $Base1{"Type"} and $Base1{"Type"} eq "Typedef") {
14803            %Base1 = get_OneStep_BaseType($Base1{"Tid"}, $TypeInfo{1});
14804        }
14805        my %Base2 = get_Type($Tid2, 2);
14806        while(defined $Base2{"Type"} and $Base2{"Type"} eq "Typedef") {
14807            %Base2 = get_OneStep_BaseType($Base2{"Tid"}, $TypeInfo{2});
14808        }
14809        my $BName1 = uncover_typedefs($Base1{"Name"}, 1);
14810        my $BName2 = uncover_typedefs($Base2{"Name"}, 2);
14811        if($BName1 eq $BName2)
14812        { # equal base types
14813            return 0;
14814        }
14815
14816        if(not checkDump(1, "2.13")
14817        or not checkDump(2, "2.13"))
14818        { # broken array names in ABI dumps < 2.13
14819            if($TT1 eq "Array"
14820            and $TT2 eq "Array") {
14821                return 0;
14822            }
14823        }
14824
14825        if(not checkDump(1, "2.6")
14826        or not checkDump(2, "2.6"))
14827        { # added restrict attribute in 2.6
14828            if($TN1!~/\brestrict\b/
14829            and $TN2=~/\brestrict\b/) {
14830                return 0;
14831            }
14832        }
14833
14834        if(not checkDump(1, "2.20")
14835        or not checkDump(2, "2.20"))
14836        { # added type prefix in 2.20
14837            if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/
14838            or $TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) {
14839                return 0;
14840            }
14841        }
14842    }
14843    else
14844    {
14845        # typedef struct {...} type_t
14846        # typedef struct type_t {...} type_t
14847        if(index($TN1, " ".$TN2)!=-1)
14848        {
14849            if($TN1=~/\A(struct|union|enum) \Q$TN2\E\Z/) {
14850                return 0;
14851            }
14852        }
14853        if(index($TN2, " ".$TN1)!=-1)
14854        {
14855            if($TN2=~/\A(struct|union|enum) \Q$TN1\E\Z/) {
14856                return 0;
14857            }
14858        }
14859
14860        if($TT1 eq "FuncPtr"
14861        and $TT2 eq "FuncPtr")
14862        {
14863            my $TN1_C = $TN1;
14864            my $TN2_C = $TN2;
14865
14866            $TN1_C=~s/\b(struct|union) //g;
14867            $TN2_C=~s/\b(struct|union) //g;
14868
14869            if($TN1_C eq $TN2_C) {
14870                return 0;
14871            }
14872        }
14873    }
14874
14875    my ($N1, $N2) = ($TN1, $TN2);
14876    $N1=~s/\b(struct|union) //g;
14877    $N2=~s/\b(struct|union) //g;
14878
14879    if($N1 eq $N2)
14880    { # QList<struct QUrl> and QList<QUrl>
14881        return 0;
14882    }
14883
14884    return 1;
14885}
14886
14887sub differentDumps($)
14888{
14889    my $Check = $_[0];
14890    if(defined $Cache{"differentDumps"}{$Check}) {
14891        return $Cache{"differentDumps"}{$Check};
14892    }
14893    if($UsedDump{1}{"V"} and $UsedDump{2}{"V"})
14894    {
14895        if($Check eq "G")
14896        {
14897            if(getGccVersion(1) ne getGccVersion(2))
14898            { # different GCC versions
14899                return ($Cache{"differentDumps"}{$Check}=1);
14900            }
14901        }
14902        if($Check eq "V")
14903        {
14904            if(cmpVersions(formatVersion($UsedDump{1}{"V"}, 2),
14905            formatVersion($UsedDump{2}{"V"}, 2))!=0)
14906            { # different dump versions (skip micro version)
14907                return ($Cache{"differentDumps"}{$Check}=1);
14908            }
14909        }
14910    }
14911    return ($Cache{"differentDumps"}{$Check}=0);
14912}
14913
14914sub formatVersion($$)
14915{ # cut off version digits
14916    my ($V, $Digits) = @_;
14917    my @Elems = split(/\./, $V);
14918    return join(".", splice(@Elems, 0, $Digits));
14919}
14920
14921sub htmlSpecChars($)
14922{
14923    my $Str = $_[0];
14924    if(not $Str) {
14925        return $Str;
14926    }
14927    $Str=~s/\&([^#]|\Z)/&amp;$1/g;
14928    $Str=~s/</&lt;/g;
14929    $Str=~s/\-\>/&#45;&gt;/g; # &minus;
14930    $Str=~s/>/&gt;/g;
14931    $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g;
14932    $Str=~s/([^ ]) ([^ ])/$1\@SP\@$2/g;
14933    $Str=~s/ /&#160;/g; # &nbsp;
14934    $Str=~s/\@SP\@/ /g;
14935    $Str=~s/\n/<br\/>/g;
14936    $Str=~s/\"/&quot;/g;
14937    $Str=~s/\'/&#39;/g;
14938    return $Str;
14939}
14940
14941sub xmlSpecChars($)
14942{
14943    my $Str = $_[0];
14944    if(not $Str) {
14945        return $Str;
14946    }
14947
14948    $Str=~s/\&([^#]|\Z)/&amp;$1/g;
14949    $Str=~s/</&lt;/g;
14950    $Str=~s/>/&gt;/g;
14951
14952    $Str=~s/\"/&quot;/g;
14953    $Str=~s/\'/&#39;/g;
14954
14955    return $Str;
14956}
14957
14958sub xmlSpecChars_R($)
14959{
14960    my $Str = $_[0];
14961    if(not $Str) {
14962        return $Str;
14963    }
14964
14965    $Str=~s/&amp;/&/g;
14966    $Str=~s/&lt;/</g;
14967    $Str=~s/&gt;/>/g;
14968
14969    $Str=~s/&quot;/"/g;
14970    $Str=~s/&#39;/'/g;
14971
14972    return $Str;
14973}
14974
14975sub black_name($)
14976{
14977    my $Name = $_[0];
14978    return "<span class='iname_b'>".highLight_Signature($Name)."</span>";
14979}
14980
14981sub highLight_Signature($)
14982{
14983    my $Signature = $_[0];
14984    return highLight_Signature_PPos_Italic($Signature, "", 0, 0, 0);
14985}
14986
14987sub highLight_Signature_Italic_Color($)
14988{
14989    my $Signature = $_[0];
14990    return highLight_Signature_PPos_Italic($Signature, "", 1, 1, 1);
14991}
14992
14993sub separate_symbol($)
14994{
14995    my $Symbol = $_[0];
14996    my ($Name, $Spec, $Ver) = ($Symbol, "", "");
14997    if($Symbol=~/\A([^\@\$\?]+)([\@\$]+)([^\@\$]+)\Z/) {
14998        ($Name, $Spec, $Ver) = ($1, $2, $3);
14999    }
15000    return ($Name, $Spec, $Ver);
15001}
15002
15003sub cut_f_attrs($)
15004{
15005    if($_[0]=~s/(\))((| (const volatile|const|volatile))(| \[static\]))\Z/$1/) {
15006        return $2;
15007    }
15008    return "";
15009}
15010
15011sub highLight_Signature_PPos_Italic($$$$$)
15012{
15013    my ($FullSignature, $Param_Pos, $ItalicParams, $ColorParams, $ShowReturn) = @_;
15014    $Param_Pos = "" if(not defined $Param_Pos);
15015    if($CheckObjectsOnly) {
15016        $ItalicParams=$ColorParams=0;
15017    }
15018    my ($Signature, $VersionSpec, $SymbolVersion) = separate_symbol($FullSignature);
15019    my $Return = "";
15020    if($ShowRetVal and $Signature=~s/([^:]):([^:].+?)\Z/$1/g) {
15021        $Return = $2;
15022    }
15023    my $SCenter = find_center($Signature, "(");
15024    if(not $SCenter)
15025    { # global data
15026        $Signature = htmlSpecChars($Signature);
15027        $Signature=~s!(\[data\])!<span class='attr'>$1</span>!g;
15028        $Signature .= (($SymbolVersion)?"<span class='sym_ver'>&#160;$VersionSpec&#160;$SymbolVersion</span>":"");
15029        if($Return and $ShowReturn) {
15030            $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
15031        }
15032        return $Signature;
15033    }
15034    my ($Begin, $End) = (substr($Signature, 0, $SCenter), "");
15035    $Begin.=" " if($Begin!~/ \Z/);
15036    $End = cut_f_attrs($Signature);
15037    my @Parts = ();
15038    my ($Short, $Params) = split_Signature($Signature);
15039    my @SParts = separate_Params($Params, 1, 1);
15040    foreach my $Pos (0 .. $#SParts)
15041    {
15042        my $Part = $SParts[$Pos];
15043        $Part=~s/\A\s+|\s+\Z//g;
15044        my ($Part_Styled, $ParamName) = (htmlSpecChars($Part), "");
15045        if($Part=~/\([\*]+(\w+)\)/i) {
15046            $ParamName = $1;#func-ptr
15047        }
15048        elsif($Part=~/(\w+)[\,\)]*\Z/i) {
15049            $ParamName = $1;
15050        }
15051        if(not $ParamName)
15052        {
15053            push(@Parts, $Part_Styled);
15054            next;
15055        }
15056        if($ItalicParams and not $TName_Tid{1}{$Part}
15057        and not $TName_Tid{2}{$Part})
15058        {
15059            my $Style = "param";
15060            if($Param_Pos ne ""
15061            and $Pos==$Param_Pos) {
15062                $Style = "focus_p";
15063            }
15064            elsif($ColorParams) {
15065                $Style = "color_p";
15066            }
15067            $Part_Styled =~ s!(\W)$ParamName([\,\)]|\Z)!$1<span class=\'$Style\'>$ParamName</span>$2!ig;
15068        }
15069        $Part_Styled=~s/,(\w)/, $1/g;
15070        push(@Parts, $Part_Styled);
15071    }
15072    if(@Parts)
15073    {
15074        foreach my $Num (0 .. $#Parts)
15075        {
15076            if($Num==$#Parts)
15077            { # add ")" to the last parameter
15078                $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]." )</span>";
15079            }
15080            elsif(length($Parts[$Num])<=45) {
15081                $Parts[$Num] = "<span class='nowrap'>".$Parts[$Num]."</span>";
15082            }
15083        }
15084        $Signature = htmlSpecChars($Begin)."<span class='sym_p'>(&#160;".join(" ", @Parts)."</span>".$End;
15085    }
15086    else {
15087        $Signature = htmlSpecChars($Begin)."<span class='sym_p'>(&#160;)</span>".$End;
15088    }
15089    if($Return and $ShowReturn) {
15090        $Signature .= "<span class='sym_p nowrap'> &#160;<b>:</b>&#160;&#160;".htmlSpecChars($Return)."</span>";
15091    }
15092    $Signature=~s!\[\]![&#160;]!g;
15093    $Signature=~s!operator=!operator&#160;=!g;
15094    $Signature=~s!(\[in-charge\]|\[not-in-charge\]|\[in-charge-deleting\]|\[static\])!<span class='attr'>$1</span>!g;
15095    if($SymbolVersion) {
15096        $Signature .= "<span class='sym_ver'>&#160;$VersionSpec&#160;$SymbolVersion</span>";
15097    }
15098    return $Signature;
15099}
15100
15101sub split_Signature($)
15102{
15103    my $Signature = $_[0];
15104    if(my $ShortName = substr($Signature, 0, find_center($Signature, "(")))
15105    {
15106        $Signature=~s/\A\Q$ShortName\E\(//g;
15107        cut_f_attrs($Signature);
15108        $Signature=~s/\)\Z//;
15109        return ($ShortName, $Signature);
15110    }
15111
15112    # error
15113    return ($Signature, "");
15114}
15115
15116sub separate_Params($$$)
15117{
15118    my ($Params, $Comma, $Sp) = @_;
15119    my @Parts = ();
15120    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
15121    my $Part = 0;
15122    foreach my $Pos (0 .. length($Params) - 1)
15123    {
15124        my $S = substr($Params, $Pos, 1);
15125        if(defined $B{$S}) {
15126            $B{$S} += 1;
15127        }
15128        if($S eq "," and
15129        $B{"("}==$B{")"} and $B{"<"}==$B{">"})
15130        {
15131            if($Comma)
15132            { # include comma
15133                $Parts[$Part] .= $S;
15134            }
15135            $Part += 1;
15136        }
15137        else {
15138            $Parts[$Part] .= $S;
15139        }
15140    }
15141    if(not $Sp)
15142    { # remove spaces
15143        foreach (@Parts)
15144        {
15145            s/\A //g;
15146            s/ \Z//g;
15147        }
15148    }
15149    return @Parts;
15150}
15151
15152sub find_center($$)
15153{
15154    my ($Sign, $Target) = @_;
15155    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
15156    my $Center = 0;
15157    if($Sign=~s/(operator([^\w\s\(\)]+|\(\)))//g)
15158    { # operators
15159        $Center+=length($1);
15160    }
15161    foreach my $Pos (0 .. length($Sign)-1)
15162    {
15163        my $S = substr($Sign, $Pos, 1);
15164        if($S eq $Target)
15165        {
15166            if($B{"("}==$B{")"}
15167            and $B{"<"}==$B{">"}) {
15168                return $Center;
15169            }
15170        }
15171        if(defined $B{$S}) {
15172            $B{$S}+=1;
15173        }
15174        $Center+=1;
15175    }
15176    return 0;
15177}
15178
15179sub appendFile($$)
15180{
15181    my ($Path, $Content) = @_;
15182    return if(not $Path);
15183    if(my $Dir = get_dirname($Path)) {
15184        mkpath($Dir);
15185    }
15186    open(FILE, ">>", $Path) || die ("can't open file \'$Path\': $!\n");
15187    print FILE $Content;
15188    close(FILE);
15189}
15190
15191sub writeFile($$)
15192{
15193    my ($Path, $Content) = @_;
15194    return if(not $Path);
15195    if(my $Dir = get_dirname($Path)) {
15196        mkpath($Dir);
15197    }
15198    open(FILE, ">", $Path) || die ("can't open file \'$Path\': $!\n");
15199    print FILE $Content;
15200    close(FILE);
15201}
15202
15203sub readFile($)
15204{
15205    my $Path = $_[0];
15206    return "" if(not $Path or not -f $Path);
15207    open(FILE, $Path);
15208    local $/ = undef;
15209    my $Content = <FILE>;
15210    close(FILE);
15211    if($Path!~/\.(tu|class|abi)\Z/) {
15212        $Content=~s/\r/\n/g;
15213    }
15214    return $Content;
15215}
15216
15217sub get_filename($)
15218{ # much faster than basename() from File::Basename module
15219    if(defined $Cache{"get_filename"}{$_[0]}) {
15220        return $Cache{"get_filename"}{$_[0]};
15221    }
15222    if($_[0] and $_[0]=~/([^\/\\]+)[\/\\]*\Z/) {
15223        return ($Cache{"get_filename"}{$_[0]}=$1);
15224    }
15225    return ($Cache{"get_filename"}{$_[0]}="");
15226}
15227
15228sub get_dirname($)
15229{ # much faster than dirname() from File::Basename module
15230    if(defined $Cache{"get_dirname"}{$_[0]}) {
15231        return $Cache{"get_dirname"}{$_[0]};
15232    }
15233    if($_[0] and $_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) {
15234        return ($Cache{"get_dirname"}{$_[0]}=$1);
15235    }
15236    return ($Cache{"get_dirname"}{$_[0]}="");
15237}
15238
15239sub separate_path($) {
15240    return (get_dirname($_[0]), get_filename($_[0]));
15241}
15242
15243sub esc($)
15244{
15245    my $Str = $_[0];
15246    $Str=~s/([()\[\]{}$ &'"`;,<>\+])/\\$1/g;
15247    return $Str;
15248}
15249
15250sub readLineNum($$)
15251{
15252    my ($Path, $Num) = @_;
15253    return "" if(not $Path or not -f $Path);
15254    open(FILE, $Path);
15255    foreach (1 ... $Num) {
15256        <FILE>;
15257    }
15258    my $Line = <FILE>;
15259    close(FILE);
15260    return $Line;
15261}
15262
15263sub readAttributes($$)
15264{
15265    my ($Path, $Num) = @_;
15266    return () if(not $Path or not -f $Path);
15267    my %Attributes = ();
15268    if(readLineNum($Path, $Num)=~/<!--\s+(.+)\s+-->/)
15269    {
15270        foreach my $AttrVal (split(/;/, $1))
15271        {
15272            if($AttrVal=~/(.+):(.+)/)
15273            {
15274                my ($Name, $Value) = ($1, $2);
15275                $Attributes{$Name} = $Value;
15276            }
15277        }
15278    }
15279    return \%Attributes;
15280}
15281
15282sub is_abs($) {
15283    return ($_[0]=~/\A(\/|\w+:[\/\\])/);
15284}
15285
15286sub get_abs_path($)
15287{ # abs_path() should NOT be called for absolute inputs
15288  # because it can change them
15289    my $Path = $_[0];
15290    if(not is_abs($Path)) {
15291        $Path = abs_path($Path);
15292    }
15293    return $Path;
15294}
15295
15296sub get_OSgroup()
15297{
15298    my $N = $Config{"osname"};
15299    if($N=~/macos|darwin|rhapsody/i) {
15300        return "macos";
15301    }
15302    elsif($N=~/freebsd|openbsd|netbsd/i) {
15303        return "bsd";
15304    }
15305    elsif($N=~/haiku|beos/i) {
15306        return "beos";
15307    }
15308    elsif($N=~/symbian|epoc/i) {
15309        return "symbian";
15310    }
15311    elsif($N=~/win/i) {
15312        return "windows";
15313    }
15314    else {
15315        return $N;
15316    }
15317}
15318
15319sub getGccVersion($)
15320{
15321    my $LibVersion = $_[0];
15322    if($GCC_VERSION{$LibVersion})
15323    { # dump version
15324        return $GCC_VERSION{$LibVersion};
15325    }
15326    elsif($UsedDump{$LibVersion}{"V"})
15327    { # old-version dumps
15328        return "unknown";
15329    }
15330    my $GccVersion = get_dumpversion($GCC_PATH); # host version
15331    if(not $GccVersion) {
15332        return "unknown";
15333    }
15334    return $GccVersion;
15335}
15336
15337sub showArch($)
15338{
15339    my $Arch = $_[0];
15340    if($Arch eq "arm"
15341    or $Arch eq "mips") {
15342        return uc($Arch);
15343    }
15344    return $Arch;
15345}
15346
15347sub getArch($)
15348{
15349    my $LibVersion = $_[0];
15350
15351    if($TargetArch) {
15352        return $TargetArch;
15353    }
15354    elsif($CPU_ARCH{$LibVersion})
15355    { # dump version
15356        return $CPU_ARCH{$LibVersion};
15357    }
15358    elsif($UsedDump{$LibVersion}{"V"})
15359    { # old-version dumps
15360        return "unknown";
15361    }
15362
15363    return getArch_GCC($LibVersion);
15364}
15365
15366sub get_Report_Title($)
15367{
15368    my $Level = $_[0];
15369
15370    my $ArchInfo = " on <span style='color:Blue;'>".showArch(getArch(1))."</span>";
15371    if(getArch(1) ne getArch(2)
15372    or getArch(1) eq "unknown"
15373    or $Level eq "Source")
15374    { # don't show architecture in the header
15375        $ArchInfo="";
15376    }
15377    my $Title = "";
15378    if($Level eq "Source") {
15379        $Title .= "Source compatibility";
15380    }
15381    elsif($Level eq "Binary") {
15382        $Title .= "Binary compatibility";
15383    }
15384    else {
15385        $Title .= "API compatibility";
15386    }
15387
15388    if($UsedDump{1}{"DWARF"} and $UsedDump{2}{"DWARF"})
15389    {
15390        my $M1 = $UsedDump{1}{"M"};
15391        my $M2 = $UsedDump{2}{"M"};
15392
15393        if($M1 eq $M2)
15394        {
15395            $Title .= " report for the <span style='color:Blue;'>$M1</span> object";
15396            $Title .= " between <span style='color:Red;'>".$Descriptor{1}{"Version"}."</span> and <span style='color:Red;'>".$Descriptor{2}{"Version"}."</span> versions";
15397        }
15398        else
15399        {
15400            $Title .= " report between <span style='color:Blue;'>$M1</span> (<span style='color:Red;'>".$Descriptor{1}{"Version"}."</span>)";
15401            $Title .= " and <span style='color:Blue;'>$M2</span> (<span style='color:Red;'>".$Descriptor{2}{"Version"}."</span>) objects";
15402        }
15403    }
15404    else
15405    {
15406        $Title .= " report for the <span style='color:Blue;'>$TargetTitle</span> $TargetComponent";
15407        $Title .= " between <span style='color:Red;'>".$Descriptor{1}{"Version"}."</span> and <span style='color:Red;'>".$Descriptor{2}{"Version"}."</span> versions";
15408    }
15409
15410    $Title .= $ArchInfo;
15411
15412    if($AppPath) {
15413        $Title .= " <span class='nowrap'>&#160;(relating to the portability of application <span style='color:Blue;'>".get_filename($AppPath)."</span>)</span>";
15414    }
15415    $Title = "<h1>".$Title."</h1>\n";
15416    return $Title;
15417}
15418
15419sub get_CheckedHeaders($)
15420{
15421    my $LibVersion = $_[0];
15422
15423    my @Headers = ();
15424
15425    foreach my $Path (keys(%{$Registered_Headers{$LibVersion}}))
15426    {
15427        my $File = get_filename($Path);
15428        if(not is_target_header($File, $LibVersion)) {
15429            next;
15430        }
15431
15432        if(skipHeader($File, $LibVersion)) {
15433            next;
15434        }
15435
15436        push(@Headers, $Path);
15437    }
15438
15439    return @Headers;
15440}
15441
15442sub get_SourceInfo()
15443{
15444    my ($CheckedHeaders, $CheckedSources, $CheckedLibs) = ("", "");
15445    if(not $CheckObjectsOnly)
15446    {
15447        if(my @Headers = get_CheckedHeaders(1))
15448        {
15449            $CheckedHeaders = "<a name='Headers'></a><h2>Header Files (".($#Headers+1).")</h2><hr/>\n";
15450            $CheckedHeaders .= "<div class='h_list'>\n";
15451            foreach my $Header_Path (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
15452            {
15453                my $Identity = $Registered_Headers{1}{$Header_Path}{"Identity"};
15454                my $Name = get_filename($Identity);
15455                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15456                $CheckedHeaders .= $Name.$Comment."<br/>\n";
15457            }
15458            $CheckedHeaders .= "</div>\n";
15459            $CheckedHeaders .= "<br/>$TOP_REF<br/>\n";
15460        }
15461
15462        if(my @Sources = keys(%{$Registered_Sources{1}}))
15463        {
15464            $CheckedSources = "<a name='Sources'></a><h2>Source Files (".($#Sources+1).")</h2><hr/>\n";
15465            $CheckedSources .= "<div class='h_list'>\n";
15466            foreach my $Header_Path (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
15467            {
15468                my $Identity = $Registered_Sources{1}{$Header_Path}{"Identity"};
15469                my $Name = get_filename($Identity);
15470                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15471                $CheckedSources .= $Name.$Comment."<br/>\n";
15472            }
15473            $CheckedSources .= "</div>\n";
15474            $CheckedSources .= "<br/>$TOP_REF<br/>\n";
15475        }
15476    }
15477    if(not $CheckHeadersOnly)
15478    {
15479        $CheckedLibs = "<a name='Libs'></a><h2>".get_ObjTitle()." (".keys(%{$Library_Symbol{1}}).")</h2><hr/>\n";
15480        $CheckedLibs .= "<div class='lib_list'>\n";
15481        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$Library_Symbol{1}}))
15482        {
15483            $Library.=" (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
15484            $CheckedLibs .= $Library."<br/>\n";
15485        }
15486        $CheckedLibs .= "</div>\n";
15487        $CheckedLibs .= "<br/>$TOP_REF<br/>\n";
15488    }
15489    return $CheckedHeaders.$CheckedSources.$CheckedLibs;
15490}
15491
15492sub get_ObjTitle()
15493{
15494    if(defined $UsedDump{1}{"DWARF"}) {
15495        return "Objects";
15496    }
15497    else {
15498        return ucfirst($SLIB_TYPE)." Libraries";
15499    }
15500}
15501
15502sub get_TypeProblems_Count($$$)
15503{
15504    my ($TypeChanges, $TargetPriority, $Level) = @_;
15505    my $Type_Problems_Count = 0;
15506    foreach my $Type_Name (sort keys(%{$TypeChanges}))
15507    {
15508        my %Kinds_Target = ();
15509        foreach my $Kind (keys(%{$TypeChanges->{$Type_Name}}))
15510        {
15511            foreach my $Location (keys(%{$TypeChanges->{$Type_Name}{$Kind}}))
15512            {
15513                my $Target = $TypeChanges->{$Type_Name}{$Kind}{$Location}{"Target"};
15514                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15515                next if($Severity ne $TargetPriority);
15516                if($Kinds_Target{$Kind}{$Target}) {
15517                    next;
15518                }
15519
15520                if(my $MaxSeverity = $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target})
15521                {
15522                    if($Severity_Val{$MaxSeverity}>$Severity_Val{$Severity})
15523                    { # select a problem with the highest priority
15524                        next;
15525                    }
15526                }
15527                $Kinds_Target{$Kind}{$Target} = 1;
15528                $Type_Problems_Count += 1;
15529            }
15530        }
15531    }
15532    return $Type_Problems_Count;
15533}
15534
15535sub get_Summary($)
15536{
15537    my $Level = $_[0];
15538    my ($Added, $Removed, $I_Problems_High, $I_Problems_Medium, $I_Problems_Low, $T_Problems_High,
15539    $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);
15540    %{$RESULT{$Level}} = (
15541        "Problems"=>0,
15542        "Warnings"=>0,
15543        "Affected"=>0 );
15544    # check rules
15545    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15546    {
15547        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
15548        {
15549            if(not defined $CompatRules{$Level}{$Kind})
15550            { # unknown rule
15551                if(not $UnknownRules{$Level}{$Kind})
15552                { # only one warning
15553                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
15554                    $UnknownRules{$Level}{$Kind}=1;
15555                }
15556                delete($CompatProblems{$Level}{$Interface}{$Kind});
15557            }
15558        }
15559    }
15560    foreach my $Constant (sort keys(%{$CompatProblems_Constants{$Level}}))
15561    {
15562        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
15563        {
15564            if(not defined $CompatRules{$Level}{$Kind})
15565            { # unknown rule
15566                if(not $UnknownRules{$Level}{$Kind})
15567                { # only one warning
15568                    printMsg("WARNING", "unknown rule \"$Kind\" (\"$Level\")");
15569                    $UnknownRules{$Level}{$Kind}=1;
15570                }
15571                delete($CompatProblems_Constants{$Level}{$Constant}{$Kind});
15572            }
15573        }
15574    }
15575    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15576    {
15577        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
15578        {
15579            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols")
15580            {
15581                foreach my $Location (sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
15582                {
15583                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15584                    if($Kind eq "Added_Symbol") {
15585                        $Added += 1;
15586                    }
15587                    elsif($Kind eq "Removed_Symbol")
15588                    {
15589                        $Removed += 1;
15590                        $TotalAffected{$Level}{$Interface} = $Severity;
15591                    }
15592                    else
15593                    {
15594                        if($Severity eq "Safe") {
15595                            $I_Other += 1;
15596                        }
15597                        elsif($Severity eq "High") {
15598                            $I_Problems_High += 1;
15599                        }
15600                        elsif($Severity eq "Medium") {
15601                            $I_Problems_Medium += 1;
15602                        }
15603                        elsif($Severity eq "Low") {
15604                            $I_Problems_Low += 1;
15605                        }
15606                        if(($Severity ne "Low" or $StrictCompat)
15607                        and $Severity ne "Safe") {
15608                            $TotalAffected{$Level}{$Interface} = $Severity;
15609                        }
15610                    }
15611                }
15612            }
15613        }
15614    }
15615    my %TypeChanges = ();
15616    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
15617    {
15618        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
15619        {
15620            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
15621            {
15622                foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
15623                {
15624                    my $Type_Name = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"};
15625                    my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"};
15626                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15627                    my $MaxSeverity = $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target};
15628
15629                    if($MaxSeverity and $Severity_Val{$MaxSeverity}>$Severity_Val{$Severity})
15630                    { # select a problem with the highest priority
15631                        next;
15632                    }
15633
15634                    if(($Severity ne "Low" or $StrictCompat)
15635                    and $Severity ne "Safe")
15636                    {
15637                        if(defined $TotalAffected{$Level}{$Interface})
15638                        {
15639                            if($Severity_Val{$Severity}>$Severity_Val{$TotalAffected{$Level}{$Interface}}) {
15640                                $TotalAffected{$Level}{$Interface} = $Severity;
15641                            }
15642                        }
15643                        else {
15644                            $TotalAffected{$Level}{$Interface} = $Severity;
15645                        }
15646                    }
15647
15648                    $TypeChanges{$Type_Name}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location};
15649
15650                    if($MaxSeverity)
15651                    {
15652                        if($Severity_Val{$Severity}>$Severity_Val{$MaxSeverity}) {
15653                            $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target} = $Severity;
15654                        }
15655                    }
15656                    else {
15657                        $Type_MaxSeverity{$Level}{$Type_Name}{$Kind}{$Target} = $Severity;
15658                    }
15659                }
15660            }
15661        }
15662    }
15663
15664    $T_Problems_High = get_TypeProblems_Count(\%TypeChanges, "High", $Level);
15665    $T_Problems_Medium = get_TypeProblems_Count(\%TypeChanges, "Medium", $Level);
15666    $T_Problems_Low = get_TypeProblems_Count(\%TypeChanges, "Low", $Level);
15667    $T_Other = get_TypeProblems_Count(\%TypeChanges, "Safe", $Level);
15668
15669    %TypeChanges = (); # free memory
15670
15671    if($CheckObjectsOnly)
15672    { # only removed exported symbols
15673        $RESULT{$Level}{"Affected"} = $Removed*100/keys(%{$Symbol_Library{1}});
15674    }
15675    else
15676    { # changed and removed public symbols
15677        my $SCount = keys(%{$CheckedSymbols{$Level}});
15678        if($ExtendedCheck)
15679        { # don't count external_func_0 for constants
15680            $SCount-=1;
15681        }
15682        if($SCount)
15683        {
15684            my %Weight = (
15685                "High" => 100,
15686                "Medium" => 50,
15687                "Low" => 25
15688            );
15689            foreach (keys(%{$TotalAffected{$Level}})) {
15690                $RESULT{$Level}{"Affected"}+=$Weight{$TotalAffected{$Level}{$_}};
15691            }
15692            $RESULT{$Level}{"Affected"} = $RESULT{$Level}{"Affected"}/$SCount;
15693        }
15694        else {
15695            $RESULT{$Level}{"Affected"} = 0;
15696        }
15697    }
15698    $RESULT{$Level}{"Affected"} = show_number($RESULT{$Level}{"Affected"});
15699    if($RESULT{$Level}{"Affected"}>=100) {
15700        $RESULT{$Level}{"Affected"} = 100;
15701    }
15702
15703    $RESULT{$Level}{"Problems"} += $Removed;
15704    $RESULT{$Level}{"Problems"} += $T_Problems_High + $I_Problems_High;
15705    $RESULT{$Level}{"Problems"} += $T_Problems_Medium + $I_Problems_Medium;
15706    if($StrictCompat) {
15707        $RESULT{$Level}{"Problems"} += $T_Problems_Low + $I_Problems_Low;
15708    }
15709    else {
15710        $RESULT{$Level}{"Warnings"} += $T_Problems_Low + $I_Problems_Low;
15711    }
15712
15713    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
15714    {
15715        foreach my $Kind (keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
15716        {
15717            my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
15718            if($Severity eq "Safe")
15719            {
15720                $C_Other+=1;
15721            }
15722            elsif($Severity eq "Low")
15723            {
15724                $C_Problems_Low+=1;
15725            }
15726        }
15727    }
15728
15729    if($C_Problems_Low)
15730    {
15731        if($StrictCompat) {
15732            $RESULT{$Level}{"Problems"} += $C_Problems_Low;
15733        }
15734        else {
15735            $RESULT{$Level}{"Warnings"} += $C_Problems_Low;
15736        }
15737    }
15738    if($RESULT{$Level}{"Problems"}
15739    and $RESULT{$Level}{"Affected"}) {
15740        $RESULT{$Level}{"Verdict"} = "incompatible";
15741    }
15742    else {
15743        $RESULT{$Level}{"Verdict"} = "compatible";
15744    }
15745
15746    my $TotalTypes = keys(%{$CheckedTypes{$Level}});
15747    if(not $TotalTypes)
15748    { # list all the types
15749        $TotalTypes = keys(%{$TName_Tid{1}});
15750    }
15751
15752    my ($Arch1, $Arch2) = (getArch(1), getArch(2));
15753    my ($GccV1, $GccV2) = (getGccVersion(1), getGccVersion(2));
15754
15755    my ($TestInfo, $TestResults, $Problem_Summary) = ();
15756
15757    if($ReportFormat eq "xml")
15758    { # XML
15759        # test info
15760        $TestInfo .= "  <library>$TargetLibraryName</library>\n";
15761        $TestInfo .= "  <version1>\n";
15762        $TestInfo .= "    <number>".$Descriptor{1}{"Version"}."</number>\n";
15763        $TestInfo .= "    <arch>$Arch1</arch>\n";
15764        $TestInfo .= "    <gcc>$GccV1</gcc>\n";
15765        $TestInfo .= "  </version1>\n";
15766
15767        $TestInfo .= "  <version2>\n";
15768        $TestInfo .= "    <number>".$Descriptor{2}{"Version"}."</number>\n";
15769        $TestInfo .= "    <arch>$Arch2</arch>\n";
15770        $TestInfo .= "    <gcc>$GccV2</gcc>\n";
15771        $TestInfo .= "  </version2>\n";
15772        $TestInfo = "<test_info>\n".$TestInfo."</test_info>\n\n";
15773
15774        # test results
15775        if(my @Headers = keys(%{$Registered_Headers{1}}))
15776        {
15777            $TestResults .= "  <headers>\n";
15778            foreach my $Name (sort {lc($Registered_Headers{1}{$a}{"Identity"}) cmp lc($Registered_Headers{1}{$b}{"Identity"})} @Headers)
15779            {
15780                my $Identity = $Registered_Headers{1}{$Name}{"Identity"};
15781                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15782                $TestResults .= "    <name>".get_filename($Name).$Comment."</name>\n";
15783            }
15784            $TestResults .= "  </headers>\n";
15785        }
15786
15787        if(my @Sources = keys(%{$Registered_Sources{1}}))
15788        {
15789            $TestResults .= "  <sources>\n";
15790            foreach my $Name (sort {lc($Registered_Sources{1}{$a}{"Identity"}) cmp lc($Registered_Sources{1}{$b}{"Identity"})} @Sources)
15791            {
15792                my $Identity = $Registered_Sources{1}{$Name}{"Identity"};
15793                my $Comment = ($Identity=~/[\/\\]/)?" ($Identity)":"";
15794                $TestResults .= "    <name>".get_filename($Name).$Comment."</name>\n";
15795            }
15796            $TestResults .= "  </sources>\n";
15797        }
15798
15799        $TestResults .= "  <libs>\n";
15800        foreach my $Library (sort {lc($a) cmp lc($b)}  keys(%{$Library_Symbol{1}}))
15801        {
15802            $Library.=" (.$LIB_EXT)" if($Library!~/\.\w+\Z/);
15803            $TestResults .= "    <name>$Library</name>\n";
15804        }
15805        $TestResults .= "  </libs>\n";
15806
15807        $TestResults .= "  <symbols>".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))."</symbols>\n";
15808        $TestResults .= "  <types>".$TotalTypes."</types>\n";
15809
15810        $TestResults .= "  <verdict>".$RESULT{$Level}{"Verdict"}."</verdict>\n";
15811        $TestResults .= "  <affected>".$RESULT{$Level}{"Affected"}."</affected>\n";
15812        $TestResults = "<test_results>\n".$TestResults."</test_results>\n\n";
15813
15814        # problem summary
15815        $Problem_Summary .= "  <added_symbols>".$Added."</added_symbols>\n";
15816        $Problem_Summary .= "  <removed_symbols>".$Removed."</removed_symbols>\n";
15817
15818        $Problem_Summary .= "  <problems_with_types>\n";
15819        $Problem_Summary .= "    <high>$T_Problems_High</high>\n";
15820        $Problem_Summary .= "    <medium>$T_Problems_Medium</medium>\n";
15821        $Problem_Summary .= "    <low>$T_Problems_Low</low>\n";
15822        $Problem_Summary .= "    <safe>$T_Other</safe>\n";
15823        $Problem_Summary .= "  </problems_with_types>\n";
15824
15825        $Problem_Summary .= "  <problems_with_symbols>\n";
15826        $Problem_Summary .= "    <high>$I_Problems_High</high>\n";
15827        $Problem_Summary .= "    <medium>$I_Problems_Medium</medium>\n";
15828        $Problem_Summary .= "    <low>$I_Problems_Low</low>\n";
15829        $Problem_Summary .= "    <safe>$I_Other</safe>\n";
15830        $Problem_Summary .= "  </problems_with_symbols>\n";
15831
15832        $Problem_Summary .= "  <problems_with_constants>\n";
15833        $Problem_Summary .= "    <low>$C_Problems_Low</low>\n";
15834        $Problem_Summary .= "  </problems_with_constants>\n";
15835
15836        $Problem_Summary = "<problem_summary>\n".$Problem_Summary."</problem_summary>\n\n";
15837
15838        return ($TestInfo.$TestResults.$Problem_Summary, "");
15839    }
15840    else
15841    { # HTML
15842        # test info
15843        $TestInfo = "<h2>Test Info</h2><hr/>\n";
15844        $TestInfo .= "<table class='summary'>\n";
15845        $TestInfo .= "<tr><th>".ucfirst($TargetComponent)." Name</th><td>$TargetTitle</td></tr>\n";
15846
15847        my (@VInf1, @VInf2, $AddTestInfo) = ();
15848        if($Arch1 ne "unknown"
15849        and $Arch2 ne "unknown")
15850        { # CPU arch
15851            if($Arch1 eq $Arch2)
15852            { # go to the separate section
15853                $AddTestInfo .= "<tr><th>CPU Type</th><td>".showArch($Arch1)."</td></tr>\n";
15854            }
15855            else
15856            { # go to the version number
15857                push(@VInf1, showArch($Arch1));
15858                push(@VInf2, showArch($Arch2));
15859            }
15860        }
15861        if($GccV1 ne "unknown"
15862        and $GccV2 ne "unknown"
15863        and $OStarget ne "windows")
15864        { # GCC version
15865            if($GccV1 eq $GccV2)
15866            { # go to the separate section
15867                $AddTestInfo .= "<tr><th>GCC Version</th><td>$GccV1</td></tr>\n";
15868            }
15869            else
15870            { # go to the version number
15871                push(@VInf1, "gcc ".$GccV1);
15872                push(@VInf2, "gcc ".$GccV2);
15873            }
15874        }
15875        # show long version names with GCC version and CPU architecture name (if different)
15876        $TestInfo .= "<tr><th>Version #1</th><td>".$Descriptor{1}{"Version"}.(@VInf1?" (".join(", ", reverse(@VInf1)).")":"")."</td></tr>\n";
15877        $TestInfo .= "<tr><th>Version #2</th><td>".$Descriptor{2}{"Version"}.(@VInf2?" (".join(", ", reverse(@VInf2)).")":"")."</td></tr>\n";
15878        $TestInfo .= $AddTestInfo;
15879        #if($COMMON_LANGUAGE{1}) {
15880        #    $TestInfo .= "<tr><th>Language</th><td>".$COMMON_LANGUAGE{1}."</td></tr>\n";
15881        #}
15882        if($ExtendedCheck) {
15883            $TestInfo .= "<tr><th>Mode</th><td>Extended</td></tr>\n";
15884        }
15885        if($JoinReport)
15886        {
15887            if($Level eq "Binary") {
15888                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Binary Compatibility</td></tr>\n"; # Run-time
15889            }
15890            if($Level eq "Source") {
15891                $TestInfo .= "<tr><th>Subject</th><td width='150px'>Source Compatibility</td></tr>\n"; # Build-time
15892            }
15893        }
15894        $TestInfo .= "</table>\n";
15895
15896        # test results
15897        $TestResults = "<h2>Test Results</h2><hr/>\n";
15898        $TestResults .= "<table class='summary'>";
15899
15900        if(my @Headers = get_CheckedHeaders(1))
15901        {
15902            my $Headers_Link = "<a href='#Headers' style='color:Blue;'>".($#Headers + 1)."</a>";
15903            $TestResults .= "<tr><th>Total Header Files</th><td>".$Headers_Link."</td></tr>\n";
15904        }
15905        elsif($CheckObjectsOnly) {
15906            $TestResults .= "<tr><th>Total Header Files</th><td>0&#160;(not&#160;analyzed)</td></tr>\n";
15907        }
15908
15909        if(my @Sources = keys(%{$Registered_Sources{1}}))
15910        {
15911            my $Src_Link = "<a href='#Sources' style='color:Blue;'>".($#Sources + 1)."</a>";
15912            $TestResults .= "<tr><th>Total Source Files</th><td>".$Src_Link."</td></tr>\n";
15913        }
15914
15915        if(not $ExtendedCheck)
15916        {
15917            my $Libs_Link = "0";
15918            $Libs_Link = "<a href='#Libs' style='color:Blue;'>".keys(%{$Library_Symbol{1}})."</a>" if(keys(%{$Library_Symbol{1}})>0);
15919            $TestResults .= "<tr><th>Total ".get_ObjTitle()."</th><td>".($CheckHeadersOnly?"0&#160;(not&#160;analyzed)":$Libs_Link)."</td></tr>\n";
15920        }
15921
15922        $TestResults .= "<tr><th>Total Symbols / Types</th><td>".(keys(%{$CheckedSymbols{$Level}}) - keys(%ExtendedSymbols))." / ".$TotalTypes."</td></tr>\n";
15923
15924        my $META_DATA = "verdict:".$RESULT{$Level}{"Verdict"}.";";
15925        if($JoinReport) {
15926            $META_DATA = "kind:".lc($Level).";".$META_DATA;
15927        }
15928        $TestResults .= "<tr><th>Verdict</th>";
15929        if($RESULT{$Level}{"Verdict"} eq "incompatible") {
15930            $TestResults .= "<td><span style='color:Red;'><b>Incompatible<br/>(".$RESULT{$Level}{"Affected"}."%)</b></span></td>";
15931        }
15932        else {
15933            $TestResults .= "<td><span style='color:Green;'><b>Compatible</b></span></td>";
15934        }
15935        $TestResults .= "</tr>\n";
15936        $TestResults .= "</table>\n";
15937
15938        $META_DATA .= "affected:".$RESULT{$Level}{"Affected"}.";";# in percents
15939        # problem summary
15940        $Problem_Summary = "<h2>Problem Summary</h2><hr/>\n";
15941        $Problem_Summary .= "<table class='summary'>";
15942        $Problem_Summary .= "<tr><th></th><th style='text-align:center;'>Severity</th><th style='text-align:center;'>Count</th></tr>";
15943
15944        my $Added_Link = "0";
15945        if($Added>0)
15946        {
15947            if($JoinReport) {
15948                $Added_Link = "<a href='#".$Level."_Added' style='color:Blue;'>$Added</a>";
15949            }
15950            else {
15951                $Added_Link = "<a href='#Added' style='color:Blue;'>$Added</a>";
15952            }
15953        }
15954        $META_DATA .= "added:$Added;";
15955        $Problem_Summary .= "<tr><th>Added Symbols</th><td>-</td><td".getStyle("I", "A", $Added).">$Added_Link</td></tr>\n";
15956
15957        my $Removed_Link = "0";
15958        if($Removed>0)
15959        {
15960            if($JoinReport) {
15961                $Removed_Link = "<a href='#".$Level."_Removed' style='color:Blue;'>$Removed</a>"
15962            }
15963            else {
15964                $Removed_Link = "<a href='#Removed' style='color:Blue;'>$Removed</a>"
15965            }
15966        }
15967        $META_DATA .= "removed:$Removed;";
15968        $Problem_Summary .= "<tr><th>Removed Symbols</th>";
15969        $Problem_Summary .= "<td>High</td><td".getStyle("I", "R", $Removed).">$Removed_Link</td></tr>\n";
15970
15971        my $TH_Link = "0";
15972        $TH_Link = "<a href='#".get_Anchor("Type", $Level, "High")."' style='color:Blue;'>$T_Problems_High</a>" if($T_Problems_High>0);
15973        $TH_Link = "n/a" if($CheckObjectsOnly);
15974        $META_DATA .= "type_problems_high:$T_Problems_High;";
15975        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Data Types</th>";
15976        $Problem_Summary .= "<td>High</td><td".getStyle("T", "H", $T_Problems_High).">$TH_Link</td></tr>\n";
15977
15978        my $TM_Link = "0";
15979        $TM_Link = "<a href='#".get_Anchor("Type", $Level, "Medium")."' style='color:Blue;'>$T_Problems_Medium</a>" if($T_Problems_Medium>0);
15980        $TM_Link = "n/a" if($CheckObjectsOnly);
15981        $META_DATA .= "type_problems_medium:$T_Problems_Medium;";
15982        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("T", "M", $T_Problems_Medium).">$TM_Link</td></tr>\n";
15983
15984        my $TL_Link = "0";
15985        $TL_Link = "<a href='#".get_Anchor("Type", $Level, "Low")."' style='color:Blue;'>$T_Problems_Low</a>" if($T_Problems_Low>0);
15986        $TL_Link = "n/a" if($CheckObjectsOnly);
15987        $META_DATA .= "type_problems_low:$T_Problems_Low;";
15988        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("T", "L", $T_Problems_Low).">$TL_Link</td></tr>\n";
15989
15990        my $IH_Link = "0";
15991        $IH_Link = "<a href='#".get_Anchor("Symbol", $Level, "High")."' style='color:Blue;'>$I_Problems_High</a>" if($I_Problems_High>0);
15992        $IH_Link = "n/a" if($CheckObjectsOnly);
15993        $META_DATA .= "interface_problems_high:$I_Problems_High;";
15994        $Problem_Summary .= "<tr><th rowspan='3'>Problems with<br/>Symbols</th>";
15995        $Problem_Summary .= "<td>High</td><td".getStyle("I", "H", $I_Problems_High).">$IH_Link</td></tr>\n";
15996
15997        my $IM_Link = "0";
15998        $IM_Link = "<a href='#".get_Anchor("Symbol", $Level, "Medium")."' style='color:Blue;'>$I_Problems_Medium</a>" if($I_Problems_Medium>0);
15999        $IM_Link = "n/a" if($CheckObjectsOnly);
16000        $META_DATA .= "interface_problems_medium:$I_Problems_Medium;";
16001        $Problem_Summary .= "<tr><td>Medium</td><td".getStyle("I", "M", $I_Problems_Medium).">$IM_Link</td></tr>\n";
16002
16003        my $IL_Link = "0";
16004        $IL_Link = "<a href='#".get_Anchor("Symbol", $Level, "Low")."' style='color:Blue;'>$I_Problems_Low</a>" if($I_Problems_Low>0);
16005        $IL_Link = "n/a" if($CheckObjectsOnly);
16006        $META_DATA .= "interface_problems_low:$I_Problems_Low;";
16007        $Problem_Summary .= "<tr><td>Low</td><td".getStyle("I", "L", $I_Problems_Low).">$IL_Link</td></tr>\n";
16008
16009        my $ChangedConstants_Link = "0";
16010        if(keys(%{$CheckedSymbols{$Level}}) and $C_Problems_Low) {
16011            $ChangedConstants_Link = "<a href='#".get_Anchor("Constant", $Level, "Low")."' style='color:Blue;'>$C_Problems_Low</a>";
16012        }
16013        $ChangedConstants_Link = "n/a" if($CheckObjectsOnly);
16014        $META_DATA .= "changed_constants:$C_Problems_Low;";
16015        $Problem_Summary .= "<tr><th>Problems with<br/>Constants</th><td>Low</td><td".getStyle("C", "L", $C_Problems_Low).">$ChangedConstants_Link</td></tr>\n";
16016
16017        # Safe Changes
16018        if($T_Other and not $CheckObjectsOnly)
16019        {
16020            my $TS_Link = "<a href='#".get_Anchor("Type", $Level, "Safe")."' style='color:Blue;'>$T_Other</a>";
16021            $Problem_Summary .= "<tr><th>Other Changes<br/>in Data Types</th><td>-</td><td".getStyle("T", "S", $T_Other).">$TS_Link</td></tr>\n";
16022        }
16023
16024        if($I_Other and not $CheckObjectsOnly)
16025        {
16026            my $IS_Link = "<a href='#".get_Anchor("Symbol", $Level, "Safe")."' style='color:Blue;'>$I_Other</a>";
16027            $Problem_Summary .= "<tr><th>Other Changes<br/>in Symbols</th><td>-</td><td".getStyle("I", "S", $I_Other).">$IS_Link</td></tr>\n";
16028        }
16029
16030        if($C_Other and not $CheckObjectsOnly)
16031        {
16032            my $CS_Link = "<a href='#".get_Anchor("Constant", $Level, "Safe")."' style='color:Blue;'>$C_Other</a>";
16033            $Problem_Summary .= "<tr><th>Other Changes<br/>in Constants</th><td>-</td><td".getStyle("C", "S", $C_Other).">$CS_Link</td></tr>\n";
16034        }
16035
16036        $META_DATA .= "tool_version:$TOOL_VERSION";
16037        $Problem_Summary .= "</table>\n";
16038        # $TestInfo = getLegend().$TestInfo;
16039        return ($TestInfo.$TestResults.$Problem_Summary, $META_DATA);
16040    }
16041}
16042
16043sub getStyle($$$)
16044{
16045    my ($Subj, $Act, $Num) = @_;
16046    my %Style = (
16047        "A"=>"new",
16048        "R"=>"failed",
16049        "S"=>"passed",
16050        "L"=>"warning",
16051        "M"=>"failed",
16052        "H"=>"failed"
16053    );
16054    if($Num>0) {
16055        return " class='".$Style{$Act}."'";
16056    }
16057    return "";
16058}
16059
16060sub show_number($)
16061{
16062    if($_[0])
16063    {
16064        my $Num = cut_off_number($_[0], 2, 0);
16065        if($Num eq "0")
16066        {
16067            foreach my $P (3 .. 7)
16068            {
16069                $Num = cut_off_number($_[0], $P, 1);
16070                if($Num ne "0") {
16071                    last;
16072                }
16073            }
16074        }
16075        if($Num eq "0") {
16076            $Num = $_[0];
16077        }
16078        return $Num;
16079    }
16080    return $_[0];
16081}
16082
16083sub cut_off_number($$$)
16084{
16085    my ($num, $digs_to_cut, $z) = @_;
16086    if($num!~/\./)
16087    {
16088        $num .= ".";
16089        foreach (1 .. $digs_to_cut-1) {
16090            $num .= "0";
16091        }
16092    }
16093    elsif($num=~/\.(.+)\Z/ and length($1)<$digs_to_cut-1)
16094    {
16095        foreach (1 .. $digs_to_cut - 1 - length($1)) {
16096            $num .= "0";
16097        }
16098    }
16099    elsif($num=~/\d+\.(\d){$digs_to_cut,}/) {
16100      $num=sprintf("%.".($digs_to_cut-1)."f", $num);
16101    }
16102    $num=~s/\.[0]+\Z//g;
16103    if($z) {
16104        $num=~s/(\.[1-9]+)[0]+\Z/$1/g;
16105    }
16106    return $num;
16107}
16108
16109sub get_Report_ChangedConstants($$)
16110{
16111    my ($TargetSeverity, $Level) = @_;
16112    my $CHANGED_CONSTANTS = "";
16113
16114    my %ReportMap = ();
16115    foreach my $Constant (keys(%{$CompatProblems_Constants{$Level}}))
16116    {
16117        my $Header = $Constants{1}{$Constant}{"Header"};
16118        if(not $Header)
16119        { # added
16120            $Header = $Constants{2}{$Constant}{"Header"}
16121        }
16122
16123        foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$CompatProblems_Constants{$Level}{$Constant}}))
16124        {
16125            if(not defined $CompatRules{$Level}{$Kind}) {
16126                next;
16127            }
16128            if($TargetSeverity ne $CompatRules{$Level}{$Kind}{"Severity"}) {
16129                next;
16130            }
16131            $ReportMap{$Header}{$Constant}{$Kind} = 1;
16132        }
16133    }
16134
16135    if($ReportFormat eq "xml")
16136    { # XML
16137        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16138        {
16139            $CHANGED_CONSTANTS .= "  <header name=\"$HeaderName\">\n";
16140            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16141            {
16142                $CHANGED_CONSTANTS .= "    <constant name=\"$Constant\">\n";
16143                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
16144                {
16145                    my $Change = $CompatRules{$Level}{$Kind}{"Change"};
16146                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16147                    my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"};
16148
16149                    $CHANGED_CONSTANTS .= "      <problem id=\"$Kind\">\n";
16150                    $CHANGED_CONSTANTS .= "        <change".getXmlParams($Change, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Change</change>\n";
16151                    $CHANGED_CONSTANTS .= "        <effect".getXmlParams($Effect, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Effect</effect>\n";
16152                    if($Overcome) {
16153                        $CHANGED_CONSTANTS .= "        <overcome".getXmlParams($Overcome, $CompatProblems_Constants{$Level}{$Constant}{$Kind}).">$Overcome</overcome>\n";
16154                    }
16155                    $CHANGED_CONSTANTS .= "      </problem>\n";
16156                }
16157                $CHANGED_CONSTANTS .= "    </constant>\n";
16158            }
16159            $CHANGED_CONSTANTS .= "    </header>\n";
16160        }
16161        $CHANGED_CONSTANTS = "<problems_with_constants severity=\"Low\">\n".$CHANGED_CONSTANTS."</problems_with_constants>\n\n";
16162    }
16163    else
16164    { # HTML
16165        my $Number = 0;
16166        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16167        {
16168            $CHANGED_CONSTANTS .= "<span class='h_name'>$HeaderName</span><br/>\n";
16169            foreach my $Constant (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16170            {
16171                my $Report = "";
16172
16173                foreach my $Kind (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}{$Constant}}))
16174                {
16175                    my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, $CompatProblems_Constants{$Level}{$Constant}{$Kind});
16176                    my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16177                    $Report .= "<tr><th>1</th><td align='left' valign='top'>".$Change."</td><td align='left' valign='top'>$Effect</td></tr>\n";
16178                    $Number += 1;
16179                }
16180                if($Report)
16181                {
16182                    $Report = $ContentDivStart."<table class='ptable'><tr><th width='2%'></th><th width='47%'>Change</th><th>Effect</th></tr>".$Report."</table><br/>$ContentDivEnd\n";
16183                    $Report = $ContentSpanStart."<span class='extendable'>[+]</span> ".$Constant.$ContentSpanEnd."<br/>\n".$Report;
16184                    $Report = insertIDs($Report);
16185                }
16186                $CHANGED_CONSTANTS .= $Report;
16187            }
16188            $CHANGED_CONSTANTS .= "<br/>\n";
16189        }
16190        if($CHANGED_CONSTANTS)
16191        {
16192            my $Title = "Problems with Constants, $TargetSeverity Severity";
16193            if($TargetSeverity eq "Safe")
16194            { # Safe Changes
16195                $Title = "Other Changes in Constants";
16196            }
16197            $CHANGED_CONSTANTS = "<a name='".get_Anchor("Constant", $Level, $TargetSeverity)."'></a><h2>$Title ($Number)</h2><hr/>\n".$CHANGED_CONSTANTS.$TOP_REF."<br/>\n";
16198        }
16199    }
16200    return $CHANGED_CONSTANTS;
16201}
16202
16203sub getTitle($$$)
16204{
16205    my ($Header, $Library, $NameSpace) = @_;
16206    my $Title = "";
16207    if($Library and $Library!~/\.\w+\Z/) {
16208        $Library .= " (.$LIB_EXT)";
16209    }
16210    if($Header and $Library)
16211    {
16212        $Title .= "<span class='h_name'>$Header</span>";
16213        $Title .= ", <span class='lib_name'>$Library</span><br/>\n";
16214    }
16215    elsif($Library) {
16216        $Title .= "<span class='lib_name'>$Library</span><br/>\n";
16217    }
16218    elsif($Header) {
16219        $Title .= "<span class='h_name'>$Header</span><br/>\n";
16220    }
16221    if($NameSpace) {
16222        $Title .= "<span class='ns'>namespace <b>$NameSpace</b></span><br/>\n";
16223    }
16224    return $Title;
16225}
16226
16227sub get_Report_Added($)
16228{
16229    my $Level = $_[0];
16230    my $ADDED_INTERFACES = "";
16231    my %ReportMap = ();
16232    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
16233    {
16234        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Interface}}))
16235        {
16236            if($Kind eq "Added_Symbol")
16237            {
16238                my $HeaderName = $CompleteSignature{2}{$Interface}{"Header"};
16239                my $DyLib = $Symbol_Library{2}{$Interface};
16240                if($Level eq "Source" and $ReportFormat eq "html")
16241                { # do not show library name in HTML report
16242                    $DyLib = "";
16243                }
16244                $ReportMap{$HeaderName}{$DyLib}{$Interface} = 1;
16245            }
16246        }
16247    }
16248    if($ReportFormat eq "xml")
16249    { # XML
16250        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16251        {
16252            $ADDED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
16253            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16254            {
16255                $ADDED_INTERFACES .= "    <library name=\"$DyLib\">\n";
16256                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16257                    $ADDED_INTERFACES .= "      <name>$Interface</name>\n";
16258                }
16259                $ADDED_INTERFACES .= "    </library>\n";
16260            }
16261            $ADDED_INTERFACES .= "  </header>\n";
16262        }
16263        $ADDED_INTERFACES = "<added_symbols>\n".$ADDED_INTERFACES."</added_symbols>\n\n";
16264    }
16265    else
16266    { # HTML
16267        my $Added_Number = 0;
16268        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16269        {
16270            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16271            {
16272                my %NameSpaceSymbols = ();
16273                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16274                    $NameSpaceSymbols{select_Symbol_NS($Interface, 2)}{$Interface} = 1;
16275                }
16276                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16277                {
16278                    $ADDED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
16279                    my @SortedInterfaces = sort {lc(get_Signature($a, 2)) cmp lc(get_Signature($b, 2))} keys(%{$NameSpaceSymbols{$NameSpace}});
16280                    foreach my $Interface (@SortedInterfaces)
16281                    {
16282                        $Added_Number += 1;
16283                        my $Signature = get_Signature($Interface, 2);
16284                        if($NameSpace) {
16285                            $Signature=~s/\b\Q$NameSpace\E::\b//g;
16286                        }
16287                        if($Interface=~/\A(_Z|\?)/)
16288                        {
16289                            if($Signature) {
16290                                $ADDED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Interface</b>]</span><br/><br/>".$ContentDivEnd."\n");
16291                            }
16292                            else {
16293                                $ADDED_INTERFACES .= "<span class=\"iname\">".$Interface."</span><br/>\n";
16294                            }
16295                        }
16296                        else
16297                        {
16298                            if($Signature) {
16299                                $ADDED_INTERFACES .= "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
16300                            }
16301                            else {
16302                                $ADDED_INTERFACES .= "<span class=\"iname\">".$Interface."</span><br/>\n";
16303                            }
16304                        }
16305                    }
16306                    $ADDED_INTERFACES .= "<br/>\n";
16307                }
16308            }
16309        }
16310        if($ADDED_INTERFACES)
16311        {
16312            my $Anchor = "<a name='Added'></a>";
16313            if($JoinReport) {
16314                $Anchor = "<a name='".$Level."_Added'></a>";
16315            }
16316            $ADDED_INTERFACES = $Anchor."<h2>Added Symbols ($Added_Number)</h2><hr/>\n".$ADDED_INTERFACES.$TOP_REF."<br/>\n";
16317        }
16318    }
16319    return $ADDED_INTERFACES;
16320}
16321
16322sub get_Report_Removed($)
16323{
16324    my $Level = $_[0];
16325    my $REMOVED_INTERFACES = "";
16326    my %ReportMap = ();
16327    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
16328    {
16329        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
16330        {
16331            if($Kind eq "Removed_Symbol")
16332            {
16333                my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
16334                my $DyLib = $Symbol_Library{1}{$Symbol};
16335                if($Level eq "Source" and $ReportFormat eq "html")
16336                { # do not show library name in HTML report
16337                    $DyLib = "";
16338                }
16339                $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
16340            }
16341        }
16342    }
16343    if($ReportFormat eq "xml")
16344    { # XML
16345        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16346        {
16347            $REMOVED_INTERFACES .= "  <header name=\"$HeaderName\">\n";
16348            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16349            {
16350                $REMOVED_INTERFACES .= "    <library name=\"$DyLib\">\n";
16351                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16352                    $REMOVED_INTERFACES .= "      <name>$Symbol</name>\n";
16353                }
16354                $REMOVED_INTERFACES .= "    </library>\n";
16355            }
16356            $REMOVED_INTERFACES .= "  </header>\n";
16357        }
16358        $REMOVED_INTERFACES = "<removed_symbols>\n".$REMOVED_INTERFACES."</removed_symbols>\n\n";
16359    }
16360    else
16361    { # HTML
16362        my $Removed_Number = 0;
16363        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16364        {
16365            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16366            {
16367                my %NameSpaceSymbols = ();
16368                foreach my $Interface (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16369                    $NameSpaceSymbols{select_Symbol_NS($Interface, 1)}{$Interface} = 1;
16370                }
16371                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16372                {
16373                    $REMOVED_INTERFACES .= getTitle($HeaderName, $DyLib, $NameSpace);
16374                    my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NameSpaceSymbols{$NameSpace}});
16375                    foreach my $Symbol (@SortedInterfaces)
16376                    {
16377                        $Removed_Number += 1;
16378                        my $SubReport = "";
16379                        my $Signature = get_Signature($Symbol, 1);
16380                        if($NameSpace) {
16381                            $Signature=~s/\b\Q$NameSpace\E::\b//g;
16382                        }
16383                        if($Symbol=~/\A(_Z|\?)/)
16384                        {
16385                            if($Signature) {
16386                                $REMOVED_INTERFACES .= insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Symbol</b>]</span><br/><br/>".$ContentDivEnd."\n");
16387                            }
16388                            else {
16389                                $REMOVED_INTERFACES .= "<span class=\"iname\">".$Symbol."</span><br/>\n";
16390                            }
16391                        }
16392                        else
16393                        {
16394                            if($Signature) {
16395                                $REMOVED_INTERFACES .= "<span class=\"iname\">".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
16396                            }
16397                            else {
16398                                $REMOVED_INTERFACES .= "<span class=\"iname\">".$Symbol."</span><br/>\n";
16399                            }
16400                        }
16401                    }
16402                }
16403                $REMOVED_INTERFACES .= "<br/>\n";
16404            }
16405        }
16406        if($REMOVED_INTERFACES)
16407        {
16408            my $Anchor = "<a name='Removed'></a><a name='Withdrawn'></a>";
16409            if($JoinReport) {
16410                $Anchor = "<a name='".$Level."_Removed'></a><a name='".$Level."_Withdrawn'></a>";
16411            }
16412            $REMOVED_INTERFACES = $Anchor."<h2>Removed Symbols ($Removed_Number)</h2><hr/>\n".$REMOVED_INTERFACES.$TOP_REF."<br/>\n";
16413        }
16414    }
16415    return $REMOVED_INTERFACES;
16416}
16417
16418sub getXmlParams($$)
16419{
16420    my ($Content, $Problem) = @_;
16421    return "" if(not $Content or not $Problem);
16422    my %XMLparams = ();
16423    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
16424    {
16425        my $Macro = "\@".lc($Attr);
16426        if($Content=~/\Q$Macro\E/) {
16427            $XMLparams{lc($Attr)} = $Problem->{$Attr};
16428        }
16429    }
16430    my @PString = ();
16431    foreach my $P (sort {$b cmp $a} keys(%XMLparams)) {
16432        push(@PString, $P."=\"".xmlSpecChars($XMLparams{$P})."\"");
16433    }
16434    if(@PString) {
16435        return " ".join(" ", @PString);
16436    }
16437    else {
16438        return "";
16439    }
16440}
16441
16442sub addMarkup($)
16443{
16444    my $Content = $_[0];
16445    # auto-markup
16446    $Content=~s/\n[ ]*//; # spaces
16447    $Content=~s!(\@\w+\s*\(\@\w+\))!<nowrap>$1</nowrap>!g; # @old_type (@old_size)
16448    $Content=~s!(... \(\w+\))!<nowrap><b>$1</b></nowrap>!g; # ... (va_list)
16449    $Content=~s!<nowrap>(.+?)</nowrap>!<span class='nowrap'>$1</span>!g;
16450    $Content=~s!([2-9]\))!<br/>$1!g; # 1), 2), ...
16451    if($Content=~/\ANOTE:/)
16452    { # notes
16453        $Content=~s!(NOTE):!<b>$1</b>:!g;
16454    }
16455    else {
16456        $Content=~s!(NOTE):!<br/><b>$1</b>:!g;
16457    }
16458    $Content=~s! (out)-! <b>$1</b>-!g; # out-parameters
16459    my @Keywords = (
16460        "void",
16461        "const",
16462        "static",
16463        "restrict",
16464        "volatile",
16465        "register",
16466        "virtual"
16467    );
16468    my $MKeys = join("|", @Keywords);
16469    foreach (@Keywords) {
16470        $MKeys .= "|non-".$_;
16471    }
16472    $Content=~s!(added\s*|to\s*|from\s*|became\s*)($MKeys)([^\w-]|\Z)!$1<b>$2</b>$3!ig; # intrinsic types, modifiers
16473
16474    # Markdown
16475    $Content=~s!\*\*([\w\-]+)\*\*!<b>$1</b>!ig;
16476    $Content=~s!\*([\w\-]+)\*!<i>$1</i>!ig;
16477    return $Content;
16478}
16479
16480sub applyMacroses($$$$)
16481{
16482    my ($Level, $Kind, $Content, $Problem) = @_;
16483    return "" if(not $Content or not $Problem);
16484    $Problem->{"Word_Size"} = $WORD_SIZE{2};
16485    $Content = addMarkup($Content);
16486    # macros
16487    foreach my $Attr (sort {$b cmp $a} keys(%{$Problem}))
16488    {
16489        my $Macro = "\@".lc($Attr);
16490        my $Value = $Problem->{$Attr};
16491        if(not defined $Value
16492        or $Value eq "") {
16493            next;
16494        }
16495        if($Value=~/\s\(/ and $Value!~/['"]/)
16496        { # functions
16497            $Value=~s/\s*\[[\w\-]+\]//g; # remove quals
16498            $Value=~s/\s\w+(\)|,)/$1/g; # remove parameter names
16499            $Value = black_name($Value);
16500        }
16501        elsif($Value=~/\s/) {
16502            $Value = "<span class='value'>".htmlSpecChars($Value)."</span>";
16503        }
16504        elsif($Value=~/\A\d+\Z/
16505        and ($Attr eq "Old_Size" or $Attr eq "New_Size"))
16506        { # bits to bytes
16507            if($Value % $BYTE_SIZE)
16508            { # bits
16509                if($Value==1) {
16510                    $Value = "<b>".$Value."</b> bit";
16511                }
16512                else {
16513                    $Value = "<b>".$Value."</b> bits";
16514                }
16515            }
16516            else
16517            { # bytes
16518                $Value /= $BYTE_SIZE;
16519                if($Value==1) {
16520                    $Value = "<b>".$Value."</b> byte";
16521                }
16522                else {
16523                    $Value = "<b>".$Value."</b> bytes";
16524                }
16525            }
16526        }
16527        else
16528        {
16529            $Value = "<b>".htmlSpecChars($Value)."</b>";
16530        }
16531        $Content=~s/\Q$Macro\E/$Value/g;
16532    }
16533
16534    if($Content=~/(\A|[^\@\w])\@\w/)
16535    {
16536        if(not $IncompleteRules{$Level}{$Kind})
16537        { # only one warning
16538            printMsg("WARNING", "incomplete rule \"$Kind\" (\"$Level\")");
16539            $IncompleteRules{$Level}{$Kind} = 1;
16540        }
16541    }
16542    return $Content;
16543}
16544
16545sub get_Report_SymbolProblems($$)
16546{
16547    my ($TargetSeverity, $Level) = @_;
16548    my $INTERFACE_PROBLEMS = "";
16549    my (%ReportMap, %SymbolChanges) = ();
16550
16551    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
16552    {
16553        my ($SN, $SS, $SV) = separate_symbol($Symbol);
16554        if($SV and defined $CompatProblems{$Level}{$SN}) {
16555            next;
16556        }
16557        foreach my $Kind (sort keys(%{$CompatProblems{$Level}{$Symbol}}))
16558        {
16559            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Symbols"
16560            and $Kind ne "Added_Symbol" and $Kind ne "Removed_Symbol")
16561            {
16562                my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
16563                my $DyLib = $Symbol_Library{1}{$Symbol};
16564                if(not $DyLib and my $VSym = $SymVer{1}{$Symbol})
16565                { # Symbol with Version
16566                    $DyLib = $Symbol_Library{1}{$VSym};
16567                }
16568                if(not $DyLib)
16569                { # const global data
16570                    $DyLib = "";
16571                }
16572                if($Level eq "Source" and $ReportFormat eq "html")
16573                { # do not show library name in HTML report
16574                    $DyLib = "";
16575                }
16576                %{$SymbolChanges{$Symbol}{$Kind}} = %{$CompatProblems{$Level}{$Symbol}{$Kind}};
16577                foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16578                {
16579                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16580                    if($Severity ne $TargetSeverity) {
16581                        delete($SymbolChanges{$Symbol}{$Kind}{$Location});
16582                    }
16583                }
16584                if(not keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16585                {
16586                    delete($SymbolChanges{$Symbol}{$Kind});
16587                    next;
16588                }
16589                $ReportMap{$HeaderName}{$DyLib}{$Symbol} = 1;
16590            }
16591        }
16592        if(not keys(%{$SymbolChanges{$Symbol}})) {
16593            delete($SymbolChanges{$Symbol});
16594        }
16595    }
16596
16597    if($ReportFormat eq "xml")
16598    { # XML
16599        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16600        {
16601            $INTERFACE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
16602            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16603            {
16604                $INTERFACE_PROBLEMS .= "    <library name=\"$DyLib\">\n";
16605                my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$ReportMap{$HeaderName}{$DyLib}});
16606                foreach my $Symbol (@SortedInterfaces)
16607                {
16608                    $INTERFACE_PROBLEMS .= "      <symbol name=\"$Symbol\">\n";
16609                    foreach my $Kind (keys(%{$SymbolChanges{$Symbol}}))
16610                    {
16611                        foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16612                        {
16613                            my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
16614                            $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
16615
16616                            $INTERFACE_PROBLEMS .= "        <problem id=\"$Kind\">\n";
16617                            my $Change = $CompatRules{$Level}{$Kind}{"Change"};
16618                            $INTERFACE_PROBLEMS .= "          <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
16619                            my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16620                            $INTERFACE_PROBLEMS .= "          <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
16621                            if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
16622                                $INTERFACE_PROBLEMS .= "          <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
16623                            }
16624                            $INTERFACE_PROBLEMS .= "        </problem>\n";
16625                        }
16626                    }
16627                    $INTERFACE_PROBLEMS .= "      </symbol>\n";
16628                }
16629                $INTERFACE_PROBLEMS .= "    </library>\n";
16630            }
16631            $INTERFACE_PROBLEMS .= "  </header>\n";
16632        }
16633        $INTERFACE_PROBLEMS = "<problems_with_symbols severity=\"$TargetSeverity\">\n".$INTERFACE_PROBLEMS."</problems_with_symbols>\n\n";
16634    }
16635    else
16636    { # HTML
16637        my $ProblemsNum = 0;
16638        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16639        {
16640            foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$ReportMap{$HeaderName}}))
16641            {
16642                my (%NameSpaceSymbols, %NewSignature) = ();
16643                foreach my $Symbol (keys(%{$ReportMap{$HeaderName}{$DyLib}})) {
16644                    $NameSpaceSymbols{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1;
16645                }
16646                foreach my $NameSpace (sort keys(%NameSpaceSymbols))
16647                {
16648                    $INTERFACE_PROBLEMS .= getTitle($HeaderName, $DyLib, $NameSpace);
16649                    my @SortedInterfaces = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$NameSpaceSymbols{$NameSpace}});
16650                    foreach my $Symbol (@SortedInterfaces)
16651                    {
16652                        my $Signature = get_Signature($Symbol, 1);
16653                        my $SYMBOL_REPORT = "";
16654                        my $ProblemNum = 1;
16655                        foreach my $Kind (keys(%{$SymbolChanges{$Symbol}}))
16656                        {
16657                            foreach my $Location (sort keys(%{$SymbolChanges{$Symbol}{$Kind}}))
16658                            {
16659                                my %Problem = %{$SymbolChanges{$Symbol}{$Kind}{$Location}};
16660                                $Problem{"Param_Pos"} = showPos($Problem{"Param_Pos"});
16661                                if($Problem{"New_Signature"}) {
16662                                    $NewSignature{$Symbol} = $Problem{"New_Signature"};
16663                                }
16664                                if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
16665                                {
16666                                    my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
16667                                    $SYMBOL_REPORT .= "<tr><th>$ProblemNum</th><td align='left' valign='top'>".$Change."</td><td align='left' valign='top'>".$Effect."</td></tr>\n";
16668                                    $ProblemNum += 1;
16669                                    $ProblemsNum += 1;
16670                                }
16671                            }
16672                        }
16673                        $ProblemNum -= 1;
16674                        if($SYMBOL_REPORT)
16675                        {
16676                            $INTERFACE_PROBLEMS .= $ContentSpanStart."<span class='extendable'>[+]</span> ";
16677                            if($Signature) {
16678                                $INTERFACE_PROBLEMS .= highLight_Signature_Italic_Color($Signature);
16679                            }
16680                            else {
16681                                $INTERFACE_PROBLEMS .= $Symbol;
16682                            }
16683                            $INTERFACE_PROBLEMS .= " ($ProblemNum)".$ContentSpanEnd."<br/>\n";
16684                            $INTERFACE_PROBLEMS .= $ContentDivStart."\n";
16685                            if($NewSignature{$Symbol})
16686                            { # argument list changed to
16687                                $INTERFACE_PROBLEMS .= "\n<span class='new_sign_lbl'>changed to:</span><br/><span class='new_sign'>".highLight_Signature_Italic_Color($NewSignature{$Symbol})."</span><br/>\n";
16688                            }
16689                            if($Symbol=~/\A(_Z|\?)/) {
16690                                $INTERFACE_PROBLEMS .= "<span class='mangled'>&#160;&#160;&#160;&#160;[symbol: <b>$Symbol</b>]</span><br/>\n";
16691                            }
16692                            $INTERFACE_PROBLEMS .= "<table class='ptable'><tr><th width='2%'></th><th width='47%'>Change</th><th>Effect</th></tr>$SYMBOL_REPORT</table><br/>\n";
16693                            $INTERFACE_PROBLEMS .= $ContentDivEnd;
16694                            if($NameSpace) {
16695                                $INTERFACE_PROBLEMS=~s/\b\Q$NameSpace\E::\b//g;
16696                            }
16697                        }
16698                    }
16699                    $INTERFACE_PROBLEMS .= "<br/>";
16700                }
16701            }
16702        }
16703
16704        if($INTERFACE_PROBLEMS)
16705        {
16706            $INTERFACE_PROBLEMS = insertIDs($INTERFACE_PROBLEMS);
16707            my $Title = "Problems with Symbols, $TargetSeverity Severity";
16708            if($TargetSeverity eq "Safe")
16709            { # Safe Changes
16710                $Title = "Other Changes in Symbols";
16711            }
16712            $INTERFACE_PROBLEMS = "<a name=\'".get_Anchor("Symbol", $Level, $TargetSeverity)."\'></a><a name=\'".get_Anchor("Interface", $Level, $TargetSeverity)."\'></a>\n<h2>$Title ($ProblemsNum)</h2><hr/>\n".$INTERFACE_PROBLEMS.$TOP_REF."<br/>\n";
16713        }
16714    }
16715    return $INTERFACE_PROBLEMS;
16716}
16717
16718sub get_Report_TypeProblems($$)
16719{
16720    my ($TargetSeverity, $Level) = @_;
16721    my $TYPE_PROBLEMS = "";
16722    my (%ReportMap, %TypeChanges) = ();
16723
16724    foreach my $Interface (sort keys(%{$CompatProblems{$Level}}))
16725    {
16726        foreach my $Kind (keys(%{$CompatProblems{$Level}{$Interface}}))
16727        {
16728            if($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
16729            {
16730                foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$CompatProblems{$Level}{$Interface}{$Kind}}))
16731                {
16732                    my $TypeName = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Type_Name"};
16733                    my $Target = $CompatProblems{$Level}{$Interface}{$Kind}{$Location}{"Target"};
16734                    my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16735
16736                    if($Severity eq "Safe"
16737                    and $TargetSeverity ne "Safe") {
16738                        next;
16739                    }
16740
16741                    if(my $MaxSeverity = $Type_MaxSeverity{$Level}{$TypeName}{$Kind}{$Target})
16742                    {
16743                        if($Severity_Val{$MaxSeverity}>$Severity_Val{$Severity})
16744                        { # select a problem with the highest priority
16745                            next;
16746                        }
16747                    }
16748
16749                    $TypeChanges{$TypeName}{$Kind}{$Location} = $CompatProblems{$Level}{$Interface}{$Kind}{$Location};
16750                }
16751            }
16752        }
16753    }
16754
16755    my %Kinds_Locations = ();
16756    foreach my $TypeName (keys(%TypeChanges))
16757    {
16758        my %Kind_Target = ();
16759        foreach my $Kind (sort keys(%{$TypeChanges{$TypeName}}))
16760        {
16761            foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
16762            {
16763                my $Severity = $CompatRules{$Level}{$Kind}{"Severity"};
16764                if($Severity ne $TargetSeverity)
16765                { # other priority
16766                    delete($TypeChanges{$TypeName}{$Kind}{$Location});
16767                    next;
16768                }
16769                $Kinds_Locations{$TypeName}{$Kind}{$Location} = 1;
16770                my $Target = $TypeChanges{$TypeName}{$Kind}{$Location}{"Target"};
16771                if($Kind_Target{$Kind}{$Target})
16772                { # duplicate target
16773                    delete($TypeChanges{$TypeName}{$Kind}{$Location});
16774                    next;
16775                }
16776                $Kind_Target{$Kind}{$Target} = 1;
16777                my $HeaderName = $TypeInfo{1}{$TName_Tid{1}{$TypeName}}{"Header"};
16778                $ReportMap{$HeaderName}{$TypeName} = 1;
16779            }
16780            if(not keys(%{$TypeChanges{$TypeName}{$Kind}})) {
16781                delete($TypeChanges{$TypeName}{$Kind});
16782            }
16783        }
16784        if(not keys(%{$TypeChanges{$TypeName}})) {
16785            delete($TypeChanges{$TypeName});
16786        }
16787    }
16788
16789    my @Symbols = sort {lc($tr_name{$a}?$tr_name{$a}:$a) cmp lc($tr_name{$b}?$tr_name{$b}:$b)} keys(%{$CompatProblems{$Level}});
16790    if($ReportFormat eq "xml")
16791    { # XML
16792        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16793        {
16794            $TYPE_PROBLEMS .= "  <header name=\"$HeaderName\">\n";
16795            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}}))
16796            {
16797                $TYPE_PROBLEMS .= "    <type name=\"".xmlSpecChars($TypeName)."\">\n";
16798                foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}}))
16799                {
16800                    foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
16801                    {
16802                        my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}};
16803                        $TYPE_PROBLEMS .= "      <problem id=\"$Kind\">\n";
16804                        my $Change = $CompatRules{$Level}{$Kind}{"Change"};
16805                        $TYPE_PROBLEMS .= "        <change".getXmlParams($Change, \%Problem).">$Change</change>\n";
16806                        my $Effect = $CompatRules{$Level}{$Kind}{"Effect"};
16807                        $TYPE_PROBLEMS .= "        <effect".getXmlParams($Effect, \%Problem).">$Effect</effect>\n";
16808                        if(my $Overcome = $CompatRules{$Level}{$Kind}{"Overcome"}) {
16809                            $TYPE_PROBLEMS .= "        <overcome".getXmlParams($Overcome, \%Problem).">$Overcome</overcome>\n";
16810                        }
16811                        $TYPE_PROBLEMS .= "      </problem>\n";
16812                    }
16813                }
16814                $TYPE_PROBLEMS .= getAffectedSymbols($Level, $TypeName, $Kinds_Locations{$TypeName}, \@Symbols);
16815                if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) {
16816                    $TYPE_PROBLEMS .= showVTables($TypeName);
16817                }
16818                $TYPE_PROBLEMS .= "    </type>\n";
16819            }
16820            $TYPE_PROBLEMS .= "  </header>\n";
16821        }
16822        $TYPE_PROBLEMS = "<problems_with_types severity=\"$TargetSeverity\">\n".$TYPE_PROBLEMS."</problems_with_types>\n\n";
16823    }
16824    else
16825    { # HTML
16826        my $ProblemsNum = 0;
16827        foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%ReportMap))
16828        {
16829            my (%NameSpace_Type) = ();
16830            foreach my $TypeName (keys(%{$ReportMap{$HeaderName}})) {
16831                $NameSpace_Type{select_Type_NS($TypeName, 1)}{$TypeName} = 1;
16832            }
16833            foreach my $NameSpace (sort keys(%NameSpace_Type))
16834            {
16835                $TYPE_PROBLEMS .= getTitle($HeaderName, "", $NameSpace);
16836                my @SortedTypes = sort {lc(show_Type($a, 0, 1)) cmp lc(show_Type($b, 0, 1))} keys(%{$NameSpace_Type{$NameSpace}});
16837                foreach my $TypeName (@SortedTypes)
16838                {
16839                    my $ProblemNum = 1;
16840                    my $TYPE_REPORT = "";
16841
16842                    foreach my $Kind (sort {$b=~/Size/ <=> $a=~/Size/} sort keys(%{$TypeChanges{$TypeName}}))
16843                    {
16844                        foreach my $Location (sort {cmpLocations($b, $a)} sort keys(%{$TypeChanges{$TypeName}{$Kind}}))
16845                        {
16846                            my %Problem = %{$TypeChanges{$TypeName}{$Kind}{$Location}};
16847                            if(my $Change = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Change"}, \%Problem))
16848                            {
16849                                my $Effect = applyMacroses($Level, $Kind, $CompatRules{$Level}{$Kind}{"Effect"}, \%Problem);
16850                                $TYPE_REPORT .= "<tr><th>$ProblemNum</th><td align='left' valign='top'>".$Change."</td><td align='left' valign='top'>$Effect</td></tr>\n";
16851                                $ProblemNum += 1;
16852                                $ProblemsNum += 1;
16853                            }
16854                        }
16855                    }
16856                    $ProblemNum -= 1;
16857                    if($TYPE_REPORT)
16858                    {
16859                        my $Affected = getAffectedSymbols($Level, $TypeName, $Kinds_Locations{$TypeName}, \@Symbols);
16860                        my $ShowVTables = "";
16861                        if($Level eq "Binary" and grep {$_=~/Virtual|Base_Class/} keys(%{$Kinds_Locations{$TypeName}})) {
16862                            $ShowVTables = showVTables($TypeName);
16863                        }
16864
16865                        $TYPE_PROBLEMS .= $ContentSpanStart."<span class='extendable'>[+]</span> ".show_Type($TypeName, 1, 1)." ($ProblemNum)".$ContentSpanEnd;
16866                        $TYPE_PROBLEMS .= "<br/>\n".$ContentDivStart."<table class='ptable'><tr>\n";
16867                        $TYPE_PROBLEMS .= "<th width='2%'></th><th width='47%'>Change</th>\n";
16868                        $TYPE_PROBLEMS .= "<th>Effect</th></tr>".$TYPE_REPORT."</table>\n";
16869                        $TYPE_PROBLEMS .= $ShowVTables.$Affected."<br/><br/>".$ContentDivEnd."\n";
16870                        if($NameSpace) {
16871                            $TYPE_PROBLEMS=~s/\b\Q$NameSpace\E::(\w|\~)/$1/g;
16872                        }
16873                    }
16874                }
16875                $TYPE_PROBLEMS .= "<br/>";
16876            }
16877        }
16878
16879        if($TYPE_PROBLEMS)
16880        {
16881            $TYPE_PROBLEMS = insertIDs($TYPE_PROBLEMS);
16882            my $Title = "Problems with Data Types, $TargetSeverity Severity";
16883            if($TargetSeverity eq "Safe")
16884            { # Safe Changes
16885                $Title = "Other Changes in Data Types";
16886            }
16887            $TYPE_PROBLEMS = "<a name=\'".get_Anchor("Type", $Level, $TargetSeverity)."\'></a>\n<h2>$Title ($ProblemsNum)</h2><hr/>\n".$TYPE_PROBLEMS.$TOP_REF."<br/>\n";
16888        }
16889    }
16890    return $TYPE_PROBLEMS;
16891}
16892
16893sub show_Type($$$)
16894{
16895    my ($Name, $Html, $LibVersion) = @_;
16896    my $TType = $TypeInfo{$LibVersion}{$TName_Tid{$LibVersion}{$Name}}{"Type"};
16897    $TType = lc($TType);
16898    if($TType=~/struct|union|enum/) {
16899        $Name=~s/\A\Q$TType\E //g;
16900    }
16901    if($Html) {
16902        $Name = "<span class='ttype'>".$TType."</span> ".htmlSpecChars($Name);
16903    }
16904    else {
16905        $Name = $TType." ".$Name;
16906    }
16907    return $Name;
16908}
16909
16910sub get_Anchor($$$)
16911{
16912    my ($Kind, $Level, $Severity) = @_;
16913    if($JoinReport)
16914    {
16915        if($Severity eq "Safe") {
16916            return "Other_".$Level."_Changes_In_".$Kind."s";
16917        }
16918        else {
16919            return $Kind."_".$Level."_Problems_".$Severity;
16920        }
16921    }
16922    else
16923    {
16924        if($Severity eq "Safe") {
16925            return "Other_Changes_In_".$Kind."s";
16926        }
16927        else {
16928            return $Kind."_Problems_".$Severity;
16929        }
16930    }
16931}
16932
16933sub showVTables($)
16934{
16935    my $TypeName = $_[0];
16936    my $TypeId1 = $TName_Tid{1}{$TypeName};
16937    my %Type1 = get_Type($TypeId1, 1);
16938    if(defined $Type1{"VTable"}
16939    and keys(%{$Type1{"VTable"}}))
16940    {
16941        my $TypeId2 = $TName_Tid{2}{$TypeName};
16942        my %Type2 = get_Type($TypeId2, 2);
16943        if(defined $Type2{"VTable"}
16944        and keys(%{$Type2{"VTable"}}))
16945        {
16946            my %Indexes = map {$_=>1} (keys(%{$Type1{"VTable"}}), keys(%{$Type2{"VTable"}}));
16947            my %Entries = ();
16948            foreach my $Index (sort {int($a)<=>int($b)} (keys(%Indexes)))
16949            {
16950                $Entries{$Index}{"E1"} = simpleVEntry($Type1{"VTable"}{$Index});
16951                $Entries{$Index}{"E2"} = simpleVEntry($Type2{"VTable"}{$Index});
16952            }
16953            my $VTABLES = "";
16954            if($ReportFormat eq "xml")
16955            { # XML
16956                $VTABLES .= "      <vtable>\n";
16957                foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
16958                {
16959                    $VTABLES .= "        <entry offset=\"".$Index."\">\n";
16960                    $VTABLES .= "          <old>".xmlSpecChars($Entries{$Index}{"E1"})."</old>\n";
16961                    $VTABLES .= "          <new>".xmlSpecChars($Entries{$Index}{"E2"})."</new>\n";
16962                    $VTABLES .= "        </entry>\n";
16963                }
16964                $VTABLES .= "      </vtable>\n\n";
16965            }
16966            else
16967            { # HTML
16968                $VTABLES .= "<table class='vtable'>";
16969                $VTABLES .= "<tr><th>Offset</th>";
16970                $VTABLES .= "<th>Virtual Table (Old) - ".(keys(%{$Type1{"VTable"}}))." entries</th>";
16971                $VTABLES .= "<th>Virtual Table (New) - ".(keys(%{$Type2{"VTable"}}))." entries</th></tr>";
16972                foreach my $Index (sort {int($a)<=>int($b)} (keys(%Entries)))
16973                {
16974                    my ($Color1, $Color2) = ("", "");
16975                    if($Entries{$Index}{"E1"} ne $Entries{$Index}{"E2"})
16976                    {
16977                        if($Entries{$Index}{"E1"})
16978                        {
16979                            $Color1 = " class='failed'";
16980                            $Color2 = " class='failed'";
16981                        }
16982                        else {
16983                            $Color2 = " class='warning'";
16984                        }
16985                    }
16986                    $VTABLES .= "<tr><th>".$Index."</th>\n";
16987                    $VTABLES .= "<td$Color1>".htmlSpecChars($Entries{$Index}{"E1"})."</td>\n";
16988                    $VTABLES .= "<td$Color2>".htmlSpecChars($Entries{$Index}{"E2"})."</td></tr>\n";
16989                }
16990                $VTABLES .= "</table><br/>\n";
16991                $VTABLES = $ContentDivStart.$VTABLES.$ContentDivEnd;
16992                $VTABLES = $ContentSpanStart_Info."[+] show v-table (old and new)".$ContentSpanEnd."<br/>\n".$VTABLES;
16993            }
16994            return $VTABLES;
16995        }
16996    }
16997    return "";
16998}
16999
17000sub simpleVEntry($)
17001{
17002    my $VEntry = $_[0];
17003    if(not defined $VEntry
17004    or $VEntry eq "") {
17005        return "";
17006    }
17007
17008    $VEntry=~s/ \[.+?\]\Z//; # support for ABI Dumper
17009    $VEntry=~s/\A(.+)::(_ZThn.+)\Z/$2/; # thunks
17010    $VEntry=~s/_ZTI\w+/typeinfo/g; # typeinfo
17011    if($VEntry=~/\A_ZThn.+\Z/) {
17012        $VEntry = "non-virtual thunk";
17013    }
17014    $VEntry=~s/\A\(int \(\*\)\(...\)\)\s*([a-z_])/$1/i;
17015    # support for old GCC versions
17016    $VEntry=~s/\A0u\Z/(int (*)(...))0/;
17017    $VEntry=~s/\A4294967268u\Z/(int (*)(...))-0x000000004/;
17018    $VEntry=~s/\A&_Z\Z/& _Z/;
17019    $VEntry=~s/([^:]+)::\~([^:]+)\Z/~$1/; # destructors
17020    return $VEntry;
17021}
17022
17023sub adjustParamPos($$$)
17024{
17025    my ($Pos, $Symbol, $LibVersion) = @_;
17026    if(defined $CompleteSignature{$LibVersion}{$Symbol})
17027    {
17028        if(not $CompleteSignature{$LibVersion}{$Symbol}{"Static"}
17029        and $CompleteSignature{$LibVersion}{$Symbol}{"Class"})
17030        {
17031            return $Pos-1;
17032        }
17033
17034        return $Pos;
17035    }
17036
17037    return undef;
17038}
17039
17040sub getParamPos($$$)
17041{
17042    my ($Name, $Symbol, $LibVersion) = @_;
17043
17044    if(defined $CompleteSignature{$LibVersion}{$Symbol}
17045    and defined $CompleteSignature{$LibVersion}{$Symbol}{"Param"})
17046    {
17047        my $Info = $CompleteSignature{$LibVersion}{$Symbol};
17048        foreach (keys(%{$Info->{"Param"}}))
17049        {
17050            if($Info->{"Param"}{$_}{"name"} eq $Name)
17051            {
17052                return $_;
17053            }
17054        }
17055    }
17056
17057    return undef;
17058}
17059
17060sub getParamName($)
17061{
17062    my $Loc = $_[0];
17063    $Loc=~s/\->.*//g;
17064    return $Loc;
17065}
17066
17067sub getAffectedSymbols($$$$)
17068{
17069    my ($Level, $Target_TypeName, $Kinds_Locations, $Syms) = @_;
17070    my $LIMIT = 10;
17071
17072    if(defined $AffectLimit)
17073    {
17074        $LIMIT = $AffectLimit;
17075    }
17076
17077    my %SymSel = ();
17078    my %SymLocKind = ();
17079
17080    foreach my $Symbol (@{$Syms})
17081    {
17082        if(index($Symbol, "_Z")==0
17083        and $Symbol=~/(C2|D2|D0)[EI]/)
17084        { # duplicated problems for C2 constructors, D2 and D0 destructors
17085            next;
17086        }
17087
17088        foreach my $Kind (sort keys(%{$Kinds_Locations}))
17089        {
17090            if(not defined $CompatProblems{$Level}{$Symbol}
17091            or not defined $CompatProblems{$Level}{$Symbol}{$Kind}) {
17092                next;
17093            }
17094
17095            foreach my $Loc (sort keys(%{$Kinds_Locations->{$Kind}}))
17096            {
17097                if(not defined $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}) {
17098                    next;
17099                }
17100
17101                my ($SN, $SS, $SV) = separate_symbol($Symbol);
17102                if($Level eq "Source")
17103                { # remove symbol version
17104                    $Symbol = $SN;
17105                }
17106
17107                if($SV and defined $CompatProblems{$Level}{$SN}
17108                and defined $CompatProblems{$Level}{$SN}{$Kind}{$Loc})
17109                { # duplicated problems for versioned symbols
17110                    next;
17111                }
17112
17113                my $Type_Name = $CompatProblems{$Level}{$Symbol}{$Kind}{$Loc}{"Type_Name"};
17114                if($Type_Name ne $Target_TypeName) {
17115                    next;
17116                }
17117
17118                $SymLocKind{$Symbol}{$Loc}{$Kind} = 1;
17119            }
17120        }
17121    }
17122
17123    foreach my $Symbol (sort keys(%SymLocKind))
17124    {
17125        LOOP: foreach my $Loc (sort {$a=~/retval/ cmp $b=~/retval/} sort {length($a)<=>length($b)} sort keys(%{$SymLocKind{$Symbol}}))
17126        {
17127            foreach my $Kind (keys(%{$SymLocKind{$Symbol}{$Loc}}))
17128            {
17129                $SymSel{$Symbol}{"Loc"} = $Loc;
17130                $SymSel{$Symbol}{"Kind"} = $Kind;
17131
17132                last LOOP;
17133            }
17134        }
17135    }
17136
17137    my $Affected = "";
17138    my $Num = 0;
17139
17140    if($ReportFormat eq "xml")
17141    { # XML
17142        $Affected .= "      <affected>\n";
17143
17144        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
17145        {
17146            my $PName = getParamName($Loc);
17147            my $Desc = getAffectDesc($Level, $Symbol, $SymSel{$Symbol}{"Kind"}, $SymSel{$Symbol}{"Loc"});
17148
17149            my $Target = "";
17150            if($PName)
17151            {
17152                $Target .= " param=\"$PName\"";
17153                $Desc=~s/parameter $PName /parameter \@param /;
17154            }
17155            elsif($Loc=~/\Aretval(\-|\Z)/i) {
17156                $Target .= " affected=\"retval\"";
17157            }
17158            elsif($Loc=~/\Athis(\-|\Z)/i) {
17159                $Target .= " affected=\"this\"";
17160            }
17161
17162            if($Desc=~s/\AField ([^\s]+) /Field \@field /) {
17163                $Target .= " field=\"$1\"";
17164            }
17165
17166            $Affected .= "        <symbol name=\"$Symbol\"$Target>\n";
17167            $Affected .= "          <comment>".xmlSpecChars($Desc)."</comment>\n";
17168            $Affected .= "        </symbol>\n";
17169
17170            if($Num>$LIMIT) {
17171                last LOOP;
17172            }
17173
17174            $Num += 1;
17175        }
17176        $Affected .= "      </affected>\n";
17177    }
17178    else
17179    { # HTML
17180        foreach my $Symbol (sort {lc($a) cmp lc($b)} keys(%SymSel))
17181        {
17182            my $Desc = getAffectDesc($Level, $Symbol, $SymSel{$Symbol}{"Kind"}, $SymSel{$Symbol}{"Loc"});
17183            my $S = get_Signature($Symbol, 1);
17184            my $PName = getParamName($SymSel{$Symbol}{"Loc"});print "$Symbol\n" if($Symbol eq "sftp_file_set_blocking");
17185            my $Pos = adjustParamPos(getParamPos($PName, $Symbol, 1), $Symbol, 1);
17186
17187            $Affected .= "<span class='iname_a'>".highLight_Signature_PPos_Italic($S, $Pos, 1, 0, 0)."</span><br/>";
17188            $Affected .= "<div class='affect'>".htmlSpecChars($Desc)."</div>\n";
17189
17190            if($Num>$LIMIT) {
17191                last;
17192            }
17193
17194            $Num += 1;
17195        }
17196
17197        if(keys(%SymSel)>$LIMIT) {
17198            $Affected .= " ...<br/>"; # and others ...
17199        }
17200
17201        $Affected = "<div class='affected'>".$Affected."</div>";
17202        if($Affected)
17203        {
17204            $Affected = $ContentDivStart.$Affected.$ContentDivEnd;
17205            $Affected = $ContentSpanStart_Affected."[+] affected symbols (".keys(%SymSel).")".$ContentSpanEnd.$Affected;
17206        }
17207    }
17208
17209    return $Affected;
17210}
17211
17212sub cmpLocations($$)
17213{
17214    my ($L1, $L2) = @_;
17215    if($L2=~/\A(retval|this)\b/
17216    and $L1!~/\A(retval|this)\b/)
17217    {
17218        if($L1!~/\-\>/) {
17219            return 1;
17220        }
17221        elsif($L2=~/\-\>/) {
17222            return 1;
17223        }
17224    }
17225    return 0;
17226}
17227
17228sub getAffectDesc($$$$)
17229{
17230    my ($Level, $Symbol, $Kind, $Location) = @_;
17231
17232    my %Problem = %{$CompatProblems{$Level}{$Symbol}{$Kind}{$Location}};
17233
17234    my $Location_I = $Location;
17235    $Location=~s/\A(.*)\-\>(.+?)\Z/$1/; # without the latest affected field
17236
17237    my @Sentence = ();
17238
17239    if($Kind eq "Overridden_Virtual_Method"
17240    or $Kind eq "Overridden_Virtual_Method_B") {
17241        push(@Sentence, "The method '".$Problem{"New_Value"}."' will be called instead of this method.");
17242    }
17243    elsif($CompatRules{$Level}{$Kind}{"Kind"} eq "Types")
17244    {
17245        my %SymInfo = %{$CompleteSignature{1}{$Symbol}};
17246
17247        if($Location eq "this" or $Kind=~/(\A|_)Virtual(_|\Z)/)
17248        {
17249            my $METHOD_TYPE = $SymInfo{"Constructor"}?"constructor":"method";
17250            my $ClassName = $TypeInfo{1}{$SymInfo{"Class"}}{"Name"};
17251
17252            if($ClassName eq $Problem{"Type_Name"}) {
17253                push(@Sentence, "This $METHOD_TYPE is from \'".$Problem{"Type_Name"}."\' class.");
17254            }
17255            else {
17256                push(@Sentence, "This $METHOD_TYPE is from derived class \'".$ClassName."\'.");
17257            }
17258        }
17259        else
17260        {
17261            my $TypeID = undef;
17262
17263            if($Location=~/retval/)
17264            { # return value
17265                if(index($Location, "->")!=-1) {
17266                    push(@Sentence, "Field \'".$Location."\' in return value");
17267                }
17268                else {
17269                    push(@Sentence, "Return value");
17270                }
17271
17272                $TypeID = $SymInfo{"Return"};
17273            }
17274            elsif($Location=~/this/)
17275            { # "this" pointer
17276                if(index($Location, "->")!=-1) {
17277                    push(@Sentence, "Field \'".$Location."\' in the object of this method");
17278                }
17279                else {
17280                    push(@Sentence, "\'this\' pointer");
17281                }
17282
17283                $TypeID = $SymInfo{"Class"};
17284            }
17285            else
17286            { # parameters
17287
17288                my $PName = getParamName($Location);
17289                my $PPos = getParamPos($PName, $Symbol, 1);
17290
17291                if(index($Location, "->")!=-1) {
17292                    push(@Sentence, "Field \'".$Location."\' in ".showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
17293                }
17294                else {
17295                    push(@Sentence, showPos(adjustParamPos($PPos, $Symbol, 1))." parameter");
17296                }
17297                if($PName) {
17298                    push(@Sentence, "\'".$PName."\'");
17299                }
17300
17301                $TypeID = $SymInfo{"Param"}{$PPos}{"type"};
17302            }
17303
17304            if($Location!~/this/)
17305            {
17306                if(my %PureType = get_PureType($TypeID, $TypeInfo{1}))
17307                {
17308                    if($PureType{"Type"} eq "Pointer") {
17309                        push(@Sentence, "(pointer)");
17310                    }
17311                    elsif($PureType{"Type"} eq "Ref") {
17312                        push(@Sentence, "(reference)");
17313                    }
17314                }
17315            }
17316
17317            if($Location eq "this") {
17318                push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
17319            }
17320            else
17321            {
17322                my $Location_T = $Location;
17323                $Location_T=~s/\A\w+(\->|\Z)//; # location in type
17324
17325                my $TypeID_Problem = $TypeID;
17326                if($Location_T) {
17327                    $TypeID_Problem = getFieldType($Location_T, $TypeID, 1);
17328                }
17329
17330                if($TypeInfo{1}{$TypeID_Problem}{"Name"} eq $Problem{"Type_Name"}) {
17331                    push(@Sentence, "has type \'".$Problem{"Type_Name"}."\'.");
17332                }
17333                else {
17334                    push(@Sentence, "has base type \'".$Problem{"Type_Name"}."\'.");
17335                }
17336            }
17337        }
17338    }
17339    if($ExtendedSymbols{$Symbol}) {
17340        push(@Sentence, " This is a symbol from an external library that may use the \'$TargetLibraryName\' library and change the ABI after recompiling.");
17341    }
17342
17343    my $Sent = join(" ", @Sentence);
17344
17345    $Sent=~s/->/./g;
17346
17347    if($ReportFormat eq "xml")
17348    {
17349        $Sent=~s/'//g;
17350    }
17351
17352    return $Sent;
17353}
17354
17355sub getFieldType($$$)
17356{
17357    my ($Location, $TypeId, $LibVersion) = @_;
17358
17359    my @Fields = split(/\->/, $Location);
17360
17361    foreach my $Name (@Fields)
17362    {
17363        my %Info = get_BaseType($TypeId, $LibVersion);
17364
17365        foreach my $Pos (keys(%{$Info{"Memb"}}))
17366        {
17367            if($Info{"Memb"}{$Pos}{"name"} eq $Name)
17368            {
17369                $TypeId = $Info{"Memb"}{$Pos}{"type"};
17370                last;
17371            }
17372        }
17373    }
17374
17375    return $TypeId;
17376}
17377
17378sub get_XmlSign($$)
17379{
17380    my ($Symbol, $LibVersion) = @_;
17381    my $Info = $CompleteSignature{$LibVersion}{$Symbol};
17382    my $Report = "";
17383    foreach my $Pos (sort {int($a)<=>int($b)} keys(%{$Info->{"Param"}}))
17384    {
17385        my $Name = $Info->{"Param"}{$Pos}{"name"};
17386        my $Type = $Info->{"Param"}{$Pos}{"type"};
17387        my $TypeName = $TypeInfo{$LibVersion}{$Type}{"Name"};
17388        foreach my $Typedef (keys(%ChangedTypedef))
17389        {
17390            if(my $Base = $Typedef_BaseName{$LibVersion}{$Typedef}) {
17391                $TypeName=~s/\b\Q$Typedef\E\b/$Base/g;
17392            }
17393        }
17394        $Report .= "    <param pos=\"$Pos\">\n";
17395        $Report .= "      <name>".$Name."</name>\n";
17396        $Report .= "      <type>".xmlSpecChars($TypeName)."</type>\n";
17397        $Report .= "    </param>\n";
17398    }
17399    if(my $Return = $Info->{"Return"})
17400    {
17401        my $RTName = $TypeInfo{$LibVersion}{$Return}{"Name"};
17402        $Report .= "    <retval>\n";
17403        $Report .= "      <type>".xmlSpecChars($RTName)."</type>\n";
17404        $Report .= "    </retval>\n";
17405    }
17406    return $Report;
17407}
17408
17409sub get_Report_SymbolsInfo($)
17410{
17411    my $Level = $_[0];
17412    my $Report = "<symbols_info>\n";
17413    foreach my $Symbol (sort keys(%{$CompatProblems{$Level}}))
17414    {
17415        my ($SN, $SS, $SV) = separate_symbol($Symbol);
17416        if($SV and defined $CompatProblems{$Level}{$SN}) {
17417            next;
17418        }
17419        $Report .= "  <symbol name=\"$Symbol\">\n";
17420        my ($S1, $P1, $S2, $P2) = ();
17421        if(not $AddedInt{$Level}{$Symbol})
17422        {
17423            if(defined $CompleteSignature{1}{$Symbol}
17424            and defined $CompleteSignature{1}{$Symbol}{"Header"})
17425            {
17426                $P1 = get_XmlSign($Symbol, 1);
17427                $S1 = get_Signature($Symbol, 1);
17428            }
17429            elsif($Symbol=~/\A(_Z|\?)/) {
17430                $S1 = $tr_name{$Symbol};
17431            }
17432        }
17433        if(not $RemovedInt{$Level}{$Symbol})
17434        {
17435            if(defined $CompleteSignature{2}{$Symbol}
17436            and defined $CompleteSignature{2}{$Symbol}{"Header"})
17437            {
17438                $P2 = get_XmlSign($Symbol, 2);
17439                $S2 = get_Signature($Symbol, 2);
17440            }
17441            elsif($Symbol=~/\A(_Z|\?)/) {
17442                $S2 = $tr_name{$Symbol};
17443            }
17444        }
17445        if($S1)
17446        {
17447            $Report .= "    <old signature=\"".xmlSpecChars($S1)."\">\n";
17448            $Report .= $P1;
17449            $Report .= "    </old>\n";
17450        }
17451        if($S2 and $S2 ne $S1)
17452        {
17453            $Report .= "    <new signature=\"".xmlSpecChars($S2)."\">\n";
17454            $Report .= $P2;
17455            $Report .= "    </new>\n";
17456        }
17457        $Report .= "  </symbol>\n";
17458    }
17459    $Report .= "</symbols_info>\n";
17460    return $Report;
17461}
17462
17463sub writeReport($$)
17464{
17465    my ($Level, $Report) = @_;
17466    if($ReportFormat eq "xml") {
17467        $Report = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".$Report;
17468    }
17469    if($StdOut)
17470    { # --stdout option
17471        print STDOUT $Report;
17472    }
17473    else
17474    {
17475        my $RPath = getReportPath($Level);
17476        mkpath(get_dirname($RPath));
17477
17478        open(REPORT, ">", $RPath) || die ("can't open file \'$RPath\': $!\n");
17479        print REPORT $Report;
17480        close(REPORT);
17481    }
17482}
17483
17484sub getReport($)
17485{
17486    my $Level = $_[0];
17487    if($ReportFormat eq "xml")
17488    { # XML
17489        if($Level eq "Join")
17490        {
17491            my $Report = "<reports>\n";
17492            $Report .= getReport("Binary");
17493            $Report .= getReport("Source");
17494            $Report .= "</reports>\n";
17495            return $Report;
17496        }
17497        else
17498        {
17499            my $Report = "<report kind=\"".lc($Level)."\" version=\"$XML_REPORT_VERSION\">\n\n";
17500            my ($Summary, $MetaData) = get_Summary($Level);
17501            $Report .= $Summary."\n";
17502            $Report .= get_Report_Added($Level).get_Report_Removed($Level);
17503            $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
17504
17505            # additional symbols info (if needed)
17506            # $Report .= get_Report_SymbolsInfo($Level);
17507
17508            $Report .= "</report>\n";
17509            return $Report;
17510        }
17511    }
17512    else
17513    { # HTML
17514        my $CssStyles = readModule("Styles", "Report.css");
17515        my $JScripts = readModule("Scripts", "Sections.js");
17516        if($Level eq "Join")
17517        {
17518            $CssStyles .= "\n".readModule("Styles", "Tabs.css");
17519            $JScripts .= "\n".readModule("Scripts", "Tabs.js");
17520            my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." compatibility report";
17521            my $Keywords = $TargetTitle.", compatibility, API, report";
17522            my $Description = "Compatibility report for the $TargetTitle $TargetComponent between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
17523            my ($BSummary, $BMetaData) = get_Summary("Binary");
17524            my ($SSummary, $SMetaData) = get_Summary("Source");
17525            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>";
17526            $Report .= get_Report_Title("Join")."
17527            <br/><div class='tabset'>
17528            <a id='BinaryID' href='#BinaryTab' class='tab active'>Binary<br/>Compatibility</a>
17529            <a id='SourceID' href='#SourceTab' style='margin-left:3px' class='tab disabled'>Source<br/>Compatibility</a>
17530            </div>";
17531            $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>";
17532            $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>";
17533            $Report .= getReportFooter($TargetTitle, not $JoinReport);
17534            $Report .= "\n</body></html>\n";
17535            return $Report;
17536        }
17537        else
17538        {
17539            my ($Summary, $MetaData) = get_Summary($Level);
17540            my $Title = $TargetTitle.": ".$Descriptor{1}{"Version"}." to ".$Descriptor{2}{"Version"}." ".lc($Level)." compatibility report";
17541            my $Keywords = $TargetTitle.", ".lc($Level)." compatibility, API, report";
17542            my $Description = "$Level compatibility report for the ".$TargetTitle." ".$TargetComponent." between ".$Descriptor{1}{"Version"}." and ".$Descriptor{2}{"Version"}." versions";
17543            if($Level eq "Binary")
17544            {
17545                if(getArch(1) eq getArch(2)
17546                and getArch(1) ne "unknown") {
17547                    $Description .= " on ".showArch(getArch(1));
17548                }
17549            }
17550            my $Report = "<!-\- $MetaData -\->\n".composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."\n<body>\n<div><a name='Top'></a>\n";
17551            $Report .= get_Report_Title($Level)."\n".$Summary."\n";
17552            $Report .= get_Report_Added($Level).get_Report_Removed($Level);
17553            $Report .= get_Report_Problems("High", $Level).get_Report_Problems("Medium", $Level).get_Report_Problems("Low", $Level).get_Report_Problems("Safe", $Level);
17554            $Report .= get_SourceInfo();
17555            $Report .= "</div>\n<br/><br/><br/><hr/>\n";
17556            $Report .= getReportFooter($TargetTitle, not $JoinReport);
17557            $Report .= "\n</body></html>\n";
17558            return $Report;
17559        }
17560    }
17561}
17562
17563sub getLegend()
17564{
17565    return "<br/>
17566<table class='summary'>
17567<tr>
17568    <td class='new'>added</td>
17569    <td class='passed'>compatible</td>
17570</tr>
17571<tr>
17572    <td class='warning'>warning</td>
17573    <td class='failed'>incompatible</td>
17574</tr></table>\n";
17575}
17576
17577sub createReport()
17578{
17579    if($JoinReport)
17580    { # --stdout
17581        writeReport("Join", getReport("Join"));
17582    }
17583    elsif($DoubleReport)
17584    { # default
17585        writeReport("Binary", getReport("Binary"));
17586        writeReport("Source", getReport("Source"));
17587    }
17588    elsif($BinaryOnly)
17589    { # --binary
17590        writeReport("Binary", getReport("Binary"));
17591    }
17592    elsif($SourceOnly)
17593    { # --source
17594        writeReport("Source", getReport("Source"));
17595    }
17596}
17597
17598sub getReportFooter($$)
17599{
17600    my ($LibName, $Single) = @_;
17601    my $Class = "footer";
17602    if(not $Single) {
17603        $Class .= " double_report";
17604    }
17605    my $Footer = "<div class=\'$Class\' align='right'><i>Generated on ".(localtime time);
17606    $Footer .= " by <a href='".$HomePage."'>ABI Compliance Checker</a> $TOOL_VERSION &#160;";
17607    $Footer .= "</i></div>";
17608    $Footer .= "<br/>";
17609    return $Footer;
17610}
17611
17612sub get_Report_Problems($$)
17613{
17614    my ($Severity, $Level) = @_;
17615
17616    my $Report = get_Report_TypeProblems($Severity, $Level);
17617    if(my $SProblems = get_Report_SymbolProblems($Severity, $Level)) {
17618        $Report .= $SProblems;
17619    }
17620
17621    if($Severity eq "Low" or $Severity eq "Safe") {
17622        $Report .= get_Report_ChangedConstants($Severity, $Level);
17623    }
17624
17625    if($ReportFormat eq "html")
17626    {
17627        if($Report)
17628        { # add anchor
17629            if($JoinReport)
17630            {
17631                if($Severity eq "Safe") {
17632                    $Report = "<a name=\'Other_".$Level."_Changes\'></a>".$Report;
17633                }
17634                else {
17635                    $Report = "<a name=\'".$Severity."_Risk_".$Level."_Problems\'></a>".$Report;
17636                }
17637            }
17638            else
17639            {
17640                if($Severity eq "Safe") {
17641                    $Report = "<a name=\'Other_Changes\'></a>".$Report;
17642                }
17643                else {
17644                    $Report = "<a name=\'".$Severity."_Risk_Problems\'></a>".$Report;
17645                }
17646            }
17647        }
17648    }
17649    return $Report;
17650}
17651
17652sub composeHTML_Head($$$$$)
17653{
17654    my ($Title, $Keywords, $Description, $Styles, $Scripts) = @_;
17655    return "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
17656    <html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
17657    <head>
17658    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />
17659    <meta name=\"keywords\" content=\"$Keywords\" />
17660    <meta name=\"description\" content=\"$Description\" />
17661    <title>
17662        $Title
17663    </title>
17664    <style type=\"text/css\">
17665    $Styles
17666    </style>
17667    <script type=\"text/javascript\" language=\"JavaScript\">
17668    <!--
17669    $Scripts
17670    -->
17671    </script>
17672    </head>";
17673}
17674
17675sub insertIDs($)
17676{
17677    my $Text = $_[0];
17678    while($Text=~/CONTENT_ID/)
17679    {
17680        if(int($Content_Counter)%2) {
17681            $ContentID -= 1;
17682        }
17683        $Text=~s/CONTENT_ID/c_$ContentID/;
17684        $ContentID += 1;
17685        $Content_Counter += 1;
17686    }
17687    return $Text;
17688}
17689
17690sub checkPreprocessedUnit($)
17691{
17692    my $Path = $_[0];
17693    my ($CurHeader, $CurHeaderName) = ("", "");
17694    my $CurClass = ""; # extra info
17695    open(PREPROC, $Path) || die ("can't open file \'$Path\': $!\n");
17696
17697    while(my $Line = <PREPROC>)
17698    { # detecting public and private constants
17699        if(substr($Line, 0, 1) eq "#")
17700        {
17701            chomp($Line);
17702            if($Line=~/\A\#\s+\d+\s+\"(.+)\"/)
17703            {
17704                $CurHeader = path_format($1, $OSgroup);
17705                $CurHeaderName = get_filename($CurHeader);
17706                $CurClass = "";
17707
17708                if(index($CurHeader, $TMP_DIR)==0) {
17709                    next;
17710                }
17711
17712                if(substr($CurHeaderName, 0, 1) eq "<")
17713                { # <built-in>, <command-line>, etc.
17714                    $CurHeaderName = "";
17715                    $CurHeader = "";
17716                }
17717
17718                if($ExtraInfo)
17719                {
17720                    if($CurHeaderName) {
17721                        $PreprocessedHeaders{$Version}{$CurHeader} = 1;
17722                    }
17723                }
17724            }
17725            if(not $ExtraDump)
17726            {
17727                if($CurHeaderName)
17728                {
17729                    if(not $Include_Neighbors{$Version}{$CurHeaderName}
17730                    and not $Registered_Headers{$Version}{$CurHeader})
17731                    { # not a target
17732                        next;
17733                    }
17734                    if(not is_target_header($CurHeaderName, 1)
17735                    and not is_target_header($CurHeaderName, 2))
17736                    { # user-defined header
17737                        next;
17738                    }
17739                }
17740            }
17741
17742            if($Line=~/\A\#\s*define\s+(\w+)\s+(.+)\s*\Z/)
17743            {
17744                my ($Name, $Value) = ($1, $2);
17745                if(not $Constants{$Version}{$Name}{"Access"})
17746                {
17747                    $Constants{$Version}{$Name}{"Access"} = "public";
17748                    $Constants{$Version}{$Name}{"Value"} = $Value;
17749                    if($CurHeaderName) {
17750                        $Constants{$Version}{$Name}{"Header"} = $CurHeaderName;
17751                    }
17752                }
17753            }
17754            elsif($Line=~/\A\#[ \t]*undef[ \t]+([_A-Z]+)[ \t]*/) {
17755                $Constants{$Version}{$1}{"Access"} = "private";
17756            }
17757        }
17758        else
17759        {
17760            if(defined $ExtraDump)
17761            {
17762                if($Line=~/(\w+)\s*\(/)
17763                { # functions
17764                    $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader;
17765                }
17766                #elsif($Line=~/(\w+)\s*;/)
17767                #{ # data
17768                #    $SymbolHeader{$Version}{$CurClass}{$1} = $CurHeader;
17769                #}
17770                elsif($Line=~/(\A|\s)class\s+(\w+)/) {
17771                    $CurClass = $2;
17772                }
17773            }
17774        }
17775    }
17776    close(PREPROC);
17777    foreach my $Constant (keys(%{$Constants{$Version}}))
17778    {
17779        if($Constants{$Version}{$Constant}{"Access"} eq "private")
17780        {
17781            delete($Constants{$Version}{$Constant});
17782            next;
17783        }
17784        if(not $ExtraDump and ($Constant=~/_h\Z/i
17785        or isBuiltIn($Constants{$Version}{$Constant}{"Header"})))
17786        { # skip
17787            delete($Constants{$Version}{$Constant});
17788        }
17789        else {
17790            delete($Constants{$Version}{$Constant}{"Access"});
17791        }
17792    }
17793    if($Debug)
17794    {
17795        mkpath($DEBUG_PATH{$Version});
17796        copy($Path, $DEBUG_PATH{$Version}."/preprocessor.txt");
17797    }
17798}
17799
17800sub uncoverConstant($$)
17801{
17802    my ($LibVersion, $Constant) = @_;
17803    return "" if(not $LibVersion or not $Constant);
17804    return $Constant if(isCyclical(\@RecurConstant, $Constant));
17805    if(defined $Cache{"uncoverConstant"}{$LibVersion}{$Constant}) {
17806        return $Cache{"uncoverConstant"}{$LibVersion}{$Constant};
17807    }
17808
17809    if(defined $Constants{$LibVersion}{$Constant})
17810    {
17811        my $Value = $Constants{$LibVersion}{$Constant}{"Value"};
17812        if(defined $Constants{$LibVersion}{$Value})
17813        {
17814            push(@RecurConstant, $Constant);
17815            my $Uncovered = uncoverConstant($LibVersion, $Value);
17816            if($Uncovered ne "") {
17817                $Value = $Uncovered;
17818            }
17819            pop(@RecurConstant);
17820        }
17821
17822        # FIXME: uncover $Value using all the enum constants
17823        # USE CASE: change of define NC_LONG from NC_INT (enum value) to NC_INT (define)
17824        return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = $Value);
17825    }
17826    return ($Cache{"uncoverConstant"}{$LibVersion}{$Constant} = "");
17827}
17828
17829sub simpleConstant($$)
17830{
17831    my ($LibVersion, $Value) = @_;
17832    if($Value=~/\W/)
17833    {
17834        my $Value_Copy = $Value;
17835        while($Value_Copy=~s/([a-z_]\w+)/\@/i)
17836        {
17837            my $Word = $1;
17838            if($Value!~/$Word\s*\(/)
17839            {
17840                my $Val = uncoverConstant($LibVersion, $Word);
17841                if($Val ne "")
17842                {
17843                    $Value=~s/\b$Word\b/$Val/g;
17844                }
17845            }
17846        }
17847    }
17848    return $Value;
17849}
17850
17851sub computeValue($)
17852{
17853    my $Value = $_[0];
17854
17855    if($Value=~/\A\((-?[\d]+)\)\Z/) {
17856        return $1;
17857    }
17858
17859    if($Value=~/\A[\d\-\+()]+\Z/) {
17860        return eval($Value);
17861    }
17862
17863    return $Value;
17864}
17865
17866my %IgnoreConstant = map {$_=>1} (
17867    "VERSION",
17868    "VERSIONCODE",
17869    "VERNUM",
17870    "VERS_INFO",
17871    "PATCHLEVEL",
17872    "INSTALLPREFIX",
17873    "VBUILD",
17874    "VPATCH",
17875    "VMINOR",
17876    "BUILD_STRING",
17877    "BUILD_TIME",
17878    "PACKAGE_STRING",
17879    "PRODUCTION",
17880    "CONFIGURE_COMMAND",
17881    "INSTALLDIR",
17882    "BINDIR",
17883    "CONFIG_FILE_PATH",
17884    "DATADIR",
17885    "EXTENSION_DIR",
17886    "INCLUDE_PATH",
17887    "LIBDIR",
17888    "LOCALSTATEDIR",
17889    "SBINDIR",
17890    "SYSCONFDIR",
17891    "RELEASE",
17892    "SOURCE_ID",
17893    "SUBMINOR",
17894    "MINOR",
17895    "MINNOR",
17896    "MINORVERSION",
17897    "MAJOR",
17898    "MAJORVERSION",
17899    "MICRO",
17900    "MICROVERSION",
17901    "BINARY_AGE",
17902    "INTERFACE_AGE",
17903    "CORE_ABI",
17904    "PATCH",
17905    "COPYRIGHT",
17906    "TIMESTAMP",
17907    "REVISION",
17908    "PACKAGE_TAG",
17909    "PACKAGEDATE",
17910    "NUMVERSION",
17911    "Release",
17912    "Version"
17913);
17914
17915sub constantFilter($$$)
17916{
17917    my ($Name, $Value, $Level) = @_;
17918
17919    if($Level eq "Binary")
17920    {
17921        if($Name=~/_t\Z/)
17922        { # __malloc_ptr_t
17923            return 1;
17924        }
17925        foreach (keys(%IgnoreConstant))
17926        {
17927            if($Name=~/(\A|_)$_(_|\Z)/)
17928            { # version
17929                return 1;
17930            }
17931            if(/\A[A-Z].*[a-z]\Z/)
17932            {
17933                if($Name=~/(\A|[a-z])$_([A-Z]|\Z)/)
17934                { # version
17935                    return 1;
17936                }
17937            }
17938        }
17939        if($Name=~/(\A|_)(lib|open|)$TargetLibraryShortName(_|)(VERSION|VER|DATE|API|PREFIX)(_|\Z)/i)
17940        { # version
17941            return 1;
17942        }
17943        if($Value=~/\A('|"|)[\/\\]\w+([\/\\]|:|('|"|)\Z)/ or $Value=~/[\/\\]\w+[\/\\]\w+/)
17944        { # /lib64:/usr/lib64:/lib:/usr/lib:/usr/X11R6/lib/Xaw3d ...
17945            return 1;
17946        }
17947
17948        if($Value=~/\A["'].*['"]/i)
17949        { # string
17950            return 0;
17951        }
17952
17953        if($Value=~/\A[({]*\s*[a-z_]+\w*(\s+|[\|,])/i)
17954        { # static int gcry_pth_init
17955          # extern ABC
17956          # (RE_BACKSLASH_ESCAPE_IN_LISTS | RE...
17957          # { H5FD_MEM_SUPER, H5FD_MEM_SUPER, ...
17958            return 1;
17959        }
17960        if($Value=~/\w+\s*\(/i)
17961        { # foo(p)
17962            return 1;
17963        }
17964        if($Value=~/\A[a-z_]+\w*\Z/i)
17965        { # asn1_node_st
17966          # __SMTH_P
17967            return 1;
17968        }
17969    }
17970
17971    return 0;
17972}
17973
17974sub mergeConstants($)
17975{
17976    my $Level = $_[0];
17977    foreach my $Constant (keys(%{$Constants{1}}))
17978    {
17979        if($SkipConstants{1}{$Constant})
17980        { # skipped by the user
17981            next;
17982        }
17983
17984        if(my $Header = $Constants{1}{$Constant}{"Header"})
17985        {
17986            if(not is_target_header($Header, 1)
17987            and not is_target_header($Header, 2))
17988            { # user-defined header
17989                next;
17990            }
17991        }
17992        else {
17993            next;
17994        }
17995
17996        my $Old_Value = uncoverConstant(1, $Constant);
17997
17998        if(constantFilter($Constant, $Old_Value, $Level))
17999        { # separate binary and source problems
18000            next;
18001        }
18002
18003        if(not defined $Constants{2}{$Constant}{"Value"})
18004        { # removed
18005            %{$CompatProblems_Constants{$Level}{$Constant}{"Removed_Constant"}} = (
18006                "Target"=>$Constant,
18007                "Old_Value"=>$Old_Value  );
18008            next;
18009        }
18010
18011        if($Constants{2}{$Constant}{"Value"} eq "")
18012        { # empty value
18013          # TODO: implement a rule
18014            next;
18015        }
18016
18017        my $New_Value = uncoverConstant(2, $Constant);
18018
18019        my $Old_Value_Pure = $Old_Value;
18020        my $New_Value_Pure = $New_Value;
18021
18022        $Old_Value_Pure=~s/(\W)\s+/$1/g;
18023        $Old_Value_Pure=~s/\s+(\W)/$1/g;
18024        $New_Value_Pure=~s/(\W)\s+/$1/g;
18025        $New_Value_Pure=~s/\s+(\W)/$1/g;
18026
18027        next if($New_Value_Pure eq "" or $Old_Value_Pure eq "");
18028
18029        if($New_Value_Pure ne $Old_Value_Pure)
18030        { # different values
18031            if(simpleConstant(1, $Old_Value) eq simpleConstant(2, $New_Value))
18032            { # complex values
18033                next;
18034            }
18035            if(computeValue($Old_Value) eq computeValue($New_Value))
18036            { # expressions
18037                next;
18038            }
18039            if(convert_integer($Old_Value) eq convert_integer($New_Value))
18040            { # 0x0001 and 0x1, 0x1 and 1 equal constants
18041                next;
18042            }
18043            if($Old_Value eq "0" and $New_Value eq "NULL")
18044            { # 0 => NULL
18045                next;
18046            }
18047            if($Old_Value eq "NULL" and $New_Value eq "0")
18048            { # NULL => 0
18049                next;
18050            }
18051            %{$CompatProblems_Constants{$Level}{$Constant}{"Changed_Constant"}} = (
18052                "Target"=>$Constant,
18053                "Old_Value"=>$Old_Value,
18054                "New_Value"=>$New_Value  );
18055        }
18056    }
18057
18058    foreach my $Constant (keys(%{$Constants{2}}))
18059    {
18060        if(not defined $Constants{1}{$Constant}{"Value"})
18061        {
18062            if($SkipConstants{2}{$Constant})
18063            { # skipped by the user
18064                next;
18065            }
18066
18067            if(my $Header = $Constants{2}{$Constant}{"Header"})
18068            {
18069                if(not is_target_header($Header, 1)
18070                and not is_target_header($Header, 2))
18071                { # user-defined header
18072                    next;
18073                }
18074            }
18075            else {
18076                next;
18077            }
18078
18079            my $New_Value = uncoverConstant(2, $Constant);
18080            if(not defined $New_Value or $New_Value eq "") {
18081                next;
18082            }
18083
18084            if(constantFilter($Constant, $New_Value, $Level))
18085            { # separate binary and source problems
18086                next;
18087            }
18088
18089            %{$CompatProblems_Constants{$Level}{$Constant}{"Added_Constant"}} = (
18090                "Target"=>$Constant,
18091                "New_Value"=>$New_Value  );
18092        }
18093    }
18094}
18095
18096sub convert_integer($)
18097{
18098    my $Value = $_[0];
18099    if($Value=~/\A0x[a-f0-9]+\Z/)
18100    { # hexadecimal
18101        return hex($Value);
18102    }
18103    elsif($Value=~/\A0[0-7]+\Z/)
18104    { # octal
18105        return oct($Value);
18106    }
18107    elsif($Value=~/\A0b[0-1]+\Z/)
18108    { # binary
18109        return oct($Value);
18110    }
18111    else {
18112        return $Value;
18113    }
18114}
18115
18116sub readSymbols($)
18117{
18118    my $LibVersion = $_[0];
18119    my @LibPaths = getSOPaths($LibVersion);
18120    if($#LibPaths==-1 and not $CheckHeadersOnly)
18121    {
18122        if($LibVersion==1)
18123        {
18124            printMsg("WARNING", "checking headers only");
18125            $CheckHeadersOnly = 1;
18126        }
18127        else {
18128            exitStatus("Error", "$SLIB_TYPE libraries are not found in ".$Descriptor{$LibVersion}{"Version"});
18129        }
18130    }
18131
18132    foreach my $LibPath (@LibPaths) {
18133        readSymbols_Lib($LibVersion, $LibPath, 0, "+Weak", 1, 1);
18134    }
18135
18136    if($CheckUndefined)
18137    {
18138        my %UndefinedLibs = ();
18139
18140        my @Libs = (keys(%{$Library_Symbol{$LibVersion}}), keys(%{$DepLibrary_Symbol{$LibVersion}}));
18141
18142        foreach my $LibName (sort @Libs)
18143        {
18144            if(defined $UndefinedSymbols{$LibVersion}{$LibName})
18145            {
18146                foreach my $Symbol (keys(%{$UndefinedSymbols{$LibVersion}{$LibName}}))
18147                {
18148                    if($Symbol_Library{$LibVersion}{$Symbol}
18149                    or $DepSymbol_Library{$LibVersion}{$Symbol})
18150                    { # exported by target library
18151                        next;
18152                    }
18153                    if(index($Symbol, '@')!=-1)
18154                    { # exported default symbol version (@@)
18155                        $Symbol=~s/\@/\@\@/;
18156                        if($Symbol_Library{$LibVersion}{$Symbol}
18157                        or $DepSymbol_Library{$LibVersion}{$Symbol}) {
18158                            next;
18159                        }
18160                    }
18161                    foreach my $Path (find_SymbolLibs($LibVersion, $Symbol)) {
18162                        $UndefinedLibs{$Path} = 1;
18163                    }
18164                }
18165            }
18166        }
18167        if($ExtraInfo)
18168        { # extra information for other tools
18169            if(my @Paths = sort keys(%UndefinedLibs))
18170            {
18171                my $LibString = "";
18172                my %Dirs = ();
18173                foreach (@Paths)
18174                {
18175                    $KnownLibs{$_} = 1;
18176                    my ($Dir, $Name) = separate_path($_);
18177
18178                    if(not grep {$Dir eq $_} (@{$SystemPaths{"lib"}})) {
18179                        $Dirs{esc($Dir)} = 1;
18180                    }
18181
18182                    $Name = parse_libname($Name, "name", $OStarget);
18183                    $Name=~s/\Alib//;
18184
18185                    $LibString .= " -l$Name";
18186                }
18187
18188                foreach my $Dir (sort {$b cmp $a} keys(%Dirs))
18189                {
18190                    $LibString = " -L".esc($Dir).$LibString;
18191                }
18192
18193                writeFile($ExtraInfo."/libs-string", $LibString);
18194            }
18195        }
18196    }
18197
18198    if($ExtraInfo) {
18199        writeFile($ExtraInfo."/lib-paths", join("\n", sort keys(%KnownLibs)));
18200    }
18201
18202    if(not $CheckHeadersOnly)
18203    {
18204        if($#LibPaths!=-1)
18205        {
18206            if(not keys(%{$Symbol_Library{$LibVersion}}))
18207            {
18208                printMsg("WARNING", "the set of public symbols in library(ies) is empty ($LibVersion)");
18209                printMsg("WARNING", "checking headers only");
18210                $CheckHeadersOnly = 1;
18211            }
18212        }
18213    }
18214
18215   # clean memory
18216   %SystemObjects = ();
18217}
18218
18219my %Prefix_Lib_Map=(
18220 # symbols for autodetecting library dependencies (by prefix)
18221    "pthread_" => ["libpthread"],
18222    "g_" => ["libglib-2.0", "libgobject-2.0", "libgio-2.0"],
18223    "cairo_" => ["libcairo"],
18224    "gtk_" => ["libgtk-x11-2.0"],
18225    "atk_" => ["libatk-1.0"],
18226    "gdk_" => ["libgdk-x11-2.0"],
18227    "gl" => ["libGL"],
18228    "glu" => ["libGLU"],
18229    "popt" => ["libpopt"],
18230    "Py" => ["libpython"],
18231    "jpeg_" => ["libjpeg"],
18232    "BZ2_" => ["libbz2"],
18233    "Fc" => ["libfontconfig"],
18234    "Xft" => ["libXft"],
18235    "SSL_" => ["libssl"],
18236    "sem_" => ["libpthread"],
18237    "snd_" => ["libasound"],
18238    "art_" => ["libart_lgpl_2"],
18239    "dbus_g" => ["libdbus-glib-1"],
18240    "GOMP_" => ["libgomp"],
18241    "omp_" => ["libgomp"],
18242    "cms" => ["liblcms"]
18243);
18244
18245my %Pattern_Lib_Map=(
18246    "SL[a-z]" => ["libslang"]
18247);
18248
18249my %Symbol_Lib_Map=(
18250 # symbols for autodetecting library dependencies (by name)
18251    "pow" => "libm",
18252    "fmod" => "libm",
18253    "sin" => "libm",
18254    "floor" => "libm",
18255    "cos" => "libm",
18256    "dlopen" => "libdl",
18257    "deflate" => "libz",
18258    "inflate" => "libz",
18259    "move_panel" => "libpanel",
18260    "XOpenDisplay" => "libX11",
18261    "resize_term" => "libncurses",
18262    "clock_gettime" => "librt",
18263    "crypt" => "libcrypt"
18264);
18265
18266sub find_SymbolLibs($$)
18267{
18268    my ($LibVersion, $Symbol) = @_;
18269
18270    if(index($Symbol, "g_")==0 and $Symbol=~/[A-Z]/)
18271    { # debug symbols
18272        return ();
18273    }
18274
18275    my %Paths = ();
18276
18277    if(my $LibName = $Symbol_Lib_Map{$Symbol})
18278    {
18279        if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18280            $Paths{$Path} = 1;
18281        }
18282    }
18283
18284    if(my $SymbolPrefix = getPrefix($Symbol))
18285    {
18286        if(defined $Cache{"find_SymbolLibs"}{$SymbolPrefix}) {
18287            return @{$Cache{"find_SymbolLibs"}{$SymbolPrefix}};
18288        }
18289
18290        if(not keys(%Paths))
18291        {
18292            if(defined $Prefix_Lib_Map{$SymbolPrefix})
18293            {
18294                foreach my $LibName (@{$Prefix_Lib_Map{$SymbolPrefix}})
18295                {
18296                    if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18297                        $Paths{$Path} = 1;
18298                    }
18299                }
18300            }
18301        }
18302
18303        if(not keys(%Paths))
18304        {
18305            foreach my $Prefix (sort keys(%Pattern_Lib_Map))
18306            {
18307                if($Symbol=~/\A$Prefix/)
18308                {
18309                    foreach my $LibName (@{$Pattern_Lib_Map{$Prefix}})
18310                    {
18311                        if(my $Path = get_LibPath($LibVersion, $LibName.".".$LIB_EXT)) {
18312                            $Paths{$Path} = 1;
18313                        }
18314                    }
18315                }
18316            }
18317        }
18318
18319        if(not keys(%Paths))
18320        {
18321            if($SymbolPrefix)
18322            { # try to find a library by symbol prefix
18323                if($SymbolPrefix eq "inotify" and
18324                index($Symbol, "\@GLIBC")!=-1)
18325                {
18326                    if(my $Path = get_LibPath($LibVersion, "libc.$LIB_EXT")) {
18327                        $Paths{$Path} = 1;
18328                    }
18329                }
18330                else
18331                {
18332                    if(my $Path = get_LibPath_Prefix($LibVersion, $SymbolPrefix)) {
18333                        $Paths{$Path} = 1;
18334                    }
18335                }
18336            }
18337        }
18338
18339        if(my @Paths = keys(%Paths)) {
18340            $Cache{"find_SymbolLibs"}{$SymbolPrefix} = \@Paths;
18341        }
18342    }
18343    return keys(%Paths);
18344}
18345
18346sub get_LibPath_Prefix($$)
18347{
18348    my ($LibVersion, $Prefix) = @_;
18349
18350    $Prefix = lc($Prefix);
18351    $Prefix=~s/[_]+\Z//g;
18352
18353    foreach ("-2", "2", "-1", "1", "")
18354    { # libgnome-2.so
18355      # libxml2.so
18356      # libdbus-1.so
18357        if(my $Path = get_LibPath($LibVersion, "lib".$Prefix.$_.".".$LIB_EXT)) {
18358            return $Path;
18359        }
18360    }
18361    return "";
18362}
18363
18364sub getPrefix($)
18365{
18366    my $Str = $_[0];
18367    if($Str=~/\A([_]*[A-Z][a-z]{1,5})[A-Z]/)
18368    { # XmuValidArea: Xmu
18369        return $1;
18370    }
18371    elsif($Str=~/\A([_]*[a-z]+)[A-Z]/)
18372    { # snfReadFont: snf
18373        return $1;
18374    }
18375    elsif($Str=~/\A([_]*[A-Z]{2,})[A-Z][a-z]+([A-Z][a-z]+|\Z)/)
18376    { # XRRTimes: XRR
18377        return $1;
18378    }
18379    elsif($Str=~/\A([_]*[a-z]{1,2}\d+)[a-z\d]*_[a-z]+/i)
18380    { # H5HF_delete: H5
18381        return $1;
18382    }
18383    elsif($Str=~/\A([_]*[a-z0-9]{2,}_)[a-z]+/i)
18384    { # alarm_event_add: alarm_
18385        return $1;
18386    }
18387    elsif($Str=~/\A(([a-z])\2{1,})/i)
18388    { # ffopen
18389        return $1;
18390    }
18391    return "";
18392}
18393
18394sub getSymbolSize($$)
18395{ # size from the shared library
18396    my ($Symbol, $LibVersion) = @_;
18397    return 0 if(not $Symbol);
18398    if(defined $Symbol_Library{$LibVersion}{$Symbol}
18399    and my $LibName = $Symbol_Library{$LibVersion}{$Symbol})
18400    {
18401        if(defined $Library_Symbol{$LibVersion}{$LibName}{$Symbol}
18402        and my $Size = $Library_Symbol{$LibVersion}{$LibName}{$Symbol})
18403        {
18404            if($Size<0) {
18405                return -$Size;
18406            }
18407        }
18408    }
18409    return 0;
18410}
18411
18412sub canonifyName($$)
18413{ # make TIFFStreamOpen(char const*, std::basic_ostream<char, std::char_traits<char> >*)
18414  # to be TIFFStreamOpen(char const*, std::basic_ostream<char>*)
18415    my ($Name, $Type) = @_;
18416
18417    # single
18418    while($Name=~/([^<>,]+),\s*$DEFAULT_STD_PARMS<([^<>,]+)>\s*/ and $1 eq $3)
18419    {
18420        my $P = $1;
18421        $Name=~s/\Q$P\E,\s*$DEFAULT_STD_PARMS<\Q$P\E>\s*/$P/g;
18422    }
18423
18424    # double
18425    if($Name=~/$DEFAULT_STD_PARMS/)
18426    {
18427        if($Type eq "S")
18428        {
18429            my ($ShortName, $FuncParams) = split_Signature($Name);
18430
18431            foreach my $FParam (separate_Params($FuncParams, 0, 0))
18432            {
18433                if(index($FParam, "<")!=-1)
18434                {
18435                    $FParam=~s/>([^<>]+)\Z/>/; # remove quals
18436                    my $FParam_N = canonifyName($FParam, "T");
18437                    if($FParam_N ne $FParam) {
18438                        $Name=~s/\Q$FParam\E/$FParam_N/g;
18439                    }
18440                }
18441            }
18442        }
18443        elsif($Type eq "T")
18444        {
18445            my ($ShortTmpl, $TmplParams) = template_Base($Name);
18446
18447            my @TParams = separate_Params($TmplParams, 0, 0);
18448            if($#TParams>=1)
18449            {
18450                my $FParam = $TParams[0];
18451                foreach my $Pos (1 .. $#TParams)
18452                {
18453                    my $TParam = $TParams[$Pos];
18454                    if($TParam=~/\A$DEFAULT_STD_PARMS<\Q$FParam\E\s*>\Z/) {
18455                        $Name=~s/\Q$FParam, $TParam\E\s*/$FParam/g;
18456                    }
18457                }
18458            }
18459        }
18460    }
18461    if($Type eq "S") {
18462        return formatName($Name, "S");
18463    }
18464    return $Name;
18465}
18466
18467sub translateSymbols(@)
18468{
18469    my $LibVersion = pop(@_);
18470    my (@MnglNames1, @MnglNames2, @UnmangledNames) = ();
18471    foreach my $Symbol (sort @_)
18472    {
18473        if(index($Symbol, "_Z")==0)
18474        {
18475            next if($tr_name{$Symbol});
18476            $Symbol=~s/[\@\$]+(.*)\Z//;
18477            push(@MnglNames1, $Symbol);
18478        }
18479        elsif(index($Symbol, "?")==0)
18480        {
18481            next if($tr_name{$Symbol});
18482            push(@MnglNames2, $Symbol);
18483        }
18484        else
18485        { # not mangled
18486            $tr_name{$Symbol} = $Symbol;
18487            $mangled_name_gcc{$Symbol} = $Symbol;
18488            $mangled_name{$LibVersion}{$Symbol} = $Symbol;
18489        }
18490    }
18491    if($#MnglNames1 > -1)
18492    { # GCC names
18493        @UnmangledNames = reverse(unmangleArray(@MnglNames1));
18494        foreach my $MnglName (@MnglNames1)
18495        {
18496            if(my $Unmangled = pop(@UnmangledNames))
18497            {
18498                $tr_name{$MnglName} = canonifyName($Unmangled, "S");
18499                if(not $mangled_name_gcc{$tr_name{$MnglName}}) {
18500                    $mangled_name_gcc{$tr_name{$MnglName}} = $MnglName;
18501                }
18502                if(index($MnglName, "_ZTV")==0
18503                and $tr_name{$MnglName}=~/vtable for (.+)/)
18504                { # bind class name and v-table symbol
18505                    my $ClassName = $1;
18506                    $ClassVTable{$ClassName} = $MnglName;
18507                    $VTableClass{$MnglName} = $ClassName;
18508                }
18509            }
18510        }
18511    }
18512    if($#MnglNames2 > -1)
18513    { # MSVC names
18514        @UnmangledNames = reverse(unmangleArray(@MnglNames2));
18515        foreach my $MnglName (@MnglNames2)
18516        {
18517            if(my $Unmangled = pop(@UnmangledNames))
18518            {
18519                $tr_name{$MnglName} = formatName($Unmangled, "S");
18520                $mangled_name{$LibVersion}{$tr_name{$MnglName}} = $MnglName;
18521            }
18522        }
18523    }
18524    return \%tr_name;
18525}
18526
18527sub link_symbol($$$)
18528{
18529    my ($Symbol, $RunWith, $Deps) = @_;
18530    if(link_symbol_internal($Symbol, $RunWith, \%Symbol_Library)) {
18531        return 1;
18532    }
18533    if($Deps eq "+Deps")
18534    { # check the dependencies
18535        if(link_symbol_internal($Symbol, $RunWith, \%DepSymbol_Library)) {
18536            return 1;
18537        }
18538    }
18539    return 0;
18540}
18541
18542sub link_symbol_internal($$$)
18543{
18544    my ($Symbol, $RunWith, $Where) = @_;
18545    return 0 if(not $Where or not $Symbol);
18546    if($Where->{$RunWith}{$Symbol})
18547    { # the exact match by symbol name
18548        return 1;
18549    }
18550    if(my $VSym = $SymVer{$RunWith}{$Symbol})
18551    { # indirect symbol version, i.e.
18552      # foo_old and its symlink foo@v (or foo@@v)
18553      # foo_old may be in symtab table
18554        if($Where->{$RunWith}{$VSym}) {
18555            return 1;
18556        }
18557    }
18558    my ($Sym, $Spec, $Ver) = separate_symbol($Symbol);
18559    if($Sym and $Ver)
18560    { # search for the symbol with the same version
18561      # or without version
18562        if($Where->{$RunWith}{$Sym})
18563        { # old: foo@v|foo@@v
18564          # new: foo
18565            return 1;
18566        }
18567        if($Where->{$RunWith}{$Sym."\@".$Ver})
18568        { # old: foo|foo@@v
18569          # new: foo@v
18570            return 1;
18571        }
18572        if($Where->{$RunWith}{$Sym."\@\@".$Ver})
18573        { # old: foo|foo@v
18574          # new: foo@@v
18575            return 1;
18576        }
18577    }
18578    return 0;
18579}
18580
18581sub readSymbols_App($)
18582{
18583    my $Path = $_[0];
18584    return () if(not $Path);
18585    my @Imported = ();
18586    if($OSgroup eq "macos")
18587    {
18588        my $NM = get_CmdPath("nm");
18589        if(not $NM) {
18590            exitStatus("Not_Found", "can't find \"nm\"");
18591        }
18592        open(APP, "$NM -g \"$Path\" 2>\"$TMP_DIR/null\" |");
18593        while(<APP>)
18594        {
18595            if(/ U _([\w\$]+)\s*\Z/) {
18596                push(@Imported, $1);
18597            }
18598        }
18599        close(APP);
18600    }
18601    elsif($OSgroup eq "windows")
18602    {
18603        my $DumpBinCmd = get_CmdPath("dumpbin");
18604        if(not $DumpBinCmd) {
18605            exitStatus("Not_Found", "can't find \"dumpbin.exe\"");
18606        }
18607        open(APP, "$DumpBinCmd /IMPORTS \"$Path\" 2>\"$TMP_DIR/null\" |");
18608        while(<APP>)
18609        {
18610            if(/\s*\w+\s+\w+\s+\w+\s+([\w\?\@]+)\s*/) {
18611                push(@Imported, $1);
18612            }
18613        }
18614        close(APP);
18615    }
18616    else
18617    {
18618        my $ReadelfCmd = get_CmdPath("readelf");
18619        if(not $ReadelfCmd) {
18620            exitStatus("Not_Found", "can't find \"readelf\"");
18621        }
18622        open(APP, "$ReadelfCmd -Ws \"$Path\" 2>\"$TMP_DIR/null\" |");
18623        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
18624        while(<APP>)
18625        {
18626            if(defined $symtab)
18627            { # do nothing with symtab
18628                if(index($_, "'.dynsym'")!=-1)
18629                { # dynamic table
18630                    $symtab = undef;
18631                }
18632            }
18633            elsif(index($_, "'.symtab'")!=-1)
18634            { # symbol table
18635                $symtab = 1;
18636            }
18637            elsif(my @Info = readline_ELF($_))
18638            {
18639                my ($Ndx, $Symbol) = ($Info[5], $Info[6]);
18640                if($Ndx eq "UND")
18641                { # only imported symbols
18642                    push(@Imported, $Symbol);
18643                }
18644            }
18645        }
18646        close(APP);
18647    }
18648    return @Imported;
18649}
18650
18651my %ELF_BIND = map {$_=>1} (
18652    "WEAK",
18653    "GLOBAL"
18654);
18655
18656my %ELF_TYPE = map {$_=>1} (
18657    "FUNC",
18658    "IFUNC",
18659    "OBJECT",
18660    "COMMON"
18661);
18662
18663my %ELF_VIS = map {$_=>1} (
18664    "DEFAULT",
18665    "PROTECTED"
18666);
18667
18668sub readline_ELF($)
18669{ # read the line of 'readelf' output corresponding to the symbol
18670    my @Info = split(/\s+/, $_[0]);
18671    #  Num:   Value      Size Type   Bind   Vis       Ndx  Name
18672    #  3629:  000b09c0   32   FUNC   GLOBAL DEFAULT   13   _ZNSt12__basic_fileIcED1Ev@@GLIBCXX_3.4
18673    #  135:   00000000    0   FUNC   GLOBAL DEFAULT   UND  av_image_fill_pointers@LIBAVUTIL_52 (3)
18674    shift(@Info); # spaces
18675    shift(@Info); # num
18676
18677    if($#Info==7)
18678    { # UND SYMBOL (N)
18679        if($Info[7]=~/\(\d+\)/) {
18680            pop(@Info);
18681        }
18682    }
18683
18684    if($#Info!=6)
18685    { # other lines
18686        return ();
18687    }
18688    return () if(not defined $ELF_TYPE{$Info[2]} and $Info[5] ne "UND");
18689    return () if(not defined $ELF_BIND{$Info[3]});
18690    return () if(not defined $ELF_VIS{$Info[4]});
18691    if($Info[5] eq "ABS" and $Info[0]=~/\A0+\Z/)
18692    { # 1272: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS CXXABI_1.3
18693        return ();
18694    }
18695    if($OStarget eq "symbian")
18696    { # _ZN12CCTTokenType4NewLE4TUid3RFs@@ctfinder{000a0000}[102020e5].dll
18697        if(index($Info[6], "_._.absent_export_")!=-1)
18698        { # "_._.absent_export_111"@@libstdcpp{00010001}[10282872].dll
18699            return ();
18700        }
18701        $Info[6]=~s/\@.+//g; # remove version
18702    }
18703    if(index($Info[2], "0x") == 0)
18704    { # size == 0x3d158
18705        $Info[2] = hex($Info[2]);
18706    }
18707    return @Info;
18708}
18709
18710sub get_LibPath($$)
18711{
18712    my ($LibVersion, $Name) = @_;
18713    return "" if(not $LibVersion or not $Name);
18714    if(defined $Cache{"get_LibPath"}{$LibVersion}{$Name}) {
18715        return $Cache{"get_LibPath"}{$LibVersion}{$Name};
18716    }
18717    return ($Cache{"get_LibPath"}{$LibVersion}{$Name} = get_LibPath_I($LibVersion, $Name));
18718}
18719
18720sub get_LibPath_I($$)
18721{
18722    my ($LibVersion, $Name) = @_;
18723    if(is_abs($Name))
18724    {
18725        if(-f $Name)
18726        { # absolute path
18727            return $Name;
18728        }
18729        else
18730        { # broken
18731            return "";
18732        }
18733    }
18734    if(defined $RegisteredObjects{$LibVersion}{$Name})
18735    { # registered paths
18736        return $RegisteredObjects{$LibVersion}{$Name};
18737    }
18738    if(defined $RegisteredSONAMEs{$LibVersion}{$Name})
18739    { # registered paths
18740        return $RegisteredSONAMEs{$LibVersion}{$Name};
18741    }
18742    if(my $DefaultPath = $DyLib_DefaultPath{$Name})
18743    { # ldconfig default paths
18744        return $DefaultPath;
18745    }
18746    foreach my $Dir (@DefaultLibPaths, @{$SystemPaths{"lib"}})
18747    { # search in default linker directories
18748      # and then in all system paths
18749        if(-f $Dir."/".$Name) {
18750            return join_P($Dir,$Name);
18751        }
18752    }
18753    if(not defined $Cache{"checkSystemFiles"}) {
18754        checkSystemFiles();
18755    }
18756    if(my @AllObjects = keys(%{$SystemObjects{$Name}})) {
18757        return $AllObjects[0];
18758    }
18759    if(my $ShortName = parse_libname($Name, "name+ext", $OStarget))
18760    {
18761        if($ShortName ne $Name)
18762        { # FIXME: check this case
18763            if(my $Path = get_LibPath($LibVersion, $ShortName)) {
18764                return $Path;
18765            }
18766        }
18767    }
18768    # can't find
18769    return "";
18770}
18771
18772sub readSymbols_Lib($$$$$$)
18773{
18774    my ($LibVersion, $Lib_Path, $IsNeededLib, $Weak, $Deps, $Vers) = @_;
18775    return () if(not $LibVersion or not $Lib_Path);
18776
18777    my $Real_Path = realpath($Lib_Path);
18778
18779    if(not $Real_Path)
18780    { # broken link
18781        return ();
18782    }
18783
18784    my $Lib_Name = get_filename($Real_Path);
18785
18786    if($ExtraInfo)
18787    {
18788        $KnownLibs{$Real_Path} = 1;
18789        $KnownLibs{$Lib_Path} = 1; # links
18790    }
18791
18792    if($IsNeededLib)
18793    {
18794        if($CheckedDyLib{$LibVersion}{$Lib_Name}) {
18795            return ();
18796        }
18797    }
18798    return () if(isCyclical(\@RecurLib, $Lib_Name) or $#RecurLib>=1);
18799    $CheckedDyLib{$LibVersion}{$Lib_Name} = 1;
18800
18801    push(@RecurLib, $Lib_Name);
18802    my (%Value_Interface, %Interface_Value, %NeededLib) = ();
18803    my $Lib_ShortName = parse_libname($Lib_Name, "name+ext", $OStarget);
18804
18805    if(not $IsNeededLib)
18806    { # special cases: libstdc++ and libc
18807        if(my $ShortName = parse_libname($Lib_Name, "short", $OStarget))
18808        {
18809            if($ShortName eq "libstdc++")
18810            { # libstdc++.so.6
18811                $STDCXX_TESTING = 1;
18812            }
18813            elsif($ShortName eq "libc")
18814            { # libc-2.11.3.so
18815                $GLIBC_TESTING = 1;
18816            }
18817        }
18818    }
18819    my $DebugPath = "";
18820    if($Debug and not $DumpSystem)
18821    { # debug mode
18822        $DebugPath = $DEBUG_PATH{$LibVersion}."/libs/".get_filename($Lib_Path).".txt";
18823        mkpath(get_dirname($DebugPath));
18824    }
18825    if($OStarget eq "macos")
18826    { # Mac OS X: *.dylib, *.a
18827        my $NM = get_CmdPath("nm");
18828        if(not $NM) {
18829            exitStatus("Not_Found", "can't find \"nm\"");
18830        }
18831        $NM .= " -g \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
18832        if($DebugPath)
18833        { # debug mode
18834          # write to file
18835            system($NM." >\"$DebugPath\"");
18836            open(LIB, $DebugPath);
18837        }
18838        else
18839        { # write to pipe
18840            open(LIB, $NM." |");
18841        }
18842        while(<LIB>)
18843        {
18844            if($CheckUndefined)
18845            {
18846                if(not $IsNeededLib)
18847                {
18848                    if(/ U _([\w\$]+)\s*\Z/)
18849                    {
18850                        $UndefinedSymbols{$LibVersion}{$Lib_Name}{$1} = 0;
18851                        next;
18852                    }
18853                }
18854            }
18855
18856            if(/ [STD] _([\w\$]+)\s*\Z/)
18857            {
18858                my $Symbol = $1;
18859                if($IsNeededLib)
18860                {
18861                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
18862                    {
18863                        $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
18864                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1;
18865                    }
18866                }
18867                else
18868                {
18869                    $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
18870                    $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = 1;
18871                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
18872                    {
18873                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
18874                            setLanguage($LibVersion, "C++");
18875                        }
18876                    }
18877                    if($CheckObjectsOnly
18878                    and $LibVersion==1) {
18879                        $CheckedSymbols{"Binary"}{$Symbol} = 1;
18880                    }
18881                }
18882            }
18883        }
18884        close(LIB);
18885
18886        if($Deps)
18887        {
18888            if($LIB_TYPE eq "dynamic")
18889            { # dependencies
18890
18891                my $OtoolCmd = get_CmdPath("otool");
18892                if(not $OtoolCmd) {
18893                    exitStatus("Not_Found", "can't find \"otool\"");
18894                }
18895
18896                open(LIB, "$OtoolCmd -L \"$Lib_Path\" 2>\"$TMP_DIR/null\" |");
18897                while(<LIB>)
18898                {
18899                    if(/\s*([\/\\].+\.$LIB_EXT)\s*/
18900                    and $1 ne $Lib_Path) {
18901                        $NeededLib{$1} = 1;
18902                    }
18903                }
18904                close(LIB);
18905            }
18906        }
18907    }
18908    elsif($OStarget eq "windows")
18909    { # Windows *.dll, *.lib
18910        my $DumpBinCmd = get_CmdPath("dumpbin");
18911        if(not $DumpBinCmd) {
18912            exitStatus("Not_Found", "can't find \"dumpbin\"");
18913        }
18914        $DumpBinCmd .= " /EXPORTS \"".$Lib_Path."\" 2>$TMP_DIR/null";
18915        if($DebugPath)
18916        { # debug mode
18917          # write to file
18918            system($DumpBinCmd." >\"$DebugPath\"");
18919            open(LIB, $DebugPath);
18920        }
18921        else
18922        { # write to pipe
18923            open(LIB, $DumpBinCmd." |");
18924        }
18925        while(<LIB>)
18926        { # 1197 4AC 0000A620 SetThreadStackGuarantee
18927          # 1198 4AD          SetThreadToken (forwarded to ...)
18928          # 3368 _o2i_ECPublicKey
18929          # 1 0 00005B30 ??0?N = ... (with pdb)
18930            if(/\A\s*\d+\s+[a-f\d]+\s+[a-f\d]+\s+([\w\?\@]+)\s*(?:=.+)?\Z/i
18931            or /\A\s*\d+\s+[a-f\d]+\s+([\w\?\@]+)\s*\(\s*forwarded\s+/
18932            or /\A\s*\d+\s+_([\w\?\@]+)\s*(?:=.+)?\Z/)
18933            { # dynamic, static and forwarded symbols
18934                my $realname = $1;
18935                if($IsNeededLib)
18936                {
18937                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
18938                    {
18939                        $DepSymbol_Library{$LibVersion}{$realname} = $Lib_Name;
18940                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
18941                    }
18942                }
18943                else
18944                {
18945                    $Symbol_Library{$LibVersion}{$realname} = $Lib_Name;
18946                    $Library_Symbol{$LibVersion}{$Lib_Name}{$realname} = 1;
18947                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
18948                    {
18949                        if(index($realname, "_Z")==0 or index($realname, "?")==0) {
18950                            setLanguage($LibVersion, "C++");
18951                        }
18952                    }
18953                    if($CheckObjectsOnly
18954                    and $LibVersion==1) {
18955                        $CheckedSymbols{"Binary"}{$realname} = 1;
18956                    }
18957                }
18958            }
18959        }
18960        close(LIB);
18961
18962        if($Deps)
18963        {
18964            if($LIB_TYPE eq "dynamic")
18965            { # dependencies
18966                open(LIB, "$DumpBinCmd /DEPENDENTS \"$Lib_Path\" 2>\"$TMP_DIR/null\" |");
18967                while(<LIB>)
18968                {
18969                    if(/\s*([^\s]+?\.$LIB_EXT)\s*/i
18970                    and $1 ne $Lib_Path) {
18971                        $NeededLib{path_format($1, $OSgroup)} = 1;
18972                    }
18973                }
18974                close(LIB);
18975            }
18976        }
18977    }
18978    else
18979    { # Unix; *.so, *.a
18980      # Symbian: *.dso, *.lib
18981        my $ReadelfCmd = get_CmdPath("readelf");
18982        if(not $ReadelfCmd) {
18983            exitStatus("Not_Found", "can't find \"readelf\"");
18984        }
18985        my $Cmd = $ReadelfCmd." -Ws \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
18986        if($DebugPath)
18987        { # debug mode
18988          # write to file
18989            system($Cmd." >\"$DebugPath\"");
18990            open(LIB, $DebugPath);
18991        }
18992        else
18993        { # write to pipe
18994            open(LIB, $Cmd." |");
18995        }
18996        my $symtab = undef; # indicates that we are processing 'symtab' section of 'readelf' output
18997        while(<LIB>)
18998        {
18999            if($LIB_TYPE eq "dynamic")
19000            { # dynamic library specifics
19001                if(defined $symtab)
19002                {
19003                    if(index($_, "'.dynsym'")!=-1)
19004                    { # dynamic table
19005                        $symtab = undef;
19006                    }
19007                    # do nothing with symtab
19008                    next;
19009                }
19010                elsif(index($_, "'.symtab'")!=-1)
19011                { # symbol table
19012                    $symtab = 1;
19013                    next;
19014                }
19015            }
19016            if(my ($Value, $Size, $Type, $Bind, $Vis, $Ndx, $Symbol) = readline_ELF($_))
19017            { # read ELF entry
19018                if($Ndx eq "UND")
19019                { # ignore interfaces that are imported from somewhere else
19020                    if($CheckUndefined)
19021                    {
19022                        if(not $IsNeededLib) {
19023                            $UndefinedSymbols{$LibVersion}{$Lib_Name}{$Symbol} = 0;
19024                        }
19025                    }
19026                    next;
19027                }
19028                if($Bind eq "WEAK")
19029                {
19030                    $WeakSymbols{$LibVersion}{$Symbol} = 1;
19031                    if($Weak eq "-Weak")
19032                    { # skip WEAK symbols
19033                        next;
19034                    }
19035                }
19036                my $Short = $Symbol;
19037                $Short=~s/\@.+//g;
19038                if($Type eq "OBJECT")
19039                { # global data
19040                    $GlobalDataObject{$LibVersion}{$Symbol} = $Size;
19041                    $GlobalDataObject{$LibVersion}{$Short} = $Size;
19042                }
19043                if($IsNeededLib)
19044                {
19045                    if(not defined $RegisteredObjects_Short{$LibVersion}{$Lib_ShortName})
19046                    {
19047                        $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19048                        $DepLibrary_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
19049                    }
19050                }
19051                else
19052                {
19053                    $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
19054                    $Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol} = ($Type eq "OBJECT")?-$Size:1;
19055                    if($Vers)
19056                    {
19057                        if($LIB_EXT eq "so")
19058                        { # value
19059                            $Interface_Value{$LibVersion}{$Symbol} = $Value;
19060                            $Value_Interface{$LibVersion}{$Value}{$Symbol} = 1;
19061                        }
19062                    }
19063                    if($COMMON_LANGUAGE{$LibVersion} ne "C++")
19064                    {
19065                        if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
19066                            setLanguage($LibVersion, "C++");
19067                        }
19068                    }
19069                    if($CheckObjectsOnly
19070                    and $LibVersion==1) {
19071                        $CheckedSymbols{"Binary"}{$Symbol} = 1;
19072                    }
19073                }
19074            }
19075        }
19076        close(LIB);
19077
19078        if($Deps and $LIB_TYPE eq "dynamic")
19079        { # dynamic library specifics
19080            $Cmd = $ReadelfCmd." -Wd \"$Lib_Path\" 2>\"$TMP_DIR/null\"";
19081            open(LIB, $Cmd." |");
19082
19083            while(<LIB>)
19084            {
19085                if(/NEEDED.+\[([^\[\]]+)\]/)
19086                { # dependencies:
19087                  # 0x00000001 (NEEDED) Shared library: [libc.so.6]
19088                    $NeededLib{$1} = 1;
19089                }
19090            }
19091
19092            close(LIB);
19093        }
19094    }
19095    if($Vers)
19096    {
19097        if(not $IsNeededLib and $LIB_EXT eq "so")
19098        { # get symbol versions
19099            my %Found = ();
19100
19101            # by value
19102            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19103            {
19104                next if(index($Symbol,"\@")==-1);
19105                if(my $Value = $Interface_Value{$LibVersion}{$Symbol})
19106                {
19107                    foreach my $Symbol_SameValue (keys(%{$Value_Interface{$LibVersion}{$Value}}))
19108                    {
19109                        if($Symbol_SameValue ne $Symbol
19110                        and index($Symbol_SameValue,"\@")==-1)
19111                        {
19112                            $SymVer{$LibVersion}{$Symbol_SameValue} = $Symbol;
19113                            $Found{$Symbol} = 1;
19114                            last;
19115                        }
19116                    }
19117                }
19118            }
19119
19120            # default
19121            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19122            {
19123                next if(defined $Found{$Symbol});
19124                next if(index($Symbol,"\@\@")==-1);
19125
19126                if($Symbol=~/\A([^\@]*)\@\@/
19127                and not $SymVer{$LibVersion}{$1})
19128                {
19129                    $SymVer{$LibVersion}{$1} = $Symbol;
19130                    $Found{$Symbol} = 1;
19131                }
19132            }
19133
19134            # non-default
19135            foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
19136            {
19137                next if(defined $Found{$Symbol});
19138                next if(index($Symbol,"\@")==-1);
19139
19140                if($Symbol=~/\A([^\@]*)\@([^\@]*)/
19141                and not $SymVer{$LibVersion}{$1})
19142                {
19143                    $SymVer{$LibVersion}{$1} = $Symbol;
19144                    $Found{$Symbol} = 1;
19145                }
19146            }
19147        }
19148    }
19149    if($Deps)
19150    {
19151        foreach my $DyLib (sort keys(%NeededLib))
19152        {
19153            $Library_Needed{$LibVersion}{$Lib_Name}{get_filename($DyLib)} = 1;
19154
19155            if(my $DepPath = get_LibPath($LibVersion, $DyLib))
19156            {
19157                if(not $CheckedDyLib{$LibVersion}{get_filename($DepPath)}) {
19158                    readSymbols_Lib($LibVersion, $DepPath, 1, "+Weak", $Deps, $Vers);
19159                }
19160            }
19161        }
19162    }
19163    pop(@RecurLib);
19164    return $Library_Symbol{$LibVersion};
19165}
19166
19167sub get_prefixes($)
19168{
19169    my %Prefixes = ();
19170    get_prefixes_I([$_[0]], \%Prefixes);
19171    return keys(%Prefixes);
19172}
19173
19174sub get_prefixes_I($$)
19175{
19176    foreach my $P (@{$_[0]})
19177    {
19178        my @Parts = reverse(split(/[\/\\]+/, $P));
19179        my $Name = $Parts[0];
19180        foreach (1 .. $#Parts)
19181        {
19182            $_[1]->{$Name}{$P} = 1;
19183            last if($_>4 or $Parts[$_] eq "include");
19184            $Name = $Parts[$_].$SLASH.$Name;
19185        }
19186    }
19187}
19188
19189sub checkSystemFiles()
19190{
19191    $Cache{"checkSystemFiles"} = 1;
19192
19193    my @SysHeaders = ();
19194
19195    foreach my $DevelPath (@{$SystemPaths{"lib"}})
19196    {
19197        next if(not -d $DevelPath);
19198
19199        my @Files = cmd_find($DevelPath,"f");
19200        foreach my $Link (cmd_find($DevelPath,"l"))
19201        { # add symbolic links
19202            if(-f $Link) {
19203                push(@Files, $Link);
19204            }
19205        }
19206
19207        if(not $CheckObjectsOnly)
19208        {
19209            # search for headers in /usr/lib
19210            my @Headers = grep { /\.h(pp|xx)?\Z|\/include\// } @Files;
19211            @Headers = grep { not /\/(gcc|jvm|syslinux|kbd|parrot|xemacs|perl|llvm)/ } @Headers;
19212            push(@SysHeaders, @Headers);
19213        }
19214
19215        # search for libraries in /usr/lib (including symbolic links)
19216        my @Libs = grep { /\.$LIB_EXT[0-9.]*\Z/ } @Files;
19217        foreach my $Path (@Libs)
19218        {
19219            my $N = get_filename($Path);
19220            $SystemObjects{$N}{$Path} = 1;
19221            $SystemObjects{parse_libname($N, "name+ext", $OStarget)}{$Path} = 1;
19222        }
19223    }
19224
19225    if(not $CheckObjectsOnly)
19226    {
19227        foreach my $DevelPath (@{$SystemPaths{"include"}})
19228        {
19229            next if(not -d $DevelPath);
19230            # search for all header files in the /usr/include
19231            # with or without extension (ncurses.h, QtCore, ...)
19232            push(@SysHeaders, cmd_find($DevelPath,"f"));
19233            foreach my $Link (cmd_find($DevelPath,"l"))
19234            { # add symbolic links
19235                if(-f $Link) {
19236                    push(@SysHeaders, $Link);
19237                }
19238            }
19239        }
19240        get_prefixes_I(\@SysHeaders, \%SystemHeaders);
19241    }
19242}
19243
19244sub getSOPaths($)
19245{
19246    my $LibVersion = $_[0];
19247    my @Paths = ();
19248    foreach my $Dest (split(/\s*\n\s*/, $Descriptor{$LibVersion}{"Libs"}))
19249    {
19250        if(not -e $Dest) {
19251            exitStatus("Access_Error", "can't access \'$Dest\'");
19252        }
19253        $Dest = get_abs_path($Dest);
19254        my @SoPaths_Dest = getSOPaths_Dest($Dest, $LibVersion);
19255        foreach (@SoPaths_Dest) {
19256            push(@Paths, $_);
19257        }
19258    }
19259    return sort @Paths;
19260}
19261
19262sub skipLib($$)
19263{
19264    my ($Path, $LibVersion) = @_;
19265    return 1 if(not $Path or not $LibVersion);
19266    my $Name = get_filename($Path);
19267    if($SkipLibs{$LibVersion}{"Name"}{$Name}) {
19268        return 1;
19269    }
19270    my $ShortName = parse_libname($Name, "name+ext", $OStarget);
19271    if($SkipLibs{$LibVersion}{"Name"}{$ShortName}) {
19272        return 1;
19273    }
19274    foreach my $Dir (keys(%{$SkipLibs{$LibVersion}{"Path"}}))
19275    {
19276        if($Path=~/\Q$Dir\E([\/\\]|\Z)/) {
19277            return 1;
19278        }
19279    }
19280    foreach my $P (keys(%{$SkipLibs{$LibVersion}{"Pattern"}}))
19281    {
19282        if($Name=~/$P/) {
19283            return 1;
19284        }
19285        if($P=~/[\/\\]/ and $Path=~/$P/) {
19286            return 1;
19287        }
19288    }
19289    return 0;
19290}
19291
19292sub specificHeader($$)
19293{
19294    my ($Header, $Spec) = @_;
19295    my $Name = get_filename($Header);
19296
19297    if($Spec eq "windows")
19298    {# MS Windows
19299        return 1 if($Name=~/(\A|[._-])(win|wince|wnt)(\d\d|[._-]|\Z)/i);
19300        return 1 if($Name=~/([._-]w|win)(32|64)/i);
19301        return 1 if($Name=~/\A(Win|Windows)[A-Z]/);
19302        return 1 if($Name=~/\A(w|win|windows)(32|64|\.)/i);
19303        my @Dirs = (
19304            "win32",
19305            "win64",
19306            "win",
19307            "windows",
19308            "msvcrt"
19309        ); # /gsf-win32/
19310        if(my $DIRs = join("|", @Dirs)) {
19311            return 1 if($Header=~/[\/\\](|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i);
19312        }
19313    }
19314    elsif($Spec eq "macos")
19315    { # Mac OS
19316        return 1 if($Name=~/(\A|[_-])mac[._-]/i);
19317    }
19318
19319    return 0;
19320}
19321
19322sub skipAlienHeader($)
19323{
19324    my $Path = $_[0];
19325    my $Name = get_filename($Path);
19326    my $Dir = get_dirname($Path);
19327
19328    if($Tolerance=~/2/)
19329    { # 2 - skip internal headers
19330        my @Terms = (
19331            "p",
19332            "priv",
19333            "int",
19334            "impl",
19335            "implementation",
19336            "internal",
19337            "private",
19338            "old",
19339            "compat",
19340            "debug",
19341            "test",
19342            "gen"
19343        );
19344
19345        my @Dirs = (
19346            "private",
19347            "priv",
19348            "port",
19349            "impl",
19350            "internal",
19351            "detail",
19352            "details",
19353            "old",
19354            "compat",
19355            "debug",
19356            "config",
19357            "compiler",
19358            "platform",
19359            "test"
19360        );
19361
19362        if(my $TERMs = join("|", @Terms)) {
19363            return 1 if($Name=~/(\A|[._-])($TERMs)([._-]|\Z)/i);
19364        }
19365        if(my $DIRs = join("|", @Dirs)) {
19366            return 1 if($Dir=~/(\A|[\/\\])(|[^\/\\]+[._-])($DIRs)(|[._-][^\/\\]+)([\/\\]|\Z)/i);
19367        }
19368
19369        return 1 if($Name=~/[a-z](Imp|Impl|I|P)(\.|\Z)/);
19370    }
19371
19372    if($Tolerance=~/1/)
19373    { # 1 - skip non-Linux headers
19374        if($OSgroup ne "windows")
19375        {
19376            if(specificHeader($Path, "windows")) {
19377                return 1;
19378            }
19379        }
19380        if($OSgroup ne "macos")
19381        {
19382            if(specificHeader($Path, "macos")) {
19383                return 1;
19384            }
19385        }
19386    }
19387
19388    # valid
19389    return 0;
19390}
19391
19392sub skipHeader($$)
19393{
19394    my ($Path, $LibVersion) = @_;
19395    return 1 if(not $Path or not $LibVersion);
19396    if(defined $Cache{"skipHeader"}{$Path}) {
19397        return $Cache{"skipHeader"}{$Path};
19398    }
19399    if(defined $Tolerance and $Tolerance=~/1|2/)
19400    { # --tolerant
19401        if(skipAlienHeader($Path)) {
19402            return ($Cache{"skipHeader"}{$Path} = 1);
19403        }
19404    }
19405    if(not keys(%{$SkipHeaders{$LibVersion}})) {
19406        return 0;
19407    }
19408    return ($Cache{"skipHeader"}{$Path} = skipHeader_I(@_));
19409}
19410
19411sub skipHeader_I($$)
19412{ # returns:
19413  #  1 - if header should NOT be included and checked
19414  #  2 - if header should NOT be included, but should be checked
19415    my ($Path, $LibVersion) = @_;
19416    my $Name = get_filename($Path);
19417    if(my $Kind = $SkipHeaders{$LibVersion}{"Name"}{$Name}) {
19418        return $Kind;
19419    }
19420    foreach my $D (sort {$SkipHeaders{$LibVersion}{"Path"}{$a} cmp $SkipHeaders{$LibVersion}{"Path"}{$b}}
19421    keys(%{$SkipHeaders{$LibVersion}{"Path"}}))
19422    {
19423        if(index($Path, $D)!=-1)
19424        {
19425            if($Path=~/\Q$D\E([\/\\]|\Z)/) {
19426                return $SkipHeaders{$LibVersion}{"Path"}{$D};
19427            }
19428        }
19429    }
19430    foreach my $P (sort {$SkipHeaders{$LibVersion}{"Pattern"}{$a} cmp $SkipHeaders{$LibVersion}{"Pattern"}{$b}}
19431    keys(%{$SkipHeaders{$LibVersion}{"Pattern"}}))
19432    {
19433        if(my $Kind = $SkipHeaders{$LibVersion}{"Pattern"}{$P})
19434        {
19435            if($Name=~/$P/) {
19436                return $Kind;
19437            }
19438            if($P=~/[\/\\]/ and $Path=~/$P/) {
19439                return $Kind;
19440            }
19441        }
19442    }
19443
19444    return 0;
19445}
19446
19447sub registerObject_Dir($$)
19448{
19449    my ($Dir, $LibVersion) = @_;
19450    if(grep {$_ eq $Dir} @{$SystemPaths{"lib"}})
19451    { # system directory
19452        return;
19453    }
19454    if($RegisteredObject_Dirs{$LibVersion}{$Dir})
19455    { # already registered
19456        return;
19457    }
19458    foreach my $Path (find_libs($Dir,"",1))
19459    {
19460        next if(ignore_path($Path));
19461        next if(skipLib($Path, $LibVersion));
19462        registerObject($Path, $LibVersion);
19463    }
19464    $RegisteredObject_Dirs{$LibVersion}{$Dir} = 1;
19465}
19466
19467sub registerObject($$)
19468{
19469    my ($Path, $LibVersion) = @_;
19470
19471    my $Name = get_filename($Path);
19472    $RegisteredObjects{$LibVersion}{$Name} = $Path;
19473    if($OSgroup=~/linux|bsd/i)
19474    {
19475        if(my $SONAME = getSONAME($Path)) {
19476            $RegisteredSONAMEs{$LibVersion}{$SONAME} = $Path;
19477        }
19478    }
19479    if(my $Short = parse_libname($Name, "name+ext", $OStarget)) {
19480        $RegisteredObjects_Short{$LibVersion}{$Short} = $Path;
19481    }
19482
19483    if(not $CheckedArch{$LibVersion} and -f $Path)
19484    {
19485        if(my $ObjArch = getArch_Object($Path))
19486        {
19487            if($ObjArch ne getArch_GCC($LibVersion))
19488            { # translation unit dump generated by the GCC compiler should correspond to the input objects
19489                $CheckedArch{$LibVersion} = 1;
19490                printMsg("WARNING", "the architectures of input objects and the used GCC compiler are not equal, please change the compiler by --gcc-path=PATH option.");
19491            }
19492        }
19493    }
19494}
19495
19496sub getArch_Object($)
19497{
19498    my $Path = $_[0];
19499
19500    my %MachineType = (
19501        "14C" => "x86",
19502        "8664" => "x86_64",
19503        "1C0" => "arm",
19504        "200" => "ia64"
19505    );
19506
19507    my %ArchName = (
19508        "s390:31-bit" => "s390",
19509        "s390:64-bit" => "s390x",
19510        "powerpc:common" => "ppc32",
19511        "powerpc:common64" => "ppc64",
19512        "i386:x86-64" => "x86_64",
19513        "mips:3000" => "mips",
19514        "sparc:v8plus" => "sparcv9"
19515    );
19516
19517    if($OSgroup eq "windows")
19518    {
19519        my $DumpbinCmd = get_CmdPath("dumpbin");
19520        if(not $DumpbinCmd) {
19521            exitStatus("Not_Found", "can't find \"dumpbin\"");
19522        }
19523
19524        my $Cmd = $DumpbinCmd." /headers \"$Path\"";
19525        my $Out = `$Cmd`;
19526
19527        if($Out=~/(\w+)\smachine/)
19528        {
19529            if(my $Type = $MachineType{uc($1)})
19530            {
19531                return $Type;
19532            }
19533        }
19534    }
19535    elsif($OSgroup=~/linux|bsd/)
19536    {
19537        my $ObjdumpCmd = get_CmdPath("objdump");
19538        if(not $ObjdumpCmd) {
19539            exitStatus("Not_Found", "can't find \"objdump\"");
19540        }
19541
19542        my $Cmd = $ObjdumpCmd." -f \"$Path\"";
19543        my $Out = `$Cmd`;
19544
19545        if($Out=~/architecture:\s+([\w\-\:]+)/)
19546        {
19547            my $Arch = $1;
19548            if($Arch=~s/\:(.+)//)
19549            {
19550                my $Suffix = $1;
19551
19552                if(my $Name = $ArchName{$Arch.":".$Suffix})
19553                {
19554                    $Arch = $Name;
19555                }
19556            }
19557
19558            if($Arch=~/i[3-6]86/) {
19559                $Arch = "x86";
19560            }
19561
19562            if($Arch eq "x86-64") {
19563                $Arch = "x86_64";
19564            }
19565
19566            if($Arch eq "ia64-elf64") {
19567                $Arch = "ia64";
19568            }
19569
19570            return $Arch;
19571        }
19572    }
19573    else
19574    { # macos, etc.
19575        # TODO
19576    }
19577
19578    return undef;
19579}
19580
19581sub getSONAME($)
19582{
19583    my $Path = $_[0];
19584    return if(not $Path);
19585    if(defined $Cache{"getSONAME"}{$Path}) {
19586        return $Cache{"getSONAME"}{$Path};
19587    }
19588    my $ObjdumpCmd = get_CmdPath("objdump");
19589    if(not $ObjdumpCmd) {
19590        exitStatus("Not_Found", "can't find \"objdump\"");
19591    }
19592    my $SonameCmd = "$ObjdumpCmd -x \"$Path\" 2>$TMP_DIR/null";
19593    if($OSgroup eq "windows") {
19594        $SonameCmd .= " | find \"SONAME\"";
19595    }
19596    else {
19597        $SonameCmd .= " | grep SONAME";
19598    }
19599    if(my $SonameInfo = `$SonameCmd`)
19600    {
19601        if($SonameInfo=~/SONAME\s+([^\s]+)/) {
19602            return ($Cache{"getSONAME"}{$Path} = $1);
19603        }
19604    }
19605    return ($Cache{"getSONAME"}{$Path}="");
19606}
19607
19608sub getSOPaths_Dest($$)
19609{
19610    my ($Dest, $LibVersion) = @_;
19611    if(skipLib($Dest, $LibVersion)) {
19612        return ();
19613    }
19614    if(-f $Dest)
19615    {
19616        if(not parse_libname($Dest, "name", $OStarget)) {
19617            exitStatus("Error", "incorrect format of library (should be *.$LIB_EXT): \'$Dest\'");
19618        }
19619        registerObject($Dest, $LibVersion);
19620        registerObject_Dir(get_dirname($Dest), $LibVersion);
19621        return ($Dest);
19622    }
19623    elsif(-d $Dest)
19624    {
19625        $Dest=~s/[\/\\]+\Z//g;
19626        my %Libs = ();
19627        if(grep { $Dest eq $_ } @{$SystemPaths{"lib"}})
19628        { # you have specified /usr/lib as the search directory (<libs>) in the XML descriptor
19629          # and the real name of the library by -l option (bz2, stdc++, Xaw, ...)
19630            foreach my $Path (cmd_find($Dest,"","*".esc($TargetLibraryName)."*.$LIB_EXT*",2))
19631            { # all files and symlinks that match the name of a library
19632                if(get_filename($Path)=~/\A(|lib)\Q$TargetLibraryName\E[\d\-]*\.$LIB_EXT[\d\.]*\Z/i)
19633                {
19634                    registerObject($Path, $LibVersion);
19635                    $Libs{realpath($Path)}=1;
19636                }
19637            }
19638        }
19639        else
19640        { # search for all files and symlinks
19641            foreach my $Path (find_libs($Dest,"",""))
19642            {
19643                next if(ignore_path($Path));
19644                next if(skipLib($Path, $LibVersion));
19645                registerObject($Path, $LibVersion);
19646                $Libs{realpath($Path)}=1;
19647            }
19648            if($OSgroup eq "macos")
19649            { # shared libraries on MacOS X may have no extension
19650                foreach my $Path (cmd_find($Dest,"f"))
19651                {
19652                    next if(ignore_path($Path));
19653                    next if(skipLib($Path, $LibVersion));
19654                    if(get_filename($Path)!~/\./
19655                    and cmd_file($Path)=~/(shared|dynamic)\s+library/i)
19656                    {
19657                        registerObject($Path, $LibVersion);
19658                        $Libs{realpath($Path)}=1;
19659                    }
19660                }
19661            }
19662        }
19663        return keys(%Libs);
19664    }
19665    else {
19666        return ();
19667    }
19668}
19669
19670sub isCyclical($$)
19671{
19672    my ($Stack, $Value) = @_;
19673    return (grep {$_ eq $Value} @{$Stack});
19674}
19675
19676sub getGCC_Opts($)
19677{ # to use in module
19678    my $LibVersion = $_[0];
19679
19680    my @Opts = ();
19681
19682    if($CompilerOptions{$LibVersion})
19683    { # user-defined options
19684        push(@Opts, $CompilerOptions{$LibVersion});
19685    }
19686    if($GccOptions)
19687    { # additional
19688        push(@Opts, $GccOptions);
19689    }
19690
19691    if(@Opts) {
19692        return join(" ", @Opts);
19693    }
19694
19695    return undef;
19696}
19697
19698sub getArch_GCC($)
19699{
19700    my $LibVersion = $_[0];
19701
19702    if(defined $Cache{"getArch_GCC"}{$LibVersion}) {
19703        return $Cache{"getArch_GCC"}{$LibVersion};
19704    }
19705
19706    my $Arch = undef;
19707
19708    if($GCC_PATH)
19709    {
19710        writeFile("$TMP_DIR/test.c", "int main(){return 0;}\n");
19711
19712        my $Cmd = $GCC_PATH." test.c -o test";
19713        if(my $Opts = getGCC_Opts($LibVersion))
19714        { # user-defined options
19715            $Cmd .= " ".$Opts;
19716        }
19717
19718        chdir($TMP_DIR);
19719        system($Cmd);
19720        chdir($ORIG_DIR);
19721
19722        $Arch = getArch_Object("$TMP_DIR/test");
19723
19724        unlink("$TMP_DIR/test.c");
19725        unlink("$TMP_DIR/test");
19726    }
19727
19728    if(not $Arch) {
19729        exitStatus("Error", "can't check ARCH type");
19730    }
19731
19732    return ($Cache{"getArch_GCC"}{$LibVersion} = $Arch);
19733}
19734
19735sub detectWordSize($)
19736{
19737    my $LibVersion = $_[0];
19738
19739    my $Size = undef;
19740
19741    # speed up detection
19742    if(my $Arch = getArch($LibVersion))
19743    {
19744        if($Arch=~/\A(x86_64|s390x|ppc64|ia64|alpha)\Z/) {
19745            $Size = "8";
19746        }
19747        elsif($Arch=~/\A(x86|s390|ppc32)\Z/) {
19748            $Size = "4";
19749        }
19750    }
19751
19752    if($GCC_PATH)
19753    {
19754        writeFile("$TMP_DIR/empty.h", "");
19755
19756        my $Cmd = $GCC_PATH." -E -dD empty.h";
19757        if(my $Opts = getGCC_Opts($LibVersion))
19758        { # user-defined options
19759            $Cmd .= " ".$Opts;
19760        }
19761
19762        chdir($TMP_DIR);
19763        my $Defines = `$Cmd`;
19764        chdir($ORIG_DIR);
19765
19766        unlink("$TMP_DIR/empty.h");
19767
19768        if($Defines=~/ __SIZEOF_POINTER__\s+(\d+)/)
19769        { # GCC 4
19770            $Size = $1;
19771        }
19772        elsif($Defines=~/ __PTRDIFF_TYPE__\s+(\w+)/)
19773        { # GCC 3
19774            my $PTRDIFF = $1;
19775            if($PTRDIFF=~/long/) {
19776                $Size = "8";
19777            }
19778            else {
19779                $Size = "4";
19780            }
19781        }
19782    }
19783
19784    if(not $Size) {
19785        exitStatus("Error", "can't check WORD size");
19786    }
19787
19788    return $Size;
19789}
19790
19791sub getWordSize($)
19792{ # to use in module
19793    return $WORD_SIZE{$_[0]};
19794}
19795
19796sub majorVersion($)
19797{
19798    my $V = $_[0];
19799    return 0 if(not $V);
19800    my @VParts = split(/\./, $V);
19801    return $VParts[0];
19802}
19803
19804sub cmpVersions($$)
19805{ # compare two versions in dotted-numeric format
19806    my ($V1, $V2) = @_;
19807    return 0 if($V1 eq $V2);
19808    my @V1Parts = split(/\./, $V1);
19809    my @V2Parts = split(/\./, $V2);
19810    for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++)
19811    {
19812        return -1 if(int($V1Parts[$i]) < int($V2Parts[$i]));
19813        return 1 if(int($V1Parts[$i]) > int($V2Parts[$i]));
19814    }
19815    return -1 if($#V1Parts < $#V2Parts);
19816    return 1 if($#V1Parts > $#V2Parts);
19817    return 0;
19818}
19819
19820sub read_ABI_Dump($$)
19821{
19822    my ($LibVersion, $Path) = @_;
19823    return if(not $LibVersion or not -e $Path);
19824    my $FilePath = "";
19825    if(isDump_U($Path))
19826    { # input *.abi
19827        $FilePath = $Path;
19828    }
19829    else
19830    { # input *.abi.tar.gz
19831        $FilePath = unpackDump($Path);
19832        if(not isDump_U($FilePath)) {
19833            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
19834        }
19835    }
19836
19837    my $ABI = {};
19838
19839    my $Line = readLineNum($FilePath, 0);
19840    if($Line=~/xml/)
19841    { # XML format
19842        loadModule("XmlDump");
19843        $ABI = readXmlDump($FilePath);
19844    }
19845    else
19846    { # Perl Data::Dumper format (default)
19847        open(DUMP, $FilePath);
19848        local $/ = undef;
19849        my $Content = <DUMP>;
19850        close(DUMP);
19851
19852        if(get_dirname($FilePath) eq $TMP_DIR."/unpack")
19853        { # remove temp file
19854            unlink($FilePath);
19855        }
19856        if($Content!~/};\s*\Z/) {
19857            exitStatus("Invalid_Dump", "specified ABI dump \'$Path\' is not valid, try to recreate it");
19858        }
19859        $ABI = eval($Content);
19860        if(not $ABI) {
19861            exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
19862        }
19863    }
19864    # new dumps (>=1.22) have a personal versioning
19865    my $DVersion = $ABI->{"ABI_DUMP_VERSION"};
19866    my $ToolVersion = $ABI->{"ABI_COMPLIANCE_CHECKER_VERSION"};
19867    if(not $DVersion)
19868    { # old dumps (<=1.21.6) have been marked by the tool version
19869        $DVersion = $ToolVersion;
19870    }
19871    $UsedDump{$LibVersion}{"V"} = $DVersion;
19872    $UsedDump{$LibVersion}{"M"} = $ABI->{"LibraryName"};
19873
19874    if($ABI->{"ABI_DUMP_VERSION"})
19875    {
19876        if(cmpVersions($DVersion, $ABI_DUMP_VERSION)>0)
19877        { # Don't know how to parse future dump formats
19878            exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $ABI_DUMP_VERSION)");
19879        }
19880    }
19881    else
19882    { # support for old ABI dumps
19883        if(cmpVersions($DVersion, $TOOL_VERSION)>0)
19884        { # Don't know how to parse future dump formats
19885            exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (newer than $TOOL_VERSION)");
19886        }
19887    }
19888
19889    if(majorVersion($DVersion)<2)
19890    {
19891        exitStatus("Dump_Version", "incompatible version \'$DVersion\' of specified ABI dump (allowed only 2.0<=V<=$ABI_DUMP_VERSION)");
19892    }
19893
19894    if(defined $ABI->{"ABI_DUMPER_VERSION"})
19895    { # DWARF ABI Dump
19896        $UseConv_Real{$LibVersion}{"P"} = 1;
19897        $UseConv_Real{$LibVersion}{"R"} = 0; # not implemented yet
19898
19899        $UsedDump{$LibVersion}{"DWARF"} = 1;
19900
19901        $TargetComponent = "module";
19902    }
19903
19904    if(not checkDump($LibVersion, "2.11"))
19905    { # old ABI dumps
19906        $UsedDump{$LibVersion}{"BinOnly"} = 1;
19907    }
19908    elsif($ABI->{"BinOnly"})
19909    { # ABI dump created with --binary option
19910        $UsedDump{$LibVersion}{"BinOnly"} = 1;
19911    }
19912    else
19913    { # default
19914        $UsedDump{$LibVersion}{"SrcBin"} = 1;
19915    }
19916
19917    if(defined $ABI->{"Mode"}
19918    and $ABI->{"Mode"} eq "Extended")
19919    { # --ext option
19920        $ExtendedCheck = 1;
19921    }
19922    if($ABI->{"Extra"}) {
19923        $ExtraDump = 1;
19924    }
19925
19926    if(my $Lang = $ABI->{"Language"})
19927    {
19928        $UsedDump{$LibVersion}{"L"} = $Lang;
19929        setLanguage($LibVersion, $Lang);
19930    }
19931    if(checkDump($LibVersion, "2.15")) {
19932        $TypeInfo{$LibVersion} = $ABI->{"TypeInfo"};
19933    }
19934    else
19935    { # support for old ABI dumps
19936        my $TInfo = $ABI->{"TypeInfo"};
19937        if(not $TInfo)
19938        { # support for older ABI dumps
19939            $TInfo = $ABI->{"TypeDescr"};
19940        }
19941        my %Tid_TDid = ();
19942        foreach my $TDid (keys(%{$TInfo}))
19943        {
19944            foreach my $Tid (keys(%{$TInfo->{$TDid}}))
19945            {
19946                $MAX_ID = $Tid if($Tid>$MAX_ID);
19947                $MAX_ID = $TDid if($TDid and $TDid>$MAX_ID);
19948                $Tid_TDid{$Tid}{$TDid} = 1;
19949            }
19950        }
19951        my %NewID = ();
19952        foreach my $Tid (keys(%Tid_TDid))
19953        {
19954            my @TDids = keys(%{$Tid_TDid{$Tid}});
19955            if($#TDids>=1)
19956            {
19957                foreach my $TDid (@TDids)
19958                {
19959                    if($TDid) {
19960                        %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}};
19961                    }
19962                    else
19963                    {
19964                        my $ID = ++$MAX_ID;
19965
19966                        $NewID{$TDid}{$Tid} = $ID;
19967                        %{$TypeInfo{$LibVersion}{$ID}} = %{$TInfo->{$TDid}{$Tid}};
19968                        $TypeInfo{$LibVersion}{$ID}{"Tid"} = $ID;
19969                    }
19970                }
19971            }
19972            else
19973            {
19974                my $TDid = $TDids[0];
19975                %{$TypeInfo{$LibVersion}{$Tid}} = %{$TInfo->{$TDid}{$Tid}};
19976            }
19977        }
19978        foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
19979        {
19980            my %Info = %{$TypeInfo{$LibVersion}{$Tid}};
19981            if(defined $Info{"BaseType"})
19982            {
19983                my $Bid = $Info{"BaseType"}{"Tid"};
19984                my $BDid = $Info{"BaseType"}{"TDid"};
19985                $BDid="" if(not defined $BDid);
19986                delete($TypeInfo{$LibVersion}{$Tid}{"BaseType"}{"TDid"});
19987                if(defined $NewID{$BDid} and my $ID = $NewID{$BDid}{$Bid}) {
19988                    $TypeInfo{$LibVersion}{$Tid}{"BaseType"} = $ID;
19989                }
19990            }
19991            delete($TypeInfo{$LibVersion}{$Tid}{"TDid"});
19992        }
19993    }
19994    read_Machine_DumpInfo($ABI, $LibVersion);
19995    $SymbolInfo{$LibVersion} = $ABI->{"SymbolInfo"};
19996    if(not $SymbolInfo{$LibVersion})
19997    { # support for old dumps
19998        $SymbolInfo{$LibVersion} = $ABI->{"FuncDescr"};
19999    }
20000    if(not keys(%{$SymbolInfo{$LibVersion}}))
20001    { # validation of old-version dumps
20002        if(not $ExtendedCheck) {
20003            exitStatus("Invalid_Dump", "the input dump d$LibVersion is invalid");
20004        }
20005    }
20006    if(checkDump($LibVersion, "2.15")) {
20007        $DepLibrary_Symbol{$LibVersion} = $ABI->{"DepSymbols"};
20008    }
20009    else
20010    { # support for old ABI dumps
20011        my $DepSymbols = $ABI->{"DepSymbols"};
20012        if(not $DepSymbols) {
20013            $DepSymbols = $ABI->{"DepInterfaces"};
20014        }
20015        if(not $DepSymbols)
20016        { # Cannot reconstruct DepSymbols. This may result in false
20017          # positives if the old dump is for library 2. Not a problem if
20018          # old dumps are only from old libraries.
20019            $DepSymbols = {};
20020        }
20021        foreach my $Symbol (keys(%{$DepSymbols})) {
20022            $DepSymbol_Library{$LibVersion}{$Symbol} = 1;
20023        }
20024    }
20025    $SymVer{$LibVersion} = $ABI->{"SymbolVersion"};
20026
20027    if(my $V = $TargetVersion{$LibVersion}) {
20028        $Descriptor{$LibVersion}{"Version"} = $V;
20029    }
20030    else {
20031        $Descriptor{$LibVersion}{"Version"} = $ABI->{"LibraryVersion"};
20032    }
20033
20034    $SkipTypes{$LibVersion} = $ABI->{"SkipTypes"};
20035    if(not $SkipTypes{$LibVersion})
20036    { # support for old dumps
20037        $SkipTypes{$LibVersion} = $ABI->{"OpaqueTypes"};
20038    }
20039
20040    if(not $SkipSymbols{$LibVersion})
20041    { # if not defined by -skip-symbols option
20042        $SkipSymbols{$LibVersion} = $ABI->{"SkipSymbols"};
20043        if(not $SkipSymbols{$LibVersion})
20044        { # support for old dumps
20045            $SkipSymbols{$LibVersion} = $ABI->{"SkipInterfaces"};
20046        }
20047        if(not $SkipSymbols{$LibVersion})
20048        { # support for old dumps
20049            $SkipSymbols{$LibVersion} = $ABI->{"InternalInterfaces"};
20050        }
20051    }
20052    $SkipNameSpaces{$LibVersion} = $ABI->{"SkipNameSpaces"};
20053
20054    if(not $TargetHeaders{$LibVersion})
20055    { # if not defined by -headers-list option
20056        $TargetHeaders{$LibVersion} = $ABI->{"TargetHeaders"};
20057    }
20058
20059    foreach my $Path (keys(%{$ABI->{"SkipHeaders"}}))
20060    {
20061        $SkipHeadersList{$LibVersion}{$Path} = $ABI->{"SkipHeaders"}{$Path};
20062        my ($CPath, $Type) = classifyPath($Path);
20063        $SkipHeaders{$LibVersion}{$Type}{$CPath} = $ABI->{"SkipHeaders"}{$Path};
20064    }
20065    read_Source_DumpInfo($ABI, $LibVersion);
20066    read_Libs_DumpInfo($ABI, $LibVersion);
20067    if(not checkDump($LibVersion, "2.10.1")
20068    or not $TargetHeaders{$LibVersion})
20069    { # support for old ABI dumps: added target headers
20070        foreach (keys(%{$Registered_Headers{$LibVersion}})) {
20071            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20072        }
20073        foreach (keys(%{$Registered_Sources{$LibVersion}})) {
20074            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20075        }
20076    }
20077    $Constants{$LibVersion} = $ABI->{"Constants"};
20078    if(defined $ABI->{"GccConstants"})
20079    { # 3.0
20080        foreach my $Name (keys(%{$ABI->{"GccConstants"}})) {
20081            $Constants{$LibVersion}{$Name}{"Value"} = $ABI->{"GccConstants"}{$Name};
20082        }
20083    }
20084
20085    $NestedNameSpaces{$LibVersion} = $ABI->{"NameSpaces"};
20086    if(not $NestedNameSpaces{$LibVersion})
20087    { # support for old dumps
20088      # Cannot reconstruct NameSpaces. This may affect design
20089      # of the compatibility report.
20090        $NestedNameSpaces{$LibVersion} = {};
20091    }
20092    # target system type
20093    # needed to adopt HTML report
20094    if(not $DumpSystem)
20095    { # to use in createSymbolsList(...)
20096        $OStarget = $ABI->{"Target"};
20097    }
20098    # recreate environment
20099    foreach my $Lib_Name (keys(%{$Library_Symbol{$LibVersion}}))
20100    {
20101        foreach my $Symbol (keys(%{$Library_Symbol{$LibVersion}{$Lib_Name}}))
20102        {
20103            $Symbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
20104            if($Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol}<=-1)
20105            { # data marked as -size in the dump
20106                $GlobalDataObject{$LibVersion}{$Symbol} = -$Library_Symbol{$LibVersion}{$Lib_Name}{$Symbol};
20107            }
20108            if($COMMON_LANGUAGE{$LibVersion} ne "C++")
20109            {
20110                if(index($Symbol, "_Z")==0 or index($Symbol, "?")==0) {
20111                    setLanguage($LibVersion, "C++");
20112                }
20113            }
20114        }
20115    }
20116    foreach my $Lib_Name (keys(%{$DepLibrary_Symbol{$LibVersion}}))
20117    {
20118        foreach my $Symbol (keys(%{$DepLibrary_Symbol{$LibVersion}{$Lib_Name}})) {
20119            $DepSymbol_Library{$LibVersion}{$Symbol} = $Lib_Name;
20120        }
20121    }
20122
20123    my @VFunc = ();
20124    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20125    {
20126        if(my $MnglName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
20127        {
20128            if(not $Symbol_Library{$LibVersion}{$MnglName}
20129            and not $DepSymbol_Library{$LibVersion}{$MnglName}) {
20130                push(@VFunc, $MnglName);
20131            }
20132        }
20133    }
20134    translateSymbols(@VFunc, $LibVersion);
20135    translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
20136    translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion);
20137
20138    if(not checkDump($LibVersion, "3.0"))
20139    { # support for old ABI dumps
20140        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20141        {
20142            if(my $BaseType = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"})
20143            {
20144                if(ref($BaseType) eq "HASH") {
20145                    $TypeInfo{$LibVersion}{$TypeId}{"BaseType"} = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"}{"Tid"};
20146                }
20147            }
20148        }
20149    }
20150
20151    if(not checkDump($LibVersion, "3.2"))
20152    { # support for old ABI dumps
20153        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20154        {
20155            if(defined $TypeInfo{$LibVersion}{$TypeId}{"VTable"})
20156            {
20157                foreach my $Offset (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"VTable"}})) {
20158                    $TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset} = simplifyVTable($TypeInfo{$LibVersion}{$TypeId}{"VTable"}{$Offset});
20159                }
20160            }
20161        }
20162
20163        # repair target headers list
20164        delete($TargetHeaders{$LibVersion});
20165        foreach (keys(%{$Registered_Headers{$LibVersion}})) {
20166            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20167        }
20168        foreach (keys(%{$Registered_Sources{$LibVersion}})) {
20169            $TargetHeaders{$LibVersion}{get_filename($_)} = 1;
20170        }
20171
20172        # non-target constants from anon enums
20173        foreach my $Name (keys(%{$Constants{$LibVersion}}))
20174        {
20175            if(not $ExtraDump
20176            and not is_target_header($Constants{$LibVersion}{$Name}{"Header"}, $LibVersion))
20177            {
20178                delete($Constants{$LibVersion}{$Name});
20179            }
20180        }
20181    }
20182
20183    if(not checkDump($LibVersion, "2.20"))
20184    { # support for old ABI dumps
20185        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20186        {
20187            my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"};
20188
20189            if($TType=~/Struct|Union|Enum|Typedef/)
20190            { # repair complex types first
20191                next;
20192            }
20193
20194            if(my $BaseId = $TypeInfo{$LibVersion}{$TypeId}{"BaseType"})
20195            {
20196                my $BType = lc($TypeInfo{$LibVersion}{$BaseId}{"Type"});
20197                if($BType=~/Struct|Union|Enum/i)
20198                {
20199                    my $BName = $TypeInfo{$LibVersion}{$BaseId}{"Name"};
20200                    $TypeInfo{$LibVersion}{$TypeId}{"Name"}=~s/\A\Q$BName\E\b/$BType $BName/g;
20201                }
20202            }
20203        }
20204        foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20205        {
20206            my $TType = $TypeInfo{$LibVersion}{$TypeId}{"Type"};
20207            my $TName = $TypeInfo{$LibVersion}{$TypeId}{"Name"};
20208            if($TType=~/Struct|Union|Enum/) {
20209                $TypeInfo{$LibVersion}{$TypeId}{"Name"} = lc($TType)." ".$TName;
20210            }
20211        }
20212    }
20213
20214    foreach my $TypeId (sort {int($a)<=>int($b)} keys(%{$TypeInfo{$LibVersion}}))
20215    { # order is important
20216        if(defined $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"})
20217        { # support for old ABI dumps < 2.0 (ACC 1.22)
20218            foreach my $BId (keys(%{$TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}}))
20219            {
20220                if(my $Access = $TypeInfo{$LibVersion}{$TypeId}{"BaseClass"}{$BId})
20221                {
20222                    if($Access ne "public") {
20223                        $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId}{"access"} = $Access;
20224                    }
20225                }
20226                $TypeInfo{$LibVersion}{$TypeId}{"Base"}{$BId} = {};
20227            }
20228            delete($TypeInfo{$LibVersion}{$TypeId}{"BaseClass"});
20229        }
20230        if(my $Header = $TypeInfo{$LibVersion}{$TypeId}{"Header"})
20231        { # support for old ABI dumps
20232            $TypeInfo{$LibVersion}{$TypeId}{"Header"} = path_format($Header, $OSgroup);
20233        }
20234        elsif(my $Source = $TypeInfo{$LibVersion}{$TypeId}{"Source"})
20235        { # DWARF ABI Dumps
20236            $TypeInfo{$LibVersion}{$TypeId}{"Header"} = $Source;
20237        }
20238        if(not defined $TypeInfo{$LibVersion}{$TypeId}{"Tid"}) {
20239            $TypeInfo{$LibVersion}{$TypeId}{"Tid"} = $TypeId;
20240        }
20241        my %TInfo = %{$TypeInfo{$LibVersion}{$TypeId}};
20242        if(defined $TInfo{"Base"})
20243        {
20244            foreach (keys(%{$TInfo{"Base"}})) {
20245                $Class_SubClasses{$LibVersion}{$_}{$TypeId}=1;
20246            }
20247        }
20248        if($TInfo{"Type"} eq "MethodPtr")
20249        {
20250            if(defined $TInfo{"Param"})
20251            { # support for old ABI dumps <= 1.17
20252                if(not defined $TInfo{"Param"}{"0"})
20253                {
20254                    my $Max = keys(%{$TInfo{"Param"}});
20255                    foreach my $Pos (1 .. $Max) {
20256                        $TInfo{"Param"}{$Pos-1} = $TInfo{"Param"}{$Pos};
20257                    }
20258                    delete($TInfo{"Param"}{$Max});
20259                    %{$TypeInfo{$LibVersion}{$TypeId}} = %TInfo;
20260                }
20261            }
20262        }
20263        if($TInfo{"BaseType"} eq $TypeId)
20264        { # fix ABI dump
20265            delete($TypeInfo{$LibVersion}{$TypeId}{"BaseType"});
20266        }
20267        if($TInfo{"Type"} eq "Typedef" and not $TInfo{"Artificial"})
20268        {
20269            if(my $BTid = $TInfo{"BaseType"})
20270            {
20271                my $BName = $TypeInfo{$LibVersion}{$BTid}{"Name"};
20272                if(not $BName)
20273                { # broken type
20274                    next;
20275                }
20276                if($TInfo{"Name"} eq $BName)
20277                { # typedef to "class Class"
20278                  # should not be registered in TName_Tid
20279                    next;
20280                }
20281                if(not $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}}) {
20282                    $Typedef_BaseName{$LibVersion}{$TInfo{"Name"}} = $BName;
20283                }
20284            }
20285        }
20286        if(not $TName_Tid{$LibVersion}{$TInfo{"Name"}})
20287        { # classes: class (id1), typedef (artificial, id2 > id1)
20288            $TName_Tid{$LibVersion}{formatName($TInfo{"Name"}, "T")} = $TypeId;
20289        }
20290    }
20291
20292    if(not checkDump($LibVersion, "2.15"))
20293    { # support for old ABI dumps
20294        my %Dups = ();
20295        foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20296        {
20297            if(my $ClassId = $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
20298            {
20299                if(not defined $TypeInfo{$LibVersion}{$ClassId})
20300                { # remove template decls
20301                    delete($SymbolInfo{$LibVersion}{$InfoId});
20302                    next;
20303                }
20304            }
20305            my $MName = $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"};
20306            if(not $MName and $SymbolInfo{$LibVersion}{$InfoId}{"Class"})
20307            { # templates
20308                delete($SymbolInfo{$LibVersion}{$InfoId});
20309            }
20310        }
20311    }
20312
20313    foreach my $InfoId (keys(%{$SymbolInfo{$LibVersion}}))
20314    {
20315        if(my $Class = $SymbolInfo{$LibVersion}{$InfoId}{"Class"}
20316        and not $SymbolInfo{$LibVersion}{$InfoId}{"Static"}
20317        and not $SymbolInfo{$LibVersion}{$InfoId}{"Data"})
20318        { # support for old ABI dumps (< 3.1)
20319            if(not defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"}
20320            or $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{0}{"name"} ne "this")
20321            { # add "this" first parameter
20322                my $ThisTid = getTypeIdByName($TypeInfo{$LibVersion}{$Class}{"Name"}."*const", $LibVersion);
20323                my %PInfo = ("name"=>"this", "type"=>"$ThisTid");
20324
20325                if(defined $SymbolInfo{$LibVersion}{$InfoId}{"Param"})
20326                {
20327                    my @Pos = sort {int($a)<=>int($b)} keys(%{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}});
20328                    foreach my $Pos (reverse(0 .. $#Pos)) {
20329                        %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos+1}} = %{$SymbolInfo{$LibVersion}{$InfoId}{"Param"}{$Pos}};
20330                    }
20331                }
20332                $SymbolInfo{$LibVersion}{$InfoId}{"Param"}{"0"} = \%PInfo;
20333            }
20334        }
20335
20336        if(not $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"})
20337        { # ABI dumps have no mangled names for C-functions
20338            $SymbolInfo{$LibVersion}{$InfoId}{"MnglName"} = $SymbolInfo{$LibVersion}{$InfoId}{"ShortName"};
20339        }
20340        if(my $Header = $SymbolInfo{$LibVersion}{$InfoId}{"Header"})
20341        { # support for old ABI dumps
20342            $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = path_format($Header, $OSgroup);
20343        }
20344        elsif(my $Source = $SymbolInfo{$LibVersion}{$InfoId}{"Source"})
20345        { # DWARF ABI Dumps
20346            $SymbolInfo{$LibVersion}{$InfoId}{"Header"} = $Source;
20347        }
20348    }
20349
20350    $Descriptor{$LibVersion}{"Dump"} = 1;
20351}
20352
20353sub read_Machine_DumpInfo($$)
20354{
20355    my ($ABI, $LibVersion) = @_;
20356    if($ABI->{"Arch"}) {
20357        $CPU_ARCH{$LibVersion} = $ABI->{"Arch"};
20358    }
20359    if($ABI->{"WordSize"}) {
20360        $WORD_SIZE{$LibVersion} = $ABI->{"WordSize"};
20361    }
20362    else
20363    { # support for old dumps
20364        $WORD_SIZE{$LibVersion} = $ABI->{"SizeOfPointer"};
20365    }
20366    if(not $WORD_SIZE{$LibVersion})
20367    { # support for old dumps (<1.23)
20368        if(my $Tid = getTypeIdByName("char*", $LibVersion))
20369        { # size of char*
20370            $WORD_SIZE{$LibVersion} = $TypeInfo{$LibVersion}{$Tid}{"Size"};
20371        }
20372        else
20373        {
20374            my $PSize = 0;
20375            foreach my $Tid (keys(%{$TypeInfo{$LibVersion}}))
20376            {
20377                if($TypeInfo{$LibVersion}{$Tid}{"Type"} eq "Pointer")
20378                { # any "pointer"-type
20379                    $PSize = $TypeInfo{$LibVersion}{$Tid}{"Size"};
20380                    last;
20381                }
20382            }
20383            if($PSize)
20384            { # a pointer type size
20385                $WORD_SIZE{$LibVersion} = $PSize;
20386            }
20387            else {
20388                printMsg("WARNING", "cannot identify a WORD size in the ABI dump (too old format)");
20389            }
20390        }
20391    }
20392    if($ABI->{"GccVersion"}) {
20393        $GCC_VERSION{$LibVersion} = $ABI->{"GccVersion"};
20394    }
20395}
20396
20397sub read_Libs_DumpInfo($$)
20398{
20399    my ($ABI, $LibVersion) = @_;
20400    $Library_Symbol{$LibVersion} = $ABI->{"Symbols"};
20401    if(not $Library_Symbol{$LibVersion})
20402    { # support for old dumps
20403        $Library_Symbol{$LibVersion} = $ABI->{"Interfaces"};
20404    }
20405    if(keys(%{$Library_Symbol{$LibVersion}})
20406    and not $DumpAPI) {
20407        $Descriptor{$LibVersion}{"Libs"} = "OK";
20408    }
20409}
20410
20411sub read_Source_DumpInfo($$)
20412{
20413    my ($ABI, $LibVersion) = @_;
20414
20415    if(keys(%{$ABI->{"Headers"}})
20416    and not $DumpAPI) {
20417        $Descriptor{$LibVersion}{"Headers"} = "OK";
20418    }
20419    foreach my $Identity (sort {$ABI->{"Headers"}{$a}<=>$ABI->{"Headers"}{$b}} keys(%{$ABI->{"Headers"}}))
20420    {
20421        $Registered_Headers{$LibVersion}{$Identity}{"Identity"} = $Identity;
20422        $Registered_Headers{$LibVersion}{$Identity}{"Pos"} = $ABI->{"Headers"}{$Identity};
20423    }
20424
20425    if(keys(%{$ABI->{"Sources"}})
20426    and not $DumpAPI) {
20427        $Descriptor{$LibVersion}{"Sources"} = "OK";
20428    }
20429    foreach my $Name (sort {$ABI->{"Sources"}{$a}<=>$ABI->{"Sources"}{$b}} keys(%{$ABI->{"Sources"}}))
20430    {
20431        $Registered_Sources{$LibVersion}{$Name}{"Identity"} = $Name;
20432        $Registered_Sources{$LibVersion}{$Name}{"Pos"} = $ABI->{"Headers"}{$Name};
20433    }
20434}
20435
20436sub find_libs($$$)
20437{
20438    my ($Path, $Type, $MaxDepth) = @_;
20439    # FIXME: correct the search pattern
20440    return cmd_find($Path, $Type, '\.'.$LIB_EXT.'[0-9.]*\Z', $MaxDepth, 1);
20441}
20442
20443sub createDescriptor($$)
20444{
20445    my ($LibVersion, $Path) = @_;
20446    if(not $LibVersion or not $Path
20447    or not -e $Path) {
20448        return "";
20449    }
20450    if(-d $Path)
20451    { # directory with headers files and shared objects
20452        return "
20453            <version>
20454                ".$TargetVersion{$LibVersion}."
20455            </version>
20456
20457            <headers>
20458                $Path
20459            </headers>
20460
20461            <libs>
20462                $Path
20463            </libs>";
20464    }
20465    else
20466    { # files
20467        if($Path=~/\.(xml|desc)\Z/i)
20468        { # standard XML-descriptor
20469            return readFile($Path);
20470        }
20471        elsif(is_header($Path, 2, $LibVersion))
20472        { # header file
20473            return "
20474                <version>
20475                    ".$TargetVersion{$LibVersion}."
20476                </version>
20477
20478                <headers>
20479                    $Path
20480                </headers>
20481
20482                <libs>
20483                    none
20484                </libs>";
20485        }
20486        elsif(parse_libname($Path, "name", $OStarget))
20487        { # shared object
20488            return "
20489                <version>
20490                    ".$TargetVersion{$LibVersion}."
20491                </version>
20492
20493                <headers>
20494                    none
20495                </headers>
20496
20497                <libs>
20498                    $Path
20499                </libs>";
20500        }
20501        else
20502        { # standard XML-descriptor
20503            return readFile($Path);
20504        }
20505    }
20506}
20507
20508sub detect_lib_default_paths()
20509{
20510    my %LPaths = ();
20511    if($OSgroup eq "bsd")
20512    {
20513        if(my $LdConfig = get_CmdPath("ldconfig"))
20514        {
20515            foreach my $Line (split(/\n/, `$LdConfig -r 2>\"$TMP_DIR/null\"`))
20516            {
20517                if($Line=~/\A[ \t]*\d+:\-l(.+) \=\> (.+)\Z/)
20518                {
20519                    my $Name = "lib".$1;
20520                    if(not defined $LPaths{$Name}) {
20521                        $LPaths{$Name} = $2;
20522                    }
20523                }
20524            }
20525        }
20526        else {
20527            printMsg("WARNING", "can't find ldconfig");
20528        }
20529    }
20530    else
20531    {
20532        if(my $LdConfig = get_CmdPath("ldconfig"))
20533        {
20534            if($SystemRoot and $OSgroup eq "linux")
20535            { # use host (x86) ldconfig with the target (arm) ld.so.conf
20536                if(-e $SystemRoot."/etc/ld.so.conf") {
20537                    $LdConfig .= " -f ".$SystemRoot."/etc/ld.so.conf";
20538                }
20539            }
20540            foreach my $Line (split(/\n/, `$LdConfig -p 2>\"$TMP_DIR/null\"`))
20541            {
20542                if($Line=~/\A[ \t]*([^ \t]+) .* \=\> (.+)\Z/)
20543                {
20544                    my ($Name, $Path) = ($1, $2);
20545                    $Path=~s/[\/]{2,}/\//;
20546                    if(not defined $LPaths{$Name})
20547                    { # get first element from the list of available paths
20548
20549                      # libstdc++.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libstdc++.so.6
20550                      # libstdc++.so.6 (libc6) => /usr/lib/i386-linux-gnu/libstdc++.so.6
20551                      # libstdc++.so.6 (libc6) => /usr/lib32/libstdc++.so.6
20552
20553                        $LPaths{$Name} = $Path;
20554                    }
20555                }
20556            }
20557        }
20558        elsif($OSgroup eq "linux") {
20559            printMsg("WARNING", "can't find ldconfig");
20560        }
20561    }
20562    return \%LPaths;
20563}
20564
20565sub detect_bin_default_paths()
20566{
20567    my $EnvPaths = $ENV{"PATH"};
20568    if($OSgroup eq "beos") {
20569        $EnvPaths.=":".$ENV{"BETOOLS"};
20570    }
20571    my $Sep = ($OSgroup eq "windows")?";":":|;";
20572    foreach my $Path (split(/$Sep/, $EnvPaths))
20573    {
20574        $Path = path_format($Path, $OSgroup);
20575        next if(not $Path);
20576        if($SystemRoot
20577        and $Path=~/\A\Q$SystemRoot\E\//)
20578        { # do NOT use binaries from target system
20579            next;
20580        }
20581        push_U(\@DefaultBinPaths, $Path);
20582    }
20583}
20584
20585sub detect_inc_default_paths()
20586{
20587    return () if(not $GCC_PATH);
20588    my %DPaths = ("Cpp"=>[],"Gcc"=>[],"Inc"=>[]);
20589    writeFile("$TMP_DIR/empty.h", "");
20590    foreach my $Line (split(/\n/, `$GCC_PATH -v -x c++ -E \"$TMP_DIR/empty.h\" 2>&1`))
20591    { # detecting GCC default include paths
20592        next if(index($Line, "/cc1plus ")!=-1);
20593
20594        if($Line=~/\A[ \t]*((\/|\w+:\\).+)[ \t]*\Z/)
20595        {
20596            my $Path = realpath($1);
20597            $Path = path_format($Path, $OSgroup);
20598            if(index($Path, "c++")!=-1
20599            or index($Path, "/g++/")!=-1)
20600            {
20601                push_U($DPaths{"Cpp"}, $Path);
20602                if(not defined $MAIN_CPP_DIR
20603                or get_depth($MAIN_CPP_DIR)>get_depth($Path)) {
20604                    $MAIN_CPP_DIR = $Path;
20605                }
20606            }
20607            elsif(index($Path, "gcc")!=-1) {
20608                push_U($DPaths{"Gcc"}, $Path);
20609            }
20610            else
20611            {
20612                if($Path=~/local[\/\\]+include/)
20613                { # local paths
20614                    next;
20615                }
20616                if($SystemRoot
20617                and $Path!~/\A\Q$SystemRoot\E(\/|\Z)/)
20618                { # The GCC include path for user headers is not a part of the system root
20619                  # The reason: you are not specified the --cross-gcc option or selected a wrong compiler
20620                  # or it is the internal cross-GCC path like arm-linux-gnueabi/include
20621                    next;
20622                }
20623                push_U($DPaths{"Inc"}, $Path);
20624            }
20625        }
20626    }
20627    unlink("$TMP_DIR/empty.h");
20628    return %DPaths;
20629}
20630
20631sub detect_default_paths($)
20632{
20633    my ($HSearch, $LSearch, $BSearch, $GSearch) = (1, 1, 1, 1);
20634    my $Search = $_[0];
20635    if($Search!~/inc/) {
20636        $HSearch = 0;
20637    }
20638    if($Search!~/lib/) {
20639        $LSearch = 0;
20640    }
20641    if($Search!~/bin/) {
20642        $BSearch = 0;
20643    }
20644    if($Search!~/gcc/) {
20645        $GSearch = 0;
20646    }
20647    if(@{$SystemPaths{"include"}})
20648    { # <search_headers> section of the XML descriptor
20649      # do NOT search for systems headers
20650        $HSearch = 0;
20651    }
20652    if(@{$SystemPaths{"lib"}})
20653    { # <search_headers> section of the XML descriptor
20654      # do NOT search for systems headers
20655        $LSearch = 0;
20656    }
20657    foreach my $Type (keys(%{$OS_AddPath{$OSgroup}}))
20658    { # additional search paths
20659        next if($Type eq "include" and not $HSearch);
20660        next if($Type eq "lib" and not $LSearch);
20661        next if($Type eq "bin" and not $BSearch);
20662        push_U($SystemPaths{$Type}, grep { -d $_ } @{$OS_AddPath{$OSgroup}{$Type}});
20663    }
20664    if($OSgroup ne "windows")
20665    { # unix-like
20666        foreach my $Type ("include", "lib", "bin")
20667        { # automatic detection of system "devel" directories
20668            next if($Type eq "include" and not $HSearch);
20669            next if($Type eq "lib" and not $LSearch);
20670            next if($Type eq "bin" and not $BSearch);
20671            my ($UsrDir, $RootDir) = ("/usr", "/");
20672            if($SystemRoot and $Type ne "bin")
20673            { # 1. search for target headers and libraries
20674              # 2. use host commands: ldconfig, readelf, etc.
20675                ($UsrDir, $RootDir) = ("$SystemRoot/usr", $SystemRoot);
20676            }
20677            push_U($SystemPaths{$Type}, cmd_find($RootDir,"d","*$Type*",1));
20678            if(-d $RootDir."/".$Type)
20679            { # if "/lib" is symbolic link
20680                if($RootDir eq "/") {
20681                    push_U($SystemPaths{$Type}, "/".$Type);
20682                }
20683                else {
20684                    push_U($SystemPaths{$Type}, $RootDir."/".$Type);
20685                }
20686            }
20687            if(-d $UsrDir)
20688            {
20689                push_U($SystemPaths{$Type}, cmd_find($UsrDir,"d","*$Type*",1));
20690                if(-d $UsrDir."/".$Type)
20691                { # if "/usr/lib" is symbolic link
20692                    push_U($SystemPaths{$Type}, $UsrDir."/".$Type);
20693                }
20694            }
20695        }
20696    }
20697    if($BSearch)
20698    {
20699        detect_bin_default_paths();
20700        push_U($SystemPaths{"bin"}, @DefaultBinPaths);
20701    }
20702    # check environment variables
20703    if($OSgroup eq "beos")
20704    {
20705        foreach (my @Paths = @{$SystemPaths{"bin"}})
20706        {
20707            if($_ eq ".") {
20708                next;
20709            }
20710            # search for /boot/develop/abi/x86/gcc4/tools/gcc-4.4.4-haiku-101111/bin/
20711            if(my @Dirs = sort cmd_find($_, "d", "bin")) {
20712                push_U($SystemPaths{"bin"}, sort {get_depth($a)<=>get_depth($b)} @Dirs);
20713            }
20714        }
20715        if($HSearch)
20716        {
20717            push_U(\@DefaultIncPaths, grep { is_abs($_) } (
20718                split(/:|;/, $ENV{"BEINCLUDES"})
20719                ));
20720        }
20721        if($LSearch)
20722        {
20723            push_U(\@DefaultLibPaths, grep { is_abs($_) } (
20724                split(/:|;/, $ENV{"BELIBRARIES"}),
20725                split(/:|;/, $ENV{"LIBRARY_PATH"})
20726                ));
20727        }
20728    }
20729    if($LSearch)
20730    { # using linker to get system paths
20731        if(my $LPaths = detect_lib_default_paths())
20732        { # unix-like
20733            my %Dirs = ();
20734            foreach my $Name (keys(%{$LPaths}))
20735            {
20736                if($SystemRoot
20737                and $LPaths->{$Name}!~/\A\Q$SystemRoot\E\//)
20738                { # wrong ldconfig configuration
20739                  # check your <sysroot>/etc/ld.so.conf
20740                    next;
20741                }
20742                $DyLib_DefaultPath{$Name} = $LPaths->{$Name};
20743                if(my $Dir = get_dirname($LPaths->{$Name})) {
20744                    $Dirs{$Dir} = 1;
20745                }
20746            }
20747            push_U(\@DefaultLibPaths, sort {get_depth($a)<=>get_depth($b)} sort keys(%Dirs));
20748        }
20749        push_U($SystemPaths{"lib"}, @DefaultLibPaths);
20750    }
20751    if($BSearch)
20752    {
20753        if($CrossGcc)
20754        { # --cross-gcc=arm-linux-gcc
20755            if(-e $CrossGcc)
20756            { # absolute or relative path
20757                $GCC_PATH = get_abs_path($CrossGcc);
20758            }
20759            elsif($CrossGcc!~/\// and get_CmdPath($CrossGcc))
20760            { # command name
20761                $GCC_PATH = $CrossGcc;
20762            }
20763            else {
20764                exitStatus("Access_Error", "can't access \'$CrossGcc\'");
20765            }
20766            if($GCC_PATH=~/\s/) {
20767                $GCC_PATH = "\"".$GCC_PATH."\"";
20768            }
20769        }
20770    }
20771    if($GSearch)
20772    { # GCC path and default include dirs
20773        if(not $CrossGcc)
20774        { # try default gcc
20775            $GCC_PATH = get_CmdPath("gcc");
20776        }
20777        if(not $GCC_PATH)
20778        { # try to find gcc-X.Y
20779            foreach my $Path (@{$SystemPaths{"bin"}})
20780            {
20781                if(my @GCCs = cmd_find($Path, "", '/gcc-[0-9.]*\Z', 1, 1))
20782                { # select the latest version
20783                    @GCCs = sort {$b cmp $a} @GCCs;
20784                    if(check_gcc($GCCs[0], "3"))
20785                    {
20786                        $GCC_PATH = $GCCs[0];
20787                        last;
20788                    }
20789                }
20790            }
20791        }
20792        if(not $GCC_PATH) {
20793            exitStatus("Not_Found", "can't find GCC>=3.0 in PATH");
20794        }
20795
20796        if(not $CheckObjectsOnly_Opt)
20797        {
20798            if(my $GCC_Ver = get_dumpversion($GCC_PATH))
20799            {
20800                my $GccTarget = get_dumpmachine($GCC_PATH);
20801                printMsg("INFO", "Using GCC $GCC_Ver ($GccTarget, target: ".getArch_GCC(1).")");
20802                if($GccTarget=~/symbian/)
20803                {
20804                    $OStarget = "symbian";
20805                    $LIB_EXT = $OS_LibExt{$LIB_TYPE}{$OStarget};
20806                }
20807
20808                # check GCC version
20809                if($GCC_Ver=~/\A4\.8(|\.[012])\Z/)
20810                { # bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57850
20811                  # introduced in 4.8
20812                  # fixed in 4.8.3
20813                    printMsg("WARNING", "Not working properly with GCC $GCC_Ver. Please update or downgrade GCC or use a local installation by --gcc-path=PATH option.");
20814                    $EMERGENCY_MODE_48 = 1;
20815                }
20816            }
20817            else {
20818                exitStatus("Error", "something is going wrong with the GCC compiler");
20819            }
20820        }
20821        if($HSearch)
20822        {
20823            if(not $NoStdInc)
20824            { # do NOT search in GCC standard paths
20825                my %DPaths = detect_inc_default_paths();
20826                @DefaultCppPaths = @{$DPaths{"Cpp"}};
20827                @DefaultGccPaths = @{$DPaths{"Gcc"}};
20828                @DefaultIncPaths = @{$DPaths{"Inc"}};
20829                push_U($SystemPaths{"include"}, @DefaultIncPaths);
20830            }
20831        }
20832    }
20833    if($HSearch)
20834    { # users include paths
20835        my $IncPath = "/usr/include";
20836        if($SystemRoot) {
20837            $IncPath = $SystemRoot.$IncPath;
20838        }
20839        if(-d $IncPath) {
20840            push_U(\@UsersIncPath, $IncPath);
20841        }
20842    }
20843
20844    if($ExtraInfo)
20845    {
20846        writeFile($ExtraInfo."/default-libs", join("\n", @DefaultLibPaths));
20847        writeFile($ExtraInfo."/default-includes", join("\n", (@DefaultCppPaths, @DefaultGccPaths, @DefaultIncPaths)));
20848    }
20849}
20850
20851sub getLIB_EXT($)
20852{
20853    my $Target = $_[0];
20854    if(my $Ext = $OS_LibExt{$LIB_TYPE}{$Target}) {
20855        return $Ext;
20856    }
20857    return $OS_LibExt{$LIB_TYPE}{"default"};
20858}
20859
20860sub getAR_EXT($)
20861{
20862    my $Target = $_[0];
20863    if(my $Ext = $OS_Archive{$Target}) {
20864        return $Ext;
20865    }
20866    return $OS_Archive{"default"};
20867}
20868
20869sub get_dumpversion($)
20870{
20871    my $Cmd = $_[0];
20872    return "" if(not $Cmd);
20873    if($Cache{"get_dumpversion"}{$Cmd}) {
20874        return $Cache{"get_dumpversion"}{$Cmd};
20875    }
20876    my $V = `$Cmd -dumpversion 2>\"$TMP_DIR/null\"`;
20877    chomp($V);
20878    return ($Cache{"get_dumpversion"}{$Cmd} = $V);
20879}
20880
20881sub get_dumpmachine($)
20882{
20883    my $Cmd = $_[0];
20884    return "" if(not $Cmd);
20885    if($Cache{"get_dumpmachine"}{$Cmd}) {
20886        return $Cache{"get_dumpmachine"}{$Cmd};
20887    }
20888    my $Machine = `$Cmd -dumpmachine 2>\"$TMP_DIR/null\"`;
20889    chomp($Machine);
20890    return ($Cache{"get_dumpmachine"}{$Cmd} = $Machine);
20891}
20892
20893sub checkCmd($)
20894{
20895    my $Cmd = $_[0];
20896    return "" if(not $Cmd);
20897    my @Options = (
20898        "--version",
20899        "-help"
20900    );
20901    foreach my $Opt (@Options)
20902    {
20903        my $Info = `$Cmd $Opt 2>\"$TMP_DIR/null\"`;
20904        if($Info) {
20905            return 1;
20906        }
20907    }
20908    return 0;
20909}
20910
20911sub check_gcc($$)
20912{
20913    my ($Cmd, $ReqVer) = @_;
20914    return 0 if(not $Cmd or not $ReqVer);
20915    if(defined $Cache{"check_gcc"}{$Cmd}{$ReqVer}) {
20916        return $Cache{"check_gcc"}{$Cmd}{$ReqVer};
20917    }
20918    if(my $GccVer = get_dumpversion($Cmd))
20919    {
20920        $GccVer=~s/(-|_)[a-z_]+.*\Z//; # remove suffix (like "-haiku-100818")
20921        if(cmpVersions($GccVer, $ReqVer)>=0) {
20922            return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = $Cmd);
20923        }
20924    }
20925    return ($Cache{"check_gcc"}{$Cmd}{$ReqVer} = "");
20926}
20927
20928sub get_depth($)
20929{
20930    if(defined $Cache{"get_depth"}{$_[0]}) {
20931        return $Cache{"get_depth"}{$_[0]};
20932    }
20933    return ($Cache{"get_depth"}{$_[0]} = ($_[0]=~tr![\/\\]|\:\:!!));
20934}
20935
20936sub registerGccHeaders()
20937{
20938    return if($Cache{"registerGccHeaders"}); # this function should be called once
20939
20940    foreach my $Path (@DefaultGccPaths)
20941    {
20942        my @Headers = cmd_find($Path,"f");
20943        @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers;
20944        foreach my $HPath (@Headers)
20945        {
20946            my $FileName = get_filename($HPath);
20947            if(not defined $DefaultGccHeader{$FileName})
20948            { # skip duplicated
20949                $DefaultGccHeader{$FileName} = $HPath;
20950            }
20951        }
20952    }
20953    $Cache{"registerGccHeaders"} = 1;
20954}
20955
20956sub registerCppHeaders()
20957{
20958    return if($Cache{"registerCppHeaders"}); # this function should be called once
20959
20960    foreach my $CppDir (@DefaultCppPaths)
20961    {
20962        my @Headers = cmd_find($CppDir,"f");
20963        @Headers = sort {get_depth($a)<=>get_depth($b)} @Headers;
20964        foreach my $Path (@Headers)
20965        {
20966            my $FileName = get_filename($Path);
20967            if(not defined $DefaultCppHeader{$FileName})
20968            { # skip duplicated
20969                $DefaultCppHeader{$FileName} = $Path;
20970            }
20971        }
20972    }
20973    $Cache{"registerCppHeaders"} = 1;
20974}
20975
20976sub parse_libname($$$)
20977{
20978    return "" if(not $_[0]);
20979    if(defined $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]}) {
20980        return $Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]};
20981    }
20982    return ($Cache{"parse_libname"}{$_[2]}{$_[1]}{$_[0]} = parse_libname_I(@_));
20983}
20984
20985sub parse_libname_I($$$)
20986{
20987    my ($Name, $Type, $Target) = @_;
20988
20989    if($Target eq "symbian") {
20990        return parse_libname_symbian($Name, $Type);
20991    }
20992    elsif($Target eq "windows") {
20993        return parse_libname_windows($Name, $Type);
20994    }
20995
20996    # unix
20997    my $Ext = getLIB_EXT($Target);
20998    if($Name=~/((((lib|).+?)([\-\_][\d\-\.\_]+.*?|))\.$Ext)(\.(.+)|)\Z/)
20999    { # libSDL-1.2.so.0.7.1
21000      # libwbxml2.so.0.0.18
21001      # libopcodes-2.21.53-system.20110810.so
21002        if($Type eq "name")
21003        { # libSDL-1.2
21004          # libwbxml2
21005            return $2;
21006        }
21007        elsif($Type eq "name+ext")
21008        { # libSDL-1.2.so
21009          # libwbxml2.so
21010            return $1;
21011        }
21012        elsif($Type eq "version")
21013        {
21014            if(defined $7
21015            and $7 ne "")
21016            { # 0.7.1
21017                return $7;
21018            }
21019            else
21020            { # libc-2.5.so (=>2.5 version)
21021                my $MV = $5;
21022                $MV=~s/\A[\-\_]+//g;
21023                return $MV;
21024            }
21025        }
21026        elsif($Type eq "short")
21027        { # libSDL
21028          # libwbxml2
21029            return $3;
21030        }
21031        elsif($Type eq "shortest")
21032        { # SDL
21033          # wbxml
21034            return shortest_name($3);
21035        }
21036    }
21037    return "";# error
21038}
21039
21040sub parse_libname_symbian($$)
21041{
21042    my ($Name, $Type) = @_;
21043    my $Ext = getLIB_EXT("symbian");
21044    if($Name=~/(((.+?)(\{.+\}|))\.$Ext)\Z/)
21045    { # libpthread{00010001}.dso
21046        if($Type eq "name")
21047        { # libpthread{00010001}
21048            return $2;
21049        }
21050        elsif($Type eq "name+ext")
21051        { # libpthread{00010001}.dso
21052            return $1;
21053        }
21054        elsif($Type eq "version")
21055        { # 00010001
21056            my $V = $4;
21057            $V=~s/\{(.+)\}/$1/;
21058            return $V;
21059        }
21060        elsif($Type eq "short")
21061        { # libpthread
21062            return $3;
21063        }
21064        elsif($Type eq "shortest")
21065        { # pthread
21066            return shortest_name($3);
21067        }
21068    }
21069    return "";# error
21070}
21071
21072sub parse_libname_windows($$)
21073{
21074    my ($Name, $Type) = @_;
21075    my $Ext = getLIB_EXT("windows");
21076    if($Name=~/((.+?)\.$Ext)\Z/)
21077    { # netapi32.dll
21078        if($Type eq "name")
21079        { # netapi32
21080            return $2;
21081        }
21082        elsif($Type eq "name+ext")
21083        { # netapi32.dll
21084            return $1;
21085        }
21086        elsif($Type eq "version")
21087        { # DLL version embedded
21088          # at binary-level
21089            return "";
21090        }
21091        elsif($Type eq "short")
21092        { # netapi32
21093            return $2;
21094        }
21095        elsif($Type eq "shortest")
21096        { # netapi
21097            return shortest_name($2);
21098        }
21099    }
21100    return "";# error
21101}
21102
21103sub shortest_name($)
21104{
21105    my $Name = $_[0];
21106    # remove prefix
21107    $Name=~s/\A(lib|open)//;
21108    # remove suffix
21109    $Name=~s/[\W\d_]+\Z//i;
21110    $Name=~s/([a-z]{2,})(lib)\Z/$1/i;
21111    return $Name;
21112}
21113
21114sub createSymbolsList($$$$$)
21115{
21116    my ($DPath, $SaveTo, $LName, $LVersion, $ArchName) = @_;
21117    read_ABI_Dump(1, $DPath);
21118    if(not $CheckObjectsOnly) {
21119        prepareSymbols(1);
21120    }
21121    my %SymbolHeaderLib = ();
21122    my $Total = 0;
21123    # Get List
21124    foreach my $Symbol (sort keys(%{$CompleteSignature{1}}))
21125    {
21126        if(not link_symbol($Symbol, 1, "-Deps"))
21127        { # skip src only and all external functions
21128            next;
21129        }
21130        if(not symbolFilter($Symbol, 1, "Public", "Binary"))
21131        { # skip other symbols
21132            next;
21133        }
21134        my $HeaderName = $CompleteSignature{1}{$Symbol}{"Header"};
21135        if(not $HeaderName)
21136        { # skip src only and all external functions
21137            next;
21138        }
21139        my $DyLib = $Symbol_Library{1}{$Symbol};
21140        if(not $DyLib)
21141        { # skip src only and all external functions
21142            next;
21143        }
21144        $SymbolHeaderLib{$HeaderName}{$DyLib}{$Symbol} = 1;
21145        $Total+=1;
21146    }
21147    # Draw List
21148    my $SYMBOLS_LIST = "<h1>Public symbols in <span style='color:Blue;'>$LName</span> (<span style='color:Red;'>$LVersion</span>)";
21149    $SYMBOLS_LIST .= " on <span style='color:Blue;'>".showArch($ArchName)."</span><br/>Total: $Total</h1><br/>";
21150    foreach my $HeaderName (sort {lc($a) cmp lc($b)} keys(%SymbolHeaderLib))
21151    {
21152        foreach my $DyLib (sort {lc($a) cmp lc($b)} keys(%{$SymbolHeaderLib{$HeaderName}}))
21153        {
21154            my %NS_Symbol = ();
21155            foreach my $Symbol (keys(%{$SymbolHeaderLib{$HeaderName}{$DyLib}})) {
21156                $NS_Symbol{select_Symbol_NS($Symbol, 1)}{$Symbol} = 1;
21157            }
21158            foreach my $NameSpace (sort keys(%NS_Symbol))
21159            {
21160                $SYMBOLS_LIST .= getTitle($HeaderName, $DyLib, $NameSpace);
21161                my @SortedInterfaces = sort {lc(get_Signature($a, 1)) cmp lc(get_Signature($b, 1))} keys(%{$NS_Symbol{$NameSpace}});
21162                foreach my $Symbol (@SortedInterfaces)
21163                {
21164                    my $SubReport = "";
21165                    my $Signature = get_Signature($Symbol, 1);
21166                    if($NameSpace) {
21167                        $Signature=~s/\b\Q$NameSpace\E::\b//g;
21168                    }
21169                    if($Symbol=~/\A(_Z|\?)/)
21170                    {
21171                        if($Signature) {
21172                            $SubReport = insertIDs($ContentSpanStart.highLight_Signature_Italic_Color($Signature).$ContentSpanEnd."<br/>\n".$ContentDivStart."<span class='mangled'>[symbol: <b>$Symbol</b>]</span><br/><br/>".$ContentDivEnd."\n");
21173                        }
21174                        else {
21175                            $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
21176                        }
21177                    }
21178                    else
21179                    {
21180                        if($Signature) {
21181                            $SubReport = "<span class='iname'>".highLight_Signature_Italic_Color($Signature)."</span><br/>\n";
21182                        }
21183                        else {
21184                            $SubReport = "<span class='iname'>".$Symbol."</span><br/>\n";
21185                        }
21186                    }
21187                    $SYMBOLS_LIST .= $SubReport;
21188                }
21189            }
21190            $SYMBOLS_LIST .= "<br/>\n";
21191        }
21192    }
21193    # clear info
21194    (%TypeInfo, %SymbolInfo, %Library_Symbol, %DepSymbol_Library,
21195    %DepLibrary_Symbol, %SymVer, %SkipTypes, %SkipSymbols,
21196    %NestedNameSpaces, %ClassMethods, %AllocableClass, %ClassNames,
21197    %CompleteSignature, %SkipNameSpaces, %Symbol_Library, %Library_Symbol) = ();
21198    ($Content_Counter, $ContentID) = (0, 0);
21199    # print report
21200    my $CssStyles = readModule("Styles", "SymbolsList.css");
21201    my $JScripts = readModule("Scripts", "Sections.js");
21202    $SYMBOLS_LIST = "<a name='Top'></a>".$SYMBOLS_LIST.$TOP_REF."<br/>\n";
21203    my $Title = "$LName: public symbols";
21204    my $Keywords = "$LName, API, symbols";
21205    my $Description = "List of symbols in $LName ($LVersion) on ".showArch($ArchName);
21206    $SYMBOLS_LIST = composeHTML_Head($Title, $Keywords, $Description, $CssStyles, $JScripts)."
21207    <body><div>\n$SYMBOLS_LIST</div>
21208    <br/><br/><hr/>\n".getReportFooter($LName, 1)."
21209    </body></html>";
21210    writeFile($SaveTo, $SYMBOLS_LIST);
21211}
21212
21213sub add_target_libs($)
21214{
21215    foreach (@{$_[0]}) {
21216        $TargetLibs{$_} = 1;
21217    }
21218}
21219
21220sub is_target_lib($)
21221{
21222    my $LName = $_[0];
21223    if(not $LName) {
21224        return 0;
21225    }
21226    if($TargetLibraryName
21227    and $LName!~/\Q$TargetLibraryName\E/) {
21228        return 0;
21229    }
21230    if(keys(%TargetLibs)
21231    and not $TargetLibs{$LName}
21232    and not $TargetLibs{parse_libname($LName, "name+ext", $OStarget)}) {
21233        return 0;
21234    }
21235    return 1;
21236}
21237
21238sub is_target_header($$)
21239{ # --header, --headers-list
21240    my ($H, $V) = @_;
21241    if(keys(%{$TargetHeaders{$V}}))
21242    {
21243        if($TargetHeaders{$V}{$H}) {
21244            return 1;
21245        }
21246    }
21247    return 0;
21248}
21249
21250sub checkVersionNum($$)
21251{
21252    my ($LibVersion, $Path) = @_;
21253    if(my $VerNum = $TargetVersion{$LibVersion}) {
21254        return $VerNum;
21255    }
21256    my $UsedAltDescr = 0;
21257    foreach my $Part (split(/\s*,\s*/, $Path))
21258    { # try to get version string from file path
21259        next if(isDump($Part)); # ABI dump
21260        next if($Part=~/\.(xml|desc)\Z/i); # XML descriptor
21261        my $VerNum = "";
21262        if(parse_libname($Part, "name", $OStarget))
21263        {
21264            $UsedAltDescr = 1;
21265            $VerNum = parse_libname($Part, "version", $OStarget);
21266            if(not $VerNum) {
21267                $VerNum = readStrVer($Part);
21268            }
21269        }
21270        elsif(is_header($Part, 2, $LibVersion) or -d $Part)
21271        {
21272            $UsedAltDescr = 1;
21273            $VerNum = readStrVer($Part);
21274        }
21275        if($VerNum ne "")
21276        {
21277            $TargetVersion{$LibVersion} = $VerNum;
21278            if($DumpAPI) {
21279                printMsg("WARNING", "setting version number to $VerNum (use -vnum option to change it)");
21280            }
21281            else {
21282                printMsg("WARNING", "setting ".($LibVersion==1?"1st":"2nd")." version number to \"$VerNum\" (use -v$LibVersion option to change it)");
21283            }
21284            return $TargetVersion{$LibVersion};
21285        }
21286    }
21287    if($UsedAltDescr)
21288    {
21289        if($DumpAPI) {
21290            exitStatus("Error", "version number is not set (use -vnum option)");
21291        }
21292        else {
21293            exitStatus("Error", ($LibVersion==1?"1st":"2nd")." version number is not set (use -v$LibVersion option)");
21294        }
21295    }
21296}
21297
21298sub readStrVer($)
21299{
21300    my $Str = $_[0];
21301    return "" if(not $Str);
21302    $Str=~s/\Q$TargetLibraryName\E//g;
21303    if($Str=~/(\/|\\|\w|\A)[\-\_]*(\d+[\d\.\-]+\d+|\d+)/)
21304    { # .../libssh-0.4.0/...
21305        return $2;
21306    }
21307    elsif(my $V = parse_libname($Str, "version", $OStarget)) {
21308        return $V;
21309    }
21310    return "";
21311}
21312
21313sub readLibs($)
21314{
21315    my $LibVersion = $_[0];
21316    if($OStarget eq "windows")
21317    { # dumpbin.exe will crash
21318        # without VS Environment
21319        check_win32_env();
21320    }
21321    readSymbols($LibVersion);
21322    translateSymbols(keys(%{$Symbol_Library{$LibVersion}}), $LibVersion);
21323    translateSymbols(keys(%{$DepSymbol_Library{$LibVersion}}), $LibVersion);
21324}
21325
21326sub dump_sorting($)
21327{
21328    my $Hash = $_[0];
21329    return [] if(not $Hash);
21330    my @Keys = keys(%{$Hash});
21331    return [] if($#Keys<0);
21332    if($Keys[0]=~/\A\d+\Z/)
21333    { # numbers
21334        return [sort {int($a)<=>int($b)} @Keys];
21335    }
21336    else
21337    { # strings
21338        return [sort {$a cmp $b} @Keys];
21339    }
21340}
21341
21342sub printMsg($$)
21343{
21344    my ($Type, $Msg) = @_;
21345    if($Type!~/\AINFO/) {
21346        $Msg = $Type.": ".$Msg;
21347    }
21348    if($Type!~/_C\Z/) {
21349        $Msg .= "\n";
21350    }
21351    if($Quiet)
21352    { # --quiet option
21353        appendFile($COMMON_LOG_PATH, $Msg);
21354    }
21355    else
21356    {
21357        if($Type eq "ERROR") {
21358            print STDERR $Msg;
21359        }
21360        else {
21361            print $Msg;
21362        }
21363    }
21364}
21365
21366sub exitStatus($$)
21367{
21368    my ($Code, $Msg) = @_;
21369    printMsg("ERROR", $Msg);
21370    exit($ERROR_CODE{$Code});
21371}
21372
21373sub exitReport()
21374{ # the tool has run without any errors
21375    printReport();
21376    if($COMPILE_ERRORS)
21377    { # errors in headers may add false positives/negatives
21378        exit($ERROR_CODE{"Compile_Error"});
21379    }
21380    if($BinaryOnly and $RESULT{"Binary"}{"Problems"})
21381    { # --binary
21382        exit($ERROR_CODE{"Incompatible"});
21383    }
21384    elsif($SourceOnly and $RESULT{"Source"}{"Problems"})
21385    { # --source
21386        exit($ERROR_CODE{"Incompatible"});
21387    }
21388    elsif($RESULT{"Source"}{"Problems"}
21389    or $RESULT{"Binary"}{"Problems"})
21390    { # default
21391        exit($ERROR_CODE{"Incompatible"});
21392    }
21393    else {
21394        exit($ERROR_CODE{"Compatible"});
21395    }
21396}
21397
21398sub readRules($)
21399{
21400    my $Kind = $_[0];
21401    if(not -f $RULES_PATH{$Kind}) {
21402        exitStatus("Module_Error", "can't access \'".$RULES_PATH{$Kind}."\'");
21403    }
21404    my $Content = readFile($RULES_PATH{$Kind});
21405    while(my $Rule = parseTag(\$Content, "rule"))
21406    {
21407        my $RId = parseTag(\$Rule, "id");
21408        my @Properties = ("Severity", "Change", "Effect", "Overcome", "Kind");
21409        foreach my $Prop (@Properties) {
21410            if(my $Value = parseTag(\$Rule, lc($Prop)))
21411            {
21412                $Value=~s/\n[ ]*//;
21413                $CompatRules{$Kind}{$RId}{$Prop} = $Value;
21414            }
21415        }
21416        if($CompatRules{$Kind}{$RId}{"Kind"}=~/\A(Symbols|Parameters)\Z/) {
21417            $CompatRules{$Kind}{$RId}{"Kind"} = "Symbols";
21418        }
21419        else {
21420            $CompatRules{$Kind}{$RId}{"Kind"} = "Types";
21421        }
21422    }
21423}
21424
21425sub getReportPath($)
21426{
21427    my $Level = $_[0];
21428    my $Dir = "compat_reports/$TargetLibraryName/".$Descriptor{1}{"Version"}."_to_".$Descriptor{2}{"Version"};
21429    if($Level eq "Binary")
21430    {
21431        if($BinaryReportPath)
21432        { # --bin-report-path
21433            return $BinaryReportPath;
21434        }
21435        elsif($OutputReportPath)
21436        { # --report-path
21437            return $OutputReportPath;
21438        }
21439        else
21440        { # default
21441            return $Dir."/abi_compat_report.$ReportFormat";
21442        }
21443    }
21444    elsif($Level eq "Source")
21445    {
21446        if($SourceReportPath)
21447        { # --src-report-path
21448            return $SourceReportPath;
21449        }
21450        elsif($OutputReportPath)
21451        { # --report-path
21452            return $OutputReportPath;
21453        }
21454        else
21455        { # default
21456            return $Dir."/src_compat_report.$ReportFormat";
21457        }
21458    }
21459    else
21460    {
21461        if($OutputReportPath)
21462        { # --report-path
21463            return $OutputReportPath;
21464        }
21465        else
21466        { # default
21467            return $Dir."/compat_report.$ReportFormat";
21468        }
21469    }
21470}
21471
21472sub printStatMsg($)
21473{
21474    my $Level = $_[0];
21475    printMsg("INFO", "total \"$Level\" compatibility problems: ".$RESULT{$Level}{"Problems"}.", warnings: ".$RESULT{$Level}{"Warnings"});
21476}
21477
21478sub listAffected($)
21479{
21480    my $Level = $_[0];
21481    my $List = "";
21482    foreach (keys(%{$TotalAffected{$Level}}))
21483    {
21484        if($StrictCompat and $TotalAffected{$Level}{$_} eq "Low")
21485        { # skip "Low"-severity problems
21486            next;
21487        }
21488        $List .= "$_\n";
21489    }
21490    my $Dir = get_dirname(getReportPath($Level));
21491    if($Level eq "Binary") {
21492        writeFile($Dir."/abi_affected.txt", $List);
21493    }
21494    elsif($Level eq "Source") {
21495        writeFile($Dir."/src_affected.txt", $List);
21496    }
21497}
21498
21499sub printReport()
21500{
21501    printMsg("INFO", "creating compatibility report ...");
21502    createReport();
21503    if($JoinReport or $DoubleReport)
21504    {
21505        if($RESULT{"Binary"}{"Problems"}
21506        or $RESULT{"Source"}{"Problems"}) {
21507            printMsg("INFO", "result: INCOMPATIBLE (Binary: ".$RESULT{"Binary"}{"Affected"}."\%, Source: ".$RESULT{"Source"}{"Affected"}."\%)");
21508        }
21509        else {
21510            printMsg("INFO", "result: COMPATIBLE");
21511        }
21512        printStatMsg("Binary");
21513        printStatMsg("Source");
21514        if($ListAffected)
21515        { # --list-affected
21516            listAffected("Binary");
21517            listAffected("Source");
21518        }
21519    }
21520    elsif($BinaryOnly)
21521    {
21522        if($RESULT{"Binary"}{"Problems"}) {
21523            printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Binary"}{"Affected"}."\%)");
21524        }
21525        else {
21526            printMsg("INFO", "result: COMPATIBLE");
21527        }
21528        printStatMsg("Binary");
21529        if($ListAffected)
21530        { # --list-affected
21531            listAffected("Binary");
21532        }
21533    }
21534    elsif($SourceOnly)
21535    {
21536        if($RESULT{"Source"}{"Problems"}) {
21537            printMsg("INFO", "result: INCOMPATIBLE (".$RESULT{"Source"}{"Affected"}."\%)");
21538        }
21539        else {
21540            printMsg("INFO", "result: COMPATIBLE");
21541        }
21542        printStatMsg("Source");
21543        if($ListAffected)
21544        { # --list-affected
21545            listAffected("Source");
21546        }
21547    }
21548    if($StdOut)
21549    {
21550        if($JoinReport or not $DoubleReport)
21551        { # --binary or --source
21552            printMsg("INFO", "compatibility report has been generated to stdout");
21553        }
21554        else
21555        { # default
21556            printMsg("INFO", "compatibility reports have been generated to stdout");
21557        }
21558    }
21559    else
21560    {
21561        if($JoinReport)
21562        {
21563            printMsg("INFO", "see detailed report:\n  ".getReportPath("Join"));
21564        }
21565        elsif($DoubleReport)
21566        { # default
21567            printMsg("INFO", "see detailed reports:\n  ".getReportPath("Binary")."\n  ".getReportPath("Source"));
21568        }
21569        elsif($BinaryOnly)
21570        { # --binary
21571            printMsg("INFO", "see detailed report:\n  ".getReportPath("Binary"));
21572        }
21573        elsif($SourceOnly)
21574        { # --source
21575            printMsg("INFO", "see detailed report:\n  ".getReportPath("Source"));
21576        }
21577    }
21578}
21579
21580sub check_win32_env()
21581{
21582    if(not $ENV{"DevEnvDir"}
21583    or not $ENV{"LIB"}) {
21584        exitStatus("Error", "can't start without VS environment (vsvars32.bat)");
21585    }
21586}
21587
21588sub diffSets($$)
21589{
21590    my ($S1, $S2) = @_;
21591    my @SK1 = keys(%{$S1});
21592    my @SK2 = keys(%{$S2});
21593    if($#SK1!=$#SK2) {
21594        return 1;
21595    }
21596    foreach my $K1 (@SK1)
21597    {
21598        if(not defined $S2->{$K1}) {
21599            return 1;
21600        }
21601    }
21602    return 0;
21603}
21604
21605sub defaultDumpPath($$)
21606{
21607    my ($N, $V) = @_;
21608    return "abi_dumps/".$N."/".$N."_".$V.".abi.".$AR_EXT; # gzipped by default
21609}
21610
21611sub create_ABI_Dump()
21612{
21613    if(not -e $DumpAPI) {
21614        exitStatus("Access_Error", "can't access \'$DumpAPI\'");
21615    }
21616
21617    my @DParts = split(/\s*,\s*/, $DumpAPI);
21618    foreach my $Part (@DParts)
21619    {
21620        if(not -e $Part) {
21621            exitStatus("Access_Error", "can't access \'$Part\'");
21622        }
21623    }
21624    checkVersionNum(1, $DumpAPI);
21625    foreach my $Part (@DParts)
21626    {
21627        if(isDump($Part)) {
21628            read_ABI_Dump(1, $Part);
21629        }
21630        else {
21631            readDescriptor(1, createDescriptor(1, $Part));
21632        }
21633    }
21634
21635    if(not $Descriptor{1}{"Version"})
21636    { # set to default: X
21637        $Descriptor{1}{"Version"} = "X";
21638    }
21639
21640    initLogging(1);
21641    detect_default_paths("inc|lib|bin|gcc"); # complete analysis
21642
21643    my $DumpPath = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"});
21644    if($OutputDumpPath)
21645    { # user defined path
21646        $DumpPath = $OutputDumpPath;
21647    }
21648    my $Archive = ($DumpPath=~s/\Q.$AR_EXT\E\Z//g);
21649
21650    if(not $Archive and not $StdOut)
21651    { # check archive utilities
21652        if($OSgroup eq "windows")
21653        { # using zip
21654            my $ZipCmd = get_CmdPath("zip");
21655            if(not $ZipCmd) {
21656                exitStatus("Not_Found", "can't find \"zip\"");
21657            }
21658        }
21659        else
21660        { # using tar and gzip
21661            my $TarCmd = get_CmdPath("tar");
21662            if(not $TarCmd) {
21663                exitStatus("Not_Found", "can't find \"tar\"");
21664            }
21665            my $GzipCmd = get_CmdPath("gzip");
21666            if(not $GzipCmd) {
21667                exitStatus("Not_Found", "can't find \"gzip\"");
21668            }
21669        }
21670    }
21671
21672    if(not $Descriptor{1}{"Dump"})
21673    {
21674        if(not $CheckHeadersOnly) {
21675            readLibs(1);
21676        }
21677        if($CheckHeadersOnly) {
21678            setLanguage(1, "C++");
21679        }
21680        if(not $CheckObjectsOnly) {
21681            searchForHeaders(1);
21682        }
21683        $WORD_SIZE{1} = detectWordSize(1);
21684    }
21685    if(not $Descriptor{1}{"Dump"})
21686    {
21687        if($Descriptor{1}{"Headers"}) {
21688            readHeaders(1);
21689        }
21690    }
21691    cleanDump(1);
21692    if(not keys(%{$SymbolInfo{1}}))
21693    { # check if created dump is valid
21694        if(not $ExtendedCheck and not $CheckObjectsOnly)
21695        {
21696            if($CheckHeadersOnly) {
21697                exitStatus("Empty_Set", "the set of public symbols is empty");
21698            }
21699            else {
21700                exitStatus("Empty_Intersection", "the sets of public symbols in headers and libraries have empty intersection");
21701            }
21702        }
21703    }
21704    my %HeadersInfo = ();
21705    foreach my $HPath (keys(%{$Registered_Headers{1}})) {
21706        $HeadersInfo{$Registered_Headers{1}{$HPath}{"Identity"}} = $Registered_Headers{1}{$HPath}{"Pos"};
21707    }
21708    if($ExtraDump)
21709    { # add unmangled names to the ABI dump
21710        my @Names = ();
21711        foreach my $InfoId (keys(%{$SymbolInfo{1}}))
21712        {
21713            if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"}) {
21714                push(@Names, $MnglName);
21715            }
21716        }
21717        translateSymbols(@Names, 1);
21718        foreach my $InfoId (keys(%{$SymbolInfo{1}}))
21719        {
21720            if(my $MnglName = $SymbolInfo{1}{$InfoId}{"MnglName"})
21721            {
21722                if(my $Unmangled = $tr_name{$MnglName})
21723                {
21724                    if($MnglName ne $Unmangled) {
21725                        $SymbolInfo{1}{$InfoId}{"Unmangled"} = $Unmangled;
21726                    }
21727                }
21728            }
21729        }
21730    }
21731
21732    my %GccConstants = (); # built-in GCC constants
21733    foreach my $Name (keys(%{$Constants{1}}))
21734    {
21735        if(not defined $Constants{1}{$Name}{"Header"})
21736        {
21737            $GccConstants{$Name} = $Constants{1}{$Name}{"Value"};
21738            delete($Constants{1}{$Name});
21739        }
21740    }
21741
21742    printMsg("INFO", "creating library ABI dump ...");
21743    my %ABI = (
21744        "TypeInfo" => $TypeInfo{1},
21745        "SymbolInfo" => $SymbolInfo{1},
21746        "Symbols" => $Library_Symbol{1},
21747        "DepSymbols" => $DepLibrary_Symbol{1},
21748        "SymbolVersion" => $SymVer{1},
21749        "LibraryVersion" => $Descriptor{1}{"Version"},
21750        "LibraryName" => $TargetLibraryName,
21751        "Language" => $COMMON_LANGUAGE{1},
21752        "SkipTypes" => $SkipTypes{1},
21753        "SkipSymbols" => $SkipSymbols{1},
21754        "SkipNameSpaces" => $SkipNameSpaces{1},
21755        "SkipHeaders" => $SkipHeadersList{1},
21756        "Headers" => \%HeadersInfo,
21757        "Constants" => $Constants{1},
21758        "GccConstants" => \%GccConstants,
21759        "NameSpaces" => $NestedNameSpaces{1},
21760        "Target" => $OStarget,
21761        "Arch" => getArch(1),
21762        "WordSize" => $WORD_SIZE{1},
21763        "GccVersion" => get_dumpversion($GCC_PATH),
21764        "ABI_DUMP_VERSION" => $ABI_DUMP_VERSION,
21765        "ABI_COMPLIANCE_CHECKER_VERSION" => $TOOL_VERSION
21766    );
21767    if(diffSets($TargetHeaders{1}, \%HeadersInfo)) {
21768        $ABI{"TargetHeaders"} = $TargetHeaders{1};
21769    }
21770    if($UseXML) {
21771        $ABI{"XML_ABI_DUMP_VERSION"} = $XML_ABI_DUMP_VERSION;
21772    }
21773    if($ExtendedCheck)
21774    { # --ext option
21775        $ABI{"Mode"} = "Extended";
21776    }
21777    if($BinaryOnly)
21778    { # --binary
21779        $ABI{"BinOnly"} = 1;
21780    }
21781    if($ExtraDump)
21782    { # --extra-dump
21783        $ABI{"Extra"} = 1;
21784        $ABI{"UndefinedSymbols"} = $UndefinedSymbols{1};
21785        $ABI{"Needed"} = $Library_Needed{1};
21786    }
21787
21788    my $ABI_DUMP = "";
21789    if($UseXML)
21790    {
21791        loadModule("XmlDump");
21792        $ABI_DUMP = createXmlDump(\%ABI);
21793    }
21794    else
21795    { # default
21796        $ABI_DUMP = Dumper(\%ABI);
21797    }
21798    if($StdOut)
21799    { # --stdout option
21800        print STDOUT $ABI_DUMP;
21801        printMsg("INFO", "ABI dump has been generated to stdout");
21802        return;
21803    }
21804    else
21805    { # write to gzipped file
21806        my ($DDir, $DName) = separate_path($DumpPath);
21807        my $DPath = $TMP_DIR."/".$DName;
21808        if(not $Archive) {
21809            $DPath = $DumpPath;
21810        }
21811
21812        mkpath($DDir);
21813
21814        open(DUMP, ">", $DPath) || die ("can't open file \'$DPath\': $!\n");
21815        print DUMP $ABI_DUMP;
21816        close(DUMP);
21817
21818        if(not -s $DPath) {
21819            exitStatus("Error", "can't create ABI dump because something is going wrong with the Data::Dumper module");
21820        }
21821        if($Archive) {
21822            $DumpPath = createArchive($DPath, $DDir);
21823        }
21824
21825        if($OutputDumpPath) {
21826            printMsg("INFO", "library ABI has been dumped to:\n  $OutputDumpPath");
21827        }
21828        else {
21829            printMsg("INFO", "library ABI has been dumped to:\n  $DumpPath");
21830        }
21831        printMsg("INFO", "you can transfer this dump everywhere and use instead of the ".$Descriptor{1}{"Version"}." version descriptor");
21832    }
21833}
21834
21835sub quickEmptyReports()
21836{ # Quick "empty" reports
21837  # 4 times faster than merging equal dumps
21838  # NOTE: the dump contains the "LibraryVersion" attribute
21839  # if you change the version, then your dump will be different
21840  # OVERCOME: use -v1 and v2 options for comparing dumps
21841  # and don't change version in the XML descriptor (and dumps)
21842  # OVERCOME 2: separate meta info from the dumps in ACC 2.0
21843    if(-s $Descriptor{1}{"Path"} == -s $Descriptor{2}{"Path"})
21844    {
21845        my $FilePath1 = unpackDump($Descriptor{1}{"Path"});
21846        my $FilePath2 = unpackDump($Descriptor{2}{"Path"});
21847        if($FilePath1 and $FilePath2)
21848        {
21849            my $Line = readLineNum($FilePath1, 0);
21850            if($Line=~/xml/)
21851            { # XML format
21852                # is not supported yet
21853                return;
21854            }
21855
21856            local $/ = undef;
21857
21858            open(DUMP1, $FilePath1);
21859            my $Content1 = <DUMP1>;
21860            close(DUMP1);
21861
21862            open(DUMP2, $FilePath2);
21863            my $Content2 = <DUMP2>;
21864            close(DUMP2);
21865
21866            if($Content1 eq $Content2)
21867            {
21868                # clean memory
21869                undef $Content2;
21870
21871                # read a number of headers, libs, symbols and types
21872                my $ABIdump = eval($Content1);
21873
21874                # clean memory
21875                undef $Content1;
21876
21877                if(not $ABIdump) {
21878                    exitStatus("Error", "internal error - eval() procedure seem to not working correctly, try to remove 'use strict' and try again");
21879                }
21880                if(not $ABIdump->{"TypeInfo"})
21881                { # support for old dumps
21882                    $ABIdump->{"TypeInfo"} = $ABIdump->{"TypeDescr"};
21883                }
21884                if(not $ABIdump->{"SymbolInfo"})
21885                { # support for old dumps
21886                    $ABIdump->{"SymbolInfo"} = $ABIdump->{"FuncDescr"};
21887                }
21888                read_Source_DumpInfo($ABIdump, 1);
21889                read_Libs_DumpInfo($ABIdump, 1);
21890                read_Machine_DumpInfo($ABIdump, 1);
21891                read_Machine_DumpInfo($ABIdump, 2);
21892
21893                %{$CheckedTypes{"Binary"}} = %{$ABIdump->{"TypeInfo"}};
21894                %{$CheckedTypes{"Source"}} = %{$ABIdump->{"TypeInfo"}};
21895
21896                %{$CheckedSymbols{"Binary"}} = %{$ABIdump->{"SymbolInfo"}};
21897                %{$CheckedSymbols{"Source"}} = %{$ABIdump->{"SymbolInfo"}};
21898
21899                $Descriptor{1}{"Version"} = $TargetVersion{1}?$TargetVersion{1}:$ABIdump->{"LibraryVersion"};
21900                $Descriptor{2}{"Version"} = $TargetVersion{2}?$TargetVersion{2}:$ABIdump->{"LibraryVersion"};
21901                exitReport();
21902            }
21903        }
21904    }
21905}
21906
21907sub initLogging($)
21908{
21909    my $LibVersion = $_[0];
21910    # create log directory
21911    my ($LOG_DIR, $LOG_FILE) = ("logs/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"}, "log.txt");
21912    if($OutputLogPath{$LibVersion})
21913    { # user-defined by -log-path option
21914        ($LOG_DIR, $LOG_FILE) = separate_path($OutputLogPath{$LibVersion});
21915    }
21916    if($LogMode ne "n") {
21917        mkpath($LOG_DIR);
21918    }
21919    $LOG_PATH{$LibVersion} = get_abs_path($LOG_DIR)."/".$LOG_FILE;
21920    if($Debug)
21921    { # debug directory
21922        $DEBUG_PATH{$LibVersion} = "debug/$TargetLibraryName/".$Descriptor{$LibVersion}{"Version"};
21923
21924        if(not $ExtraInfo)
21925        { # enable --extra-info
21926            $ExtraInfo = $DEBUG_PATH{$LibVersion}."/extra-info";
21927        }
21928    }
21929    resetLogging($LibVersion);
21930}
21931
21932sub writeLog($$)
21933{
21934    my ($LibVersion, $Msg) = @_;
21935    if($LogMode ne "n") {
21936        appendFile($LOG_PATH{$LibVersion}, $Msg);
21937    }
21938}
21939
21940sub resetLogging($)
21941{
21942    my $LibVersion = $_[0];
21943    if($LogMode!~/a|n/)
21944    { # remove old log
21945        unlink($LOG_PATH{$LibVersion});
21946        if($Debug) {
21947            rmtree($DEBUG_PATH{$LibVersion});
21948        }
21949    }
21950}
21951
21952sub printErrorLog($)
21953{
21954    my $LibVersion = $_[0];
21955    if($LogMode ne "n") {
21956        printMsg("ERROR", "see log for details:\n  ".$LOG_PATH{$LibVersion}."\n");
21957    }
21958}
21959
21960sub isDump($)
21961{
21962    if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump)(\.tar\.gz(\.\w+|)|\.zip|\.xml|)\Z/)
21963    { # NOTE: name.abi.tar.gz.amd64 (dh & cdbs)
21964        return $1;
21965    }
21966    return 0;
21967}
21968
21969sub isDump_U($)
21970{
21971    if(get_filename($_[0])=~/\A(.+)\.(abi|abidump|dump)(\.xml|)\Z/) {
21972        return $1;
21973    }
21974    return 0;
21975}
21976
21977sub compareInit()
21978{
21979    # read input XML descriptors or ABI dumps
21980    if(not $Descriptor{1}{"Path"}) {
21981        exitStatus("Error", "-old option is not specified");
21982    }
21983    my @DParts1 = split(/\s*,\s*/, $Descriptor{1}{"Path"});
21984    foreach my $Part (@DParts1)
21985    {
21986        if(not -e $Part) {
21987            exitStatus("Access_Error", "can't access \'$Part\'");
21988        }
21989    }
21990    if(not $Descriptor{2}{"Path"}) {
21991        exitStatus("Error", "-new option is not specified");
21992    }
21993    my @DParts2 = split(/\s*,\s*/, $Descriptor{2}{"Path"});
21994    foreach my $Part (@DParts2)
21995    {
21996        if(not -e $Part) {
21997            exitStatus("Access_Error", "can't access \'$Part\'");
21998        }
21999    }
22000    detect_default_paths("bin"); # to extract dumps
22001    if($#DParts1==0 and $#DParts2==0
22002    and isDump($Descriptor{1}{"Path"})
22003    and isDump($Descriptor{2}{"Path"}))
22004    { # optimization: equal ABI dumps
22005        quickEmptyReports();
22006    }
22007    checkVersionNum(1, $Descriptor{1}{"Path"});
22008    checkVersionNum(2, $Descriptor{2}{"Path"});
22009    printMsg("INFO", "preparation, please wait ...");
22010    foreach my $Part (@DParts1)
22011    {
22012        if(isDump($Part)) {
22013            read_ABI_Dump(1, $Part);
22014        }
22015        else {
22016            readDescriptor(1, createDescriptor(1, $Part));
22017        }
22018    }
22019    foreach my $Part (@DParts2)
22020    {
22021        if(isDump($Part)) {
22022            read_ABI_Dump(2, $Part);
22023        }
22024        else {
22025            readDescriptor(2, createDescriptor(2, $Part));
22026        }
22027    }
22028
22029    if(not $Descriptor{1}{"Version"})
22030    { # set to default: X
22031        $Descriptor{1}{"Version"} = "X";
22032    }
22033
22034    if(not $Descriptor{2}{"Version"})
22035    { # set to default: Y
22036        $Descriptor{2}{"Version"} = "Y";
22037    }
22038
22039    initLogging(1);
22040    initLogging(2);
22041    # check consistency
22042    if(not $Descriptor{1}{"Headers"}
22043    and not $Descriptor{1}{"Libs"}) {
22044        exitStatus("Error", "descriptor d1 does not contain both header files and libraries info");
22045    }
22046    if(not $Descriptor{2}{"Headers"}
22047    and not $Descriptor{2}{"Libs"}) {
22048        exitStatus("Error", "descriptor d2 does not contain both header files and libraries info");
22049    }
22050    if($Descriptor{1}{"Headers"} and not $Descriptor{1}{"Libs"}
22051    and not $Descriptor{2}{"Headers"} and $Descriptor{2}{"Libs"}) {
22052        exitStatus("Error", "can't compare headers with $SLIB_TYPE libraries");
22053    }
22054    elsif(not $Descriptor{1}{"Headers"} and $Descriptor{1}{"Libs"}
22055    and $Descriptor{2}{"Headers"} and not $Descriptor{2}{"Libs"}) {
22056        exitStatus("Error", "can't compare $SLIB_TYPE libraries with headers");
22057    }
22058    if(not $Descriptor{1}{"Headers"})
22059    {
22060        if($CheckHeadersOnly_Opt) {
22061            exitStatus("Error", "can't find header files info in descriptor d1");
22062        }
22063    }
22064    if(not $Descriptor{2}{"Headers"})
22065    {
22066        if($CheckHeadersOnly_Opt) {
22067            exitStatus("Error", "can't find header files info in descriptor d2");
22068        }
22069    }
22070    if(not $Descriptor{1}{"Headers"}
22071    or not $Descriptor{2}{"Headers"})
22072    {
22073        if(not $CheckObjectsOnly_Opt)
22074        {
22075            printMsg("WARNING", "comparing $SLIB_TYPE libraries only");
22076            $CheckObjectsOnly = 1;
22077        }
22078    }
22079    if(not $Descriptor{1}{"Libs"})
22080    {
22081        if($CheckObjectsOnly_Opt) {
22082            exitStatus("Error", "can't find $SLIB_TYPE libraries info in descriptor d1");
22083        }
22084    }
22085    if(not $Descriptor{2}{"Libs"})
22086    {
22087        if($CheckObjectsOnly_Opt) {
22088            exitStatus("Error", "can't find $SLIB_TYPE libraries info in descriptor d2");
22089        }
22090    }
22091    if(not $Descriptor{1}{"Libs"}
22092    or not $Descriptor{2}{"Libs"})
22093    { # comparing standalone header files
22094      # comparing ABI dumps created with --headers-only
22095        if(not $CheckHeadersOnly_Opt)
22096        {
22097            printMsg("WARNING", "checking headers only");
22098            $CheckHeadersOnly = 1;
22099        }
22100    }
22101    if($UseDumps)
22102    { # --use-dumps
22103      # parallel processing
22104        my $DumpPath1 = defaultDumpPath($TargetLibraryName, $Descriptor{1}{"Version"});
22105        my $DumpPath2 = defaultDumpPath($TargetLibraryName, $Descriptor{2}{"Version"});
22106
22107        unlink($DumpPath1);
22108        unlink($DumpPath2);
22109
22110        my $pid = fork();
22111        if($pid)
22112        { # dump on two CPU cores
22113            my @PARAMS = ("-dump", $Descriptor{1}{"Path"}, "-l", $TargetLibraryName);
22114            if($RelativeDirectory{1}) {
22115                @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{1});
22116            }
22117            if($OutputLogPath{1}) {
22118                @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{1});
22119            }
22120            if($CrossGcc) {
22121                @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
22122            }
22123            if($Quiet)
22124            {
22125                @PARAMS = (@PARAMS, "-quiet");
22126                @PARAMS = (@PARAMS, "-logging-mode", "a");
22127            }
22128            elsif($LogMode and $LogMode ne "w")
22129            { # "w" is default
22130                @PARAMS = (@PARAMS, "-logging-mode", $LogMode);
22131            }
22132            if($ExtendedCheck) {
22133                @PARAMS = (@PARAMS, "-extended");
22134            }
22135            if($UserLang) {
22136                @PARAMS = (@PARAMS, "-lang", $UserLang);
22137            }
22138            if($TargetVersion{1}) {
22139                @PARAMS = (@PARAMS, "-vnum", $TargetVersion{1});
22140            }
22141            if($BinaryOnly) {
22142                @PARAMS = (@PARAMS, "-binary");
22143            }
22144            if($SourceOnly) {
22145                @PARAMS = (@PARAMS, "-source");
22146            }
22147            if($SortDump) {
22148                @PARAMS = (@PARAMS, "-sort");
22149            }
22150            if($DumpFormat and $DumpFormat ne "perl") {
22151                @PARAMS = (@PARAMS, "-dump-format", $DumpFormat);
22152            }
22153            if($CheckHeadersOnly) {
22154                @PARAMS = (@PARAMS, "-headers-only");
22155            }
22156            if($CheckObjectsOnly) {
22157                @PARAMS = (@PARAMS, "-objects-only");
22158            }
22159            if($Debug)
22160            {
22161                @PARAMS = (@PARAMS, "-debug");
22162                printMsg("INFO", "running perl $0 @PARAMS");
22163            }
22164            system("perl", $0, @PARAMS);
22165            if(not -f $DumpPath1) {
22166                exit(1);
22167            }
22168        }
22169        else
22170        { # child
22171            my @PARAMS = ("-dump", $Descriptor{2}{"Path"}, "-l", $TargetLibraryName);
22172            if($RelativeDirectory{2}) {
22173                @PARAMS = (@PARAMS, "-relpath", $RelativeDirectory{2});
22174            }
22175            if($OutputLogPath{2}) {
22176                @PARAMS = (@PARAMS, "-log-path", $OutputLogPath{2});
22177            }
22178            if($CrossGcc) {
22179                @PARAMS = (@PARAMS, "-cross-gcc", $CrossGcc);
22180            }
22181            if($Quiet)
22182            {
22183                @PARAMS = (@PARAMS, "-quiet");
22184                @PARAMS = (@PARAMS, "-logging-mode", "a");
22185            }
22186            elsif($LogMode and $LogMode ne "w")
22187            { # "w" is default
22188                @PARAMS = (@PARAMS, "-logging-mode", $LogMode);
22189            }
22190            if($ExtendedCheck) {
22191                @PARAMS = (@PARAMS, "-extended");
22192            }
22193            if($UserLang) {
22194                @PARAMS = (@PARAMS, "-lang", $UserLang);
22195            }
22196            if($TargetVersion{2}) {
22197                @PARAMS = (@PARAMS, "-vnum", $TargetVersion{2});
22198            }
22199            if($BinaryOnly) {
22200                @PARAMS = (@PARAMS, "-binary");
22201            }
22202            if($SourceOnly) {
22203                @PARAMS = (@PARAMS, "-source");
22204            }
22205            if($SortDump) {
22206                @PARAMS = (@PARAMS, "-sort");
22207            }
22208            if($DumpFormat and $DumpFormat ne "perl") {
22209                @PARAMS = (@PARAMS, "-dump-format", $DumpFormat);
22210            }
22211            if($CheckHeadersOnly) {
22212                @PARAMS = (@PARAMS, "-headers-only");
22213            }
22214            if($CheckObjectsOnly) {
22215                @PARAMS = (@PARAMS, "-objects-only");
22216            }
22217            if($Debug)
22218            {
22219                @PARAMS = (@PARAMS, "-debug");
22220                printMsg("INFO", "running perl $0 @PARAMS");
22221            }
22222            system("perl", $0, @PARAMS);
22223            if(not -f $DumpPath2) {
22224                exit(1);
22225            }
22226            else {
22227                exit(0);
22228            }
22229        }
22230        waitpid($pid, 0);
22231
22232        my @CMP_PARAMS = ("-l", $TargetLibraryName);
22233        @CMP_PARAMS = (@CMP_PARAMS, "-d1", $DumpPath1);
22234        @CMP_PARAMS = (@CMP_PARAMS, "-d2", $DumpPath2);
22235        if($TargetTitle ne $TargetLibraryName) {
22236            @CMP_PARAMS = (@CMP_PARAMS, "-title", $TargetTitle);
22237        }
22238        if($ShowRetVal) {
22239            @CMP_PARAMS = (@CMP_PARAMS, "-show-retval");
22240        }
22241        if($CrossGcc) {
22242            @CMP_PARAMS = (@CMP_PARAMS, "-cross-gcc", $CrossGcc);
22243        }
22244        @CMP_PARAMS = (@CMP_PARAMS, "-logging-mode", "a");
22245        if($Quiet) {
22246            @CMP_PARAMS = (@CMP_PARAMS, "-quiet");
22247        }
22248        if($ReportFormat and $ReportFormat ne "html")
22249        { # HTML is default format
22250            @CMP_PARAMS = (@CMP_PARAMS, "-report-format", $ReportFormat);
22251        }
22252        if($OutputReportPath) {
22253            @CMP_PARAMS = (@CMP_PARAMS, "-report-path", $OutputReportPath);
22254        }
22255        if($BinaryReportPath) {
22256            @CMP_PARAMS = (@CMP_PARAMS, "-bin-report-path", $BinaryReportPath);
22257        }
22258        if($SourceReportPath) {
22259            @CMP_PARAMS = (@CMP_PARAMS, "-src-report-path", $SourceReportPath);
22260        }
22261        if($LoggingPath) {
22262            @CMP_PARAMS = (@CMP_PARAMS, "-log-path", $LoggingPath);
22263        }
22264        if($CheckHeadersOnly) {
22265            @CMP_PARAMS = (@CMP_PARAMS, "-headers-only");
22266        }
22267        if($CheckObjectsOnly) {
22268            @CMP_PARAMS = (@CMP_PARAMS, "-objects-only");
22269        }
22270        if($BinaryOnly) {
22271            @CMP_PARAMS = (@CMP_PARAMS, "-binary");
22272        }
22273        if($SourceOnly) {
22274            @CMP_PARAMS = (@CMP_PARAMS, "-source");
22275        }
22276        if($Debug)
22277        {
22278            @CMP_PARAMS = (@CMP_PARAMS, "-debug");
22279            printMsg("INFO", "running perl $0 @CMP_PARAMS");
22280        }
22281        system("perl", $0, @CMP_PARAMS);
22282        exit($?>>8);
22283    }
22284    if(not $Descriptor{1}{"Dump"}
22285    or not $Descriptor{2}{"Dump"})
22286    { # need GCC toolchain to analyze
22287      # header files and libraries
22288        detect_default_paths("inc|lib|gcc");
22289    }
22290    if(not $Descriptor{1}{"Dump"})
22291    {
22292        if(not $CheckHeadersOnly) {
22293            readLibs(1);
22294        }
22295        if($CheckHeadersOnly) {
22296            setLanguage(1, "C++");
22297        }
22298        if(not $CheckObjectsOnly) {
22299            searchForHeaders(1);
22300        }
22301        $WORD_SIZE{1} = detectWordSize(1);
22302    }
22303    if(not $Descriptor{2}{"Dump"})
22304    {
22305        if(not $CheckHeadersOnly) {
22306            readLibs(2);
22307        }
22308        if($CheckHeadersOnly) {
22309            setLanguage(2, "C++");
22310        }
22311        if(not $CheckObjectsOnly) {
22312            searchForHeaders(2);
22313        }
22314        $WORD_SIZE{2} = detectWordSize(2);
22315    }
22316    if($WORD_SIZE{1} ne $WORD_SIZE{2})
22317    { # support for old ABI dumps
22318      # try to synch different WORD sizes
22319        if(not checkDump(1, "2.1"))
22320        {
22321            $WORD_SIZE{1} = $WORD_SIZE{2};
22322            printMsg("WARNING", "set WORD size to ".$WORD_SIZE{2}." bytes");
22323        }
22324        elsif(not checkDump(2, "2.1"))
22325        {
22326            $WORD_SIZE{2} = $WORD_SIZE{1};
22327            printMsg("WARNING", "set WORD size to ".$WORD_SIZE{1}." bytes");
22328        }
22329    }
22330    elsif(not $WORD_SIZE{1}
22331    and not $WORD_SIZE{2})
22332    { # support for old ABI dumps
22333        $WORD_SIZE{1} = "4";
22334        $WORD_SIZE{2} = "4";
22335    }
22336    if($Descriptor{1}{"Dump"})
22337    { # support for old ABI dumps
22338        prepareTypes(1);
22339    }
22340    if($Descriptor{2}{"Dump"})
22341    { # support for old ABI dumps
22342        prepareTypes(2);
22343    }
22344    if($AppPath and not keys(%{$Symbol_Library{1}})) {
22345        printMsg("WARNING", "the application ".get_filename($AppPath)." has no symbols imported from the $SLIB_TYPE libraries");
22346    }
22347    # started to process input data
22348    if(not $CheckObjectsOnly)
22349    {
22350        if($Descriptor{1}{"Headers"}
22351        and not $Descriptor{1}{"Dump"}) {
22352            readHeaders(1);
22353        }
22354        if($Descriptor{2}{"Headers"}
22355        and not $Descriptor{2}{"Dump"}) {
22356            readHeaders(2);
22357        }
22358    }
22359
22360    # clean memory
22361    %SystemHeaders = ();
22362    %mangled_name_gcc = ();
22363
22364    prepareSymbols(1);
22365    prepareSymbols(2);
22366
22367    # clean memory
22368    %SymbolInfo = ();
22369
22370    # Virtual Tables
22371    registerVTable(1);
22372    registerVTable(2);
22373
22374    if(not checkDump(1, "1.22")
22375    and checkDump(2, "1.22"))
22376    { # support for old ABI dumps
22377        foreach my $ClassName (keys(%{$VirtualTable{2}}))
22378        {
22379            if($ClassName=~/</)
22380            { # templates
22381                if(not defined $VirtualTable{1}{$ClassName})
22382                { # synchronize
22383                    delete($VirtualTable{2}{$ClassName});
22384                }
22385            }
22386        }
22387    }
22388
22389    registerOverriding(1);
22390    registerOverriding(2);
22391
22392    setVirtFuncPositions(1);
22393    setVirtFuncPositions(2);
22394
22395    # Other
22396    addParamNames(1);
22397    addParamNames(2);
22398
22399    detectChangedTypedefs();
22400}
22401
22402sub compareAPIs($)
22403{
22404    my $Level = $_[0];
22405    readRules($Level);
22406    loadModule("CallConv");
22407    if($Level eq "Binary") {
22408        printMsg("INFO", "comparing ABIs ...");
22409    }
22410    else {
22411        printMsg("INFO", "comparing APIs ...");
22412    }
22413    if($CheckHeadersOnly
22414    or $Level eq "Source")
22415    { # added/removed in headers
22416        detectAdded_H($Level);
22417        detectRemoved_H($Level);
22418    }
22419    else
22420    { # added/removed in libs
22421        detectAdded($Level);
22422        detectRemoved($Level);
22423    }
22424    if(not $CheckObjectsOnly)
22425    {
22426        mergeSymbols($Level);
22427        if(keys(%{$CheckedSymbols{$Level}})) {
22428            mergeConstants($Level);
22429        }
22430    }
22431
22432    $Cache{"mergeTypes"} = (); # free memory
22433
22434    if($CheckHeadersOnly
22435    or $Level eq "Source")
22436    { # added/removed in headers
22437        mergeHeaders($Level);
22438    }
22439    else
22440    { # added/removed in libs
22441        mergeLibs($Level);
22442    }
22443}
22444
22445sub getSysOpts()
22446{
22447    my %Opts = (
22448    "OStarget"=>$OStarget,
22449    "Debug"=>$Debug,
22450    "Quiet"=>$Quiet,
22451    "LogMode"=>$LogMode,
22452    "CheckHeadersOnly"=>$CheckHeadersOnly,
22453
22454    "SystemRoot"=>$SystemRoot,
22455    "GCC_PATH"=>$GCC_PATH,
22456    "TargetSysInfo"=>$TargetSysInfo,
22457    "CrossPrefix"=>$CrossPrefix,
22458    "TargetLibraryName"=>$TargetLibraryName,
22459    "CrossGcc"=>$CrossGcc,
22460    "UseStaticLibs"=>$UseStaticLibs,
22461    "NoStdInc"=>$NoStdInc,
22462
22463    "BinaryOnly" => $BinaryOnly,
22464    "SourceOnly" => $SourceOnly
22465    );
22466    return \%Opts;
22467}
22468
22469sub get_CodeError($)
22470{
22471    my %CODE_ERROR = reverse(%ERROR_CODE);
22472    return $CODE_ERROR{$_[0]};
22473}
22474
22475sub scenario()
22476{
22477    if($StdOut)
22478    { # enable quiet mode
22479        $Quiet = 1;
22480        $JoinReport = 1;
22481    }
22482    if(not $LogMode)
22483    { # default
22484        $LogMode = "w";
22485    }
22486    if($UserLang)
22487    { # --lang=C++
22488        $UserLang = uc($UserLang);
22489        $COMMON_LANGUAGE{1}=$UserLang;
22490        $COMMON_LANGUAGE{2}=$UserLang;
22491    }
22492    if($LoggingPath)
22493    {
22494        $OutputLogPath{1} = $LoggingPath;
22495        $OutputLogPath{2} = $LoggingPath;
22496        if($Quiet) {
22497            $COMMON_LOG_PATH = $LoggingPath;
22498        }
22499    }
22500    if($Quick) {
22501        $ADD_TMPL_INSTANCES = 0;
22502    }
22503    if($OutputDumpPath)
22504    { # validate
22505        if(not isDump($OutputDumpPath)) {
22506            exitStatus("Error", "the dump path should be a path to *.abi.$AR_EXT or *.abi file");
22507        }
22508    }
22509    if($BinaryOnly and $SourceOnly)
22510    { # both --binary and --source
22511      # is the default mode
22512        $DoubleReport = 1;
22513        $JoinReport = 0;
22514        $BinaryOnly = 0;
22515        $SourceOnly = 0;
22516        if($OutputReportPath)
22517        { # --report-path
22518            $DoubleReport = 0;
22519            $JoinReport = 1;
22520        }
22521    }
22522    elsif($BinaryOnly or $SourceOnly)
22523    { # --binary or --source
22524        $DoubleReport = 0;
22525        $JoinReport = 0;
22526    }
22527    if($UseXML)
22528    { # --xml option
22529        $ReportFormat = "xml";
22530        $DumpFormat = "xml";
22531    }
22532    if($ReportFormat)
22533    { # validate
22534        $ReportFormat = lc($ReportFormat);
22535        if($ReportFormat!~/\A(xml|html|htm)\Z/) {
22536            exitStatus("Error", "unknown report format \'$ReportFormat\'");
22537        }
22538        if($ReportFormat eq "htm")
22539        { # HTM == HTML
22540            $ReportFormat = "html";
22541        }
22542        elsif($ReportFormat eq "xml")
22543        { # --report-format=XML equal to --xml
22544            $UseXML = 1;
22545        }
22546    }
22547    else
22548    { # default: HTML
22549        $ReportFormat = "html";
22550    }
22551    if($DumpFormat)
22552    { # validate
22553        $DumpFormat = lc($DumpFormat);
22554        if($DumpFormat!~/\A(xml|perl)\Z/) {
22555            exitStatus("Error", "unknown ABI dump format \'$DumpFormat\'");
22556        }
22557        if($DumpFormat eq "xml")
22558        { # --dump-format=XML equal to --xml
22559            $UseXML = 1;
22560        }
22561    }
22562    else
22563    { # default: Perl Data::Dumper
22564        $DumpFormat = "perl";
22565    }
22566    if($Quiet and $LogMode!~/a|n/)
22567    { # --quiet log
22568        if(-f $COMMON_LOG_PATH) {
22569            unlink($COMMON_LOG_PATH);
22570        }
22571    }
22572    if($ExtraInfo) {
22573        $CheckUndefined = 1;
22574    }
22575    if($TestTool and $UseDumps)
22576    { # --test && --use-dumps == --test-dump
22577        $TestDump = 1;
22578    }
22579    if($Tolerant)
22580    { # enable all
22581        $Tolerance = 1234;
22582    }
22583    if($Help)
22584    {
22585        HELP_MESSAGE();
22586        exit(0);
22587    }
22588    if($InfoMsg)
22589    {
22590        INFO_MESSAGE();
22591        exit(0);
22592    }
22593    if($ShowVersion)
22594    {
22595        printMsg("INFO", "ABI Compliance Checker (ABICC) $TOOL_VERSION\nCopyright (C) 2015 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.");
22596        exit(0);
22597    }
22598    if($DumpVersion)
22599    {
22600        printMsg("INFO", $TOOL_VERSION);
22601        exit(0);
22602    }
22603    if($ExtendedCheck) {
22604        $CheckHeadersOnly = 1;
22605    }
22606    if($SystemRoot_Opt)
22607    { # user defined root
22608        if(not -e $SystemRoot_Opt) {
22609            exitStatus("Access_Error", "can't access \'$SystemRoot\'");
22610        }
22611        $SystemRoot = $SystemRoot_Opt;
22612        $SystemRoot=~s/[\/]+\Z//g;
22613        if($SystemRoot) {
22614            $SystemRoot = get_abs_path($SystemRoot);
22615        }
22616    }
22617    $Data::Dumper::Sortkeys = 1;
22618
22619    if($SortDump)
22620    {
22621        $Data::Dumper::Useperl = 1;
22622        $Data::Dumper::Sortkeys = \&dump_sorting;
22623    }
22624
22625    if($TargetLibsPath)
22626    {
22627        if(not -f $TargetLibsPath) {
22628            exitStatus("Access_Error", "can't access file \'$TargetLibsPath\'");
22629        }
22630        foreach my $Lib (split(/\s*\n\s*/, readFile($TargetLibsPath))) {
22631            $TargetLibs{$Lib} = 1;
22632        }
22633    }
22634    if($TargetHeadersPath)
22635    { # --headers-list
22636        if(not -f $TargetHeadersPath) {
22637            exitStatus("Access_Error", "can't access file \'$TargetHeadersPath\'");
22638        }
22639        foreach my $Header (split(/\s*\n\s*/, readFile($TargetHeadersPath)))
22640        {
22641            $TargetHeaders{1}{$Header} = 1;
22642            $TargetHeaders{2}{$Header} = 1;
22643        }
22644    }
22645    if($TargetHeader)
22646    { # --header
22647        $TargetHeaders{1}{$TargetHeader} = 1;
22648        $TargetHeaders{2}{$TargetHeader} = 1;
22649    }
22650    if($TestTool
22651    or $TestDump)
22652    { # --test, --test-dump
22653        detect_default_paths("bin|gcc"); # to compile libs
22654        loadModule("RegTests");
22655        testTool($TestDump, $Debug, $Quiet, $ExtendedCheck, $LogMode, $ReportFormat, $DumpFormat,
22656        $LIB_EXT, $GCC_PATH, $SortDump, $CheckHeadersOnly, $CheckObjectsOnly);
22657        exit(0);
22658    }
22659    if($DumpSystem)
22660    { # --dump-system
22661
22662        if(not $TargetSysInfo) {
22663            exitStatus("Error", "-sysinfo option should be specified to dump system ABI");
22664        }
22665
22666        if(not -d $TargetSysInfo) {
22667            exitStatus("Access_Error", "can't access \'$TargetSysInfo\'");
22668        }
22669
22670        loadModule("SysCheck");
22671        if($DumpSystem=~/\.(xml|desc)\Z/)
22672        { # system XML descriptor
22673            if(not -f $DumpSystem) {
22674                exitStatus("Access_Error", "can't access file \'$DumpSystem\'");
22675            }
22676            my $Ret = readSystemDescriptor(readFile($DumpSystem));
22677            foreach (@{$Ret->{"Tools"}})
22678            {
22679                push_U($SystemPaths{"bin"}, $_);
22680                $TargetTools{$_} = 1;
22681            }
22682            if($Ret->{"CrossPrefix"}) {
22683                $CrossPrefix = $Ret->{"CrossPrefix"};
22684            }
22685        }
22686        elsif($SystemRoot_Opt)
22687        { # -sysroot "/" option
22688          # default target: /usr/lib, /usr/include
22689          # search libs: /usr/lib and /lib
22690            if(not -e $SystemRoot."/usr/lib") {
22691                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/lib'");
22692            }
22693            if(not -e $SystemRoot."/lib") {
22694                exitStatus("Access_Error", "can't access '".$SystemRoot."/lib'");
22695            }
22696            if(not -e $SystemRoot."/usr/include") {
22697                exitStatus("Access_Error", "can't access '".$SystemRoot."/usr/include'");
22698            }
22699            readSystemDescriptor("
22700                <name>
22701                    $DumpSystem
22702                </name>
22703                <headers>
22704                    $SystemRoot/usr/include
22705                </headers>
22706                <libs>
22707                    $SystemRoot/usr/lib
22708                </libs>
22709                <search_libs>
22710                    $SystemRoot/lib
22711                </search_libs>");
22712        }
22713        else {
22714            exitStatus("Error", "-sysroot <dirpath> option should be specified, usually it's \"/\"");
22715        }
22716        detect_default_paths("bin|gcc"); # to check symbols
22717        if($OStarget eq "windows")
22718        { # to run dumpbin.exe
22719          # and undname.exe
22720            check_win32_env();
22721        }
22722        dumpSystem(getSysOpts());
22723        exit(0);
22724    }
22725    if($CmpSystems)
22726    { # --cmp-systems
22727        detect_default_paths("bin"); # to extract dumps
22728        loadModule("SysCheck");
22729        cmpSystems($Descriptor{1}{"Path"}, $Descriptor{2}{"Path"}, getSysOpts());
22730        exit(0);
22731    }
22732    if(not $TargetLibraryName) {
22733        exitStatus("Error", "library name is not selected (-l option)");
22734    }
22735    else
22736    { # validate library name
22737        if($TargetLibraryName=~/[\*\/\\]/) {
22738            exitStatus("Error", "\"\\\", \"\/\" and \"*\" symbols are not allowed in the library name");
22739        }
22740    }
22741    if(not $TargetTitle) {
22742        $TargetTitle = $TargetLibraryName;
22743    }
22744    if($CheckHeadersOnly_Opt and $CheckObjectsOnly_Opt) {
22745        exitStatus("Error", "you can't specify both -headers-only and -objects-only options at the same time");
22746    }
22747    if($SymbolsListPath)
22748    {
22749        if(not -f $SymbolsListPath) {
22750            exitStatus("Access_Error", "can't access file \'$SymbolsListPath\'");
22751        }
22752        foreach my $Interface (split(/\s*\n\s*/, readFile($SymbolsListPath))) {
22753            $SymbolsList{$Interface} = 1;
22754        }
22755    }
22756    if($TypesListPath)
22757    {
22758        if(not -f $TypesListPath) {
22759            exitStatus("Access_Error", "can't access file \'$TypesListPath\'");
22760        }
22761        foreach my $Type (split(/\s*\n\s*/, readFile($TypesListPath))) {
22762            $TypesList{$Type} = 1;
22763        }
22764    }
22765    if($SkipSymbolsListPath)
22766    {
22767        if(not -f $SkipSymbolsListPath) {
22768            exitStatus("Access_Error", "can't access file \'$SkipSymbolsListPath\'");
22769        }
22770        foreach my $Interface (split(/\s*\n\s*/, readFile($SkipSymbolsListPath))) {
22771            $SkipSymbols{$Interface} = 1;
22772        }
22773    }
22774    if($SkipHeadersPath)
22775    {
22776        if(not -f $SkipHeadersPath) {
22777            exitStatus("Access_Error", "can't access file \'$SkipHeadersPath\'");
22778        }
22779        foreach my $Path (split(/\s*\n\s*/, readFile($SkipHeadersPath)))
22780        { # register for both versions
22781            $SkipHeadersList{1}{$Path} = 1;
22782            $SkipHeadersList{2}{$Path} = 1;
22783            my ($CPath, $Type) = classifyPath($Path);
22784            $SkipHeaders{1}{$Type}{$CPath} = 1;
22785            $SkipHeaders{2}{$Type}{$CPath} = 1;
22786        }
22787    }
22788    if($ParamNamesPath)
22789    {
22790        if(not -f $ParamNamesPath) {
22791            exitStatus("Access_Error", "can't access file \'$ParamNamesPath\'");
22792        }
22793        foreach my $Line (split(/\n/, readFile($ParamNamesPath)))
22794        {
22795            if($Line=~s/\A(\w+)\;//)
22796            {
22797                my $Interface = $1;
22798                if($Line=~/;(\d+);/)
22799                {
22800                    while($Line=~s/(\d+);(\w+)//) {
22801                        $AddIntParams{$Interface}{$1}=$2;
22802                    }
22803                }
22804                else
22805                {
22806                    my $Num = 0;
22807                    foreach my $Name (split(/;/, $Line)) {
22808                        $AddIntParams{$Interface}{$Num++}=$Name;
22809                    }
22810                }
22811            }
22812        }
22813    }
22814    if($AppPath)
22815    {
22816        if(not -f $AppPath) {
22817            exitStatus("Access_Error", "can't access file \'$AppPath\'");
22818        }
22819        foreach my $Interface (readSymbols_App($AppPath)) {
22820            $SymbolsList_App{$Interface} = 1;
22821        }
22822    }
22823    if($DumpAPI)
22824    { # --dump-abi
22825      # make an API dump
22826        create_ABI_Dump();
22827        exit($COMPILE_ERRORS);
22828    }
22829    # default: compare APIs
22830    #  -d1 <path>
22831    #  -d2 <path>
22832    compareInit();
22833    if($JoinReport or $DoubleReport)
22834    {
22835        compareAPIs("Binary");
22836        compareAPIs("Source");
22837    }
22838    elsif($BinaryOnly) {
22839        compareAPIs("Binary");
22840    }
22841    elsif($SourceOnly) {
22842        compareAPIs("Source");
22843    }
22844    exitReport();
22845}
22846
22847scenario();
22848